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