wc_db.c revision 299742
1/*
2 * wc_db.c :  manipulating the administrative database
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24#define SVN_WC__I_AM_WC_DB
25
26#include <assert.h>
27#include <apr_pools.h>
28#include <apr_hash.h>
29
30#include "svn_private_config.h"
31#include "svn_types.h"
32#include "svn_error.h"
33#include "svn_dirent_uri.h"
34#include "svn_path.h"
35#include "svn_hash.h"
36#include "svn_sorts.h"
37#include "svn_wc.h"
38#include "svn_checksum.h"
39#include "svn_pools.h"
40
41#include "wc.h"
42#include "wc_db.h"
43#include "adm_files.h"
44#include "wc-queries.h"
45#include "entries.h"
46#include "lock.h"
47#include "conflicts.h"
48#include "wc_db_private.h"
49#include "workqueue.h"
50#include "token-map.h"
51
52#include "private/svn_sorts_private.h"
53#include "private/svn_sqlite.h"
54#include "private/svn_skel.h"
55#include "private/svn_wc_private.h"
56#include "private/svn_token.h"
57
58
59#define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
60
61
62/*
63 * Some filename constants.
64 */
65#define SDB_FILE  "wc.db"
66
67#define WCROOT_TEMPDIR_RELPATH   "tmp"
68
69
70/*
71 * PARAMETER ASSERTIONS
72 *
73 * Every (semi-)public entrypoint in this file has a set of assertions on
74 * the parameters passed into the function. Since this is a brand new API,
75 * we want to make sure that everybody calls it properly. The original WC
76 * code had years to catch stray bugs, but we do not have that luxury in
77 * the wc-nb rewrite. Any extra assurances that we can find will be
78 * welcome. The asserts will ensure we have no doubt about the values
79 * passed into the function.
80 *
81 * Some parameters are *not* specifically asserted. Typically, these are
82 * params that will be used immediately, so something like a NULL value
83 * will be obvious.
84 *
85 * ### near 1.7 release, it would be a Good Thing to review the assertions
86 * ### and decide if any can be removed or switched to assert() in order
87 * ### to remove their runtime cost in the production release.
88 *
89 *
90 * DATABASE OPERATIONS
91 *
92 * Each function should leave the database in a consistent state. If it
93 * does *not*, then the implication is some other function needs to be
94 * called to restore consistency. Subtle requirements like that are hard
95 * to maintain over a long period of time, so this API will not allow it.
96 *
97 *
98 * STANDARD VARIABLE NAMES
99 *
100 * db     working copy database (this module)
101 * sdb    SQLite database (not to be confused with 'db')
102 * wc_id  a WCROOT id associated with a node
103 */
104
105#define INVALID_REPOS_ID ((apr_int64_t) -1)
106#define UNKNOWN_WC_ID ((apr_int64_t) -1)
107#define FORMAT_FROM_SDB (-1)
108
109/* Check if column number I, a property-skel column, contains a non-empty
110   set of properties. The empty set of properties is stored as "()", so we
111   have properties if the size of the column is larger than 2. */
112#define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
113                 (svn_sqlite__column_bytes(stmt, i) > 2)
114
115int
116svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
117{
118  return relpath_depth(local_relpath);
119}
120
121
122/* Representation of a new base row for the NODES table */
123typedef struct insert_base_baton_t {
124  /* common to all insertions into BASE */
125  svn_wc__db_status_t status;
126  svn_node_kind_t kind;
127  apr_int64_t repos_id;
128  const char *repos_relpath;
129  svn_revnum_t revision;
130
131  /* Only used when repos_id == INVALID_REPOS_ID */
132  const char *repos_root_url;
133  const char *repos_uuid;
134
135  /* common to all "normal" presence insertions */
136  const apr_hash_t *props;
137  svn_revnum_t changed_rev;
138  apr_time_t changed_date;
139  const char *changed_author;
140  const apr_hash_t *dav_cache;
141
142  /* for inserting directories */
143  const apr_array_header_t *children;
144  svn_depth_t depth;
145
146  /* for inserting files */
147  const svn_checksum_t *checksum;
148
149  /* for inserting symlinks */
150  const char *target;
151
152  svn_boolean_t file_external;
153
154  /* may need to insert/update ACTUAL to record a conflict  */
155  const svn_skel_t *conflict;
156
157  /* may need to insert/update ACTUAL to record new properties */
158  svn_boolean_t update_actual_props;
159  const apr_hash_t *new_actual_props;
160
161  /* A depth-first ordered array of svn_prop_inherited_item_t *
162     structures representing the properties inherited by the base
163     node. */
164  apr_array_header_t *iprops;
165
166  /* maybe we should copy information from a previous record? */
167  svn_boolean_t keep_recorded_info;
168
169  /* insert a base-deleted working node as well as a base node */
170  svn_boolean_t insert_base_deleted;
171
172  /* delete the current working nodes above BASE */
173  svn_boolean_t delete_working;
174
175  /* may have work items to queue in this transaction  */
176  const svn_skel_t *work_items;
177
178} insert_base_baton_t;
179
180
181/* Representation of a new working row for the NODES table */
182typedef struct insert_working_baton_t {
183  /* common to all insertions into WORKING (including NODE_DATA) */
184  svn_wc__db_status_t presence;
185  svn_node_kind_t kind;
186  int op_depth;
187
188  /* common to all "normal" presence insertions */
189  const apr_hash_t *props;
190  svn_revnum_t changed_rev;
191  apr_time_t changed_date;
192  const char *changed_author;
193  apr_int64_t original_repos_id;
194  const char *original_repos_relpath;
195  svn_revnum_t original_revnum;
196  svn_boolean_t moved_here;
197
198  /* for inserting directories */
199  const apr_array_header_t *children;
200  svn_depth_t depth;
201
202  /* for inserting (copied/moved-here) files */
203  const svn_checksum_t *checksum;
204
205  /* for inserting symlinks */
206  const char *target;
207
208  svn_boolean_t update_actual_props;
209  const apr_hash_t *new_actual_props;
210
211  /* may have work items to queue in this transaction  */
212  const svn_skel_t *work_items;
213
214  /* may have conflict to install in this transaction */
215  const svn_skel_t *conflict;
216
217  /* If the value is > 0 and < op_depth, also insert a not-present
218     at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
219  int not_present_op_depth;
220
221} insert_working_baton_t;
222
223/* Representation of a new row for the EXTERNALS table */
224typedef struct insert_external_baton_t {
225  /* common to all insertions into EXTERNALS */
226  svn_node_kind_t kind;
227  svn_wc__db_status_t presence;
228
229  /* The repository of the external */
230  apr_int64_t repos_id;
231  /* for file and symlink externals */
232  const char *repos_relpath;
233  svn_revnum_t revision;
234
235  /* Only used when repos_id == INVALID_REPOS_ID */
236  const char *repos_root_url;
237  const char *repos_uuid;
238
239  /* for file and symlink externals */
240  const apr_hash_t *props;
241  apr_array_header_t *iprops;
242  svn_revnum_t changed_rev;
243  apr_time_t changed_date;
244  const char *changed_author;
245  const apr_hash_t *dav_cache;
246
247  /* for inserting files */
248  const svn_checksum_t *checksum;
249
250  /* for inserting symlinks */
251  const char *target;
252
253  const char *record_ancestor_relpath;
254  const char *recorded_repos_relpath;
255  svn_revnum_t recorded_peg_revision;
256  svn_revnum_t recorded_revision;
257
258  /* may need to insert/update ACTUAL to record a conflict  */
259  const svn_skel_t *conflict;
260
261  /* may need to insert/update ACTUAL to record new properties */
262  svn_boolean_t update_actual_props;
263  const apr_hash_t *new_actual_props;
264
265  /* maybe we should copy information from a previous record? */
266  svn_boolean_t keep_recorded_info;
267
268  /* may have work items to queue in this transaction  */
269  const svn_skel_t *work_items;
270
271} insert_external_baton_t;
272
273
274/* Forward declarations  */
275static svn_error_t *
276add_work_items(svn_sqlite__db_t *sdb,
277               const svn_skel_t *skel,
278               apr_pool_t *scratch_pool);
279
280static svn_error_t *
281set_actual_props(svn_wc__db_wcroot_t *wcroot,
282                 const char *local_relpath,
283                 apr_hash_t *props,
284                 apr_pool_t *scratch_pool);
285
286static svn_error_t *
287insert_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
297static svn_error_t *
298db_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
305static svn_error_t *
306read_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
336static svn_error_t *
337scan_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
352static svn_error_t *
353convert_to_working_status(svn_wc__db_status_t *working_status,
354                          svn_wc__db_status_t status);
355
356static svn_error_t *
357db_is_switched(svn_boolean_t *is_switched,
358               svn_node_kind_t *kind,
359               svn_wc__db_wcroot_t *wcroot,
360               const char *local_relpath,
361               apr_pool_t *scratch_pool);
362
363
364/* Return the absolute path, in local path style, of LOCAL_RELPATH
365   in WCROOT.  */
366static const char *
367path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
368                       const char *local_relpath,
369                       apr_pool_t *result_pool)
370{
371  const char *local_abspath
372    = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
373
374  return svn_dirent_local_style(local_abspath, result_pool);
375}
376
377
378/* Return a file size from column SLOT of the SQLITE statement STMT, or
379   SVN_INVALID_FILESIZE if the column value is NULL.  */
380static svn_filesize_t
381get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
382{
383  if (svn_sqlite__column_is_null(stmt, slot))
384    return SVN_INVALID_FILESIZE;
385  return svn_sqlite__column_int64(stmt, slot);
386}
387
388
389/* Return a lock info structure constructed from the given columns of the
390   SQLITE statement STMT, or return NULL if the token column value is null.  */
391static svn_wc__db_lock_t *
392lock_from_columns(svn_sqlite__stmt_t *stmt,
393                  int col_token,
394                  int col_owner,
395                  int col_comment,
396                  int col_date,
397                  apr_pool_t *result_pool)
398{
399  svn_wc__db_lock_t *lock;
400
401  if (svn_sqlite__column_is_null(stmt, col_token))
402    {
403      lock = NULL;
404    }
405  else
406    {
407      lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
408      lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
409      lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
410      lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
411      lock->date = svn_sqlite__column_int64(stmt, col_date);
412    }
413  return lock;
414}
415
416
417svn_error_t *
418svn_wc__db_fetch_repos_info(const char **repos_root_url,
419                            const char **repos_uuid,
420                            svn_wc__db_wcroot_t *wcroot,
421                            apr_int64_t repos_id,
422                            apr_pool_t *result_pool)
423{
424  svn_sqlite__stmt_t *stmt;
425  svn_boolean_t have_row;
426
427  if (!repos_root_url && !repos_uuid)
428    return SVN_NO_ERROR;
429
430  if (repos_id == INVALID_REPOS_ID)
431    {
432      if (repos_root_url)
433        *repos_root_url = NULL;
434      if (repos_uuid)
435        *repos_uuid = NULL;
436      return SVN_NO_ERROR;
437    }
438
439  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
440                                    STMT_SELECT_REPOSITORY_BY_ID));
441  SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
442  SVN_ERR(svn_sqlite__step(&have_row, stmt));
443  if (!have_row)
444    return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
445                             _("No REPOSITORY table entry for id '%ld'"),
446                             (long int)repos_id);
447
448  if (repos_root_url)
449    *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
450  if (repos_uuid)
451    *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
452
453  return svn_error_trace(svn_sqlite__reset(stmt));
454}
455
456/* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
457   SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
458   column value is null.  Any of the output parameters may be NULL if not
459   required.  */
460static void
461repos_location_from_columns(apr_int64_t *repos_id,
462                            svn_revnum_t *revision,
463                            const char **repos_relpath,
464                            svn_sqlite__stmt_t *stmt,
465                            int col_repos_id,
466                            int col_revision,
467                            int col_repos_relpath,
468                            apr_pool_t *result_pool)
469{
470  if (repos_id)
471    {
472      /* Fetch repository information via REPOS_ID. */
473      if (svn_sqlite__column_is_null(stmt, col_repos_id))
474        *repos_id = INVALID_REPOS_ID;
475      else
476        *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
477    }
478  if (revision)
479    {
480      *revision = svn_sqlite__column_revnum(stmt, col_revision);
481    }
482  if (repos_relpath)
483    {
484      *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
485                                               result_pool);
486    }
487}
488
489/* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
490   value. If one does not exist, then create a new one. */
491static svn_error_t *
492create_repos_id(apr_int64_t *repos_id,
493                const char *repos_root_url,
494                const char *repos_uuid,
495                svn_sqlite__db_t *sdb,
496                apr_pool_t *scratch_pool)
497{
498  svn_sqlite__stmt_t *get_stmt;
499  svn_sqlite__stmt_t *insert_stmt;
500  svn_boolean_t have_row;
501
502  SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
503  SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
504  SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
505
506  if (have_row)
507    {
508      *repos_id = svn_sqlite__column_int64(get_stmt, 0);
509      return svn_error_trace(svn_sqlite__reset(get_stmt));
510    }
511  SVN_ERR(svn_sqlite__reset(get_stmt));
512
513  /* NOTE: strictly speaking, there is a race condition between the
514     above query and the insertion below. We're simply going to ignore
515     that, as it means two processes are *modifying* the working copy
516     at the same time, *and* new repositores are becoming visible.
517     This is rare enough, let alone the miniscule chance of hitting
518     this race condition. Further, simply failing out will leave the
519     database in a consistent state, and the user can just re-run the
520     failed operation. */
521
522  SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
523                                    STMT_INSERT_REPOSITORY));
524  SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
525  return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
526}
527
528
529/* Initialize the baton with appropriate "blank" values. This allows the
530   insertion function to leave certain columns null.  */
531static void
532blank_ibb(insert_base_baton_t *pibb)
533{
534  memset(pibb, 0, sizeof(*pibb));
535  pibb->revision = SVN_INVALID_REVNUM;
536  pibb->changed_rev = SVN_INVALID_REVNUM;
537  pibb->depth = svn_depth_infinity;
538  pibb->repos_id = INVALID_REPOS_ID;
539}
540
541
542/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
543
544   ### What about KIND and OP_DEPTH?  KIND ought to be redundant; I'm
545       discussing on dev@ whether we can let that be null for presence
546       == base-deleted.  OP_DEPTH is the op-depth of what, and why?
547       It is used to select the lowest working node higher than OP_DEPTH,
548       so, in terms of the API, OP_DEPTH means ...?
549
550   Given a wc:
551
552              0         1         2         3         4
553              normal
554   A          normal
555   A/B        normal              normal
556   A/B/C                          not-pres  normal
557   A/B/C/D                                            normal
558
559   That is checkout, delete A/B, copy a replacement A/B, delete copied
560   child A/B/C, add replacement A/B/C, add A/B/C/D.
561
562   Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
563   must extend the A/B deletion:
564
565              0         1         2         3         4
566              normal
567   A          normal
568   A/B        normal              normal
569   A/B/C      normal              not-pres  normal
570   A/B/C/D    normal              base-del            normal
571   A/B/C/D/E  normal              base-del
572
573   When adding a node if the parent has a higher working node then the
574   parent node is deleted (or replaced) and the delete must be extended
575   to cover new node.
576
577   In the example above A/B/C/D and A/B/C/D/E are the nodes that get
578   the extended delete, A/B/C is already deleted.
579
580   If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete
581   was recorded, otherwise to FALSE.
582 */
583static svn_error_t *
584db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
585                        const char *local_relpath,
586                        svn_node_kind_t kind,
587                        int op_depth,
588                        apr_pool_t *scratch_pool)
589{
590  svn_boolean_t have_row;
591  svn_sqlite__stmt_t *stmt;
592  int parent_op_depth;
593  const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
594
595  SVN_ERR_ASSERT(local_relpath[0]);
596
597  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
598                                    STMT_SELECT_LOWEST_WORKING_NODE));
599  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
600                            op_depth));
601  SVN_ERR(svn_sqlite__step(&have_row, stmt));
602  if (have_row)
603    parent_op_depth = svn_sqlite__column_int(stmt, 0);
604  SVN_ERR(svn_sqlite__reset(stmt));
605  if (have_row)
606    {
607      int existing_op_depth;
608
609      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
610                                op_depth));
611      SVN_ERR(svn_sqlite__step(&have_row, stmt));
612      if (have_row)
613        existing_op_depth = svn_sqlite__column_int(stmt, 0);
614      SVN_ERR(svn_sqlite__reset(stmt));
615      if (!have_row || parent_op_depth < existing_op_depth)
616        {
617          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
618                              STMT_INSTALL_WORKING_NODE_FOR_DELETE));
619          SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
620                                    local_relpath, parent_op_depth,
621                                    parent_relpath, kind_map, kind));
622          SVN_ERR(svn_sqlite__update(NULL, stmt));
623        }
624    }
625
626  return SVN_NO_ERROR;
627}
628
629
630/* This is the reverse of db_extend_parent_delete.
631
632   When removing a node if the parent has a higher working node then
633   the parent node and this node are both deleted or replaced and any
634   delete over this node must be removed.
635
636   This function (like most wcroot functions) assumes that its caller
637   only uses this function within an sqlite transaction if atomic
638   behavior is needed.
639 */
640static svn_error_t *
641db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
642                         const char *local_relpath,
643                         int op_depth,
644                         apr_pool_t *scratch_pool)
645{
646  svn_sqlite__stmt_t *stmt;
647  svn_boolean_t have_row;
648  int working_depth;
649  svn_wc__db_status_t presence;
650  const char *moved_to;
651
652  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
653                                    STMT_SELECT_LOWEST_WORKING_NODE));
654  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
655                            op_depth));
656  SVN_ERR(svn_sqlite__step(&have_row, stmt));
657
658  if (!have_row)
659    return svn_error_trace(svn_sqlite__reset(stmt));
660
661  working_depth = svn_sqlite__column_int(stmt, 0);
662  presence = svn_sqlite__column_token(stmt, 1, presence_map);
663  moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
664
665  SVN_ERR(svn_sqlite__reset(stmt));
666
667  if (moved_to)
668    {
669      /* Turn the move into a copy to keep the NODES table valid */
670      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
671                                        STMT_CLEAR_MOVED_HERE_RECURSIVE));
672      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
673                                moved_to, relpath_depth(moved_to)));
674      SVN_ERR(svn_sqlite__step_done(stmt));
675
676      /* This leaves just the moved_to information on the origin,
677         which we will remove in the next step */
678    }
679
680  if (presence == svn_wc__db_status_base_deleted)
681    {
682      /* Nothing left to shadow; remove the base-deleted node */
683      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
684    }
685  else if (moved_to)
686    {
687      /* Clear moved to information, as this node is no longer base-deleted */
688      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
689                                        STMT_CLEAR_MOVED_TO_RELPATH));
690      }
691  else
692    {
693      /* Nothing to update */
694      return SVN_NO_ERROR;
695    }
696
697  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
698                            working_depth));
699
700  return svn_error_trace(svn_sqlite__update(NULL, stmt));
701}
702
703
704
705/* Insert the base row represented by (insert_base_baton_t *) BATON. */
706static svn_error_t *
707insert_base_node(const insert_base_baton_t *pibb,
708                 svn_wc__db_wcroot_t *wcroot,
709                 const char *local_relpath,
710                 apr_pool_t *scratch_pool)
711{
712  apr_int64_t repos_id = pibb->repos_id;
713  svn_sqlite__stmt_t *stmt;
714  svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
715  apr_int64_t recorded_time;
716  svn_boolean_t present;
717
718  /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
719     bind the appropriate parent_relpath. */
720  const char *parent_relpath =
721    (*local_relpath == '\0') ? NULL
722    : svn_relpath_dirname(local_relpath, scratch_pool);
723
724  if (pibb->repos_id == INVALID_REPOS_ID)
725    SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
726                            wcroot->sdb, scratch_pool));
727
728  SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
729  SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
730
731  if (pibb->keep_recorded_info)
732    {
733      svn_boolean_t have_row;
734      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
735                                        STMT_SELECT_BASE_NODE));
736      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
737      SVN_ERR(svn_sqlite__step(&have_row, stmt));
738      if (have_row)
739        {
740          /* Preserve size and modification time if caller asked us to. */
741          recorded_size = get_recorded_size(stmt, 6);
742          recorded_time = svn_sqlite__column_int64(stmt, 12);
743        }
744      SVN_ERR(svn_sqlite__reset(stmt));
745    }
746
747  present = (pibb->status == svn_wc__db_status_normal
748             || pibb->status == svn_wc__db_status_incomplete);
749
750  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
751  SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
752                            "tstr"               /* 8 - 11 */
753                            "isnnnnns",          /* 12 - 19 */
754                            wcroot->wc_id,       /* 1 */
755                            local_relpath,       /* 2 */
756                            0,              /* op_depth is 0 for base */
757                            parent_relpath,      /* 4 */
758                            repos_id,
759                            pibb->repos_relpath,
760                            pibb->revision,
761                            presence_map, pibb->status, /* 8 */
762                            (pibb->kind == svn_node_dir && present) /* 9 */
763                              ? svn_token__to_word(depth_map, pibb->depth)
764                              : NULL,
765                            kind_map, pibb->kind, /* 10 */
766                            pibb->changed_rev,    /* 11 */
767                            pibb->changed_date,   /* 12 */
768                            pibb->changed_author, /* 13 */
769                            (pibb->kind == svn_node_symlink && present) ?
770                                pibb->target : NULL)); /* 19 */
771  if (pibb->kind == svn_node_file && present)
772    {
773      if (!pibb->checksum
774          && pibb->status != svn_wc__db_status_not_present
775          && pibb->status != svn_wc__db_status_excluded
776          && pibb->status != svn_wc__db_status_server_excluded)
777        return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
778                                 _("The file '%s' has no checksum."),
779                                 path_for_error_message(wcroot, local_relpath,
780                                                        scratch_pool));
781
782      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
783                                        scratch_pool));
784
785      if (recorded_size != SVN_INVALID_FILESIZE)
786        {
787          SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
788          SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
789        }
790    }
791
792  /* Set properties.  Must be null if presence not normal or incomplete. */
793  assert(pibb->status == svn_wc__db_status_normal
794         || pibb->status == svn_wc__db_status_incomplete
795         || pibb->props == NULL);
796  if (present)
797    {
798      SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
799                                          scratch_pool));
800
801      SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
802                                      scratch_pool));
803    }
804
805  if (pibb->dav_cache)
806    SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
807                                        scratch_pool));
808
809  if (pibb->file_external)
810    SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
811
812  SVN_ERR(svn_sqlite__insert(NULL, stmt));
813
814  if (pibb->update_actual_props)
815    {
816      /* Cast away const, to allow calling property helpers */
817      apr_hash_t *base_props = (apr_hash_t *)pibb->props;
818      apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
819
820      if (base_props != NULL
821          && new_actual_props != NULL
822          && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
823        {
824          apr_array_header_t *diffs;
825
826          SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
827                                 scratch_pool));
828
829          if (diffs->nelts == 0)
830            new_actual_props = NULL;
831        }
832
833      SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
834                               scratch_pool));
835    }
836
837  if (pibb->kind == svn_node_dir && pibb->children)
838    SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
839                                       local_relpath,
840                                       repos_id,
841                                       pibb->repos_relpath,
842                                       pibb->revision,
843                                       pibb->children,
844                                       0 /* BASE */,
845                                       scratch_pool));
846
847  /* When this is not the root node, check shadowing behavior */
848  if (*local_relpath)
849    {
850      if (parent_relpath
851          && ((pibb->status == svn_wc__db_status_normal)
852              || (pibb->status == svn_wc__db_status_incomplete))
853          && ! pibb->file_external)
854        {
855          SVN_ERR(db_extend_parent_delete(wcroot, local_relpath,
856                                          pibb->kind, 0,
857                                          scratch_pool));
858        }
859      else if (pibb->status == svn_wc__db_status_not_present
860               || pibb->status == svn_wc__db_status_server_excluded
861               || pibb->status == svn_wc__db_status_excluded)
862        {
863          SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0,
864                                           scratch_pool));
865        }
866    }
867
868  if (pibb->delete_working)
869    {
870      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
871                                    STMT_DELETE_WORKING_NODE));
872      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
873      SVN_ERR(svn_sqlite__step_done(stmt));
874    }
875  if (pibb->insert_base_deleted)
876    {
877      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
878                                        STMT_INSERT_DELETE_FROM_BASE));
879      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
880                                wcroot->wc_id, local_relpath,
881                                relpath_depth(local_relpath)));
882      SVN_ERR(svn_sqlite__step_done(stmt));
883    }
884
885  SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
886  if (pibb->conflict)
887    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
888                                              pibb->conflict, scratch_pool));
889
890  return SVN_NO_ERROR;
891}
892
893
894/* Initialize the baton with appropriate "blank" values. This allows the
895   insertion function to leave certain columns null.  */
896static void
897blank_iwb(insert_working_baton_t *piwb)
898{
899  memset(piwb, 0, sizeof(*piwb));
900  piwb->changed_rev = SVN_INVALID_REVNUM;
901  piwb->depth = svn_depth_infinity;
902
903  /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
904     value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
905}
906
907
908/* Insert a row in NODES for each (const char *) child name in CHILDREN,
909   whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH.  Set each
910   child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
911   repos_path by appending the child name to REPOS_PATH, and revision to
912   REVISION (which should match the parent's revision).
913
914   If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
915static svn_error_t *
916insert_incomplete_children(svn_sqlite__db_t *sdb,
917                           apr_int64_t wc_id,
918                           const char *local_relpath,
919                           apr_int64_t repos_id,
920                           const char *repos_path,
921                           svn_revnum_t revision,
922                           const apr_array_header_t *children,
923                           int op_depth,
924                           apr_pool_t *scratch_pool)
925{
926  svn_sqlite__stmt_t *stmt;
927  int i;
928  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
929  apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
930
931  SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
932  SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
933                 == (repos_path != NULL));
934
935  /* If we're inserting WORKING nodes, we might be replacing existing
936   * nodes which were moved-away. We need to retain the moved-to relpath of
937   * such nodes in order not to lose move information during replace. */
938  if (op_depth > 0)
939    {
940      for (i = children->nelts; i--; )
941        {
942          const char *name = APR_ARRAY_IDX(children, i, const char *);
943          svn_boolean_t have_row;
944
945          svn_pool_clear(iterpool);
946
947          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
948                                            STMT_SELECT_WORKING_NODE));
949          SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
950                                    svn_relpath_join(local_relpath, name,
951                                                     iterpool)));
952          SVN_ERR(svn_sqlite__step(&have_row, stmt));
953          if (have_row && !svn_sqlite__column_is_null(stmt, 14))
954            svn_hash_sets(moved_to_relpaths, name,
955                          svn_sqlite__column_text(stmt, 14, scratch_pool));
956
957          SVN_ERR(svn_sqlite__reset(stmt));
958        }
959    }
960
961  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
962
963  for (i = children->nelts; i--; )
964    {
965      const char *name = APR_ARRAY_IDX(children, i, const char *);
966
967      svn_pool_clear(iterpool);
968
969      SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
970                                wc_id,
971                                svn_relpath_join(local_relpath, name,
972                                                 iterpool),
973                                op_depth,
974                                local_relpath,
975                                revision,
976                                "incomplete", /* 8, presence */
977                                "unknown",    /* 10, kind */
978                                /* 21, moved_to */
979                                svn_hash_gets(moved_to_relpaths, name)));
980      if (repos_id != INVALID_REPOS_ID)
981        {
982          SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
983          SVN_ERR(svn_sqlite__bind_text(stmt, 6,
984                                        svn_relpath_join(repos_path, name,
985                                                         iterpool)));
986        }
987
988      SVN_ERR(svn_sqlite__insert(NULL, stmt));
989    }
990
991  svn_pool_destroy(iterpool);
992
993  return SVN_NO_ERROR;
994}
995
996
997/* Insert the working row represented by (insert_working_baton_t *) BATON. */
998static svn_error_t *
999insert_working_node(const insert_working_baton_t *piwb,
1000                    svn_wc__db_wcroot_t *wcroot,
1001                    const char *local_relpath,
1002                    apr_pool_t *scratch_pool)
1003{
1004  const char *parent_relpath;
1005  const char *moved_to_relpath = NULL;
1006  svn_sqlite__stmt_t *stmt;
1007  svn_boolean_t have_row;
1008  svn_boolean_t present;
1009
1010  SVN_ERR_ASSERT(piwb->op_depth > 0);
1011
1012  /* We cannot insert a WORKING_NODE row at the wcroot.  */
1013  SVN_ERR_ASSERT(*local_relpath != '\0');
1014  parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1015
1016  /* Preserve existing moved-to information for this relpath,
1017   * which might exist in case we're replacing an existing base-deleted
1018   * node. */
1019  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1020  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1021                            piwb->op_depth));
1022  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1023  if (have_row)
1024    moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1025  SVN_ERR(svn_sqlite__reset(stmt));
1026
1027  present = (piwb->presence == svn_wc__db_status_normal
1028             || piwb->presence == svn_wc__db_status_incomplete);
1029
1030  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1031  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1032                "nnnn" /* properties translated_size last_mod_time dav_cache */
1033                "sns", /* symlink_target, file_external, moved_to */
1034                wcroot->wc_id, local_relpath,
1035                piwb->op_depth,
1036                parent_relpath,
1037                presence_map, piwb->presence,
1038                (piwb->kind == svn_node_dir && present)
1039                            ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1040                kind_map, piwb->kind,
1041                piwb->changed_rev,
1042                piwb->changed_date,
1043                piwb->changed_author,
1044                /* Note: incomplete nodes may have a NULL target.  */
1045                (piwb->kind == svn_node_symlink && present)
1046                            ? piwb->target : NULL,
1047                moved_to_relpath));
1048
1049  if (piwb->moved_here)
1050    {
1051      SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1052    }
1053
1054  if (piwb->kind == svn_node_file && present)
1055    {
1056      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1057                                        scratch_pool));
1058    }
1059
1060  if (piwb->original_repos_relpath != NULL)
1061    {
1062      SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1063      SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1064      SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1065    }
1066
1067  /* Set properties.  Must be null if presence not normal or incomplete. */
1068  assert(piwb->presence == svn_wc__db_status_normal
1069         || piwb->presence == svn_wc__db_status_incomplete
1070         || piwb->props == NULL);
1071  if (present && piwb->original_repos_relpath)
1072    SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1073
1074  SVN_ERR(svn_sqlite__insert(NULL, stmt));
1075
1076  /* Insert incomplete children, if specified.
1077     The children are part of the same op and so have the same op_depth.
1078     (The only time we'd want a different depth is during a recursive
1079     simple add, but we never insert children here during a simple add.) */
1080  if (piwb->kind == svn_node_dir && piwb->children)
1081    SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1082                                       local_relpath,
1083                                       INVALID_REPOS_ID /* inherit repos_id */,
1084                                       NULL /* inherit repos_path */,
1085                                       piwb->original_revnum,
1086                                       piwb->children,
1087                                       piwb->op_depth,
1088                                       scratch_pool));
1089
1090  if (piwb->update_actual_props)
1091    {
1092      /* Cast away const, to allow calling property helpers */
1093      apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1094      apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1095
1096      if (base_props != NULL
1097          && new_actual_props != NULL
1098          && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1099        {
1100          apr_array_header_t *diffs;
1101
1102          SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1103                                 scratch_pool));
1104
1105          if (diffs->nelts == 0)
1106            new_actual_props = NULL;
1107        }
1108
1109      SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
1110                               scratch_pool));
1111    }
1112
1113  if (piwb->kind == svn_node_dir)
1114    {
1115      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116                                        STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1117      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1118      SVN_ERR(svn_sqlite__step_done(stmt));
1119
1120      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1121                                        STMT_DELETE_ACTUAL_EMPTY));
1122      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1123      SVN_ERR(svn_sqlite__step_done(stmt));
1124    }
1125
1126  if (piwb->not_present_op_depth > 0
1127      && piwb->not_present_op_depth < piwb->op_depth)
1128    {
1129      /* And also insert a not-present node to tell the commit processing that
1130         a child of the parent node was not copied. */
1131      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1132                                        STMT_INSERT_NODE));
1133
1134      SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1135                                wcroot->wc_id, local_relpath,
1136                                piwb->not_present_op_depth, parent_relpath,
1137                                piwb->original_repos_id,
1138                                piwb->original_repos_relpath,
1139                                piwb->original_revnum,
1140                                presence_map, svn_wc__db_status_not_present,
1141                                /* NULL */
1142                                kind_map, piwb->kind));
1143
1144      SVN_ERR(svn_sqlite__step_done(stmt));
1145    }
1146
1147  SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1148  if (piwb->conflict)
1149    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1150                                              piwb->conflict, scratch_pool));
1151
1152  return SVN_NO_ERROR;
1153}
1154
1155
1156/* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1157   of any status, in all op-depths in the NODES table. */
1158static svn_error_t *
1159gather_children(const apr_array_header_t **children,
1160                svn_wc__db_wcroot_t *wcroot,
1161                const char *parent_relpath,
1162                int stmt_idx,
1163                int op_depth,
1164                apr_pool_t *result_pool,
1165                apr_pool_t *scratch_pool)
1166{
1167  apr_array_header_t *result;
1168  svn_sqlite__stmt_t *stmt;
1169  svn_boolean_t have_row;
1170
1171  result = apr_array_make(result_pool, 16, sizeof(const char*));
1172
1173  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
1174  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
1175  if (op_depth >= 0)
1176    SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth));
1177
1178  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1179  while (have_row)
1180    {
1181      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1182      const char *name = svn_relpath_basename(child_relpath, result_pool);
1183
1184      APR_ARRAY_PUSH(result, const char *) = name;
1185
1186      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1187    }
1188
1189  SVN_ERR(svn_sqlite__reset(stmt));
1190  *children = result;
1191  return SVN_NO_ERROR;
1192}
1193
1194/* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1195 * Else, return FALSE. */
1196static svn_boolean_t
1197is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1198{
1199  const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1200                                                       child_abspath);
1201
1202  /* To be an immediate child local_relpath should have one (not empty)
1203     component */
1204  return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1205}
1206
1207
1208/* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1209static void
1210remove_from_access_cache(apr_hash_t *access_cache,
1211                         const char *local_abspath)
1212{
1213  svn_wc_adm_access_t *adm_access;
1214
1215  adm_access = svn_hash_gets(access_cache, local_abspath);
1216  if (adm_access)
1217    svn_wc__adm_access_set_entries(adm_access, NULL);
1218}
1219
1220
1221/* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1222 * the specified DEPTH, from the access baton cache in WCROOT.
1223 * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1224 *
1225 * This function must be called when the access baton cache goes stale,
1226 * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1227 *
1228 * Use SCRATCH_POOL for temporary allocations. */
1229static svn_error_t *
1230flush_entries(svn_wc__db_wcroot_t *wcroot,
1231              const char *local_abspath,
1232              svn_depth_t depth,
1233              apr_pool_t *scratch_pool)
1234{
1235  const char *parent_abspath;
1236
1237  if (apr_hash_count(wcroot->access_cache) == 0)
1238    return SVN_NO_ERROR;
1239
1240  remove_from_access_cache(wcroot->access_cache, local_abspath);
1241
1242  if (depth > svn_depth_empty)
1243    {
1244      apr_hash_index_t *hi;
1245
1246      /* Flush access batons of children within the specified depth. */
1247      for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1248           hi;
1249           hi = apr_hash_next(hi))
1250        {
1251          const char *item_abspath = apr_hash_this_key(hi);
1252
1253          if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1254              is_immediate_child_path(local_abspath, item_abspath))
1255            {
1256              remove_from_access_cache(wcroot->access_cache, item_abspath);
1257            }
1258          else if (depth == svn_depth_infinity &&
1259                   svn_dirent_is_ancestor(local_abspath, item_abspath))
1260            {
1261              remove_from_access_cache(wcroot->access_cache, item_abspath);
1262            }
1263        }
1264    }
1265
1266  /* We're going to be overly aggressive here and just flush the parent
1267     without doing much checking.  This may hurt performance for
1268     legacy API consumers, but that's not our problem. :) */
1269  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1270  remove_from_access_cache(wcroot->access_cache, parent_abspath);
1271
1272  return SVN_NO_ERROR;
1273}
1274
1275
1276/* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1277   not perform its work within a transaction, assuming the caller will
1278   manage that.  */
1279static svn_error_t *
1280add_single_work_item(svn_sqlite__db_t *sdb,
1281                     const svn_skel_t *work_item,
1282                     apr_pool_t *scratch_pool)
1283{
1284  svn_stringbuf_t *serialized;
1285  svn_sqlite__stmt_t *stmt;
1286
1287  serialized = svn_skel__unparse(work_item, scratch_pool);
1288  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1289  SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1290  return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1291}
1292
1293
1294/* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1295   SKEL is usually passed to the various wc_db operation functions. It may
1296   be NULL, indicating no additional work items are needed, it may be a
1297   single work item, or it may be a list of work items.  */
1298static svn_error_t *
1299add_work_items(svn_sqlite__db_t *sdb,
1300               const svn_skel_t *skel,
1301               apr_pool_t *scratch_pool)
1302{
1303  apr_pool_t *iterpool;
1304
1305  /* Maybe there are no work items to insert.  */
1306  if (skel == NULL)
1307    return SVN_NO_ERROR;
1308
1309  /* Should have a list.  */
1310  SVN_ERR_ASSERT(!skel->is_atom);
1311
1312  /* Is the list a single work item? Or a list of work items?  */
1313  if (SVN_WC__SINGLE_WORK_ITEM(skel))
1314    return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1315
1316  /* SKEL is a list-of-lists, aka list of work items.  */
1317
1318  iterpool = svn_pool_create(scratch_pool);
1319  for (skel = skel->children; skel; skel = skel->next)
1320    {
1321      svn_pool_clear(iterpool);
1322
1323      SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1324    }
1325  svn_pool_destroy(iterpool);
1326
1327  return SVN_NO_ERROR;
1328}
1329
1330
1331/* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH.  */
1332static svn_error_t *
1333does_node_exist(svn_boolean_t *exists,
1334                const svn_wc__db_wcroot_t *wcroot,
1335                const char *local_relpath)
1336{
1337  svn_sqlite__stmt_t *stmt;
1338
1339  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1340  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1341  SVN_ERR(svn_sqlite__step(exists, stmt));
1342
1343  return svn_error_trace(svn_sqlite__reset(stmt));
1344}
1345
1346svn_error_t *
1347svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1348                                     apr_pool_t *scratch_pool)
1349{
1350  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1351
1352  return SVN_NO_ERROR;
1353}
1354
1355/* Helper for create_db(). Initializes our wc.db schema.
1356 */
1357static svn_error_t *
1358init_db(/* output values */
1359        apr_int64_t *repos_id,
1360        apr_int64_t *wc_id,
1361        /* input values */
1362        svn_sqlite__db_t *db,
1363        const char *repos_root_url,
1364        const char *repos_uuid,
1365        const char *root_node_repos_relpath,
1366        svn_revnum_t root_node_revision,
1367        svn_depth_t root_node_depth,
1368        apr_pool_t *scratch_pool)
1369{
1370  svn_sqlite__stmt_t *stmt;
1371
1372  /* Create the database's schema.  */
1373  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1374  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1375  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1376  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1377
1378  SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1379
1380  /* Insert the repository. */
1381  SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1382                          db, scratch_pool));
1383
1384  /* Insert the wcroot. */
1385  /* ### Right now, this just assumes wc metadata is being stored locally. */
1386  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1387  SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1388
1389  if (root_node_repos_relpath)
1390    {
1391      svn_wc__db_status_t status = svn_wc__db_status_normal;
1392
1393      if (root_node_revision > 0)
1394        status = svn_wc__db_status_incomplete; /* Will be filled by update */
1395
1396      SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1397      SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1398                                *wc_id,              /* 1 */
1399                                "",                  /* 2 */
1400                                0,                   /* op_depth is 0 for base */
1401                                SVN_VA_NULL,         /* 4 */
1402                                *repos_id,
1403                                root_node_repos_relpath,
1404                                root_node_revision,
1405                                presence_map, status, /* 8 */
1406                                svn_token__to_word(depth_map,
1407                                                   root_node_depth),
1408                                kind_map, svn_node_dir /* 10 */));
1409
1410      SVN_ERR(svn_sqlite__insert(NULL, stmt));
1411    }
1412
1413  return SVN_NO_ERROR;
1414}
1415
1416/* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1417   records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1418   REPOSITORY and for WC_ID into WCROOT.  Return the DB connection
1419   in *SDB.
1420
1421   If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1422   the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1423   revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1424   */
1425static svn_error_t *
1426create_db(svn_sqlite__db_t **sdb,
1427          apr_int64_t *repos_id,
1428          apr_int64_t *wc_id,
1429          const char *dir_abspath,
1430          const char *repos_root_url,
1431          const char *repos_uuid,
1432          const char *sdb_fname,
1433          const char *root_node_repos_relpath,
1434          svn_revnum_t root_node_revision,
1435          svn_depth_t root_node_depth,
1436          svn_boolean_t exclusive,
1437          apr_int32_t timeout,
1438          apr_pool_t *result_pool,
1439          apr_pool_t *scratch_pool)
1440{
1441  SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1442                                  svn_sqlite__mode_rwcreate, exclusive,
1443                                  timeout,
1444                                  NULL /* my_statements */,
1445                                  result_pool, scratch_pool));
1446
1447  SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1448                                *sdb, repos_root_url, repos_uuid,
1449                                root_node_repos_relpath, root_node_revision,
1450                                root_node_depth, scratch_pool),
1451                        *sdb);
1452
1453  return SVN_NO_ERROR;
1454}
1455
1456
1457svn_error_t *
1458svn_wc__db_init(svn_wc__db_t *db,
1459                const char *local_abspath,
1460                const char *repos_relpath,
1461                const char *repos_root_url,
1462                const char *repos_uuid,
1463                svn_revnum_t initial_rev,
1464                svn_depth_t depth,
1465                apr_pool_t *scratch_pool)
1466{
1467  svn_sqlite__db_t *sdb;
1468  apr_int64_t repos_id;
1469  apr_int64_t wc_id;
1470  svn_wc__db_wcroot_t *wcroot;
1471  svn_boolean_t sqlite_exclusive = FALSE;
1472  apr_int32_t sqlite_timeout = 0; /* default timeout */
1473  apr_hash_index_t *hi;
1474
1475  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1476  SVN_ERR_ASSERT(repos_relpath != NULL);
1477  SVN_ERR_ASSERT(depth == svn_depth_empty
1478                 || depth == svn_depth_files
1479                 || depth == svn_depth_immediates
1480                 || depth == svn_depth_infinity);
1481
1482  /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
1483
1484  SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
1485                              SVN_CONFIG_SECTION_WORKING_COPY,
1486                              SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1487                              FALSE));
1488
1489  /* Create the SDB and insert the basic rows.  */
1490  SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1491                    repos_uuid, SDB_FILE,
1492                    repos_relpath, initial_rev, depth, sqlite_exclusive,
1493                    sqlite_timeout,
1494                    db->state_pool, scratch_pool));
1495
1496  /* Create the WCROOT for this directory.  */
1497  SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1498                        apr_pstrdup(db->state_pool, local_abspath),
1499                        sdb, wc_id, FORMAT_FROM_SDB,
1500                        FALSE /* auto-upgrade */,
1501                        db->state_pool, scratch_pool));
1502
1503  /* Any previously cached children may now have a new WCROOT, most likely that
1504     of the new WCROOT, but there might be descendant directories that are their
1505     own working copy, in which case setting WCROOT to our new WCROOT might
1506     actually break things for those.
1507
1508     Clearing is the safest thing we can do in this case, as a test would lead
1509     to unnecessary probing, while the standard code probes later anyway. So we
1510     only lose a bit of memory
1511
1512     ### Perhaps we could check wcroot->abspath to detect which case we have
1513         where, but currently it is already very hard to trigger this from
1514         the short living 'svn' client. (GUI clients like TortoiseSVN are far
1515         more likely to get in these cases)
1516     */
1517  for (hi = apr_hash_first(scratch_pool, db->dir_data);
1518       hi;
1519       hi = apr_hash_next(hi))
1520    {
1521      const char *abspath = apr_hash_this_key(hi);
1522      if (svn_dirent_is_ancestor(wcroot->abspath, abspath))
1523        svn_hash_sets(db->dir_data, abspath, NULL);
1524    }
1525
1526  /* The WCROOT is complete. Stash it into DB.  */
1527  svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1528
1529  return SVN_NO_ERROR;
1530}
1531
1532
1533svn_error_t *
1534svn_wc__db_to_relpath(const char **local_relpath,
1535                      svn_wc__db_t *db,
1536                      const char *wri_abspath,
1537                      const char *local_abspath,
1538                      apr_pool_t *result_pool,
1539                      apr_pool_t *scratch_pool)
1540{
1541  svn_wc__db_wcroot_t *wcroot;
1542  const char *relpath;
1543
1544  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1545
1546  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1547                              wri_abspath, result_pool, scratch_pool));
1548
1549  /* This function is indirectly called from the upgrade code, so we
1550     can't verify the wcroot here. Just check that it is not NULL */
1551  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1552
1553  if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1554    {
1555      *local_relpath = apr_pstrdup(result_pool,
1556                                   svn_dirent_skip_ancestor(wcroot->abspath,
1557                                                            local_abspath));
1558    }
1559  else
1560    /* Probably moving from $TMP. Should we allow this? */
1561    *local_relpath = apr_pstrdup(result_pool, local_abspath);
1562
1563  return SVN_NO_ERROR;
1564}
1565
1566
1567svn_error_t *
1568svn_wc__db_from_relpath(const char **local_abspath,
1569                        svn_wc__db_t *db,
1570                        const char *wri_abspath,
1571                        const char *local_relpath,
1572                        apr_pool_t *result_pool,
1573                        apr_pool_t *scratch_pool)
1574{
1575  svn_wc__db_wcroot_t *wcroot;
1576  const char *unused_relpath;
1577#if 0
1578  SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1579#endif
1580
1581  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1582                              wri_abspath, scratch_pool, scratch_pool));
1583
1584  /* This function is indirectly called from the upgrade code, so we
1585     can't verify the wcroot here. Just check that it is not NULL */
1586  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1587
1588
1589  *local_abspath = svn_dirent_join(wcroot->abspath,
1590                                   local_relpath,
1591                                   result_pool);
1592  return SVN_NO_ERROR;
1593}
1594
1595
1596svn_error_t *
1597svn_wc__db_get_wcroot(const char **wcroot_abspath,
1598                      svn_wc__db_t *db,
1599                      const char *wri_abspath,
1600                      apr_pool_t *result_pool,
1601                      apr_pool_t *scratch_pool)
1602{
1603  svn_wc__db_wcroot_t *wcroot;
1604  const char *unused_relpath;
1605
1606  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1607                              wri_abspath, scratch_pool, scratch_pool));
1608
1609  /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1610     where call upgrade */
1611  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1612
1613  *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1614
1615  return SVN_NO_ERROR;
1616}
1617
1618
1619svn_error_t *
1620svn_wc__db_base_add_directory(svn_wc__db_t *db,
1621                              const char *local_abspath,
1622                              const char *wri_abspath,
1623                              const char *repos_relpath,
1624                              const char *repos_root_url,
1625                              const char *repos_uuid,
1626                              svn_revnum_t revision,
1627                              const apr_hash_t *props,
1628                              svn_revnum_t changed_rev,
1629                              apr_time_t changed_date,
1630                              const char *changed_author,
1631                              const apr_array_header_t *children,
1632                              svn_depth_t depth,
1633                              apr_hash_t *dav_cache,
1634                              svn_boolean_t update_actual_props,
1635                              apr_hash_t *new_actual_props,
1636                              apr_array_header_t *new_iprops,
1637                              const svn_skel_t *conflict,
1638                              const svn_skel_t *work_items,
1639                              apr_pool_t *scratch_pool)
1640{
1641  svn_wc__db_wcroot_t *wcroot;
1642  const char *local_relpath;
1643  insert_base_baton_t ibb;
1644
1645  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1646  SVN_ERR_ASSERT(repos_relpath != NULL);
1647  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1648  SVN_ERR_ASSERT(repos_uuid != NULL);
1649  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1650  SVN_ERR_ASSERT(props != NULL);
1651  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1652#if 0
1653  SVN_ERR_ASSERT(children != NULL);
1654#endif
1655
1656  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1657                              wri_abspath, scratch_pool, scratch_pool));
1658  VERIFY_USABLE_WCROOT(wcroot);
1659  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1660
1661  blank_ibb(&ibb);
1662
1663  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1664  ibb.repos_root_url = repos_root_url;
1665  ibb.repos_uuid = repos_uuid;
1666
1667  ibb.status = svn_wc__db_status_normal;
1668  ibb.kind = svn_node_dir;
1669  ibb.repos_relpath = repos_relpath;
1670  ibb.revision = revision;
1671
1672  ibb.iprops = new_iprops;
1673  ibb.props = props;
1674  ibb.changed_rev = changed_rev;
1675  ibb.changed_date = changed_date;
1676  ibb.changed_author = changed_author;
1677
1678  ibb.children = children;
1679  ibb.depth = depth;
1680
1681  ibb.dav_cache = dav_cache;
1682  ibb.conflict = conflict;
1683  ibb.work_items = work_items;
1684
1685  if (update_actual_props)
1686    {
1687      ibb.update_actual_props = TRUE;
1688      ibb.new_actual_props = new_actual_props;
1689    }
1690
1691  /* Insert the directory and all its children transactionally.
1692
1693     Note: old children can stick around, even if they are no longer present
1694     in this directory's revision.  */
1695  SVN_WC__DB_WITH_TXN(
1696            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1697            wcroot);
1698
1699  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1700  return SVN_NO_ERROR;
1701}
1702
1703svn_error_t *
1704svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1705                                         const char *local_abspath,
1706                                         const char *repos_relpath,
1707                                         const char *repos_root_url,
1708                                         const char *repos_uuid,
1709                                         svn_revnum_t revision,
1710                                         svn_depth_t depth,
1711                                         svn_boolean_t insert_base_deleted,
1712                                         svn_boolean_t delete_working,
1713                                         svn_skel_t *conflict,
1714                                         svn_skel_t *work_items,
1715                                         apr_pool_t *scratch_pool)
1716{
1717  svn_wc__db_wcroot_t *wcroot;
1718  const char *local_relpath;
1719  struct insert_base_baton_t ibb;
1720
1721  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1722  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1723  SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1724
1725  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1726                                                db, local_abspath,
1727                                                scratch_pool, scratch_pool));
1728
1729  VERIFY_USABLE_WCROOT(wcroot);
1730
1731  blank_ibb(&ibb);
1732
1733  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1734  ibb.repos_root_url = repos_root_url;
1735  ibb.repos_uuid = repos_uuid;
1736
1737  ibb.status = svn_wc__db_status_incomplete;
1738  ibb.kind = svn_node_dir;
1739  ibb.repos_relpath = repos_relpath;
1740  ibb.revision = revision;
1741  ibb.depth = depth;
1742  ibb.insert_base_deleted = insert_base_deleted;
1743  ibb.delete_working = delete_working;
1744
1745  ibb.conflict = conflict;
1746  ibb.work_items = work_items;
1747
1748  SVN_WC__DB_WITH_TXN(
1749            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1750            wcroot);
1751
1752  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1753
1754  return SVN_NO_ERROR;
1755}
1756
1757
1758svn_error_t *
1759svn_wc__db_base_add_file(svn_wc__db_t *db,
1760                         const char *local_abspath,
1761                         const char *wri_abspath,
1762                         const char *repos_relpath,
1763                         const char *repos_root_url,
1764                         const char *repos_uuid,
1765                         svn_revnum_t revision,
1766                         const apr_hash_t *props,
1767                         svn_revnum_t changed_rev,
1768                         apr_time_t changed_date,
1769                         const char *changed_author,
1770                         const svn_checksum_t *checksum,
1771                         apr_hash_t *dav_cache,
1772                         svn_boolean_t delete_working,
1773                         svn_boolean_t update_actual_props,
1774                         apr_hash_t *new_actual_props,
1775                         apr_array_header_t *new_iprops,
1776                         svn_boolean_t keep_recorded_info,
1777                         svn_boolean_t insert_base_deleted,
1778                         const svn_skel_t *conflict,
1779                         const svn_skel_t *work_items,
1780                         apr_pool_t *scratch_pool)
1781{
1782  svn_wc__db_wcroot_t *wcroot;
1783  const char *local_relpath;
1784  insert_base_baton_t ibb;
1785
1786  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1787  SVN_ERR_ASSERT(repos_relpath != NULL);
1788  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1789  SVN_ERR_ASSERT(repos_uuid != NULL);
1790  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1791  SVN_ERR_ASSERT(props != NULL);
1792  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1793  SVN_ERR_ASSERT(checksum != NULL);
1794
1795  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1796                              wri_abspath, scratch_pool, scratch_pool));
1797  VERIFY_USABLE_WCROOT(wcroot);
1798  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1799
1800  blank_ibb(&ibb);
1801
1802  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1803  ibb.repos_root_url = repos_root_url;
1804  ibb.repos_uuid = repos_uuid;
1805
1806  ibb.status = svn_wc__db_status_normal;
1807  ibb.kind = svn_node_file;
1808  ibb.repos_relpath = repos_relpath;
1809  ibb.revision = revision;
1810
1811  ibb.props = props;
1812  ibb.changed_rev = changed_rev;
1813  ibb.changed_date = changed_date;
1814  ibb.changed_author = changed_author;
1815
1816  ibb.checksum = checksum;
1817
1818  ibb.dav_cache = dav_cache;
1819  ibb.iprops = new_iprops;
1820
1821  if (update_actual_props)
1822    {
1823      ibb.update_actual_props = TRUE;
1824      ibb.new_actual_props = new_actual_props;
1825    }
1826
1827  ibb.keep_recorded_info = keep_recorded_info;
1828  ibb.insert_base_deleted = insert_base_deleted;
1829  ibb.delete_working = delete_working;
1830
1831  ibb.conflict = conflict;
1832  ibb.work_items = work_items;
1833
1834  SVN_WC__DB_WITH_TXN(
1835            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1836            wcroot);
1837
1838  /* If this used to be a directory we should remove children so pass
1839   * depth infinity. */
1840  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1841                        scratch_pool));
1842  return SVN_NO_ERROR;
1843}
1844
1845
1846svn_error_t *
1847svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1848                            const char *local_abspath,
1849                            const char *wri_abspath,
1850                            const char *repos_relpath,
1851                            const char *repos_root_url,
1852                            const char *repos_uuid,
1853                            svn_revnum_t revision,
1854                            const apr_hash_t *props,
1855                            svn_revnum_t changed_rev,
1856                            apr_time_t changed_date,
1857                            const char *changed_author,
1858                            const char *target,
1859                            apr_hash_t *dav_cache,
1860                            svn_boolean_t delete_working,
1861                            svn_boolean_t update_actual_props,
1862                            apr_hash_t *new_actual_props,
1863                            apr_array_header_t *new_iprops,
1864                            svn_boolean_t keep_recorded_info,
1865                            svn_boolean_t insert_base_deleted,
1866                            const svn_skel_t *conflict,
1867                            const svn_skel_t *work_items,
1868                            apr_pool_t *scratch_pool)
1869{
1870  svn_wc__db_wcroot_t *wcroot;
1871  const char *local_relpath;
1872  insert_base_baton_t ibb;
1873
1874  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1875  SVN_ERR_ASSERT(repos_relpath != NULL);
1876  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1877  SVN_ERR_ASSERT(repos_uuid != NULL);
1878  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1879  SVN_ERR_ASSERT(props != NULL);
1880  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1881  SVN_ERR_ASSERT(target != NULL);
1882
1883  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1884                              wri_abspath, scratch_pool, scratch_pool));
1885  VERIFY_USABLE_WCROOT(wcroot);
1886  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1887  blank_ibb(&ibb);
1888
1889  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1890  ibb.repos_root_url = repos_root_url;
1891  ibb.repos_uuid = repos_uuid;
1892
1893  ibb.status = svn_wc__db_status_normal;
1894  ibb.kind = svn_node_symlink;
1895  ibb.repos_relpath = repos_relpath;
1896  ibb.revision = revision;
1897
1898  ibb.props = props;
1899  ibb.changed_rev = changed_rev;
1900  ibb.changed_date = changed_date;
1901  ibb.changed_author = changed_author;
1902
1903  ibb.target = target;
1904
1905  ibb.dav_cache = dav_cache;
1906  ibb.iprops = new_iprops;
1907
1908  if (update_actual_props)
1909    {
1910      ibb.update_actual_props = TRUE;
1911      ibb.new_actual_props = new_actual_props;
1912    }
1913
1914  ibb.keep_recorded_info = keep_recorded_info;
1915  ibb.insert_base_deleted = insert_base_deleted;
1916  ibb.delete_working = delete_working;
1917
1918  ibb.conflict = conflict;
1919  ibb.work_items = work_items;
1920
1921  SVN_WC__DB_WITH_TXN(
1922            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1923            wcroot);
1924
1925  /* If this used to be a directory we should remove children so pass
1926   * depth infinity. */
1927  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1928                        scratch_pool));
1929  return SVN_NO_ERROR;
1930}
1931
1932
1933static svn_error_t *
1934add_excluded_or_not_present_node(svn_wc__db_t *db,
1935                                 const char *local_abspath,
1936                                 const char *repos_relpath,
1937                                 const char *repos_root_url,
1938                                 const char *repos_uuid,
1939                                 svn_revnum_t revision,
1940                                 svn_node_kind_t kind,
1941                                 svn_wc__db_status_t status,
1942                                 const svn_skel_t *conflict,
1943                                 const svn_skel_t *work_items,
1944                                 apr_pool_t *scratch_pool)
1945{
1946  svn_wc__db_wcroot_t *wcroot;
1947  const char *local_relpath;
1948  insert_base_baton_t ibb;
1949  const char *dir_abspath, *name;
1950
1951  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1952  SVN_ERR_ASSERT(repos_relpath != NULL);
1953  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1954  SVN_ERR_ASSERT(repos_uuid != NULL);
1955  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1956  SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1957                 || status == svn_wc__db_status_excluded
1958                 || status == svn_wc__db_status_not_present);
1959
1960  /* These absent presence nodes are only useful below a parent node that is
1961     present. To avoid problems with working copies obstructing the child
1962     we calculate the wcroot and local_relpath of the parent and then add
1963     our own relpath. */
1964
1965  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1966
1967  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1968                              dir_abspath, scratch_pool, scratch_pool));
1969  VERIFY_USABLE_WCROOT(wcroot);
1970
1971  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1972
1973  blank_ibb(&ibb);
1974
1975  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1976  ibb.repos_root_url = repos_root_url;
1977  ibb.repos_uuid = repos_uuid;
1978
1979  ibb.status = status;
1980  ibb.kind = kind;
1981  ibb.repos_relpath = repos_relpath;
1982  ibb.revision = revision;
1983
1984  /* Depending upon KIND, any of these might get used. */
1985  ibb.children = NULL;
1986  ibb.depth = svn_depth_unknown;
1987  ibb.checksum = NULL;
1988  ibb.target = NULL;
1989
1990  ibb.conflict = conflict;
1991  ibb.work_items = work_items;
1992
1993  SVN_WC__DB_WITH_TXN(
1994            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1995            wcroot);
1996
1997  /* If this used to be a directory we should remove children so pass
1998   * depth infinity. */
1999  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2000                        scratch_pool));
2001
2002  return SVN_NO_ERROR;
2003}
2004
2005
2006svn_error_t *
2007svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2008                                  const char *local_abspath,
2009                                  const char *repos_relpath,
2010                                  const char *repos_root_url,
2011                                  const char *repos_uuid,
2012                                  svn_revnum_t revision,
2013                                  svn_node_kind_t kind,
2014                                  svn_wc__db_status_t status,
2015                                  const svn_skel_t *conflict,
2016                                  const svn_skel_t *work_items,
2017                                  apr_pool_t *scratch_pool)
2018{
2019  SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2020                 || status == svn_wc__db_status_excluded);
2021
2022  return add_excluded_or_not_present_node(
2023    db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2024    kind, status, conflict, work_items, scratch_pool);
2025}
2026
2027
2028svn_error_t *
2029svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2030                                     const char *local_abspath,
2031                                     const char *repos_relpath,
2032                                     const char *repos_root_url,
2033                                     const char *repos_uuid,
2034                                     svn_revnum_t revision,
2035                                     svn_node_kind_t kind,
2036                                     const svn_skel_t *conflict,
2037                                     const svn_skel_t *work_items,
2038                                     apr_pool_t *scratch_pool)
2039{
2040  return add_excluded_or_not_present_node(
2041    db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2042    kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2043}
2044
2045/* Recursively clear moved-here information at the copy-half of the move
2046 * which moved a node to MOVED_TO_RELPATH. This transforms this side of the
2047 * move into a simple copy.
2048 */
2049static svn_error_t *
2050clear_moved_here(svn_wc__db_wcroot_t *wcroot,
2051                 const char *moved_to_relpath,
2052                 apr_pool_t *scratch_pool)
2053{
2054  svn_sqlite__stmt_t *stmt;
2055  int affected_rows;
2056
2057  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2058                                    STMT_CLEAR_MOVED_HERE_RECURSIVE));
2059  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath,
2060                            relpath_depth(moved_to_relpath)));
2061
2062  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2063
2064  if (affected_rows == 0)
2065     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2066                              _("The node '%s' was not found."),
2067                              path_for_error_message(wcroot, moved_to_relpath,
2068                                                     scratch_pool));
2069
2070  return SVN_NO_ERROR;
2071}
2072
2073svn_error_t *
2074svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
2075                                  const char *src_relpath,
2076                                  int delete_op_depth,
2077                                  const char *dst_relpath,
2078                                  const svn_skel_t *work_items,
2079                                  apr_pool_t *scratch_pool)
2080{
2081  svn_sqlite__stmt_t *stmt;
2082  int affected;
2083
2084  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2085                                    STMT_CLEAR_MOVED_TO_RELPATH));
2086  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2087                            delete_op_depth));
2088  SVN_ERR(svn_sqlite__update(&affected, stmt));
2089
2090  if (affected != 1)
2091    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2092                             _("Path '%s' is not moved"),
2093                             path_for_error_message(wcroot, src_relpath,
2094                                                    scratch_pool));
2095
2096  SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool));
2097
2098  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2099  return SVN_NO_ERROR;
2100}
2101
2102
2103/* The body of svn_wc__db_base_remove().
2104 */
2105static svn_error_t *
2106db_base_remove(svn_wc__db_wcroot_t *wcroot,
2107               const char *local_relpath,
2108               svn_wc__db_t *db, /* For checking conflicts */
2109               svn_boolean_t keep_as_working,
2110               svn_boolean_t mark_not_present,
2111               svn_boolean_t mark_excluded,
2112               svn_revnum_t marker_revision,
2113               svn_skel_t *conflict,
2114               svn_skel_t *work_items,
2115               apr_pool_t *scratch_pool)
2116{
2117  svn_sqlite__stmt_t *stmt;
2118  svn_boolean_t have_row;
2119  svn_wc__db_status_t status;
2120  svn_revnum_t revision;
2121  apr_int64_t repos_id;
2122  const char *repos_relpath;
2123  svn_node_kind_t kind;
2124  svn_boolean_t keep_working;
2125  int op_depth;
2126  svn_node_kind_t wrk_kind;
2127  svn_boolean_t no_delete_wc = FALSE;
2128  svn_boolean_t file_external;
2129
2130  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
2131                                            &repos_relpath, &repos_id,
2132                                            NULL, NULL, NULL, NULL, NULL,
2133                                            NULL, NULL, NULL, NULL,
2134                                            &file_external,
2135                                            wcroot, local_relpath,
2136                                            scratch_pool, scratch_pool));
2137
2138  /* Check if there is already a working node */
2139  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2140                                    STMT_SELECT_NODE_INFO));
2141  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2142  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2143
2144  if (!have_row)
2145    return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
2146
2147  op_depth = svn_sqlite__column_int(stmt, 0);
2148  wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
2149
2150  if (op_depth > 0
2151      && op_depth == relpath_depth(local_relpath))
2152    {
2153      svn_wc__db_status_t presence;
2154      presence = svn_sqlite__column_token(stmt, 3, presence_map);
2155
2156      if (presence == svn_wc__db_status_base_deleted)
2157        {
2158          keep_working = FALSE;
2159          no_delete_wc = TRUE;
2160        }
2161      else
2162        {
2163          keep_working = TRUE;
2164        }
2165    }
2166  else
2167    keep_working = FALSE;
2168  SVN_ERR(svn_sqlite__reset(stmt));
2169
2170  if (keep_as_working && op_depth == 0)
2171    {
2172      if (status == svn_wc__db_status_normal
2173          || status == svn_wc__db_status_incomplete)
2174        {
2175          SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
2176                                                   NULL, NULL,
2177                                                   scratch_pool));
2178        }
2179      keep_working = TRUE;
2180    }
2181
2182  /* Step 1: Create workqueue operations to remove files and dirs in the
2183     local-wc */
2184  if (!keep_working && !no_delete_wc)
2185    {
2186      svn_skel_t *work_item;
2187      const char *local_abspath;
2188
2189      local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2190                                      scratch_pool);
2191      if (wrk_kind == svn_node_dir)
2192        {
2193          apr_pool_t *iterpool;
2194          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2195                                            STMT_SELECT_WORKING_PRESENT));
2196          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2197
2198          iterpool = svn_pool_create(scratch_pool);
2199
2200          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2201
2202          while (have_row)
2203            {
2204              const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2205              svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2206                                                              kind_map);
2207              const char *node_abspath;
2208              svn_error_t *err;
2209
2210              svn_pool_clear(iterpool);
2211
2212              node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2213                                             iterpool);
2214
2215              if (node_kind == svn_node_dir)
2216                err = svn_wc__wq_build_dir_remove(&work_item,
2217                                                  db, wcroot->abspath,
2218                                                  node_abspath, FALSE,
2219                                                  iterpool, iterpool);
2220              else
2221                err = svn_wc__wq_build_file_remove(&work_item,
2222                                                   db,
2223                                                   wcroot->abspath,
2224                                                   node_abspath,
2225                                                   iterpool, iterpool);
2226
2227              if (!err)
2228                err = add_work_items(wcroot->sdb, work_item, iterpool);
2229              if (err)
2230                return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2231
2232              SVN_ERR(svn_sqlite__step(&have_row, stmt));
2233           }
2234
2235          SVN_ERR(svn_sqlite__reset(stmt));
2236
2237          SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2238                                              db, wcroot->abspath,
2239                                              local_abspath, FALSE,
2240                                              scratch_pool, iterpool));
2241          svn_pool_destroy(iterpool);
2242        }
2243      else
2244        SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2245                                             db, wcroot->abspath,
2246                                             local_abspath,
2247                                             scratch_pool, scratch_pool));
2248
2249      SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2250    }
2251
2252  /* Step 2: Delete ACTUAL nodes */
2253  if (! keep_working)
2254    {
2255      /* There won't be a record in NODE left for this node, so we want
2256         to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2257      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2258                                        STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2259      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2260      SVN_ERR(svn_sqlite__step_done(stmt));
2261    }
2262  else if (! keep_as_working)
2263    {
2264      /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2265      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2266                                       STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2267      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2268      SVN_ERR(svn_sqlite__step_done(stmt));
2269    }
2270  /* Else: Everything has been turned into a copy, so we want to keep all
2271           ACTUAL_NODE records */
2272
2273  /* Step 3: Delete WORKING nodes */
2274  if (!keep_working)
2275    {
2276      apr_pool_t *iterpool;
2277
2278      /* When deleting everything in working we should break moves from
2279         here and to here.
2280       */
2281      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2282                                        STMT_SELECT_MOVED_OUTSIDE));
2283      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2284                                             local_relpath,
2285                                             relpath_depth(local_relpath)));
2286      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2287      iterpool = svn_pool_create(scratch_pool);
2288      while (have_row)
2289        {
2290          const char *moved_to_relpath;
2291          svn_error_t *err;
2292
2293          svn_pool_clear(iterpool);
2294          moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2295          err = clear_moved_here(wcroot, moved_to_relpath, iterpool);
2296          if (err)
2297            return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2298          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2299        }
2300      svn_pool_destroy(iterpool);
2301      SVN_ERR(svn_sqlite__reset(stmt));
2302    }
2303  else
2304    {
2305      /* We are keeping things that are in WORKING, but we should still
2306         break moves of things in BASE. (Mixed revisions make it
2307         impossible to guarantee that we can keep everything moved) */
2308
2309      apr_pool_t *iterpool;
2310
2311      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2312                                        STMT_SELECT_MOVED_DESCENDANTS_SRC));
2313      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2314                                local_relpath, 0));
2315      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2316      iterpool = svn_pool_create(scratch_pool);
2317      while (have_row)
2318        {
2319          int delete_op_depth = svn_sqlite__column_int(stmt, 0);
2320          const char *src_relpath;
2321          const char *dst_relpath;
2322          svn_error_t *err;
2323
2324          svn_pool_clear(iterpool);
2325
2326          src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2327          dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
2328
2329          err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
2330                                                  delete_op_depth,
2331                                                  dst_relpath,
2332                                                  NULL,
2333                                                  iterpool);
2334
2335          if (err)
2336            return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2337
2338          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2339        }
2340      svn_pool_destroy(iterpool);
2341      SVN_ERR(svn_sqlite__reset(stmt));
2342    }
2343  if (keep_working)
2344    {
2345      SVN_ERR(svn_sqlite__get_statement(
2346                    &stmt, wcroot->sdb,
2347                    STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
2348      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2349      SVN_ERR(svn_sqlite__step_done(stmt));
2350    }
2351  else
2352    {
2353      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2354                                        STMT_DELETE_WORKING_RECURSIVE));
2355      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2356      SVN_ERR(svn_sqlite__step_done(stmt));
2357    }
2358
2359  /* Step 4: Delete the BASE node descendants */
2360  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2361                                    STMT_DELETE_BASE_RECURSIVE));
2362  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2363  SVN_ERR(svn_sqlite__step_done(stmt));
2364
2365  SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
2366
2367  if (mark_not_present || mark_excluded)
2368    {
2369      struct insert_base_baton_t ibb;
2370      svn_boolean_t no_marker = FALSE;
2371
2372      if (file_external)
2373        {
2374          const char *parent_local_relpath;
2375          const char *name;
2376          svn_error_t *err;
2377
2378          /* For file externals we only want to place a not present marker
2379             if there is a BASE parent */
2380
2381          svn_relpath_split(&parent_local_relpath, &name, local_relpath,
2382                            scratch_pool);
2383
2384          err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
2385                                                  &repos_relpath, &repos_id,
2386                                                  NULL, NULL, NULL, NULL, NULL,
2387                                                  NULL, NULL, NULL, NULL, NULL,
2388                                                  wcroot, parent_local_relpath,
2389                                                  scratch_pool, scratch_pool);
2390
2391          if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2392            return svn_error_trace(err);
2393          else if (err)
2394            {
2395              svn_error_clear(err);
2396              no_marker = TRUE;
2397            }
2398          else
2399            {
2400              /* Replace the repos_relpath with something more expected than
2401                 the unrelated old file external repository relpath, which
2402                 one day may come from a different repository */
2403              repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool);
2404            }
2405        }
2406
2407      if (!no_marker)
2408        {
2409          blank_ibb(&ibb);
2410
2411          ibb.repos_id = repos_id;
2412          ibb.status = mark_excluded ? svn_wc__db_status_excluded
2413                                     : svn_wc__db_status_not_present;
2414          ibb.kind = kind;
2415          ibb.repos_relpath = repos_relpath;
2416          ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
2417                            ? marker_revision
2418                            : revision;
2419
2420          /* Depending upon KIND, any of these might get used. */
2421          ibb.children = NULL;
2422          ibb.depth = svn_depth_unknown;
2423          ibb.checksum = NULL;
2424          ibb.target = NULL;
2425
2426          SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2427        }
2428    }
2429
2430  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2431  if (conflict)
2432    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2433                                              conflict, scratch_pool));
2434
2435  return SVN_NO_ERROR;
2436}
2437
2438
2439svn_error_t *
2440svn_wc__db_base_remove(svn_wc__db_t *db,
2441                       const char *local_abspath,
2442                       svn_boolean_t keep_as_working,
2443                       svn_boolean_t mark_not_present,
2444                       svn_boolean_t mark_excluded,
2445                       svn_revnum_t marker_revision,
2446                       svn_skel_t *conflict,
2447                       svn_skel_t *work_items,
2448                       apr_pool_t *scratch_pool)
2449{
2450  svn_wc__db_wcroot_t *wcroot;
2451  const char *local_relpath;
2452
2453  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2454
2455  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2456                              local_abspath, scratch_pool, scratch_pool));
2457  VERIFY_USABLE_WCROOT(wcroot);
2458
2459  SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2460                                     db, keep_as_working,
2461                                     mark_not_present, mark_excluded,
2462                                     marker_revision,
2463                                     conflict, work_items, scratch_pool),
2464                      wcroot);
2465
2466  /* If this used to be a directory we should remove children so pass
2467   * depth infinity. */
2468  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2469                        scratch_pool));
2470
2471  return SVN_NO_ERROR;
2472}
2473
2474
2475svn_error_t *
2476svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2477                                  svn_node_kind_t *kind,
2478                                  svn_revnum_t *revision,
2479                                  const char **repos_relpath,
2480                                  apr_int64_t *repos_id,
2481                                  svn_revnum_t *changed_rev,
2482                                  apr_time_t *changed_date,
2483                                  const char **changed_author,
2484                                  svn_depth_t *depth,
2485                                  const svn_checksum_t **checksum,
2486                                  const char **target,
2487                                  svn_wc__db_lock_t **lock,
2488                                  svn_boolean_t *had_props,
2489                                  apr_hash_t **props,
2490                                  svn_boolean_t *update_root,
2491                                  svn_wc__db_wcroot_t *wcroot,
2492                                  const char *local_relpath,
2493                                  apr_pool_t *result_pool,
2494                                  apr_pool_t *scratch_pool)
2495{
2496  svn_sqlite__stmt_t *stmt;
2497  svn_boolean_t have_row;
2498  svn_error_t *err = SVN_NO_ERROR;
2499
2500  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2501                                    lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2502                                         : STMT_SELECT_BASE_NODE));
2503  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2504  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2505
2506  if (have_row)
2507    {
2508      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2509                                                                 presence_map);
2510      svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2511
2512      if (kind)
2513        {
2514          *kind = node_kind;
2515        }
2516      if (status)
2517        {
2518          *status = node_status;
2519        }
2520      repos_location_from_columns(repos_id, revision, repos_relpath,
2521                                  stmt, 0, 4, 1, result_pool);
2522      SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2523      SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2524      if (lock)
2525        {
2526          *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2527        }
2528      if (changed_rev)
2529        {
2530          *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2531        }
2532      if (changed_date)
2533        {
2534          *changed_date = svn_sqlite__column_int64(stmt, 8);
2535        }
2536      if (changed_author)
2537        {
2538          /* Result may be NULL. */
2539          *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2540        }
2541      if (depth)
2542        {
2543          if (node_kind != svn_node_dir)
2544            {
2545              *depth = svn_depth_unknown;
2546            }
2547          else
2548            {
2549              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2550                                                     svn_depth_unknown);
2551            }
2552        }
2553      if (checksum)
2554        {
2555          if (node_kind != svn_node_file)
2556            {
2557              *checksum = NULL;
2558            }
2559          else
2560            {
2561              err = svn_sqlite__column_checksum(checksum, stmt, 5,
2562                                                result_pool);
2563              if (err != NULL)
2564                err = svn_error_createf(
2565                        err->apr_err, err,
2566                        _("The node '%s' has a corrupt checksum value."),
2567                        path_for_error_message(wcroot, local_relpath,
2568                                               scratch_pool));
2569            }
2570        }
2571      if (target)
2572        {
2573          if (node_kind != svn_node_symlink)
2574            *target = NULL;
2575          else
2576            *target = svn_sqlite__column_text(stmt, 11, result_pool);
2577        }
2578      if (had_props)
2579        {
2580          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2581        }
2582      if (props)
2583        {
2584          if (node_status == svn_wc__db_status_normal
2585              || node_status == svn_wc__db_status_incomplete)
2586            {
2587              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2588                                                    result_pool, scratch_pool));
2589              if (*props == NULL)
2590                *props = apr_hash_make(result_pool);
2591            }
2592          else
2593            {
2594              assert(svn_sqlite__column_is_null(stmt, 13));
2595              *props = NULL;
2596            }
2597        }
2598      if (update_root)
2599        {
2600          /* It's an update root iff it's a file external. */
2601          *update_root = svn_sqlite__column_boolean(stmt, 14);
2602        }
2603    }
2604  else
2605    {
2606      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2607                              _("The node '%s' was not found."),
2608                              path_for_error_message(wcroot, local_relpath,
2609                                                     scratch_pool));
2610    }
2611
2612  /* Note: given the composition, no need to wrap for tracing.  */
2613  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2614}
2615
2616
2617svn_error_t *
2618svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2619                         svn_node_kind_t *kind,
2620                         svn_revnum_t *revision,
2621                         const char **repos_relpath,
2622                         const char **repos_root_url,
2623                         const char **repos_uuid,
2624                         svn_revnum_t *changed_rev,
2625                         apr_time_t *changed_date,
2626                         const char **changed_author,
2627                         svn_depth_t *depth,
2628                         const svn_checksum_t **checksum,
2629                         const char **target,
2630                         svn_wc__db_lock_t **lock,
2631                         svn_boolean_t *had_props,
2632                         apr_hash_t **props,
2633                         svn_boolean_t *update_root,
2634                         svn_wc__db_t *db,
2635                         const char *local_abspath,
2636                         apr_pool_t *result_pool,
2637                         apr_pool_t *scratch_pool)
2638{
2639  svn_wc__db_wcroot_t *wcroot;
2640  const char *local_relpath;
2641  apr_int64_t repos_id;
2642
2643  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2644
2645  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2646                              local_abspath, scratch_pool, scratch_pool));
2647  VERIFY_USABLE_WCROOT(wcroot);
2648
2649  SVN_WC__DB_WITH_TXN4(
2650          svn_wc__db_base_get_info_internal(status, kind, revision,
2651                                            repos_relpath, &repos_id,
2652                                            changed_rev, changed_date,
2653                                            changed_author, depth,
2654                                            checksum, target, lock,
2655                                            had_props, props, update_root,
2656                                            wcroot, local_relpath,
2657                                            result_pool, scratch_pool),
2658          svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2659                                      wcroot, repos_id, result_pool),
2660          SVN_NO_ERROR,
2661          SVN_NO_ERROR,
2662          wcroot);
2663  SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2664
2665  return SVN_NO_ERROR;
2666}
2667
2668/* The implementation of svn_wc__db_base_get_children_info */
2669static svn_error_t *
2670base_get_children_info(apr_hash_t **nodes,
2671                       svn_wc__db_wcroot_t *wcroot,
2672                       const char *local_relpath,
2673                       svn_boolean_t obtain_locks,
2674                       apr_pool_t *result_pool,
2675                       apr_pool_t *scratch_pool)
2676{
2677  svn_sqlite__stmt_t *stmt;
2678  svn_boolean_t have_row;
2679  apr_int64_t last_repos_id = INVALID_REPOS_ID;
2680  const char *last_repos_root_url = NULL;
2681
2682  *nodes = apr_hash_make(result_pool);
2683
2684  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2685                                    obtain_locks
2686                                      ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK
2687                                      : STMT_SELECT_BASE_CHILDREN_INFO));
2688  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2689
2690  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2691
2692  while (have_row)
2693    {
2694      struct svn_wc__db_base_info_t *info;
2695      apr_int64_t repos_id;
2696      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2697      const char *name = svn_relpath_basename(child_relpath, result_pool);
2698
2699      info = apr_pcalloc(result_pool, sizeof(*info));
2700
2701      repos_id = svn_sqlite__column_int64(stmt, 1);
2702      info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2703      info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2704      info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2705      info->revnum = svn_sqlite__column_revnum(stmt, 5);
2706
2707      info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2708                                                  svn_depth_unknown);
2709
2710      info->update_root = svn_sqlite__column_boolean(stmt, 7);
2711
2712      if (obtain_locks)
2713        info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2714
2715      if (repos_id != last_repos_id)
2716        {
2717          svn_error_t *err;
2718
2719          err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
2720                                            wcroot, repos_id,
2721                                            result_pool);
2722
2723          if (err)
2724            return svn_error_trace(
2725                     svn_error_compose_create(err,
2726                                              svn_sqlite__reset(stmt)));
2727
2728          last_repos_id = repos_id;
2729        }
2730
2731      info->repos_root_url = last_repos_root_url;
2732
2733      svn_hash_sets(*nodes, name, info);
2734
2735      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2736    }
2737
2738  SVN_ERR(svn_sqlite__reset(stmt));
2739
2740  return SVN_NO_ERROR;
2741}
2742
2743svn_error_t *
2744svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2745                                  svn_wc__db_t *db,
2746                                  const char *dir_abspath,
2747                                  apr_pool_t *result_pool,
2748                                  apr_pool_t *scratch_pool)
2749{
2750  svn_wc__db_wcroot_t *wcroot;
2751  const char *local_relpath;
2752
2753  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2754
2755  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2756                              dir_abspath, scratch_pool, scratch_pool));
2757  VERIFY_USABLE_WCROOT(wcroot);
2758
2759  return svn_error_trace(base_get_children_info(nodes,
2760                                                wcroot,
2761                                                local_relpath,
2762                                                TRUE /* obtain_locks */,
2763                                                result_pool,
2764                                                scratch_pool));
2765}
2766
2767
2768svn_error_t *
2769svn_wc__db_base_get_props(apr_hash_t **props,
2770                          svn_wc__db_t *db,
2771                          const char *local_abspath,
2772                          apr_pool_t *result_pool,
2773                          apr_pool_t *scratch_pool)
2774{
2775  svn_wc__db_status_t presence;
2776
2777  SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2778                                   NULL, NULL, NULL, NULL, NULL,
2779                                   NULL, NULL, NULL, NULL, props, NULL,
2780                                   db, local_abspath,
2781                                   result_pool, scratch_pool));
2782  if (presence != svn_wc__db_status_normal
2783      && presence != svn_wc__db_status_incomplete)
2784    {
2785      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2786                               _("The node '%s' has a BASE status that"
2787                                  " has no properties."),
2788                               svn_dirent_local_style(local_abspath,
2789                                                      scratch_pool));
2790    }
2791
2792  return SVN_NO_ERROR;
2793}
2794
2795
2796svn_error_t *
2797svn_wc__db_base_get_children(const apr_array_header_t **children,
2798                             svn_wc__db_t *db,
2799                             const char *local_abspath,
2800                             apr_pool_t *result_pool,
2801                             apr_pool_t *scratch_pool)
2802{
2803  svn_wc__db_wcroot_t *wcroot;
2804  const char *local_relpath;
2805
2806  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2807
2808  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2809                                             local_abspath,
2810                                             scratch_pool, scratch_pool));
2811  VERIFY_USABLE_WCROOT(wcroot);
2812
2813  return svn_error_trace(
2814              gather_children(children, wcroot, local_relpath,
2815                              STMT_SELECT_OP_DEPTH_CHILDREN, 0,
2816                              result_pool, scratch_pool));
2817}
2818
2819
2820svn_error_t *
2821svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2822                              const char *local_abspath,
2823                              const apr_hash_t *props,
2824                              apr_pool_t *scratch_pool)
2825{
2826  svn_wc__db_wcroot_t *wcroot;
2827  const char *local_relpath;
2828  svn_sqlite__stmt_t *stmt;
2829  int affected_rows;
2830
2831  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2832
2833  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2834                              local_abspath, scratch_pool, scratch_pool));
2835  VERIFY_USABLE_WCROOT(wcroot);
2836
2837  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2838                                    STMT_UPDATE_BASE_NODE_DAV_CACHE));
2839  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2840  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2841
2842  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2843
2844  if (affected_rows != 1)
2845    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2846                             _("The node '%s' was not found."),
2847                             svn_dirent_local_style(local_abspath,
2848                                                    scratch_pool));
2849
2850  return SVN_NO_ERROR;
2851}
2852
2853
2854svn_error_t *
2855svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2856                              svn_wc__db_t *db,
2857                              const char *local_abspath,
2858                              apr_pool_t *result_pool,
2859                              apr_pool_t *scratch_pool)
2860{
2861  svn_wc__db_wcroot_t *wcroot;
2862  const char *local_relpath;
2863  svn_sqlite__stmt_t *stmt;
2864  svn_boolean_t have_row;
2865
2866  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2867
2868  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2869                              local_abspath, scratch_pool, scratch_pool));
2870  VERIFY_USABLE_WCROOT(wcroot);
2871
2872  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2873                                    STMT_SELECT_BASE_DAV_CACHE));
2874
2875  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2876  if (!have_row)
2877    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2878                             svn_sqlite__reset(stmt),
2879                             _("The node '%s' was not found."),
2880                             svn_dirent_local_style(local_abspath,
2881                                                    scratch_pool));
2882
2883  SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2884                                        scratch_pool));
2885  return svn_error_trace(svn_sqlite__reset(stmt));
2886}
2887
2888
2889svn_error_t *
2890svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2891                                          const char *local_abspath,
2892                                          apr_pool_t *scratch_pool)
2893{
2894  svn_wc__db_wcroot_t *wcroot;
2895  const char *local_relpath;
2896  svn_sqlite__stmt_t *stmt;
2897
2898  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2899                                             db, local_abspath,
2900                                             scratch_pool, scratch_pool));
2901  VERIFY_USABLE_WCROOT(wcroot);
2902
2903  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2904                                    STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2905  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2906
2907  SVN_ERR(svn_sqlite__step_done(stmt));
2908
2909  return SVN_NO_ERROR;
2910}
2911
2912
2913svn_error_t *
2914svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2915                          svn_node_kind_t *kind,
2916                          svn_revnum_t *revision,
2917                          const char **repos_relpath,
2918                          apr_int64_t *repos_id,
2919                          svn_revnum_t *changed_rev,
2920                          apr_time_t *changed_date,
2921                          const char **changed_author,
2922                          svn_depth_t *depth,
2923                          const svn_checksum_t **checksum,
2924                          const char **target,
2925                          svn_boolean_t *had_props,
2926                          apr_hash_t **props,
2927                          svn_wc__db_wcroot_t *wcroot,
2928                          const char *local_relpath,
2929                          int op_depth,
2930                          apr_pool_t *result_pool,
2931                          apr_pool_t *scratch_pool)
2932{
2933  svn_sqlite__stmt_t *stmt;
2934  svn_boolean_t have_row;
2935  svn_error_t *err = SVN_NO_ERROR;
2936
2937  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2938                                    STMT_SELECT_DEPTH_NODE));
2939  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2940                            wcroot->wc_id, local_relpath, op_depth));
2941  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2942
2943  if (have_row)
2944    {
2945      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2946                                                                 presence_map);
2947      svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2948
2949      if (kind)
2950        {
2951          *kind = node_kind;
2952        }
2953      if (status)
2954        {
2955          *status = node_status;
2956
2957          if (op_depth > 0)
2958            SVN_ERR(convert_to_working_status(status, *status));
2959        }
2960      repos_location_from_columns(repos_id, revision, repos_relpath,
2961                                  stmt, 0, 4, 1, result_pool);
2962
2963      if (changed_rev)
2964        {
2965          *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2966        }
2967      if (changed_date)
2968        {
2969          *changed_date = svn_sqlite__column_int64(stmt, 8);
2970        }
2971      if (changed_author)
2972        {
2973          /* Result may be NULL. */
2974          *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2975        }
2976      if (depth)
2977        {
2978          if (node_kind != svn_node_dir)
2979            {
2980              *depth = svn_depth_unknown;
2981            }
2982          else
2983            {
2984              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2985                                                     svn_depth_unknown);
2986            }
2987        }
2988      if (checksum)
2989        {
2990          if (node_kind != svn_node_file)
2991            {
2992              *checksum = NULL;
2993            }
2994          else
2995            {
2996              err = svn_sqlite__column_checksum(checksum, stmt, 5,
2997                                                result_pool);
2998              if (err != NULL)
2999                err = svn_error_createf(
3000                        err->apr_err, err,
3001                        _("The node '%s' has a corrupt checksum value."),
3002                        path_for_error_message(wcroot, local_relpath,
3003                                               scratch_pool));
3004            }
3005        }
3006      if (target)
3007        {
3008          if (node_kind != svn_node_symlink)
3009            *target = NULL;
3010          else
3011            *target = svn_sqlite__column_text(stmt, 11, result_pool);
3012        }
3013      if (had_props)
3014        {
3015          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12);
3016        }
3017      if (props)
3018        {
3019          if (node_status == svn_wc__db_status_normal
3020              || node_status == svn_wc__db_status_incomplete)
3021            {
3022              SVN_ERR(svn_sqlite__column_properties(props, stmt, 12,
3023                                                    result_pool, scratch_pool));
3024              if (*props == NULL)
3025                *props = apr_hash_make(result_pool);
3026            }
3027          else
3028            {
3029              assert(svn_sqlite__column_is_null(stmt, 12));
3030              *props = NULL;
3031            }
3032        }
3033    }
3034  else
3035    {
3036      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3037                              _("The node '%s' was not found."),
3038                              path_for_error_message(wcroot, local_relpath,
3039                                                     scratch_pool));
3040    }
3041
3042  /* Note: given the composition, no need to wrap for tracing.  */
3043  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
3044}
3045
3046/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
3047typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
3048                                          svn_wc__db_wcroot_t *wcroot,
3049                                          const char *local_relpath,
3050                                          apr_pool_t *scratch_pool);
3051
3052/* Baton for passing args to with_triggers(). */
3053struct with_triggers_baton_t {
3054  int create_trigger;
3055  int drop_trigger;
3056  svn_wc__db_txn_callback_t cb_func;
3057  void *cb_baton;
3058};
3059
3060/* Helper for creating SQLite triggers, running the main transaction
3061   callback, and then dropping the triggers.  It guarantees that the
3062   triggers will not survive the transaction.  This could be used for
3063   any general prefix/postscript statements where the postscript
3064   *must* be executed if the transaction completes.
3065
3066   Implements svn_wc__db_txn_callback_t. */
3067static svn_error_t *
3068with_triggers(void *baton,
3069              svn_wc__db_wcroot_t *wcroot,
3070              const char *local_relpath,
3071              apr_pool_t *scratch_pool)
3072{
3073  struct with_triggers_baton_t *b = baton;
3074  svn_error_t *err1;
3075  svn_error_t *err2;
3076
3077  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3078
3079  err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3080
3081  err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3082
3083  return svn_error_trace(svn_error_compose_create(err1, err2));
3084}
3085
3086
3087/* Prototype for the "work callback" used by with_finalization().  */
3088typedef svn_error_t * (*work_callback_t)(
3089                          void *baton,
3090                          svn_wc__db_wcroot_t *wcroot,
3091                          svn_cancel_func_t cancel_func,
3092                          void *cancel_baton,
3093                          svn_wc_notify_func2_t notify_func,
3094                          void *notify_baton,
3095                          apr_pool_t *scratch_pool);
3096
3097/* Utility function to provide several features, with a guaranteed
3098   finalization (ie. to drop temporary tables).
3099
3100   1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3101      sqlite transaction
3102   2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3103      the "work" step: WORK_CB(WORK_BATON).
3104   3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3105      from the above two steps.
3106
3107   CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3108   typical values. These are passed to the work callback, which typically
3109   provides notification about the work done by TXN_CB.  */
3110static svn_error_t *
3111with_finalization(svn_wc__db_wcroot_t *wcroot,
3112                  const char *local_relpath,
3113                  svn_wc__db_txn_callback_t txn_cb,
3114                  void *txn_baton,
3115                  work_callback_t work_cb,
3116                  void *work_baton,
3117                  svn_cancel_func_t cancel_func,
3118                  void *cancel_baton,
3119                  svn_wc_notify_func2_t notify_func,
3120                  void *notify_baton,
3121                  int finalize_stmt_idx,
3122                  apr_pool_t *scratch_pool)
3123{
3124  svn_error_t *err1;
3125  svn_error_t *err2;
3126
3127  err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
3128  if (!err1)
3129    {
3130      err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
3131
3132      err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
3133    }
3134
3135  if (err1 == NULL && notify_func != NULL)
3136    {
3137      err2 = work_cb(work_baton, wcroot,
3138                     cancel_func, cancel_baton,
3139                     notify_func, notify_baton,
3140                     scratch_pool);
3141      err1 = svn_error_compose_create(err1, err2);
3142    }
3143
3144  err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3145
3146  return svn_error_trace(svn_error_compose_create(err1, err2));
3147}
3148
3149
3150/* Initialize the baton with appropriate "blank" values. This allows the
3151   insertion function to leave certain columns null.  */
3152static void
3153blank_ieb(insert_external_baton_t *ieb)
3154{
3155  memset(ieb, 0, sizeof(*ieb));
3156  ieb->revision = SVN_INVALID_REVNUM;
3157  ieb->changed_rev = SVN_INVALID_REVNUM;
3158  ieb->repos_id = INVALID_REPOS_ID;
3159
3160  ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3161  ieb->recorded_revision = SVN_INVALID_REVNUM;
3162}
3163
3164/* Insert the externals row represented by (insert_external_baton_t *) BATON.
3165 *
3166 * Implements svn_wc__db_txn_callback_t. */
3167static svn_error_t *
3168insert_external_node(const insert_external_baton_t *ieb,
3169                     svn_wc__db_wcroot_t *wcroot,
3170                     const char *local_relpath,
3171                     apr_pool_t *scratch_pool)
3172{
3173  svn_wc__db_status_t status;
3174  svn_error_t *err;
3175  svn_boolean_t update_root;
3176  apr_int64_t repos_id;
3177  svn_sqlite__stmt_t *stmt;
3178
3179  if (ieb->repos_id != INVALID_REPOS_ID)
3180    repos_id = ieb->repos_id;
3181  else
3182    SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3183                            wcroot->sdb, scratch_pool));
3184
3185  /* And there must be no existing BASE node or it must be a file external */
3186  err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3187                                          NULL, NULL, NULL, NULL, NULL,
3188                                          NULL, NULL, NULL, NULL, &update_root,
3189                                          wcroot, local_relpath,
3190                                          scratch_pool, scratch_pool);
3191  if (err)
3192    {
3193      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3194        return svn_error_trace(err);
3195
3196      svn_error_clear(err);
3197    }
3198  else if (status == svn_wc__db_status_normal && !update_root)
3199    return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3200
3201  if (ieb->kind == svn_node_file
3202      || ieb->kind == svn_node_symlink)
3203    {
3204      struct insert_base_baton_t ibb;
3205
3206      blank_ibb(&ibb);
3207
3208      ibb.status          = svn_wc__db_status_normal;
3209      ibb.kind            = ieb->kind;
3210
3211      ibb.repos_id        = repos_id;
3212      ibb.repos_relpath   = ieb->repos_relpath;
3213      ibb.revision        = ieb->revision;
3214
3215      ibb.props           = ieb->props;
3216      ibb.iprops          = ieb->iprops;
3217      ibb.changed_rev     = ieb->changed_rev;
3218      ibb.changed_date    = ieb->changed_date;
3219      ibb.changed_author  = ieb->changed_author;
3220
3221      ibb.dav_cache       = ieb->dav_cache;
3222
3223      ibb.checksum        = ieb->checksum;
3224      ibb.target          = ieb->target;
3225
3226      ibb.conflict        = ieb->conflict;
3227
3228      ibb.update_actual_props = ieb->update_actual_props;
3229      ibb.new_actual_props    = ieb->new_actual_props;
3230
3231      ibb.keep_recorded_info  = ieb->keep_recorded_info;
3232
3233      ibb.work_items      = ieb->work_items;
3234
3235      ibb.file_external = TRUE;
3236
3237      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3238    }
3239  else
3240    SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3241
3242  /* The externals table only support presence normal and excluded */
3243  SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3244                 || ieb->presence == svn_wc__db_status_excluded);
3245
3246  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3247
3248  SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3249                            wcroot->wc_id,
3250                            local_relpath,
3251                            svn_relpath_dirname(local_relpath,
3252                                                scratch_pool),
3253                            presence_map, ieb->presence,
3254                            kind_map, ieb->kind,
3255                            ieb->record_ancestor_relpath,
3256                            repos_id,
3257                            ieb->recorded_repos_relpath));
3258
3259  if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3260    SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3261
3262  if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3263    SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3264
3265  SVN_ERR(svn_sqlite__insert(NULL, stmt));
3266
3267  return SVN_NO_ERROR;
3268}
3269
3270svn_error_t *
3271svn_wc__db_external_add_file(svn_wc__db_t *db,
3272                             const char *local_abspath,
3273                             const char *wri_abspath,
3274
3275                             const char *repos_relpath,
3276                             const char *repos_root_url,
3277                             const char *repos_uuid,
3278                             svn_revnum_t revision,
3279
3280                             const apr_hash_t *props,
3281                             apr_array_header_t *iprops,
3282
3283                             svn_revnum_t changed_rev,
3284                             apr_time_t changed_date,
3285                             const char *changed_author,
3286
3287                             const svn_checksum_t *checksum,
3288
3289                             const apr_hash_t *dav_cache,
3290
3291                             const char *record_ancestor_abspath,
3292                             const char *recorded_repos_relpath,
3293                             svn_revnum_t recorded_peg_revision,
3294                             svn_revnum_t recorded_revision,
3295
3296                             svn_boolean_t update_actual_props,
3297                             apr_hash_t *new_actual_props,
3298
3299                             svn_boolean_t keep_recorded_info,
3300                             const svn_skel_t *conflict,
3301                             const svn_skel_t *work_items,
3302                             apr_pool_t *scratch_pool)
3303{
3304  svn_wc__db_wcroot_t *wcroot;
3305  const char *local_relpath;
3306  insert_external_baton_t ieb;
3307
3308  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3309
3310  if (! wri_abspath)
3311    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3312
3313  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3314                              wri_abspath, scratch_pool, scratch_pool));
3315  VERIFY_USABLE_WCROOT(wcroot);
3316
3317  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3318                                        record_ancestor_abspath));
3319
3320  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3321
3322  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3323
3324  blank_ieb(&ieb);
3325
3326  ieb.kind = svn_node_file;
3327  ieb.presence = svn_wc__db_status_normal;
3328
3329  ieb.repos_root_url = repos_root_url;
3330  ieb.repos_uuid = repos_uuid;
3331
3332  ieb.repos_relpath = repos_relpath;
3333  ieb.revision = revision;
3334
3335  ieb.props = props;
3336  ieb.iprops = iprops;
3337
3338  ieb.changed_rev = changed_rev;
3339  ieb.changed_date = changed_date;
3340  ieb.changed_author = changed_author;
3341
3342  ieb.checksum = checksum;
3343
3344  ieb.dav_cache = dav_cache;
3345
3346  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3347                                                wcroot->abspath,
3348                                                record_ancestor_abspath);
3349  ieb.recorded_repos_relpath = recorded_repos_relpath;
3350  ieb.recorded_peg_revision = recorded_peg_revision;
3351  ieb.recorded_revision = recorded_revision;
3352
3353  ieb.update_actual_props = update_actual_props;
3354  ieb.new_actual_props = new_actual_props;
3355
3356  ieb.keep_recorded_info = keep_recorded_info;
3357
3358  ieb.conflict = conflict;
3359  ieb.work_items = work_items;
3360
3361  SVN_WC__DB_WITH_TXN(
3362            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3363            wcroot);
3364
3365  return SVN_NO_ERROR;
3366}
3367
3368svn_error_t *
3369svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3370                                const char *local_abspath,
3371                                const char *wri_abspath,
3372                                const char *repos_relpath,
3373                                const char *repos_root_url,
3374                                const char *repos_uuid,
3375                                svn_revnum_t revision,
3376                                const apr_hash_t *props,
3377                                svn_revnum_t changed_rev,
3378                                apr_time_t changed_date,
3379                                const char *changed_author,
3380                                const char *target,
3381                                const apr_hash_t *dav_cache,
3382                                const char *record_ancestor_abspath,
3383                                const char *recorded_repos_relpath,
3384                                svn_revnum_t recorded_peg_revision,
3385                                svn_revnum_t recorded_revision,
3386                                svn_boolean_t update_actual_props,
3387                                apr_hash_t *new_actual_props,
3388                                svn_boolean_t keep_recorded_info,
3389                                const svn_skel_t *work_items,
3390                                apr_pool_t *scratch_pool)
3391{
3392  svn_wc__db_wcroot_t *wcroot;
3393  const char *local_relpath;
3394  insert_external_baton_t ieb;
3395
3396  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3397
3398  if (! wri_abspath)
3399    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3400
3401  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3402                              wri_abspath, scratch_pool, scratch_pool));
3403  VERIFY_USABLE_WCROOT(wcroot);
3404
3405  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3406                                        record_ancestor_abspath));
3407
3408  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3409
3410  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3411
3412  blank_ieb(&ieb);
3413
3414  ieb.kind = svn_node_symlink;
3415  ieb.presence = svn_wc__db_status_normal;
3416
3417  ieb.repos_root_url = repos_root_url;
3418  ieb.repos_uuid = repos_uuid;
3419
3420  ieb.repos_relpath = repos_relpath;
3421  ieb.revision = revision;
3422
3423  ieb.props = props;
3424
3425  ieb.changed_rev = changed_rev;
3426  ieb.changed_date = changed_date;
3427  ieb.changed_author = changed_author;
3428
3429  ieb.target = target;
3430
3431  ieb.dav_cache = dav_cache;
3432
3433  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3434                                                wcroot->abspath,
3435                                                record_ancestor_abspath);
3436  ieb.recorded_repos_relpath = recorded_repos_relpath;
3437  ieb.recorded_peg_revision = recorded_peg_revision;
3438  ieb.recorded_revision = recorded_revision;
3439
3440  ieb.update_actual_props = update_actual_props;
3441  ieb.new_actual_props = new_actual_props;
3442
3443  ieb.keep_recorded_info = keep_recorded_info;
3444
3445  ieb.work_items = work_items;
3446
3447  SVN_WC__DB_WITH_TXN(
3448            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3449            wcroot);
3450
3451  return SVN_NO_ERROR;
3452}
3453
3454svn_error_t *
3455svn_wc__db_external_add_dir(svn_wc__db_t *db,
3456                            const char *local_abspath,
3457                            const char *wri_abspath,
3458                            const char *repos_root_url,
3459                            const char *repos_uuid,
3460                            const char *record_ancestor_abspath,
3461                            const char *recorded_repos_relpath,
3462                            svn_revnum_t recorded_peg_revision,
3463                            svn_revnum_t recorded_revision,
3464                            const svn_skel_t *work_items,
3465                            apr_pool_t *scratch_pool)
3466{
3467  svn_wc__db_wcroot_t *wcroot;
3468  const char *local_relpath;
3469  insert_external_baton_t ieb;
3470
3471  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3472
3473  if (! wri_abspath)
3474    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3475
3476  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3477                              wri_abspath, scratch_pool, scratch_pool));
3478  VERIFY_USABLE_WCROOT(wcroot);
3479
3480  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3481                                        record_ancestor_abspath));
3482
3483  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3484
3485  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3486
3487  blank_ieb(&ieb);
3488
3489  ieb.kind = svn_node_dir;
3490  ieb.presence = svn_wc__db_status_normal;
3491
3492  ieb.repos_root_url = repos_root_url;
3493  ieb.repos_uuid = repos_uuid;
3494
3495  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3496                                                wcroot->abspath,
3497                                                record_ancestor_abspath);
3498  ieb.recorded_repos_relpath = recorded_repos_relpath;
3499  ieb.recorded_peg_revision = recorded_peg_revision;
3500  ieb.recorded_revision = recorded_revision;
3501
3502  ieb.work_items = work_items;
3503
3504  SVN_WC__DB_WITH_TXN(
3505            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3506            wcroot);
3507
3508  return SVN_NO_ERROR;
3509}
3510
3511/* The body of svn_wc__db_external_remove(). */
3512static svn_error_t *
3513db_external_remove(const svn_skel_t *work_items,
3514                   svn_wc__db_wcroot_t *wcroot,
3515                   const char *local_relpath,
3516                   apr_pool_t *scratch_pool)
3517{
3518  svn_sqlite__stmt_t *stmt;
3519  int affected_rows;
3520
3521  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3522                                    STMT_DELETE_EXTERNAL));
3523  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3524  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
3525
3526  if (!affected_rows)
3527    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3528                             _("The node '%s' is not an external."),
3529                             path_for_error_message(wcroot, local_relpath,
3530                                                    scratch_pool));
3531
3532  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3533
3534  /* ### What about actual? */
3535  return SVN_NO_ERROR;
3536}
3537
3538svn_error_t *
3539svn_wc__db_external_remove(svn_wc__db_t *db,
3540                           const char *local_abspath,
3541                           const char *wri_abspath,
3542                           const svn_skel_t *work_items,
3543                           apr_pool_t *scratch_pool)
3544{
3545  svn_wc__db_wcroot_t *wcroot;
3546  const char *local_relpath;
3547
3548  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3549
3550  if (! wri_abspath)
3551    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3552
3553  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3554                              wri_abspath, scratch_pool, scratch_pool));
3555  VERIFY_USABLE_WCROOT(wcroot);
3556
3557  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3558
3559  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3560
3561  SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3562                                         scratch_pool),
3563                      wcroot);
3564
3565  return SVN_NO_ERROR;
3566}
3567
3568svn_error_t *
3569svn_wc__db_external_read(svn_wc__db_status_t *status,
3570                         svn_node_kind_t *kind,
3571                         const char **definining_abspath,
3572                         const char **repos_root_url,
3573                         const char **repos_uuid,
3574                         const char **recorded_repos_relpath,
3575                         svn_revnum_t *recorded_peg_revision,
3576                         svn_revnum_t *recorded_revision,
3577                         svn_wc__db_t *db,
3578                         const char *local_abspath,
3579                         const char *wri_abspath,
3580                         apr_pool_t *result_pool,
3581                         apr_pool_t *scratch_pool)
3582{
3583  svn_wc__db_wcroot_t *wcroot;
3584  const char *local_relpath;
3585  svn_sqlite__stmt_t *stmt;
3586  svn_boolean_t have_info;
3587  svn_error_t *err = NULL;
3588  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3589
3590  if (! wri_abspath)
3591    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3592
3593  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3594                              wri_abspath, scratch_pool, scratch_pool));
3595  VERIFY_USABLE_WCROOT(wcroot);
3596
3597  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3598
3599  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3600
3601  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3602                                    STMT_SELECT_EXTERNAL_INFO));
3603  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3604  SVN_ERR(svn_sqlite__step(&have_info, stmt));
3605
3606  if (have_info)
3607    {
3608      if (status)
3609        *status = svn_sqlite__column_token(stmt, 0, presence_map);
3610
3611      if (kind)
3612        *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3613
3614      if (definining_abspath)
3615        {
3616          const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3617
3618          *definining_abspath = svn_dirent_join(wcroot->abspath,
3619                                                record_relpath, result_pool);
3620        }
3621
3622      if (repos_root_url || repos_uuid)
3623        {
3624          apr_int64_t repos_id;
3625
3626          repos_id = svn_sqlite__column_int64(stmt, 3);
3627
3628          err = svn_error_compose_create(
3629                        err,
3630                        svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3631                                                    wcroot, repos_id,
3632                                                    result_pool));
3633        }
3634
3635      if (recorded_repos_relpath)
3636        *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3637                                                          result_pool);
3638
3639      if (recorded_peg_revision)
3640        *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3641
3642      if (recorded_revision)
3643        *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3644    }
3645  else
3646    {
3647      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3648                              _("The node '%s' is not an external."),
3649                              svn_dirent_local_style(local_abspath,
3650                                                     scratch_pool));
3651    }
3652
3653  return svn_error_trace(
3654                svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3655}
3656
3657svn_error_t *
3658svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3659                                       svn_wc__db_t *db,
3660                                       const char *local_abspath,
3661                                       svn_boolean_t immediates_only,
3662                                       apr_pool_t *result_pool,
3663                                       apr_pool_t *scratch_pool)
3664{
3665  svn_wc__db_wcroot_t *wcroot;
3666  svn_sqlite__stmt_t *stmt;
3667  const char *local_relpath;
3668  svn_boolean_t have_row;
3669  svn_wc__committable_external_info_t *info;
3670  svn_node_kind_t db_kind;
3671  apr_array_header_t *result = NULL;
3672
3673  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3674
3675  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3676                              local_abspath, scratch_pool, scratch_pool));
3677  VERIFY_USABLE_WCROOT(wcroot);
3678
3679  SVN_ERR(svn_sqlite__get_statement(
3680                &stmt, wcroot->sdb,
3681                immediates_only
3682                    ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3683                    : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3684
3685  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3686
3687  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3688
3689  if (have_row)
3690    result = apr_array_make(result_pool, 0,
3691                            sizeof(svn_wc__committable_external_info_t *));
3692
3693  while (have_row)
3694    {
3695      info = apr_palloc(result_pool, sizeof(*info));
3696
3697      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3698      info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3699                                            result_pool);
3700
3701      db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3702      SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3703      info->kind = db_kind;
3704
3705      info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3706      info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3707
3708      APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3709
3710      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3711    }
3712
3713  *externals = result;
3714  return svn_error_trace(svn_sqlite__reset(stmt));
3715}
3716
3717svn_error_t *
3718svn_wc__db_externals_defined_below(apr_hash_t **externals,
3719                                   svn_wc__db_t *db,
3720                                   const char *local_abspath,
3721                                   apr_pool_t *result_pool,
3722                                   apr_pool_t *scratch_pool)
3723{
3724  svn_wc__db_wcroot_t *wcroot;
3725  svn_sqlite__stmt_t *stmt;
3726  const char *local_relpath;
3727  svn_boolean_t have_row;
3728
3729  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3730
3731  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3732                              local_abspath, scratch_pool, scratch_pool));
3733  VERIFY_USABLE_WCROOT(wcroot);
3734
3735  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3736                                    STMT_SELECT_EXTERNALS_DEFINED));
3737
3738  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3739
3740  *externals = apr_hash_make(result_pool);
3741  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3742
3743  while (have_row)
3744    {
3745      const char *def_local_relpath;
3746
3747      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3748      def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3749
3750      svn_hash_sets(*externals,
3751                    svn_dirent_join(wcroot->abspath, local_relpath,
3752                                    result_pool),
3753                    svn_dirent_join(wcroot->abspath, def_local_relpath,
3754                                    result_pool));
3755
3756      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3757    }
3758
3759  return svn_error_trace(svn_sqlite__reset(stmt));
3760}
3761
3762svn_error_t *
3763svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3764                                        apr_hash_t **depths,
3765                                        svn_wc__db_t *db,
3766                                        const char *local_abspath,
3767                                        apr_pool_t *result_pool,
3768                                        apr_pool_t *scratch_pool)
3769{
3770  svn_wc__db_wcroot_t *wcroot;
3771  svn_sqlite__stmt_t *stmt;
3772  const char *local_relpath;
3773  svn_boolean_t have_row;
3774  svn_error_t *err = NULL;
3775  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3776
3777  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3778
3779  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3780                              local_abspath, scratch_pool, iterpool));
3781  VERIFY_USABLE_WCROOT(wcroot);
3782
3783  *externals = apr_hash_make(result_pool);
3784  if (depths != NULL)
3785    *depths = apr_hash_make(result_pool);
3786
3787  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3788                                    STMT_SELECT_EXTERNAL_PROPERTIES));
3789
3790  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3791
3792  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3793
3794  while (have_row)
3795    {
3796      apr_hash_t *node_props;
3797      const char *external_value;
3798
3799      svn_pool_clear(iterpool);
3800      err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3801                                          iterpool);
3802
3803      if (err)
3804        break;
3805
3806      external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3807
3808      if (external_value)
3809        {
3810          const char *node_abspath;
3811          const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3812
3813          node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3814                                         result_pool);
3815
3816          svn_hash_sets(*externals, node_abspath,
3817                        apr_pstrdup(result_pool, external_value));
3818
3819          if (depths)
3820            {
3821              svn_depth_t depth
3822                = svn_sqlite__column_token_null(stmt, 2, depth_map,
3823                                                svn_depth_unknown);
3824
3825              svn_hash_sets(*depths, node_abspath,
3826                            /* Use static string */
3827                            svn_token__to_word(depth_map, depth));
3828            }
3829        }
3830
3831      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3832    }
3833
3834  svn_pool_destroy(iterpool);
3835
3836  return svn_error_trace(svn_error_compose_create(err,
3837                                                  svn_sqlite__reset(stmt)));
3838}
3839
3840/* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3841   The new ACTUAL data won't have any conflicts. */
3842static svn_error_t *
3843copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3844            const char *src_relpath,
3845            svn_wc__db_wcroot_t *dst_wcroot,
3846            const char *dst_relpath,
3847            apr_pool_t *scratch_pool)
3848{
3849  svn_sqlite__stmt_t *stmt;
3850  svn_boolean_t have_row;
3851
3852  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3853                                    STMT_SELECT_ACTUAL_NODE));
3854  SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3855  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3856  if (have_row)
3857    {
3858      apr_size_t props_size;
3859      const char *changelist;
3860      const char *properties;
3861
3862      /* Skipping conflict data... */
3863      changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3864      /* No need to parse the properties when simply copying. */
3865      properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3866
3867      if (changelist || properties)
3868        {
3869          SVN_ERR(svn_sqlite__reset(stmt));
3870
3871          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3872                                            STMT_INSERT_ACTUAL_NODE));
3873          SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3874                                    dst_wcroot->wc_id, dst_relpath,
3875                                svn_relpath_dirname(dst_relpath, scratch_pool),
3876                                    properties, props_size, changelist));
3877          SVN_ERR(svn_sqlite__step(&have_row, stmt));
3878        }
3879    }
3880  SVN_ERR(svn_sqlite__reset(stmt));
3881
3882  return SVN_NO_ERROR;
3883}
3884
3885/* Helper for svn_wc__db_op_copy to handle copying from one db to
3886   another */
3887static svn_error_t *
3888cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3889              const char *src_relpath,
3890              svn_wc__db_wcroot_t *dst_wcroot,
3891              const char *dst_relpath,
3892              svn_wc__db_status_t dst_status,
3893              int dst_op_depth,
3894              int dst_np_op_depth,
3895              svn_node_kind_t kind,
3896              const apr_array_header_t *children,
3897              apr_int64_t copyfrom_id,
3898              const char *copyfrom_relpath,
3899              svn_revnum_t copyfrom_rev,
3900              apr_pool_t *scratch_pool)
3901{
3902  insert_working_baton_t iwb;
3903  svn_revnum_t changed_rev;
3904  apr_time_t changed_date;
3905  const char *changed_author;
3906  const svn_checksum_t *checksum;
3907  apr_hash_t *props;
3908  svn_depth_t depth;
3909
3910  SVN_ERR_ASSERT(kind == svn_node_file
3911                 || kind == svn_node_dir
3912                 );
3913
3914  SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3915                    &changed_rev, &changed_date, &changed_author, &depth,
3916                    &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3917                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3918                    src_wcroot, src_relpath, scratch_pool, scratch_pool));
3919
3920  if (dst_status != svn_wc__db_status_not_present
3921      && dst_status != svn_wc__db_status_excluded
3922      && dst_status != svn_wc__db_status_server_excluded)
3923    {
3924      SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3925                                     scratch_pool, scratch_pool));
3926    }
3927  else
3928    props = NULL;
3929
3930  blank_iwb(&iwb);
3931  iwb.presence = dst_status;
3932  iwb.kind = kind;
3933
3934  iwb.props = props;
3935  iwb.changed_rev = changed_rev;
3936  iwb.changed_date = changed_date;
3937  iwb.changed_author = changed_author;
3938  iwb.original_repos_id = copyfrom_id;
3939  iwb.original_repos_relpath = copyfrom_relpath;
3940  iwb.original_revnum = copyfrom_rev;
3941  iwb.moved_here = FALSE;
3942
3943  iwb.op_depth = dst_op_depth;
3944
3945  iwb.checksum = checksum;
3946  iwb.children = children;
3947  iwb.depth = depth;
3948
3949  iwb.not_present_op_depth = dst_np_op_depth;
3950
3951  SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3952
3953  SVN_ERR(copy_actual(src_wcroot, src_relpath,
3954                      dst_wcroot, dst_relpath, scratch_pool));
3955
3956  return SVN_NO_ERROR;
3957}
3958
3959/* Helper for scan_deletion_txn. Extracts the moved-to information, if
3960   any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
3961static svn_error_t *
3962get_moved_to(const char **moved_to_relpath_p,
3963             const char **moved_to_op_root_relpath_p,
3964             svn_boolean_t *scan,
3965             svn_sqlite__stmt_t *stmt,
3966             const char *current_relpath,
3967             svn_wc__db_wcroot_t *wcroot,
3968             const char *local_relpath,
3969             apr_pool_t *result_pool,
3970             apr_pool_t *scratch_pool)
3971{
3972  const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3973
3974  if (moved_to_relpath)
3975    {
3976      const char *moved_to_op_root_relpath = moved_to_relpath;
3977
3978      if (strcmp(current_relpath, local_relpath))
3979        {
3980          /* LOCAL_RELPATH is a child inside the move op-root. */
3981          const char *moved_child_relpath;
3982
3983          /* The CURRENT_RELPATH is the op_root of the delete-half of
3984           * the move. LOCAL_RELPATH is a child that was moved along.
3985           * Compute the child's new location within the move target. */
3986          moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3987                                                          local_relpath);
3988          SVN_ERR_ASSERT(moved_child_relpath &&
3989                         strlen(moved_child_relpath) > 0);
3990          moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3991                                              moved_child_relpath,
3992                                              result_pool);
3993        }
3994
3995      if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3996        *moved_to_op_root_relpath_p
3997          = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3998
3999      if (moved_to_relpath && moved_to_relpath_p)
4000        *moved_to_relpath_p
4001          = apr_pstrdup(result_pool, moved_to_relpath);
4002
4003      *scan = FALSE;
4004    }
4005
4006  return SVN_NO_ERROR;
4007}
4008
4009
4010/* The body of svn_wc__db_scan_deletion().
4011 */
4012static svn_error_t *
4013scan_deletion(const char **base_del_relpath,
4014              const char **moved_to_relpath,
4015              const char **work_del_relpath,
4016              const char **moved_to_op_root_relpath,
4017              svn_wc__db_wcroot_t *wcroot,
4018              const char *local_relpath,
4019              apr_pool_t *result_pool,
4020              apr_pool_t *scratch_pool)
4021{
4022  const char *current_relpath = local_relpath;
4023  svn_sqlite__stmt_t *stmt;
4024  svn_wc__db_status_t work_presence;
4025  svn_boolean_t have_row, scan, have_base;
4026  int op_depth;
4027
4028  /* Initialize all the OUT parameters.  */
4029  if (base_del_relpath != NULL)
4030    *base_del_relpath = NULL;
4031  if (moved_to_relpath != NULL)
4032    *moved_to_relpath = NULL;
4033  if (work_del_relpath != NULL)
4034    *work_del_relpath = NULL;
4035  if (moved_to_op_root_relpath != NULL)
4036    *moved_to_op_root_relpath = NULL;
4037
4038  /* If looking for moved-to info then we need to scan every path
4039     until we find it.  If not looking for moved-to we only need to
4040     check op-roots and parents of op-roots. */
4041  scan = (moved_to_op_root_relpath || moved_to_relpath);
4042
4043  SVN_ERR(svn_sqlite__get_statement(
4044                    &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
4045
4046  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
4047  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4048  if (!have_row)
4049    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
4050                             _("The node '%s' was not found."),
4051                             path_for_error_message(wcroot, local_relpath,
4052                                                    scratch_pool));
4053
4054  work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
4055  have_base = !svn_sqlite__column_is_null(stmt, 0);
4056  if (work_presence != svn_wc__db_status_not_present
4057      && work_presence != svn_wc__db_status_base_deleted)
4058    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
4059                             svn_sqlite__reset(stmt),
4060                             _("Expected node '%s' to be deleted."),
4061                             path_for_error_message(wcroot, local_relpath,
4062                                                    scratch_pool));
4063
4064  op_depth = svn_sqlite__column_int(stmt, 2);
4065
4066  /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
4067     treat this as an op-root.  At commit time we need to explicitly
4068     delete such nodes otherwise they will be present in the
4069     repository copy. */
4070  if (work_presence == svn_wc__db_status_not_present
4071      && work_del_relpath && !*work_del_relpath)
4072    {
4073      *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4074
4075      if (!scan && !base_del_relpath)
4076        {
4077          /* We have all we need, exit early */
4078          SVN_ERR(svn_sqlite__reset(stmt));
4079          return SVN_NO_ERROR;
4080        }
4081    }
4082
4083
4084  while (TRUE)
4085    {
4086      svn_error_t *err;
4087      const char *parent_relpath;
4088      int current_depth = relpath_depth(current_relpath);
4089
4090      /* Step CURRENT_RELPATH to op-root */
4091
4092      while (TRUE)
4093        {
4094          if (scan)
4095            {
4096              err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4097                                 &scan, stmt, current_relpath,
4098                                 wcroot, local_relpath,
4099                                 result_pool, scratch_pool);
4100              if (err || (!scan
4101                          && !base_del_relpath
4102                          && !work_del_relpath))
4103                {
4104                  /* We have all we need (or an error occurred) */
4105                  SVN_ERR(svn_sqlite__reset(stmt));
4106                  return svn_error_trace(err);
4107                }
4108            }
4109
4110          if (current_depth <= op_depth)
4111            break;
4112
4113          current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4114          --current_depth;
4115
4116          if (scan || current_depth == op_depth)
4117            {
4118              SVN_ERR(svn_sqlite__reset(stmt));
4119              SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4120                                        current_relpath));
4121              SVN_ERR(svn_sqlite__step(&have_row, stmt));
4122              SVN_ERR_ASSERT(have_row);
4123              have_base = !svn_sqlite__column_is_null(stmt, 0);
4124            }
4125        }
4126      SVN_ERR(svn_sqlite__reset(stmt));
4127
4128      /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4129
4130      SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4131      parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4132      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4133      SVN_ERR(svn_sqlite__step(&have_row, stmt));
4134      if (!have_row)
4135        {
4136          /* No row means no WORKING node which mean we just fell off
4137             the WORKING tree, so CURRENT_RELPATH is the op-root
4138             closest to the wc root. */
4139          if (have_base && base_del_relpath)
4140            *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4141          break;
4142        }
4143
4144      /* Still in the WORKING tree so the first time we get here
4145         CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4146      if (work_del_relpath && !*work_del_relpath)
4147        {
4148          *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4149
4150          if (!scan && !base_del_relpath)
4151            break; /* We have all we need */
4152        }
4153
4154      current_relpath = parent_relpath;
4155      op_depth = svn_sqlite__column_int(stmt, 2);
4156      have_base = !svn_sqlite__column_is_null(stmt, 0);
4157    }
4158
4159  SVN_ERR(svn_sqlite__reset(stmt));
4160
4161  return SVN_NO_ERROR;
4162}
4163
4164svn_error_t *
4165svn_wc__db_scan_deletion_internal(
4166              const char **base_del_relpath,
4167              const char **moved_to_relpath,
4168              const char **work_del_relpath,
4169              const char **moved_to_op_root_relpath,
4170              svn_wc__db_wcroot_t *wcroot,
4171              const char *local_relpath,
4172              apr_pool_t *result_pool,
4173              apr_pool_t *scratch_pool)
4174{
4175  return svn_error_trace(
4176            scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
4177                          moved_to_op_root_relpath,
4178                          wcroot, local_relpath,
4179                          result_pool, scratch_pool));
4180}
4181
4182
4183svn_error_t *
4184svn_wc__db_scan_deletion(const char **base_del_abspath,
4185                         const char **moved_to_abspath,
4186                         const char **work_del_abspath,
4187                         const char **moved_to_op_root_abspath,
4188                         svn_wc__db_t *db,
4189                         const char *local_abspath,
4190                         apr_pool_t *result_pool,
4191                         apr_pool_t *scratch_pool)
4192{
4193  svn_wc__db_wcroot_t *wcroot;
4194  const char *local_relpath;
4195  const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4196  const char *moved_to_op_root_relpath;
4197
4198  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4199
4200  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4201                              local_abspath, scratch_pool, scratch_pool));
4202  VERIFY_USABLE_WCROOT(wcroot);
4203
4204  SVN_WC__DB_WITH_TXN(
4205    scan_deletion(&base_del_relpath, &moved_to_relpath,
4206                  &work_del_relpath, &moved_to_op_root_relpath,
4207                  wcroot, local_relpath, result_pool, scratch_pool),
4208    wcroot);
4209
4210  if (base_del_abspath)
4211    {
4212      *base_del_abspath = (base_del_relpath
4213                           ? svn_dirent_join(wcroot->abspath,
4214                                             base_del_relpath, result_pool)
4215                           : NULL);
4216    }
4217  if (moved_to_abspath)
4218    {
4219      *moved_to_abspath = (moved_to_relpath
4220                           ? svn_dirent_join(wcroot->abspath,
4221                                             moved_to_relpath, result_pool)
4222                           : NULL);
4223    }
4224  if (work_del_abspath)
4225    {
4226      *work_del_abspath = (work_del_relpath
4227                           ? svn_dirent_join(wcroot->abspath,
4228                                             work_del_relpath, result_pool)
4229                           : NULL);
4230    }
4231  if (moved_to_op_root_abspath)
4232    {
4233      *moved_to_op_root_abspath = (moved_to_op_root_relpath
4234                           ? svn_dirent_join(wcroot->abspath,
4235                                             moved_to_op_root_relpath,
4236                                             result_pool)
4237                           : NULL);
4238    }
4239
4240  return SVN_NO_ERROR;
4241}
4242
4243
4244/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4245   appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4246   since they are available.  This is a helper for
4247   svn_wc__db_op_copy. */
4248static svn_error_t *
4249get_info_for_copy(apr_int64_t *copyfrom_id,
4250                  const char **copyfrom_relpath,
4251                  svn_revnum_t *copyfrom_rev,
4252                  svn_wc__db_status_t *status,
4253                  svn_node_kind_t *kind,
4254                  svn_boolean_t *op_root,
4255                  svn_wc__db_wcroot_t *src_wcroot,
4256                  const char *local_relpath,
4257                  svn_wc__db_wcroot_t *dst_wcroot,
4258                  apr_pool_t *result_pool,
4259                  apr_pool_t *scratch_pool)
4260{
4261  const char *repos_relpath;
4262  svn_revnum_t revision;
4263  svn_wc__db_status_t node_status;
4264  apr_int64_t repos_id;
4265  svn_boolean_t is_op_root;
4266
4267  SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4268                    NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4269                    copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4270                    NULL, &is_op_root, NULL, NULL,
4271                    NULL /* have_base */,
4272                    NULL /* have_more_work */,
4273                    NULL /* have_work */,
4274                    src_wcroot, local_relpath, result_pool, scratch_pool));
4275
4276  if (op_root)
4277    *op_root = is_op_root;
4278
4279  if (node_status == svn_wc__db_status_excluded)
4280    {
4281      /* The parent cannot be excluded, so look at the parent and then
4282         adjust the relpath */
4283      const char *parent_relpath, *base_name;
4284
4285      svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4286                       scratch_pool);
4287      SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4288                                NULL, NULL, NULL,
4289                                src_wcroot, parent_relpath, dst_wcroot,
4290                                scratch_pool, scratch_pool));
4291      if (*copyfrom_relpath)
4292        *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4293                                             result_pool);
4294    }
4295  else if (node_status == svn_wc__db_status_added)
4296    {
4297      SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4298                            NULL, NULL, NULL, src_wcroot, local_relpath,
4299                            scratch_pool, scratch_pool));
4300    }
4301  else if (node_status == svn_wc__db_status_deleted && is_op_root)
4302    {
4303      const char *base_del_relpath, *work_del_relpath;
4304
4305      SVN_ERR(scan_deletion(&base_del_relpath, NULL,
4306                            &work_del_relpath,
4307                            NULL, src_wcroot, local_relpath,
4308                            scratch_pool, scratch_pool));
4309      if (work_del_relpath)
4310        {
4311          const char *op_root_relpath;
4312          const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4313                                                               scratch_pool);
4314
4315          /* Similar to, but not the same as, the _scan_addition and
4316             _join above.  Can we use get_copyfrom here? */
4317          SVN_ERR(scan_addition(NULL, &op_root_relpath,
4318                                NULL, NULL, /* repos_* */
4319                                copyfrom_relpath, copyfrom_id, copyfrom_rev,
4320                                NULL, NULL, NULL,
4321                                src_wcroot, parent_del_relpath,
4322                                scratch_pool, scratch_pool));
4323          *copyfrom_relpath
4324            = svn_relpath_join(*copyfrom_relpath,
4325                               svn_relpath_skip_ancestor(op_root_relpath,
4326                                                         local_relpath),
4327                               result_pool);
4328        }
4329      else if (base_del_relpath)
4330        {
4331          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4332                                                    copyfrom_relpath,
4333                                                    copyfrom_id, NULL, NULL,
4334                                                    NULL, NULL, NULL, NULL,
4335                                                    NULL, NULL, NULL, NULL,
4336                                                    src_wcroot, local_relpath,
4337                                                    result_pool,
4338                                                    scratch_pool));
4339        }
4340      else
4341        SVN_ERR_MALFUNCTION();
4342    }
4343  else if (node_status == svn_wc__db_status_deleted)
4344    {
4345      /* Keep original_* from read_info() to allow seeing the difference
4346         between base-deleted and not present */
4347    }
4348  else
4349    {
4350      *copyfrom_relpath = repos_relpath;
4351      *copyfrom_rev = revision;
4352      *copyfrom_id = repos_id;
4353    }
4354
4355  if (status)
4356    *status = node_status;
4357
4358  if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4359    {
4360      const char *repos_root_url;
4361      const char *repos_uuid;
4362
4363      /* Pass the right repos-id for the destination db. We can't just use
4364         the id of the source database, as this value can change after
4365         relocation (and perhaps also when we start storing multiple
4366         working copies in a single db)! */
4367
4368      SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4369                                          src_wcroot, *copyfrom_id,
4370                                          scratch_pool));
4371
4372      SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4373                              dst_wcroot->sdb, scratch_pool));
4374    }
4375
4376  return SVN_NO_ERROR;
4377}
4378
4379
4380/* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4381static svn_error_t *
4382op_depth_of(int *op_depth,
4383            svn_wc__db_wcroot_t *wcroot,
4384            const char *local_relpath)
4385{
4386  svn_sqlite__stmt_t *stmt;
4387  svn_boolean_t have_row;
4388
4389  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4390                                    STMT_SELECT_NODE_INFO));
4391  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4392  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4393  SVN_ERR_ASSERT(have_row);
4394  *op_depth = svn_sqlite__column_int(stmt, 0);
4395  SVN_ERR(svn_sqlite__reset(stmt));
4396
4397  return SVN_NO_ERROR;
4398}
4399
4400
4401/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4402   revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4403   by checking if this would be a direct child of a copy of its parent
4404   directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4405
4406   If the node is not a direct copy at the same revision of the parent
4407   *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4408   node should be inserted at this op_depth. This will be the case when the
4409   parent already defined an incomplete child with the same name. Otherwise
4410   *NP_OP_DEPTH will be set to -1.
4411
4412   If the parent node is not the parent of the to be copied node, then
4413   *OP_DEPTH will be set to the proper op_depth for a new operation root.
4414
4415   Set *PARENT_OP_DEPTH to the op_depth of the parent.
4416
4417 */
4418static svn_error_t *
4419op_depth_for_copy(int *op_depth,
4420                  int *np_op_depth,
4421                  int *parent_op_depth,
4422                  apr_int64_t copyfrom_repos_id,
4423                  const char *copyfrom_relpath,
4424                  svn_revnum_t copyfrom_revision,
4425                  svn_wc__db_wcroot_t *wcroot,
4426                  const char *local_relpath,
4427                  apr_pool_t *scratch_pool)
4428{
4429  const char *parent_relpath, *name;
4430  svn_sqlite__stmt_t *stmt;
4431  svn_boolean_t have_row;
4432  int incomplete_op_depth = -1;
4433  int min_op_depth = 1; /* Never touch BASE */
4434
4435  *op_depth = relpath_depth(local_relpath);
4436  *np_op_depth = -1;
4437
4438  svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4439  *parent_op_depth = relpath_depth(parent_relpath);
4440
4441  if (!copyfrom_relpath)
4442    return SVN_NO_ERROR;
4443
4444  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4445                                    STMT_SELECT_WORKING_NODE));
4446  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4447  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4448  if (have_row)
4449    {
4450      svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4451                                                            presence_map);
4452
4453      min_op_depth = svn_sqlite__column_int(stmt, 0);
4454      if (status == svn_wc__db_status_incomplete)
4455        incomplete_op_depth = min_op_depth;
4456    }
4457  SVN_ERR(svn_sqlite__reset(stmt));
4458
4459  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4460                                    STMT_SELECT_WORKING_NODE));
4461  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4462  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4463  if (have_row)
4464    {
4465      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4466                                                              presence_map);
4467
4468      *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4469      if (*parent_op_depth < min_op_depth)
4470        {
4471          /* We want to create a copy; not overwrite the lower layers */
4472          SVN_ERR(svn_sqlite__reset(stmt));
4473          return SVN_NO_ERROR;
4474        }
4475
4476      /* You can only add children below a node that exists.
4477         In WORKING that must be status added, which is represented
4478         as presence normal */
4479      SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4480
4481      if ((incomplete_op_depth < 0)
4482          || (incomplete_op_depth == *parent_op_depth))
4483        {
4484          apr_int64_t parent_copyfrom_repos_id
4485            = svn_sqlite__column_int64(stmt, 10);
4486          const char *parent_copyfrom_relpath
4487            = svn_sqlite__column_text(stmt, 11, NULL);
4488          svn_revnum_t parent_copyfrom_revision
4489            = svn_sqlite__column_revnum(stmt, 12);
4490
4491          if (parent_copyfrom_repos_id == copyfrom_repos_id)
4492            {
4493              if (copyfrom_revision == parent_copyfrom_revision
4494                  && !strcmp(copyfrom_relpath,
4495                             svn_relpath_join(parent_copyfrom_relpath, name,
4496                                              scratch_pool)))
4497                *op_depth = *parent_op_depth;
4498              else if (incomplete_op_depth > 0)
4499                *np_op_depth = incomplete_op_depth;
4500            }
4501        }
4502    }
4503  SVN_ERR(svn_sqlite__reset(stmt));
4504
4505  return SVN_NO_ERROR;
4506}
4507
4508
4509/* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4510 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4511 * copy operation is part of a move, and indicates the op-depth of the
4512 * move destination op-root. */
4513static svn_error_t *
4514db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4515           const char *src_relpath,
4516           svn_wc__db_wcroot_t *dst_wcroot,
4517           const char *dst_relpath,
4518           const svn_skel_t *work_items,
4519           int move_op_depth,
4520           apr_pool_t *scratch_pool)
4521{
4522  const char *copyfrom_relpath;
4523  svn_revnum_t copyfrom_rev;
4524  svn_wc__db_status_t status;
4525  svn_wc__db_status_t dst_presence;
4526  svn_boolean_t op_root;
4527  apr_int64_t copyfrom_id;
4528  int dst_op_depth;
4529  int dst_np_op_depth;
4530  int dst_parent_op_depth;
4531  svn_node_kind_t kind;
4532  const apr_array_header_t *children;
4533
4534  SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
4535                            &status, &kind, &op_root,
4536                            src_wcroot, src_relpath, dst_wcroot,
4537                            scratch_pool, scratch_pool));
4538
4539  SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4540                            &dst_parent_op_depth,
4541                            copyfrom_id, copyfrom_relpath, copyfrom_rev,
4542                            dst_wcroot, dst_relpath, scratch_pool));
4543
4544  SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4545
4546  /* ### New status, not finished, see notes/wc-ng/copying */
4547  switch (status)
4548    {
4549    case svn_wc__db_status_normal:
4550    case svn_wc__db_status_added:
4551    case svn_wc__db_status_moved_here:
4552    case svn_wc__db_status_copied:
4553      dst_presence = svn_wc__db_status_normal;
4554      break;
4555    case svn_wc__db_status_deleted:
4556      if (op_root)
4557        {
4558          /* If the lower layer is already shadowcopied we can skip adding
4559             a not present node. */
4560          svn_error_t *err;
4561          svn_wc__db_status_t dst_status;
4562
4563          err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4564                          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4565                          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4566                          dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4567
4568          if (err)
4569            {
4570              if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4571                svn_error_clear(err);
4572              else
4573                return svn_error_trace(err);
4574            }
4575          else if (dst_status == svn_wc__db_status_deleted)
4576            {
4577              /* Node is already deleted; skip the NODES work, but do
4578                 install wq items if requested */
4579              SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4580                                     scratch_pool));
4581              return SVN_NO_ERROR;
4582            }
4583        }
4584      else
4585        {
4586          /* This node is either a not-present node (which should be copied), or
4587             a base-delete of some lower layer (which shouldn't).
4588             Subversion <= 1.7 always added a not-present node here, which is
4589             safe (as it postpones the hard work until commit time and then we
4590             ask the repository), but it breaks some move scenarios.
4591             */
4592
4593           if (! copyfrom_relpath)
4594             {
4595               SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4596                                     scratch_pool));
4597               return SVN_NO_ERROR;
4598             }
4599
4600           /* Fall through. Install not present node */
4601        }
4602    case svn_wc__db_status_not_present:
4603    case svn_wc__db_status_excluded:
4604      /* These presence values should not create a new op depth */
4605      if (dst_np_op_depth > 0)
4606        {
4607          dst_op_depth = dst_np_op_depth;
4608          dst_np_op_depth = -1;
4609        }
4610      if (status == svn_wc__db_status_excluded)
4611        dst_presence = svn_wc__db_status_excluded;
4612      else
4613        dst_presence = svn_wc__db_status_not_present;
4614      break;
4615    case svn_wc__db_status_server_excluded:
4616      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4617                               _("Cannot copy '%s' excluded by server"),
4618                               path_for_error_message(src_wcroot,
4619                                                      src_relpath,
4620                                                      scratch_pool));
4621    default:
4622      /* Perhaps we should allow incomplete to incomplete? We can't
4623         avoid incomplete working nodes as one step in copying a
4624         directory is to add incomplete children. */
4625      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4626                               _("Cannot handle status of '%s'"),
4627                               path_for_error_message(src_wcroot,
4628                                                      src_relpath,
4629                                                      scratch_pool));
4630    }
4631
4632  if (kind == svn_node_dir)
4633    {
4634      int src_op_depth;
4635
4636      SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4637      SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
4638                              STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
4639                              scratch_pool, scratch_pool));
4640    }
4641  else
4642    children = NULL;
4643
4644  if (src_wcroot == dst_wcroot)
4645    {
4646      svn_sqlite__stmt_t *stmt;
4647      const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4648                                                           scratch_pool);
4649
4650      SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4651                                        STMT_INSERT_WORKING_NODE_COPY_FROM));
4652
4653      SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4654                    src_wcroot->wc_id, src_relpath,
4655                    dst_relpath,
4656                    dst_op_depth,
4657                    dst_parent_relpath,
4658                    presence_map, dst_presence));
4659
4660      if (move_op_depth > 0)
4661        {
4662          if (relpath_depth(dst_relpath) == move_op_depth)
4663            {
4664              /* We're moving the root of the move operation.
4665               *
4666               * When an added node or the op-root of a copy is moved,
4667               * there is no 'moved-from' corresponding to the moved-here
4668               * node. So the net effect is the same as copy+delete.
4669               * Perform a normal copy operation in these cases. */
4670              if (!(status == svn_wc__db_status_added ||
4671                    (status == svn_wc__db_status_copied && op_root)))
4672                SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4673            }
4674          else
4675            {
4676              svn_sqlite__stmt_t *info_stmt;
4677              svn_boolean_t have_row;
4678
4679              /* We're moving a child along with the root of the move.
4680               *
4681               * Set moved-here depending on dst_parent, propagating the
4682               * above decision to moved-along children at the same op_depth.
4683               * We can't use scan_addition() to detect moved-here because
4684               * the delete-half of the move might not yet exist. */
4685              SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4686                                                STMT_SELECT_NODE_INFO));
4687              SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4688                                        dst_parent_relpath));
4689              SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4690              SVN_ERR_ASSERT(have_row);
4691              if (svn_sqlite__column_boolean(info_stmt, 15) &&
4692                  dst_op_depth == dst_parent_op_depth)
4693                {
4694                  SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4695                  SVN_ERR(svn_sqlite__reset(info_stmt));
4696                }
4697              else
4698                {
4699                  SVN_ERR(svn_sqlite__reset(info_stmt));
4700
4701                  /* If the child has been moved into the tree we're moving,
4702                   * keep its moved-here bit set. */
4703                  SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4704                                                    dst_wcroot->sdb,
4705                                                    STMT_SELECT_NODE_INFO));
4706                  SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4707                                            dst_wcroot->wc_id, src_relpath));
4708                  SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4709                  SVN_ERR_ASSERT(have_row);
4710                  if (svn_sqlite__column_boolean(info_stmt, 15))
4711                    SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4712                  SVN_ERR(svn_sqlite__reset(info_stmt));
4713                }
4714            }
4715        }
4716
4717      SVN_ERR(svn_sqlite__step_done(stmt));
4718
4719      /* ### Copying changelist is OK for a move but what about a copy? */
4720      SVN_ERR(copy_actual(src_wcroot, src_relpath,
4721                          dst_wcroot, dst_relpath, scratch_pool));
4722
4723      if (dst_np_op_depth > 0)
4724        {
4725          /* We introduce a not-present node at the parent's op_depth to
4726             properly start a new op-depth at our own op_depth. This marks
4727             us as an op_root for commit and allows reverting just this
4728             operation */
4729
4730          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4731                                            STMT_INSERT_NODE));
4732          SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4733                                    src_wcroot->wc_id, dst_relpath,
4734                                    dst_np_op_depth, dst_parent_relpath,
4735                                    copyfrom_id, copyfrom_relpath,
4736                                    copyfrom_rev,
4737                                    presence_map,
4738                                       svn_wc__db_status_not_present,
4739                                    /* NULL */
4740                                    kind_map, kind));
4741
4742          SVN_ERR(svn_sqlite__step_done(stmt));
4743        }
4744      /* Insert incomplete children, if relevant.
4745         The children are part of the same op and so have the same op_depth.
4746         (The only time we'd want a different depth is during a recursive
4747         simple add, but we never insert children here during a simple add.) */
4748      if (kind == svn_node_dir
4749          && dst_presence == svn_wc__db_status_normal)
4750        SVN_ERR(insert_incomplete_children(
4751                  dst_wcroot->sdb,
4752                  dst_wcroot->wc_id,
4753                  dst_relpath,
4754                  copyfrom_id,
4755                  copyfrom_relpath,
4756                  copyfrom_rev,
4757                  children,
4758                  dst_op_depth,
4759                  scratch_pool));
4760    }
4761  else
4762    {
4763      SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4764                            dst_relpath, dst_presence, dst_op_depth,
4765                            dst_np_op_depth, kind,
4766                            children, copyfrom_id, copyfrom_relpath,
4767                            copyfrom_rev, scratch_pool));
4768    }
4769
4770  SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4771
4772  return SVN_NO_ERROR;
4773}
4774
4775/* Baton for passing args to op_copy_txn(). */
4776struct op_copy_baton
4777{
4778  svn_wc__db_wcroot_t *src_wcroot;
4779  const char *src_relpath;
4780
4781  svn_wc__db_wcroot_t *dst_wcroot;
4782  const char *dst_relpath;
4783
4784  const svn_skel_t *work_items;
4785
4786  svn_boolean_t is_move;
4787  const char *dst_op_root_relpath;
4788};
4789
4790/* Helper for svn_wc__db_op_copy(). */
4791static svn_error_t *
4792op_copy_txn(svn_wc__db_wcroot_t *wcroot,
4793            struct op_copy_baton *ocb,
4794            apr_pool_t *scratch_pool)
4795{
4796  int move_op_depth;
4797
4798  if (wcroot != ocb->dst_wcroot)
4799    {
4800       /* Source and destination databases differ; so also start a lock
4801          in the destination database, by calling ourself in an extra lock. */
4802
4803      SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool),
4804                          ocb->dst_wcroot);
4805
4806      return SVN_NO_ERROR;
4807    }
4808
4809  /* From this point we can assume a lock in the src and dst databases */
4810
4811  if (ocb->is_move)
4812    move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4813  else
4814    move_op_depth = 0;
4815
4816  SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4817                     ocb->dst_wcroot, ocb->dst_relpath,
4818                     ocb->work_items, move_op_depth, scratch_pool));
4819
4820  return SVN_NO_ERROR;
4821}
4822
4823svn_error_t *
4824svn_wc__db_op_copy(svn_wc__db_t *db,
4825                   const char *src_abspath,
4826                   const char *dst_abspath,
4827                   const char *dst_op_root_abspath,
4828                   svn_boolean_t is_move,
4829                   const svn_skel_t *work_items,
4830                   apr_pool_t *scratch_pool)
4831{
4832  struct op_copy_baton ocb = {0};
4833
4834  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4835  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4836  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4837
4838  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4839                                                &ocb.src_relpath, db,
4840                                                src_abspath,
4841                                                scratch_pool, scratch_pool));
4842  VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4843
4844  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4845                                                &ocb.dst_relpath,
4846                                                db, dst_abspath,
4847                                                scratch_pool, scratch_pool));
4848  VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4849
4850  ocb.work_items = work_items;
4851  ocb.is_move = is_move;
4852  ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4853                                                     dst_op_root_abspath);
4854
4855  /* Call with the sdb in src_wcroot. It might call itself again to
4856     also obtain a lock in dst_wcroot */
4857  SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool),
4858                      ocb.src_wcroot);
4859
4860  return SVN_NO_ERROR;
4861}
4862
4863/* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
4864static svn_error_t *
4865clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
4866                       const char *local_relpath,
4867                       int op_depth,
4868                       apr_pool_t *scratch_pool)
4869{
4870  svn_sqlite__stmt_t *stmt;
4871  svn_boolean_t have_row, shadowed;
4872  svn_boolean_t keep_conflict = FALSE;
4873
4874  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4875                                    STMT_SELECT_NODE_INFO));
4876
4877  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4878  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4879
4880  if (have_row)
4881    {
4882      svn_wc__db_status_t presence;
4883
4884      shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4885      presence = svn_sqlite__column_token(stmt, 3, presence_map);
4886
4887      if (shadowed && presence == svn_wc__db_status_base_deleted)
4888        {
4889          keep_conflict = TRUE;
4890          SVN_ERR(svn_sqlite__step(&have_row, stmt));
4891
4892          if (have_row)
4893            shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4894          else
4895            shadowed = FALSE;
4896        }
4897    }
4898  else
4899    shadowed = FALSE;
4900
4901  SVN_ERR(svn_sqlite__reset(stmt));
4902  if (shadowed)
4903    return SVN_NO_ERROR;
4904
4905  if (keep_conflict)
4906    {
4907      /* We don't want to accidentally remove delete-delete conflicts */
4908      SVN_ERR(svn_sqlite__get_statement(
4909                          &stmt, wcroot->sdb,
4910                          STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
4911      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4912      SVN_ERR(svn_sqlite__step_done(stmt));
4913      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4914                                        STMT_DELETE_ACTUAL_EMPTY));
4915      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4916      SVN_ERR(svn_sqlite__step_done(stmt));
4917    }
4918  else
4919    {
4920      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4921                                        STMT_DELETE_ACTUAL_NODE));
4922      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4923      SVN_ERR(svn_sqlite__step_done(stmt));
4924    }
4925
4926  return SVN_NO_ERROR;
4927}
4928
4929svn_error_t *
4930svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
4931                                  const char *src_op_relpath,
4932                                  int src_op_depth,
4933                                  const char *dst_op_relpath,
4934                                  svn_skel_t *conflict,
4935                                  svn_skel_t *work_items,
4936                                  apr_pool_t *scratch_pool)
4937{
4938  svn_sqlite__stmt_t *stmt, *stmt2;
4939  svn_boolean_t have_row;
4940  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4941  int dst_op_depth = relpath_depth(dst_op_relpath);
4942  svn_boolean_t locked;
4943  svn_error_t *err = NULL;
4944
4945  SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
4946                                               FALSE, scratch_pool));
4947
4948  if (!locked)
4949    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
4950                             _("No write-lock in '%s'"),
4951                             path_for_error_message(wcroot, dst_op_relpath,
4952                                                    scratch_pool));
4953
4954  SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
4955                                    STMT_COPY_NODE_MOVE));
4956
4957  /* Replace entire subtree at one op-depth. */
4958  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4959                                    STMT_SELECT_LAYER_FOR_REPLACE));
4960  SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
4961                            src_op_relpath, src_op_depth,
4962                            dst_op_relpath, dst_op_depth));
4963  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4964  while (have_row)
4965    {
4966      const char *src_relpath;
4967      const char *dst_relpath;
4968
4969      svn_pool_clear(iterpool);
4970
4971      src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
4972      dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
4973
4974      err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
4975                              src_relpath, src_op_depth,
4976                              dst_relpath, dst_op_depth,
4977                              svn_relpath_dirname(dst_relpath, iterpool));
4978      if (!err)
4979        err = svn_sqlite__step_done(stmt2);
4980
4981      /* stmt2 is reset (never modified or by step_done) */
4982
4983      if (err)
4984        break;
4985
4986      /* The node can't be deleted where it is added, so extension of
4987         an existing shadowing is only interesting 2 levels deep. */
4988      if (relpath_depth(dst_relpath) > (dst_op_depth+1))
4989        {
4990          svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
4991
4992          if (exists)
4993            {
4994              svn_wc__db_status_t presence;
4995
4996              presence = svn_sqlite__column_token(stmt, 3, presence_map);
4997
4998              if (presence != svn_wc__db_status_normal)
4999                exists = FALSE;
5000            }
5001
5002          if (!exists)
5003            {
5004              svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
5005
5006              err = db_extend_parent_delete(wcroot, dst_relpath,
5007                                            kind, dst_op_depth, iterpool);
5008
5009              if (err)
5010                break;
5011            }
5012        }
5013
5014      SVN_ERR(svn_sqlite__step(&have_row, stmt));
5015    }
5016
5017  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5018
5019  /* And now remove the records that are no longer needed */
5020  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5021                                    STMT_SELECT_NO_LONGER_MOVED_RV));
5022  SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
5023                            dst_op_relpath, dst_op_depth,
5024                            src_op_relpath, src_op_depth));
5025  SVN_ERR(svn_sqlite__step(&have_row, stmt));
5026  while (have_row)
5027    {
5028      const char *dst_relpath;
5029      svn_wc__db_status_t shadowed_presence;
5030
5031      svn_pool_clear(iterpool);
5032
5033      dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
5034
5035      if (!svn_sqlite__column_is_null(stmt, 2))
5036        shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
5037      else
5038        shadowed_presence = svn_wc__db_status_not_present;
5039
5040      if (shadowed_presence != svn_wc__db_status_normal
5041          && shadowed_presence != svn_wc__db_status_incomplete)
5042        {
5043          err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5044                                            STMT_DELETE_NODE);
5045        }
5046      else
5047        {
5048          err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5049                                         STMT_REPLACE_WITH_BASE_DELETED);
5050        }
5051
5052      if (!err)
5053        err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
5054                                             dst_op_depth);
5055
5056      if (!err)
5057        err = svn_sqlite__step_done(stmt2);
5058
5059      /* stmt2 is reset (never modified or by step_done) */
5060      if (err)
5061        break;
5062
5063      /* Delete ACTUAL information about this node that we just deleted */
5064      err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
5065                                   scratch_pool);
5066
5067      if (err)
5068        break;
5069
5070      /* Retract base-delete for the node itself */
5071      err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
5072                                     scratch_pool);
5073
5074      if (err)
5075        break;
5076
5077      SVN_ERR(svn_sqlite__step(&have_row, stmt));
5078    }
5079  svn_pool_destroy(iterpool);
5080
5081  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5082
5083  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5084
5085  if (conflict)
5086    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
5087                                              conflict, scratch_pool));
5088
5089  return SVN_NO_ERROR;
5090}
5091
5092/* The txn body of svn_wc__db_op_handle_move_back */
5093static svn_error_t *
5094handle_move_back(svn_boolean_t *moved_back,
5095                 svn_wc__db_wcroot_t *wcroot,
5096                 const char *local_relpath,
5097                 const char *moved_from_relpath,
5098                 const svn_skel_t *work_items,
5099                 apr_pool_t *scratch_pool)
5100{
5101  svn_sqlite__stmt_t *stmt;
5102  svn_wc__db_status_t status;
5103  svn_boolean_t op_root;
5104  svn_boolean_t have_more_work;
5105  int from_op_depth = 0;
5106  svn_boolean_t have_row;
5107  svn_boolean_t different = FALSE;
5108
5109  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5110
5111  SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
5112                                        NULL, NULL, NULL, NULL, NULL, NULL,
5113                                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5114                                        &op_root, NULL, NULL, NULL,
5115                                        &have_more_work, NULL,
5116                                        wcroot, local_relpath,
5117                                        scratch_pool, scratch_pool));
5118
5119  if (status != svn_wc__db_status_added || !op_root)
5120    return SVN_NO_ERROR;
5121
5122  /* We have two cases here: BASE-move-back and WORKING-move-back */
5123  if (have_more_work)
5124    SVN_ERR(op_depth_of(&from_op_depth, wcroot,
5125                        svn_relpath_dirname(local_relpath, scratch_pool)));
5126  else
5127    from_op_depth = 0;
5128
5129  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5130                                    STMT_SELECT_MOVED_BACK));
5131
5132  SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
5133                                          local_relpath,
5134                                          from_op_depth,
5135                                          relpath_depth(local_relpath)));
5136
5137  SVN_ERR(svn_sqlite__step(&have_row, stmt));
5138
5139  SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
5140
5141  {
5142    svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
5143    const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
5144
5145    if (!moved_here
5146        || !moved_to
5147        || strcmp(moved_to, moved_from_relpath))
5148      {
5149        different = TRUE;
5150        have_row = FALSE;
5151      }
5152  }
5153
5154  while (have_row)
5155    {
5156      svn_wc__db_status_t upper_status;
5157      svn_wc__db_status_t lower_status;
5158
5159      upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
5160
5161      if (svn_sqlite__column_is_null(stmt, 5))
5162        {
5163          /* No lower layer replaced. */
5164          if (upper_status != svn_wc__db_status_not_present)
5165            {
5166              different = TRUE;
5167              break;
5168            }
5169          continue;
5170        }
5171
5172      lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
5173
5174      if (upper_status != lower_status)
5175        {
5176          different = TRUE;
5177          break;
5178        }
5179
5180      if (upper_status == svn_wc__db_status_not_present
5181          || upper_status == svn_wc__db_status_excluded)
5182        {
5183          SVN_ERR(svn_sqlite__step(&have_row, stmt));
5184          continue; /* Nothing to check */
5185        }
5186      else if (upper_status != svn_wc__db_status_normal)
5187        {
5188          /* Not a normal move. Mixed revision move? */
5189          different = TRUE;
5190          break;
5191        }
5192
5193      {
5194        const char *upper_repos_relpath;
5195        const char *lower_repos_relpath;
5196
5197        upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
5198        lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
5199
5200        if (! upper_repos_relpath
5201            || strcmp(upper_repos_relpath, lower_repos_relpath))
5202          {
5203            different = TRUE;
5204            break;
5205          }
5206      }
5207
5208      {
5209        svn_revnum_t upper_rev;
5210        svn_revnum_t lower_rev;
5211
5212        upper_rev = svn_sqlite__column_revnum(stmt, 4);
5213        lower_rev = svn_sqlite__column_revnum(stmt, 8);
5214
5215        if (upper_rev != lower_rev)
5216          {
5217            different = TRUE;
5218            break;
5219          }
5220      }
5221
5222      {
5223        apr_int64_t upper_repos_id;
5224        apr_int64_t lower_repos_id;
5225
5226        upper_repos_id = svn_sqlite__column_int64(stmt, 2);
5227        lower_repos_id = svn_sqlite__column_int64(stmt, 6);
5228
5229        if (upper_repos_id != lower_repos_id)
5230          {
5231            different = TRUE;
5232            break;
5233          }
5234      }
5235
5236      /* Check moved_here? */
5237
5238      SVN_ERR(svn_sqlite__step(&have_row, stmt));
5239    }
5240  SVN_ERR(svn_sqlite__reset(stmt));
5241
5242  if (! different)
5243    {
5244      /* Ok, we can now safely remove this complete move, because we
5245         determined that it 100% matches the layer below it. */
5246
5247      /* ### We could copy the recorded timestamps from the higher to the
5248             lower layer in an attempt to improve status performance, but
5249             generally these values should be the same anyway as it was
5250             a no-op move. */
5251      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5252                                        STMT_DELETE_WORKING_OP_DEPTH));
5253
5254      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
5255                                             local_relpath,
5256                                             relpath_depth(local_relpath)));
5257
5258      SVN_ERR(svn_sqlite__step_done(stmt));
5259
5260      if (moved_back)
5261        *moved_back = TRUE;
5262    }
5263
5264  return SVN_NO_ERROR;
5265}
5266
5267svn_error_t *
5268svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
5269                               svn_wc__db_t *db,
5270                               const char *local_abspath,
5271                               const char *moved_from_abspath,
5272                               const svn_skel_t *work_items,
5273                               apr_pool_t *scratch_pool)
5274{
5275  svn_wc__db_wcroot_t *wcroot;
5276  const char *local_relpath;
5277  const char *moved_from_relpath;
5278  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5279
5280  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5281                                                local_abspath,
5282                                                scratch_pool, scratch_pool));
5283  VERIFY_USABLE_WCROOT(wcroot);
5284
5285  if (moved_back)
5286    *moved_back = FALSE;
5287
5288  moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
5289                                                moved_from_abspath);
5290
5291  if (! local_relpath[0]
5292      || !moved_from_relpath)
5293    {
5294       /* WC-Roots can't be moved */
5295      SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5296      return SVN_NO_ERROR;
5297    }
5298
5299  SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
5300                                       moved_from_relpath, work_items,
5301                                       scratch_pool),
5302                      wcroot);
5303
5304  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5305                        scratch_pool));
5306
5307  return SVN_NO_ERROR;
5308}
5309
5310
5311/* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
5312 *
5313 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
5314 * a move, and indicates the op-depth of the move destination op-root. */
5315static svn_error_t *
5316db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
5317                          const char *src_relpath,
5318                          int src_op_depth,
5319                          svn_wc__db_wcroot_t *dst_wcroot,
5320                          const char *dst_relpath,
5321                          int dst_op_depth,
5322                          int del_op_depth,
5323                          apr_int64_t repos_id,
5324                          const char *repos_relpath,
5325                          svn_revnum_t revision,
5326                          int move_op_depth,
5327                          apr_pool_t *scratch_pool)
5328{
5329  const apr_array_header_t *children;
5330  apr_pool_t *iterpool;
5331  svn_wc__db_status_t status;
5332  svn_node_kind_t kind;
5333  svn_revnum_t node_revision;
5334  const char *node_repos_relpath;
5335  apr_int64_t node_repos_id;
5336  svn_sqlite__stmt_t *stmt;
5337  svn_wc__db_status_t dst_presence;
5338  int i;
5339
5340  {
5341    svn_error_t *err;
5342    err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5343                                    &node_repos_relpath, &node_repos_id,
5344                                    NULL, NULL, NULL, NULL, NULL, NULL,
5345                                    NULL, NULL,
5346                                    src_wcroot, src_relpath, src_op_depth,
5347                                    scratch_pool, scratch_pool);
5348
5349    if (err)
5350      {
5351        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5352          return svn_error_trace(err);
5353
5354        svn_error_clear(err);
5355        return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5356      }
5357  }
5358
5359  if (src_op_depth == 0)
5360    {
5361      /* If the node is switched or has a different revision then its parent
5362         we shouldn't copy it. (We can't as we would have to insert it at
5363         an unshadowed depth) */
5364      if (status == svn_wc__db_status_not_present
5365          || status == svn_wc__db_status_excluded
5366          || status == svn_wc__db_status_server_excluded
5367          || node_revision != revision
5368          || node_repos_id != repos_id
5369          || strcmp(node_repos_relpath, repos_relpath))
5370        {
5371          /* Add a not-present node in the destination wcroot */
5372          struct insert_working_baton_t iwb;
5373          const char *repos_root_url;
5374          const char *repos_uuid;
5375
5376          SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5377                                              src_wcroot, node_repos_id,
5378                                              scratch_pool));
5379
5380          SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5381                                  dst_wcroot->sdb, scratch_pool));
5382
5383          blank_iwb(&iwb);
5384
5385          iwb.op_depth = dst_op_depth;
5386          if (status != svn_wc__db_status_excluded)
5387            iwb.presence = svn_wc__db_status_not_present;
5388          else
5389            iwb.presence = svn_wc__db_status_excluded;
5390
5391          iwb.kind = kind;
5392
5393          iwb.original_repos_id = node_repos_id;
5394          iwb.original_revnum = node_revision;
5395          iwb.original_repos_relpath = node_repos_relpath;
5396
5397          SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5398                                      scratch_pool));
5399
5400          return SVN_NO_ERROR;
5401        }
5402    }
5403
5404  iterpool = svn_pool_create(scratch_pool);
5405
5406  switch (status)
5407    {
5408    case svn_wc__db_status_normal:
5409    case svn_wc__db_status_added:
5410    case svn_wc__db_status_moved_here:
5411    case svn_wc__db_status_copied:
5412      dst_presence = svn_wc__db_status_normal;
5413      break;
5414    case svn_wc__db_status_deleted:
5415    case svn_wc__db_status_not_present:
5416      dst_presence = svn_wc__db_status_not_present;
5417      break;
5418    case svn_wc__db_status_excluded:
5419      dst_presence = svn_wc__db_status_excluded;
5420      break;
5421    case svn_wc__db_status_server_excluded:
5422      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5423                               _("Cannot copy '%s' excluded by server"),
5424                               path_for_error_message(src_wcroot,
5425                                                      src_relpath,
5426                                                      scratch_pool));
5427    default:
5428      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5429                               _("Cannot handle status of '%s'"),
5430                               path_for_error_message(src_wcroot,
5431                                                      src_relpath,
5432                                                      scratch_pool));
5433    }
5434
5435  if (dst_presence == svn_wc__db_status_normal
5436      && src_wcroot == dst_wcroot) /* ### Remove limitation */
5437    {
5438      SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5439                             STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5440
5441      SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5442                        src_wcroot->wc_id, src_relpath,
5443                        dst_relpath,
5444                        dst_op_depth,
5445                        svn_relpath_dirname(dst_relpath, iterpool),
5446                        presence_map, dst_presence,
5447                        src_op_depth));
5448
5449      /* moved_here */
5450      if (dst_op_depth == move_op_depth)
5451        SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5452
5453      SVN_ERR(svn_sqlite__step_done(stmt));
5454
5455      {
5456        /* And mark it deleted to allow proper shadowing */
5457        struct insert_working_baton_t iwb;
5458
5459        blank_iwb(&iwb);
5460
5461        iwb.op_depth = del_op_depth;
5462        iwb.presence = svn_wc__db_status_base_deleted;
5463
5464        iwb.kind = kind;
5465
5466        SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5467                                    scratch_pool));
5468      }
5469    }
5470  else
5471    {
5472      struct insert_working_baton_t iwb;
5473      if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5474        dst_presence = svn_wc__db_status_not_present;
5475
5476      /* And mark it deleted to allow proper shadowing */
5477
5478      blank_iwb(&iwb);
5479
5480      iwb.op_depth = dst_op_depth;
5481      iwb.presence = dst_presence;
5482      iwb.kind = kind;
5483
5484      SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5485                                    scratch_pool));
5486    }
5487
5488  if (dst_presence == svn_wc__db_status_not_present)
5489    {
5490      /* Don't create descendants of a not present node! */
5491
5492      /* This code is currently still triggered by copying deleted nodes
5493         between separate working copies. See ### comment above. */
5494
5495      svn_pool_destroy(iterpool);
5496      return SVN_NO_ERROR;
5497    }
5498
5499  SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
5500                          STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
5501                          scratch_pool, iterpool));
5502
5503  for (i = 0; i < children->nelts; i++)
5504    {
5505      const char *name = APR_ARRAY_IDX(children, i, const char *);
5506      const char *child_src_relpath;
5507      const char *child_dst_relpath;
5508      const char *child_repos_relpath = NULL;
5509
5510      svn_pool_clear(iterpool);
5511      child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5512      child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5513
5514      if (repos_relpath)
5515        child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5516
5517      SVN_ERR(db_op_copy_shadowed_layer(
5518                         src_wcroot, child_src_relpath, src_op_depth,
5519                         dst_wcroot, child_dst_relpath, dst_op_depth,
5520                         del_op_depth,
5521                         repos_id, child_repos_relpath, revision,
5522                         move_op_depth, scratch_pool));
5523    }
5524
5525  svn_pool_destroy(iterpool);
5526
5527  return SVN_NO_ERROR;
5528}
5529
5530/* Helper for svn_wc__db_op_copy_shadowed_layer(). */
5531static svn_error_t *
5532op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot,
5533                           struct op_copy_baton *ocb,
5534                           apr_pool_t *scratch_pool)
5535{
5536  const char *src_parent_relpath;
5537  const char *dst_parent_relpath;
5538  int src_op_depth;
5539  int dst_op_depth;
5540  int del_op_depth;
5541  const char *repos_relpath = NULL;
5542  apr_int64_t repos_id = INVALID_REPOS_ID;
5543  svn_revnum_t revision = SVN_INVALID_REVNUM;
5544
5545  if (wcroot != ocb->dst_wcroot)
5546    {
5547      /* Source and destination databases differ; so also start a lock
5548         in the destination database, by calling ourself in an extra lock. */
5549
5550      SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb,
5551                                                     scratch_pool),
5552                          ocb->dst_wcroot);
5553
5554      return SVN_NO_ERROR;
5555    }
5556
5557  /* From this point we can assume a lock in the src and dst databases */
5558
5559
5560  /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5561  SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5562
5563  src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5564  dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5565
5566  /* src_parent must be status normal or added; get its op-depth */
5567  SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5568
5569  /* dst_parent must be status added; get its op-depth */
5570  SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5571
5572  del_op_depth = relpath_depth(ocb->dst_relpath);
5573
5574  /* Get some information from the parent */
5575  SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5576                                    &repos_id, NULL, NULL, NULL, NULL, NULL,
5577                                    NULL, NULL, NULL,
5578                                    ocb->src_wcroot,
5579                                    src_parent_relpath, src_op_depth,
5580                                    scratch_pool, scratch_pool));
5581
5582  if (repos_relpath == NULL)
5583    {
5584      /* The node is a local addition and has no shadowed information */
5585      return SVN_NO_ERROR;
5586    }
5587
5588  /* And calculate the child repos relpath */
5589  repos_relpath = svn_relpath_join(repos_relpath,
5590                                   svn_relpath_basename(ocb->src_relpath,
5591                                                        NULL),
5592                                   scratch_pool);
5593
5594  SVN_ERR(db_op_copy_shadowed_layer(
5595                        ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5596                        ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5597                        del_op_depth,
5598                        repos_id, repos_relpath, revision,
5599                        (ocb->is_move ? dst_op_depth : 0),
5600                        scratch_pool));
5601
5602  return SVN_NO_ERROR;
5603}
5604
5605svn_error_t *
5606svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5607                                  const char *src_abspath,
5608                                  const char *dst_abspath,
5609                                  svn_boolean_t is_move,
5610                                  apr_pool_t *scratch_pool)
5611{
5612  struct op_copy_baton ocb = {0};
5613
5614  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5615  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5616
5617  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5618                                                &ocb.src_relpath, db,
5619                                                src_abspath,
5620                                                scratch_pool, scratch_pool));
5621  VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5622
5623  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5624                                                &ocb.dst_relpath,
5625                                                db, dst_abspath,
5626                                                scratch_pool, scratch_pool));
5627  VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5628
5629  ocb.is_move = is_move;
5630  ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5631
5632  ocb.work_items = NULL;
5633
5634  /* Call with the sdb in src_wcroot. It might call itself again to
5635     also obtain a lock in dst_wcroot */
5636  SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb,
5637                                                 scratch_pool),
5638                      ocb.src_wcroot);
5639
5640  return SVN_NO_ERROR;
5641}
5642
5643
5644/* If there are any server-excluded base nodes then the copy must fail
5645   as it's not possible to commit such a copy.
5646   Return an error if there are any server-excluded nodes. */
5647static svn_error_t *
5648catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5649                              const char *local_relpath,
5650                              apr_pool_t *scratch_pool)
5651{
5652  svn_sqlite__stmt_t *stmt;
5653  svn_boolean_t have_row;
5654  const char *server_excluded_relpath;
5655
5656  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5657                                    STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5658  SVN_ERR(svn_sqlite__bindf(stmt, "is",
5659                            wcroot->wc_id,
5660                            local_relpath));
5661  SVN_ERR(svn_sqlite__step(&have_row, stmt));
5662  if (have_row)
5663    server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5664  SVN_ERR(svn_sqlite__reset(stmt));
5665  if (have_row)
5666    return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5667                             _("Cannot copy '%s' excluded by server"),
5668                             path_for_error_message(wcroot,
5669                                                    server_excluded_relpath,
5670                                                    scratch_pool));
5671
5672  return SVN_NO_ERROR;
5673}
5674
5675
5676svn_error_t *
5677svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5678                       const char *local_abspath,
5679                       const apr_hash_t *props,
5680                       svn_revnum_t changed_rev,
5681                       apr_time_t changed_date,
5682                       const char *changed_author,
5683                       const char *original_repos_relpath,
5684                       const char *original_root_url,
5685                       const char *original_uuid,
5686                       svn_revnum_t original_revision,
5687                       const apr_array_header_t *children,
5688                       svn_depth_t depth,
5689                       svn_boolean_t is_move,
5690                       const svn_skel_t *conflict,
5691                       const svn_skel_t *work_items,
5692                       apr_pool_t *scratch_pool)
5693{
5694  svn_wc__db_wcroot_t *wcroot;
5695  const char *local_relpath;
5696  insert_working_baton_t iwb;
5697  int parent_op_depth;
5698
5699  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5700  SVN_ERR_ASSERT(props != NULL);
5701  /* ### any assertions for CHANGED_* ?  */
5702  /* ### any assertions for ORIGINAL_* ?  */
5703#if 0
5704  SVN_ERR_ASSERT(children != NULL);
5705#endif
5706
5707  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5708                              local_abspath, scratch_pool, scratch_pool));
5709  VERIFY_USABLE_WCROOT(wcroot);
5710
5711  blank_iwb(&iwb);
5712
5713  iwb.presence = svn_wc__db_status_normal;
5714  iwb.kind = svn_node_dir;
5715
5716  if (original_root_url != NULL)
5717    {
5718      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5719                              original_root_url, original_uuid,
5720                              wcroot->sdb, scratch_pool));
5721      iwb.original_repos_relpath = original_repos_relpath;
5722      iwb.original_revnum = original_revision;
5723
5724      iwb.props = props;
5725      iwb.changed_rev = changed_rev;
5726      iwb.changed_date = changed_date;
5727      iwb.changed_author = changed_author;
5728    }
5729
5730  /* ### Should we do this inside the transaction? */
5731  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5732                            &parent_op_depth, iwb.original_repos_id,
5733                            original_repos_relpath, original_revision,
5734                            wcroot, local_relpath, scratch_pool));
5735
5736  iwb.children = children;
5737  iwb.depth = depth;
5738  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5739                               iwb.op_depth == parent_op_depth);
5740
5741  iwb.work_items = work_items;
5742  iwb.conflict = conflict;
5743
5744  SVN_WC__DB_WITH_TXN(
5745                insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5746                wcroot);
5747  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5748
5749  return SVN_NO_ERROR;
5750}
5751
5752
5753svn_error_t *
5754svn_wc__db_op_copy_file(svn_wc__db_t *db,
5755                        const char *local_abspath,
5756                        const apr_hash_t *props,
5757                        svn_revnum_t changed_rev,
5758                        apr_time_t changed_date,
5759                        const char *changed_author,
5760                        const char *original_repos_relpath,
5761                        const char *original_root_url,
5762                        const char *original_uuid,
5763                        svn_revnum_t original_revision,
5764                        const svn_checksum_t *checksum,
5765                        svn_boolean_t update_actual_props,
5766                        const apr_hash_t *new_actual_props,
5767                        svn_boolean_t is_move,
5768                        const svn_skel_t *conflict,
5769                        const svn_skel_t *work_items,
5770                        apr_pool_t *scratch_pool)
5771{
5772  svn_wc__db_wcroot_t *wcroot;
5773  const char *local_relpath;
5774  insert_working_baton_t iwb;
5775  int parent_op_depth;
5776
5777  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5778  SVN_ERR_ASSERT(props != NULL);
5779  /* ### any assertions for CHANGED_* ?  */
5780  SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5781                  && ! original_uuid && ! checksum
5782                  && original_revision == SVN_INVALID_REVNUM)
5783                 || (original_repos_relpath && original_root_url
5784                     && original_uuid && checksum
5785                     && original_revision != SVN_INVALID_REVNUM));
5786
5787  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5788                              local_abspath, scratch_pool, scratch_pool));
5789  VERIFY_USABLE_WCROOT(wcroot);
5790
5791  blank_iwb(&iwb);
5792
5793  iwb.presence = svn_wc__db_status_normal;
5794  iwb.kind = svn_node_file;
5795
5796  if (original_root_url != NULL)
5797    {
5798      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5799                              original_root_url, original_uuid,
5800                              wcroot->sdb, scratch_pool));
5801      iwb.original_repos_relpath = original_repos_relpath;
5802      iwb.original_revnum = original_revision;
5803
5804      iwb.props = props;
5805      iwb.changed_rev = changed_rev;
5806      iwb.changed_date = changed_date;
5807      iwb.changed_author = changed_author;
5808    }
5809
5810  /* ### Should we do this inside the transaction? */
5811  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5812                            &parent_op_depth, iwb.original_repos_id,
5813                            original_repos_relpath, original_revision,
5814                            wcroot, local_relpath, scratch_pool));
5815
5816  iwb.checksum = checksum;
5817  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5818                               iwb.op_depth == parent_op_depth);
5819
5820  if (update_actual_props)
5821    {
5822      iwb.update_actual_props = update_actual_props;
5823      iwb.new_actual_props = new_actual_props;
5824    }
5825
5826  iwb.work_items = work_items;
5827  iwb.conflict = conflict;
5828
5829  SVN_WC__DB_WITH_TXN(
5830          insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5831          wcroot);
5832  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5833
5834  return SVN_NO_ERROR;
5835}
5836
5837
5838svn_error_t *
5839svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5840                           const char *local_abspath,
5841                           const apr_hash_t *props,
5842                           svn_revnum_t changed_rev,
5843                           apr_time_t changed_date,
5844                           const char *changed_author,
5845                           const char *original_repos_relpath,
5846                           const char *original_root_url,
5847                           const char *original_uuid,
5848                           svn_revnum_t original_revision,
5849                           const char *target,
5850                           svn_boolean_t is_move,
5851                           const svn_skel_t *conflict,
5852                           const svn_skel_t *work_items,
5853                           apr_pool_t *scratch_pool)
5854{
5855  svn_wc__db_wcroot_t *wcroot;
5856  const char *local_relpath;
5857  insert_working_baton_t iwb;
5858  int parent_op_depth;
5859
5860  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5861  SVN_ERR_ASSERT(props != NULL);
5862  /* ### any assertions for CHANGED_* ?  */
5863  /* ### any assertions for ORIGINAL_* ?  */
5864  SVN_ERR_ASSERT(target != NULL);
5865
5866  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5867                              local_abspath, scratch_pool, scratch_pool));
5868  VERIFY_USABLE_WCROOT(wcroot);
5869
5870  blank_iwb(&iwb);
5871
5872  iwb.presence = svn_wc__db_status_normal;
5873  iwb.kind = svn_node_symlink;
5874
5875
5876  if (original_root_url != NULL)
5877    {
5878      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5879                              original_root_url, original_uuid,
5880                              wcroot->sdb, scratch_pool));
5881      iwb.original_repos_relpath = original_repos_relpath;
5882      iwb.original_revnum = original_revision;
5883
5884      iwb.props = props;
5885      iwb.changed_rev = changed_rev;
5886      iwb.changed_date = changed_date;
5887      iwb.changed_author = changed_author;
5888    }
5889
5890  /* ### Should we do this inside the transaction? */
5891  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5892                            &parent_op_depth, iwb.original_repos_id,
5893                            original_repos_relpath, original_revision,
5894                            wcroot, local_relpath, scratch_pool));
5895
5896  iwb.target = target;
5897  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5898                               iwb.op_depth == parent_op_depth);
5899
5900  iwb.work_items = work_items;
5901  iwb.conflict = conflict;
5902
5903  SVN_WC__DB_WITH_TXN(
5904            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5905            wcroot);
5906  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5907
5908  return SVN_NO_ERROR;
5909}
5910
5911
5912svn_error_t *
5913svn_wc__db_op_add_directory(svn_wc__db_t *db,
5914                            const char *local_abspath,
5915                            const apr_hash_t *props,
5916                            const svn_skel_t *work_items,
5917                            apr_pool_t *scratch_pool)
5918{
5919  svn_wc__db_wcroot_t *wcroot;
5920  const char *local_relpath;
5921  const char *dir_abspath;
5922  const char *name;
5923  insert_working_baton_t iwb;
5924
5925  /* Resolve wcroot via parent directory to avoid obstruction handling */
5926  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5927  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5928
5929  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5930                              dir_abspath, scratch_pool, scratch_pool));
5931  VERIFY_USABLE_WCROOT(wcroot);
5932
5933  blank_iwb(&iwb);
5934
5935  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5936  iwb.presence = svn_wc__db_status_normal;
5937  iwb.kind = svn_node_dir;
5938  iwb.op_depth = relpath_depth(local_relpath);
5939  if (props && apr_hash_count((apr_hash_t *)props))
5940    {
5941      iwb.update_actual_props = TRUE;
5942      iwb.new_actual_props = props;
5943    }
5944
5945  iwb.work_items = work_items;
5946
5947  SVN_WC__DB_WITH_TXN(
5948            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5949            wcroot);
5950  /* Use depth infinity to make sure we have no invalid cached information
5951   * about children of this dir. */
5952  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5953                        scratch_pool));
5954
5955  return SVN_NO_ERROR;
5956}
5957
5958
5959svn_error_t *
5960svn_wc__db_op_add_file(svn_wc__db_t *db,
5961                       const char *local_abspath,
5962                       const apr_hash_t *props,
5963                       const svn_skel_t *work_items,
5964                       apr_pool_t *scratch_pool)
5965{
5966  svn_wc__db_wcroot_t *wcroot;
5967  const char *local_relpath;
5968  insert_working_baton_t iwb;
5969  const char *dir_abspath;
5970  const char *name;
5971
5972  /* Resolve wcroot via parent directory to avoid obstruction handling */
5973  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5974  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5975
5976  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5977                              dir_abspath, scratch_pool, scratch_pool));
5978  VERIFY_USABLE_WCROOT(wcroot);
5979
5980  blank_iwb(&iwb);
5981
5982  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5983  iwb.presence = svn_wc__db_status_normal;
5984  iwb.kind = svn_node_file;
5985  iwb.op_depth = relpath_depth(local_relpath);
5986  if (props && apr_hash_count((apr_hash_t *)props))
5987    {
5988      iwb.update_actual_props = TRUE;
5989      iwb.new_actual_props = props;
5990    }
5991
5992  iwb.work_items = work_items;
5993
5994  SVN_WC__DB_WITH_TXN(
5995            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5996            wcroot);
5997  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5998
5999  return SVN_NO_ERROR;
6000}
6001
6002
6003svn_error_t *
6004svn_wc__db_op_add_symlink(svn_wc__db_t *db,
6005                          const char *local_abspath,
6006                          const char *target,
6007                          const apr_hash_t *props,
6008                          const svn_skel_t *work_items,
6009                          apr_pool_t *scratch_pool)
6010{
6011  svn_wc__db_wcroot_t *wcroot;
6012  const char *local_relpath;
6013  insert_working_baton_t iwb;
6014  const char *dir_abspath;
6015  const char *name;
6016
6017  /* Resolve wcroot via parent directory to avoid obstruction handling */
6018  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6019  SVN_ERR_ASSERT(target != NULL);
6020
6021  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
6022
6023  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6024                              dir_abspath, scratch_pool, scratch_pool));
6025
6026  VERIFY_USABLE_WCROOT(wcroot);
6027
6028  blank_iwb(&iwb);
6029
6030  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
6031  iwb.presence = svn_wc__db_status_normal;
6032  iwb.kind = svn_node_symlink;
6033  iwb.op_depth = relpath_depth(local_relpath);
6034  if (props && apr_hash_count((apr_hash_t *)props))
6035    {
6036      iwb.update_actual_props = TRUE;
6037      iwb.new_actual_props = props;
6038    }
6039
6040  iwb.target = target;
6041
6042  iwb.work_items = work_items;
6043
6044  SVN_WC__DB_WITH_TXN(
6045            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
6046            wcroot);
6047  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6048
6049  return SVN_NO_ERROR;
6050}
6051
6052/* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
6053static svn_error_t *
6054db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
6055                   const char *local_relpath,
6056                   apr_int64_t recorded_size,
6057                   apr_int64_t recorded_time,
6058                   apr_pool_t *scratch_pool)
6059{
6060  svn_sqlite__stmt_t *stmt;
6061  int affected_rows;
6062
6063  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6064                                    STMT_UPDATE_NODE_FILEINFO));
6065  SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
6066                            recorded_size, recorded_time));
6067  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6068
6069  SVN_ERR_ASSERT(affected_rows == 1);
6070
6071  return SVN_NO_ERROR;
6072}
6073
6074
6075svn_error_t *
6076svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
6077                                  const char *local_abspath,
6078                                  svn_filesize_t recorded_size,
6079                                  apr_time_t recorded_time,
6080                                  apr_pool_t *scratch_pool)
6081{
6082  svn_wc__db_wcroot_t *wcroot;
6083  const char *local_relpath;
6084
6085  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6086
6087  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6088                              local_abspath, scratch_pool, scratch_pool));
6089  VERIFY_USABLE_WCROOT(wcroot);
6090
6091  SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6092                             recorded_size, recorded_time, scratch_pool));
6093
6094  /* We *totally* monkeyed the entries. Toss 'em.  */
6095  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6096
6097  return SVN_NO_ERROR;
6098}
6099
6100
6101/* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
6102 * PROPS.
6103 *
6104 * Note: PROPS=NULL means the actual props are the same as the pristine
6105 * props; to indicate no properties when the pristine has some props,
6106 * PROPS must be an empty hash. */
6107static svn_error_t *
6108set_actual_props(svn_wc__db_wcroot_t *wcroot,
6109                 const char *local_relpath,
6110                 apr_hash_t *props,
6111                 apr_pool_t *scratch_pool)
6112{
6113  svn_sqlite__stmt_t *stmt;
6114  int affected_rows;
6115
6116  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6117                                    STMT_UPDATE_ACTUAL_PROPS));
6118  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6119  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
6120  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6121
6122  if (affected_rows == 1 || !props)
6123    {
6124      /* Perhaps the entire ACTUAL record is unneeded now? */
6125      if (!props && affected_rows)
6126        {
6127          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6128                                            STMT_DELETE_ACTUAL_EMPTY));
6129          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6130          SVN_ERR(svn_sqlite__step_done(stmt));
6131        }
6132
6133      return SVN_NO_ERROR; /* We are done */
6134    }
6135
6136  /* We have to insert a row in ACTUAL */
6137
6138  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6139                                    STMT_INSERT_ACTUAL_PROPS));
6140  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6141  if (*local_relpath != '\0')
6142    SVN_ERR(svn_sqlite__bind_text(stmt, 3,
6143                                  svn_relpath_dirname(local_relpath,
6144                                                      scratch_pool)));
6145  SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
6146  return svn_error_trace(svn_sqlite__step_done(stmt));
6147}
6148
6149svn_error_t *
6150svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot,
6151                                 const char *local_relpath,
6152                                 apr_hash_t *props,
6153                                 svn_boolean_t clear_recorded_info,
6154                                 apr_pool_t *scratch_pool)
6155{
6156  SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
6157
6158  if (clear_recorded_info)
6159    {
6160      SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6161                                 SVN_INVALID_FILESIZE, 0,
6162                                 scratch_pool));
6163    }
6164
6165  return SVN_NO_ERROR;
6166}
6167
6168/* The body of svn_wc__db_op_set_props().
6169
6170   Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
6171   Create an entry in the ACTUAL table for the node if it does not yet
6172   have one.
6173   To specify no properties, BATON->props must be an empty hash, not NULL.
6174   BATON is of type 'struct set_props_baton_t'.
6175*/
6176static svn_error_t *
6177set_props_txn(svn_wc__db_wcroot_t *wcroot,
6178              const char *local_relpath,
6179              apr_hash_t *props,
6180              svn_boolean_t clear_recorded_info,
6181              const svn_skel_t *conflict,
6182              const svn_skel_t *work_items,
6183              apr_pool_t *scratch_pool)
6184{
6185  apr_hash_t *pristine_props;
6186
6187  /* Check if the props are modified. If no changes, then wipe out the
6188     ACTUAL props.  PRISTINE_PROPS==NULL means that any
6189     ACTUAL props are okay as provided, so go ahead and set them.  */
6190  SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
6191                                 scratch_pool, scratch_pool));
6192  if (props && pristine_props)
6193    {
6194      apr_array_header_t *prop_diffs;
6195
6196      SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
6197                             scratch_pool));
6198      if (prop_diffs->nelts == 0)
6199        props = NULL;
6200    }
6201
6202  SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props,
6203                                           clear_recorded_info, scratch_pool));
6204
6205  /* And finally.  */
6206  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6207  if (conflict)
6208    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6209                                              conflict, scratch_pool));
6210
6211  return SVN_NO_ERROR;
6212}
6213
6214
6215svn_error_t *
6216svn_wc__db_op_set_props(svn_wc__db_t *db,
6217                        const char *local_abspath,
6218                        apr_hash_t *props,
6219                        svn_boolean_t clear_recorded_info,
6220                        const svn_skel_t *conflict,
6221                        const svn_skel_t *work_items,
6222                        apr_pool_t *scratch_pool)
6223{
6224  svn_wc__db_wcroot_t *wcroot;
6225  const char *local_relpath;
6226
6227  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6228
6229  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6230                              db, local_abspath, scratch_pool, scratch_pool));
6231  VERIFY_USABLE_WCROOT(wcroot);
6232
6233  SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
6234                                    clear_recorded_info, conflict, work_items,
6235                                    scratch_pool),
6236                      wcroot);
6237  return SVN_NO_ERROR;
6238}
6239
6240
6241svn_error_t *
6242svn_wc__db_op_modified(svn_wc__db_t *db,
6243                       const char *local_abspath,
6244                       apr_pool_t *scratch_pool)
6245{
6246  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6247
6248  NOT_IMPLEMENTED();
6249}
6250
6251/* */
6252static svn_error_t *
6253populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
6254                      const char *local_relpath,
6255                      svn_depth_t depth,
6256                      const apr_array_header_t *changelist_filter,
6257                      apr_pool_t *scratch_pool)
6258{
6259  svn_sqlite__stmt_t *stmt;
6260  int affected_rows = 0;
6261  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6262                                      STMT_CREATE_TARGETS_LIST));
6263
6264  if (changelist_filter && changelist_filter->nelts > 0)
6265    {
6266      /* Iterate over the changelists, adding the nodes which match.
6267         Common case: we only have one changelist, so this only
6268         happens once. */
6269      int i;
6270      int stmt_idx;
6271
6272      switch (depth)
6273        {
6274          case svn_depth_empty:
6275            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
6276            break;
6277
6278          case svn_depth_files:
6279            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
6280            break;
6281
6282          case svn_depth_immediates:
6283            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
6284            break;
6285
6286          case svn_depth_infinity:
6287            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
6288            break;
6289
6290          default:
6291            /* We don't know how to handle unknown or exclude. */
6292            SVN_ERR_MALFUNCTION();
6293            break;
6294        }
6295
6296      for (i = 0; i < changelist_filter->nelts; i++)
6297        {
6298          int sub_affected;
6299          const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
6300                                                 const char *);
6301
6302          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6303                                        STMT_INSERT_TARGET_WITH_CHANGELIST));
6304          SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6305                                    local_relpath, changelist));
6306          SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6307
6308          /* If the root is matched by the changelist, we don't have to match
6309             the children. As that tells us the root is a file */
6310          if (!sub_affected && depth > svn_depth_empty)
6311            {
6312              SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6313              SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6314                                        local_relpath, changelist));
6315              SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6316            }
6317
6318          affected_rows += sub_affected;
6319        }
6320    }
6321  else /* No changelist filtering */
6322    {
6323      int stmt_idx;
6324      int sub_affected;
6325
6326      switch (depth)
6327        {
6328          case svn_depth_empty:
6329            stmt_idx = STMT_INSERT_TARGET;
6330            break;
6331
6332          case svn_depth_files:
6333            stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
6334            break;
6335
6336          case svn_depth_immediates:
6337            stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
6338            break;
6339
6340          case svn_depth_infinity:
6341            stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
6342            break;
6343
6344          default:
6345            /* We don't know how to handle unknown or exclude. */
6346            SVN_ERR_MALFUNCTION();
6347            break;
6348        }
6349
6350      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6351                                        STMT_INSERT_TARGET));
6352      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6353      SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6354      affected_rows += sub_affected;
6355
6356      if (depth > svn_depth_empty)
6357        {
6358          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6359          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6360          SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6361          affected_rows += sub_affected;
6362        }
6363    }
6364
6365  /* Does the target exist? */
6366  if (affected_rows == 0)
6367    {
6368      svn_boolean_t exists;
6369      SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6370
6371      if (!exists)
6372        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6373                                 _("The node '%s' was not found."),
6374                                 path_for_error_message(wcroot,
6375                                                        local_relpath,
6376                                                        scratch_pool));
6377    }
6378
6379  return SVN_NO_ERROR;
6380}
6381
6382
6383#if 0
6384static svn_error_t *
6385dump_targets(svn_wc__db_wcroot_t *wcroot,
6386             apr_pool_t *scratch_pool)
6387{
6388  svn_sqlite__stmt_t *stmt;
6389  svn_boolean_t have_row;
6390
6391  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6392                                    STMT_SELECT_TARGETS));
6393  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6394  while (have_row)
6395    {
6396      const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6397      SVN_DBG(("Target: '%s'\n", target));
6398      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6399    }
6400
6401  SVN_ERR(svn_sqlite__reset(stmt));
6402
6403  return SVN_NO_ERROR;
6404}
6405#endif
6406
6407
6408struct set_changelist_baton_t
6409{
6410  const char *new_changelist;
6411  const apr_array_header_t *changelist_filter;
6412  svn_depth_t depth;
6413};
6414
6415
6416/* The main part of svn_wc__db_op_set_changelist().
6417 *
6418 * Implements svn_wc__db_txn_callback_t. */
6419static svn_error_t *
6420set_changelist_txn(void *baton,
6421                   svn_wc__db_wcroot_t *wcroot,
6422                   const char *local_relpath,
6423                   apr_pool_t *scratch_pool)
6424{
6425  struct set_changelist_baton_t *scb = baton;
6426  svn_sqlite__stmt_t *stmt;
6427
6428  SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6429                                scb->changelist_filter, scratch_pool));
6430
6431  /* Ensure we have actual nodes for our targets. */
6432  if (scb->new_changelist)
6433    {
6434      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6435                                        STMT_INSERT_ACTUAL_EMPTIES_FILES));
6436      SVN_ERR(svn_sqlite__step_done(stmt));
6437    }
6438
6439  /* Now create our notification table. */
6440  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6441                                      STMT_CREATE_CHANGELIST_LIST));
6442  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6443                                      STMT_CREATE_CHANGELIST_TRIGGER));
6444
6445  /* Update our changelists. */
6446  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6447                                    STMT_UPDATE_ACTUAL_CHANGELISTS));
6448  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6449                            scb->new_changelist));
6450  SVN_ERR(svn_sqlite__step_done(stmt));
6451
6452  if (scb->new_changelist)
6453    {
6454      /* We have to notify that we skipped directories, so do that now. */
6455      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6456                                        STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6457      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6458                                scb->new_changelist));
6459      SVN_ERR(svn_sqlite__step_done(stmt));
6460    }
6461
6462  /* We may have left empty ACTUAL nodes, so remove them.  This is only a
6463     potential problem if we removed changelists. */
6464  if (!scb->new_changelist)
6465    {
6466      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467                                        STMT_DELETE_ACTUAL_EMPTIES));
6468      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469      SVN_ERR(svn_sqlite__step_done(stmt));
6470    }
6471
6472  return SVN_NO_ERROR;
6473}
6474
6475
6476/* Send notifications for svn_wc__db_op_set_changelist().
6477 *
6478 * Implements work_callback_t. */
6479static svn_error_t *
6480do_changelist_notify(void *baton,
6481                     svn_wc__db_wcroot_t *wcroot,
6482                     svn_cancel_func_t cancel_func,
6483                     void *cancel_baton,
6484                     svn_wc_notify_func2_t notify_func,
6485                     void *notify_baton,
6486                     apr_pool_t *scratch_pool)
6487{
6488  svn_sqlite__stmt_t *stmt;
6489  svn_boolean_t have_row;
6490  apr_pool_t *iterpool;
6491
6492  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6493                                    STMT_SELECT_CHANGELIST_LIST));
6494  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6495
6496  iterpool = svn_pool_create(scratch_pool);
6497  while (have_row)
6498    {
6499      /* ### wc_id is column 0. use it one day...  */
6500      const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6501      svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6502      svn_wc_notify_t *notify;
6503      const char *notify_abspath;
6504
6505      svn_pool_clear(iterpool);
6506
6507      if (cancel_func)
6508        {
6509          svn_error_t *err = cancel_func(cancel_baton);
6510
6511          if (err)
6512            return svn_error_trace(svn_error_compose_create(
6513                                                    err,
6514                                                    svn_sqlite__reset(stmt)));
6515        }
6516
6517      notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6518                                       iterpool);
6519      notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6520      notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6521      notify_func(notify_baton, notify, iterpool);
6522
6523      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6524    }
6525  svn_pool_destroy(iterpool);
6526
6527  return svn_error_trace(svn_sqlite__reset(stmt));
6528}
6529
6530
6531svn_error_t *
6532svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6533                             const char *local_abspath,
6534                             const char *new_changelist,
6535                             const apr_array_header_t *changelist_filter,
6536                             svn_depth_t depth,
6537                             svn_wc_notify_func2_t notify_func,
6538                             void *notify_baton,
6539                             svn_cancel_func_t cancel_func,
6540                             void *cancel_baton,
6541                             apr_pool_t *scratch_pool)
6542{
6543  svn_wc__db_wcroot_t *wcroot;
6544  const char *local_relpath;
6545  struct set_changelist_baton_t scb;
6546
6547  scb.new_changelist = new_changelist;
6548  scb.changelist_filter = changelist_filter;
6549  scb.depth = depth;
6550
6551  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6552
6553  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6554                                                db, local_abspath,
6555                                                scratch_pool, scratch_pool));
6556  VERIFY_USABLE_WCROOT(wcroot);
6557
6558  /* Flush the entries before we do the work. Even if no work is performed,
6559     the flush isn't a problem. */
6560  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6561
6562  /* Perform the set-changelist operation (transactionally), perform any
6563     notifications necessary, and then clean out our temporary tables.  */
6564  return svn_error_trace(with_finalization(wcroot, local_relpath,
6565                                           set_changelist_txn, &scb,
6566                                           do_changelist_notify, NULL,
6567                                           cancel_func, cancel_baton,
6568                                           notify_func, notify_baton,
6569                                           STMT_FINALIZE_CHANGELIST,
6570                                           scratch_pool));
6571}
6572
6573/* Implementation of svn_wc__db_op_mark_conflict() */
6574svn_error_t *
6575svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6576                                  const char *local_relpath,
6577                                  const svn_skel_t *conflict_skel,
6578                                  apr_pool_t *scratch_pool)
6579{
6580  svn_sqlite__stmt_t *stmt;
6581  svn_boolean_t got_row;
6582  svn_boolean_t is_complete;
6583
6584  SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6585  SVN_ERR_ASSERT(is_complete);
6586
6587  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6588                                    STMT_SELECT_ACTUAL_NODE));
6589  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6590  SVN_ERR(svn_sqlite__step(&got_row, stmt));
6591  SVN_ERR(svn_sqlite__reset(stmt));
6592
6593  if (got_row)
6594    {
6595      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6596                                        STMT_UPDATE_ACTUAL_CONFLICT));
6597      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6598    }
6599  else
6600    {
6601      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6602                                        STMT_INSERT_ACTUAL_CONFLICT));
6603      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6604      if (*local_relpath != '\0')
6605        SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6606                                      svn_relpath_dirname(local_relpath,
6607                                                          scratch_pool)));
6608    }
6609
6610  {
6611    svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6612
6613    SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6614  }
6615
6616  SVN_ERR(svn_sqlite__update(NULL, stmt));
6617
6618  return SVN_NO_ERROR;
6619}
6620
6621svn_error_t *
6622svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6623                            const char *local_abspath,
6624                            const svn_skel_t *conflict_skel,
6625                            const svn_skel_t *work_items,
6626                            apr_pool_t *scratch_pool)
6627{
6628  svn_wc__db_wcroot_t *wcroot;
6629  const char *local_relpath;
6630
6631  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6632
6633  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6634                              local_abspath, scratch_pool, scratch_pool));
6635  VERIFY_USABLE_WCROOT(wcroot);
6636
6637  SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6638                                            conflict_skel, scratch_pool));
6639
6640  /* ### Should be handled in the same transaction as setting the conflict */
6641  if (work_items)
6642    SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6643
6644  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6645
6646  return SVN_NO_ERROR;
6647
6648}
6649
6650/* The body of svn_wc__db_op_mark_resolved().
6651 */
6652svn_error_t *
6653svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
6654                                     const char *local_relpath,
6655                                     svn_wc__db_t *db,
6656                                     svn_boolean_t resolved_text,
6657                                     svn_boolean_t resolved_props,
6658                                     svn_boolean_t resolved_tree,
6659                                     const svn_skel_t *work_items,
6660                                     apr_pool_t *scratch_pool)
6661{
6662  svn_sqlite__stmt_t *stmt;
6663  svn_boolean_t have_row;
6664  int total_affected_rows = 0;
6665  svn_boolean_t resolved_all;
6666  apr_size_t conflict_len;
6667  const void *conflict_data;
6668  svn_skel_t *conflicts;
6669
6670  /* Check if we have a conflict in ACTUAL */
6671  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6672                                    STMT_SELECT_ACTUAL_NODE));
6673  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6674
6675  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6676
6677  if (! have_row)
6678    {
6679      SVN_ERR(svn_sqlite__reset(stmt));
6680
6681      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6682                                        STMT_SELECT_NODE_INFO));
6683
6684      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6685
6686      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6687      SVN_ERR(svn_sqlite__reset(stmt));
6688
6689      if (have_row)
6690        return SVN_NO_ERROR;
6691
6692      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6693                               _("The node '%s' was not found."),
6694                                   path_for_error_message(wcroot,
6695                                                          local_relpath,
6696                                                          scratch_pool));
6697    }
6698
6699  conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6700                                          scratch_pool);
6701  conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6702  SVN_ERR(svn_sqlite__reset(stmt));
6703
6704  SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6705                                        db, wcroot->abspath,
6706                                        resolved_text,
6707                                        resolved_props ? "" : NULL,
6708                                        resolved_tree,
6709                                        scratch_pool, scratch_pool));
6710
6711  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6712                                    STMT_UPDATE_ACTUAL_CONFLICT));
6713  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6714
6715  if (! resolved_all)
6716    {
6717      svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6718
6719      SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6720    }
6721
6722  SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6723
6724  /* Now, remove the actual node if it doesn't have any more useful
6725     information.  We only need to do this if we've remove data ourselves. */
6726  if (total_affected_rows > 0)
6727    {
6728      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6729                                        STMT_DELETE_ACTUAL_EMPTY));
6730      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6731      SVN_ERR(svn_sqlite__step_done(stmt));
6732    }
6733
6734  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6735
6736  return SVN_NO_ERROR;
6737}
6738
6739svn_error_t *
6740svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6741                            const char *local_abspath,
6742                            svn_boolean_t resolved_text,
6743                            svn_boolean_t resolved_props,
6744                            svn_boolean_t resolved_tree,
6745                            const svn_skel_t *work_items,
6746                            apr_pool_t *scratch_pool)
6747{
6748  svn_wc__db_wcroot_t *wcroot;
6749  const char *local_relpath;
6750
6751  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6752
6753  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6754                              local_abspath, scratch_pool, scratch_pool));
6755  VERIFY_USABLE_WCROOT(wcroot);
6756
6757  SVN_WC__DB_WITH_TXN(
6758    svn_wc__db_op_mark_resolved_internal(
6759                        wcroot, local_relpath, db,
6760                        resolved_text, resolved_props, resolved_tree,
6761                        work_items, scratch_pool),
6762    wcroot);
6763
6764  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6765  return SVN_NO_ERROR;
6766}
6767
6768/* Clear moved-to information at the delete-half of the move which moved
6769 * MOVED_TO_RELPATH here. This transforms the delete part of the move into a
6770 * normal delete.
6771 *
6772 * Note that the moved-to location is always an op-root, while this is not the
6773 * case for a moved-from location.
6774 */
6775static svn_error_t *
6776clear_moved_to(svn_wc__db_wcroot_t *wcroot,
6777               const char *moved_to_relpath,
6778               apr_pool_t *scratch_pool)
6779{
6780  svn_sqlite__stmt_t *stmt;
6781  const char *moved_from_relpath;
6782  int moved_from_op_depth;
6783
6784  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6785                                    STMT_SELECT_MOVED_FROM_RELPATH));
6786  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath));
6787  SVN_ERR(svn_sqlite__step_row(stmt));
6788
6789  moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6790  moved_from_op_depth = svn_sqlite__column_int(stmt, 1);
6791  SVN_ERR(svn_sqlite__reset(stmt));
6792
6793  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6794                                    STMT_CLEAR_MOVED_TO_RELPATH));
6795  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6796                            moved_from_relpath, moved_from_op_depth));
6797  SVN_ERR(svn_sqlite__update(NULL, stmt));
6798
6799  return SVN_NO_ERROR;
6800}
6801
6802/* Helper function for op_revert_txn. Raises move tree conflicts on
6803   descendants to ensure database stability on a non recursive revert
6804   of an ancestor that contains a possible move related tree conflict.
6805 */
6806static svn_error_t *
6807revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,
6808                              svn_wc__db_t *db,
6809                              const char *local_relpath,
6810                              int op_depth_below,
6811                              apr_pool_t *scratch_pool)
6812{
6813  svn_skel_t *conflict;
6814  svn_wc_operation_t operation;
6815  svn_boolean_t tree_conflicted;
6816  const apr_array_header_t *locations;
6817  svn_wc_conflict_reason_t reason;
6818  svn_wc_conflict_action_t action;
6819
6820  SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
6821                                            local_relpath,
6822                                            scratch_pool, scratch_pool));
6823
6824  if (!conflict)
6825    return SVN_NO_ERROR;
6826
6827  SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL,
6828                                     &tree_conflicted,
6829                                     db, wcroot->abspath,
6830                                     conflict,
6831                                     scratch_pool, scratch_pool));
6832
6833  if (!tree_conflicted
6834      || (operation != svn_wc_operation_update
6835          && operation != svn_wc_operation_switch))
6836    {
6837      return SVN_NO_ERROR;
6838    }
6839
6840  SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6841                                              NULL,
6842                                              db, wcroot->abspath,
6843                                              conflict,
6844                                              scratch_pool,
6845                                              scratch_pool));
6846
6847  if (reason == svn_wc_conflict_reason_deleted
6848      || reason == svn_wc_conflict_reason_replaced)
6849    {
6850      SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
6851        wcroot, local_relpath, op_depth_below, db,
6852        operation, action,
6853        (locations && locations->nelts > 0)
6854        ? APR_ARRAY_IDX(locations, 0,
6855                        const svn_wc_conflict_version_t *)
6856        : NULL,
6857        (locations && locations->nelts > 1)
6858        ? APR_ARRAY_IDX(locations, 1,
6859                        const svn_wc_conflict_version_t *)
6860        : NULL,
6861        scratch_pool));
6862
6863      /* Transform the move information into revert information */
6864      SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6865                                          STMT_MOVE_NOTIFY_TO_REVERT));
6866    }
6867
6868  return SVN_NO_ERROR;
6869}
6870
6871/* Baton for op_revert_txn and op_revert_recursive_txn */
6872struct revert_baton_t
6873{
6874  svn_wc__db_t *db;
6875  svn_boolean_t clear_changelists;
6876};
6877
6878/* One of the two alternative bodies of svn_wc__db_op_revert().
6879 *
6880 * Implements svn_wc__db_txn_callback_t. */
6881static svn_error_t *
6882op_revert_txn(void *baton,
6883              svn_wc__db_wcroot_t *wcroot,
6884              const char *local_relpath,
6885              apr_pool_t *scratch_pool)
6886{
6887  struct revert_baton_t *rvb = baton;
6888  svn_wc__db_t *db = rvb->db;
6889  svn_sqlite__stmt_t *stmt;
6890  svn_boolean_t have_row;
6891  int op_depth;
6892  svn_boolean_t moved_here;
6893  int affected_rows;
6894  const char *moved_to;
6895  int op_depth_below;
6896
6897  /* ### Similar structure to op_revert_recursive_txn, should they be
6898         combined? */
6899
6900  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6901                                    STMT_SELECT_NODE_INFO));
6902  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6903  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6904  if (!have_row)
6905    {
6906      SVN_ERR(svn_sqlite__reset(stmt));
6907
6908      /* There was no NODE row, so attempt to delete an ACTUAL_NODE row.  */
6909      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6910                                        STMT_DELETE_ACTUAL_NODE));
6911      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6912      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6913      if (affected_rows)
6914        {
6915          /* Can't do non-recursive actual-only revert if actual-only
6916             children exist. Raise an error to cancel the transaction.  */
6917          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6918                                            STMT_ACTUAL_HAS_CHILDREN));
6919          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6920          SVN_ERR(svn_sqlite__step(&have_row, stmt));
6921          SVN_ERR(svn_sqlite__reset(stmt));
6922          if (have_row)
6923            return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6924                                     _("Can't revert '%s' without"
6925                                       " reverting children"),
6926                                     path_for_error_message(wcroot,
6927                                                            local_relpath,
6928                                                            scratch_pool));
6929          return SVN_NO_ERROR;
6930        }
6931
6932      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6933                               _("The node '%s' was not found."),
6934                               path_for_error_message(wcroot,
6935                                                      local_relpath,
6936                                                      scratch_pool));
6937    }
6938
6939  op_depth = svn_sqlite__column_int(stmt, 0);
6940  moved_here = svn_sqlite__column_boolean(stmt, 15);
6941  moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6942
6943  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6944  if (have_row)
6945    op_depth_below = svn_sqlite__column_int(stmt, 0);
6946  else
6947    op_depth_below = -1;
6948
6949  SVN_ERR(svn_sqlite__reset(stmt));
6950
6951  if (moved_to)
6952    {
6953      SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
6954                                                local_relpath, op_depth,
6955                                                moved_to, NULL, scratch_pool));
6956    }
6957
6958  if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6959    {
6960      int op_depth_increased;
6961
6962      /* Can't do non-recursive revert if children exist */
6963      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6964                                        STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6965      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6966                                local_relpath, op_depth));
6967      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6968      SVN_ERR(svn_sqlite__reset(stmt));
6969      if (have_row)
6970        return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6971                                 _("Can't revert '%s' without"
6972                                   " reverting children"),
6973                                 path_for_error_message(wcroot,
6974                                                        local_relpath,
6975                                                        scratch_pool));
6976
6977      /* Rewrite the op-depth of all deleted children making the
6978         direct children into roots of deletes. */
6979      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6980                                     STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6981      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6982                                local_relpath,
6983                                op_depth));
6984      SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt));
6985
6986      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6987                                        STMT_DELETE_WORKING_NODE));
6988      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6989      SVN_ERR(svn_sqlite__step_done(stmt));
6990
6991      /* ### This removes the lock, but what about the access baton? */
6992      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6993                                        STMT_DELETE_WC_LOCK_ORPHAN));
6994      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6995      SVN_ERR(svn_sqlite__step_done(stmt));
6996
6997      /* If this node was moved-here, clear moved-to at the move source. */
6998      if (moved_here)
6999        SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7000
7001      /* If the node was moved itself, we don't have interesting moved
7002         children (and the move itself was already broken) */
7003      if (op_depth_increased && !moved_to)
7004        SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath,
7005                                              op_depth_below, scratch_pool));
7006    }
7007
7008  if (rvb->clear_changelists)
7009    {
7010      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7011                                        STMT_DELETE_ACTUAL_NODE));
7012      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7013      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7014    }
7015  else
7016    {
7017      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7018                                  STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
7019      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7020      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7021      if (!affected_rows)
7022        {
7023          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7024                                  STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
7025          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7026          SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7027        }
7028    }
7029
7030  return SVN_NO_ERROR;
7031}
7032
7033
7034/* One of the two alternative bodies of svn_wc__db_op_revert().
7035 *
7036 * Implements svn_wc__db_txn_callback_t. */
7037static svn_error_t *
7038op_revert_recursive_txn(void *baton,
7039                        svn_wc__db_wcroot_t *wcroot,
7040                        const char *local_relpath,
7041                        apr_pool_t *scratch_pool)
7042{
7043  struct revert_baton_t *rvb = baton;
7044  svn_sqlite__stmt_t *stmt;
7045  svn_boolean_t have_row;
7046  int op_depth;
7047  int select_op_depth;
7048  svn_boolean_t moved_here;
7049  int affected_rows;
7050  apr_pool_t *iterpool;
7051
7052  /* ### Similar structure to op_revert_txn, should they be
7053         combined? */
7054
7055  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7056                                    STMT_SELECT_NODE_INFO));
7057  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7058  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7059  if (!have_row)
7060    {
7061      SVN_ERR(svn_sqlite__reset(stmt));
7062
7063      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7064                                        STMT_DELETE_ACTUAL_NODE));
7065      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7066                                local_relpath));
7067      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7068
7069      if (affected_rows)
7070        return SVN_NO_ERROR;  /* actual-only revert */
7071
7072      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7073                               _("The node '%s' was not found."),
7074                               path_for_error_message(wcroot,
7075                                                      local_relpath,
7076                                                      scratch_pool));
7077    }
7078
7079  op_depth = svn_sqlite__column_int(stmt, 0);
7080  moved_here = svn_sqlite__column_boolean(stmt, 15);
7081  SVN_ERR(svn_sqlite__reset(stmt));
7082
7083  if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
7084    return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
7085                             _("Can't revert '%s' without"
7086                               " reverting parent"),
7087                             path_for_error_message(wcroot,
7088                                                    local_relpath,
7089                                                    scratch_pool));
7090
7091  /* Remove moved-here from move destinations outside the tree. */
7092  SVN_ERR(svn_sqlite__get_statement(
7093                    &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
7094  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7095                            op_depth));
7096  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7097  while (have_row)
7098    {
7099      const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7100      const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7101      int move_op_depth = svn_sqlite__column_int(stmt, 2);
7102      svn_error_t *err;
7103
7104      err = svn_wc__db_op_break_move_internal(wcroot,
7105                                              src_relpath, move_op_depth,
7106                                              dst_relpath, NULL, scratch_pool);
7107      if (err)
7108        return svn_error_compose_create(err, svn_sqlite__reset(stmt));
7109
7110      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7111    }
7112  SVN_ERR(svn_sqlite__reset(stmt));
7113
7114  /* Don't delete BASE nodes */
7115  select_op_depth = op_depth ? op_depth : 1;
7116
7117  /* Reverting any non wc-root node */
7118  SVN_ERR(svn_sqlite__get_statement(
7119                    &stmt, wcroot->sdb,
7120                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7121  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
7122                            local_relpath, select_op_depth));
7123  SVN_ERR(svn_sqlite__step_done(stmt));
7124
7125  if (rvb->clear_changelists)
7126    {
7127      SVN_ERR(svn_sqlite__get_statement(
7128                        &stmt, wcroot->sdb,
7129                        STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7130      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7131      SVN_ERR(svn_sqlite__step_done(stmt));
7132    }
7133  else
7134    {
7135      SVN_ERR(svn_sqlite__get_statement(
7136                        &stmt, wcroot->sdb,
7137                        STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7138      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7139      SVN_ERR(svn_sqlite__step_done(stmt));
7140
7141      SVN_ERR(svn_sqlite__get_statement(
7142                        &stmt, wcroot->sdb,
7143                        STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7144      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7145      SVN_ERR(svn_sqlite__step_done(stmt));
7146    }
7147
7148  /* ### This removes the locks, but what about the access batons? */
7149  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7150                                    STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7151  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7152                            local_relpath));
7153  SVN_ERR(svn_sqlite__step_done(stmt));
7154
7155  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7156                                    STMT_SELECT_MOVED_HERE_CHILDREN));
7157  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7158
7159  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7160
7161  iterpool = svn_pool_create(scratch_pool);
7162  while (have_row)
7163    {
7164      const char *moved_here_child_relpath;
7165      svn_error_t *err;
7166
7167      svn_pool_clear(iterpool);
7168
7169      moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
7170      err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool);
7171      if (err)
7172        return svn_error_trace(svn_error_compose_create(
7173                                        err,
7174                                        svn_sqlite__reset(stmt)));
7175
7176      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7177    }
7178  SVN_ERR(svn_sqlite__reset(stmt));
7179  svn_pool_destroy(iterpool);
7180
7181  /* Clear potential moved-to pointing at the target node itself. */
7182  if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
7183      && moved_here)
7184    SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7185
7186  return SVN_NO_ERROR;
7187}
7188
7189svn_error_t *
7190svn_wc__db_op_revert(svn_wc__db_t *db,
7191                     const char *local_abspath,
7192                     svn_depth_t depth,
7193                     svn_boolean_t clear_changelists,
7194                     apr_pool_t *result_pool,
7195                     apr_pool_t *scratch_pool)
7196{
7197  svn_wc__db_wcroot_t *wcroot;
7198  const char *local_relpath;
7199  struct revert_baton_t rvb;
7200  struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
7201                                       STMT_DROP_REVERT_LIST_TRIGGERS,
7202                                       NULL, NULL};
7203
7204  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7205
7206  rvb.db = db;
7207  rvb.clear_changelists = clear_changelists;
7208  wtb.cb_baton = &rvb;
7209
7210  switch (depth)
7211    {
7212    case svn_depth_empty:
7213      wtb.cb_func = op_revert_txn;
7214      break;
7215    case svn_depth_infinity:
7216      wtb.cb_func = op_revert_recursive_txn;
7217      break;
7218    default:
7219      return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7220                               _("Unsupported depth for revert of '%s'"),
7221                               svn_dirent_local_style(local_abspath,
7222                                                      scratch_pool));
7223    }
7224
7225  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7226                              db, local_abspath, scratch_pool, scratch_pool));
7227  VERIFY_USABLE_WCROOT(wcroot);
7228
7229  SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
7230                      wcroot);
7231
7232  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
7233
7234  return SVN_NO_ERROR;
7235}
7236
7237/* The body of svn_wc__db_revert_list_read().
7238 */
7239static svn_error_t *
7240revert_list_read(svn_boolean_t *reverted,
7241                 const apr_array_header_t **marker_paths,
7242                 svn_boolean_t *copied_here,
7243                 svn_node_kind_t *kind,
7244                 svn_wc__db_wcroot_t *wcroot,
7245                 const char *local_relpath,
7246                 svn_wc__db_t *db,
7247                 apr_pool_t *result_pool,
7248                 apr_pool_t *scratch_pool)
7249{
7250  svn_sqlite__stmt_t *stmt;
7251  svn_boolean_t have_row;
7252
7253  *reverted = FALSE;
7254  *marker_paths = NULL;
7255  *copied_here = FALSE;
7256  *kind = svn_node_unknown;
7257
7258  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7259                                    STMT_SELECT_REVERT_LIST));
7260  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7261  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7262  if (have_row)
7263    {
7264      svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
7265      svn_boolean_t another_row = FALSE;
7266
7267      if (is_actual)
7268        {
7269          apr_size_t conflict_len;
7270          const void *conflict_data;
7271
7272          conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
7273                                                  scratch_pool);
7274          if (conflict_data)
7275            {
7276              svn_skel_t *conflicts = svn_skel__parse(conflict_data,
7277                                                      conflict_len,
7278                                                      scratch_pool);
7279
7280              SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
7281                                                    db, wcroot->abspath,
7282                                                    conflicts,
7283                                                    result_pool,
7284                                                    scratch_pool));
7285            }
7286
7287          if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
7288            *reverted = TRUE;
7289
7290          SVN_ERR(svn_sqlite__step(&another_row, stmt));
7291        }
7292
7293      if (!is_actual || another_row)
7294        {
7295          *reverted = TRUE;
7296          if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
7297            {
7298              int op_depth = svn_sqlite__column_int(stmt, 3);
7299              *copied_here = (op_depth == relpath_depth(local_relpath));
7300            }
7301          *kind = svn_sqlite__column_token(stmt, 2, kind_map);
7302        }
7303
7304    }
7305  SVN_ERR(svn_sqlite__reset(stmt));
7306
7307  if (have_row)
7308    {
7309      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7310                                        STMT_DELETE_REVERT_LIST));
7311      SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7312      SVN_ERR(svn_sqlite__step_done(stmt));
7313    }
7314
7315  return SVN_NO_ERROR;
7316}
7317
7318svn_error_t *
7319svn_wc__db_revert_list_read(svn_boolean_t *reverted,
7320                            const apr_array_header_t **marker_files,
7321                            svn_boolean_t *copied_here,
7322                            svn_node_kind_t *kind,
7323                            svn_wc__db_t *db,
7324                            const char *local_abspath,
7325                            apr_pool_t *result_pool,
7326                            apr_pool_t *scratch_pool)
7327{
7328  svn_wc__db_wcroot_t *wcroot;
7329  const char *local_relpath;
7330
7331  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7332                              db, local_abspath, scratch_pool, scratch_pool));
7333  VERIFY_USABLE_WCROOT(wcroot);
7334
7335  SVN_WC__DB_WITH_TXN(
7336    revert_list_read(reverted, marker_files, copied_here, kind,
7337                     wcroot, local_relpath, db,
7338                     result_pool, scratch_pool),
7339    wcroot);
7340  return SVN_NO_ERROR;
7341}
7342
7343
7344/* The body of svn_wc__db_revert_list_read_copied_children().
7345 */
7346static svn_error_t *
7347revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
7348                                 const char *local_relpath,
7349                                 apr_array_header_t **children_p,
7350                                 apr_pool_t *result_pool,
7351                                 apr_pool_t *scratch_pool)
7352{
7353  svn_sqlite__stmt_t *stmt;
7354  svn_boolean_t have_row;
7355  apr_array_header_t *children;
7356
7357  children =
7358    apr_array_make(result_pool, 0,
7359                  sizeof(svn_wc__db_revert_list_copied_child_info_t *));
7360
7361  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7362                                    STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
7363  SVN_ERR(svn_sqlite__bindf(stmt, "sd",
7364                            local_relpath, relpath_depth(local_relpath)));
7365  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7366  while (have_row)
7367    {
7368      svn_wc__db_revert_list_copied_child_info_t *child_info;
7369      const char *child_relpath;
7370
7371      child_info = apr_palloc(result_pool, sizeof(*child_info));
7372
7373      child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7374      child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7375                                            result_pool);
7376      child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7377      APR_ARRAY_PUSH(
7378        children,
7379        svn_wc__db_revert_list_copied_child_info_t *) = child_info;
7380
7381      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7382    }
7383   SVN_ERR(svn_sqlite__reset(stmt));
7384
7385  *children_p = children;
7386
7387  return SVN_NO_ERROR;
7388}
7389
7390
7391svn_error_t *
7392svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children,
7393                                            svn_wc__db_t *db,
7394                                            const char *local_abspath,
7395                                            apr_pool_t *result_pool,
7396                                            apr_pool_t *scratch_pool)
7397{
7398  svn_wc__db_wcroot_t *wcroot;
7399  const char *local_relpath;
7400
7401  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7402                              db, local_abspath, scratch_pool, scratch_pool));
7403  VERIFY_USABLE_WCROOT(wcroot);
7404
7405  SVN_WC__DB_WITH_TXN(
7406    revert_list_read_copied_children(wcroot, local_relpath, children,
7407                                     result_pool, scratch_pool),
7408    wcroot);
7409  return SVN_NO_ERROR;
7410}
7411
7412
7413svn_error_t *
7414svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
7415                              void *notify_baton,
7416                              svn_wc__db_t *db,
7417                              const char *local_abspath,
7418                              apr_pool_t *scratch_pool)
7419{
7420  svn_wc__db_wcroot_t *wcroot;
7421  const char *local_relpath;
7422  svn_sqlite__stmt_t *stmt;
7423  svn_boolean_t have_row;
7424  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7425
7426  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7427                              db, local_abspath, scratch_pool, iterpool));
7428  VERIFY_USABLE_WCROOT(wcroot);
7429
7430  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7431                                    STMT_SELECT_REVERT_LIST_RECURSIVE));
7432  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7433  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7434  if (!have_row)
7435    return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7436  while (have_row)
7437    {
7438      const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7439      svn_wc_notify_t *notify;
7440
7441      svn_pool_clear(iterpool);
7442
7443      notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7444                                                    notify_relpath,
7445                                                    iterpool),
7446                                    svn_wc_notify_revert,
7447                                    iterpool);
7448
7449      if (!svn_sqlite__column_is_null(stmt, 1))
7450        notify->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7451      else
7452        {
7453          if (!svn_sqlite__column_is_null(stmt, 3))
7454            notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none);
7455
7456          switch (svn_sqlite__column_int(stmt, 2))
7457            {
7458              case 0:
7459                continue;
7460              case 1:
7461                /* standard revert */
7462                break;
7463              case 2:
7464                notify->action = svn_wc_notify_tree_conflict;
7465                break;
7466              default:
7467                SVN_ERR_MALFUNCTION();
7468            }
7469        }
7470
7471      notify_func(notify_baton, notify, iterpool);
7472
7473      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7474    }
7475  SVN_ERR(svn_sqlite__reset(stmt));
7476
7477  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7478                                    STMT_DELETE_REVERT_LIST_RECURSIVE));
7479  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7480  SVN_ERR(svn_sqlite__step_done(stmt));
7481
7482  svn_pool_destroy(iterpool);
7483
7484  return SVN_NO_ERROR;
7485}
7486
7487svn_error_t *
7488svn_wc__db_revert_list_done(svn_wc__db_t *db,
7489                            const char *local_abspath,
7490                            apr_pool_t *scratch_pool)
7491{
7492  svn_wc__db_wcroot_t *wcroot;
7493  const char *local_relpath;
7494
7495  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7496                              db, local_abspath, scratch_pool, scratch_pool));
7497  VERIFY_USABLE_WCROOT(wcroot);
7498
7499  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7500
7501  return SVN_NO_ERROR;
7502}
7503
7504/* The body of svn_wc__db_op_remove_node().
7505 */
7506static svn_error_t *
7507remove_node_txn(svn_boolean_t *left_changes,
7508                svn_wc__db_wcroot_t *wcroot,
7509                const char *local_relpath,
7510                svn_wc__db_t *db,
7511                svn_boolean_t destroy_wc,
7512                svn_boolean_t destroy_changes,
7513                const svn_skel_t *conflict,
7514                const svn_skel_t *work_items,
7515                svn_cancel_func_t cancel_func,
7516                void *cancel_baton,
7517                apr_pool_t *scratch_pool)
7518{
7519  svn_sqlite__stmt_t *stmt;
7520
7521  /* Note that unlike many similar functions it is a valid scenario for this
7522     function to be called on a wcroot! */
7523
7524   /* db set when destroying wc */
7525  SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7526
7527  if (left_changes)
7528    *left_changes = FALSE;
7529
7530  if (destroy_wc
7531      && (!destroy_changes || *local_relpath == '\0'))
7532    {
7533      svn_boolean_t have_row;
7534      apr_pool_t *iterpool;
7535      svn_error_t *err = NULL;
7536
7537      /* Install WQ items for deleting the unmodified files and all dirs */
7538      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7539                                        STMT_SELECT_WORKING_PRESENT));
7540      SVN_ERR(svn_sqlite__bindf(stmt, "is",
7541                                wcroot->wc_id, local_relpath));
7542
7543      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7544
7545      iterpool = svn_pool_create(scratch_pool);
7546
7547      while (have_row)
7548        {
7549          const char *child_relpath;
7550          const char *child_abspath;
7551          svn_node_kind_t child_kind;
7552          svn_boolean_t have_checksum;
7553          svn_filesize_t recorded_size;
7554          apr_int64_t recorded_time;
7555          const svn_io_dirent2_t *dirent;
7556          svn_boolean_t modified_p = TRUE;
7557          svn_skel_t *work_item = NULL;
7558
7559          svn_pool_clear(iterpool);
7560
7561          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7562          child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7563
7564          child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7565                                          iterpool);
7566
7567          if (child_kind == svn_node_file)
7568            {
7569              have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7570              recorded_size = get_recorded_size(stmt, 3);
7571              recorded_time = svn_sqlite__column_int64(stmt, 4);
7572            }
7573
7574          if (cancel_func)
7575            err = cancel_func(cancel_baton);
7576
7577          if (err)
7578            break;
7579
7580          err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7581                                    iterpool, iterpool);
7582
7583          if (err)
7584            break;
7585
7586          if (destroy_changes
7587              || dirent->kind != svn_node_file
7588              || child_kind != svn_node_file)
7589            {
7590              /* Not interested in keeping changes */
7591              modified_p = FALSE;
7592            }
7593          else if (child_kind == svn_node_file
7594                   && dirent->kind == svn_node_file
7595                   && dirent->filesize == recorded_size
7596                   && dirent->mtime == recorded_time)
7597            {
7598              modified_p = FALSE; /* File matches recorded state */
7599            }
7600          else if (have_checksum)
7601            err = svn_wc__internal_file_modified_p(&modified_p,
7602                                                   db, child_abspath,
7603                                                   FALSE, iterpool);
7604
7605          if (err)
7606            break;
7607
7608          if (modified_p)
7609            {
7610              if (left_changes)
7611                *left_changes = TRUE;
7612            }
7613          else if (child_kind == svn_node_dir)
7614            {
7615              err = svn_wc__wq_build_dir_remove(&work_item,
7616                                                db, wcroot->abspath,
7617                                                child_abspath, FALSE,
7618                                                iterpool, iterpool);
7619            }
7620          else /* svn_node_file || svn_node_symlink */
7621            {
7622              err = svn_wc__wq_build_file_remove(&work_item,
7623                                                 db, wcroot->abspath,
7624                                                 child_abspath,
7625                                                 iterpool, iterpool);
7626            }
7627
7628          if (err)
7629            break;
7630
7631          if (work_item)
7632            {
7633              err = add_work_items(wcroot->sdb, work_item, iterpool);
7634              if (err)
7635                break;
7636            }
7637
7638          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7639        }
7640      svn_pool_destroy(iterpool);
7641
7642      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7643    }
7644
7645  if (destroy_wc && *local_relpath != '\0')
7646    {
7647      /* Create work item for destroying the root */
7648      svn_wc__db_status_t status;
7649      svn_node_kind_t kind;
7650      SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7651                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7652                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7653                        wcroot, local_relpath,
7654                        scratch_pool, scratch_pool));
7655
7656      if (status == svn_wc__db_status_normal
7657          || status == svn_wc__db_status_added
7658          || status == svn_wc__db_status_incomplete)
7659        {
7660          svn_skel_t *work_item = NULL;
7661          const char *local_abspath = svn_dirent_join(wcroot->abspath,
7662                                                          local_relpath,
7663                                                          scratch_pool);
7664
7665          if (kind == svn_node_dir)
7666            {
7667              SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7668                                                  db, wcroot->abspath,
7669                                                  local_abspath,
7670                                                  destroy_changes
7671                                                      /* recursive */,
7672                                                  scratch_pool, scratch_pool));
7673            }
7674          else
7675            {
7676              svn_boolean_t modified_p = FALSE;
7677
7678              if (!destroy_changes)
7679                {
7680                  SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7681                                                           db, local_abspath,
7682                                                           FALSE,
7683                                                           scratch_pool));
7684                }
7685
7686              if (!modified_p)
7687                SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7688                                                     db, wcroot->abspath,
7689                                                     local_abspath,
7690                                                     scratch_pool,
7691                                                     scratch_pool));
7692              else
7693                {
7694                  if (left_changes)
7695                    *left_changes = TRUE;
7696                }
7697            }
7698
7699          SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7700        }
7701    }
7702
7703  /* Remove all nodes below local_relpath */
7704  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7705                                    STMT_DELETE_NODE_RECURSIVE));
7706  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7707  SVN_ERR(svn_sqlite__step_done(stmt));
7708
7709  /* Delete the root NODE when this is not the working copy root */
7710  if (local_relpath[0] != '\0')
7711    {
7712      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7713                                        STMT_DELETE_NODE_ALL_LAYERS));
7714      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7715      SVN_ERR(svn_sqlite__step_done(stmt));
7716    }
7717
7718  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7719                                    STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7720
7721  /* Delete all actual nodes at or below local_relpath */
7722  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7723                                         local_relpath));
7724  SVN_ERR(svn_sqlite__step_done(stmt));
7725
7726  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7727  if (conflict)
7728    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7729                                              conflict, scratch_pool));
7730
7731  return SVN_NO_ERROR;
7732}
7733
7734svn_error_t *
7735svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7736                          svn_wc__db_t *db,
7737                          const char *local_abspath,
7738                          svn_boolean_t destroy_wc,
7739                          svn_boolean_t destroy_changes,
7740                          const svn_skel_t *conflict,
7741                          const svn_skel_t *work_items,
7742                          svn_cancel_func_t cancel_func,
7743                          void *cancel_baton,
7744                          apr_pool_t *scratch_pool)
7745{
7746  svn_wc__db_wcroot_t *wcroot;
7747  const char *local_relpath;
7748
7749  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7750
7751  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7752                              local_abspath, scratch_pool, scratch_pool));
7753  VERIFY_USABLE_WCROOT(wcroot);
7754
7755  SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7756                                      wcroot, local_relpath, db,
7757                                      destroy_wc, destroy_changes,
7758                                      conflict, work_items,
7759                                      cancel_func, cancel_baton, scratch_pool),
7760                      wcroot);
7761
7762  /* Flush everything below this node in all ways */
7763  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7764                        scratch_pool));
7765
7766  return SVN_NO_ERROR;
7767}
7768
7769
7770/* The body of svn_wc__db_op_set_base_depth().
7771 */
7772static svn_error_t *
7773db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7774                     const char *local_relpath,
7775                     svn_depth_t depth,
7776                     apr_pool_t *scratch_pool)
7777{
7778  svn_sqlite__stmt_t *stmt;
7779  int affected_rows;
7780
7781  /* Flush any entries before we start monkeying the database.  */
7782  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7783                                    STMT_UPDATE_NODE_BASE_DEPTH));
7784  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7785                            svn_token__to_word(depth_map, depth)));
7786  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7787
7788  if (affected_rows == 0)
7789    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7790                             _("The node '%s' is not a committed directory"),
7791                             path_for_error_message(wcroot, local_relpath,
7792                                                    scratch_pool));
7793
7794  return SVN_NO_ERROR;
7795}
7796
7797
7798svn_error_t *
7799svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7800                             const char *local_abspath,
7801                             svn_depth_t depth,
7802                             apr_pool_t *scratch_pool)
7803{
7804  svn_wc__db_wcroot_t *wcroot;
7805  const char *local_relpath;
7806
7807  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7808  SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7809
7810  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7811                              local_abspath, scratch_pool, scratch_pool));
7812  VERIFY_USABLE_WCROOT(wcroot);
7813
7814  /* ### We set depth on working and base to match entry behavior.
7815         Maybe these should be separated later? */
7816  SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7817                                           scratch_pool),
7818                      wcroot);
7819
7820  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7821
7822  return SVN_NO_ERROR;
7823}
7824
7825
7826static svn_error_t *
7827info_below_working(svn_boolean_t *have_base,
7828                   svn_boolean_t *have_work,
7829                   svn_wc__db_status_t *status,
7830                   svn_wc__db_wcroot_t *wcroot,
7831                   const char *local_relpath,
7832                   int below_op_depth, /* < 0 is ignored */
7833                   apr_pool_t *scratch_pool);
7834
7835
7836/* Convert STATUS, the raw status obtained from the presence map, to
7837   the status appropriate for a working (op_depth > 0) node and return
7838   it in *WORKING_STATUS. */
7839static svn_error_t *
7840convert_to_working_status(svn_wc__db_status_t *working_status,
7841                          svn_wc__db_status_t status)
7842{
7843  svn_wc__db_status_t work_status = status;
7844
7845  SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7846                 || work_status == svn_wc__db_status_not_present
7847                 || work_status == svn_wc__db_status_base_deleted
7848                 || work_status == svn_wc__db_status_incomplete
7849                 || work_status == svn_wc__db_status_excluded);
7850
7851  if (work_status == svn_wc__db_status_excluded)
7852    {
7853      *working_status = svn_wc__db_status_excluded;
7854    }
7855  else if (work_status == svn_wc__db_status_not_present
7856           || work_status == svn_wc__db_status_base_deleted)
7857    {
7858      /* The caller should scan upwards to detect whether this
7859         deletion has occurred because this node has been moved
7860         away, or it is a regular deletion. Also note that the
7861         deletion could be of the BASE tree, or a child of
7862         something that has been copied/moved here. */
7863
7864      *working_status = svn_wc__db_status_deleted;
7865    }
7866  else /* normal or incomplete */
7867    {
7868      /* The caller should scan upwards to detect whether this
7869         addition has occurred because of a simple addition,
7870         a copy, or is the destination of a move. */
7871      *working_status = svn_wc__db_status_added;
7872    }
7873
7874  return SVN_NO_ERROR;
7875}
7876
7877
7878/* Return the status of the node, if any, below the "working" node (or
7879   below BELOW_OP_DEPTH if >= 0).
7880   Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7881   working node is present, and *STATUS to the status of the first
7882   layer below the selected node. */
7883static svn_error_t *
7884info_below_working(svn_boolean_t *have_base,
7885                   svn_boolean_t *have_work,
7886                   svn_wc__db_status_t *status,
7887                   svn_wc__db_wcroot_t *wcroot,
7888                   const char *local_relpath,
7889                   int below_op_depth,
7890                   apr_pool_t *scratch_pool)
7891{
7892  svn_sqlite__stmt_t *stmt;
7893  svn_boolean_t have_row;
7894
7895  *have_base = *have_work =  FALSE;
7896  *status = svn_wc__db_status_normal;
7897
7898  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7899                                    STMT_SELECT_NODE_INFO));
7900  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7901  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7902
7903  if (below_op_depth >= 0)
7904    {
7905      while (have_row &&
7906             (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7907        {
7908          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7909        }
7910    }
7911  if (have_row)
7912    {
7913      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7914      if (have_row)
7915        *status = svn_sqlite__column_token(stmt, 3, presence_map);
7916
7917      while (have_row)
7918        {
7919          int op_depth = svn_sqlite__column_int(stmt, 0);
7920
7921          if (op_depth > 0)
7922            *have_work = TRUE;
7923          else
7924            *have_base = TRUE;
7925
7926          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7927        }
7928    }
7929  SVN_ERR(svn_sqlite__reset(stmt));
7930
7931  if (*have_work)
7932    SVN_ERR(convert_to_working_status(status, *status));
7933
7934  return SVN_NO_ERROR;
7935}
7936
7937/* Helper function for op_delete_txn */
7938static svn_error_t *
7939delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7940                      const char *child_moved_from_relpath,
7941                      int op_depth,
7942                      const char *new_moved_to_relpath,
7943                      apr_pool_t *scratch_pool)
7944{
7945  svn_sqlite__stmt_t *stmt;
7946  int affected;
7947
7948  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7949                                    STMT_UPDATE_MOVED_TO_RELPATH));
7950
7951  SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7952                            wcroot->wc_id,
7953                            child_moved_from_relpath,
7954                            op_depth,
7955                            new_moved_to_relpath));
7956  SVN_ERR(svn_sqlite__update(&affected, stmt));
7957#ifdef SVN_DEBUG
7958  /* Not fatal in release mode. The move recording is broken,
7959     but the rest of the working copy can handle this. */
7960  SVN_ERR_ASSERT(affected == 1);
7961#endif
7962
7963  return SVN_NO_ERROR;
7964}
7965
7966
7967struct op_delete_baton_t {
7968  const char *moved_to_relpath; /* NULL if delete is not part of a move */
7969  svn_skel_t *conflict;
7970  svn_skel_t *work_items;
7971  svn_boolean_t delete_dir_externals;
7972  svn_boolean_t notify;
7973};
7974
7975/* This structure is used while rewriting move information for nodes.
7976 *
7977 * The most simple case of rewriting move information happens when
7978 * a moved-away subtree is moved again:  mv A B; mv B C
7979 * The second move requires rewriting moved-to info at or within A.
7980 *
7981 * Another example is a move of a subtree which had nodes moved into it:
7982 *   mv A B/F; mv B G
7983 * This requires rewriting such that A/F is marked has having moved to G/F.
7984 *
7985 * Another case is where a node becomes a nested moved node.
7986 * A nested move happens when a subtree child is moved before or after
7987 * the subtree itself is moved. For example:
7988 *   mv A/F A/G; mv A B
7989 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7990 * Note that the following sequence results in the same DB state:
7991 *   mv A B; mv B/F B/G
7992 * We do not care about the order the moves were performed in.
7993 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7994 */
7995struct moved_node_t {
7996  /* The source of the move. */
7997  const char *local_relpath;
7998
7999  /* The move destination. */
8000  const char *moved_to_relpath;
8001
8002  /* The op-depth of the deleted node at the source of the move. */
8003  int op_depth;
8004
8005  /* When >= 1 the op_depth at which local_relpath was moved to its
8006     location. Used to find its original location outside the delete */
8007  int moved_from_depth;
8008};
8009
8010/* Helper function to resolve the original location of local_relpath at OP_DEPTH
8011   before it was moved into the tree rooted at ROOT_RELPATH. */
8012static svn_error_t *
8013resolve_moved_from(const char **moved_from_relpath,
8014                   int *moved_from_op_depth,
8015                   svn_wc__db_wcroot_t *wcroot,
8016                   const char *root_relpath,
8017                   const char *local_relpath,
8018                   int op_depth,
8019                   apr_pool_t *result_pool,
8020                   apr_pool_t *scratch_pool)
8021{
8022  const char *suffix = "";
8023  svn_sqlite__stmt_t *stmt;
8024  const char *m_from_relpath;
8025  int m_from_op_depth;
8026  int m_move_from_depth;
8027  svn_boolean_t have_row;
8028
8029  while (relpath_depth(local_relpath) > op_depth)
8030    {
8031      const char *name;
8032      svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
8033      suffix = svn_relpath_join(suffix, name, scratch_pool);
8034    }
8035
8036  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8037                                    STMT_SELECT_MOVED_FROM_FOR_DELETE));
8038  SVN_ERR(svn_sqlite__bindf(stmt, "is",
8039                            wcroot->wc_id, local_relpath));
8040  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8041
8042  if (!have_row)
8043    {
8044      /* assert(have_row); */
8045      *moved_from_relpath = NULL;
8046      *moved_from_op_depth = -1;
8047
8048      SVN_ERR(svn_sqlite__reset(stmt));
8049
8050      return SVN_NO_ERROR;
8051    }
8052
8053  m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
8054  m_from_op_depth = svn_sqlite__column_int(stmt, 1);
8055  m_move_from_depth = svn_sqlite__column_int(stmt, 2);
8056
8057  SVN_ERR(svn_sqlite__reset(stmt));
8058
8059  if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
8060    {
8061      *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
8062                                             result_pool);
8063      *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
8064      return SVN_NO_ERROR;
8065    }
8066  else if (!m_move_from_depth)
8067    {
8068      *moved_from_relpath = NULL;
8069      *moved_from_op_depth = -1;
8070      return SVN_NO_ERROR;
8071    }
8072
8073  return svn_error_trace(
8074        resolve_moved_from(moved_from_relpath,
8075                           moved_from_op_depth,
8076                           wcroot,
8077                           root_relpath,
8078                           svn_relpath_join(m_from_relpath, suffix,
8079                                            scratch_pool),
8080                           m_move_from_depth,
8081                           result_pool, scratch_pool));
8082}
8083
8084static svn_error_t *
8085delete_node(void *baton,
8086            svn_wc__db_wcroot_t *wcroot,
8087            const char *local_relpath,
8088            apr_pool_t *scratch_pool)
8089{
8090  struct op_delete_baton_t *b = baton;
8091  svn_wc__db_status_t status;
8092  svn_boolean_t have_row, op_root;
8093  svn_boolean_t add_work = FALSE;
8094  svn_sqlite__stmt_t *stmt;
8095  int working_op_depth; /* Depth of what is to be deleted */
8096  int keep_op_depth = 0; /* Depth of what is below what is deleted */
8097  svn_node_kind_t kind;
8098  apr_array_header_t *moved_nodes = NULL;
8099  int delete_op_depth = relpath_depth(local_relpath);
8100
8101  assert(*local_relpath); /* Can't delete wcroot */
8102
8103  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8104                                    STMT_SELECT_NODE_INFO));
8105  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8106  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8107
8108  if (!have_row)
8109    {
8110      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8111                               svn_sqlite__reset(stmt),
8112                               _("The node '%s' was not found."),
8113                               path_for_error_message(wcroot,
8114                                                      local_relpath,
8115                                                      scratch_pool));
8116    }
8117
8118  working_op_depth = svn_sqlite__column_int(stmt, 0);
8119  status = svn_sqlite__column_token(stmt, 3, presence_map);
8120  kind = svn_sqlite__column_token(stmt, 4, kind_map);
8121
8122  if (working_op_depth < delete_op_depth)
8123    {
8124      op_root = FALSE;
8125      add_work = TRUE;
8126      keep_op_depth = working_op_depth;
8127    }
8128  else
8129    {
8130      op_root = TRUE;
8131
8132      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8133
8134      if (have_row)
8135        {
8136          svn_wc__db_status_t below_status;
8137          int below_op_depth;
8138
8139          below_op_depth = svn_sqlite__column_int(stmt, 0);
8140          below_status = svn_sqlite__column_token(stmt, 3, presence_map);
8141
8142          if (below_status != svn_wc__db_status_not_present
8143              && below_status != svn_wc__db_status_base_deleted)
8144            {
8145              add_work = TRUE;
8146              keep_op_depth = below_op_depth;
8147            }
8148          else
8149            keep_op_depth = 0;
8150        }
8151      else
8152        keep_op_depth = -1;
8153    }
8154
8155  SVN_ERR(svn_sqlite__reset(stmt));
8156
8157  if (working_op_depth != 0) /* WORKING */
8158    SVN_ERR(convert_to_working_status(&status, status));
8159
8160  if (status == svn_wc__db_status_deleted
8161      || status == svn_wc__db_status_not_present)
8162    return SVN_NO_ERROR;
8163
8164  /* Don't copy BASE directories with server excluded nodes */
8165  if (status == svn_wc__db_status_normal && kind == svn_node_dir)
8166    {
8167      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8168                                        STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
8169      SVN_ERR(svn_sqlite__bindf(stmt, "is",
8170                                wcroot->wc_id, local_relpath));
8171      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8172      if (have_row)
8173        {
8174          const char *absent_path = svn_sqlite__column_text(stmt, 0,
8175                                                            scratch_pool);
8176
8177          return svn_error_createf(
8178                               SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
8179                               svn_sqlite__reset(stmt),
8180                          _("Cannot delete '%s' as '%s' is excluded by server"),
8181                               path_for_error_message(wcroot, local_relpath,
8182                                                      scratch_pool),
8183                               path_for_error_message(wcroot, absent_path,
8184                                                      scratch_pool));
8185        }
8186      SVN_ERR(svn_sqlite__reset(stmt));
8187    }
8188  else if (status == svn_wc__db_status_server_excluded)
8189    {
8190      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8191                          _("Cannot delete '%s' as it is excluded by server"),
8192                               path_for_error_message(wcroot, local_relpath,
8193                                                      scratch_pool));
8194    }
8195  else if (status == svn_wc__db_status_excluded)
8196    {
8197      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8198                          _("Cannot delete '%s' as it is excluded"),
8199                               path_for_error_message(wcroot, local_relpath,
8200                                                      scratch_pool));
8201    }
8202
8203  if (b->moved_to_relpath)
8204    {
8205      const char *moved_from_relpath = NULL;
8206      struct moved_node_t *moved_node;
8207      int move_op_depth;
8208
8209      moved_nodes = apr_array_make(scratch_pool, 1,
8210                                   sizeof(struct moved_node_t *));
8211
8212      /* The node is being moved-away.
8213       * Figure out if the node was moved-here before, or whether this
8214       * is the first time the node is moved. */
8215      if (status == svn_wc__db_status_added)
8216        SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
8217                              &moved_from_relpath,
8218                              NULL,
8219                              &move_op_depth,
8220                              wcroot, local_relpath,
8221                              scratch_pool, scratch_pool));
8222
8223      if (op_root && moved_from_relpath)
8224        {
8225          const char *part = svn_relpath_skip_ancestor(local_relpath,
8226                                                       moved_from_relpath);
8227
8228          /* Existing move-root is moved to another location */
8229          moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8230          if (!part)
8231            moved_node->local_relpath = moved_from_relpath;
8232          else
8233            moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
8234                                                         part, scratch_pool);
8235          moved_node->op_depth = move_op_depth;
8236          moved_node->moved_to_relpath = b->moved_to_relpath;
8237          moved_node->moved_from_depth = -1;
8238
8239          APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8240        }
8241      else if (!op_root && (status == svn_wc__db_status_normal
8242                            || status == svn_wc__db_status_copied
8243                            || status == svn_wc__db_status_moved_here))
8244        {
8245          /* The node is becoming a move-root for the first time,
8246           * possibly because of a nested move operation. */
8247          moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8248          moved_node->local_relpath = local_relpath;
8249          moved_node->op_depth = delete_op_depth;
8250          moved_node->moved_to_relpath = b->moved_to_relpath;
8251          moved_node->moved_from_depth = -1;
8252
8253          APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8254        }
8255      /* Else: We can't track history of local additions and/or of things we are
8256               about to delete. */
8257
8258      /* And update all moved_to values still pointing to this location */
8259      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8260                                        STMT_UPDATE_MOVED_TO_DESCENDANTS));
8261      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
8262                                             local_relpath,
8263                                             b->moved_to_relpath));
8264      SVN_ERR(svn_sqlite__update(NULL, stmt));
8265    }
8266
8267  /* Find children that were moved out of the subtree rooted at this node.
8268   * We'll need to update their op-depth columns because their deletion
8269   * is now implied by the deletion of their parent (i.e. this node). */
8270    {
8271      apr_pool_t *iterpool;
8272      int i;
8273
8274      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8275                                        STMT_SELECT_MOVED_FOR_DELETE));
8276      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
8277                                delete_op_depth));
8278
8279      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8280      iterpool = svn_pool_create(scratch_pool);
8281      while (have_row)
8282        {
8283          struct moved_node_t *mn;
8284          const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8285          const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
8286          int child_op_depth = svn_sqlite__column_int(stmt, 2);
8287          int moved_from_depth = -1;
8288          svn_boolean_t fixup = FALSE;
8289
8290          if (! b->moved_to_relpath
8291              && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
8292            {
8293              /* a NULL moved_here_depth will be reported as 0 */
8294              int moved_here_depth = svn_sqlite__column_int(stmt, 3);
8295
8296              /* Plain delete. Fixup move information of descendants that were
8297                 moved here, or that were moved out */
8298
8299              if (moved_here_depth >= delete_op_depth)
8300                {
8301                  /* The move we recorded here must be moved to the location
8302                     this node had before it was moved here.
8303
8304                     This might contain multiple steps when the node was moved
8305                     in several places within the to be deleted tree */
8306
8307                  /* ### TODO: Add logic */
8308                  fixup = TRUE;
8309                  moved_from_depth = moved_here_depth;
8310                }
8311              else
8312                {
8313                  /* Update the op-depth of an moved away node that was
8314                     registered as moved by the records that we are about
8315                     to delete */
8316                  fixup = TRUE;
8317                  child_op_depth = delete_op_depth;
8318                }
8319            }
8320          else if (b->moved_to_relpath)
8321            {
8322              /* The node is moved to a new location */
8323
8324              if (delete_op_depth == child_op_depth)
8325                {
8326                  /* Update the op-depth of a tree shadowed by this tree */
8327                  fixup = TRUE;
8328                  /*child_op_depth = delete_depth;*/
8329                }
8330              else if (child_op_depth >= delete_op_depth
8331                       && !svn_relpath_skip_ancestor(local_relpath,
8332                                                     mv_to_relpath))
8333                {
8334                  /* Update the move destination of something that is now moved
8335                     away further */
8336
8337                  child_relpath = svn_relpath_skip_ancestor(local_relpath,
8338                                                            child_relpath);
8339
8340                  if (child_relpath)
8341                    {
8342                      child_relpath = svn_relpath_join(b->moved_to_relpath,
8343                                                       child_relpath,
8344                                                       scratch_pool);
8345
8346                      if (child_op_depth > delete_op_depth
8347                           && svn_relpath_skip_ancestor(local_relpath,
8348                                                        child_relpath))
8349                        child_op_depth = delete_op_depth;
8350                      else
8351                        {
8352                          /* Calculate depth of the shadowing at the new location */
8353                          child_op_depth = child_op_depth
8354                                                - relpath_depth(local_relpath)
8355                                                + relpath_depth(b->moved_to_relpath);
8356                        }
8357
8358                      fixup = TRUE;
8359                    }
8360                }
8361            }
8362
8363          if (fixup)
8364            {
8365              mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8366
8367              mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
8368              mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
8369              mn->op_depth = child_op_depth;
8370              mn->moved_from_depth = moved_from_depth;
8371
8372              if (!moved_nodes)
8373                moved_nodes = apr_array_make(scratch_pool, 1,
8374                                             sizeof(struct moved_node_t *));
8375              APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
8376            }
8377
8378          SVN_ERR(svn_sqlite__step(&have_row, stmt));
8379        }
8380      SVN_ERR(svn_sqlite__reset(stmt));
8381
8382      for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
8383        {
8384          struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
8385                                                  struct moved_node_t *);
8386
8387          if (mn->moved_from_depth > 0)
8388            {
8389              svn_pool_clear(iterpool);
8390
8391              SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
8392                                         wcroot, local_relpath,
8393                                         mn->local_relpath,
8394                                         mn->moved_from_depth,
8395                                         scratch_pool, iterpool));
8396
8397              if (!mn->local_relpath)
8398                svn_sort__array_delete(moved_nodes, i--, 1);
8399            }
8400        }
8401
8402      svn_pool_destroy(iterpool);
8403    }
8404
8405  if (!b->moved_to_relpath)
8406    {
8407      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8408                                        STMT_CLEAR_MOVED_TO_DESCENDANTS));
8409      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8410                                local_relpath));
8411      SVN_ERR(svn_sqlite__update(NULL, stmt));
8412
8413      if (op_root)
8414        {
8415          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8416                                            STMT_CLEAR_MOVED_TO_FROM_DEST));
8417          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8418                                    local_relpath));
8419
8420          SVN_ERR(svn_sqlite__update(NULL, stmt));
8421        }
8422    }
8423
8424
8425  /* ### Put actual-only nodes into the list? */
8426  if (b->notify)
8427    {
8428      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8429                                        STMT_INSERT_DELETE_LIST));
8430      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8431                                wcroot->wc_id, local_relpath, working_op_depth));
8432      SVN_ERR(svn_sqlite__step_done(stmt));
8433    }
8434
8435  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8436                                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8437  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8438                            wcroot->wc_id, local_relpath, delete_op_depth));
8439  SVN_ERR(svn_sqlite__step_done(stmt));
8440
8441  /* Delete ACTUAL_NODE rows, but leave those that have changelist
8442     and a NODES row. */
8443  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8444                         STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8445  SVN_ERR(svn_sqlite__bindf(stmt, "is",
8446                            wcroot->wc_id, local_relpath));
8447  SVN_ERR(svn_sqlite__step_done(stmt));
8448
8449  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8450                         STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8451  SVN_ERR(svn_sqlite__bindf(stmt, "is",
8452                            wcroot->wc_id, local_relpath));
8453  SVN_ERR(svn_sqlite__step_done(stmt));
8454
8455  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8456                                    STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8457  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8458                            local_relpath));
8459  SVN_ERR(svn_sqlite__step_done(stmt));
8460
8461  if (add_work)
8462    {
8463      /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8464
8465      /* Delete the node and possible descendants. */
8466      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8467                                 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8468      SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8469                                wcroot->wc_id, local_relpath,
8470                                keep_op_depth, delete_op_depth));
8471      SVN_ERR(svn_sqlite__step_done(stmt));
8472    }
8473
8474  if (moved_nodes)
8475    {
8476      int i;
8477
8478      for (i = 0; i < moved_nodes->nelts; ++i)
8479        {
8480          const struct moved_node_t *moved_node
8481            = APR_ARRAY_IDX(moved_nodes, i, void *);
8482
8483          SVN_ERR(delete_update_movedto(wcroot,
8484                                        moved_node->local_relpath,
8485                                        moved_node->op_depth,
8486                                        moved_node->moved_to_relpath,
8487                                        scratch_pool));
8488        }
8489    }
8490
8491  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8492                                    STMT_DELETE_FILE_EXTERNALS));
8493  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8494  SVN_ERR(svn_sqlite__step_done(stmt));
8495
8496  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8497                                    b->delete_dir_externals
8498                                    ? STMT_DELETE_EXTERNAL_REGISTATIONS
8499                                    : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8500  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8501  SVN_ERR(svn_sqlite__step_done(stmt));
8502
8503  SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8504  if (b->conflict)
8505    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8506                                              b->conflict, scratch_pool));
8507
8508  return SVN_NO_ERROR;
8509}
8510
8511static svn_error_t *
8512op_delete_txn(void *baton,
8513              svn_wc__db_wcroot_t *wcroot,
8514              const char *local_relpath,
8515              apr_pool_t *scratch_pool)
8516{
8517
8518  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8519  SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8520  return SVN_NO_ERROR;
8521}
8522
8523
8524struct op_delete_many_baton_t {
8525  apr_array_header_t *rel_targets;
8526  svn_boolean_t delete_dir_externals;
8527  const svn_skel_t *work_items;
8528};
8529
8530static svn_error_t *
8531op_delete_many_txn(void *baton,
8532                   svn_wc__db_wcroot_t *wcroot,
8533                   const char *local_relpath,
8534                   apr_pool_t *scratch_pool)
8535{
8536  struct op_delete_many_baton_t *odmb = baton;
8537  struct op_delete_baton_t odb;
8538  int i;
8539  apr_pool_t *iterpool;
8540
8541  odb.moved_to_relpath = NULL;
8542  odb.conflict = NULL;
8543  odb.work_items = NULL;
8544  odb.delete_dir_externals = odmb->delete_dir_externals;
8545  odb.notify = TRUE;
8546
8547  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8548  iterpool = svn_pool_create(scratch_pool);
8549  for (i = 0; i < odmb->rel_targets->nelts; i++)
8550    {
8551      const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8552                                                 const char *);
8553
8554
8555      svn_pool_clear(iterpool);
8556      SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8557    }
8558  svn_pool_destroy(iterpool);
8559
8560  SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8561
8562  return SVN_NO_ERROR;
8563}
8564
8565
8566static svn_error_t *
8567do_delete_notify(void *baton,
8568                 svn_wc__db_wcroot_t *wcroot,
8569                 svn_cancel_func_t cancel_func,
8570                 void *cancel_baton,
8571                 svn_wc_notify_func2_t notify_func,
8572                 void *notify_baton,
8573                 apr_pool_t *scratch_pool)
8574{
8575  svn_sqlite__stmt_t *stmt;
8576  svn_boolean_t have_row;
8577  apr_pool_t *iterpool;
8578
8579  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8580                                    STMT_SELECT_DELETE_LIST));
8581  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8582
8583  iterpool = svn_pool_create(scratch_pool);
8584  while (have_row)
8585    {
8586      const char *notify_relpath;
8587      const char *notify_abspath;
8588
8589      svn_pool_clear(iterpool);
8590
8591      notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8592      notify_abspath = svn_dirent_join(wcroot->abspath,
8593                                       notify_relpath,
8594                                       iterpool);
8595
8596      notify_func(notify_baton,
8597                  svn_wc_create_notify(notify_abspath,
8598                                       svn_wc_notify_delete,
8599                                       iterpool),
8600                  iterpool);
8601
8602      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8603    }
8604  svn_pool_destroy(iterpool);
8605
8606  SVN_ERR(svn_sqlite__reset(stmt));
8607
8608  /* We only allow cancellation after notification for all deleted nodes
8609   * has happened. The nodes are already deleted so we should notify for
8610   * all of them. */
8611  if (cancel_func)
8612    SVN_ERR(cancel_func(cancel_baton));
8613
8614  return SVN_NO_ERROR;
8615}
8616
8617
8618svn_error_t *
8619svn_wc__db_op_delete(svn_wc__db_t *db,
8620                     const char *local_abspath,
8621                     const char *moved_to_abspath,
8622                     svn_boolean_t delete_dir_externals,
8623                     svn_skel_t *conflict,
8624                     svn_skel_t *work_items,
8625                     svn_cancel_func_t cancel_func,
8626                     void *cancel_baton,
8627                     svn_wc_notify_func2_t notify_func,
8628                     void *notify_baton,
8629                     apr_pool_t *scratch_pool)
8630{
8631  svn_wc__db_wcroot_t *wcroot;
8632  svn_wc__db_wcroot_t *moved_to_wcroot;
8633  const char *local_relpath;
8634  const char *moved_to_relpath;
8635  struct op_delete_baton_t odb;
8636
8637  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8638
8639  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8640                                                db, local_abspath,
8641                                                scratch_pool, scratch_pool));
8642  VERIFY_USABLE_WCROOT(wcroot);
8643
8644  if (moved_to_abspath)
8645    {
8646      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8647                                                    &moved_to_relpath,
8648                                                    db, moved_to_abspath,
8649                                                    scratch_pool,
8650                                                    scratch_pool));
8651      VERIFY_USABLE_WCROOT(moved_to_wcroot);
8652
8653      if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8654        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8655                                 _("Cannot move '%s' to '%s' because they "
8656                                   "are not in the same working copy"),
8657                                 svn_dirent_local_style(local_abspath,
8658                                                        scratch_pool),
8659                                 svn_dirent_local_style(moved_to_abspath,
8660                                                        scratch_pool));
8661    }
8662  else
8663    moved_to_relpath = NULL;
8664
8665  odb.moved_to_relpath = moved_to_relpath;
8666  odb.conflict = conflict;
8667  odb.work_items = work_items;
8668  odb.delete_dir_externals = delete_dir_externals;
8669
8670  if (notify_func)
8671    {
8672      /* Perform the deletion operation (transactionally), perform any
8673         notifications necessary, and then clean out our temporary tables.  */
8674      odb.notify = TRUE;
8675      SVN_ERR(with_finalization(wcroot, local_relpath,
8676                                op_delete_txn, &odb,
8677                                do_delete_notify, NULL,
8678                                cancel_func, cancel_baton,
8679                                notify_func, notify_baton,
8680                                STMT_FINALIZE_DELETE,
8681                                scratch_pool));
8682    }
8683  else
8684    {
8685      /* Avoid the trigger work */
8686      odb.notify = FALSE;
8687      SVN_WC__DB_WITH_TXN(
8688                    delete_node(&odb, wcroot, local_relpath, scratch_pool),
8689                    wcroot);
8690    }
8691
8692  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8693                        scratch_pool));
8694
8695  return SVN_NO_ERROR;
8696}
8697
8698
8699svn_error_t *
8700svn_wc__db_op_delete_many(svn_wc__db_t *db,
8701                          apr_array_header_t *targets,
8702                          svn_boolean_t delete_dir_externals,
8703                          const svn_skel_t *work_items,
8704                          svn_cancel_func_t cancel_func,
8705                          void *cancel_baton,
8706                          svn_wc_notify_func2_t notify_func,
8707                          void *notify_baton,
8708                          apr_pool_t *scratch_pool)
8709{
8710  svn_wc__db_wcroot_t *wcroot;
8711  const char *local_relpath;
8712  struct op_delete_many_baton_t odmb;
8713  int i;
8714  apr_pool_t *iterpool;
8715
8716  odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8717                                    sizeof(const char *));
8718  odmb.work_items = work_items;
8719  odmb.delete_dir_externals = delete_dir_externals;
8720  iterpool = svn_pool_create(scratch_pool);
8721  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8722                                                db,
8723                                                APR_ARRAY_IDX(targets, 0,
8724                                                              const char *),
8725                                                scratch_pool, iterpool));
8726  VERIFY_USABLE_WCROOT(wcroot);
8727  for (i = 0; i < targets->nelts; i++)
8728    {
8729      const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8730      svn_wc__db_wcroot_t *target_wcroot;
8731
8732      svn_pool_clear(iterpool);
8733
8734      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8735                                                    &local_relpath, db,
8736                                                    APR_ARRAY_IDX(targets, i,
8737                                                                  const char *),
8738                                                    scratch_pool, iterpool));
8739      VERIFY_USABLE_WCROOT(target_wcroot);
8740      SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8741
8742      /* Assert that all targets are within the same working copy. */
8743      SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8744
8745      APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8746      SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8747                            iterpool));
8748
8749    }
8750  svn_pool_destroy(iterpool);
8751
8752  /* Perform the deletion operation (transactionally), perform any
8753     notifications necessary, and then clean out our temporary tables.  */
8754  return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8755                                           op_delete_many_txn, &odmb,
8756                                           do_delete_notify, NULL,
8757                                           cancel_func, cancel_baton,
8758                                           notify_func, notify_baton,
8759                                           STMT_FINALIZE_DELETE,
8760                                           scratch_pool));
8761}
8762
8763/* Helper function for read_info() to provide better diagnostics than just
8764   asserting.
8765
8766   ### BH: Yes this code is ugly, and that is why I only introduce it in
8767   ### read_info(). But we really need something to determine the root cause
8768   ### of this problem to diagnose why TortoiseSVN users were seeing all those
8769   ### assertions.
8770
8771   Adds an error to the *err chain if invalid values are encountered. In that
8772   case the value is set to the first value in the map, assuming that caller
8773   will just return the combined error.
8774 */
8775static int
8776column_token_err(svn_error_t **err,
8777                 svn_sqlite__stmt_t *stmt,
8778                 int column,
8779                 const svn_token_map_t *map)
8780{
8781  svn_error_t *err2;
8782  const char *word = svn_sqlite__column_text(stmt, column, NULL);
8783  int value;
8784
8785  /* svn_token__from_word_err() handles NULL for us */
8786  err2 = svn_token__from_word_err(&value, map, word);
8787
8788  if (err2)
8789    {
8790      *err = svn_error_compose_create(
8791                *err,
8792                svn_error_createf(
8793                    SVN_ERR_WC_CORRUPT, err2,
8794                    _("Encountered invalid node state in column %d of "
8795                      "info query to working copy database"),
8796                    column));
8797      value = map[0].val;
8798    }
8799
8800  return value;
8801}
8802
8803/* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8804   DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8805static svn_error_t *
8806read_info(svn_wc__db_status_t *status,
8807          svn_node_kind_t *kind,
8808          svn_revnum_t *revision,
8809          const char **repos_relpath,
8810          apr_int64_t *repos_id,
8811          svn_revnum_t *changed_rev,
8812          apr_time_t *changed_date,
8813          const char **changed_author,
8814          svn_depth_t *depth,
8815          const svn_checksum_t **checksum,
8816          const char **target,
8817          const char **original_repos_relpath,
8818          apr_int64_t *original_repos_id,
8819          svn_revnum_t *original_revision,
8820          svn_wc__db_lock_t **lock,
8821          svn_filesize_t *recorded_size,
8822          apr_time_t *recorded_time,
8823          const char **changelist,
8824          svn_boolean_t *conflicted,
8825          svn_boolean_t *op_root,
8826          svn_boolean_t *had_props,
8827          svn_boolean_t *props_mod,
8828          svn_boolean_t *have_base,
8829          svn_boolean_t *have_more_work,
8830          svn_boolean_t *have_work,
8831          svn_wc__db_wcroot_t *wcroot,
8832          const char *local_relpath,
8833          apr_pool_t *result_pool,
8834          apr_pool_t *scratch_pool)
8835{
8836  svn_sqlite__stmt_t *stmt_info;
8837  svn_sqlite__stmt_t *stmt_act;
8838  svn_boolean_t have_info;
8839  svn_boolean_t have_act;
8840  svn_error_t *err = NULL;
8841
8842  /* Obtain the most likely to exist record first, to make sure we don't
8843     have to obtain the SQLite read-lock multiple times */
8844  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8845                                    lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8846                                         : STMT_SELECT_NODE_INFO));
8847  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8848  SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8849
8850  if (changelist || conflicted || props_mod)
8851    {
8852      SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8853                                        STMT_SELECT_ACTUAL_NODE));
8854      SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8855      SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8856    }
8857  else
8858    {
8859      have_act = FALSE;
8860      stmt_act = NULL;
8861    }
8862
8863  if (have_info)
8864    {
8865      int op_depth;
8866      svn_node_kind_t node_kind;
8867
8868      op_depth = svn_sqlite__column_int(stmt_info, 0);
8869      node_kind = column_token_err(&err, stmt_info, 4, kind_map);
8870
8871      if (status)
8872        {
8873          *status = column_token_err(&err, stmt_info, 3, presence_map);
8874
8875          if (op_depth != 0) /* WORKING */
8876            err = svn_error_compose_create(err,
8877                                           convert_to_working_status(status,
8878                                                                     *status));
8879        }
8880      if (kind)
8881        {
8882          *kind = node_kind;
8883        }
8884      if (op_depth != 0)
8885        {
8886          if (repos_id)
8887            *repos_id = INVALID_REPOS_ID;
8888          if (revision)
8889            *revision = SVN_INVALID_REVNUM;
8890          if (repos_relpath)
8891            /* Our path is implied by our parent somewhere up the tree.
8892               With the NULL value and status, the caller will know to
8893               search up the tree for the base of our path.  */
8894            *repos_relpath = NULL;
8895        }
8896      else
8897        {
8898          /* Fetch repository information. If we have a
8899             WORKING_NODE (and have been added), then the repository
8900             we're being added to will be dependent upon a parent. The
8901             caller can scan upwards to locate the repository.  */
8902          repos_location_from_columns(repos_id, revision, repos_relpath,
8903                                      stmt_info, 1, 5, 2, result_pool);
8904        }
8905      if (changed_rev)
8906        {
8907          *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8908        }
8909      if (changed_date)
8910        {
8911          *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8912        }
8913      if (changed_author)
8914        {
8915          *changed_author = svn_sqlite__column_text(stmt_info, 10,
8916                                                    result_pool);
8917        }
8918      if (recorded_time)
8919        {
8920          *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8921        }
8922      if (depth)
8923        {
8924          if (node_kind != svn_node_dir)
8925            *depth = svn_depth_unknown;
8926          else if (svn_sqlite__column_is_null(stmt_info, 11))
8927            *depth = svn_depth_unknown;
8928          else
8929            *depth = column_token_err(&err, stmt_info, 11, depth_map);
8930        }
8931      if (checksum)
8932        {
8933          if (node_kind != svn_node_file)
8934            {
8935              *checksum = NULL;
8936            }
8937          else
8938            {
8939
8940              err = svn_error_compose_create(
8941                        err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8942                                                         result_pool));
8943            }
8944        }
8945      if (recorded_size)
8946        {
8947          *recorded_size = get_recorded_size(stmt_info, 7);
8948        }
8949      if (target)
8950        {
8951          if (node_kind != svn_node_symlink)
8952            *target = NULL;
8953          else
8954            *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8955        }
8956      if (changelist)
8957        {
8958          if (have_act)
8959            *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8960          else
8961            *changelist = NULL;
8962        }
8963      if (op_depth == 0)
8964        {
8965          if (original_repos_id)
8966            *original_repos_id = INVALID_REPOS_ID;
8967          if (original_revision)
8968            *original_revision = SVN_INVALID_REVNUM;
8969          if (original_repos_relpath)
8970            *original_repos_relpath = NULL;
8971        }
8972      else
8973        {
8974          repos_location_from_columns(original_repos_id,
8975                                      original_revision,
8976                                      original_repos_relpath,
8977                                      stmt_info, 1, 5, 2, result_pool);
8978        }
8979      if (props_mod)
8980        {
8981          *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8982        }
8983      if (had_props)
8984        {
8985          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8986        }
8987      if (conflicted)
8988        {
8989          if (have_act)
8990            {
8991              *conflicted =
8992                 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8993            }
8994          else
8995            *conflicted = FALSE;
8996        }
8997
8998      if (lock)
8999        {
9000          if (op_depth != 0)
9001            *lock = NULL;
9002          else
9003            *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
9004        }
9005
9006      if (have_work)
9007        *have_work = (op_depth != 0);
9008
9009      if (op_root)
9010        {
9011          *op_root = ((op_depth > 0)
9012                      && (op_depth == relpath_depth(local_relpath)));
9013        }
9014
9015      if (have_base || have_more_work)
9016        {
9017          if (have_more_work)
9018            *have_more_work = FALSE;
9019
9020          while (!err && op_depth != 0)
9021            {
9022              err = svn_sqlite__step(&have_info, stmt_info);
9023
9024              if (err || !have_info)
9025                break;
9026
9027              op_depth = svn_sqlite__column_int(stmt_info, 0);
9028
9029              if (have_more_work)
9030                {
9031                  if (op_depth > 0)
9032                    *have_more_work = TRUE;
9033
9034                  if (!have_base)
9035                   break;
9036                }
9037            }
9038
9039          if (have_base)
9040            *have_base = (op_depth == 0);
9041        }
9042    }
9043  else if (have_act)
9044    {
9045      /* A row in ACTUAL_NODE should never exist without a corresponding
9046         node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
9047      if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
9048          err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
9049                                  _("Corrupt data for '%s'"),
9050                                  path_for_error_message(wcroot, local_relpath,
9051                                                         scratch_pool));
9052      /* ### What should we return?  Should we have a separate
9053             function for reading actual-only nodes? */
9054
9055      /* As a safety measure, until we decide if we want to use
9056         read_info for actual-only nodes, make sure the caller asked
9057         for the conflict status. */
9058      SVN_ERR_ASSERT(conflicted);
9059
9060      if (status)
9061        *status = svn_wc__db_status_normal;  /* What! No it's not! */
9062      if (kind)
9063        *kind = svn_node_unknown;
9064      if (revision)
9065        *revision = SVN_INVALID_REVNUM;
9066      if (repos_relpath)
9067        *repos_relpath = NULL;
9068      if (repos_id)
9069        *repos_id = INVALID_REPOS_ID;
9070      if (changed_rev)
9071        *changed_rev = SVN_INVALID_REVNUM;
9072      if (changed_date)
9073        *changed_date = 0;
9074      if (depth)
9075        *depth = svn_depth_unknown;
9076      if (checksum)
9077        *checksum = NULL;
9078      if (target)
9079        *target = NULL;
9080      if (original_repos_relpath)
9081        *original_repos_relpath = NULL;
9082      if (original_repos_id)
9083        *original_repos_id = INVALID_REPOS_ID;
9084      if (original_revision)
9085        *original_revision = SVN_INVALID_REVNUM;
9086      if (lock)
9087        *lock = NULL;
9088      if (recorded_size)
9089        *recorded_size = 0;
9090      if (recorded_time)
9091        *recorded_time = 0;
9092      if (changelist)
9093        *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
9094      if (op_root)
9095        *op_root = FALSE;
9096      if (had_props)
9097        *had_props = FALSE;
9098      if (props_mod)
9099        *props_mod = FALSE;
9100      if (conflicted)
9101        *conflicted = TRUE;
9102      if (have_base)
9103        *have_base = FALSE;
9104      if (have_more_work)
9105        *have_more_work = FALSE;
9106      if (have_work)
9107        *have_work = FALSE;
9108    }
9109  else
9110    {
9111      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9112                              _("The node '%s' was not found."),
9113                              path_for_error_message(wcroot, local_relpath,
9114                                                     scratch_pool));
9115    }
9116
9117  if (stmt_act != NULL)
9118    err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
9119
9120  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
9121    err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
9122                                local_relpath);
9123
9124  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
9125
9126  return SVN_NO_ERROR;
9127}
9128
9129
9130svn_error_t *
9131svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
9132                              svn_node_kind_t *kind,
9133                              svn_revnum_t *revision,
9134                              const char **repos_relpath,
9135                              apr_int64_t *repos_id,
9136                              svn_revnum_t *changed_rev,
9137                              apr_time_t *changed_date,
9138                              const char **changed_author,
9139                              svn_depth_t *depth,
9140                              const svn_checksum_t **checksum,
9141                              const char **target,
9142                              const char **original_repos_relpath,
9143                              apr_int64_t *original_repos_id,
9144                              svn_revnum_t *original_revision,
9145                              svn_wc__db_lock_t **lock,
9146                              svn_filesize_t *recorded_size,
9147                              apr_time_t *recorded_time,
9148                              const char **changelist,
9149                              svn_boolean_t *conflicted,
9150                              svn_boolean_t *op_root,
9151                              svn_boolean_t *had_props,
9152                              svn_boolean_t *props_mod,
9153                              svn_boolean_t *have_base,
9154                              svn_boolean_t *have_more_work,
9155                              svn_boolean_t *have_work,
9156                              svn_wc__db_wcroot_t *wcroot,
9157                              const char *local_relpath,
9158                              apr_pool_t *result_pool,
9159                              apr_pool_t *scratch_pool)
9160{
9161  return svn_error_trace(
9162           read_info(status, kind, revision, repos_relpath, repos_id,
9163                     changed_rev, changed_date, changed_author,
9164                     depth, checksum, target, original_repos_relpath,
9165                     original_repos_id, original_revision, lock,
9166                     recorded_size, recorded_time, changelist, conflicted,
9167                     op_root, had_props, props_mod,
9168                     have_base, have_more_work, have_work,
9169                     wcroot, local_relpath, result_pool, scratch_pool));
9170}
9171
9172
9173svn_error_t *
9174svn_wc__db_read_info(svn_wc__db_status_t *status,
9175                     svn_node_kind_t *kind,
9176                     svn_revnum_t *revision,
9177                     const char **repos_relpath,
9178                     const char **repos_root_url,
9179                     const char **repos_uuid,
9180                     svn_revnum_t *changed_rev,
9181                     apr_time_t *changed_date,
9182                     const char **changed_author,
9183                     svn_depth_t *depth,
9184                     const svn_checksum_t **checksum,
9185                     const char **target,
9186                     const char **original_repos_relpath,
9187                     const char **original_root_url,
9188                     const char **original_uuid,
9189                     svn_revnum_t *original_revision,
9190                     svn_wc__db_lock_t **lock,
9191                     svn_filesize_t *recorded_size,
9192                     apr_time_t *recorded_time,
9193                     const char **changelist,
9194                     svn_boolean_t *conflicted,
9195                     svn_boolean_t *op_root,
9196                     svn_boolean_t *have_props,
9197                     svn_boolean_t *props_mod,
9198                     svn_boolean_t *have_base,
9199                     svn_boolean_t *have_more_work,
9200                     svn_boolean_t *have_work,
9201                     svn_wc__db_t *db,
9202                     const char *local_abspath,
9203                     apr_pool_t *result_pool,
9204                     apr_pool_t *scratch_pool)
9205{
9206  svn_wc__db_wcroot_t *wcroot;
9207  const char *local_relpath;
9208  apr_int64_t repos_id, original_repos_id;
9209
9210  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9211
9212  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9213                              local_abspath, scratch_pool, scratch_pool));
9214  VERIFY_USABLE_WCROOT(wcroot);
9215
9216  SVN_WC__DB_WITH_TXN4(
9217          read_info(status, kind, revision, repos_relpath, &repos_id,
9218                    changed_rev, changed_date, changed_author,
9219                    depth, checksum, target, original_repos_relpath,
9220                    &original_repos_id, original_revision, lock,
9221                    recorded_size, recorded_time, changelist, conflicted,
9222                    op_root, have_props, props_mod,
9223                    have_base, have_more_work, have_work,
9224                    wcroot, local_relpath, result_pool, scratch_pool),
9225          svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
9226                                      wcroot, repos_id, result_pool),
9227          svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
9228                                      wcroot, original_repos_id,
9229                                      result_pool),
9230        SVN_NO_ERROR,
9231        wcroot);
9232
9233  return SVN_NO_ERROR;
9234}
9235
9236static svn_error_t *
9237is_wclocked(svn_boolean_t *locked,
9238            svn_wc__db_wcroot_t *wcroot,
9239            const char *dir_relpath,
9240            apr_pool_t *scratch_pool);
9241
9242/* Helper for read_children_info and single variant */
9243static svn_error_t *
9244find_conflict_descendants(svn_boolean_t *conflict_exists,
9245                          svn_wc__db_wcroot_t *wcroot,
9246                          const char *local_relpath,
9247                          apr_pool_t *scratch_pool)
9248{
9249  svn_sqlite__stmt_t *stmt;
9250
9251  /* Only used on files, so certainly not wcroot*/
9252  assert(local_relpath[0] != '\0');
9253
9254  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9255                                    STMT_FIND_CONFLICT_DESCENDANT));
9256
9257  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9258  SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
9259
9260  return svn_error_trace(svn_sqlite__reset(stmt));
9261}
9262
9263/* What we really want to store about a node.  This relies on the
9264   offset of svn_wc__db_info_t being zero. */
9265struct read_children_info_item_t
9266{
9267  struct svn_wc__db_info_t info;
9268  int op_depth;
9269  int nr_layers;
9270  svn_boolean_t was_dir;
9271};
9272
9273/* Implementation of svn_wc__db_read_children_info */
9274static svn_error_t *
9275read_children_info(svn_wc__db_wcroot_t *wcroot,
9276                   const char *dir_relpath,
9277                   apr_hash_t *conflicts,
9278                   apr_hash_t *nodes,
9279                   svn_boolean_t base_tree_only,
9280                   apr_pool_t *result_pool,
9281                   apr_pool_t *scratch_pool)
9282{
9283  svn_sqlite__stmt_t *stmt;
9284  svn_boolean_t have_row;
9285  const char *repos_root_url = NULL;
9286  const char *repos_uuid = NULL;
9287  apr_int64_t last_repos_id = INVALID_REPOS_ID;
9288  const char *last_repos_root_url = NULL;
9289
9290  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9291                                    (base_tree_only
9292                                     ? STMT_SELECT_BASE_NODE_CHILDREN_INFO
9293                                     : STMT_SELECT_NODE_CHILDREN_INFO)));
9294  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9295  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9296
9297  while (have_row)
9298    {
9299      /* CHILD item points to what we have about the node. We only provide
9300         CHILD->item to our caller. */
9301      struct read_children_info_item_t *child_item;
9302      const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
9303      const char *name = svn_relpath_basename(child_relpath, NULL);
9304      svn_error_t *err;
9305      int op_depth;
9306      svn_boolean_t new_child;
9307
9308      child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name));
9309      if (child_item)
9310        new_child = FALSE;
9311      else
9312        {
9313          child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9314          new_child = TRUE;
9315        }
9316
9317      op_depth = svn_sqlite__column_int(stmt, 0);
9318
9319      /* Do we have new or better information? */
9320      if (new_child)
9321        {
9322          struct svn_wc__db_info_t *child = &child_item->info;
9323          child_item->op_depth = op_depth;
9324
9325          child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
9326
9327          child->status = svn_sqlite__column_token(stmt, 3, presence_map);
9328          if (op_depth != 0)
9329            {
9330              if (child->status == svn_wc__db_status_incomplete)
9331                child->incomplete = TRUE;
9332              err = convert_to_working_status(&child->status, child->status);
9333              if (err)
9334                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9335            }
9336
9337          if (op_depth != 0)
9338            child->revnum = SVN_INVALID_REVNUM;
9339          else
9340            child->revnum = svn_sqlite__column_revnum(stmt, 5);
9341
9342          if (op_depth != 0)
9343            child->repos_relpath = NULL;
9344          else
9345            child->repos_relpath = svn_sqlite__column_text(stmt, 2,
9346                                                           result_pool);
9347
9348          if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
9349            {
9350              child->repos_root_url = NULL;
9351              child->repos_uuid = NULL;
9352            }
9353          else
9354            {
9355              apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
9356              if (!repos_root_url ||
9357                  (last_repos_id != INVALID_REPOS_ID &&
9358                   repos_id != last_repos_id))
9359                {
9360                  last_repos_root_url = repos_root_url;
9361                  err = svn_wc__db_fetch_repos_info(&repos_root_url,
9362                                                    &repos_uuid,
9363                                                    wcroot, repos_id,
9364                                                    result_pool);
9365                  if (err)
9366                    SVN_ERR(svn_error_compose_create(err,
9367                                                 svn_sqlite__reset(stmt)));
9368                }
9369
9370              if (last_repos_id == INVALID_REPOS_ID)
9371                last_repos_id = repos_id;
9372
9373              /* Assume working copy is all one repos_id so that a
9374                 single cached value is sufficient. */
9375              if (repos_id != last_repos_id)
9376                {
9377                  err= svn_error_createf(
9378                         SVN_ERR_WC_DB_ERROR, NULL,
9379                         _("The node '%s' comes from unexpected repository "
9380                           "'%s', expected '%s'; if this node is a file "
9381                           "external using the correct URL in the external "
9382                           "definition can fix the problem, see issue #4087"),
9383                         child_relpath, repos_root_url, last_repos_root_url);
9384                  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
9385                }
9386              child->repos_root_url = repos_root_url;
9387              child->repos_uuid = repos_uuid;
9388            }
9389
9390          child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
9391
9392          child->changed_date = svn_sqlite__column_int64(stmt, 9);
9393
9394          child->changed_author = svn_sqlite__column_text(stmt, 10,
9395                                                          result_pool);
9396
9397          if (child->kind != svn_node_dir)
9398            child->depth = svn_depth_unknown;
9399          else
9400            {
9401              child->has_descendants = TRUE;
9402              child_item->was_dir = TRUE;
9403              child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9404                                                           svn_depth_unknown);
9405              if (new_child)
9406                {
9407                  err = is_wclocked(&child->locked, wcroot, child_relpath,
9408                                    scratch_pool);
9409
9410                  if (err)
9411                    SVN_ERR(svn_error_compose_create(err,
9412                                                     svn_sqlite__reset(stmt)));
9413                }
9414            }
9415
9416          child->recorded_time = svn_sqlite__column_int64(stmt, 13);
9417          child->recorded_size = get_recorded_size(stmt, 7);
9418          child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
9419          child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
9420          child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9421#ifdef HAVE_SYMLINK
9422          if (child->had_props)
9423            {
9424              apr_hash_t *properties;
9425              err = svn_sqlite__column_properties(&properties, stmt, 14,
9426                                                  scratch_pool, scratch_pool);
9427              if (err)
9428                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9429
9430              child->special = (child->had_props
9431                                && svn_hash_gets(properties, SVN_PROP_SPECIAL));
9432            }
9433#endif
9434          if (op_depth == 0)
9435            child->op_root = FALSE;
9436          else
9437            child->op_root = (op_depth == relpath_depth(child_relpath));
9438
9439          if (op_depth && child->op_root)
9440            child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
9441
9442          if (new_child)
9443            svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
9444        }
9445      else if (!child_item->was_dir
9446               && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
9447        {
9448          child_item->was_dir = TRUE;
9449
9450          err = find_conflict_descendants(&child_item->info.has_descendants,
9451                                          wcroot, child_relpath,
9452                                          scratch_pool);
9453          if (err)
9454            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9455        }
9456
9457      if (op_depth == 0)
9458        {
9459          child_item->info.have_base = TRUE;
9460
9461          /* Get the lock info, available only at op_depth 0. */
9462          child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
9463                                                    result_pool);
9464
9465          /* FILE_EXTERNAL flag only on op_depth 0. */
9466          child_item->info.file_external = svn_sqlite__column_boolean(stmt,
9467                                                                      22);
9468        }
9469      else
9470        {
9471          const char *moved_to_relpath;
9472
9473          child_item->nr_layers++;
9474          child_item->info.have_more_work = (child_item->nr_layers > 1);
9475
9476
9477          /* A local_relpath can be moved multiple times at different op
9478             depths and it really depends on the caller what is interesting.
9479             We provide a simple linked list with the moved_from information */
9480
9481          moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
9482          if (moved_to_relpath)
9483            {
9484              struct svn_wc__db_moved_to_info_t *moved_to;
9485              struct svn_wc__db_moved_to_info_t **next;
9486              const char *shadow_op_relpath;
9487
9488              moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
9489              moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9490                                                           moved_to_relpath,
9491                                                           result_pool);
9492
9493              shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
9494                                                     scratch_pool);
9495
9496              moved_to->shadow_op_root_abspath =
9497                        svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9498                                        result_pool);
9499
9500              next = &child_item->info.moved_to;
9501
9502              while (*next &&
9503                     0 < strcmp((*next)->shadow_op_root_abspath,
9504                                moved_to->shadow_op_root_abspath))
9505                next = &((*next)->next);
9506
9507              moved_to->next = *next;
9508              *next = moved_to;
9509            }
9510        }
9511
9512      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9513    }
9514
9515  SVN_ERR(svn_sqlite__reset(stmt));
9516
9517  if (!base_tree_only)
9518    {
9519      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9520                                        STMT_SELECT_ACTUAL_CHILDREN_INFO));
9521      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9522      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9523
9524      while (have_row)
9525        {
9526          struct read_children_info_item_t *child_item;
9527          struct svn_wc__db_info_t *child;
9528          const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9529          const char *name = svn_relpath_basename(child_relpath, NULL);
9530
9531          child_item = svn_hash_gets(nodes, name);
9532          if (!child_item)
9533            {
9534              child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9535              child_item->info.status = svn_wc__db_status_not_present;
9536            }
9537
9538          child = &child_item->info;
9539
9540          child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9541
9542          child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9543#ifdef HAVE_SYMLINK
9544          if (child->props_mod)
9545            {
9546              svn_error_t *err;
9547              apr_hash_t *properties;
9548
9549              err = svn_sqlite__column_properties(&properties, stmt, 2,
9550                                                  scratch_pool, scratch_pool);
9551              if (err)
9552                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9553              child->special = (NULL != svn_hash_gets(properties,
9554                                                      SVN_PROP_SPECIAL));
9555            }
9556#endif
9557
9558          /* conflict */
9559          child->conflicted = !svn_sqlite__column_is_null(stmt, 3);
9560
9561          if (child->conflicted)
9562            svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9563
9564          SVN_ERR(svn_sqlite__step(&have_row, stmt));
9565        }
9566
9567      SVN_ERR(svn_sqlite__reset(stmt));
9568    }
9569
9570  return SVN_NO_ERROR;
9571}
9572
9573svn_error_t *
9574svn_wc__db_read_children_info(apr_hash_t **nodes,
9575                              apr_hash_t **conflicts,
9576                              svn_wc__db_t *db,
9577                              const char *dir_abspath,
9578                              svn_boolean_t base_tree_only,
9579                              apr_pool_t *result_pool,
9580                              apr_pool_t *scratch_pool)
9581{
9582  svn_wc__db_wcroot_t *wcroot;
9583  const char *dir_relpath;
9584
9585  *conflicts = apr_hash_make(result_pool);
9586  *nodes = apr_hash_make(result_pool);
9587  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9588
9589  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9590                                                dir_abspath,
9591                                                scratch_pool, scratch_pool));
9592  VERIFY_USABLE_WCROOT(wcroot);
9593
9594  SVN_WC__DB_WITH_TXN(
9595    read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9596                       base_tree_only, result_pool, scratch_pool),
9597    wcroot);
9598
9599  return SVN_NO_ERROR;
9600}
9601
9602/* Implementation of svn_wc__db_read_single_info.
9603
9604   ### This function is very similar to a lot of code inside
9605   read_children_info, but that performs some tricks to re-use data between
9606   different siblings.
9607
9608   ### We read the same few NODES records a few times via different helper
9609   functions, so this could be optimized bit, but everything is within
9610   a sqlite transaction and all queries are backed by an index, so generally
9611   everything (including the used indexes) should be in the sqlite page cache
9612   after the first query.
9613*/
9614static svn_error_t *
9615read_single_info(const struct svn_wc__db_info_t **info,
9616                 svn_wc__db_wcroot_t *wcroot,
9617                 const char *local_relpath,
9618                 svn_boolean_t base_tree_only,
9619                 apr_pool_t *result_pool,
9620                 apr_pool_t *scratch_pool)
9621{
9622  struct svn_wc__db_info_t *mtb;
9623  apr_int64_t repos_id;
9624  const svn_checksum_t *checksum;
9625  const char *original_repos_relpath;
9626  svn_boolean_t have_work;
9627  apr_hash_t *properties;
9628
9629  mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9630
9631  if (!base_tree_only)
9632    SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9633                      &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9634                      &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9635                      &checksum, NULL, &original_repos_relpath, NULL, NULL,
9636                      &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9637                      &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9638                      &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9639                      &mtb->have_more_work, &have_work,
9640                      wcroot, local_relpath, result_pool, scratch_pool));
9641  else
9642    {
9643      svn_boolean_t update_root;
9644
9645      have_work = FALSE;
9646      original_repos_relpath = NULL;
9647
9648      SVN_ERR(svn_wc__db_base_get_info_internal(
9649                  &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath,
9650                  &repos_id, &mtb->changed_rev, &mtb->changed_date,
9651                  &mtb->changed_author, &mtb->depth, &checksum, NULL,
9652                  &mtb->lock, &mtb->had_props, &properties, &update_root,
9653                  wcroot, local_relpath, scratch_pool, scratch_pool));
9654
9655      mtb->have_base = TRUE;
9656      mtb->file_external = (update_root && mtb->kind == svn_node_file);
9657    }
9658
9659  /* Query the same rows in the database again for move information */
9660  if (have_work && (mtb->have_base || mtb->have_more_work))
9661    {
9662      svn_sqlite__stmt_t *stmt;
9663      svn_boolean_t have_row;
9664
9665      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9666                                        STMT_SELECT_MOVED_TO_NODE));
9667      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9668
9669      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9670
9671      while (have_row)
9672        {
9673          struct svn_wc__db_moved_to_info_t *move;
9674          int op_depth = svn_sqlite__column_int(stmt, 0);
9675          const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9676          const char *cur_relpath;
9677
9678          move = apr_pcalloc(result_pool, sizeof(*move));
9679          move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9680                                                   moved_to_relpath,
9681                                                   result_pool);
9682
9683          cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
9684                                           scratch_pool);
9685
9686          move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9687                                                         cur_relpath,
9688                                                         result_pool);
9689
9690          move->next = mtb->moved_to;
9691          mtb->moved_to = move;
9692
9693          SVN_ERR(svn_sqlite__step(&have_row, stmt));
9694        }
9695
9696      SVN_ERR(svn_sqlite__reset(stmt));
9697    }
9698
9699  /* Maybe we have to get some shadowed lock from BASE to make our test suite
9700     happy... (It might be completely unrelated, but...)
9701     This queries the same BASE row again, joined to the lock table */
9702  if (!base_tree_only && mtb->have_base
9703      && (have_work || mtb->kind == svn_node_file))
9704    {
9705      svn_boolean_t update_root;
9706      svn_wc__db_lock_t **lock_arg = NULL;
9707
9708      if (have_work)
9709        lock_arg = &mtb->lock;
9710
9711      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9712                                                NULL, NULL, NULL, NULL, NULL,
9713                                                NULL, lock_arg, NULL, NULL,
9714                                                &update_root,
9715                                                wcroot, local_relpath,
9716                                                result_pool, scratch_pool));
9717
9718      mtb->file_external = (update_root && mtb->kind == svn_node_file);
9719    }
9720
9721  if (mtb->status == svn_wc__db_status_added)
9722    {
9723      svn_wc__db_status_t status;
9724
9725      SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9726                            NULL, NULL,
9727                            wcroot, local_relpath,
9728                            result_pool, scratch_pool));
9729
9730      mtb->moved_here = (status == svn_wc__db_status_moved_here);
9731      mtb->incomplete = (status == svn_wc__db_status_incomplete);
9732    }
9733
9734#ifdef HAVE_SYMLINK
9735  if (mtb->kind == svn_node_file
9736      && (mtb->had_props || mtb->props_mod
9737          || (base_tree_only && properties)))
9738    {
9739      if (!base_tree_only)
9740        {
9741          if (mtb->props_mod)
9742            SVN_ERR(svn_wc__db_read_props_internal(&properties,
9743                                                   wcroot, local_relpath,
9744                                                   scratch_pool, scratch_pool));
9745          else
9746            SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9747                                           TRUE /* deleted_ok */,
9748                                           scratch_pool, scratch_pool));
9749        }
9750
9751      mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9752    }
9753#endif
9754
9755  mtb->has_checksum = (checksum != NULL);
9756  mtb->copied = (original_repos_relpath != NULL);
9757
9758  SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9759                                      wcroot, repos_id, result_pool));
9760
9761  if (!base_tree_only && mtb->kind == svn_node_dir)
9762    SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9763
9764  if (mtb->kind == svn_node_dir)
9765    mtb->has_descendants = TRUE;
9766  else
9767    SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
9768                                      wcroot, local_relpath, scratch_pool));
9769
9770  *info = mtb;
9771
9772  return SVN_NO_ERROR;
9773}
9774
9775svn_error_t *
9776svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9777                            svn_wc__db_t *db,
9778                            const char *local_abspath,
9779                            svn_boolean_t base_tree_only,
9780                            apr_pool_t *result_pool,
9781                            apr_pool_t *scratch_pool)
9782{
9783  svn_wc__db_wcroot_t *wcroot;
9784  const char *local_relpath;
9785
9786  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9787
9788  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9789                                                local_abspath,
9790                                                scratch_pool, scratch_pool));
9791  VERIFY_USABLE_WCROOT(wcroot);
9792
9793  SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9794                                       base_tree_only,
9795                                       result_pool, scratch_pool),
9796                      wcroot);
9797
9798  return SVN_NO_ERROR;
9799}
9800
9801svn_error_t *
9802svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9803                              svn_node_kind_t *kind,
9804                              svn_revnum_t *changed_rev,
9805                              apr_time_t *changed_date,
9806                              const char **changed_author,
9807                              svn_depth_t *depth,  /* dirs only */
9808                              const svn_checksum_t **checksum, /* files only */
9809                              const char **target, /* symlinks only */
9810                              svn_boolean_t *had_props,
9811                              apr_hash_t **props,
9812                              svn_wc__db_t *db,
9813                              const char *local_abspath,
9814                              apr_pool_t *result_pool,
9815                              apr_pool_t *scratch_pool)
9816{
9817  svn_wc__db_wcroot_t *wcroot;
9818  const char *local_relpath;
9819  svn_sqlite__stmt_t *stmt;
9820  svn_boolean_t have_row;
9821  svn_error_t *err = NULL;
9822  int op_depth;
9823  svn_wc__db_status_t raw_status;
9824  svn_node_kind_t node_kind;
9825
9826  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9827
9828  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9829                                                local_abspath,
9830                                                scratch_pool, scratch_pool));
9831  VERIFY_USABLE_WCROOT(wcroot);
9832
9833  /* Obtain the most likely to exist record first, to make sure we don't
9834     have to obtain the SQLite read-lock multiple times */
9835  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9836                                    STMT_SELECT_NODE_INFO));
9837  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9838  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9839
9840  if (!have_row)
9841    {
9842      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9843                               svn_sqlite__reset(stmt),
9844                               _("The node '%s' was not found."),
9845                               path_for_error_message(wcroot,
9846                                                      local_relpath,
9847                                                      scratch_pool));
9848    }
9849
9850  op_depth = svn_sqlite__column_int(stmt, 0);
9851  raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9852
9853  if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9854    {
9855      SVN_ERR(svn_sqlite__step_row(stmt));
9856
9857      op_depth = svn_sqlite__column_int(stmt, 0);
9858      raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9859    }
9860
9861  node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9862
9863  if (status)
9864    {
9865      if (op_depth > 0)
9866        {
9867          err = svn_error_compose_create(err,
9868                                         convert_to_working_status(
9869                                                    status,
9870                                                    raw_status));
9871        }
9872      else
9873        *status = raw_status;
9874    }
9875  if (kind)
9876    {
9877      *kind = node_kind;
9878    }
9879  if (changed_rev)
9880    {
9881      *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9882    }
9883  if (changed_date)
9884    {
9885      *changed_date = svn_sqlite__column_int64(stmt, 9);
9886    }
9887  if (changed_author)
9888    {
9889      *changed_author = svn_sqlite__column_text(stmt, 10,
9890                                                result_pool);
9891    }
9892  if (depth)
9893    {
9894      if (node_kind != svn_node_dir)
9895        {
9896          *depth = svn_depth_unknown;
9897        }
9898      else
9899        {
9900          *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9901                                                 svn_depth_unknown);
9902        }
9903    }
9904  if (checksum)
9905    {
9906      if (node_kind != svn_node_file)
9907        {
9908          *checksum = NULL;
9909        }
9910      else
9911        {
9912          svn_error_t *err2;
9913          err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9914
9915          if (err2 != NULL)
9916            {
9917              if (err)
9918                err = svn_error_compose_create(
9919                         err,
9920                         svn_error_createf(
9921                               err->apr_err, err2,
9922                              _("The node '%s' has a corrupt checksum value."),
9923                              path_for_error_message(wcroot, local_relpath,
9924                                                     scratch_pool)));
9925              else
9926                err = err2;
9927            }
9928        }
9929    }
9930  if (target)
9931    {
9932      if (node_kind != svn_node_symlink)
9933        *target = NULL;
9934      else
9935        *target = svn_sqlite__column_text(stmt, 12, result_pool);
9936    }
9937  if (had_props)
9938    {
9939      *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9940    }
9941  if (props)
9942    {
9943      if (raw_status == svn_wc__db_status_normal
9944          || raw_status == svn_wc__db_status_incomplete)
9945        {
9946          SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9947                                                result_pool, scratch_pool));
9948          if (*props == NULL)
9949            *props = apr_hash_make(result_pool);
9950        }
9951      else
9952        {
9953          assert(svn_sqlite__column_is_null(stmt, 14));
9954          *props = NULL;
9955        }
9956    }
9957
9958  return svn_error_trace(
9959            svn_error_compose_create(err,
9960                                     svn_sqlite__reset(stmt)));
9961}
9962
9963svn_error_t *
9964svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
9965                                     svn_wc__db_t *db,
9966                                     const char *dir_abspath,
9967                                     apr_pool_t *result_pool,
9968                                     apr_pool_t *scratch_pool)
9969{
9970  svn_wc__db_wcroot_t *wcroot;
9971  const char *dir_relpath;
9972  svn_sqlite__stmt_t *stmt;
9973  svn_boolean_t have_row;
9974  apr_array_header_t *nodes;
9975
9976  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9977
9978  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9979                                             dir_abspath,
9980                                             scratch_pool, scratch_pool));
9981  VERIFY_USABLE_WCROOT(wcroot);
9982
9983  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9984                                    STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9985  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9986  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9987
9988  nodes = apr_array_make(result_pool, 16,
9989                          sizeof(struct svn_wc__db_walker_info_t *));
9990  while (have_row)
9991    {
9992      struct svn_wc__db_walker_info_t *child;
9993      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9994      const char *name = svn_relpath_basename(child_relpath, result_pool);
9995      int op_depth = svn_sqlite__column_int(stmt, 1);
9996      svn_error_t *err;
9997
9998      child = apr_palloc(result_pool, sizeof(*child));
9999      child->name = name;
10000      child->status = svn_sqlite__column_token(stmt, 2, presence_map);
10001      if (op_depth > 0)
10002        {
10003          err = convert_to_working_status(&child->status, child->status);
10004          if (err)
10005            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10006        }
10007      child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
10008
10009      APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
10010
10011      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10012    }
10013
10014  SVN_ERR(svn_sqlite__reset(stmt));
10015
10016  *items = nodes;
10017
10018  return SVN_NO_ERROR;
10019}
10020
10021svn_error_t *
10022svn_wc__db_read_node_install_info(const char **wcroot_abspath,
10023                                  const svn_checksum_t **sha1_checksum,
10024                                  apr_hash_t **pristine_props,
10025                                  apr_time_t *changed_date,
10026                                  svn_wc__db_t *db,
10027                                  const char *local_abspath,
10028                                  const char *wri_abspath,
10029                                  apr_pool_t *result_pool,
10030                                  apr_pool_t *scratch_pool)
10031{
10032  svn_wc__db_wcroot_t *wcroot;
10033  const char *local_relpath;
10034  svn_sqlite__stmt_t *stmt;
10035  svn_error_t *err = NULL;
10036  svn_boolean_t have_row;
10037
10038  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10039
10040  if (!wri_abspath)
10041    wri_abspath = local_abspath;
10042
10043  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10044                              wri_abspath, scratch_pool, scratch_pool));
10045  VERIFY_USABLE_WCROOT(wcroot);
10046
10047  if (local_abspath != wri_abspath
10048      && strcmp(local_abspath, wri_abspath))
10049    {
10050      if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
10051        return svn_error_createf(
10052                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10053                    _("The node '%s' is not in working copy '%s'"),
10054                    svn_dirent_local_style(local_abspath, scratch_pool),
10055                    svn_dirent_local_style(wcroot->abspath, scratch_pool));
10056
10057      local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
10058    }
10059
10060  if (wcroot_abspath != NULL)
10061    *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
10062
10063  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10064                                    STMT_SELECT_NODE_INFO));
10065
10066  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10067
10068  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10069
10070  if (have_row)
10071    {
10072      if (sha1_checksum)
10073        err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
10074
10075      if (!err && pristine_props)
10076        {
10077          err = svn_sqlite__column_properties(pristine_props, stmt, 14,
10078                                              result_pool, scratch_pool);
10079          /* Null means no props (assuming presence normal or incomplete). */
10080          if (*pristine_props == NULL)
10081            *pristine_props = apr_hash_make(result_pool);
10082        }
10083
10084      if (changed_date)
10085        *changed_date = svn_sqlite__column_int64(stmt, 9);
10086    }
10087  else
10088    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10089                             svn_sqlite__reset(stmt),
10090                             _("The node '%s' is not installable"),
10091                             svn_dirent_local_style(local_abspath,
10092                                                    scratch_pool));
10093
10094  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10095
10096  return SVN_NO_ERROR;
10097}
10098
10099
10100
10101/* The body of svn_wc__db_read_repos_info().
10102 */
10103static svn_error_t *
10104db_read_repos_info(svn_revnum_t *revision,
10105                   const char **repos_relpath,
10106                   apr_int64_t *repos_id,
10107                   svn_wc__db_wcroot_t *wcroot,
10108                   const char *local_relpath,
10109                   apr_pool_t *result_pool,
10110                   apr_pool_t *scratch_pool)
10111{
10112  svn_wc__db_status_t status;
10113
10114  SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
10115                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10116                    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10117                    NULL, NULL, NULL,
10118                    wcroot, local_relpath, result_pool, scratch_pool));
10119
10120  if ((repos_relpath && !*repos_relpath)
10121      || (repos_id && *repos_id == INVALID_REPOS_ID))
10122    {
10123      if (status == svn_wc__db_status_added)
10124        {
10125          SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10126                                NULL, NULL, NULL, NULL, NULL,
10127                                wcroot, local_relpath,
10128                                result_pool, scratch_pool));
10129        }
10130      else if (status == svn_wc__db_status_deleted)
10131        {
10132          const char *base_del_relpath;
10133          const char *work_del_relpath;
10134
10135          SVN_ERR(scan_deletion(&base_del_relpath, NULL,
10136                                &work_del_relpath,
10137                                NULL, wcroot,
10138                                local_relpath,
10139                                scratch_pool,
10140                                scratch_pool));
10141
10142          if (work_del_relpath)
10143            {
10144              /* The parent of the WORKING delete, must be an addition */
10145              const char *work_relpath = NULL;
10146
10147              /* work_del_relpath should not be NULL. However, we have
10148               * observed instances where that assumption was not met.
10149               * Bail out in that case instead of crashing with a segfault.
10150               */
10151              SVN_ERR_ASSERT(work_del_relpath != NULL);
10152              work_relpath = svn_relpath_dirname(work_del_relpath,
10153                                                 scratch_pool);
10154
10155              SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
10156                                    NULL, NULL, NULL, NULL, NULL, NULL,
10157                                    wcroot, work_relpath,
10158                                    scratch_pool, scratch_pool));
10159
10160              if (repos_relpath)
10161                *repos_relpath = svn_relpath_join(
10162                                    *repos_relpath,
10163                                    svn_dirent_skip_ancestor(work_relpath,
10164                                                             local_relpath),
10165                                    result_pool);
10166            }
10167          else
10168            {
10169              SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
10170                                                        repos_relpath,
10171                                                        repos_id,
10172                                                        NULL, NULL, NULL,
10173                                                        NULL, NULL, NULL,
10174                                                        NULL, NULL, NULL, NULL,
10175                                                        wcroot,
10176                                                        base_del_relpath,
10177                                                        scratch_pool,
10178                                                        scratch_pool));
10179
10180              if (repos_relpath)
10181                *repos_relpath = svn_relpath_join(
10182                                    *repos_relpath,
10183                                    svn_dirent_skip_ancestor(base_del_relpath,
10184                                                             local_relpath),
10185                                    result_pool);
10186            }
10187        }
10188      else if (status == svn_wc__db_status_excluded)
10189        {
10190          const char *parent_relpath;
10191          const char *name;
10192
10193          /* A BASE excluded would have had repository information, so
10194             we have a working exclude, which must be below an addition */
10195
10196          svn_relpath_split(&parent_relpath, &name, local_relpath,
10197                            scratch_pool);
10198          SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10199                                NULL, NULL, NULL, NULL, NULL,
10200                                wcroot, parent_relpath,
10201                                scratch_pool, scratch_pool));
10202
10203          if (repos_relpath)
10204            *repos_relpath = svn_relpath_join(*repos_relpath, name,
10205                                              result_pool);
10206
10207          return SVN_NO_ERROR;
10208        }
10209      else
10210        {
10211          /* All working statee are explicitly handled and all base statee
10212             have a repos_relpath */
10213          SVN_ERR_MALFUNCTION();
10214        }
10215    }
10216
10217  return SVN_NO_ERROR;
10218}
10219
10220
10221svn_error_t *
10222svn_wc__db_read_repos_info(svn_revnum_t *revision,
10223                           const char **repos_relpath,
10224                           const char **repos_root_url,
10225                           const char **repos_uuid,
10226                           svn_wc__db_t *db,
10227                           const char *local_abspath,
10228                           apr_pool_t *result_pool,
10229                           apr_pool_t *scratch_pool)
10230{
10231  svn_wc__db_wcroot_t *wcroot;
10232  const char *local_relpath;
10233  apr_int64_t repos_id = INVALID_REPOS_ID;
10234
10235  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10236
10237  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10238                                                local_abspath,
10239                                                scratch_pool, scratch_pool));
10240  VERIFY_USABLE_WCROOT(wcroot);
10241
10242  SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
10243                                          (repos_root_url || repos_uuid)
10244                                            ? &repos_id : NULL,
10245                                          wcroot, local_relpath,
10246                                          result_pool, scratch_pool),
10247                       svn_wc__db_fetch_repos_info(repos_root_url,
10248                                                   repos_uuid,
10249                                                   wcroot, repos_id,
10250                                                   result_pool),
10251                       SVN_NO_ERROR, SVN_NO_ERROR,
10252                       wcroot);
10253
10254  return SVN_NO_ERROR;
10255}
10256
10257
10258/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
10259   a hash table mapping <tt>char *</tt> names onto svn_string_t *
10260   values for any properties of immediate or recursive child nodes of
10261   LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
10262   If FILES_ONLY is true, only report properties for file child nodes.
10263   Check for cancellation between calls of RECEIVER_FUNC.
10264*/
10265typedef struct cache_props_baton_t
10266{
10267  svn_depth_t depth;
10268  svn_boolean_t pristine;
10269  const apr_array_header_t *changelists;
10270  svn_cancel_func_t cancel_func;
10271  void *cancel_baton;
10272} cache_props_baton_t;
10273
10274
10275static svn_error_t *
10276cache_props_recursive(void *cb_baton,
10277                      svn_wc__db_wcroot_t *wcroot,
10278                      const char *local_relpath,
10279                      apr_pool_t *scratch_pool)
10280{
10281  cache_props_baton_t *baton = cb_baton;
10282  svn_sqlite__stmt_t *stmt;
10283  int stmt_idx;
10284
10285  SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
10286                                baton->changelists, scratch_pool));
10287
10288  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
10289                                      STMT_CREATE_TARGET_PROP_CACHE));
10290
10291  if (baton->pristine)
10292    stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
10293  else
10294    stmt_idx = STMT_CACHE_TARGET_PROPS;
10295
10296  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
10297  SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
10298  SVN_ERR(svn_sqlite__step_done(stmt));
10299
10300  return SVN_NO_ERROR;
10301}
10302
10303
10304svn_error_t *
10305svn_wc__db_read_props_streamily(svn_wc__db_t *db,
10306                                const char *local_abspath,
10307                                svn_depth_t depth,
10308                                svn_boolean_t pristine,
10309                                const apr_array_header_t *changelists,
10310                                svn_wc__proplist_receiver_t receiver_func,
10311                                void *receiver_baton,
10312                                svn_cancel_func_t cancel_func,
10313                                void *cancel_baton,
10314                                apr_pool_t *scratch_pool)
10315{
10316  svn_wc__db_wcroot_t *wcroot;
10317  const char *local_relpath;
10318  svn_sqlite__stmt_t *stmt;
10319  cache_props_baton_t baton;
10320  svn_boolean_t have_row;
10321  apr_pool_t *iterpool;
10322  svn_error_t *err = NULL;
10323
10324  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10325  SVN_ERR_ASSERT(receiver_func);
10326  SVN_ERR_ASSERT((depth == svn_depth_files) ||
10327                 (depth == svn_depth_immediates) ||
10328                 (depth == svn_depth_infinity));
10329
10330  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10331                                                db, local_abspath,
10332                                                scratch_pool, scratch_pool));
10333  VERIFY_USABLE_WCROOT(wcroot);
10334
10335  baton.depth = depth;
10336  baton.pristine = pristine;
10337  baton.changelists = changelists;
10338  baton.cancel_func = cancel_func;
10339  baton.cancel_baton = cancel_baton;
10340
10341  SVN_ERR(with_finalization(wcroot, local_relpath,
10342                            cache_props_recursive, &baton,
10343                            NULL, NULL,
10344                            cancel_func, cancel_baton,
10345                            NULL, NULL,
10346                            STMT_DROP_TARGETS_LIST,
10347                            scratch_pool));
10348
10349  iterpool = svn_pool_create(scratch_pool);
10350
10351  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10352                                    STMT_SELECT_ALL_TARGET_PROP_CACHE));
10353  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10354  while (!err && have_row)
10355    {
10356      apr_hash_t *props;
10357
10358      svn_pool_clear(iterpool);
10359
10360      SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
10361                                            iterpool));
10362
10363      /* see if someone wants to cancel this operation. */
10364      if (cancel_func)
10365        err = cancel_func(cancel_baton);
10366
10367      if (!err && props && apr_hash_count(props) != 0)
10368        {
10369          const char *child_relpath;
10370          const char *child_abspath;
10371
10372          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
10373          child_abspath = svn_dirent_join(wcroot->abspath,
10374                                          child_relpath, iterpool);
10375
10376          err = receiver_func(receiver_baton, child_abspath, props, iterpool);
10377        }
10378
10379      err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
10380    }
10381
10382  err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
10383
10384  svn_pool_destroy(iterpool);
10385
10386  SVN_ERR(svn_error_compose_create(
10387                    err,
10388                    svn_sqlite__exec_statements(wcroot->sdb,
10389                                                STMT_DROP_TARGET_PROP_CACHE)));
10390  return SVN_NO_ERROR;
10391}
10392
10393
10394/* Helper for svn_wc__db_read_props().
10395 */
10396svn_error_t *
10397svn_wc__db_read_props_internal(apr_hash_t **props,
10398                               svn_wc__db_wcroot_t *wcroot,
10399                               const char *local_relpath,
10400                               apr_pool_t *result_pool,
10401                               apr_pool_t *scratch_pool)
10402{
10403  svn_sqlite__stmt_t *stmt;
10404  svn_boolean_t have_row;
10405  svn_error_t *err = NULL;
10406
10407  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10408                                    STMT_SELECT_ACTUAL_PROPS));
10409  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10410  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10411
10412  if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10413    {
10414      err = svn_sqlite__column_properties(props, stmt, 0,
10415                                          result_pool, scratch_pool);
10416    }
10417  else
10418    have_row = FALSE;
10419
10420  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10421
10422  if (have_row)
10423    return SVN_NO_ERROR;
10424
10425  /* No local changes. Return the pristine props for this node.  */
10426  SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
10427                                 result_pool, scratch_pool));
10428  if (*props == NULL)
10429    {
10430      /* Pristine properties are not defined for this node.
10431         ### we need to determine whether this node is in a state that
10432         ### allows for ACTUAL properties (ie. not deleted). for now,
10433         ### just say all nodes, no matter the state, have at least an
10434         ### empty set of props.  */
10435      *props = apr_hash_make(result_pool);
10436    }
10437
10438  return SVN_NO_ERROR;
10439}
10440
10441
10442svn_error_t *
10443svn_wc__db_read_props(apr_hash_t **props,
10444                      svn_wc__db_t *db,
10445                      const char *local_abspath,
10446                      apr_pool_t *result_pool,
10447                      apr_pool_t *scratch_pool)
10448{
10449  svn_wc__db_wcroot_t *wcroot;
10450  const char *local_relpath;
10451
10452  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10453
10454  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10455                              local_abspath, scratch_pool, scratch_pool));
10456  VERIFY_USABLE_WCROOT(wcroot);
10457
10458  SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot,
10459                                                     local_relpath,
10460                                                     result_pool,
10461                                                     scratch_pool),
10462                      wcroot);
10463
10464  return SVN_NO_ERROR;
10465}
10466
10467
10468static svn_error_t *
10469db_read_pristine_props(apr_hash_t **props,
10470                       svn_wc__db_wcroot_t *wcroot,
10471                       const char *local_relpath,
10472                       svn_boolean_t deleted_ok,
10473                       apr_pool_t *result_pool,
10474                       apr_pool_t *scratch_pool)
10475{
10476  svn_sqlite__stmt_t *stmt;
10477  svn_boolean_t have_row;
10478  svn_wc__db_status_t presence;
10479
10480  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
10481  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10482
10483  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10484
10485  if (!have_row)
10486    {
10487      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10488                               svn_sqlite__reset(stmt),
10489                               _("The node '%s' was not found."),
10490                               path_for_error_message(wcroot,
10491                                                      local_relpath,
10492                                                      scratch_pool));
10493    }
10494
10495
10496  /* Examine the presence: */
10497  presence = svn_sqlite__column_token(stmt, 1, presence_map);
10498
10499  /* For "base-deleted", it is obvious the pristine props are located
10500     below the current node. Fetch the NODE from the next record. */
10501  if (presence == svn_wc__db_status_base_deleted && deleted_ok)
10502    {
10503      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10504
10505      SVN_ERR_ASSERT(have_row);
10506
10507      presence = svn_sqlite__column_token(stmt, 1, presence_map);
10508    }
10509
10510  /* normal or copied: Fetch properties (during update we want
10511     properties for incomplete as well) */
10512  if (presence == svn_wc__db_status_normal
10513      || presence == svn_wc__db_status_incomplete)
10514    {
10515      svn_error_t *err;
10516
10517      err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
10518                                          scratch_pool);
10519      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10520
10521      if (!*props)
10522        *props = apr_hash_make(result_pool);
10523
10524      return SVN_NO_ERROR;
10525    }
10526
10527  return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
10528                           svn_sqlite__reset(stmt),
10529                           _("The node '%s' has a status that"
10530                             " has no properties."),
10531                           path_for_error_message(wcroot,
10532                                                  local_relpath,
10533                                                  scratch_pool));
10534}
10535
10536
10537svn_error_t *
10538svn_wc__db_read_pristine_props(apr_hash_t **props,
10539                               svn_wc__db_t *db,
10540                               const char *local_abspath,
10541                               apr_pool_t *result_pool,
10542                               apr_pool_t *scratch_pool)
10543{
10544  svn_wc__db_wcroot_t *wcroot;
10545  const char *local_relpath;
10546
10547  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10548
10549  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10550                              local_abspath, scratch_pool, scratch_pool));
10551  VERIFY_USABLE_WCROOT(wcroot);
10552
10553  SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10554                                 result_pool, scratch_pool));
10555  return SVN_NO_ERROR;
10556}
10557
10558svn_error_t *
10559svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10560                                   svn_wc__db_t *db,
10561                                   const char *local_abspath,
10562                                   const char *propname,
10563                                   apr_pool_t *result_pool,
10564                                   apr_pool_t *scratch_pool)
10565{
10566  svn_wc__db_wcroot_t *wcroot;
10567  const char *local_relpath;
10568  svn_sqlite__stmt_t *stmt;
10569  svn_boolean_t have_row;
10570  apr_pool_t *iterpool;
10571
10572  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10573
10574  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10575                              local_abspath, scratch_pool, scratch_pool));
10576  VERIFY_USABLE_WCROOT(wcroot);
10577
10578  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10579                                    STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10580  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10581
10582  *values = apr_hash_make(result_pool);
10583
10584  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10585  iterpool = svn_pool_create(scratch_pool);
10586  while (have_row)
10587  {
10588    apr_hash_t *node_props;
10589    svn_string_t *value;
10590
10591    svn_pool_clear(iterpool);
10592
10593    SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10594                                          iterpool, iterpool));
10595
10596    value = (node_props
10597                ? svn_hash_gets(node_props, propname)
10598                : NULL);
10599
10600    if (value)
10601      {
10602        svn_hash_sets(*values,
10603                      svn_dirent_join(wcroot->abspath,
10604                                      svn_sqlite__column_text(stmt, 1, NULL),
10605                                      result_pool),
10606                      svn_string_dup(value, result_pool));
10607      }
10608
10609    SVN_ERR(svn_sqlite__step(&have_row, stmt));
10610  }
10611
10612  svn_pool_destroy(iterpool);
10613
10614  return svn_error_trace(svn_sqlite__reset(stmt));
10615}
10616
10617/* The body of svn_wc__db_read_cached_iprops(). */
10618static svn_error_t *
10619db_read_cached_iprops(apr_array_header_t **iprops,
10620                      svn_wc__db_wcroot_t *wcroot,
10621                      const char *local_relpath,
10622                      apr_pool_t *result_pool,
10623                      apr_pool_t *scratch_pool)
10624{
10625  svn_sqlite__stmt_t *stmt;
10626  svn_boolean_t have_row;
10627
10628  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10629  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10630  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10631
10632  if (!have_row)
10633    {
10634      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10635                               svn_sqlite__reset(stmt),
10636                               _("The node '%s' was not found."),
10637                               path_for_error_message(wcroot, local_relpath,
10638                                                      scratch_pool));
10639    }
10640
10641  SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10642                                    result_pool, scratch_pool));
10643
10644  SVN_ERR(svn_sqlite__reset(stmt));
10645
10646  return SVN_NO_ERROR;
10647}
10648
10649svn_error_t *
10650svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10651                              svn_wc__db_t *db,
10652                              const char *local_abspath,
10653                              apr_pool_t *result_pool,
10654                              apr_pool_t *scratch_pool)
10655{
10656  svn_wc__db_wcroot_t *wcroot;
10657  const char *local_relpath;
10658
10659  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10660
10661  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10662                                                db, local_abspath,
10663                                                scratch_pool, scratch_pool));
10664  VERIFY_USABLE_WCROOT(wcroot);
10665
10666  /* Don't use with_txn yet, as we perform just a single transaction */
10667  SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10668                                result_pool, scratch_pool));
10669
10670  if (!*iprops)
10671    {
10672      *iprops = apr_array_make(result_pool, 0,
10673                               sizeof(svn_prop_inherited_item_t *));
10674    }
10675
10676  return SVN_NO_ERROR;
10677}
10678
10679/* Remove all prop name value pairs from PROP_HASH where the property
10680   name is not PROPNAME. */
10681static void
10682filter_unwanted_props(apr_hash_t *prop_hash,
10683                      const char * propname,
10684                      apr_pool_t *scratch_pool)
10685{
10686  apr_hash_index_t *hi;
10687
10688  for (hi = apr_hash_first(scratch_pool, prop_hash);
10689       hi;
10690       hi = apr_hash_next(hi))
10691    {
10692      const char *ipropname = apr_hash_this_key(hi);
10693
10694      if (strcmp(ipropname, propname) != 0)
10695        svn_hash_sets(prop_hash, ipropname, NULL);
10696    }
10697  return;
10698}
10699
10700/* Get the changed properties as stored in the ACTUAL table */
10701static svn_error_t *
10702db_get_changed_props(apr_hash_t **actual_props,
10703                     svn_wc__db_wcroot_t *wcroot,
10704                     const char *local_relpath,
10705                     apr_pool_t *result_pool,
10706                     apr_pool_t *scratch_pool)
10707{
10708  svn_sqlite__stmt_t *stmt;
10709  svn_boolean_t have_row;
10710  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10711                                STMT_SELECT_ACTUAL_PROPS));
10712  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10713  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10714
10715  if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10716    SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10717                                          result_pool, scratch_pool));
10718  else
10719    *actual_props = NULL; /* Cached when we read that record */
10720
10721  return svn_error_trace(svn_sqlite__reset(stmt));
10722}
10723
10724/* The body of svn_wc__db_read_inherited_props().  */
10725static svn_error_t *
10726db_read_inherited_props(apr_array_header_t **inherited_props,
10727                        apr_hash_t **actual_props,
10728                        svn_wc__db_wcroot_t *wcroot,
10729                        const char *local_relpath,
10730                        const char *propname,
10731                        apr_pool_t *result_pool,
10732                        apr_pool_t *scratch_pool)
10733{
10734  int i;
10735  apr_array_header_t *cached_iprops = NULL;
10736  apr_array_header_t *iprops;
10737  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10738  svn_sqlite__stmt_t *stmt;
10739  const char *relpath;
10740  const char *expected_parent_repos_relpath = NULL;
10741  const char *parent_relpath;
10742
10743  iprops = apr_array_make(result_pool, 1,
10744                           sizeof(svn_prop_inherited_item_t *));
10745  *inherited_props = iprops;
10746
10747  if (actual_props)
10748    *actual_props = NULL;
10749
10750  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10751                                    STMT_SELECT_NODE_INFO));
10752
10753  relpath = local_relpath;
10754
10755  /* Walk up to the root of the WC looking for inherited properties.  When we
10756     reach the WC root also check for cached inherited properties. */
10757  for (relpath = local_relpath; relpath; relpath = parent_relpath)
10758    {
10759      svn_boolean_t have_row;
10760      int op_depth;
10761      svn_wc__db_status_t status;
10762      apr_hash_t *node_props;
10763
10764      parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10765                                  : NULL;
10766
10767      svn_pool_clear(iterpool);
10768
10769      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10770
10771      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10772
10773      if (!have_row)
10774        return svn_error_createf(
10775                    SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10776                    _("The node '%s' was not found."),
10777                    path_for_error_message(wcroot, relpath,
10778                                           scratch_pool));
10779
10780      op_depth = svn_sqlite__column_int(stmt, 0);
10781
10782      status = svn_sqlite__column_token(stmt, 3, presence_map);
10783
10784      if (status != svn_wc__db_status_normal
10785          && status != svn_wc__db_status_incomplete)
10786        return svn_error_createf(
10787                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10788                    _("The node '%s' has a status that has no properties."),
10789                    path_for_error_message(wcroot, relpath,
10790                                           scratch_pool));
10791
10792      if (op_depth > 0)
10793        {
10794          /* WORKING node. Nothing to check */
10795        }
10796      else if (expected_parent_repos_relpath)
10797        {
10798          const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10799
10800          if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10801            {
10802              /* The child of this node has a different parent than this node
10803                 (It is "switched"), so we can stop here. Note that switched
10804                 with the same parent is not interesting for us here. */
10805              SVN_ERR(svn_sqlite__reset(stmt));
10806              break;
10807            }
10808
10809          expected_parent_repos_relpath =
10810              svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10811        }
10812      else
10813        {
10814          const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10815
10816          expected_parent_repos_relpath =
10817              svn_relpath_dirname(repos_relpath, scratch_pool);
10818        }
10819
10820      if (op_depth == 0
10821          && !svn_sqlite__column_is_null(stmt, 16))
10822        {
10823          /* The node contains a cache. No reason to look further */
10824          SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10825                                            result_pool, iterpool));
10826
10827          parent_relpath = NULL; /* Stop after this */
10828        }
10829
10830      SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10831                                            iterpool, iterpool));
10832
10833      SVN_ERR(svn_sqlite__reset(stmt));
10834
10835      /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10836         can inherit properties from it. */
10837      if (relpath != local_relpath)
10838        {
10839          apr_hash_t *changed_props;
10840
10841          SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10842                                       result_pool, iterpool));
10843
10844          if (changed_props)
10845            node_props = changed_props;
10846          else if (node_props)
10847            node_props = svn_prop_hash_dup(node_props, result_pool);
10848
10849          if (node_props && apr_hash_count(node_props))
10850            {
10851              /* If we only want PROPNAME filter out any other properties. */
10852              if (propname)
10853                filter_unwanted_props(node_props, propname, iterpool);
10854
10855              if (apr_hash_count(node_props))
10856                {
10857                  svn_prop_inherited_item_t *iprop_elt =
10858                    apr_pcalloc(result_pool,
10859                                sizeof(svn_prop_inherited_item_t));
10860                  iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10861                                                           relpath,
10862                                                           result_pool);
10863
10864                  iprop_elt->prop_hash = node_props;
10865                  /* Build the output array in depth-first order. */
10866                  svn_sort__array_insert(iprops, &iprop_elt, 0);
10867                }
10868            }
10869        }
10870      else if (actual_props)
10871        {
10872          apr_hash_t *changed_props;
10873
10874          SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10875                                       result_pool, iterpool));
10876
10877          if (changed_props)
10878            *actual_props = changed_props;
10879          else if (node_props)
10880            *actual_props = svn_prop_hash_dup(node_props, result_pool);
10881        }
10882    }
10883
10884  if (cached_iprops)
10885    {
10886      for (i = cached_iprops->nelts - 1; i >= 0; i--)
10887        {
10888          svn_prop_inherited_item_t *cached_iprop =
10889            APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10890
10891          /* An empty property hash in the iprops cache means there are no
10892             inherited properties. */
10893          if (apr_hash_count(cached_iprop->prop_hash) == 0)
10894            continue;
10895
10896          if (propname)
10897            filter_unwanted_props(cached_iprop->prop_hash, propname,
10898                                  scratch_pool);
10899
10900          /* If we didn't filter everything then keep this iprop. */
10901          if (apr_hash_count(cached_iprop->prop_hash))
10902            svn_sort__array_insert(iprops, &cached_iprop, 0);
10903        }
10904    }
10905
10906  if (actual_props && !*actual_props)
10907    *actual_props = apr_hash_make(result_pool);
10908
10909  svn_pool_destroy(iterpool);
10910  return SVN_NO_ERROR;
10911}
10912
10913svn_error_t *
10914svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10915                                apr_hash_t **actual_props,
10916                                svn_wc__db_t *db,
10917                                const char *local_abspath,
10918                                const char *propname,
10919                                apr_pool_t *result_pool,
10920                                apr_pool_t *scratch_pool)
10921{
10922  svn_wc__db_wcroot_t *wcroot;
10923  const char *local_relpath;
10924
10925  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10926
10927  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10928                                                db, local_abspath,
10929                                                scratch_pool, scratch_pool));
10930  VERIFY_USABLE_WCROOT(wcroot);
10931
10932  SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10933                                              wcroot, local_relpath, propname,
10934                                              result_pool, scratch_pool),
10935                      wcroot);
10936
10937  return SVN_NO_ERROR;
10938}
10939
10940/* The body of svn_wc__db_get_children_with_cached_iprops().
10941 */
10942static svn_error_t *
10943get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10944                                svn_wc__db_wcroot_t *wcroot,
10945                                const char *local_relpath,
10946                                svn_depth_t depth,
10947                                apr_pool_t *result_pool,
10948                                apr_pool_t *scratch_pool)
10949{
10950  svn_sqlite__stmt_t *stmt;
10951  svn_boolean_t have_row;
10952
10953  *iprop_paths = apr_hash_make(result_pool);
10954
10955  /* First check if LOCAL_RELPATH itself has iprops */
10956  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10957                                    STMT_SELECT_IPROPS_NODE));
10958  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10959  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10960
10961  if (have_row)
10962   {
10963      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10964                                                               NULL);
10965      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10966                                                       relpath_with_cache,
10967                                                       result_pool);
10968      svn_hash_sets(*iprop_paths, abspath_with_cache,
10969                    svn_sqlite__column_text(stmt, 1, result_pool));
10970    }
10971  SVN_ERR(svn_sqlite__reset(stmt));
10972
10973  if (depth == svn_depth_empty)
10974    return SVN_NO_ERROR;
10975
10976  /* Now fetch information for children or all descendants */
10977  if (depth == svn_depth_files
10978      || depth == svn_depth_immediates)
10979    {
10980      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10981                                        STMT_SELECT_IPROPS_CHILDREN));
10982    }
10983  else /* Default to svn_depth_infinity. */
10984    {
10985      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10986                                        STMT_SELECT_IPROPS_RECURSIVE));
10987    }
10988
10989  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10990  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10991
10992  while (have_row)
10993    {
10994      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10995                                                               NULL);
10996      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10997                                                       relpath_with_cache,
10998                                                       result_pool);
10999      svn_hash_sets(*iprop_paths, abspath_with_cache,
11000                    svn_sqlite__column_text(stmt, 1, result_pool));
11001      SVN_ERR(svn_sqlite__step(&have_row, stmt));
11002    }
11003
11004  SVN_ERR(svn_sqlite__reset(stmt));
11005
11006  /* For depth files we should filter non files */
11007  if (depth == svn_depth_files)
11008    {
11009      apr_hash_index_t *hi;
11010      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11011
11012      for (hi = apr_hash_first(scratch_pool, *iprop_paths);
11013           hi;
11014           hi = apr_hash_next(hi))
11015        {
11016          const char *child_abspath = apr_hash_this_key(hi);
11017          const char *child_relpath;
11018          svn_node_kind_t child_kind;
11019
11020          svn_pool_clear(iterpool);
11021
11022          child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
11023                                              NULL);
11024
11025          if (! child_relpath)
11026            {
11027              continue; /* local_relpath itself */
11028            }
11029
11030          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
11031                                                    NULL, NULL, NULL, NULL,
11032                                                    NULL, NULL, NULL, NULL,
11033                                                    NULL, NULL, NULL, NULL,
11034                                                    wcroot, child_relpath,
11035                                                    scratch_pool,
11036                                                    scratch_pool));
11037
11038          /* Filter if not a file */
11039          if (child_kind != svn_node_file)
11040            {
11041              svn_hash_sets(*iprop_paths, child_abspath, NULL);
11042            }
11043        }
11044
11045      svn_pool_destroy(iterpool);
11046    }
11047
11048  return SVN_NO_ERROR;
11049}
11050
11051svn_error_t *
11052svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
11053                                           svn_depth_t depth,
11054                                           const char *local_abspath,
11055                                           svn_wc__db_t *db,
11056                                           apr_pool_t *result_pool,
11057                                           apr_pool_t *scratch_pool)
11058{
11059  svn_wc__db_wcroot_t *wcroot;
11060  const char *local_relpath;
11061
11062  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11063
11064  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11065                                                local_abspath, scratch_pool,
11066                                                scratch_pool));
11067  VERIFY_USABLE_WCROOT(wcroot);
11068
11069  SVN_WC__DB_WITH_TXN(
11070    get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
11071                                    depth, result_pool, scratch_pool),
11072    wcroot);
11073
11074  return SVN_NO_ERROR;
11075}
11076
11077svn_error_t *
11078svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
11079                                         svn_wc__db_t *db,
11080                                         const char *local_abspath,
11081                                         apr_pool_t *result_pool,
11082                                         apr_pool_t *scratch_pool)
11083{
11084  svn_wc__db_wcroot_t *wcroot;
11085  const char *local_relpath;
11086
11087  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11088
11089  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11090                                             local_abspath,
11091                                             scratch_pool, scratch_pool));
11092  VERIFY_USABLE_WCROOT(wcroot);
11093
11094  return svn_error_trace(
11095          gather_children(children, wcroot, local_relpath,
11096                          STMT_SELECT_WORKING_CHILDREN, -1,
11097                          result_pool, scratch_pool));
11098}
11099
11100svn_error_t *
11101svn_wc__db_base_read_not_present_children(
11102                                const apr_array_header_t **children,
11103                                svn_wc__db_t *db,
11104                                const char *local_abspath,
11105                                apr_pool_t *result_pool,
11106                                apr_pool_t *scratch_pool)
11107{
11108  svn_wc__db_wcroot_t *wcroot;
11109  const char *local_relpath;
11110
11111  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11112
11113  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11114                                             local_abspath,
11115                                             scratch_pool, scratch_pool));
11116  VERIFY_USABLE_WCROOT(wcroot);
11117
11118  return svn_error_trace(
11119          gather_children(children, wcroot, local_relpath,
11120                          STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
11121                          result_pool, scratch_pool));
11122}
11123
11124/* Helper for svn_wc__db_node_check_replace().
11125 */
11126static svn_error_t *
11127check_replace_txn(svn_boolean_t *is_replace_root_p,
11128                  svn_boolean_t *base_replace_p,
11129                  svn_boolean_t *is_replace_p,
11130                  svn_wc__db_wcroot_t *wcroot,
11131                  const char *local_relpath,
11132                  apr_pool_t *scratch_pool)
11133{
11134  svn_sqlite__stmt_t *stmt;
11135  svn_boolean_t have_row;
11136  svn_boolean_t is_replace = FALSE;
11137  int replaced_op_depth;
11138  svn_wc__db_status_t replaced_status;
11139
11140  /* Our caller initialized the output values to FALSE */
11141
11142  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11143                                    STMT_SELECT_NODE_INFO));
11144
11145  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11146
11147  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11148
11149  if (!have_row)
11150    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11151                             svn_sqlite__reset(stmt),
11152                             _("The node '%s' was not found."),
11153                             path_for_error_message(wcroot, local_relpath,
11154                                                    scratch_pool));
11155
11156  {
11157    svn_wc__db_status_t status;
11158
11159    status = svn_sqlite__column_token(stmt, 3, presence_map);
11160
11161    if (status != svn_wc__db_status_normal)
11162      return svn_error_trace(svn_sqlite__reset(stmt));
11163  }
11164
11165  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11166
11167  if (!have_row)
11168    return svn_error_trace(svn_sqlite__reset(stmt));
11169
11170  replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
11171
11172  /* If the layer below the add describes a not present or a deleted node,
11173     this is not a replacement. Deleted can only occur if an ancestor is
11174     the delete root. */
11175  if (replaced_status != svn_wc__db_status_not_present
11176      && replaced_status != svn_wc__db_status_excluded
11177      && replaced_status != svn_wc__db_status_server_excluded
11178      && replaced_status != svn_wc__db_status_base_deleted)
11179    {
11180      is_replace = TRUE;
11181      if (is_replace_p)
11182        *is_replace_p = TRUE;
11183    }
11184
11185  replaced_op_depth = svn_sqlite__column_int(stmt, 0);
11186
11187  if (base_replace_p)
11188    {
11189      int op_depth = svn_sqlite__column_int(stmt, 0);
11190
11191      while (op_depth != 0 && have_row)
11192        {
11193          SVN_ERR(svn_sqlite__step(&have_row, stmt));
11194
11195          if (have_row)
11196            op_depth = svn_sqlite__column_int(stmt, 0);
11197        }
11198
11199      if (have_row && op_depth == 0)
11200        {
11201          svn_wc__db_status_t base_status;
11202
11203          base_status = svn_sqlite__column_token(stmt, 3, presence_map);
11204
11205          *base_replace_p = (base_status != svn_wc__db_status_not_present);
11206        }
11207    }
11208
11209  SVN_ERR(svn_sqlite__reset(stmt));
11210
11211  if (!is_replace_root_p || !is_replace)
11212    return SVN_NO_ERROR;
11213
11214  if (replaced_status != svn_wc__db_status_base_deleted)
11215    {
11216      int parent_op_depth;
11217
11218      /* Check the current op-depth of the parent to see if we are a replacement
11219         root */
11220      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11221                                svn_relpath_dirname(local_relpath,
11222                                                    scratch_pool)));
11223
11224      SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
11225
11226      parent_op_depth = svn_sqlite__column_int(stmt, 0);
11227
11228      if (parent_op_depth >= replaced_op_depth)
11229        {
11230          /* Did we replace inside our directory? */
11231
11232          *is_replace_root_p = (parent_op_depth == replaced_op_depth);
11233          SVN_ERR(svn_sqlite__reset(stmt));
11234          return SVN_NO_ERROR;
11235        }
11236
11237      SVN_ERR(svn_sqlite__step(&have_row, stmt));
11238
11239      if (have_row)
11240        parent_op_depth = svn_sqlite__column_int(stmt, 0);
11241
11242      SVN_ERR(svn_sqlite__reset(stmt));
11243
11244      if (!have_row)
11245        *is_replace_root_p = TRUE; /* Parent is no replacement */
11246      else if (parent_op_depth < replaced_op_depth)
11247        *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
11248      /*else // No replacement root */
11249  }
11250
11251  return SVN_NO_ERROR;
11252}
11253
11254svn_error_t *
11255svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
11256                              svn_boolean_t *base_replace,
11257                              svn_boolean_t *is_replace,
11258                              svn_wc__db_t *db,
11259                              const char *local_abspath,
11260                              apr_pool_t *scratch_pool)
11261{
11262  svn_wc__db_wcroot_t *wcroot;
11263  const char *local_relpath;
11264
11265  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11266
11267  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11268                                             local_abspath,
11269                                             scratch_pool, scratch_pool));
11270  VERIFY_USABLE_WCROOT(wcroot);
11271
11272  if (is_replace_root)
11273    *is_replace_root = FALSE;
11274  if (base_replace)
11275    *base_replace = FALSE;
11276  if (is_replace)
11277    *is_replace = FALSE;
11278
11279  if (local_relpath[0] == '\0')
11280    return SVN_NO_ERROR; /* Working copy root can't be replaced */
11281
11282  SVN_WC__DB_WITH_TXN(
11283    check_replace_txn(is_replace_root, base_replace, is_replace,
11284                      wcroot, local_relpath, scratch_pool),
11285    wcroot);
11286
11287  return SVN_NO_ERROR;
11288}
11289
11290svn_error_t *
11291svn_wc__db_read_children(const apr_array_header_t **children,
11292                         svn_wc__db_t *db,
11293                         const char *local_abspath,
11294                         apr_pool_t *result_pool,
11295                         apr_pool_t *scratch_pool)
11296{
11297  svn_wc__db_wcroot_t *wcroot;
11298  const char *local_relpath;
11299
11300  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11301
11302  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11303                                             local_abspath,
11304                                             scratch_pool, scratch_pool));
11305  VERIFY_USABLE_WCROOT(wcroot);
11306
11307  return gather_children(children, wcroot, local_relpath,
11308                         STMT_SELECT_NODE_CHILDREN, -1,
11309                         result_pool, scratch_pool);
11310}
11311
11312
11313/* Implementation of svn_wc__db_global_relocate */
11314static svn_error_t *
11315relocate_txn(svn_wc__db_wcroot_t *wcroot,
11316             const char *local_relpath,
11317             const char *repos_root_url,
11318             apr_pool_t *scratch_pool)
11319{
11320  svn_sqlite__stmt_t *stmt;
11321  apr_int64_t new_repos_id;
11322  const char *local_dir_relpath;
11323  svn_wc__db_status_t status;
11324  const char *repos_uuid;
11325  svn_boolean_t have_base_node;
11326  apr_int64_t old_repos_id;
11327
11328  local_dir_relpath = local_relpath;
11329
11330  SVN_ERR(read_info(&status,
11331                    NULL, NULL, NULL, &old_repos_id,
11332                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11333                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11334                    NULL,
11335                    &have_base_node, NULL, NULL,
11336                    wcroot, local_relpath,
11337                    scratch_pool, scratch_pool));
11338
11339  if (status == svn_wc__db_status_excluded)
11340    {
11341      /* The parent cannot be excluded, so look at the parent and then
11342         adjust the relpath */
11343      const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
11344                                                       scratch_pool);
11345      SVN_ERR(read_info(&status,
11346                        NULL, NULL, NULL, &old_repos_id,
11347                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11348                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11349                        NULL, NULL, NULL,
11350                        NULL, NULL, NULL,
11351                        wcroot, parent_relpath,
11352                        scratch_pool, scratch_pool));
11353      local_dir_relpath = parent_relpath;
11354    }
11355
11356  if (old_repos_id == INVALID_REPOS_ID)
11357    {
11358      /* Do we need to support relocating something that is
11359         added/deleted/excluded without relocating the parent?  If not
11360         then perhaps relpath, root_url and uuid should be passed down
11361         to the children so that they don't have to scan? */
11362
11363      if (status == svn_wc__db_status_deleted)
11364        {
11365          const char *work_del_relpath;
11366
11367          SVN_ERR(scan_deletion(NULL, NULL,
11368                                &work_del_relpath, NULL,
11369                                wcroot, local_dir_relpath,
11370                                scratch_pool,
11371                                scratch_pool));
11372          if (work_del_relpath)
11373            {
11374              /* Deleted within a copy/move */
11375
11376              /* The parent of the delete is added. */
11377              status = svn_wc__db_status_added;
11378              local_dir_relpath = svn_relpath_dirname(work_del_relpath,
11379                                                      scratch_pool);
11380            }
11381        }
11382
11383      if (status == svn_wc__db_status_added)
11384        {
11385          SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
11386                                NULL, NULL, NULL, NULL, NULL, NULL,
11387                                wcroot, local_dir_relpath,
11388                                scratch_pool, scratch_pool));
11389        }
11390      else
11391        SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
11392                                                  &old_repos_id,
11393                                                  NULL, NULL, NULL, NULL, NULL,
11394                                                  NULL, NULL, NULL, NULL, NULL,
11395                                                  wcroot, local_dir_relpath,
11396                                                  scratch_pool, scratch_pool));
11397    }
11398
11399  SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot,
11400                                      old_repos_id, scratch_pool));
11401  SVN_ERR_ASSERT(repos_uuid);  /* This function affects all the children of the given local_relpath,
11402     but the way that it does this is through the repos inheritance mechanism.
11403     So, we only need to rewrite the repos_id of the given local_relpath,
11404     as well as any children with a non-null repos_id, as well as various
11405     repos_id fields in the locks and working_node tables.
11406   */
11407
11408  /* Get the repos_id for the new repository. */
11409  SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
11410                          wcroot->sdb, scratch_pool));
11411
11412  /* Set the (base and working) repos_ids and clear the dav_caches */
11413  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11414                                    STMT_RECURSIVE_UPDATE_NODE_REPO));
11415  SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
11416                            old_repos_id, new_repos_id));
11417  SVN_ERR(svn_sqlite__step_done(stmt));
11418
11419  if (have_base_node)
11420    {
11421      /* Update any locks for the root or its children. */
11422      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11423                                        STMT_UPDATE_LOCK_REPOS_ID));
11424      SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
11425      SVN_ERR(svn_sqlite__step_done(stmt));
11426    }
11427
11428  return SVN_NO_ERROR;
11429}
11430
11431
11432svn_error_t *
11433svn_wc__db_global_relocate(svn_wc__db_t *db,
11434                           const char *local_dir_abspath,
11435                           const char *repos_root_url,
11436                           apr_pool_t *scratch_pool)
11437{
11438  svn_wc__db_wcroot_t *wcroot;
11439  const char *local_relpath;
11440
11441  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
11442  /* ### assert that we were passed a directory?  */
11443
11444  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
11445                           db, local_dir_abspath, scratch_pool, scratch_pool));
11446  VERIFY_USABLE_WCROOT(wcroot);
11447
11448  SVN_WC__DB_WITH_TXN(
11449    relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool),
11450    wcroot);
11451
11452  SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity,
11453                        scratch_pool));
11454
11455  return SVN_NO_ERROR;
11456}
11457
11458
11459/* Helper for commit_node()
11460   Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
11461   (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
11462   its parent's BASE row if not. In the latter case, error if the parent
11463   BASE row does not exist.  */
11464static svn_error_t *
11465determine_commit_repos_info(apr_int64_t *repos_id,
11466                            const char **repos_relpath,
11467                            svn_wc__db_wcroot_t *wcroot,
11468                            const char *local_relpath,
11469                            apr_pool_t *result_pool,
11470                            apr_pool_t *scratch_pool)
11471{
11472  svn_sqlite__stmt_t *stmt;
11473  svn_boolean_t have_row;
11474  int op_depth;
11475
11476  /* Prefer the current node's repository information.  */
11477  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11478                                    STMT_SELECT_NODE_INFO));
11479  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11480  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11481
11482  if (!have_row)
11483    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11484                             svn_sqlite__reset(stmt),
11485                             _("The node '%s' was not found."),
11486                             path_for_error_message(wcroot, local_relpath,
11487                                                    scratch_pool));
11488
11489  op_depth = svn_sqlite__column_int(stmt, 0);
11490
11491  if (op_depth > 0)
11492    {
11493      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
11494                                                              presence_map);
11495
11496      if (presence == svn_wc__db_status_base_deleted)
11497        {
11498          SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
11499          op_depth = svn_sqlite__column_int(stmt, 0);
11500        }
11501      else
11502        {
11503          const char *parent_repos_relpath;
11504          const char *parent_relpath;
11505          const char *name;
11506
11507          SVN_ERR(svn_sqlite__reset(stmt));
11508
11509          /* The repository relative path of an add/copy is based on its
11510             ancestor, not on the shadowed base layer.
11511
11512             As this function is only used from the commit processing we know
11513             the parent directory has only a BASE row, so we can just obtain
11514             the information directly by recursing (once!)  */
11515
11516          svn_relpath_split(&parent_relpath, &name, local_relpath,
11517                            scratch_pool);
11518
11519          SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
11520                                              wcroot, parent_relpath,
11521                                              scratch_pool, scratch_pool));
11522
11523          *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
11524                                            result_pool);
11525          return SVN_NO_ERROR;
11526        }
11527    }
11528
11529
11530  SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
11531
11532  SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
11533  SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
11534
11535  *repos_id = svn_sqlite__column_int64(stmt, 1);
11536  *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
11537
11538  return svn_error_trace(svn_sqlite__reset(stmt));
11539}
11540
11541static svn_error_t *
11542moved_descendant_collect(apr_hash_t **map,
11543                        svn_wc__db_wcroot_t *wcroot,
11544                        const char *local_relpath,
11545                        int op_depth,
11546                        apr_pool_t *result_pool,
11547                        apr_pool_t *scratch_pool)
11548{
11549  svn_sqlite__stmt_t *stmt;
11550  svn_boolean_t have_row;
11551
11552  *map = NULL;
11553
11554  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11555                                    STMT_SELECT_MOVED_DESCENDANTS_SRC));
11556  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11557                                         local_relpath,
11558                                         op_depth));
11559
11560  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11561  if (! have_row)
11562    return svn_error_trace(svn_sqlite__reset(stmt));
11563
11564  /* Find all moved descendants. Key them on target, because that is
11565     always unique */
11566  while (have_row)
11567    {
11568      const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
11569      const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
11570
11571      if (!*map)
11572        *map = apr_hash_make(result_pool);
11573
11574      svn_hash_sets(*map, to_relpath, src_relpath);
11575
11576      SVN_ERR(svn_sqlite__step(&have_row, stmt));
11577    }
11578  SVN_ERR(svn_sqlite__reset(stmt));
11579
11580  return SVN_NO_ERROR;
11581}
11582
11583/* Helper for svn_wc__db_global_commit()
11584
11585   Makes local_relpath and all its descendants at the same op-depth represent
11586   the copy origin repos_id:repos_relpath@revision.
11587
11588   This code is only valid to fix-up a move from an old location, to a new
11589   location during a commit.
11590
11591   Assumptions:
11592     * local_relpath is not the working copy root (can't be moved)
11593     * repos_relpath is not the repository root (can't be moved)
11594 */
11595static svn_error_t *
11596moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
11597                        const char *local_relpath,
11598                        apr_int64_t repos_id,
11599                        const char *repos_relpath,
11600                        svn_revnum_t revision,
11601                        apr_hash_t *children,
11602                        apr_pool_t *scratch_pool)
11603{
11604  apr_pool_t *iterpool;
11605  svn_sqlite__stmt_t *stmt;
11606  apr_hash_index_t *hi;
11607
11608  SVN_ERR_ASSERT(*local_relpath != '\0'
11609                 && *repos_relpath != '\0');
11610
11611  if (!children)
11612    return SVN_NO_ERROR;
11613
11614  /* Then update them */
11615  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11616                                    STMT_COMMIT_UPDATE_ORIGIN));
11617
11618  iterpool = svn_pool_create(scratch_pool);
11619  for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11620    {
11621      const char *src_relpath = apr_hash_this_val(hi);
11622      const char *to_relpath = apr_hash_this_key(hi);
11623      const char *new_repos_relpath;
11624      int to_op_depth = relpath_depth(to_relpath);
11625      int affected;
11626      apr_hash_t *map;
11627
11628      svn_pool_clear(iterpool);
11629
11630      SVN_ERR_ASSERT(to_op_depth > 0);
11631
11632      new_repos_relpath = svn_relpath_join(
11633                            repos_relpath,
11634                            svn_relpath_skip_ancestor(local_relpath,
11635                                                      src_relpath),
11636                            iterpool);
11637
11638      SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11639                                                to_relpath,
11640                                                to_op_depth,
11641                                                repos_id,
11642                                                new_repos_relpath,
11643                                                revision));
11644      SVN_ERR(svn_sqlite__update(&affected, stmt));
11645
11646#ifdef SVN_DEBUG
11647      /* Enable in release code?
11648         Broken moves are not fatal yet, but this assertion would break
11649         committing them */
11650      SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11651#endif
11652
11653      SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
11654                                       iterpool, iterpool));
11655      SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
11656                                      repos_id, new_repos_relpath, revision,
11657                                      map, iterpool));
11658    }
11659
11660  svn_pool_destroy(iterpool);
11661  return SVN_NO_ERROR;
11662}
11663
11664/* Helper for svn_wc__db_global_commit()
11665
11666   Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11667   (BASE), setting their presence to 'not-present' if their presence wasn't
11668   'normal'.
11669
11670   Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11671   location repos_id:repos_relpath@revision.
11672
11673   Assumptions:
11674     * local_relpath is not the working copy root (can't be replaced)
11675     * repos_relpath is not the repository root (can't be replaced)
11676   */
11677static svn_error_t *
11678descendant_commit(svn_wc__db_wcroot_t *wcroot,
11679                  const char *local_relpath,
11680                  int op_depth,
11681                  apr_int64_t repos_id,
11682                  const char *repos_relpath,
11683                  svn_revnum_t revision,
11684                  apr_pool_t *scratch_pool)
11685{
11686  svn_sqlite__stmt_t *stmt;
11687
11688  SVN_ERR_ASSERT(*local_relpath != '\0'
11689                 && *repos_relpath != '\0');
11690
11691  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11692                                    STMT_COMMIT_DESCENDANTS_TO_BASE));
11693
11694  SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11695                                            local_relpath,
11696                                            op_depth,
11697                                            repos_id,
11698                                            repos_relpath,
11699                                            revision));
11700
11701  SVN_ERR(svn_sqlite__update(NULL, stmt));
11702
11703  return SVN_NO_ERROR;
11704}
11705
11706/* The body of svn_wc__db_global_commit().
11707 */
11708static svn_error_t *
11709commit_node(svn_wc__db_wcroot_t *wcroot,
11710            const char *local_relpath,
11711            svn_revnum_t new_revision,
11712            svn_revnum_t changed_rev,
11713            apr_time_t changed_date,
11714            const char *changed_author,
11715            const svn_checksum_t *new_checksum,
11716            apr_hash_t *new_dav_cache,
11717            svn_boolean_t keep_changelist,
11718            svn_boolean_t no_unlock,
11719            const svn_skel_t *work_items,
11720            apr_pool_t *scratch_pool)
11721{
11722  svn_sqlite__stmt_t *stmt_info;
11723  svn_sqlite__stmt_t *stmt_act;
11724  svn_boolean_t have_act;
11725  svn_string_t prop_blob = { 0 };
11726  svn_string_t inherited_prop_blob = { 0 };
11727  const char *changelist = NULL;
11728  const char *parent_relpath;
11729  svn_wc__db_status_t new_presence;
11730  svn_node_kind_t new_kind;
11731  const char *new_depth_str = NULL;
11732  svn_sqlite__stmt_t *stmt;
11733  apr_int64_t repos_id;
11734  const char *repos_relpath;
11735  int op_depth;
11736  svn_wc__db_status_t old_presence;
11737  svn_boolean_t moved_here;
11738
11739    /* If we are adding a file or directory, then we need to get
11740     repository information from the parent node since "this node" does
11741     not have a BASE).
11742
11743     For existing nodes, we should retain the (potentially-switched)
11744     repository information.  */
11745  SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11746                                      wcroot, local_relpath,
11747                                      scratch_pool, scratch_pool));
11748
11749  /* ### is it better to select only the data needed?  */
11750  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11751                                    STMT_SELECT_NODE_INFO));
11752  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11753  SVN_ERR(svn_sqlite__step_row(stmt_info));
11754
11755  SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11756                                    STMT_SELECT_ACTUAL_NODE));
11757  SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11758                            wcroot->wc_id, local_relpath));
11759  SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11760
11761  /* There should be something to commit!  */
11762
11763  op_depth = svn_sqlite__column_int(stmt_info, 0);
11764
11765  /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11766     or there will be a BASE_NODE that has it.  */
11767  old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11768  new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11769
11770  /* What will the new depth be?  */
11771  if (new_kind == svn_node_dir)
11772    new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11773
11774  /* Check that the repository information is not being changed.  */
11775  if (op_depth == 0)
11776    {
11777      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11778      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11779
11780      /* A commit cannot change these values.  */
11781      SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11782      SVN_ERR_ASSERT(strcmp(repos_relpath,
11783                            svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11784    }
11785
11786  if (old_presence != svn_wc__db_status_base_deleted)
11787    {
11788      /* Find the appropriate new properties -- ACTUAL overrides any properties
11789         in WORKING that arrived as part of a copy/move.
11790
11791         Note: we'll keep them as a big blob of data, rather than
11792         deserialize/serialize them.  */
11793      if (have_act)
11794        prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11795                                                 scratch_pool);
11796      if (prop_blob.data == NULL)
11797        prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11798                                                 scratch_pool);
11799
11800      inherited_prop_blob.data = svn_sqlite__column_blob(
11801                                            stmt_info, 16,
11802                                            &inherited_prop_blob.len,
11803                                            scratch_pool);
11804
11805      if (keep_changelist && have_act)
11806        changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11807
11808      moved_here = svn_sqlite__column_int(stmt_info, 15);
11809    }
11810  else
11811    {
11812      moved_here = FALSE;
11813      changelist = NULL;
11814    }
11815
11816  /* ### other stuff?  */
11817
11818  SVN_ERR(svn_sqlite__reset(stmt_info));
11819  SVN_ERR(svn_sqlite__reset(stmt_act));
11820
11821  if (op_depth > 0)
11822    {
11823      int affected_rows;
11824
11825      SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
11826
11827      /* First clear the moves that we are going to delete in a bit */
11828      {
11829        apr_hash_t *old_moves;
11830        apr_hash_index_t *hi;
11831        SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
11832                                         scratch_pool, scratch_pool));
11833
11834        if (old_moves)
11835          for (hi = apr_hash_first(scratch_pool, old_moves);
11836                hi; hi = apr_hash_next(hi))
11837            {
11838              SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
11839                                        scratch_pool));
11840            }
11841      }
11842
11843      /* This removes all layers of this node and at the same time determines
11844         if we need to remove shadowed layers below our descendants. */
11845
11846      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11847                                        STMT_DELETE_NODE_ALL_LAYERS));
11848      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11849      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11850
11851      if (affected_rows > 1)
11852        {
11853          /* We commit a shadowing operation
11854
11855           1) Remove all shadowed nodes
11856           2) And remove all nodes that have a base-deleted as lowest layer,
11857              because 1) removed that layer */
11858
11859          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11860                                            STMT_DELETE_SHADOWED_RECURSIVE));
11861
11862          SVN_ERR(svn_sqlite__bindf(stmt,
11863                                    "isd",
11864                                    wcroot->wc_id,
11865                                    local_relpath,
11866                                    op_depth));
11867
11868          SVN_ERR(svn_sqlite__step_done(stmt));
11869        }
11870
11871      /* Note that while these two calls look so similar that they might
11872         be integrated, they really affect a different op-depth and
11873         completely different nodes (via a different recursion pattern). */
11874
11875      if (old_presence != svn_wc__db_status_base_deleted)
11876        {
11877          /* Collapse descendants of the current op_depth to layer 0,
11878             this includes moved-from/to clearing */
11879          SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11880                                    repos_id, repos_relpath, new_revision,
11881                                    scratch_pool));
11882        }
11883
11884      if (old_presence != svn_wc__db_status_base_deleted)
11885        {
11886          apr_hash_t *moves = NULL;
11887
11888          SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
11889                                           scratch_pool, scratch_pool));
11890
11891          /* And make the recorded local moves represent moves of the node we
11892             just committed. */
11893          SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
11894                                      repos_id, repos_relpath, new_revision,
11895                                      moves, scratch_pool));
11896        }
11897
11898      if (moved_here)
11899        {
11900          /* This node is no longer modified, so no node was moved here */
11901          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11902                                            STMT_CLEAR_MOVED_TO_FROM_DEST));
11903          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11904                                                local_relpath));
11905
11906          SVN_ERR(svn_sqlite__step_done(stmt));
11907        }
11908    }
11909  /* Update or add the BASE_NODE row with all the new information.  */
11910
11911  if (*local_relpath == '\0')
11912    parent_relpath = NULL;
11913  else
11914    parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11915
11916  /* Preserve any incomplete status */
11917  if (old_presence != svn_wc__db_status_base_deleted)
11918    {
11919      new_presence = (old_presence == svn_wc__db_status_incomplete
11920                      ? svn_wc__db_status_incomplete
11921                      : svn_wc__db_status_normal);
11922
11923      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11924                                        STMT_APPLY_CHANGES_TO_BASE_NODE));
11925      /* symlink_target not yet used */
11926      SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11927                                wcroot->wc_id, local_relpath,
11928                                parent_relpath,
11929                                repos_id,
11930                                repos_relpath,
11931                                new_revision,
11932                                presence_map, new_presence,
11933                                new_depth_str,
11934                                kind_map, new_kind,
11935                                changed_rev,
11936                                changed_date,
11937                                changed_author,
11938                                prop_blob.data, prop_blob.len));
11939
11940      SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11941                                        scratch_pool));
11942      SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11943                                          scratch_pool));
11944      if (inherited_prop_blob.data != NULL)
11945        {
11946          SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11947                                        inherited_prop_blob.len));
11948        }
11949
11950      SVN_ERR(svn_sqlite__step_done(stmt));
11951    }
11952  else
11953    {
11954      struct insert_base_baton_t ibb;
11955      blank_ibb(&ibb);
11956
11957      ibb.repos_id = repos_id;
11958      ibb.status = svn_wc__db_status_not_present;
11959      ibb.kind = new_kind;
11960      ibb.repos_relpath = repos_relpath;
11961      ibb.revision = new_revision;
11962
11963      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
11964
11965      keep_changelist = FALSE; /* Nothing there */
11966    }
11967
11968  if (have_act)
11969    {
11970      if (keep_changelist && changelist != NULL)
11971        {
11972          /* The user told us to keep the changelist. Replace the row in
11973             ACTUAL_NODE with the basic keys and the changelist.  */
11974          SVN_ERR(svn_sqlite__get_statement(
11975                    &stmt, wcroot->sdb,
11976                    STMT_RESET_ACTUAL_WITH_CHANGELIST));
11977          SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11978                                    wcroot->wc_id, local_relpath,
11979                                    svn_relpath_dirname(local_relpath,
11980                                                        scratch_pool),
11981                                    changelist));
11982          SVN_ERR(svn_sqlite__step_done(stmt));
11983        }
11984      else
11985        {
11986          /* Toss the ACTUAL_NODE row.  */
11987          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11988                                            STMT_DELETE_ACTUAL_NODE));
11989          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11990          SVN_ERR(svn_sqlite__step_done(stmt));
11991        }
11992    }
11993
11994  if (!no_unlock)
11995    {
11996      svn_sqlite__stmt_t *lock_stmt;
11997      svn_boolean_t op_root = (op_depth > 0
11998                               && (relpath_depth(local_relpath) == op_depth));
11999
12000      /* If we are committing an add of a delete, we can assume we own
12001         all locks at or below REPOS_RELPATH (or the server would have
12002         denied the commit). As we must have passed these to the server
12003         we can now safely remove them.
12004       */
12005      SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
12006                                        op_root
12007                                          ? STMT_DELETE_LOCK_RECURSIVELY
12008                                          : STMT_DELETE_LOCK));
12009      SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
12010      SVN_ERR(svn_sqlite__step_done(lock_stmt));
12011    }
12012
12013  /* Install any work items into the queue, as part of this transaction.  */
12014  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12015
12016  return SVN_NO_ERROR;
12017}
12018
12019
12020svn_error_t *
12021svn_wc__db_global_commit(svn_wc__db_t *db,
12022                         const char *local_abspath,
12023                         svn_revnum_t new_revision,
12024                         svn_revnum_t changed_revision,
12025                         apr_time_t changed_date,
12026                         const char *changed_author,
12027                         const svn_checksum_t *new_checksum,
12028                         apr_hash_t *new_dav_cache,
12029                         svn_boolean_t keep_changelist,
12030                         svn_boolean_t no_unlock,
12031                         const svn_skel_t *work_items,
12032                         apr_pool_t *scratch_pool)
12033{
12034  const char *local_relpath;
12035  svn_wc__db_wcroot_t *wcroot;
12036
12037  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12038  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12039
12040  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12041                              local_abspath, scratch_pool, scratch_pool));
12042  VERIFY_USABLE_WCROOT(wcroot);
12043
12044  SVN_WC__DB_WITH_TXN(
12045    commit_node(wcroot, local_relpath,
12046                new_revision, changed_revision, changed_date, changed_author,
12047                new_checksum, new_dav_cache, keep_changelist,
12048                no_unlock, work_items, scratch_pool),
12049    wcroot);
12050
12051  /* We *totally* monkeyed the entries. Toss 'em.  */
12052  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12053
12054  return SVN_NO_ERROR;
12055}
12056
12057
12058svn_error_t *
12059svn_wc__db_global_update(svn_wc__db_t *db,
12060                         const char *local_abspath,
12061                         svn_node_kind_t new_kind,
12062                         const char *new_repos_relpath,
12063                         svn_revnum_t new_revision,
12064                         const apr_hash_t *new_props,
12065                         svn_revnum_t new_changed_rev,
12066                         apr_time_t new_changed_date,
12067                         const char *new_changed_author,
12068                         const apr_array_header_t *new_children,
12069                         const svn_checksum_t *new_checksum,
12070                         const char *new_target,
12071                         const apr_hash_t *new_dav_cache,
12072                         const svn_skel_t *conflict,
12073                         const svn_skel_t *work_items,
12074                         apr_pool_t *scratch_pool)
12075{
12076  NOT_IMPLEMENTED();
12077
12078#if 0
12079  svn_wc__db_wcroot_t *wcroot;
12080  const char *local_relpath;
12081
12082  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12083  /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"?  */
12084  SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
12085  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12086  SVN_ERR_ASSERT(new_props != NULL);
12087  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
12088  SVN_ERR_ASSERT((new_children != NULL
12089                  && new_checksum == NULL
12090                  && new_target == NULL)
12091                 || (new_children == NULL
12092                     && new_checksum != NULL
12093                     && new_target == NULL)
12094                 || (new_children == NULL
12095                     && new_checksum == NULL
12096                     && new_target != NULL));
12097
12098  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12099                              local_abspath, scratch_pool, scratch_pool));
12100  VERIFY_USABLE_WCROOT(wcroot);
12101
12102  SVN_WC__DB_WITH_TXN(
12103    update_node(wcroot, local_relpath,
12104                new_repos_relpath, new_revision, new_props,
12105                new_changed_rev, new_changed_date, new_changed_author,
12106                new_children, new_checksum, new_target,
12107                conflict, work_items, scratch_pool),
12108    wcroot);
12109
12110  /* We *totally* monkeyed the entries. Toss 'em.  */
12111  SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
12112
12113  return SVN_NO_ERROR;
12114#endif
12115}
12116
12117/* Sets a base nodes revision, repository relative path, and/or inherited
12118   propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
12119   SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
12120   (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
12121   inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
12122   cache for the base node.
12123 */
12124static svn_error_t *
12125db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
12126                                   const char *local_relpath,
12127                                   apr_array_header_t *iprops,
12128                                   svn_revnum_t rev,
12129                                   svn_boolean_t set_repos_relpath,
12130                                   const char *repos_relpath,
12131                                   apr_int64_t repos_id,
12132                                   apr_pool_t *scratch_pool)
12133{
12134  svn_sqlite__stmt_t *stmt;
12135
12136  SVN_ERR(flush_entries(wcroot,
12137                        svn_dirent_join(wcroot->abspath, local_relpath,
12138                                        scratch_pool),
12139                        svn_depth_empty, scratch_pool));
12140
12141
12142  if (SVN_IS_VALID_REVNUM(rev))
12143    {
12144      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12145                                        STMT_UPDATE_BASE_REVISION));
12146
12147      SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
12148                                rev));
12149
12150      SVN_ERR(svn_sqlite__step_done(stmt));
12151    }
12152
12153  if (set_repos_relpath)
12154    {
12155      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12156                                        STMT_UPDATE_BASE_REPOS));
12157
12158      SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
12159                                repos_id, repos_relpath));
12160
12161      SVN_ERR(svn_sqlite__step_done(stmt));
12162    }
12163
12164  /* Set or clear iprops. */
12165  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12166                                    STMT_UPDATE_IPROP));
12167  SVN_ERR(svn_sqlite__bindf(stmt, "is",
12168                            wcroot->wc_id,
12169                            local_relpath));
12170  SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
12171  SVN_ERR(svn_sqlite__step_done(stmt));
12172
12173  return SVN_NO_ERROR;
12174}
12175
12176/* The main body of bump_revisions_post_update().
12177 *
12178 * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
12179 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
12180 * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
12181 *
12182 * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the
12183 * values as stored currently in WCROOT for LOCAL_RELPATH.
12184 *
12185 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
12186 * working copy paths to depth-first ordered arrays of
12187 * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
12188 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
12189 * node's inherited properties.
12190 *
12191 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
12192 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
12193 */
12194static svn_error_t *
12195bump_node_revision(svn_wc__db_wcroot_t *wcroot,
12196                   const char *local_relpath,
12197                   svn_wc__db_status_t node_status,
12198                   svn_node_kind_t node_kind,
12199                   svn_revnum_t node_revision,
12200                   const char *node_repos_relpath,
12201                   apr_int64_t new_repos_id,
12202                   const char *new_repos_relpath,
12203                   svn_revnum_t new_rev,
12204                   svn_depth_t depth,
12205                   apr_hash_t *exclude_relpaths,
12206                   apr_hash_t *wcroot_iprops,
12207                   svn_boolean_t is_root,
12208                   svn_boolean_t skip_when_dir,
12209                   svn_wc__db_t *db,
12210                   apr_pool_t *scratch_pool)
12211{
12212  apr_pool_t *iterpool;
12213  apr_hash_t *children;
12214  apr_hash_index_t *hi;
12215  svn_boolean_t set_repos_relpath = FALSE;
12216  svn_depth_t depth_below_here = depth;
12217  apr_array_header_t *iprops = NULL;
12218
12219  if (new_repos_relpath != NULL
12220      && strcmp(node_repos_relpath, new_repos_relpath))
12221    set_repos_relpath = TRUE;
12222
12223  if (wcroot_iprops)
12224    iprops = svn_hash_gets(wcroot_iprops,
12225                           svn_dirent_join(wcroot->abspath, local_relpath,
12226                                           scratch_pool));
12227
12228  if (iprops
12229      || set_repos_relpath
12230      || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision))
12231    {
12232      SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
12233                                                 iprops, new_rev,
12234                                                 set_repos_relpath,
12235                                                 new_repos_relpath,
12236                                                 new_repos_id,
12237                                                 scratch_pool));
12238    }
12239
12240  /* Early out */
12241  if (depth <= svn_depth_empty
12242      || node_kind != svn_node_dir
12243      || node_status == svn_wc__db_status_server_excluded
12244      || node_status == svn_wc__db_status_excluded
12245      || node_status == svn_wc__db_status_not_present)
12246    return SVN_NO_ERROR;
12247
12248  /* And now recurse over the children */
12249
12250  depth_below_here = depth;
12251
12252  if (depth == svn_depth_immediates || depth == svn_depth_files)
12253    depth_below_here = svn_depth_empty;
12254
12255  iterpool = svn_pool_create(scratch_pool);
12256
12257  SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0,
12258                                 scratch_pool, iterpool));
12259  for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
12260    {
12261      const char *child_basename = apr_hash_this_key(hi);
12262      const struct svn_wc__db_base_info_t *child_info;
12263      const char *child_local_relpath;
12264      const char *child_repos_relpath = NULL;
12265
12266      svn_pool_clear(iterpool);
12267
12268      child_info = apr_hash_this_val(hi);
12269
12270      if (child_info->update_root && child_info->kind == svn_node_file)
12271        continue; /* Skip file externals */
12272
12273      if (depth < svn_depth_immediates && child_info->kind == svn_node_dir)
12274          continue; /* Skip directories */
12275
12276      child_local_relpath = svn_relpath_join(local_relpath, child_basename,
12277                                             iterpool);
12278
12279      /* Don't touch nodes that can't be touched via the exclude list */
12280      if (svn_hash_gets(exclude_relpaths, child_local_relpath))
12281          continue;
12282
12283      /* If the node is still marked 'not-present', then the server did not
12284          re-add it.  So it's really gone in this revision, thus we remove the
12285          node.
12286
12287          If the node is still marked 'server-excluded' and yet is not the same
12288          revision as new_rev, then the server did not re-add it, nor
12289          re-server-exclude it, so we can remove the node. */
12290      if (child_info->status == svn_wc__db_status_not_present
12291          || (child_info->status == svn_wc__db_status_server_excluded &&
12292              child_info->revnum != new_rev))
12293        {
12294          svn_sqlite__stmt_t *stmt;
12295          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12296                                    STMT_DELETE_BASE_NODE));
12297          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath));
12298          SVN_ERR(svn_sqlite__step_done(stmt));
12299          continue;
12300        }
12301
12302      /* Derive the new URL for the current (child) entry */
12303      if (new_repos_relpath)
12304        child_repos_relpath = svn_relpath_join(new_repos_relpath,
12305                                               child_basename, iterpool);
12306
12307      SVN_ERR(bump_node_revision(wcroot, child_local_relpath,
12308                                 child_info->status,
12309                                 child_info->kind,
12310                                 child_info->revnum,
12311                                 child_info->repos_relpath,
12312                                 new_repos_id,
12313                                 child_repos_relpath, new_rev,
12314                                 depth_below_here,
12315                                 exclude_relpaths, wcroot_iprops,
12316                                 FALSE /* is_root */,
12317                                 (depth < svn_depth_immediates), db,
12318                                 iterpool));
12319    }
12320
12321  /* Cleanup */
12322  svn_pool_destroy(iterpool);
12323
12324  return SVN_NO_ERROR;
12325}
12326
12327/* Helper for svn_wc__db_op_bump_revisions_post_update().
12328 */
12329static svn_error_t *
12330bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
12331                           const char *local_relpath,
12332                           svn_wc__db_t *db,
12333                           svn_depth_t depth,
12334                           const char *new_repos_relpath,
12335                           const char *new_repos_root_url,
12336                           const char *new_repos_uuid,
12337                           svn_revnum_t new_revision,
12338                           apr_hash_t *exclude_relpaths,
12339                           apr_hash_t *wcroot_iprops,
12340                           svn_boolean_t empty_update,
12341                           svn_wc_notify_func2_t notify_func,
12342                           void *notify_baton,
12343                           apr_pool_t *scratch_pool)
12344{
12345  svn_wc__db_status_t status;
12346  svn_node_kind_t kind;
12347  svn_error_t *err;
12348  apr_int64_t new_repos_id = INVALID_REPOS_ID;
12349  svn_revnum_t revision;
12350  const char *repos_relpath;
12351
12352  err = svn_wc__db_base_get_info_internal(&status, &kind, &revision,
12353                                          &repos_relpath, NULL,
12354                                          NULL, NULL, NULL, NULL, NULL, NULL,
12355                                          NULL, NULL, NULL, NULL,
12356                                          wcroot, local_relpath,
12357                                          scratch_pool, scratch_pool);
12358  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
12359    {
12360      svn_error_clear(err);
12361      return SVN_NO_ERROR;
12362    }
12363  else
12364    SVN_ERR(err);
12365
12366  switch (status)
12367    {
12368      case svn_wc__db_status_excluded:
12369      case svn_wc__db_status_server_excluded:
12370      case svn_wc__db_status_not_present:
12371        return SVN_NO_ERROR;
12372
12373      /* Explicitly ignore other statii */
12374      default:
12375        break;
12376    }
12377
12378  if (new_repos_root_url != NULL)
12379    SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
12380                            new_repos_uuid,
12381                            wcroot->sdb, scratch_pool));
12382
12383  SVN_ERR(bump_node_revision(wcroot, local_relpath,
12384                             status, kind,  revision, repos_relpath,
12385                             new_repos_id,
12386                             new_repos_relpath, new_revision,
12387                             depth, exclude_relpaths,
12388                             wcroot_iprops,
12389                             TRUE /* is_root */, FALSE, db,
12390                             scratch_pool));
12391
12392  /* ### TODO: Use empty_update flag for change knowledge */
12393  SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
12394                                     scratch_pool));
12395
12396  SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
12397                                             SVN_INVALID_REVNUM, notify_func,
12398                                             notify_baton, scratch_pool));
12399
12400  return SVN_NO_ERROR;
12401}
12402
12403svn_error_t *
12404svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
12405                                         const char *local_abspath,
12406                                         svn_depth_t depth,
12407                                         const char *new_repos_relpath,
12408                                         const char *new_repos_root_url,
12409                                         const char *new_repos_uuid,
12410                                         svn_revnum_t new_revision,
12411                                         apr_hash_t *exclude_relpaths,
12412                                         apr_hash_t *wcroot_iprops,
12413                                         svn_boolean_t empty_update,
12414                                         svn_wc_notify_func2_t notify_func,
12415                                         void *notify_baton,
12416                                         apr_pool_t *scratch_pool)
12417{
12418  const char *local_relpath;
12419  svn_wc__db_wcroot_t *wcroot;
12420
12421  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12422                              local_abspath, scratch_pool, scratch_pool));
12423
12424  VERIFY_USABLE_WCROOT(wcroot);
12425
12426  if (svn_hash_gets(exclude_relpaths, local_relpath))
12427    return SVN_NO_ERROR;
12428
12429  if (depth == svn_depth_unknown)
12430    depth = svn_depth_infinity;
12431
12432  SVN_WC__DB_WITH_TXN(
12433    bump_revisions_post_update(wcroot, local_relpath, db,
12434                               depth, new_repos_relpath, new_repos_root_url,
12435                               new_repos_uuid, new_revision,
12436                               exclude_relpaths, wcroot_iprops, empty_update,
12437                               notify_func, notify_baton, scratch_pool),
12438    wcroot);
12439
12440  return SVN_NO_ERROR;
12441}
12442
12443/* The body of svn_wc__db_lock_add().
12444 */
12445static svn_error_t *
12446lock_add_txn(svn_wc__db_wcroot_t *wcroot,
12447             const char *local_relpath,
12448             const svn_wc__db_lock_t *lock,
12449             apr_pool_t *scratch_pool)
12450{
12451  svn_sqlite__stmt_t *stmt;
12452  const char *repos_relpath;
12453  apr_int64_t repos_id;
12454
12455  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12456                                            &repos_relpath, &repos_id,
12457                                            NULL, NULL, NULL, NULL, NULL,
12458                                            NULL, NULL, NULL, NULL, NULL,
12459                                            wcroot, local_relpath,
12460                                            scratch_pool, scratch_pool));
12461
12462  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
12463  SVN_ERR(svn_sqlite__bindf(stmt, "iss",
12464                            repos_id, repos_relpath, lock->token));
12465
12466  if (lock->owner != NULL)
12467    SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
12468
12469  if (lock->comment != NULL)
12470    SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
12471
12472  if (lock->date != 0)
12473    SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
12474
12475  SVN_ERR(svn_sqlite__insert(NULL, stmt));
12476
12477  return SVN_NO_ERROR;
12478}
12479
12480
12481svn_error_t *
12482svn_wc__db_lock_add(svn_wc__db_t *db,
12483                    const char *local_abspath,
12484                    const svn_wc__db_lock_t *lock,
12485                    apr_pool_t *scratch_pool)
12486{
12487  svn_wc__db_wcroot_t *wcroot;
12488  const char *local_relpath;
12489
12490  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12491  SVN_ERR_ASSERT(lock != NULL);
12492
12493  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12494                              local_abspath, scratch_pool, scratch_pool));
12495  VERIFY_USABLE_WCROOT(wcroot);
12496
12497  SVN_WC__DB_WITH_TXN(
12498    lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
12499    wcroot);
12500
12501  /* There may be some entries, and the lock info is now out of date.  */
12502  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12503
12504  return SVN_NO_ERROR;
12505}
12506
12507
12508/* The body of svn_wc__db_lock_remove().
12509 */
12510static svn_error_t *
12511lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
12512                const char *local_relpath,
12513                svn_skel_t *work_items,
12514                apr_pool_t *scratch_pool)
12515{
12516  const char *repos_relpath;
12517  apr_int64_t repos_id;
12518  svn_sqlite__stmt_t *stmt;
12519
12520  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12521                                            &repos_relpath, &repos_id,
12522                                            NULL, NULL, NULL, NULL, NULL,
12523                                            NULL, NULL, NULL, NULL, NULL,
12524                                            wcroot, local_relpath,
12525                                            scratch_pool, scratch_pool));
12526
12527  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12528                                    STMT_DELETE_LOCK));
12529  SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
12530
12531  SVN_ERR(svn_sqlite__step_done(stmt));
12532
12533  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12534
12535  return SVN_NO_ERROR;
12536}
12537
12538
12539svn_error_t *
12540svn_wc__db_lock_remove(svn_wc__db_t *db,
12541                       const char *local_abspath,
12542                       svn_skel_t *work_items,
12543                       apr_pool_t *scratch_pool)
12544{
12545  svn_wc__db_wcroot_t *wcroot;
12546  const char *local_relpath;
12547
12548  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12549
12550  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12551                              local_abspath, scratch_pool, scratch_pool));
12552  VERIFY_USABLE_WCROOT(wcroot);
12553
12554  SVN_WC__DB_WITH_TXN(
12555    lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool),
12556    wcroot);
12557
12558  /* There may be some entries, and the lock info is now out of date.  */
12559  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12560
12561  return SVN_NO_ERROR;
12562}
12563
12564/* A helper for scan_addition().
12565 * Compute moved-from information for the node at LOCAL_RELPATH which
12566 * has been determined as having been moved-here.
12567 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
12568 * path of the move-source node in *MOVED_FROM_RELPATH.
12569 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
12570 * to the path of the op-root of the delete-half of the move.
12571 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
12572 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
12573 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
12574 * of the move. */
12575static svn_error_t *
12576get_moved_from_info(const char **moved_from_relpath,
12577                    const char **moved_from_op_root_relpath,
12578                    const char *moved_to_op_root_relpath,
12579                    int *op_depth,
12580                    svn_wc__db_wcroot_t *wcroot,
12581                    const char *local_relpath,
12582                    apr_pool_t *result_pool,
12583                    apr_pool_t *scratch_pool)
12584{
12585  svn_sqlite__stmt_t *stmt;
12586  svn_boolean_t have_row;
12587
12588  /* Run a query to get the moved-from path from the DB. */
12589  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12590                                    STMT_SELECT_MOVED_FROM_RELPATH));
12591  SVN_ERR(svn_sqlite__bindf(stmt, "is",
12592                            wcroot->wc_id, moved_to_op_root_relpath));
12593  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12594
12595  if (!have_row)
12596    {
12597      /* The move was only recorded at the copy-half, possibly because
12598       * the move operation was interrupted mid-way between the copy
12599       * and the delete. Treat this node as a normal copy. */
12600      if (moved_from_relpath)
12601        *moved_from_relpath = NULL;
12602      if (moved_from_op_root_relpath)
12603        *moved_from_op_root_relpath = NULL;
12604
12605      SVN_ERR(svn_sqlite__reset(stmt));
12606      return SVN_NO_ERROR;
12607    }
12608
12609  if (op_depth)
12610    *op_depth = svn_sqlite__column_int(stmt, 1);
12611
12612  if (moved_from_relpath || moved_from_op_root_relpath)
12613    {
12614      const char *db_delete_op_root_relpath;
12615
12616      /* The moved-from path from the DB is the relpath of
12617       * the op_root of the delete-half of the move. */
12618      db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12619                                                          result_pool);
12620      if (moved_from_op_root_relpath)
12621        *moved_from_op_root_relpath = db_delete_op_root_relpath;
12622
12623      if (moved_from_relpath)
12624        {
12625          if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12626            {
12627              /* LOCAL_RELPATH is the op_root of the copied-half of the
12628               * move, so the correct MOVED_FROM_ABSPATH is the op-root
12629               * of the delete-half. */
12630              *moved_from_relpath = db_delete_op_root_relpath;
12631            }
12632          else
12633            {
12634              const char *child_relpath;
12635
12636              /* LOCAL_RELPATH is a child that was copied along with the
12637               * op_root of the copied-half of the move. Construct the
12638               * corresponding path beneath the op_root of the delete-half. */
12639
12640              /* Grab the child path relative to the op_root of the move
12641               * destination. */
12642              child_relpath = svn_relpath_skip_ancestor(
12643                                moved_to_op_root_relpath, local_relpath);
12644
12645              SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12646
12647              /* This join is valid because LOCAL_RELPATH has not been moved
12648               * within the copied-half of the move yet -- else, it would
12649               * be its own op_root. */
12650              *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12651                                                     child_relpath,
12652                                                     result_pool);
12653            }
12654        }
12655    }
12656
12657  SVN_ERR(svn_sqlite__reset(stmt));
12658
12659  return SVN_NO_ERROR;
12660}
12661
12662/* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12663   DB+LOCAL_ABSPATH.
12664
12665   The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12666   is no 'copy-from' repository.  */
12667static svn_error_t *
12668scan_addition(svn_wc__db_status_t *status,
12669              const char **op_root_relpath_p,
12670              const char **repos_relpath,
12671              apr_int64_t *repos_id,
12672              const char **original_repos_relpath,
12673              apr_int64_t *original_repos_id,
12674              svn_revnum_t *original_revision,
12675              const char **moved_from_relpath,
12676              const char **moved_from_op_root_relpath,
12677              int *moved_from_op_depth,
12678              svn_wc__db_wcroot_t *wcroot,
12679              const char *local_relpath,
12680              apr_pool_t *result_pool,
12681              apr_pool_t *scratch_pool)
12682{
12683  const char *op_root_relpath;
12684  const char *build_relpath = "";
12685
12686  /* Initialize most of the OUT parameters. Generally, we'll only be filling
12687     in a subset of these, so it is easier to init all up front. Note that
12688     the STATUS parameter will be initialized once we read the status of
12689     the specified node.  */
12690  if (op_root_relpath_p)
12691    *op_root_relpath_p = NULL;
12692  if (original_repos_relpath)
12693    *original_repos_relpath = NULL;
12694  if (original_repos_id)
12695    *original_repos_id = INVALID_REPOS_ID;
12696  if (original_revision)
12697    *original_revision = SVN_INVALID_REVNUM;
12698  if (moved_from_relpath)
12699    *moved_from_relpath = NULL;
12700  if (moved_from_op_root_relpath)
12701    *moved_from_op_root_relpath = NULL;
12702  if (moved_from_op_depth)
12703    *moved_from_op_depth = 0;
12704
12705  {
12706    svn_sqlite__stmt_t *stmt;
12707    svn_boolean_t have_row;
12708    svn_wc__db_status_t presence;
12709    int op_depth;
12710    const char *repos_prefix_path;
12711
12712    /* ### is it faster to fetch fewer columns? */
12713    SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12714                                      STMT_SELECT_WORKING_NODE));
12715    SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12716    SVN_ERR(svn_sqlite__step(&have_row, stmt));
12717
12718    if (!have_row)
12719      {
12720        /* Reset statement before returning */
12721        SVN_ERR(svn_sqlite__reset(stmt));
12722
12723        /* ### maybe we should return a usage error instead?  */
12724        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12725                                 _("The node '%s' was not found."),
12726                                 path_for_error_message(wcroot,
12727                                                        local_relpath,
12728                                                        scratch_pool));
12729      }
12730
12731    presence = svn_sqlite__column_token(stmt, 1, presence_map);
12732
12733    /* The starting node should exist normally.  */
12734    op_depth = svn_sqlite__column_int(stmt, 0);
12735    if (op_depth == 0 || (presence != svn_wc__db_status_normal
12736                          && presence != svn_wc__db_status_incomplete))
12737      /* reset the statement as part of the error generation process */
12738      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12739                               svn_sqlite__reset(stmt),
12740                               _("Expected node '%s' to be added."),
12741                               path_for_error_message(wcroot,
12742                                                      local_relpath,
12743                                                      scratch_pool));
12744
12745    if (original_revision)
12746      *original_revision = svn_sqlite__column_revnum(stmt, 12);
12747
12748    /* Provide the default status; we'll override as appropriate. */
12749    if (status)
12750      {
12751        if (presence == svn_wc__db_status_normal)
12752          *status = svn_wc__db_status_added;
12753        else
12754          *status = svn_wc__db_status_incomplete;
12755      }
12756
12757
12758    /* Calculate the op root local path components */
12759    op_root_relpath = svn_relpath_prefix(local_relpath, op_depth,
12760                                         scratch_pool);
12761    repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath,
12762                                                  local_relpath);
12763
12764    if (op_root_relpath_p)
12765      *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12766
12767    /* ### This if-statement is quite redundant.
12768     * ### We're checking all these values again within the body anyway.
12769     * ### The body should be broken up appropriately and move into the
12770     * ### outer scope. */
12771    if (original_repos_relpath
12772        || original_repos_id
12773        || (original_revision
12774                && *original_revision == SVN_INVALID_REVNUM)
12775        || status
12776        || moved_from_relpath || moved_from_op_root_relpath)
12777      {
12778        if (local_relpath != op_root_relpath)
12779          /* requery to get the add/copy root */
12780          {
12781            SVN_ERR(svn_sqlite__reset(stmt));
12782
12783            SVN_ERR(svn_sqlite__bindf(stmt, "is",
12784                                      wcroot->wc_id, op_root_relpath));
12785            SVN_ERR(svn_sqlite__step(&have_row, stmt));
12786
12787            if (!have_row)
12788              {
12789                /* Reset statement before returning */
12790                SVN_ERR(svn_sqlite__reset(stmt));
12791
12792                /* ### maybe we should return a usage error instead?  */
12793                return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12794                                         _("The node '%s' was not found."),
12795                                         path_for_error_message(wcroot,
12796                                                                op_root_relpath,
12797                                                                scratch_pool));
12798              }
12799
12800            if (original_revision
12801                    && *original_revision == SVN_INVALID_REVNUM)
12802              *original_revision = svn_sqlite__column_revnum(stmt, 12);
12803          }
12804
12805        if (original_repos_relpath)
12806          *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12807                                                            result_pool);
12808
12809        if (!svn_sqlite__column_is_null(stmt, 10)
12810            && (status
12811                || original_repos_id
12812                || moved_from_relpath || moved_from_op_root_relpath))
12813          /* If column 10 (original_repos_id) is NULL,
12814             this is a plain add, not a copy or a move */
12815          {
12816            svn_boolean_t moved_here;
12817            if (original_repos_id)
12818              *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12819
12820            moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12821            if (status)
12822              *status = moved_here ? svn_wc__db_status_moved_here
12823                                   : svn_wc__db_status_copied;
12824
12825            if (moved_here
12826                && (moved_from_relpath || moved_from_op_root_relpath))
12827              {
12828                svn_error_t *err;
12829
12830                err = get_moved_from_info(moved_from_relpath,
12831                                          moved_from_op_root_relpath,
12832                                          op_root_relpath,
12833                                          moved_from_op_depth,
12834                                          wcroot, local_relpath,
12835                                          result_pool,
12836                                          scratch_pool);
12837
12838                if (err)
12839                  return svn_error_compose_create(
12840                                err, svn_sqlite__reset(stmt));
12841              }
12842          }
12843      }
12844
12845
12846    /* ### This loop here is to skip up to the first node which is a BASE node,
12847       because base_get_info() doesn't accommodate the scenario that
12848       we're looking at here; we found the true op_root, which may be inside
12849       further changed trees. */
12850    if (repos_relpath || repos_id)
12851      {
12852        const char *base_relpath;
12853
12854        while (TRUE)
12855          {
12856            const char *tmp;
12857
12858            SVN_ERR(svn_sqlite__reset(stmt));
12859
12860            /* Pointing at op_depth, look at the parent */
12861            repos_prefix_path =
12862                svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12863                                 repos_prefix_path,
12864                                 scratch_pool);
12865            op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12866
12867
12868            SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12869            SVN_ERR(svn_sqlite__step(&have_row, stmt));
12870
12871            if (! have_row)
12872              break;
12873
12874            op_depth = svn_sqlite__column_int(stmt, 0);
12875
12876            /* Skip to op_depth */
12877            tmp = op_root_relpath;
12878
12879            op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth,
12880                                                 scratch_pool);
12881            repos_prefix_path = svn_relpath_join(
12882                                                 svn_relpath_skip_ancestor(op_root_relpath, tmp),
12883                                                 repos_prefix_path, scratch_pool);
12884          }
12885
12886      SVN_ERR(svn_sqlite__reset(stmt));
12887
12888      build_relpath = repos_prefix_path;
12889
12890      /* If we're here, then we have an added/copied/moved (start) node, and
12891         CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12892         information for the current node, and use that to compute the start
12893         node's repository information.  */
12894      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12895                                                &base_relpath, repos_id,
12896                                                NULL, NULL, NULL, NULL, NULL,
12897                                                NULL, NULL, NULL, NULL, NULL,
12898                                                wcroot, op_root_relpath,
12899                                                scratch_pool, scratch_pool));
12900
12901        if (repos_relpath)
12902          *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12903                                            result_pool);
12904      }
12905    else
12906      SVN_ERR(svn_sqlite__reset(stmt));
12907  }
12908  /* Postconditions */
12909#ifdef SVN_DEBUG
12910  if (status)
12911    {
12912      SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12913                     || *status == svn_wc__db_status_copied
12914                     || *status == svn_wc__db_status_incomplete
12915                     || *status == svn_wc__db_status_moved_here);
12916      if (*status == svn_wc__db_status_added)
12917        {
12918          SVN_ERR_ASSERT(!original_repos_relpath
12919                         || *original_repos_relpath == NULL);
12920          SVN_ERR_ASSERT(!original_revision
12921                         || *original_revision == SVN_INVALID_REVNUM);
12922          SVN_ERR_ASSERT(!original_repos_id
12923                         || *original_repos_id == INVALID_REPOS_ID);
12924        }
12925      /* An upgrade with a missing directory can leave INCOMPLETE working
12926         op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12927       */
12928      else if (*status != svn_wc__db_status_incomplete)
12929        {
12930          SVN_ERR_ASSERT(!original_repos_relpath
12931                         || *original_repos_relpath != NULL);
12932          SVN_ERR_ASSERT(!original_revision
12933                         || *original_revision != SVN_INVALID_REVNUM);
12934          SVN_ERR_ASSERT(!original_repos_id
12935                         || *original_repos_id != INVALID_REPOS_ID);
12936        }
12937    }
12938  SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12939#endif
12940
12941  return SVN_NO_ERROR;
12942}
12943
12944svn_error_t *
12945svn_wc__db_scan_addition_internal(
12946              svn_wc__db_status_t *status,
12947              const char **op_root_relpath_p,
12948              const char **repos_relpath,
12949              apr_int64_t *repos_id,
12950              const char **original_repos_relpath,
12951              apr_int64_t *original_repos_id,
12952              svn_revnum_t *original_revision,
12953              svn_wc__db_wcroot_t *wcroot,
12954              const char *local_relpath,
12955              apr_pool_t *result_pool,
12956              apr_pool_t *scratch_pool)
12957{
12958  return svn_error_trace(
12959      scan_addition(status, op_root_relpath_p, repos_relpath, repos_id,
12960                    original_repos_relpath, original_repos_id,
12961                    original_revision, NULL, NULL, NULL,
12962                    wcroot, local_relpath, result_pool, scratch_pool));
12963}
12964
12965svn_error_t *
12966svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12967                         const char **op_root_abspath,
12968                         const char **repos_relpath,
12969                         const char **repos_root_url,
12970                         const char **repos_uuid,
12971                         const char **original_repos_relpath,
12972                         const char **original_root_url,
12973                         const char **original_uuid,
12974                         svn_revnum_t *original_revision,
12975                         svn_wc__db_t *db,
12976                         const char *local_abspath,
12977                         apr_pool_t *result_pool,
12978                         apr_pool_t *scratch_pool)
12979{
12980  svn_wc__db_wcroot_t *wcroot;
12981  const char *local_relpath;
12982  const char *op_root_relpath = NULL;
12983  apr_int64_t repos_id = INVALID_REPOS_ID;
12984  apr_int64_t original_repos_id = INVALID_REPOS_ID;
12985  apr_int64_t *repos_id_p
12986    = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12987  apr_int64_t *original_repos_id_p
12988    = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12989
12990  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12991
12992  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12993                              local_abspath, scratch_pool, scratch_pool));
12994  VERIFY_USABLE_WCROOT(wcroot);
12995
12996  SVN_WC__DB_WITH_TXN4(
12997          scan_addition(status,
12998                        op_root_abspath
12999                                ? &op_root_relpath
13000                                : NULL,
13001                        repos_relpath, repos_id_p,
13002                        original_repos_relpath, original_repos_id_p,
13003                        original_revision,
13004                        NULL, NULL, NULL,
13005                        wcroot, local_relpath, result_pool, scratch_pool),
13006          svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot,
13007                                      repos_id, result_pool),
13008          svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
13009                                      wcroot, original_repos_id,
13010                                      result_pool),
13011          SVN_NO_ERROR,
13012          wcroot);
13013
13014  if (op_root_abspath)
13015    *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13016                                       result_pool);
13017  /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
13018  SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
13019
13020  return SVN_NO_ERROR;
13021}
13022
13023svn_error_t *
13024svn_wc__db_scan_moved(const char **moved_from_abspath,
13025                      const char **op_root_abspath,
13026                      const char **op_root_moved_from_abspath,
13027                      const char **moved_from_delete_abspath,
13028                      svn_wc__db_t *db,
13029                      const char *local_abspath,
13030                      apr_pool_t *result_pool,
13031                      apr_pool_t *scratch_pool)
13032{
13033  svn_wc__db_wcroot_t *wcroot;
13034  const char *local_relpath;
13035  svn_wc__db_status_t status;
13036  const char *op_root_relpath = NULL;
13037  const char *moved_from_relpath = NULL;
13038  const char *moved_from_op_root_relpath = NULL;
13039  int moved_from_op_depth = -1;
13040
13041  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13042
13043  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13044                              local_abspath, scratch_pool, scratch_pool));
13045  VERIFY_USABLE_WCROOT(wcroot);
13046
13047  SVN_WC__DB_WITH_TXN(
13048          scan_addition(&status,
13049                        op_root_abspath
13050                                ? &op_root_relpath
13051                                : NULL,
13052                        NULL, NULL,
13053                        NULL, NULL, NULL,
13054                        moved_from_abspath
13055                            ? &moved_from_relpath
13056                            : NULL,
13057                        (op_root_moved_from_abspath
13058                         || moved_from_delete_abspath)
13059                            ? &moved_from_op_root_relpath
13060                            : NULL,
13061                        moved_from_delete_abspath
13062                            ? &moved_from_op_depth
13063                            : NULL,
13064                        wcroot, local_relpath, scratch_pool, scratch_pool),
13065          wcroot);
13066
13067  if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
13068    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
13069                             _("Path '%s' was not moved here"),
13070                             path_for_error_message(wcroot, local_relpath,
13071                                                    scratch_pool));
13072
13073  if (op_root_abspath)
13074    *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13075                                       result_pool);
13076
13077  if (moved_from_abspath)
13078    *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
13079                                          result_pool);
13080
13081  if (op_root_moved_from_abspath)
13082    *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
13083                                                  moved_from_op_root_relpath,
13084                                                  result_pool);
13085
13086  /* The deleted node is either where we moved from, or one of its ancestors */
13087  if (moved_from_delete_abspath)
13088    {
13089      const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath,
13090                                           moved_from_op_depth, scratch_pool);
13091
13092      *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
13093                                                   scratch_pool);
13094    }
13095
13096  return SVN_NO_ERROR;
13097}
13098
13099/* ### Recursive helper for svn_wc__db_follow_moved_to()
13100 */
13101static svn_error_t *
13102follow_moved_to(svn_wc__db_wcroot_t *wcroot,
13103                const char *local_relpath,
13104                int op_depth,
13105                apr_array_header_t **moved_tos,
13106                apr_pool_t *result_pool,
13107                apr_pool_t *scratch_pool)
13108{
13109  svn_sqlite__stmt_t *stmt;
13110  svn_boolean_t have_row;
13111  int shadowing_op_depth;
13112  const char *ancestor_relpath;
13113  const char *node_moved_to = NULL;
13114  int i;
13115
13116  /* Obtain the depth of the node directly shadowing local_relpath
13117     as it exists at OP_DEPTH, and perhaps moved to info */
13118  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13119                                    STMT_SELECT_OP_DEPTH_MOVED_TO));
13120  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13121                            op_depth));
13122  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13123  if (have_row)
13124    {
13125      shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
13126      node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
13127
13128      if (node_moved_to)
13129        {
13130          struct svn_wc__db_moved_to_t *moved_to;
13131
13132          moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13133          moved_to->op_depth = shadowing_op_depth;
13134          moved_to->local_relpath = node_moved_to;
13135          APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13136        }
13137    }
13138
13139  SVN_ERR(svn_sqlite__reset(stmt));
13140
13141  if (!have_row)
13142    {
13143      /* Node is not shadowed, so not moved */
13144      return SVN_NO_ERROR;
13145    }
13146  else if (node_moved_to)
13147    {
13148      /* Moved directly, so we have the final location */
13149      return SVN_NO_ERROR;
13150    }
13151  /* Need to handle being moved via an ancestor. */
13152  ancestor_relpath = local_relpath;
13153  for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
13154    {
13155      const char *ancestor_moved_to;
13156
13157      ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
13158
13159      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13160                                        STMT_SELECT_MOVED_TO));
13161      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
13162                                shadowing_op_depth));
13163      SVN_ERR(svn_sqlite__step_row(stmt));
13164
13165      ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
13166      SVN_ERR(svn_sqlite__reset(stmt));
13167      if (ancestor_moved_to)
13168        {
13169          struct svn_wc__db_moved_to_t *moved_to;
13170
13171          node_moved_to
13172              = svn_relpath_join(ancestor_moved_to,
13173                                 svn_relpath_skip_ancestor(ancestor_relpath,
13174                                                           local_relpath),
13175                                 result_pool);
13176
13177          moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13178          moved_to->op_depth = shadowing_op_depth;
13179          moved_to->local_relpath = node_moved_to;
13180          APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13181
13182          SVN_ERR(follow_moved_to(wcroot, node_moved_to,
13183                                  relpath_depth(ancestor_moved_to),
13184                                  moved_tos, result_pool, scratch_pool));
13185
13186          break;
13187        }
13188    }
13189
13190  return SVN_NO_ERROR;
13191}
13192
13193svn_error_t *
13194svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
13195                           svn_wc__db_t *db,
13196                           const char *local_abspath,
13197                           apr_pool_t *result_pool,
13198                           apr_pool_t *scratch_pool)
13199{
13200  svn_wc__db_wcroot_t *wcroot;
13201  const char *local_relpath;
13202
13203  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13204
13205  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13206                              local_abspath, scratch_pool, scratch_pool));
13207  VERIFY_USABLE_WCROOT(wcroot);
13208
13209  *moved_tos = apr_array_make(result_pool, 0,
13210                              sizeof(struct svn_wc__db_moved_to_t *));
13211
13212  /* ### Wrap in a transaction */
13213  SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos,
13214                                      result_pool, scratch_pool),
13215                      wcroot);
13216
13217  /* ### Convert moved_to to abspath */
13218
13219  return SVN_NO_ERROR;
13220}
13221
13222svn_error_t *
13223svn_wc__db_scan_moved_to_internal(const char **move_src_relpath,
13224                                  const char **move_dst_relpath,
13225                                  const char **delete_relpath,
13226                                  svn_wc__db_wcroot_t *wcroot,
13227                                  const char *local_relpath,
13228                                  int op_depth,
13229                                  apr_pool_t *result_pool,
13230                                  apr_pool_t *scratch_pool)
13231{
13232  svn_sqlite__stmt_t *stmt;
13233  svn_boolean_t have_row;
13234  int delete_op_depth;
13235  const char *relpath = local_relpath;
13236  const char *dst_relpath;
13237
13238  SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */
13239
13240  if (move_src_relpath)
13241    *move_src_relpath = NULL;
13242  if (move_dst_relpath)
13243    *move_dst_relpath = NULL;
13244  if (delete_relpath)
13245    *delete_relpath = NULL;
13246
13247  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13248                                    STMT_SELECT_OP_DEPTH_MOVED_TO));
13249  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
13250
13251  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13252
13253  if (!have_row)
13254    {
13255      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
13256                               svn_sqlite__reset(stmt),
13257                               _("Node '%s' is not shadowed"),
13258                               path_for_error_message(wcroot, local_relpath,
13259                                                      scratch_pool));
13260    }
13261
13262  delete_op_depth = svn_sqlite__column_int(stmt, 0);
13263  dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
13264
13265  SVN_ERR(svn_sqlite__reset(stmt));
13266
13267  while (!dst_relpath && have_row)
13268    {
13269      relpath = svn_relpath_dirname(relpath, scratch_pool);
13270
13271      if (relpath_depth(relpath) < delete_op_depth)
13272        break;
13273
13274      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13275                                        STMT_SELECT_DEPTH_NODE));
13276      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath,
13277                                delete_op_depth));
13278
13279      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13280
13281      if (have_row)
13282        dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool);
13283
13284      SVN_ERR(svn_sqlite__reset(stmt));
13285    }
13286
13287  if (dst_relpath)
13288    {
13289      if (move_src_relpath)
13290        *move_src_relpath = apr_pstrdup(result_pool, relpath);
13291
13292      if (move_dst_relpath)
13293        *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath);
13294
13295      if (delete_relpath)
13296        *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth,
13297                                             result_pool);
13298    }
13299
13300  return SVN_NO_ERROR;
13301}
13302
13303/* Public (within libsvn_wc) absolute path version of
13304   svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
13305   BASE. */
13306svn_error_t *
13307svn_wc__db_base_moved_to(const char **move_dst_abspath,
13308                         const char **move_dst_op_root_abspath,
13309                         const char **move_src_root_abspath,
13310                         const char **delete_abspath,
13311                         svn_wc__db_t *db,
13312                         const char *local_abspath,
13313                         apr_pool_t *result_pool,
13314                         apr_pool_t *scratch_pool)
13315{
13316  svn_wc__db_wcroot_t *wcroot;
13317  const char *local_relpath;
13318  const char *dst_root_relpath;
13319  const char *src_root_relpath, *delete_relpath;
13320
13321  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13322
13323
13324  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13325                              local_abspath, scratch_pool, scratch_pool));
13326  VERIFY_USABLE_WCROOT(wcroot);
13327
13328  SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath,
13329                                                        &dst_root_relpath,
13330                                                        &delete_relpath,
13331                                                        wcroot, local_relpath,
13332                                                        0 /* BASE */,
13333                                                        scratch_pool,
13334                                                        scratch_pool),
13335                      wcroot);
13336
13337  if (move_dst_abspath)
13338    *move_dst_abspath =
13339        dst_root_relpath
13340          ? svn_dirent_join(wcroot->abspath,
13341                            svn_dirent_join(
13342                                    dst_root_relpath,
13343                                    svn_relpath_skip_ancestor(src_root_relpath,
13344                                                              local_relpath),
13345                                    scratch_pool),
13346                            result_pool)
13347          : NULL;
13348
13349  if (move_dst_op_root_abspath)
13350    *move_dst_op_root_abspath =
13351          dst_root_relpath
13352              ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool)
13353              : NULL;
13354
13355  if (move_src_root_abspath)
13356    *move_src_root_abspath =
13357          src_root_relpath
13358              ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool)
13359              : NULL;
13360
13361  if (delete_abspath)
13362    *delete_abspath =
13363          delete_relpath
13364              ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool)
13365              : NULL;
13366
13367  return SVN_NO_ERROR;
13368}
13369
13370svn_error_t *
13371svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
13372                         apr_int64_t *repos_id,
13373                         apr_int64_t *wc_id,
13374                         svn_wc__db_t *wc_db,
13375                         const char *dir_abspath,
13376                         const char *repos_root_url,
13377                         const char *repos_uuid,
13378                         apr_pool_t *scratch_pool)
13379{
13380  svn_wc__db_wcroot_t *wcroot;
13381
13382  /* Upgrade is inherently exclusive so specify exclusive locking. */
13383  SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
13384                    repos_root_url, repos_uuid,
13385                    SDB_FILE,
13386                    NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
13387                    TRUE /* exclusive */,
13388                    0 /* timeout */,
13389                    wc_db->state_pool, scratch_pool));
13390
13391  SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
13392                                       apr_pstrdup(wc_db->state_pool,
13393                                                   dir_abspath),
13394                                       *sdb, *wc_id, FORMAT_FROM_SDB,
13395                                       FALSE /* auto-upgrade */,
13396                                       wc_db->state_pool, scratch_pool));
13397
13398  /* The WCROOT is complete. Stash it into DB.  */
13399  svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
13400
13401  return SVN_NO_ERROR;
13402}
13403
13404svn_error_t *
13405svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13406                                   const char *local_abspath,
13407                                   svn_node_kind_t kind,
13408                                   const char *parent_abspath,
13409                                   const char *def_local_abspath,
13410                                   const char *repos_relpath,
13411                                   const char *repos_root_url,
13412                                   const char *repos_uuid,
13413                                   svn_revnum_t def_peg_revision,
13414                                   svn_revnum_t def_revision,
13415                                   apr_pool_t *scratch_pool)
13416{
13417  svn_wc__db_wcroot_t *wcroot;
13418  const char *def_local_relpath;
13419  svn_sqlite__stmt_t *stmt;
13420  svn_boolean_t have_row;
13421  apr_int64_t repos_id;
13422
13423  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13424
13425  /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13426   * WC, i.e. where the svn:externals prop is set. The external target path
13427   * itself may be "hidden behind" other working copies. */
13428  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13429                                                db, def_local_abspath,
13430                                                scratch_pool, scratch_pool));
13431  VERIFY_USABLE_WCROOT(wcroot);
13432
13433
13434  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13435                                    STMT_SELECT_REPOSITORY));
13436  SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13437  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13438
13439  if (have_row)
13440    repos_id = svn_sqlite__column_int64(stmt, 0);
13441  SVN_ERR(svn_sqlite__reset(stmt));
13442
13443  if (!have_row)
13444    {
13445      /* Need to set up a new repository row. */
13446      SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13447                              wcroot->sdb, scratch_pool));
13448    }
13449
13450  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13451                                    STMT_INSERT_EXTERNAL));
13452
13453  /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13454   * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13455  SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13456                            wcroot->wc_id,
13457                            svn_dirent_skip_ancestor(wcroot->abspath,
13458                                                     local_abspath),
13459                            svn_dirent_skip_ancestor(wcroot->abspath,
13460                                                     parent_abspath),
13461                            "normal",
13462                            kind_map, kind,
13463                            def_local_relpath,
13464                            repos_id,
13465                            repos_relpath));
13466
13467  if (SVN_IS_VALID_REVNUM(def_peg_revision))
13468    SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13469
13470  if (SVN_IS_VALID_REVNUM(def_revision))
13471    SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13472
13473  SVN_ERR(svn_sqlite__insert(NULL, stmt));
13474
13475  return SVN_NO_ERROR;
13476}
13477
13478svn_error_t *
13479svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot,
13480                           const svn_skel_t *work_item,
13481                           apr_pool_t *scratch_pool)
13482{
13483  /* Add the work item(s) to the WORK_QUEUE.  */
13484  return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13485                                        scratch_pool));
13486}
13487
13488svn_error_t *
13489svn_wc__db_wq_add(svn_wc__db_t *db,
13490                  const char *wri_abspath,
13491                  const svn_skel_t *work_item,
13492                  apr_pool_t *scratch_pool)
13493{
13494  svn_wc__db_wcroot_t *wcroot;
13495  const char *local_relpath;
13496
13497  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13498
13499  /* Quick exit, if there are no work items to queue up.  */
13500  if (work_item == NULL)
13501    return SVN_NO_ERROR;
13502
13503  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13504                              wri_abspath, scratch_pool, scratch_pool));
13505  VERIFY_USABLE_WCROOT(wcroot);
13506
13507  /* Add the work item(s) to the WORK_QUEUE.  */
13508  return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13509                                        scratch_pool));
13510}
13511
13512/* The body of svn_wc__db_wq_fetch_next().
13513 */
13514static svn_error_t *
13515wq_fetch_next(apr_uint64_t *id,
13516              svn_skel_t **work_item,
13517              svn_wc__db_wcroot_t *wcroot,
13518              const char *local_relpath,
13519              apr_uint64_t completed_id,
13520              apr_pool_t *result_pool,
13521              apr_pool_t *scratch_pool)
13522{
13523  svn_sqlite__stmt_t *stmt;
13524  svn_boolean_t have_row;
13525
13526  if (completed_id != 0)
13527    {
13528      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13529                                        STMT_DELETE_WORK_ITEM));
13530      SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13531
13532      SVN_ERR(svn_sqlite__step_done(stmt));
13533    }
13534
13535  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13536                                    STMT_SELECT_WORK_ITEM));
13537  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13538
13539  if (!have_row)
13540    {
13541      *id = 0;
13542      *work_item = NULL;
13543    }
13544  else
13545    {
13546      apr_size_t len;
13547      const void *val;
13548
13549      *id = svn_sqlite__column_int64(stmt, 0);
13550
13551      val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13552
13553      *work_item = svn_skel__parse(val, len, result_pool);
13554    }
13555
13556  return svn_error_trace(svn_sqlite__reset(stmt));
13557}
13558
13559svn_error_t *
13560svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13561                         svn_skel_t **work_item,
13562                         svn_wc__db_t *db,
13563                         const char *wri_abspath,
13564                         apr_uint64_t completed_id,
13565                         apr_pool_t *result_pool,
13566                         apr_pool_t *scratch_pool)
13567{
13568  svn_wc__db_wcroot_t *wcroot;
13569  const char *local_relpath;
13570
13571  SVN_ERR_ASSERT(id != NULL);
13572  SVN_ERR_ASSERT(work_item != NULL);
13573  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13574
13575  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13576                              wri_abspath, scratch_pool, scratch_pool));
13577  VERIFY_USABLE_WCROOT(wcroot);
13578
13579  SVN_WC__DB_WITH_TXN(
13580    wq_fetch_next(id, work_item,
13581                  wcroot, local_relpath, completed_id,
13582                  result_pool, scratch_pool),
13583    wcroot);
13584
13585  return SVN_NO_ERROR;
13586}
13587
13588/* Records timestamp and date for one or more files in wcroot */
13589static svn_error_t *
13590wq_record(svn_wc__db_wcroot_t *wcroot,
13591          apr_hash_t *record_map,
13592          apr_pool_t *scratch_pool)
13593{
13594  apr_hash_index_t *hi;
13595  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13596
13597  for (hi = apr_hash_first(scratch_pool, record_map); hi;
13598       hi = apr_hash_next(hi))
13599    {
13600      const char *local_abspath = apr_hash_this_key(hi);
13601      const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
13602      const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13603                                                           local_abspath);
13604
13605      svn_pool_clear(iterpool);
13606
13607      if (! local_relpath)
13608        continue;
13609
13610      SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13611                                 dirent->filesize, dirent->mtime,
13612                                 iterpool));
13613    }
13614
13615  svn_pool_destroy(iterpool);
13616  return SVN_NO_ERROR;
13617}
13618
13619svn_error_t *
13620svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13621                                    svn_skel_t **work_item,
13622                                    svn_wc__db_t *db,
13623                                    const char *wri_abspath,
13624                                    apr_uint64_t completed_id,
13625                                    apr_hash_t *record_map,
13626                                    apr_pool_t *result_pool,
13627                                    apr_pool_t *scratch_pool)
13628{
13629  svn_wc__db_wcroot_t *wcroot;
13630  const char *local_relpath;
13631
13632  SVN_ERR_ASSERT(id != NULL);
13633  SVN_ERR_ASSERT(work_item != NULL);
13634  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13635
13636  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13637                              wri_abspath, scratch_pool, scratch_pool));
13638  VERIFY_USABLE_WCROOT(wcroot);
13639
13640  SVN_WC__DB_WITH_TXN(
13641    svn_error_compose_create(
13642            wq_fetch_next(id, work_item,
13643                          wcroot, local_relpath, completed_id,
13644                          result_pool, scratch_pool),
13645            wq_record(wcroot, record_map, scratch_pool)),
13646    wcroot);
13647
13648  return SVN_NO_ERROR;
13649}
13650
13651
13652
13653/* ### temporary API. remove before release.  */
13654svn_error_t *
13655svn_wc__db_temp_get_format(int *format,
13656                           svn_wc__db_t *db,
13657                           const char *local_dir_abspath,
13658                           apr_pool_t *scratch_pool)
13659{
13660  svn_wc__db_wcroot_t *wcroot;
13661  const char *local_relpath;
13662  svn_error_t *err;
13663
13664  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13665  /* ### assert that we were passed a directory?  */
13666
13667  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13668                                local_dir_abspath, scratch_pool, scratch_pool);
13669
13670  /* If we hit an error examining this directory, then declare this
13671     directory to not be a working copy.  */
13672  if (err)
13673    {
13674      if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13675        return svn_error_trace(err);
13676      svn_error_clear(err);
13677
13678      /* Remap the returned error.  */
13679      *format = 0;
13680      return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13681                               _("'%s' is not a working copy"),
13682                               svn_dirent_local_style(local_dir_abspath,
13683                                                      scratch_pool));
13684    }
13685
13686  SVN_ERR_ASSERT(wcroot != NULL);
13687  SVN_ERR_ASSERT(wcroot->format >= 1);
13688
13689  *format = wcroot->format;
13690
13691  return SVN_NO_ERROR;
13692}
13693
13694/* ### temporary API. remove before release.  */
13695svn_wc_adm_access_t *
13696svn_wc__db_temp_get_access(svn_wc__db_t *db,
13697                           const char *local_dir_abspath,
13698                           apr_pool_t *scratch_pool)
13699{
13700  const char *local_relpath;
13701  svn_wc__db_wcroot_t *wcroot;
13702  svn_error_t *err;
13703
13704  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13705
13706  /* ### we really need to assert that we were passed a directory. sometimes
13707     ### adm_retrieve_internal is asked about a file, and then it asks us
13708     ### for an access baton for it. we should definitely return NULL, but
13709     ### ideally: the caller would never ask us about a non-directory.  */
13710
13711  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13712                            db, local_dir_abspath, scratch_pool, scratch_pool);
13713  if (err)
13714    {
13715      svn_error_clear(err);
13716      return NULL;
13717    }
13718
13719  if (!wcroot)
13720    return NULL;
13721
13722  return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13723}
13724
13725
13726/* ### temporary API. remove before release.  */
13727void
13728svn_wc__db_temp_set_access(svn_wc__db_t *db,
13729                           const char *local_dir_abspath,
13730                           svn_wc_adm_access_t *adm_access,
13731                           apr_pool_t *scratch_pool)
13732{
13733  const char *local_relpath;
13734  svn_wc__db_wcroot_t *wcroot;
13735  svn_error_t *err;
13736
13737  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13738  /* ### assert that we were passed a directory?  */
13739
13740  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13741                            db, local_dir_abspath, scratch_pool, scratch_pool);
13742  if (err)
13743    {
13744      /* We don't even have a wcroot, so just bail. */
13745      svn_error_clear(err);
13746      return;
13747    }
13748
13749  /* Better not override something already there.  */
13750  SVN_ERR_ASSERT_NO_RETURN(
13751    svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13752  );
13753  svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13754}
13755
13756
13757/* ### temporary API. remove before release.  */
13758svn_error_t *
13759svn_wc__db_temp_close_access(svn_wc__db_t *db,
13760                             const char *local_dir_abspath,
13761                             svn_wc_adm_access_t *adm_access,
13762                             apr_pool_t *scratch_pool)
13763{
13764  const char *local_relpath;
13765  svn_wc__db_wcroot_t *wcroot;
13766
13767  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13768  /* ### assert that we were passed a directory?  */
13769
13770  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13771                              local_dir_abspath, scratch_pool, scratch_pool));
13772  svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13773
13774  return SVN_NO_ERROR;
13775}
13776
13777
13778/* ### temporary API. remove before release.  */
13779void
13780svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13781                             const char *local_dir_abspath,
13782                             apr_pool_t *scratch_pool)
13783{
13784  const char *local_relpath;
13785  svn_wc__db_wcroot_t *wcroot;
13786  svn_error_t *err;
13787
13788  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13789  /* ### assert that we were passed a directory?  */
13790
13791  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13792                            db, local_dir_abspath, scratch_pool, scratch_pool);
13793  if (err)
13794    {
13795      svn_error_clear(err);
13796      return;
13797    }
13798
13799  svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13800}
13801
13802
13803apr_hash_t *
13804svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13805                               apr_pool_t *result_pool)
13806{
13807  apr_hash_t *result = apr_hash_make(result_pool);
13808  apr_hash_index_t *hi;
13809
13810  for (hi = apr_hash_first(result_pool, db->dir_data);
13811       hi;
13812       hi = apr_hash_next(hi))
13813    {
13814      const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
13815
13816      /* This is highly redundant, 'cause the same WCROOT will appear many
13817         times in dir_data. */
13818      result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13819    }
13820
13821  return result;
13822}
13823
13824
13825svn_error_t *
13826svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13827                           svn_wc__db_t *db,
13828                           const char *local_dir_abspath,
13829                           apr_pool_t *scratch_pool)
13830{
13831  svn_wc__db_wcroot_t *wcroot;
13832  const char *local_relpath;
13833
13834  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13835
13836  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13837                            local_dir_abspath, scratch_pool, scratch_pool));
13838  VERIFY_USABLE_WCROOT(wcroot);
13839
13840  *sdb = wcroot->sdb;
13841
13842  return SVN_NO_ERROR;
13843}
13844
13845
13846svn_error_t *
13847svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13848                                 svn_wc__db_t *db,
13849                                 const char *local_abspath,
13850                                 apr_pool_t *result_pool,
13851                                 apr_pool_t *scratch_pool)
13852{
13853  svn_wc__db_wcroot_t *wcroot;
13854  const char *local_relpath;
13855  svn_sqlite__stmt_t *stmt;
13856  svn_boolean_t have_row;
13857  apr_array_header_t *new_victims;
13858
13859  /* The parent should be a working copy directory. */
13860  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13861                              local_abspath, scratch_pool, scratch_pool));
13862  VERIFY_USABLE_WCROOT(wcroot);
13863
13864  /* ### This will be much easier once we have all conflicts in one
13865         field of actual*/
13866
13867  /* Look for text, tree and property conflicts in ACTUAL */
13868  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13869                                    STMT_SELECT_CONFLICT_VICTIMS));
13870  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13871
13872  new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13873
13874  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13875  while (have_row)
13876    {
13877      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13878
13879      APR_ARRAY_PUSH(new_victims, const char *) =
13880                            svn_relpath_basename(child_relpath, result_pool);
13881
13882      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13883    }
13884
13885  SVN_ERR(svn_sqlite__reset(stmt));
13886
13887  *victims = new_victims;
13888  return SVN_NO_ERROR;
13889}
13890
13891/* The body of svn_wc__db_get_conflict_marker_files().
13892 */
13893static svn_error_t *
13894get_conflict_marker_files(apr_hash_t **marker_files_p,
13895                          svn_wc__db_wcroot_t *wcroot,
13896                          const char *local_relpath,
13897                          svn_wc__db_t *db,
13898                          apr_pool_t *result_pool,
13899                          apr_pool_t *scratch_pool)
13900{
13901  svn_sqlite__stmt_t *stmt;
13902  svn_boolean_t have_row;
13903  apr_hash_t *marker_files = apr_hash_make(result_pool);
13904
13905  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13906                                    STMT_SELECT_ACTUAL_NODE));
13907  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13908  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13909
13910  if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13911    {
13912      apr_size_t len;
13913      const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13914      svn_skel_t *conflicts;
13915      const apr_array_header_t *markers;
13916      int i;
13917
13918      conflicts = svn_skel__parse(data, len, scratch_pool);
13919
13920      /* ### ADD markers to *marker_files */
13921      SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13922                                            conflicts,
13923                                            result_pool, scratch_pool));
13924
13925      for (i = 0; markers && (i < markers->nelts); i++)
13926        {
13927          const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13928
13929          svn_hash_sets(marker_files, marker_abspath, "");
13930        }
13931    }
13932  SVN_ERR(svn_sqlite__reset(stmt));
13933
13934  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13935                                    STMT_SELECT_CONFLICT_VICTIMS));
13936  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13937  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13938
13939  while (have_row)
13940    {
13941      apr_size_t len;
13942      const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13943
13944      const apr_array_header_t *markers;
13945      int i;
13946
13947      if (data)
13948        {
13949          svn_skel_t *conflicts;
13950          conflicts = svn_skel__parse(data, len, scratch_pool);
13951
13952          SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13953                                                conflicts,
13954                                                result_pool, scratch_pool));
13955
13956          for (i = 0; markers && (i < markers->nelts); i++)
13957            {
13958              const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13959
13960              svn_hash_sets(marker_files, marker_abspath, "");
13961            }
13962        }
13963
13964      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13965    }
13966
13967  if (apr_hash_count(marker_files))
13968    *marker_files_p = marker_files;
13969  else
13970    *marker_files_p = NULL;
13971
13972  return svn_error_trace(svn_sqlite__reset(stmt));
13973}
13974
13975svn_error_t *
13976svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13977                                     svn_wc__db_t *db,
13978                                     const char *local_abspath,
13979                                     apr_pool_t *result_pool,
13980                                     apr_pool_t *scratch_pool)
13981{
13982  svn_wc__db_wcroot_t *wcroot;
13983  const char *local_relpath;
13984
13985  /* The parent should be a working copy directory. */
13986  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13987                              local_abspath, scratch_pool, scratch_pool));
13988  VERIFY_USABLE_WCROOT(wcroot);
13989
13990  SVN_WC__DB_WITH_TXN(
13991    get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13992                              result_pool, scratch_pool),
13993    wcroot);
13994
13995  return SVN_NO_ERROR;
13996}
13997
13998
13999svn_error_t *
14000svn_wc__db_read_conflict(svn_skel_t **conflict,
14001                         svn_node_kind_t *kind,
14002                         apr_hash_t **props,
14003                         svn_wc__db_t *db,
14004                         const char *local_abspath,
14005                         apr_pool_t *result_pool,
14006                         apr_pool_t *scratch_pool)
14007{
14008  svn_wc__db_wcroot_t *wcroot;
14009  const char *local_relpath;
14010
14011  /* The parent should be a working copy directory. */
14012  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14013                              local_abspath, scratch_pool, scratch_pool));
14014  VERIFY_USABLE_WCROOT(wcroot);
14015
14016  return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props,
14017                                                           wcroot, local_relpath,
14018                                                           result_pool,
14019                                                           scratch_pool));
14020}
14021
14022svn_error_t *
14023svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
14024                                  svn_node_kind_t *kind,
14025                                  apr_hash_t **props,
14026                                  svn_wc__db_wcroot_t *wcroot,
14027                                  const char *local_relpath,
14028                                  apr_pool_t *result_pool,
14029                                  apr_pool_t *scratch_pool)
14030{
14031  svn_sqlite__stmt_t *stmt;
14032  svn_boolean_t have_row;
14033
14034  if (kind)
14035    *kind = svn_node_none;
14036  if (props)
14037    *props = NULL;
14038
14039  /* Check if we have a conflict in ACTUAL */
14040  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14041                                    STMT_SELECT_ACTUAL_NODE));
14042  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14043
14044  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14045
14046  if (have_row)
14047    {
14048      apr_size_t cfl_len;
14049      const void *cfl_data;
14050
14051      /* svn_skel__parse doesn't copy data, so store in result_pool */
14052      cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
14053
14054      if (cfl_data)
14055        *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
14056      else
14057        *conflict = NULL;
14058
14059      if (props)
14060        {
14061          svn_error_t *err;
14062
14063          err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1,
14064                                                              result_pool,
14065                                                              scratch_pool));
14066
14067          if (err)
14068            return svn_error_compose_create(err, svn_sqlite__reset(stmt));
14069        }
14070    }
14071  else
14072    *conflict = NULL;
14073
14074  SVN_ERR(svn_sqlite__reset(stmt));
14075
14076  if (!have_row || kind || (props && !*props))
14077    {
14078      svn_error_t *err = NULL;
14079      svn_boolean_t have_info = FALSE;
14080
14081      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14082                                        STMT_SELECT_NODE_INFO));
14083
14084      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
14085                                local_relpath));
14086
14087      SVN_ERR(svn_sqlite__step(&have_info, stmt));
14088
14089      if (have_info)
14090        {
14091          if (kind)
14092            {
14093              svn_wc__db_status_t status;
14094              int op_depth = svn_sqlite__column_int(stmt, 0);
14095
14096              status = svn_sqlite__column_token(stmt, 3, presence_map);
14097
14098              if (op_depth > 0)
14099                err = convert_to_working_status(&status, status);
14100
14101              if (!err && (status == svn_wc__db_status_normal
14102                           || status == svn_wc__db_status_added
14103                           || status == svn_wc__db_status_deleted
14104                           || status == svn_wc__db_status_incomplete))
14105                {
14106                  *kind = svn_sqlite__column_token(stmt, 4, kind_map);
14107                }
14108            }
14109
14110          /* Need props, and no props in ACTUAL? */
14111          if (!err && (props && !*props))
14112            {
14113              err = svn_sqlite__column_properties(props, stmt, 14,
14114                                                  result_pool, scratch_pool);
14115            }
14116        }
14117
14118      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14119
14120      if (!have_row && !have_info)
14121        {
14122          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14123                                   _("The node '%s' was not found."),
14124                                   path_for_error_message(wcroot,
14125                                                          local_relpath,
14126                                                          scratch_pool));
14127        }
14128    }
14129
14130  return SVN_NO_ERROR;
14131}
14132
14133
14134svn_error_t *
14135svn_wc__db_read_kind(svn_node_kind_t *kind,
14136                     svn_wc__db_t *db,
14137                     const char *local_abspath,
14138                     svn_boolean_t allow_missing,
14139                     svn_boolean_t show_deleted,
14140                     svn_boolean_t show_hidden,
14141                     apr_pool_t *scratch_pool)
14142{
14143  svn_wc__db_wcroot_t *wcroot;
14144  const char *local_relpath;
14145  svn_sqlite__stmt_t *stmt_info;
14146  svn_boolean_t have_info;
14147  svn_wc__db_status_t status;
14148
14149  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14150
14151  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14152                              local_abspath, scratch_pool, scratch_pool));
14153  VERIFY_USABLE_WCROOT(wcroot);
14154
14155  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
14156                                    STMT_SELECT_NODE_INFO));
14157  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
14158  SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14159
14160  if (!have_info)
14161    {
14162      if (allow_missing)
14163        {
14164          *kind = svn_node_unknown;
14165          SVN_ERR(svn_sqlite__reset(stmt_info));
14166          return SVN_NO_ERROR;
14167        }
14168      else
14169        {
14170          SVN_ERR(svn_sqlite__reset(stmt_info));
14171          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14172                                   _("The node '%s' was not found."),
14173                                   path_for_error_message(wcroot,
14174                                                          local_relpath,
14175                                                          scratch_pool));
14176        }
14177    }
14178
14179  status = svn_sqlite__column_token(stmt_info, 3, presence_map);
14180
14181  if (show_deleted && status == svn_wc__db_status_base_deleted)
14182    {
14183      /* Let's return the kind of what is really deleted insead of what
14184         we have cached in the base-deleted record */
14185
14186      SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14187
14188      if (!have_info)
14189        {
14190          /* No lower layer deleted? Database inconsistency! */
14191          *kind = svn_node_none;
14192          return svn_error_trace(svn_sqlite__reset(stmt_info));
14193        }
14194    }
14195
14196  if (!(show_deleted && show_hidden))
14197    {
14198      int op_depth = svn_sqlite__column_int(stmt_info, 0);
14199      svn_boolean_t report_none = FALSE;
14200
14201      if (op_depth > 0)
14202        SVN_ERR(convert_to_working_status(&status, status));
14203
14204      switch (status)
14205        {
14206          case svn_wc__db_status_not_present:
14207            if (! (show_hidden && show_deleted))
14208              report_none = TRUE;
14209            break;
14210          case svn_wc__db_status_excluded:
14211          case svn_wc__db_status_server_excluded:
14212            if (! show_hidden)
14213              report_none = TRUE;
14214            break;
14215          case svn_wc__db_status_deleted:
14216            if (! show_deleted)
14217              report_none = TRUE;
14218            break;
14219          default:
14220            break;
14221        }
14222
14223      if (report_none)
14224        {
14225          *kind = svn_node_none;
14226          return svn_error_trace(svn_sqlite__reset(stmt_info));
14227        }
14228    }
14229
14230  *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
14231
14232  return svn_error_trace(svn_sqlite__reset(stmt_info));
14233}
14234
14235svn_error_t *
14236svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
14237                     svn_wc__db_t *db,
14238                     const char *local_abspath,
14239                     apr_pool_t *scratch_pool)
14240{
14241  svn_wc__db_wcroot_t *wcroot;
14242  const char *local_relpath;
14243
14244  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14245
14246  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14247                              local_abspath, scratch_pool, scratch_pool));
14248  VERIFY_USABLE_WCROOT(wcroot);
14249
14250  if (*local_relpath != '\0')
14251    {
14252      *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
14253                           the same wcroot */
14254      return SVN_NO_ERROR;
14255    }
14256
14257   *is_wcroot = TRUE;
14258
14259   return SVN_NO_ERROR;
14260}
14261
14262/* Find a node's kind and whether it is switched, putting the outputs in
14263 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
14264 */
14265static svn_error_t *
14266db_is_switched(svn_boolean_t *is_switched,
14267               svn_node_kind_t *kind,
14268               svn_wc__db_wcroot_t *wcroot,
14269               const char *local_relpath,
14270               apr_pool_t *scratch_pool)
14271{
14272  svn_wc__db_status_t status;
14273  apr_int64_t repos_id;
14274  const char *repos_relpath;
14275  const char *name;
14276  const char *parent_local_relpath;
14277  apr_int64_t parent_repos_id;
14278  const char *parent_repos_relpath;
14279
14280  SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
14281
14282  SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
14283                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14284                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14285                    wcroot, local_relpath, scratch_pool, scratch_pool));
14286
14287  if (status == svn_wc__db_status_server_excluded
14288      || status == svn_wc__db_status_excluded
14289      || status == svn_wc__db_status_not_present)
14290    {
14291      return svn_error_createf(
14292                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14293                    _("The node '%s' was not found."),
14294                    path_for_error_message(wcroot, local_relpath,
14295                                           scratch_pool));
14296    }
14297  else if (! repos_relpath)
14298    {
14299      /* Node is shadowed; easy out */
14300      if (is_switched)
14301        *is_switched = FALSE;
14302
14303      return SVN_NO_ERROR;
14304    }
14305
14306  if (! is_switched)
14307    return SVN_NO_ERROR;
14308
14309  svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
14310
14311  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14312                                            &parent_repos_relpath,
14313                                            &parent_repos_id, NULL, NULL, NULL,
14314                                            NULL, NULL, NULL, NULL, NULL,
14315                                            NULL, NULL,
14316                                            wcroot, parent_local_relpath,
14317                                            scratch_pool, scratch_pool));
14318
14319  if (repos_id != parent_repos_id)
14320    *is_switched = TRUE;
14321  else
14322    {
14323      const char *expected_relpath;
14324
14325      expected_relpath = svn_relpath_join(parent_repos_relpath, name,
14326                                          scratch_pool);
14327
14328      *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
14329    }
14330
14331  return SVN_NO_ERROR;
14332}
14333
14334svn_error_t *
14335svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
14336                       svn_boolean_t *is_switched,
14337                       svn_node_kind_t *kind,
14338                       svn_wc__db_t *db,
14339                       const char *local_abspath,
14340                       apr_pool_t *scratch_pool)
14341{
14342  svn_wc__db_wcroot_t *wcroot;
14343  const char *local_relpath;
14344
14345  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14346
14347  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14348                              local_abspath, scratch_pool, scratch_pool));
14349  VERIFY_USABLE_WCROOT(wcroot);
14350
14351  if (is_switched)
14352    *is_switched = FALSE;
14353
14354  if (*local_relpath == '\0')
14355    {
14356      /* Easy out */
14357      if (is_wcroot)
14358        *is_wcroot = TRUE;
14359
14360      if (kind)
14361        *kind = svn_node_dir;
14362      return SVN_NO_ERROR;
14363    }
14364
14365  if (is_wcroot)
14366    *is_wcroot = FALSE;
14367
14368  if (! is_switched && ! kind)
14369    return SVN_NO_ERROR;
14370
14371  SVN_WC__DB_WITH_TXN(
14372    db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
14373    wcroot);
14374  return SVN_NO_ERROR;
14375}
14376
14377
14378svn_error_t *
14379svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14380                               svn_wc__db_t *db,
14381                               const char *wri_abspath,
14382                               apr_pool_t *result_pool,
14383                               apr_pool_t *scratch_pool)
14384{
14385  svn_wc__db_wcroot_t *wcroot;
14386  const char *local_relpath;
14387
14388  SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14389  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14390
14391  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14392                              wri_abspath, scratch_pool, scratch_pool));
14393  VERIFY_USABLE_WCROOT(wcroot);
14394
14395  *temp_dir_abspath = svn_dirent_join_many(result_pool,
14396                                           wcroot->abspath,
14397                                           svn_wc_get_adm_dir(scratch_pool),
14398                                           WCROOT_TEMPDIR_RELPATH,
14399                                           SVN_VA_NULL);
14400  return SVN_NO_ERROR;
14401}
14402
14403
14404/* Helper for wclock_obtain_cb() to steal an existing lock */
14405static svn_error_t *
14406wclock_steal(svn_wc__db_wcroot_t *wcroot,
14407             const char *local_relpath,
14408             apr_pool_t *scratch_pool)
14409{
14410  svn_sqlite__stmt_t *stmt;
14411
14412  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14413  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14414
14415  SVN_ERR(svn_sqlite__step_done(stmt));
14416
14417  return SVN_NO_ERROR;
14418}
14419
14420
14421/* The body of svn_wc__db_wclock_obtain().
14422 */
14423static svn_error_t *
14424wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14425                 const char *local_relpath,
14426                 int levels_to_lock,
14427                 svn_boolean_t steal_lock,
14428                 svn_boolean_t enforce_empty_wq,
14429                 apr_pool_t *scratch_pool)
14430{
14431  svn_sqlite__stmt_t *stmt;
14432  svn_error_t *err;
14433  const char *lock_relpath;
14434  int max_depth;
14435  int lock_depth;
14436  svn_boolean_t got_row;
14437
14438  svn_wc__db_wclock_t lock;
14439
14440  /* Upgrade locks the root before the node exists.  Apart from that
14441     the root node always exists so we will just skip the check.
14442
14443     ### Perhaps the lock for upgrade should be created when the db is
14444         created?  1.6 used to lock .svn on creation. */
14445  if (local_relpath[0])
14446    {
14447      svn_boolean_t exists;
14448
14449      SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14450      if (!exists)
14451        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14452                                 _("The node '%s' was not found."),
14453                                 path_for_error_message(wcroot,
14454                                                        local_relpath,
14455                                                        scratch_pool));
14456    }
14457
14458  if (enforce_empty_wq)
14459    SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb));
14460
14461  /* Check if there are nodes locked below the new lock root */
14462  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14463  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14464
14465  lock_depth = relpath_depth(local_relpath);
14466  max_depth = lock_depth + levels_to_lock;
14467
14468  SVN_ERR(svn_sqlite__step(&got_row, stmt));
14469
14470  while (got_row)
14471    {
14472      svn_boolean_t own_lock;
14473
14474      lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14475
14476      /* If we are not locking with depth infinity, check if this lock
14477         voids our lock request */
14478      if (levels_to_lock >= 0
14479          && relpath_depth(lock_relpath) > max_depth)
14480        {
14481          SVN_ERR(svn_sqlite__step(&got_row, stmt));
14482          continue;
14483        }
14484
14485      /* Check if we are the lock owner, because we should be able to
14486         extend our lock. */
14487      err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot,
14488                                                 lock_relpath,
14489                                                 TRUE, scratch_pool);
14490
14491      if (err)
14492        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14493
14494      if (!own_lock && !steal_lock)
14495        {
14496          SVN_ERR(svn_sqlite__reset(stmt));
14497          err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14498                                   _("'%s' is already locked."),
14499                                   path_for_error_message(wcroot,
14500                                                          lock_relpath,
14501                                                          scratch_pool));
14502          return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14503                                   _("Working copy '%s' locked."),
14504                                   path_for_error_message(wcroot,
14505                                                          local_relpath,
14506                                                          scratch_pool));
14507        }
14508      else if (!own_lock)
14509        {
14510          err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14511
14512          if (err)
14513            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14514        }
14515
14516      SVN_ERR(svn_sqlite__step(&got_row, stmt));
14517    }
14518
14519  SVN_ERR(svn_sqlite__reset(stmt));
14520
14521  if (steal_lock)
14522    SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14523
14524  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14525  lock_relpath = local_relpath;
14526
14527  while (TRUE)
14528    {
14529      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14530
14531      SVN_ERR(svn_sqlite__step(&got_row, stmt));
14532
14533      if (got_row)
14534        {
14535          int levels = svn_sqlite__column_int(stmt, 0);
14536          if (levels >= 0)
14537            levels += relpath_depth(lock_relpath);
14538
14539          SVN_ERR(svn_sqlite__reset(stmt));
14540
14541          if (levels == -1 || levels >= lock_depth)
14542            {
14543
14544              err = svn_error_createf(
14545                              SVN_ERR_WC_LOCKED, NULL,
14546                              _("'%s' is already locked."),
14547                              svn_dirent_local_style(
14548                                       svn_dirent_join(wcroot->abspath,
14549                                                       lock_relpath,
14550                                                       scratch_pool),
14551                              scratch_pool));
14552              return svn_error_createf(
14553                              SVN_ERR_WC_LOCKED, err,
14554                              _("Working copy '%s' locked."),
14555                              path_for_error_message(wcroot,
14556                                                     local_relpath,
14557                                                     scratch_pool));
14558            }
14559
14560          break; /* There can't be interesting locks on higher nodes */
14561        }
14562      else
14563        SVN_ERR(svn_sqlite__reset(stmt));
14564
14565      if (!*lock_relpath)
14566        break;
14567
14568      lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14569    }
14570
14571  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14572  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14573                            levels_to_lock));
14574  err = svn_sqlite__insert(NULL, stmt);
14575  if (err)
14576    return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14577                             _("Failed to lock working copy '%s'."),
14578                             path_for_error_message(wcroot,
14579                                                    local_relpath,
14580                                                    scratch_pool));
14581
14582  /* And finally store that we obtained the lock */
14583  lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14584  lock.levels = levels_to_lock;
14585  APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14586
14587  return SVN_NO_ERROR;
14588}
14589
14590
14591svn_error_t *
14592svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14593                         const char *local_abspath,
14594                         int levels_to_lock,
14595                         svn_boolean_t steal_lock,
14596                         apr_pool_t *scratch_pool)
14597{
14598  svn_wc__db_wcroot_t *wcroot;
14599  const char *local_relpath;
14600
14601  SVN_ERR_ASSERT(levels_to_lock >= -1);
14602  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14603
14604  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14605                                             db, local_abspath,
14606                                             scratch_pool, scratch_pool));
14607  VERIFY_USABLE_WCROOT(wcroot);
14608
14609  if (!steal_lock)
14610    {
14611      int i;
14612      int depth = relpath_depth(local_relpath);
14613
14614      for (i = 0; i < wcroot->owned_locks->nelts; i++)
14615        {
14616          svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14617                                                     i, svn_wc__db_wclock_t);
14618
14619          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14620              && (lock->levels == -1
14621                  || (lock->levels + relpath_depth(lock->local_relpath))
14622                            >= depth))
14623            {
14624              return svn_error_createf(
14625                SVN_ERR_WC_LOCKED, NULL,
14626                _("'%s' is already locked via '%s'."),
14627                svn_dirent_local_style(local_abspath, scratch_pool),
14628                path_for_error_message(wcroot, lock->local_relpath,
14629                                       scratch_pool));
14630            }
14631        }
14632    }
14633
14634  SVN_WC__DB_WITH_TXN(
14635    wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14636                     db->enforce_empty_wq, scratch_pool),
14637    wcroot);
14638  return SVN_NO_ERROR;
14639}
14640
14641
14642/* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14643static svn_error_t *
14644find_wclock(const char **lock_relpath,
14645            svn_wc__db_wcroot_t *wcroot,
14646            const char *dir_relpath,
14647            apr_pool_t *result_pool,
14648            apr_pool_t *scratch_pool)
14649{
14650  svn_sqlite__stmt_t *stmt;
14651  svn_boolean_t have_row;
14652  int dir_depth = relpath_depth(dir_relpath);
14653  const char *first_relpath;
14654
14655  /* Check for locks on all directories that might be ancestors.
14656     As our new apis only use recursive locks the number of locks stored
14657     in the DB will be very low */
14658  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14659                                    STMT_SELECT_ANCESTOR_WCLOCKS));
14660
14661  /* Get the top level relpath to reduce the worst case number of results
14662     to the number of directories below this node plus two.
14663     (1: the node itself and 2: the wcroot). */
14664  first_relpath = strchr(dir_relpath, '/');
14665
14666  if (first_relpath != NULL)
14667    first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14668                                 first_relpath - dir_relpath);
14669  else
14670    first_relpath = dir_relpath;
14671
14672  SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14673                            wcroot->wc_id,
14674                            dir_relpath,
14675                            first_relpath));
14676
14677  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14678
14679  while (have_row)
14680    {
14681      const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14682
14683      if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14684        {
14685          int locked_levels = svn_sqlite__column_int(stmt, 1);
14686          int row_depth = relpath_depth(relpath);
14687
14688          if (locked_levels == -1
14689              || locked_levels + row_depth >= dir_depth)
14690            {
14691              *lock_relpath = apr_pstrdup(result_pool, relpath);
14692              SVN_ERR(svn_sqlite__reset(stmt));
14693              return SVN_NO_ERROR;
14694            }
14695        }
14696
14697      SVN_ERR(svn_sqlite__step(&have_row, stmt));
14698    }
14699
14700  *lock_relpath = NULL;
14701
14702  return svn_error_trace(svn_sqlite__reset(stmt));
14703}
14704
14705static svn_error_t *
14706is_wclocked(svn_boolean_t *locked,
14707            svn_wc__db_wcroot_t *wcroot,
14708            const char *dir_relpath,
14709            apr_pool_t *scratch_pool)
14710{
14711  const char *lock_relpath;
14712
14713  SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14714                      scratch_pool, scratch_pool));
14715  *locked = (lock_relpath != NULL);
14716  return SVN_NO_ERROR;
14717}
14718
14719
14720svn_error_t*
14721svn_wc__db_wclock_find_root(const char **lock_abspath,
14722                            svn_wc__db_t *db,
14723                            const char *local_abspath,
14724                            apr_pool_t *result_pool,
14725                            apr_pool_t *scratch_pool)
14726{
14727  svn_wc__db_wcroot_t *wcroot;
14728  const char *local_relpath;
14729  const char *lock_relpath;
14730
14731  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14732                              local_abspath, scratch_pool, scratch_pool));
14733  VERIFY_USABLE_WCROOT(wcroot);
14734
14735  SVN_WC__DB_WITH_TXN(
14736    find_wclock(&lock_relpath, wcroot, local_relpath,
14737                scratch_pool, scratch_pool),
14738    wcroot);
14739
14740  if (!lock_relpath)
14741    *lock_abspath = NULL;
14742  else
14743    SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14744                                    lock_relpath, result_pool, scratch_pool));
14745  return SVN_NO_ERROR;
14746}
14747
14748
14749svn_error_t *
14750svn_wc__db_wclocked(svn_boolean_t *locked,
14751                    svn_wc__db_t *db,
14752                    const char *local_abspath,
14753                    apr_pool_t *scratch_pool)
14754{
14755  svn_wc__db_wcroot_t *wcroot;
14756  const char *local_relpath;
14757
14758  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14759                              local_abspath, scratch_pool, scratch_pool));
14760  VERIFY_USABLE_WCROOT(wcroot);
14761
14762  SVN_WC__DB_WITH_TXN(
14763    is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14764    wcroot);
14765
14766  return SVN_NO_ERROR;
14767}
14768
14769
14770svn_error_t *
14771svn_wc__db_wclock_release(svn_wc__db_t *db,
14772                          const char *local_abspath,
14773                          apr_pool_t *scratch_pool)
14774{
14775  svn_sqlite__stmt_t *stmt;
14776  svn_wc__db_wcroot_t *wcroot;
14777  const char *local_relpath;
14778  int i;
14779  apr_array_header_t *owned_locks;
14780
14781  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14782                              local_abspath, scratch_pool, scratch_pool));
14783
14784  VERIFY_USABLE_WCROOT(wcroot);
14785
14786  /* First check and remove the owns-lock information as failure in
14787     removing the db record implies that we have to steal the lock later. */
14788  owned_locks = wcroot->owned_locks;
14789  for (i = 0; i < owned_locks->nelts; i++)
14790    {
14791      svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14792                                                 svn_wc__db_wclock_t);
14793
14794      if (strcmp(lock->local_relpath, local_relpath) == 0)
14795        break;
14796    }
14797
14798  if (i >= owned_locks->nelts)
14799    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14800                             _("Working copy not locked at '%s'."),
14801                             svn_dirent_local_style(local_abspath,
14802                                                    scratch_pool));
14803
14804  if (i < owned_locks->nelts)
14805    {
14806      owned_locks->nelts--;
14807
14808      /* Move the last item in the array to the deleted place */
14809      if (owned_locks->nelts > 0)
14810        APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14811           APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14812    }
14813
14814  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14815                                    STMT_DELETE_WC_LOCK));
14816
14817  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14818
14819  SVN_ERR(svn_sqlite__step_done(stmt));
14820
14821  return SVN_NO_ERROR;
14822}
14823
14824
14825/* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14826   of DB+LOCAL_ABSPATH.  */
14827svn_error_t *
14828svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock,
14829                                     svn_wc__db_wcroot_t *wcroot,
14830                                     const char *local_relpath,
14831                                     svn_boolean_t exact,
14832                                     apr_pool_t *scratch_pool)
14833{
14834  apr_array_header_t *owned_locks;
14835  int lock_level;
14836  int i;
14837
14838  *own_lock = FALSE;
14839  owned_locks = wcroot->owned_locks;
14840  lock_level = relpath_depth(local_relpath);
14841
14842  if (exact)
14843    {
14844      for (i = 0; i < owned_locks->nelts; i++)
14845        {
14846          svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14847                                                     svn_wc__db_wclock_t);
14848
14849          if (strcmp(lock->local_relpath, local_relpath) == 0)
14850            {
14851              *own_lock = TRUE;
14852              return SVN_NO_ERROR;
14853            }
14854        }
14855    }
14856  else
14857    {
14858      for (i = 0; i < owned_locks->nelts; i++)
14859        {
14860          svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14861                                                     svn_wc__db_wclock_t);
14862
14863          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14864              && (lock->levels == -1
14865                  || ((relpath_depth(lock->local_relpath) + lock->levels)
14866                      >= lock_level)))
14867            {
14868              *own_lock = TRUE;
14869              return SVN_NO_ERROR;
14870            }
14871        }
14872    }
14873
14874  return SVN_NO_ERROR;
14875}
14876
14877
14878svn_error_t *
14879svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14880                            svn_wc__db_t *db,
14881                            const char *local_abspath,
14882                            svn_boolean_t exact,
14883                            apr_pool_t *scratch_pool)
14884{
14885  svn_wc__db_wcroot_t *wcroot;
14886  const char *local_relpath;
14887
14888  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14889                              local_abspath, scratch_pool, scratch_pool));
14890
14891  if (!wcroot)
14892    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14893                             _("The node '%s' was not found."),
14894                             svn_dirent_local_style(local_abspath,
14895                                                    scratch_pool));
14896
14897  VERIFY_USABLE_WCROOT(wcroot);
14898
14899  SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath,
14900                                               exact, scratch_pool));
14901
14902  return SVN_NO_ERROR;
14903}
14904
14905/* The body of svn_wc__db_temp_op_end_directory_update().
14906 */
14907static svn_error_t *
14908end_directory_update(svn_wc__db_wcroot_t *wcroot,
14909                     const char *local_relpath,
14910                     apr_pool_t *scratch_pool)
14911{
14912  svn_sqlite__stmt_t *stmt;
14913  svn_wc__db_status_t base_status;
14914
14915  SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14916                                            NULL, NULL, NULL, NULL, NULL,
14917                                            NULL, NULL, NULL, NULL, NULL, NULL,
14918                                            wcroot, local_relpath,
14919                                            scratch_pool, scratch_pool));
14920
14921  if (base_status == svn_wc__db_status_normal)
14922    return SVN_NO_ERROR;
14923
14924  SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14925
14926  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14927                                    STMT_UPDATE_NODE_BASE_PRESENCE));
14928  SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14929                            presence_map, svn_wc__db_status_normal));
14930  SVN_ERR(svn_sqlite__step_done(stmt));
14931
14932  return SVN_NO_ERROR;
14933}
14934
14935svn_error_t *
14936svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14937                                        const char *local_dir_abspath,
14938                                        apr_pool_t *scratch_pool)
14939{
14940  svn_wc__db_wcroot_t *wcroot;
14941  const char *local_relpath;
14942
14943  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14944
14945  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14946                              local_dir_abspath, scratch_pool, scratch_pool));
14947  VERIFY_USABLE_WCROOT(wcroot);
14948
14949  SVN_WC__DB_WITH_TXN(
14950    end_directory_update(wcroot, local_relpath, scratch_pool),
14951    wcroot);
14952
14953  SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14954                        scratch_pool));
14955
14956  return SVN_NO_ERROR;
14957}
14958
14959
14960/* The body of svn_wc__db_temp_op_start_directory_update().
14961 */
14962static svn_error_t *
14963start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14964                           const char *local_relpath,
14965                           const char *new_repos_relpath,
14966                           svn_revnum_t new_rev,
14967                           apr_pool_t *scratch_pool)
14968{
14969  svn_sqlite__stmt_t *stmt;
14970
14971  /* Note: In the majority of calls, the repos_relpath is unchanged. */
14972  /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14973  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14974                    STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14975
14976  SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14977                            wcroot->wc_id,
14978                            local_relpath,
14979                            presence_map, svn_wc__db_status_incomplete,
14980                            new_rev,
14981                            new_repos_relpath));
14982  SVN_ERR(svn_sqlite__step_done(stmt));
14983
14984  return SVN_NO_ERROR;
14985
14986}
14987
14988svn_error_t *
14989svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14990                                          const char *local_abspath,
14991                                          const char *new_repos_relpath,
14992                                          svn_revnum_t new_rev,
14993                                          apr_pool_t *scratch_pool)
14994{
14995  svn_wc__db_wcroot_t *wcroot;
14996  const char *local_relpath;
14997
14998  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14999  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
15000  SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
15001
15002  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15003                              local_abspath, scratch_pool, scratch_pool));
15004  VERIFY_USABLE_WCROOT(wcroot);
15005
15006  SVN_WC__DB_WITH_TXN(
15007    start_directory_update_txn(wcroot, local_relpath,
15008                               new_repos_relpath, new_rev, scratch_pool),
15009    wcroot);
15010
15011  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
15012
15013  return SVN_NO_ERROR;
15014}
15015
15016/* Helper for svn_wc__db_op_make_copy_internal */
15017static svn_error_t *
15018db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
15019                 const char *src1_relpath,
15020                 int src1_op_depth,
15021                 const char *src2_relpath,
15022                 int src2_op_depth,
15023                 const char *dst_relpath,
15024                 apr_pool_t *scratch_pool)
15025{
15026  svn_sqlite__stmt_t *stmt;
15027  int affected_rows;
15028
15029  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15030                                     STMT_UPDATE_MOVED_TO_RELPATH));
15031  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
15032                            src1_relpath, src1_op_depth));
15033  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15034
15035  if (affected_rows == 1)
15036    {
15037      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15038                                     STMT_UPDATE_MOVED_TO_RELPATH));
15039      SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
15040                                src2_relpath, src2_op_depth,
15041                                dst_relpath));
15042      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15043    }
15044  if (affected_rows != 1)
15045    return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
15046
15047  return SVN_NO_ERROR;
15048}
15049
15050static svn_error_t *
15051db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
15052                                const char *local_relpath,
15053                                int new_shadow_layer,
15054                                apr_pool_t *scratch_pool)
15055{
15056  svn_sqlite__stmt_t *stmt;
15057  svn_boolean_t have_row;
15058  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15059
15060  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15061                        STMT_SELECT_MOVED_DESCENDANTS_SRC));
15062  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15063                            new_shadow_layer));
15064  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15065
15066  while (have_row)
15067    {
15068      int del_op_depth;
15069      const char *src_relpath;
15070      const char *dst_relpath;
15071      svn_error_t *err;
15072
15073      svn_pool_clear(iterpool);
15074
15075      del_op_depth = svn_sqlite__column_int(stmt, 0);
15076      src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15077      dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
15078
15079      err = svn_error_trace(
15080               db_move_moved_to(
15081                             wcroot,
15082                             src_relpath, del_op_depth,
15083                             src_relpath, new_shadow_layer,
15084                             dst_relpath, iterpool));
15085
15086      if (err)
15087        return svn_error_compose_create(err, svn_sqlite__reset(stmt));
15088
15089      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15090    }
15091
15092  SVN_ERR(svn_sqlite__reset(stmt));
15093
15094  return SVN_NO_ERROR;
15095}
15096
15097
15098/* The body of svn_wc__db_temp_op_make_copy().  This is
15099   used by the update editor when deleting a base node tree would be a
15100   tree-conflict because there are changes to subtrees.  This function
15101   inserts a copy of the base node tree below any existing working
15102   subtrees.  Given a tree:
15103
15104             0            1           2            3
15105    /     normal          -
15106    A     normal          -
15107    A/B   normal          -         normal
15108    A/B/C normal          -         base-del       normal
15109    A/F   normal          -         normal
15110    A/F/G normal          -         normal
15111    A/F/H normal          -         base-deleted   normal
15112    A/F/E normal          -         not-present
15113    A/X   normal          -
15114    A/X/Y incomplete      -
15115
15116    This function adds layers to A and some of its descendants in an attempt
15117    to make the working copy look like as if it were a copy of the BASE nodes.
15118
15119             0            1              2            3
15120    /     normal        -
15121    A     normal        norm
15122    A/B   normal        norm        norm
15123    A/B/C normal        norm        base-del       normal
15124    A/F   normal        norm        norm
15125    A/F/G normal        norm        norm
15126    A/F/H normal        norm        not-pres
15127    A/F/E normal        norm        base-del
15128    A/X   normal        norm
15129    A/X/Y incomplete  incomplete
15130 */
15131static svn_error_t *
15132make_copy_txn(svn_wc__db_wcroot_t *wcroot,
15133              const char *local_relpath,
15134              apr_int64_t last_repos_id,
15135              const char *last_repos_relpath,
15136              svn_revnum_t last_revision,
15137              int last_op_depth,
15138              svn_boolean_t shadowed,
15139              int root_shadow_depth,
15140              apr_pool_t *scratch_pool)
15141{
15142  svn_sqlite__stmt_t *stmt;
15143  svn_boolean_t have_row = FALSE;
15144  svn_revnum_t revision;
15145  apr_int64_t repos_id;
15146  const char *repos_relpath;
15147  svn_node_kind_t kind;
15148  int op_depth = relpath_depth(local_relpath);
15149
15150  if (last_op_depth != op_depth)
15151    {
15152      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15153                                        STMT_SELECT_DEPTH_NODE));
15154      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15155                                op_depth));
15156      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15157      SVN_ERR(svn_sqlite__reset(stmt));
15158      if (have_row)
15159        shadowed = TRUE;
15160    }
15161
15162  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
15163                                            &repos_relpath, &repos_id, NULL,
15164                                            NULL, NULL, NULL, NULL, NULL, NULL,
15165                                            NULL, NULL, NULL,
15166                                            wcroot, local_relpath,
15167                                            scratch_pool, scratch_pool));
15168
15169  if (last_repos_relpath
15170      && repos_id == last_repos_id
15171      && revision == last_revision)
15172    {
15173      const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
15174                                                   repos_relpath);
15175
15176      if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
15177        op_depth = last_op_depth;
15178    }
15179
15180  /* Can we add a new copy node at the wanted op-depth? */
15181  if (!have_row || op_depth == last_op_depth)
15182    {
15183      int i;
15184
15185      SVN_ERR(svn_sqlite__get_statement(
15186                    &stmt, wcroot->sdb,
15187                    STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
15188      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15189                                op_depth));
15190      SVN_ERR(svn_sqlite__step_done(stmt));
15191
15192      if (shadowed)
15193        SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
15194                                        op_depth, scratch_pool));
15195
15196      if (kind == svn_node_dir)
15197        {
15198          const apr_array_header_t *children;
15199          apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15200
15201          SVN_ERR(gather_children(&children, wcroot, local_relpath,
15202                                  STMT_SELECT_OP_DEPTH_CHILDREN, 0,
15203                                  scratch_pool, iterpool));
15204
15205          for (i = 0; i < children->nelts; i++)
15206            {
15207              const char *name = APR_ARRAY_IDX(children, i, const char *);
15208              const char *copy_relpath;
15209
15210              svn_pool_clear(iterpool);
15211
15212              copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
15213
15214              SVN_ERR(make_copy_txn(wcroot, copy_relpath,
15215                                    repos_id, repos_relpath, revision,
15216                                    op_depth, shadowed, root_shadow_depth,
15217                                    scratch_pool));
15218            }
15219          svn_pool_destroy(iterpool);
15220        }
15221    }
15222  else
15223    {
15224      /* Auch... we can't make a copy of whatever comes deeper, as this
15225         op-depth is already filled by something else. Let's hope
15226         the user doesn't mind.
15227
15228         Luckily we know that the moves are already moved to the shadowing
15229         layer, so we can just remove dangling base-deletes if there are
15230         any.
15231       */
15232      /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
15233      SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15234                                              root_shadow_depth,
15235                                              scratch_pool));
15236
15237      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15238                    STMT_DELETE_WORKING_BASE_DELETE));
15239      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15240                                last_op_depth));
15241      SVN_ERR(svn_sqlite__step_done(stmt));
15242      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15243                    STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
15244      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15245                                last_op_depth));
15246      SVN_ERR(svn_sqlite__step_done(stmt));
15247    }
15248
15249  /* Insert a not-present node to mark that we don't know what exists here.
15250
15251     We do this last (after recursing), to allow the move fix-up code to
15252     see the original moves. */
15253  if (last_op_depth > 0 && last_op_depth != op_depth)
15254    {
15255      insert_working_baton_t iwb;
15256
15257      blank_iwb(&iwb);
15258      iwb.presence = svn_wc__db_status_not_present;
15259      iwb.op_depth = last_op_depth;
15260
15261      iwb.original_repos_id = repos_id;
15262      iwb.original_repos_relpath = repos_relpath;
15263      iwb.original_revnum = revision;
15264      iwb.kind = kind;
15265
15266      SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
15267    }
15268
15269  return SVN_NO_ERROR;
15270}
15271
15272
15273svn_error_t *
15274svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
15275                                 const char *local_relpath,
15276                                 svn_boolean_t move_move_info,
15277                                 const svn_skel_t *conflicts,
15278                                 const svn_skel_t *work_items,
15279                                 apr_pool_t *scratch_pool)
15280{
15281  svn_sqlite__stmt_t *stmt;
15282  svn_boolean_t have_row;
15283  int op_depth = -1;
15284
15285  /* The update editor is supposed to call this function when there is
15286     no working node for LOCAL_ABSPATH. */
15287  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15288                                    STMT_SELECT_WORKING_NODE));
15289  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15290  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15291  if (have_row)
15292    op_depth = svn_sqlite__column_int(stmt, 0);
15293  SVN_ERR(svn_sqlite__reset(stmt));
15294
15295  if (have_row)
15296    {
15297      if (op_depth == relpath_depth(local_relpath))
15298        return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
15299                             _("Modification of '%s' already exists"),
15300                             path_for_error_message(wcroot,
15301                                                    local_relpath,
15302                                                    scratch_pool));
15303
15304      /* We have a working layer, but not one at the op-depth of local-relpath,
15305         so we can create a copy by just copying the lower layer */
15306
15307      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15308                                        STMT_COPY_OP_DEPTH_RECURSIVE));
15309      SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
15310                                op_depth, relpath_depth(local_relpath)));
15311      SVN_ERR(svn_sqlite__step_done(stmt));
15312    }
15313  else
15314    {
15315      int affected_rows;
15316
15317      op_depth = relpath_depth(local_relpath);
15318      /* We don't allow copies to contain server-excluded nodes;
15319         the update editor is going to have to bail out. */
15320      SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
15321                                            scratch_pool));
15322
15323      /* Insert a shadowing layer */
15324      SVN_ERR(svn_sqlite__get_statement(
15325                        &stmt, wcroot->sdb,
15326                        STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
15327
15328      /* As we are keeping whatever is below, move the*/
15329
15330      SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
15331                                wcroot->wc_id, local_relpath,
15332                                0, op_depth));
15333      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15334      SVN_ERR_ASSERT(affected_rows > 0);
15335
15336      if (!move_move_info)
15337        SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15338                                                op_depth, scratch_pool));
15339
15340
15341      SVN_ERR(make_copy_txn(wcroot, local_relpath,
15342                            INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
15343                            op_depth, FALSE, op_depth,
15344                            scratch_pool));
15345    }
15346
15347  if (conflicts)
15348    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
15349                                              conflicts, scratch_pool));
15350
15351  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
15352
15353  return SVN_NO_ERROR;
15354}
15355
15356
15357svn_error_t *
15358svn_wc__db_op_make_copy(svn_wc__db_t *db,
15359                        const char *local_abspath,
15360                        const svn_skel_t *conflicts,
15361                        const svn_skel_t *work_items,
15362                        apr_pool_t *scratch_pool)
15363{
15364  svn_wc__db_wcroot_t *wcroot;
15365  const char *local_relpath;
15366
15367  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15368
15369  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15370                              local_abspath, scratch_pool, scratch_pool));
15371  VERIFY_USABLE_WCROOT(wcroot);
15372
15373  SVN_WC__DB_WITH_TXN(
15374    svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE,
15375                                     conflicts, work_items,
15376                                     scratch_pool),
15377    wcroot);
15378
15379  SVN_ERR(flush_entries(wcroot, local_abspath,
15380                        svn_depth_infinity, scratch_pool));
15381
15382  return SVN_NO_ERROR;
15383}
15384
15385svn_error_t *
15386svn_wc__db_info_below_working(svn_boolean_t *have_base,
15387                              svn_boolean_t *have_work,
15388                              svn_wc__db_status_t *status,
15389                              svn_wc__db_t *db,
15390                              const char *local_abspath,
15391                              apr_pool_t *scratch_pool)
15392{
15393  svn_wc__db_wcroot_t *wcroot;
15394  const char *local_relpath;
15395
15396  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15397
15398  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15399                              local_abspath, scratch_pool, scratch_pool));
15400  VERIFY_USABLE_WCROOT(wcroot);
15401  SVN_ERR(info_below_working(have_base, have_work, status,
15402                             wcroot, local_relpath, -1, scratch_pool));
15403
15404  return SVN_NO_ERROR;
15405}
15406
15407svn_error_t *
15408svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
15409                                       svn_wc__db_t *db,
15410                                       const char *local_abspath,
15411                                       apr_pool_t *result_pool,
15412                                       apr_pool_t *scratch_pool)
15413{
15414  svn_wc__db_wcroot_t *wcroot;
15415  const char *local_relpath;
15416  svn_sqlite__stmt_t *stmt;
15417  svn_boolean_t have_row;
15418
15419  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15420
15421  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15422                              local_abspath, scratch_pool, scratch_pool));
15423  VERIFY_USABLE_WCROOT(wcroot);
15424
15425  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15426                                    STMT_SELECT_NOT_PRESENT_DESCENDANTS));
15427
15428  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
15429                            wcroot->wc_id,
15430                            local_relpath,
15431                            relpath_depth(local_relpath)));
15432
15433  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15434
15435  if (have_row)
15436    {
15437      apr_array_header_t *paths;
15438
15439      paths = apr_array_make(result_pool, 4, sizeof(const char*));
15440      while (have_row)
15441        {
15442          const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
15443
15444          APR_ARRAY_PUSH(paths, const char *)
15445              = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
15446                                           local_relpath, found_relpath));
15447
15448          SVN_ERR(svn_sqlite__step(&have_row, stmt));
15449        }
15450
15451      *descendants = paths;
15452    }
15453  else
15454    *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
15455
15456  return svn_error_trace(svn_sqlite__reset(stmt));
15457}
15458
15459
15460/* Like svn_wc__db_min_max_revisions(),
15461 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15462static svn_error_t *
15463get_min_max_revisions(svn_revnum_t *min_revision,
15464                      svn_revnum_t *max_revision,
15465                      svn_wc__db_wcroot_t *wcroot,
15466                      const char *local_relpath,
15467                      svn_boolean_t committed,
15468                      apr_pool_t *scratch_pool)
15469{
15470  svn_sqlite__stmt_t *stmt;
15471  svn_revnum_t min_rev, max_rev;
15472
15473  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15474                                    STMT_SELECT_MIN_MAX_REVISIONS));
15475  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15476  SVN_ERR(svn_sqlite__step_row(stmt));
15477
15478  if (committed)
15479    {
15480      min_rev = svn_sqlite__column_revnum(stmt, 2);
15481      max_rev = svn_sqlite__column_revnum(stmt, 3);
15482    }
15483  else
15484    {
15485      min_rev = svn_sqlite__column_revnum(stmt, 0);
15486      max_rev = svn_sqlite__column_revnum(stmt, 1);
15487    }
15488
15489  /* The statement returns exactly one row. */
15490  SVN_ERR(svn_sqlite__reset(stmt));
15491
15492  if (min_revision)
15493    *min_revision = min_rev;
15494  if (max_revision)
15495    *max_revision = max_rev;
15496
15497  return SVN_NO_ERROR;
15498}
15499
15500
15501svn_error_t *
15502svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
15503                             svn_revnum_t *max_revision,
15504                             svn_wc__db_t *db,
15505                             const char *local_abspath,
15506                             svn_boolean_t committed,
15507                             apr_pool_t *scratch_pool)
15508{
15509  svn_wc__db_wcroot_t *wcroot;
15510  const char *local_relpath;
15511
15512  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15513
15514  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15515                                                db, local_abspath,
15516                                                scratch_pool, scratch_pool));
15517  VERIFY_USABLE_WCROOT(wcroot);
15518
15519  return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
15520                                               wcroot, local_relpath,
15521                                               committed, scratch_pool));
15522}
15523
15524
15525/* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
15526 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
15527static svn_error_t *
15528is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
15529                            svn_wc__db_wcroot_t *wcroot,
15530                            const char *local_relpath,
15531                            apr_pool_t *scratch_pool)
15532{
15533  svn_sqlite__stmt_t *stmt;
15534  svn_boolean_t have_row;
15535
15536  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15537                                    STMT_HAS_SPARSE_NODES));
15538  SVN_ERR(svn_sqlite__bindf(stmt, "is",
15539                            wcroot->wc_id,
15540                            local_relpath));
15541  /* If this query returns a row, the working copy is sparse. */
15542  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15543  *is_sparse_checkout = have_row;
15544  SVN_ERR(svn_sqlite__reset(stmt));
15545
15546  return SVN_NO_ERROR;
15547}
15548
15549
15550/* Like svn_wc__db_has_switched_subtrees(),
15551 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15552static svn_error_t *
15553has_switched_subtrees(svn_boolean_t *is_switched,
15554                      svn_wc__db_wcroot_t *wcroot,
15555                      const char *local_relpath,
15556                      const char *trail_url,
15557                      apr_pool_t *scratch_pool)
15558{
15559  svn_sqlite__stmt_t *stmt;
15560  svn_boolean_t have_row;
15561  apr_int64_t repos_id;
15562  const char *repos_relpath;
15563
15564  /* Optional argument handling for caller */
15565  if (!is_switched)
15566    return SVN_NO_ERROR;
15567
15568  *is_switched = FALSE;
15569
15570  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15571                                            &repos_relpath, &repos_id,
15572                                            NULL, NULL, NULL, NULL, NULL,
15573                                            NULL, NULL, NULL, NULL, NULL,
15574                                            wcroot, local_relpath,
15575                                            scratch_pool, scratch_pool));
15576
15577  /* First do the cheap check where we only need info on the origin itself */
15578  if (trail_url != NULL)
15579    {
15580      const char *repos_root_url;
15581      const char *url;
15582      apr_size_t len1, len2;
15583
15584      /* If the trailing part of the URL of the working copy directory
15585         does not match the given trailing URL then the whole working
15586         copy is switched. */
15587
15588      SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
15589                                          repos_id, scratch_pool));
15590      url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15591                                        scratch_pool);
15592
15593      len1 = strlen(trail_url);
15594      len2 = strlen(url);
15595      if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15596        {
15597          *is_switched = TRUE;
15598          return SVN_NO_ERROR;
15599        }
15600    }
15601
15602  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15603  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15604  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15605  if (have_row)
15606    *is_switched = TRUE;
15607  SVN_ERR(svn_sqlite__reset(stmt));
15608
15609  return SVN_NO_ERROR;
15610}
15611
15612
15613svn_error_t *
15614svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15615                                 svn_wc__db_t *db,
15616                                 const char *local_abspath,
15617                                 const char *trail_url,
15618                                 apr_pool_t *scratch_pool)
15619{
15620  svn_wc__db_wcroot_t *wcroot;
15621  const char *local_relpath;
15622
15623  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15624
15625  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15626                                                db, local_abspath,
15627                                                scratch_pool, scratch_pool));
15628  VERIFY_USABLE_WCROOT(wcroot);
15629
15630  return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15631                                               local_relpath, trail_url,
15632                                               scratch_pool));
15633}
15634
15635svn_error_t *
15636svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15637                                 svn_wc__db_t *db,
15638                                 const char *local_abspath,
15639                                 apr_pool_t *result_pool,
15640                                 apr_pool_t *scratch_pool)
15641{
15642  svn_wc__db_wcroot_t *wcroot;
15643  const char *local_relpath;
15644  svn_sqlite__stmt_t *stmt;
15645  svn_boolean_t have_row;
15646
15647  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15648  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15649                                                db, local_abspath,
15650                                                scratch_pool, scratch_pool));
15651  VERIFY_USABLE_WCROOT(wcroot);
15652
15653  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15654                                    STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15655  SVN_ERR(svn_sqlite__bindf(stmt, "is",
15656                            wcroot->wc_id,
15657                            local_relpath));
15658  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15659
15660  if (have_row)
15661    *excluded_subtrees = apr_hash_make(result_pool);
15662  else
15663    *excluded_subtrees = NULL;
15664
15665  while (have_row)
15666    {
15667      const char *abs_path =
15668        svn_dirent_join(wcroot->abspath,
15669                        svn_sqlite__column_text(stmt, 0, NULL),
15670                        result_pool);
15671      svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15672      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15673    }
15674
15675  SVN_ERR(svn_sqlite__reset(stmt));
15676  return SVN_NO_ERROR;
15677}
15678
15679/* Like svn_wc__db_has_db_mods(),
15680 * but accepts a WCROOT/LOCAL_RELPATH pair.
15681 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15682static svn_error_t *
15683has_db_mods(svn_boolean_t *is_modified,
15684            svn_wc__db_wcroot_t *wcroot,
15685            const char *local_relpath,
15686            apr_pool_t *scratch_pool)
15687{
15688  svn_sqlite__stmt_t *stmt;
15689
15690  /* Check for additions or deletions. */
15691  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15692                                    STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15693  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15694  /* If this query returns a row, the working copy is modified. */
15695  SVN_ERR(svn_sqlite__step(is_modified, stmt));
15696  SVN_ERR(svn_sqlite__reset(stmt));
15697
15698  if (! *is_modified)
15699    {
15700      /* Check for property modifications. */
15701      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15702                                        STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15703      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15704      /* If this query returns a row, the working copy is modified. */
15705      SVN_ERR(svn_sqlite__step(is_modified, stmt));
15706      SVN_ERR(svn_sqlite__reset(stmt));
15707    }
15708
15709  return SVN_NO_ERROR;
15710}
15711
15712
15713svn_error_t *
15714svn_wc__db_has_db_mods(svn_boolean_t *is_modified,
15715                       svn_wc__db_t *db,
15716                       const char *local_abspath,
15717                       apr_pool_t *scratch_pool)
15718{
15719  svn_wc__db_wcroot_t *wcroot;
15720  const char *local_relpath;
15721
15722  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15723
15724  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15725                                                db, local_abspath,
15726                                                scratch_pool, scratch_pool));
15727  VERIFY_USABLE_WCROOT(wcroot);
15728
15729  return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath,
15730                                     scratch_pool));
15731}
15732
15733
15734/* The body of svn_wc__db_revision_status().
15735 */
15736static svn_error_t *
15737revision_status_txn(svn_revnum_t *min_revision,
15738                    svn_revnum_t *max_revision,
15739                    svn_boolean_t *is_sparse_checkout,
15740                    svn_boolean_t *is_modified,
15741                    svn_boolean_t *is_switched,
15742                    svn_wc__db_wcroot_t *wcroot,
15743                    const char *local_relpath,
15744                    svn_wc__db_t *db,
15745                    const char *trail_url,
15746                    svn_boolean_t committed,
15747                    apr_pool_t *scratch_pool)
15748{
15749  svn_error_t *err;
15750  svn_boolean_t exists;
15751
15752  SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15753
15754  if (!exists)
15755    {
15756      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15757                               _("The node '%s' was not found."),
15758                               path_for_error_message(wcroot, local_relpath,
15759                                                      scratch_pool));
15760    }
15761
15762  /* Determine mixed-revisionness. */
15763  SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15764                                local_relpath, committed, scratch_pool));
15765
15766  /* Determine sparseness. */
15767  SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15768                                      local_relpath, scratch_pool));
15769
15770  /* Check for switched nodes. */
15771  {
15772    err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15773                                trail_url, scratch_pool);
15774
15775    if (err)
15776      {
15777        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15778          return svn_error_trace(err);
15779
15780        svn_error_clear(err); /* No Base node, but no fatal error */
15781        *is_switched = FALSE;
15782      }
15783  }
15784
15785  /* Check for db mods. */
15786  SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool));
15787
15788  return SVN_NO_ERROR;
15789}
15790
15791
15792svn_error_t *
15793svn_wc__db_revision_status(svn_revnum_t *min_revision,
15794                           svn_revnum_t *max_revision,
15795                           svn_boolean_t *is_sparse_checkout,
15796                           svn_boolean_t *is_modified,
15797                           svn_boolean_t *is_switched,
15798                           svn_wc__db_t *db,
15799                           const char *local_abspath,
15800                           const char *trail_url,
15801                           svn_boolean_t committed,
15802                           apr_pool_t *scratch_pool)
15803{
15804  svn_wc__db_wcroot_t *wcroot;
15805  const char *local_relpath;
15806
15807  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15808
15809  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15810                                                db, local_abspath,
15811                                                scratch_pool, scratch_pool));
15812  VERIFY_USABLE_WCROOT(wcroot);
15813
15814  SVN_WC__DB_WITH_TXN(
15815    revision_status_txn(min_revision, max_revision,
15816                        is_sparse_checkout, is_modified, is_switched,
15817                        wcroot, local_relpath, db,
15818                        trail_url, committed,
15819                        scratch_pool),
15820    wcroot);
15821  return SVN_NO_ERROR;
15822}
15823
15824
15825svn_error_t *
15826svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15827                                          svn_wc__db_t *db,
15828                                          const char *local_abspath,
15829                                          apr_pool_t *result_pool,
15830                                          apr_pool_t *scratch_pool)
15831{
15832  svn_wc__db_wcroot_t *wcroot;
15833  const char *local_relpath;
15834  svn_sqlite__stmt_t *stmt;
15835  svn_boolean_t have_row;
15836  apr_int64_t last_repos_id = INVALID_REPOS_ID;
15837  const char *last_repos_root_url = NULL;
15838
15839  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15840
15841  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15842                                                db, local_abspath,
15843                                                scratch_pool, scratch_pool));
15844  VERIFY_USABLE_WCROOT(wcroot);
15845
15846  *lock_tokens = apr_hash_make(result_pool);
15847
15848  /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15849  SVN_ERR(svn_sqlite__get_statement(
15850              &stmt, wcroot->sdb,
15851              STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15852
15853  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15854  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15855  while (have_row)
15856    {
15857      apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15858      const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15859      const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15860
15861      if (child_repos_id != last_repos_id)
15862        {
15863          svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15864                                                         NULL, wcroot,
15865                                                         child_repos_id,
15866                                                         scratch_pool);
15867
15868          if (err)
15869            {
15870              return svn_error_trace(
15871                            svn_error_compose_create(err,
15872                                                     svn_sqlite__reset(stmt)));
15873            }
15874
15875          last_repos_id = child_repos_id;
15876        }
15877
15878      SVN_ERR_ASSERT(last_repos_root_url != NULL);
15879      svn_hash_sets(*lock_tokens,
15880                    svn_path_url_add_component2(last_repos_root_url,
15881                                                child_relpath, result_pool),
15882                    lock_token);
15883
15884      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15885    }
15886  return svn_sqlite__reset(stmt);
15887}
15888
15889
15890/* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15891 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15892#define VERIFY(expression) \
15893  do { \
15894    if (! (expression)) \
15895      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15896        _("database inconsistency at local_relpath='%s' verifying " \
15897          "expression '%s'"), local_relpath, #expression); \
15898  } while (0)
15899
15900
15901/* Verify consistency of the metadata concerning WCROOT.  This is intended
15902 * for use only during testing and debugging, so is not intended to be
15903 * blazingly fast.
15904 *
15905 * This code is a complement to any verification that we can do in SQLite
15906 * triggers.  See, for example, 'wc-checks.sql'.
15907 *
15908 * Some more verification steps we might want to add are:
15909 *
15910 *   * on every ACTUAL row (except root): a NODES row exists at its parent path
15911 *   * the op-depth root must always exist and every intermediate too
15912 */
15913static svn_error_t *
15914verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15915              apr_pool_t *scratch_pool)
15916{
15917  svn_sqlite__stmt_t *stmt;
15918  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15919
15920  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15921                                    STMT_SELECT_ALL_NODES));
15922  SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15923  while (TRUE)
15924    {
15925      svn_boolean_t have_row;
15926      const char *local_relpath, *parent_relpath;
15927      int op_depth;
15928
15929      svn_pool_clear(iterpool);
15930
15931      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15932      if (!have_row)
15933        break;
15934
15935      op_depth = svn_sqlite__column_int(stmt, 0);
15936      local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15937      parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15938
15939      /* Verify parent_relpath is the parent path of local_relpath */
15940      VERIFY((parent_relpath == NULL)
15941             ? (local_relpath[0] == '\0')
15942             : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15943                       parent_relpath) == 0));
15944
15945      /* Verify op_depth <= the tree depth of local_relpath */
15946      VERIFY(op_depth <= relpath_depth(local_relpath));
15947
15948      /* Verify parent_relpath refers to a row that exists */
15949      /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15950       * the child's and a suitable presence */
15951      if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15952        {
15953          svn_sqlite__stmt_t *stmt2;
15954          svn_boolean_t have_a_parent_row;
15955
15956          SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15957                                            STMT_SELECT_NODE_INFO));
15958          SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15959                                    parent_relpath));
15960          SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15961          VERIFY(have_a_parent_row);
15962          SVN_ERR(svn_sqlite__reset(stmt2));
15963        }
15964    }
15965  svn_pool_destroy(iterpool);
15966
15967  return svn_error_trace(svn_sqlite__reset(stmt));
15968}
15969
15970svn_error_t *
15971svn_wc__db_verify(svn_wc__db_t *db,
15972                  const char *wri_abspath,
15973                  apr_pool_t *scratch_pool)
15974{
15975  svn_wc__db_wcroot_t *wcroot;
15976  const char *local_relpath;
15977
15978  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15979                                                db, wri_abspath,
15980                                                scratch_pool, scratch_pool));
15981  VERIFY_USABLE_WCROOT(wcroot);
15982
15983  SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15984  return SVN_NO_ERROR;
15985}
15986
15987
15988svn_error_t *
15989svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot,
15990                                   svn_wc__db_verify_cb_t callback,
15991                                   void *baton,
15992                                   apr_pool_t *scratch_pool)
15993{
15994  svn_sqlite__stmt_t *stmt;
15995  svn_boolean_t have_row;
15996  svn_error_t *err = NULL;
15997  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15998
15999  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY));
16000  SVN_ERR(svn_sqlite__step(&have_row, stmt));
16001
16002  while (have_row)
16003    {
16004      const char *local_relpath;
16005      int op_depth = svn_sqlite__column_int(stmt, 1);
16006      int id = svn_sqlite__column_int(stmt, 2);
16007      const char *msg;
16008
16009      svn_pool_clear(iterpool);
16010
16011      local_relpath =  svn_sqlite__column_text(stmt, 0, iterpool);
16012      msg = svn_sqlite__column_text(stmt, 3, scratch_pool);
16013
16014      err = callback(baton, wcroot->abspath, local_relpath, op_depth,
16015                     id, msg, iterpool);
16016
16017      if (err)
16018        break;
16019
16020      SVN_ERR(svn_sqlite__step(&have_row, stmt));
16021    }
16022
16023  svn_pool_destroy(iterpool);
16024
16025  return svn_error_trace(
16026            svn_error_compose_create(err, svn_sqlite__reset(stmt)));
16027}
16028
16029svn_error_t *
16030svn_wc__db_verify_db_full(svn_wc__db_t *db,
16031                          const char *wri_abspath,
16032                          svn_wc__db_verify_cb_t callback,
16033                          void *baton,
16034                          apr_pool_t *scratch_pool)
16035{
16036  svn_wc__db_wcroot_t *wcroot;
16037  const char *local_relpath;
16038
16039  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16040
16041  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16042                              wri_abspath, scratch_pool, scratch_pool));
16043  VERIFY_USABLE_WCROOT(wcroot);
16044
16045  return svn_error_trace(
16046            svn_wc__db_verify_db_full_internal(wcroot, callback, baton,
16047                                               scratch_pool));
16048}
16049
16050svn_error_t *
16051svn_wc__db_bump_format(int *result_format,
16052                       svn_boolean_t *bumped_format,
16053                       svn_wc__db_t *db,
16054                       const char *wcroot_abspath,
16055                       apr_pool_t *scratch_pool)
16056{
16057  svn_sqlite__db_t *sdb;
16058  svn_error_t *err;
16059  int format;
16060
16061  if (bumped_format)
16062    *bumped_format = FALSE;
16063
16064  /* Do not scan upwards for a working copy root here to prevent accidental
16065   * upgrades of any working copies the WCROOT might be nested in.
16066   * Just try to open a DB at the specified path instead. */
16067  err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
16068                                svn_sqlite__mode_readwrite,
16069                                TRUE, /* exclusive */
16070                                0, /* default timeout */
16071                                NULL, /* my statements */
16072                                scratch_pool, scratch_pool);
16073  if (err)
16074    {
16075      svn_error_t *err2;
16076      apr_hash_t *entries;
16077
16078      /* Could not open an sdb. Check for an entries file instead. */
16079      err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
16080                                      scratch_pool, scratch_pool);
16081      if (err2 || apr_hash_count(entries) == 0)
16082        return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
16083                  svn_error_compose_create(err, err2),
16084                  _("Can't upgrade '%s' as it is not a working copy root"),
16085                  svn_dirent_local_style(wcroot_abspath, scratch_pool));
16086
16087      /* An entries file was found. This is a pre-wc-ng working copy
16088       * so suggest an upgrade. */
16089      return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
16090                _("Working copy '%s' is too old and must be upgraded to "
16091                  "at least format %d, as created by Subversion %s"),
16092                svn_dirent_local_style(wcroot_abspath, scratch_pool),
16093                SVN_WC__WC_NG_VERSION,
16094                svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
16095    }
16096
16097  SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
16098  err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
16099                            sdb, format, scratch_pool);
16100
16101  if (err == SVN_NO_ERROR && bumped_format)
16102    *bumped_format = (*result_format > format);
16103
16104  /* Make sure we return a different error than expected for upgrades from
16105     entries */
16106  if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
16107    err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
16108                           _("Working copy upgrade failed"));
16109
16110  err = svn_error_compose_create(err, svn_sqlite__close(sdb));
16111
16112  return svn_error_trace(err);
16113}
16114
16115svn_error_t *
16116svn_wc__db_vacuum(svn_wc__db_t *db,
16117                  const char *local_abspath,
16118                  apr_pool_t *scratch_pool)
16119{
16120  svn_wc__db_wcroot_t *wcroot;
16121  const char *local_relpath;
16122
16123  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
16124                                                db, local_abspath,
16125                                                scratch_pool, scratch_pool));
16126  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
16127
16128  return SVN_NO_ERROR;
16129}
16130
16131/* Item queued with svn_wc__db_commit_queue_add */
16132typedef struct commit_queue_item_t
16133{
16134  const char *local_relpath;
16135  svn_boolean_t recurse; /* Use legacy recursion */
16136  svn_boolean_t committed; /* Process the node as committed */
16137  svn_boolean_t remove_lock; /* Remove existing lock on node */
16138  svn_boolean_t remove_changelist; /* Remove changelist on node */
16139
16140  /* The pristine text checksum. NULL if the old value should be kept
16141     and for directories */
16142  const svn_checksum_t *new_sha1_checksum;
16143
16144  apr_hash_t *new_dav_cache; /* New DAV cache for the node */
16145} commit_queue_item_t;
16146
16147/* The queue definition for vn_wc__db_create_commit_queue,
16148   svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */
16149struct svn_wc__db_commit_queue_t
16150{
16151  svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */
16152  apr_array_header_t *items; /* List of commit_queue_item_t* */
16153  svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */
16154};
16155
16156/* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the
16157   working copy specified with WRI_ABSPATH */
16158svn_error_t *
16159svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue,
16160                               svn_wc__db_t *db,
16161                               const char *wri_abspath,
16162                               apr_pool_t *result_pool,
16163                               apr_pool_t *scratch_pool)
16164{
16165  svn_wc__db_wcroot_t *wcroot;
16166  const char *local_relpath;
16167  svn_wc__db_commit_queue_t *q;
16168
16169  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16170
16171  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16172                              wri_abspath, result_pool, scratch_pool));
16173  VERIFY_USABLE_WCROOT(wcroot);
16174
16175  q = apr_pcalloc(result_pool, sizeof(*q));
16176
16177  SVN_ERR_ASSERT(wcroot->sdb);
16178
16179  q->wcroot = wcroot;
16180  q->items = apr_array_make(result_pool, 64,
16181                            sizeof(commit_queue_item_t*));
16182  q->have_recurse = FALSE;
16183
16184  *queue = q;
16185  return SVN_NO_ERROR;
16186}
16187
16188svn_error_t *
16189svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue,
16190                            const char *local_abspath,
16191                            svn_boolean_t recurse,
16192                            svn_boolean_t is_commited,
16193                            svn_boolean_t remove_lock,
16194                            svn_boolean_t remove_changelist,
16195                            const svn_checksum_t *new_sha1_checksum,
16196                            apr_hash_t *new_dav_cache,
16197                            apr_pool_t *result_pool,
16198                            apr_pool_t *scratch_pool)
16199{
16200  commit_queue_item_t *cqi;
16201  const char *local_relpath;
16202
16203  local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath,
16204                                           local_abspath);
16205
16206  if (! local_relpath)
16207    return svn_error_createf(
16208                SVN_ERR_WC_PATH_NOT_FOUND, NULL,
16209                _("The path '%s' is not in the working copy '%s'"),
16210                svn_dirent_local_style(local_abspath, scratch_pool),
16211                svn_dirent_local_style(queue->wcroot->abspath, scratch_pool));
16212
16213  cqi = apr_pcalloc(result_pool, sizeof(*cqi));
16214  cqi->local_relpath = local_relpath;
16215  cqi->recurse = recurse;
16216  cqi->committed = is_commited;
16217  cqi->remove_lock = remove_lock;
16218  cqi->remove_changelist = remove_changelist;
16219  cqi->new_sha1_checksum = new_sha1_checksum;
16220  cqi->new_dav_cache = new_dav_cache;
16221
16222  queue->have_recurse |= recurse;
16223
16224  APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi;
16225  return SVN_NO_ERROR;
16226}
16227
16228/*** Finishing updates and commits. ***/
16229
16230/* Post process an item that is committed in the repository. Collapse layers into
16231 * BASE. Queue work items that will finish a commit of the file or directory
16232 * LOCAL_ABSPATH in DB:
16233 */
16234static svn_error_t *
16235process_committed_leaf(svn_wc__db_t *db,
16236                       svn_wc__db_wcroot_t *wcroot,
16237                       const char *local_relpath,
16238                       svn_boolean_t via_recurse,
16239                       svn_wc__db_status_t status,
16240                       svn_node_kind_t kind,
16241                       svn_boolean_t prop_mods,
16242                       const svn_checksum_t *old_checksum,
16243                       svn_revnum_t new_revnum,
16244                       apr_time_t new_changed_date,
16245                       const char *new_changed_author,
16246                       apr_hash_t *new_dav_cache,
16247                       svn_boolean_t remove_lock,
16248                       svn_boolean_t remove_changelist,
16249                       const svn_checksum_t *checksum,
16250                       apr_pool_t *scratch_pool)
16251{
16252  svn_revnum_t new_changed_rev = new_revnum;
16253  svn_skel_t *work_item = NULL;
16254
16255  {
16256    const char *lock_relpath;
16257    svn_boolean_t locked;
16258
16259    if (kind == svn_node_dir)
16260      lock_relpath = local_relpath;
16261    else
16262      lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
16263
16264    SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
16265                                                 lock_relpath, FALSE,
16266                                                 scratch_pool));
16267
16268    if (!locked)
16269      return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
16270                             _("No write-lock in '%s'"),
16271                             path_for_error_message(wcroot, local_relpath,
16272                                                    scratch_pool));
16273
16274    SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty,
16275                          scratch_pool));
16276  }
16277
16278  if (status == svn_wc__db_status_not_present)
16279    {
16280      /* We are committing the leaf of a copy operation.
16281         We leave the not-present marker to allow pulling in excluded
16282         children of a copy.
16283
16284         The next update will remove the not-present marker. */
16285
16286      return SVN_NO_ERROR;
16287    }
16288
16289  SVN_ERR_ASSERT(status == svn_wc__db_status_normal
16290                 || status == svn_wc__db_status_incomplete
16291                 || status == svn_wc__db_status_added
16292                 || status == svn_wc__db_status_deleted);
16293
16294  if (kind != svn_node_dir
16295      && status != svn_wc__db_status_deleted)
16296    {
16297      /* If we sent a delta (meaning: post-copy modification),
16298         then this file will appear in the queue and so we should have
16299         its checksum already. */
16300      if (checksum == NULL)
16301        {
16302          /* It was copied and not modified. We must have a text
16303             base for it. And the node should have a checksum. */
16304          SVN_ERR_ASSERT(old_checksum != NULL);
16305
16306          checksum = old_checksum;
16307
16308          /* Is the node completely unmodified and are we recursing? */
16309          if (via_recurse && !prop_mods)
16310            {
16311              /* If a copied node itself is not modified, but the op_root of
16312                 the copy is committed we have to make sure that changed_rev,
16313                 changed_date and changed_author don't change or the working
16314                 copy used for committing will show different last modified
16315                 information then a clean checkout of exactly the same
16316                 revisions. (Issue #3676) */
16317
16318              SVN_ERR(svn_wc__db_read_info_internal(
16319                                           NULL, NULL, NULL, NULL, NULL,
16320                                           &new_changed_rev,
16321                                           &new_changed_date,
16322                                           &new_changed_author, NULL, NULL,
16323                                           NULL, NULL, NULL, NULL, NULL,
16324                                           NULL, NULL, NULL, NULL,
16325                                           NULL, NULL, NULL, NULL,
16326                                           NULL, NULL,
16327                                           wcroot, local_relpath,
16328                                           scratch_pool, scratch_pool));
16329            }
16330        }
16331
16332      SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
16333                                           db, svn_dirent_join(wcroot->abspath,
16334                                                               local_relpath,
16335                                                               scratch_pool),
16336                                           prop_mods,
16337                                           scratch_pool, scratch_pool));
16338    }
16339
16340  /* The new text base will be found in the pristine store by its checksum. */
16341  SVN_ERR(commit_node(wcroot, local_relpath,
16342                      new_revnum, new_changed_rev,
16343                      new_changed_date, new_changed_author,
16344                      checksum,
16345                      new_dav_cache,
16346                      !remove_changelist,
16347                      !remove_lock,
16348                      work_item,
16349                      scratch_pool));
16350
16351  return SVN_NO_ERROR;
16352}
16353
16354/** Internal helper for svn_wc_process_committed_queue2().
16355 * Bump a commit item, collapsing local changes with the new repository
16356 * information to a new BASE node.
16357 *
16358 * @a new_date is the (server-side) date of the new revision, or 0.
16359 *
16360 * @a rev_author is the (server-side) author of the new
16361 * revision; it may be @c NULL.
16362 *
16363 * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH.
16364 *
16365 * If @a remove_lock is set, release any user locks on @a
16366 * local_abspath; otherwise keep them during processing.
16367 *
16368 * If @a remove_changelist is set, clear any changeset assignments
16369 * from @a local_abspath; otherwise, keep such assignments.
16370 *
16371 * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine
16372 * text.
16373 *
16374 * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
16375 * recursive commit operation. (Part of the legacy recurse handling)
16376 */
16377static svn_error_t *
16378process_committed_internal(svn_wc__db_t *db,
16379                           svn_wc__db_wcroot_t *wcroot,
16380                           const char *local_relpath,
16381                           svn_boolean_t recurse,
16382                           svn_boolean_t top_of_recurse,
16383                           svn_revnum_t new_revnum,
16384                           apr_time_t new_date,
16385                           const char *rev_author,
16386                           apr_hash_t *new_dav_cache,
16387                           svn_boolean_t remove_lock,
16388                           svn_boolean_t remove_changelist,
16389                           const svn_checksum_t *new_sha1_checksum,
16390                           apr_hash_t *items_by_relpath,
16391                           apr_pool_t *scratch_pool)
16392{
16393  svn_wc__db_status_t status;
16394  svn_node_kind_t kind;
16395  const svn_checksum_t *old_checksum;
16396  svn_boolean_t prop_mods;
16397
16398  SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL,
16399                                        NULL, NULL, &old_checksum, NULL, NULL,
16400                                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
16401                                        NULL, &prop_mods, NULL, NULL, NULL,
16402                                        wcroot, local_relpath,
16403                                        scratch_pool, scratch_pool));
16404
16405  /* NOTE: be wary of making crazy semantic changes in this function, since
16406     svn_wc_process_committed4() calls this.  */
16407
16408  SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse,
16409                                 status, kind, prop_mods, old_checksum,
16410                                 new_revnum, new_date, rev_author,
16411                                 new_dav_cache,
16412                                 remove_lock, remove_changelist,
16413                                 new_sha1_checksum,
16414                                 scratch_pool));
16415
16416  /* Only check for recursion on nodes that have children */
16417  if (kind != svn_node_dir
16418      || status == svn_wc__db_status_not_present
16419      || status == svn_wc__db_status_excluded
16420      || status == svn_wc__db_status_server_excluded
16421      /* Node deleted -> then no longer a directory */
16422      || status == svn_wc__db_status_deleted)
16423    {
16424      return SVN_NO_ERROR;
16425    }
16426
16427  if (recurse)
16428    {
16429      const apr_array_header_t *children;
16430      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16431      int i;
16432
16433      /* Read PATH's entries;  this is the absolute path. */
16434      SVN_ERR(gather_children(&children, wcroot, local_relpath,
16435                              STMT_SELECT_NODE_CHILDREN, -1,
16436                              scratch_pool, iterpool));
16437
16438      /* Recursively loop over all children. */
16439      for (i = 0; i < children->nelts; i++)
16440        {
16441          const char *name = APR_ARRAY_IDX(children, i, const char *);
16442          const char *this_relpath;
16443          const commit_queue_item_t *cqi;
16444
16445          svn_pool_clear(iterpool);
16446
16447          this_relpath = svn_dirent_join(local_relpath, name, iterpool);
16448
16449          new_sha1_checksum = NULL;
16450          cqi = svn_hash_gets(items_by_relpath, this_relpath);
16451
16452          if (cqi != NULL)
16453            new_sha1_checksum = cqi->new_sha1_checksum;
16454
16455          /* Recurse.  Pass NULL for NEW_DAV_CACHE, because the
16456             ones present in the current call are only applicable to
16457             this one committed item. */
16458          SVN_ERR(process_committed_internal(
16459                    db, wcroot, this_relpath,
16460                    TRUE /* recurse */,
16461                    FALSE /* top_of_recurse */,
16462                    new_revnum, new_date,
16463                    rev_author,
16464                    NULL /* new_dav_cache */,
16465                    FALSE /* remove_lock */,
16466                    remove_changelist,
16467                    new_sha1_checksum,
16468                    items_by_relpath,
16469                    iterpool));
16470        }
16471
16472      svn_pool_destroy(iterpool);
16473    }
16474
16475  return SVN_NO_ERROR;
16476}
16477
16478/* Return TRUE if any item of QUEUE is a parent of ITEM and will be
16479   processed recursively, return FALSE otherwise.
16480
16481   The algorithmic complexity of this search implementation is O(queue
16482   length), but it's quite quick.
16483*/
16484static svn_boolean_t
16485have_recursive_parent(const apr_array_header_t *all_items,
16486                      const commit_queue_item_t *item,
16487                      apr_pool_t *scratch_pool)
16488{
16489  const char *local_relpath = item->local_relpath;
16490  int i;
16491
16492  for (i = 0; i < all_items->nelts; i++)
16493    {
16494      const commit_queue_item_t *qi
16495        = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *);
16496
16497      if (qi == item)
16498        continue;
16499
16500      if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath,
16501                                                   local_relpath))
16502        {
16503          return TRUE;
16504        }
16505    }
16506
16507  return FALSE;
16508}
16509
16510/* Compare function for svn_sort__array */
16511static int
16512compare_queue_items(const void *v1,
16513                    const void *v2)
16514{
16515  const commit_queue_item_t *cqi1
16516              = *(const commit_queue_item_t **)v1;
16517  const commit_queue_item_t *cqi2
16518              = *(const commit_queue_item_t **)v2;
16519
16520  return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath);
16521}
16522
16523/* Internal, locked version of svn_wc__db_process_commit_queue */
16524static svn_error_t *
16525db_process_commit_queue(svn_wc__db_t *db,
16526                        svn_wc__db_commit_queue_t *queue,
16527                        svn_revnum_t new_revnum,
16528                        apr_time_t new_date,
16529                        const char *new_author,
16530                        apr_pool_t *scratch_pool)
16531{
16532  apr_hash_t *items_by_relpath = NULL;
16533  int j;
16534  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16535
16536  svn_sort__array(queue->items, compare_queue_items);
16537
16538  if (queue->have_recurse)
16539    {
16540      items_by_relpath = apr_hash_make(scratch_pool);
16541
16542      for (j = 0; j < queue->items->nelts; j++)
16543        {
16544          commit_queue_item_t *cqi
16545            = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16546
16547          svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi);
16548        }
16549    }
16550
16551  for (j = 0; j < queue->items->nelts; j++)
16552    {
16553      commit_queue_item_t *cqi
16554        = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16555
16556      svn_pool_clear(iterpool);
16557
16558      /* Skip this item if it is a child of a recursive item, because it has
16559         been (or will be) accounted for when that recursive item was (or
16560         will be) processed. */
16561      if (queue->have_recurse && have_recursive_parent(queue->items, cqi,
16562                                                       iterpool))
16563        continue;
16564
16565      if (!cqi->committed)
16566        {
16567          if (cqi->remove_lock)
16568            {
16569              svn_skel_t *work_item;
16570
16571              SVN_ERR(svn_wc__wq_build_sync_file_flags(
16572                                                    &work_item,
16573                                                    db,
16574                                                    svn_dirent_join(
16575                                                        queue->wcroot->abspath,
16576                                                        cqi->local_relpath,
16577                                                        iterpool),
16578                                                    iterpool, iterpool));
16579
16580              lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item,
16581                              iterpool);
16582            }
16583          if (cqi->remove_changelist)
16584            SVN_ERR(svn_wc__db_op_set_changelist(db,
16585                                                 svn_dirent_join(
16586                                                        queue->wcroot->abspath,
16587                                                        cqi->local_relpath,
16588                                                        iterpool),
16589                                                 NULL, NULL,
16590                                                 svn_depth_empty,
16591                                                 NULL, NULL, /* notify */
16592                                                 NULL, NULL, /* cancel */
16593                                                 iterpool));
16594        }
16595      else
16596        {
16597          SVN_ERR(process_committed_internal(
16598                                  db, queue->wcroot, cqi->local_relpath,
16599                                  cqi->recurse,
16600                                  TRUE /* top_of_recurse */,
16601                                  new_revnum, new_date, new_author,
16602                                  cqi->new_dav_cache,
16603                                  cqi->remove_lock,
16604                                  cqi->remove_changelist,
16605                                  cqi->new_sha1_checksum,
16606                                  items_by_relpath,
16607                                  iterpool));
16608        }
16609    }
16610
16611  svn_pool_destroy(iterpool);
16612
16613  return SVN_NO_ERROR;
16614}
16615
16616svn_error_t *
16617svn_wc__db_process_commit_queue(svn_wc__db_t *db,
16618                                svn_wc__db_commit_queue_t *queue,
16619                                svn_revnum_t new_revnum,
16620                                apr_time_t new_date,
16621                                const char *new_author,
16622                                apr_pool_t *scratch_pool)
16623{
16624  SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue,
16625                                              new_revnum, new_date,
16626                                              new_author, scratch_pool),
16627                        queue->wcroot);
16628
16629  return SVN_NO_ERROR;
16630}
16631