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