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