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