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