1251881Speter/*
2251881Speter * wc_db.c :  manipulating the administrative database
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#define SVN_WC__I_AM_WC_DB
25251881Speter
26251881Speter#include <assert.h>
27251881Speter#include <apr_pools.h>
28251881Speter#include <apr_hash.h>
29251881Speter
30251881Speter#include "svn_types.h"
31251881Speter#include "svn_error.h"
32251881Speter#include "svn_dirent_uri.h"
33251881Speter#include "svn_path.h"
34251881Speter#include "svn_hash.h"
35251881Speter#include "svn_sorts.h"
36251881Speter#include "svn_wc.h"
37251881Speter#include "svn_checksum.h"
38251881Speter#include "svn_pools.h"
39251881Speter
40251881Speter#include "wc.h"
41251881Speter#include "wc_db.h"
42251881Speter#include "adm_files.h"
43251881Speter#include "wc-queries.h"
44251881Speter#include "entries.h"
45251881Speter#include "lock.h"
46251881Speter#include "conflicts.h"
47251881Speter#include "wc_db_private.h"
48251881Speter#include "workqueue.h"
49251881Speter#include "token-map.h"
50251881Speter
51251881Speter#include "svn_private_config.h"
52251881Speter#include "private/svn_sqlite.h"
53251881Speter#include "private/svn_skel.h"
54251881Speter#include "private/svn_wc_private.h"
55251881Speter#include "private/svn_token.h"
56251881Speter
57251881Speter
58251881Speter#define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
59251881Speter
60251881Speter
61251881Speter/*
62251881Speter * Some filename constants.
63251881Speter */
64251881Speter#define SDB_FILE  "wc.db"
65251881Speter
66251881Speter#define WCROOT_TEMPDIR_RELPATH   "tmp"
67251881Speter
68251881Speter
69251881Speter/*
70251881Speter * PARAMETER ASSERTIONS
71251881Speter *
72251881Speter * Every (semi-)public entrypoint in this file has a set of assertions on
73251881Speter * the parameters passed into the function. Since this is a brand new API,
74251881Speter * we want to make sure that everybody calls it properly. The original WC
75251881Speter * code had years to catch stray bugs, but we do not have that luxury in
76251881Speter * the wc-nb rewrite. Any extra assurances that we can find will be
77251881Speter * welcome. The asserts will ensure we have no doubt about the values
78251881Speter * passed into the function.
79251881Speter *
80251881Speter * Some parameters are *not* specifically asserted. Typically, these are
81251881Speter * params that will be used immediately, so something like a NULL value
82251881Speter * will be obvious.
83251881Speter *
84251881Speter * ### near 1.7 release, it would be a Good Thing to review the assertions
85251881Speter * ### and decide if any can be removed or switched to assert() in order
86251881Speter * ### to remove their runtime cost in the production release.
87251881Speter *
88251881Speter *
89251881Speter * DATABASE OPERATIONS
90251881Speter *
91251881Speter * Each function should leave the database in a consistent state. If it
92251881Speter * does *not*, then the implication is some other function needs to be
93251881Speter * called to restore consistency. Subtle requirements like that are hard
94251881Speter * to maintain over a long period of time, so this API will not allow it.
95251881Speter *
96251881Speter *
97251881Speter * STANDARD VARIABLE NAMES
98251881Speter *
99251881Speter * db     working copy database (this module)
100251881Speter * sdb    SQLite database (not to be confused with 'db')
101251881Speter * wc_id  a WCROOT id associated with a node
102251881Speter */
103251881Speter
104251881Speter#define INVALID_REPOS_ID ((apr_int64_t) -1)
105251881Speter#define UNKNOWN_WC_ID ((apr_int64_t) -1)
106251881Speter#define FORMAT_FROM_SDB (-1)
107251881Speter
108251881Speter/* Check if column number I, a property-skel column, contains a non-empty
109251881Speter   set of properties. The empty set of properties is stored as "()", so we
110251881Speter   have properties if the size of the column is larger than 2. */
111251881Speter#define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
112251881Speter                 (svn_sqlite__column_bytes(stmt, i) > 2)
113251881Speter
114251881Speterint
115251881Spetersvn_wc__db_op_depth_for_upgrade(const char *local_relpath)
116251881Speter{
117251881Speter  return relpath_depth(local_relpath);
118251881Speter}
119251881Speter
120251881Speter
121251881Speter/* Representation of a new base row for the NODES table */
122251881Spetertypedef struct insert_base_baton_t {
123251881Speter  /* common to all insertions into BASE */
124251881Speter  svn_wc__db_status_t status;
125251881Speter  svn_node_kind_t kind;
126251881Speter  apr_int64_t repos_id;
127251881Speter  const char *repos_relpath;
128251881Speter  svn_revnum_t revision;
129251881Speter
130251881Speter  /* Only used when repos_id == INVALID_REPOS_ID */
131251881Speter  const char *repos_root_url;
132251881Speter  const char *repos_uuid;
133251881Speter
134251881Speter  /* common to all "normal" presence insertions */
135251881Speter  const apr_hash_t *props;
136251881Speter  svn_revnum_t changed_rev;
137251881Speter  apr_time_t changed_date;
138251881Speter  const char *changed_author;
139251881Speter  const apr_hash_t *dav_cache;
140251881Speter
141251881Speter  /* for inserting directories */
142251881Speter  const apr_array_header_t *children;
143251881Speter  svn_depth_t depth;
144251881Speter
145251881Speter  /* for inserting files */
146251881Speter  const svn_checksum_t *checksum;
147251881Speter
148251881Speter  /* for inserting symlinks */
149251881Speter  const char *target;
150251881Speter
151251881Speter  svn_boolean_t file_external;
152251881Speter
153251881Speter  /* may need to insert/update ACTUAL to record a conflict  */
154251881Speter  const svn_skel_t *conflict;
155251881Speter
156251881Speter  /* may need to insert/update ACTUAL to record new properties */
157251881Speter  svn_boolean_t update_actual_props;
158251881Speter  const apr_hash_t *new_actual_props;
159251881Speter
160251881Speter  /* A depth-first ordered array of svn_prop_inherited_item_t *
161251881Speter     structures representing the properties inherited by the base
162251881Speter     node. */
163251881Speter  apr_array_header_t *iprops;
164251881Speter
165251881Speter  /* maybe we should copy information from a previous record? */
166251881Speter  svn_boolean_t keep_recorded_info;
167251881Speter
168251881Speter  /* insert a base-deleted working node as well as a base node */
169251881Speter  svn_boolean_t insert_base_deleted;
170251881Speter
171251881Speter  /* delete the current working nodes above BASE */
172251881Speter  svn_boolean_t delete_working;
173251881Speter
174251881Speter  /* may have work items to queue in this transaction  */
175251881Speter  const svn_skel_t *work_items;
176251881Speter
177251881Speter} insert_base_baton_t;
178251881Speter
179251881Speter
180251881Speter/* Representation of a new working row for the NODES table */
181251881Spetertypedef struct insert_working_baton_t {
182251881Speter  /* common to all insertions into WORKING (including NODE_DATA) */
183251881Speter  svn_wc__db_status_t presence;
184251881Speter  svn_node_kind_t kind;
185251881Speter  int op_depth;
186251881Speter
187251881Speter  /* common to all "normal" presence insertions */
188251881Speter  const apr_hash_t *props;
189251881Speter  svn_revnum_t changed_rev;
190251881Speter  apr_time_t changed_date;
191251881Speter  const char *changed_author;
192251881Speter  apr_int64_t original_repos_id;
193251881Speter  const char *original_repos_relpath;
194251881Speter  svn_revnum_t original_revnum;
195251881Speter  svn_boolean_t moved_here;
196251881Speter
197251881Speter  /* for inserting directories */
198251881Speter  const apr_array_header_t *children;
199251881Speter  svn_depth_t depth;
200251881Speter
201251881Speter  /* for inserting (copied/moved-here) files */
202251881Speter  const svn_checksum_t *checksum;
203251881Speter
204251881Speter  /* for inserting symlinks */
205251881Speter  const char *target;
206251881Speter
207251881Speter  svn_boolean_t update_actual_props;
208251881Speter  const apr_hash_t *new_actual_props;
209251881Speter
210251881Speter  /* may have work items to queue in this transaction  */
211251881Speter  const svn_skel_t *work_items;
212251881Speter
213251881Speter  /* may have conflict to install in this transaction */
214251881Speter  const svn_skel_t *conflict;
215251881Speter
216251881Speter  /* If the value is > 0 and < op_depth, also insert a not-present
217251881Speter     at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
218251881Speter  int not_present_op_depth;
219251881Speter
220251881Speter} insert_working_baton_t;
221251881Speter
222251881Speter/* Representation of a new row for the EXTERNALS table */
223251881Spetertypedef struct insert_external_baton_t {
224251881Speter  /* common to all insertions into EXTERNALS */
225251881Speter  svn_node_kind_t kind;
226251881Speter  svn_wc__db_status_t presence;
227251881Speter
228251881Speter  /* The repository of the external */
229251881Speter  apr_int64_t repos_id;
230251881Speter  /* for file and symlink externals */
231251881Speter  const char *repos_relpath;
232251881Speter  svn_revnum_t revision;
233251881Speter
234251881Speter  /* Only used when repos_id == INVALID_REPOS_ID */
235251881Speter  const char *repos_root_url;
236251881Speter  const char *repos_uuid;
237251881Speter
238251881Speter  /* for file and symlink externals */
239251881Speter  const apr_hash_t *props;
240251881Speter  apr_array_header_t *iprops;
241251881Speter  svn_revnum_t changed_rev;
242251881Speter  apr_time_t changed_date;
243251881Speter  const char *changed_author;
244251881Speter  const apr_hash_t *dav_cache;
245251881Speter
246251881Speter  /* for inserting files */
247251881Speter  const svn_checksum_t *checksum;
248251881Speter
249251881Speter  /* for inserting symlinks */
250251881Speter  const char *target;
251251881Speter
252251881Speter  const char *record_ancestor_relpath;
253251881Speter  const char *recorded_repos_relpath;
254251881Speter  svn_revnum_t recorded_peg_revision;
255251881Speter  svn_revnum_t recorded_revision;
256251881Speter
257251881Speter  /* may need to insert/update ACTUAL to record a conflict  */
258251881Speter  const svn_skel_t *conflict;
259251881Speter
260251881Speter  /* may need to insert/update ACTUAL to record new properties */
261251881Speter  svn_boolean_t update_actual_props;
262251881Speter  const apr_hash_t *new_actual_props;
263251881Speter
264251881Speter  /* maybe we should copy information from a previous record? */
265251881Speter  svn_boolean_t keep_recorded_info;
266251881Speter
267251881Speter  /* may have work items to queue in this transaction  */
268251881Speter  const svn_skel_t *work_items;
269251881Speter
270251881Speter} insert_external_baton_t;
271251881Speter
272251881Speter
273251881Speter/* Forward declarations  */
274251881Speterstatic svn_error_t *
275251881Speteradd_work_items(svn_sqlite__db_t *sdb,
276251881Speter               const svn_skel_t *skel,
277251881Speter               apr_pool_t *scratch_pool);
278251881Speter
279251881Speterstatic svn_error_t *
280251881Speterset_actual_props(apr_int64_t wc_id,
281251881Speter                 const char *local_relpath,
282251881Speter                 apr_hash_t *props,
283251881Speter                 svn_sqlite__db_t *db,
284251881Speter                 apr_pool_t *scratch_pool);
285251881Speter
286251881Speterstatic svn_error_t *
287251881Speterinsert_incomplete_children(svn_sqlite__db_t *sdb,
288251881Speter                           apr_int64_t wc_id,
289251881Speter                           const char *local_relpath,
290251881Speter                           apr_int64_t repos_id,
291251881Speter                           const char *repos_relpath,
292251881Speter                           svn_revnum_t revision,
293251881Speter                           const apr_array_header_t *children,
294251881Speter                           int op_depth,
295251881Speter                           apr_pool_t *scratch_pool);
296251881Speter
297251881Speterstatic svn_error_t *
298251881Speterdb_read_pristine_props(apr_hash_t **props,
299251881Speter                       svn_wc__db_wcroot_t *wcroot,
300251881Speter                       const char *local_relpath,
301251881Speter                       svn_boolean_t deleted_ok,
302251881Speter                       apr_pool_t *result_pool,
303251881Speter                       apr_pool_t *scratch_pool);
304251881Speter
305251881Speterstatic svn_error_t *
306251881Speterread_info(svn_wc__db_status_t *status,
307251881Speter          svn_node_kind_t *kind,
308251881Speter          svn_revnum_t *revision,
309251881Speter          const char **repos_relpath,
310251881Speter          apr_int64_t *repos_id,
311251881Speter          svn_revnum_t *changed_rev,
312251881Speter          apr_time_t *changed_date,
313251881Speter          const char **changed_author,
314251881Speter          svn_depth_t *depth,
315251881Speter          const svn_checksum_t **checksum,
316251881Speter          const char **target,
317251881Speter          const char **original_repos_relpath,
318251881Speter          apr_int64_t *original_repos_id,
319251881Speter          svn_revnum_t *original_revision,
320251881Speter          svn_wc__db_lock_t **lock,
321251881Speter          svn_filesize_t *recorded_size,
322251881Speter          apr_time_t *recorded_time,
323251881Speter          const char **changelist,
324251881Speter          svn_boolean_t *conflicted,
325251881Speter          svn_boolean_t *op_root,
326251881Speter          svn_boolean_t *had_props,
327251881Speter          svn_boolean_t *props_mod,
328251881Speter          svn_boolean_t *have_base,
329251881Speter          svn_boolean_t *have_more_work,
330251881Speter          svn_boolean_t *have_work,
331251881Speter          svn_wc__db_wcroot_t *wcroot,
332251881Speter          const char *local_relpath,
333251881Speter          apr_pool_t *result_pool,
334251881Speter          apr_pool_t *scratch_pool);
335251881Speter
336251881Speterstatic svn_error_t *
337251881Speterscan_addition(svn_wc__db_status_t *status,
338251881Speter              const char **op_root_relpath,
339251881Speter              const char **repos_relpath,
340251881Speter              apr_int64_t *repos_id,
341251881Speter              const char **original_repos_relpath,
342251881Speter              apr_int64_t *original_repos_id,
343251881Speter              svn_revnum_t *original_revision,
344251881Speter              const char **moved_from_relpath,
345251881Speter              const char **moved_from_op_root_relpath,
346251881Speter              int *moved_from_op_depth,
347251881Speter              svn_wc__db_wcroot_t *wcroot,
348251881Speter              const char *local_relpath,
349251881Speter              apr_pool_t *result_pool,
350251881Speter              apr_pool_t *scratch_pool);
351251881Speter
352251881Speterstatic svn_error_t *
353251881Speterconvert_to_working_status(svn_wc__db_status_t *working_status,
354251881Speter                          svn_wc__db_status_t status);
355251881Speter
356251881Speterstatic svn_error_t *
357251881Speterwclock_owns_lock(svn_boolean_t *own_lock,
358251881Speter                 svn_wc__db_wcroot_t *wcroot,
359251881Speter                 const char *local_relpath,
360251881Speter                 svn_boolean_t exact,
361251881Speter                 apr_pool_t *scratch_pool);
362251881Speter
363251881Speterstatic svn_error_t *
364251881Speterdb_is_switched(svn_boolean_t *is_switched,
365251881Speter               svn_node_kind_t *kind,
366251881Speter               svn_wc__db_wcroot_t *wcroot,
367251881Speter               const char *local_relpath,
368251881Speter               apr_pool_t *scratch_pool);
369251881Speter
370251881Speter
371251881Speter/* Return the absolute path, in local path style, of LOCAL_RELPATH
372251881Speter   in WCROOT.  */
373251881Speterstatic const char *
374251881Speterpath_for_error_message(const svn_wc__db_wcroot_t *wcroot,
375251881Speter                       const char *local_relpath,
376251881Speter                       apr_pool_t *result_pool)
377251881Speter{
378251881Speter  const char *local_abspath
379251881Speter    = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
380251881Speter
381251881Speter  return svn_dirent_local_style(local_abspath, result_pool);
382251881Speter}
383251881Speter
384251881Speter
385251881Speter/* Return a file size from column SLOT of the SQLITE statement STMT, or
386251881Speter   SVN_INVALID_FILESIZE if the column value is NULL.  */
387251881Speterstatic svn_filesize_t
388251881Speterget_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
389251881Speter{
390251881Speter  if (svn_sqlite__column_is_null(stmt, slot))
391251881Speter    return SVN_INVALID_FILESIZE;
392251881Speter  return svn_sqlite__column_int64(stmt, slot);
393251881Speter}
394251881Speter
395251881Speter
396251881Speter/* Return a lock info structure constructed from the given columns of the
397251881Speter   SQLITE statement STMT, or return NULL if the token column value is null.  */
398251881Speterstatic svn_wc__db_lock_t *
399251881Speterlock_from_columns(svn_sqlite__stmt_t *stmt,
400251881Speter                  int col_token,
401251881Speter                  int col_owner,
402251881Speter                  int col_comment,
403251881Speter                  int col_date,
404251881Speter                  apr_pool_t *result_pool)
405251881Speter{
406251881Speter  svn_wc__db_lock_t *lock;
407251881Speter
408251881Speter  if (svn_sqlite__column_is_null(stmt, col_token))
409251881Speter    {
410251881Speter      lock = NULL;
411251881Speter    }
412251881Speter  else
413251881Speter    {
414251881Speter      lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
415251881Speter      lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
416251881Speter      lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
417251881Speter      lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
418251881Speter      lock->date = svn_sqlite__column_int64(stmt, col_date);
419251881Speter    }
420251881Speter  return lock;
421251881Speter}
422251881Speter
423251881Speter
424251881Spetersvn_error_t *
425251881Spetersvn_wc__db_fetch_repos_info(const char **repos_root_url,
426251881Speter                            const char **repos_uuid,
427251881Speter                            svn_sqlite__db_t *sdb,
428251881Speter                            apr_int64_t repos_id,
429251881Speter                            apr_pool_t *result_pool)
430251881Speter{
431251881Speter  svn_sqlite__stmt_t *stmt;
432251881Speter  svn_boolean_t have_row;
433251881Speter
434251881Speter  if (!repos_root_url && !repos_uuid)
435251881Speter    return SVN_NO_ERROR;
436251881Speter
437251881Speter  if (repos_id == INVALID_REPOS_ID)
438251881Speter    {
439251881Speter      if (repos_root_url)
440251881Speter        *repos_root_url = NULL;
441251881Speter      if (repos_uuid)
442251881Speter        *repos_uuid = NULL;
443251881Speter      return SVN_NO_ERROR;
444251881Speter    }
445251881Speter
446251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
447251881Speter                                    STMT_SELECT_REPOSITORY_BY_ID));
448251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
449251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
450251881Speter  if (!have_row)
451251881Speter    return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
452251881Speter                             _("No REPOSITORY table entry for id '%ld'"),
453251881Speter                             (long int)repos_id);
454251881Speter
455251881Speter  if (repos_root_url)
456251881Speter    *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
457251881Speter  if (repos_uuid)
458251881Speter    *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
459251881Speter
460251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
461251881Speter}
462251881Speter
463251881Speter/* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
464251881Speter   SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
465251881Speter   column value is null.  Any of the output parameters may be NULL if not
466251881Speter   required.  */
467251881Speterstatic void
468251881Speterrepos_location_from_columns(apr_int64_t *repos_id,
469251881Speter                            svn_revnum_t *revision,
470251881Speter                            const char **repos_relpath,
471251881Speter                            svn_sqlite__stmt_t *stmt,
472251881Speter                            int col_repos_id,
473251881Speter                            int col_revision,
474251881Speter                            int col_repos_relpath,
475251881Speter                            apr_pool_t *result_pool)
476251881Speter{
477251881Speter  if (repos_id)
478251881Speter    {
479251881Speter      /* Fetch repository information via REPOS_ID. */
480251881Speter      if (svn_sqlite__column_is_null(stmt, col_repos_id))
481251881Speter        *repos_id = INVALID_REPOS_ID;
482251881Speter      else
483251881Speter        *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
484251881Speter    }
485251881Speter  if (revision)
486251881Speter    {
487251881Speter      *revision = svn_sqlite__column_revnum(stmt, col_revision);
488251881Speter    }
489251881Speter  if (repos_relpath)
490251881Speter    {
491251881Speter      *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
492251881Speter                                               result_pool);
493251881Speter    }
494251881Speter}
495251881Speter
496251881Speter
497251881Speter/* Get the statement given by STMT_IDX, and bind the appropriate wc_id and
498251881Speter   local_relpath based upon LOCAL_ABSPATH.  Store it in *STMT, and use
499251881Speter   SCRATCH_POOL for temporary allocations.
500251881Speter
501251881Speter   Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */
502251881Speterstatic svn_error_t *
503251881Speterget_statement_for_path(svn_sqlite__stmt_t **stmt,
504251881Speter                       svn_wc__db_t *db,
505251881Speter                       const char *local_abspath,
506251881Speter                       int stmt_idx,
507251881Speter                       apr_pool_t *scratch_pool)
508251881Speter{
509251881Speter  svn_wc__db_wcroot_t *wcroot;
510251881Speter  const char *local_relpath;
511251881Speter
512251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
513251881Speter
514251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
515251881Speter                              local_abspath, scratch_pool, scratch_pool));
516251881Speter  VERIFY_USABLE_WCROOT(wcroot);
517251881Speter
518251881Speter  SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx));
519251881Speter  SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath));
520251881Speter
521251881Speter  return SVN_NO_ERROR;
522251881Speter}
523251881Speter
524251881Speter
525251881Speter/* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
526251881Speter   value. If one does not exist, then create a new one. */
527251881Speterstatic svn_error_t *
528251881Spetercreate_repos_id(apr_int64_t *repos_id,
529251881Speter                const char *repos_root_url,
530251881Speter                const char *repos_uuid,
531251881Speter                svn_sqlite__db_t *sdb,
532251881Speter                apr_pool_t *scratch_pool)
533251881Speter{
534251881Speter  svn_sqlite__stmt_t *get_stmt;
535251881Speter  svn_sqlite__stmt_t *insert_stmt;
536251881Speter  svn_boolean_t have_row;
537251881Speter
538251881Speter  SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
539251881Speter  SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
540251881Speter  SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
541251881Speter
542251881Speter  if (have_row)
543251881Speter    {
544251881Speter      *repos_id = svn_sqlite__column_int64(get_stmt, 0);
545251881Speter      return svn_error_trace(svn_sqlite__reset(get_stmt));
546251881Speter    }
547251881Speter  SVN_ERR(svn_sqlite__reset(get_stmt));
548251881Speter
549251881Speter  /* NOTE: strictly speaking, there is a race condition between the
550251881Speter     above query and the insertion below. We're simply going to ignore
551251881Speter     that, as it means two processes are *modifying* the working copy
552251881Speter     at the same time, *and* new repositores are becoming visible.
553251881Speter     This is rare enough, let alone the miniscule chance of hitting
554251881Speter     this race condition. Further, simply failing out will leave the
555251881Speter     database in a consistent state, and the user can just re-run the
556251881Speter     failed operation. */
557251881Speter
558251881Speter  SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
559251881Speter                                    STMT_INSERT_REPOSITORY));
560251881Speter  SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
561251881Speter  return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
562251881Speter}
563251881Speter
564251881Speter
565251881Speter/* Initialize the baton with appropriate "blank" values. This allows the
566251881Speter   insertion function to leave certain columns null.  */
567251881Speterstatic void
568251881Speterblank_ibb(insert_base_baton_t *pibb)
569251881Speter{
570251881Speter  memset(pibb, 0, sizeof(*pibb));
571251881Speter  pibb->revision = SVN_INVALID_REVNUM;
572251881Speter  pibb->changed_rev = SVN_INVALID_REVNUM;
573251881Speter  pibb->depth = svn_depth_infinity;
574251881Speter  pibb->repos_id = INVALID_REPOS_ID;
575251881Speter}
576251881Speter
577251881Speter
578251881Spetersvn_error_t *
579251881Spetersvn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
580251881Speter                                const char *local_relpath,
581251881Speter                                svn_node_kind_t kind,
582251881Speter                                int op_depth,
583251881Speter                                apr_pool_t *scratch_pool)
584251881Speter{
585251881Speter  svn_boolean_t have_row;
586251881Speter  svn_sqlite__stmt_t *stmt;
587251881Speter  int parent_op_depth;
588251881Speter  const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
589251881Speter
590251881Speter  SVN_ERR_ASSERT(local_relpath[0]);
591251881Speter
592251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
593251881Speter                                    STMT_SELECT_LOWEST_WORKING_NODE));
594251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
595251881Speter                            op_depth));
596251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
597251881Speter  if (have_row)
598251881Speter    parent_op_depth = svn_sqlite__column_int(stmt, 0);
599251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
600251881Speter  if (have_row)
601251881Speter    {
602251881Speter      int existing_op_depth;
603251881Speter
604251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
605251881Speter                                op_depth));
606251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
607251881Speter      if (have_row)
608251881Speter        existing_op_depth = svn_sqlite__column_int(stmt, 0);
609251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
610251881Speter      if (!have_row || parent_op_depth < existing_op_depth)
611251881Speter        {
612251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
613251881Speter                              STMT_INSTALL_WORKING_NODE_FOR_DELETE));
614251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
615251881Speter                                    local_relpath, parent_op_depth,
616251881Speter                                    parent_relpath, kind_map, kind));
617251881Speter          SVN_ERR(svn_sqlite__update(NULL, stmt));
618251881Speter        }
619251881Speter    }
620251881Speter
621251881Speter  return SVN_NO_ERROR;
622251881Speter}
623251881Speter
624251881Speter
625251881Speter/* This is the reverse of svn_wc__db_extend_parent_delete.
626251881Speter
627251881Speter   When removing a node if the parent has a higher working node then
628251881Speter   the parent node and this node are both deleted or replaced and any
629251881Speter   delete over this node must be removed.
630269847Speter
631269847Speter   This function (like most wcroot functions) assumes that its caller
632269847Speter   only uses this function within an sqlite transaction if atomic
633269847Speter   behavior is needed.
634251881Speter */
635251881Spetersvn_error_t *
636251881Spetersvn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
637251881Speter                                 const char *local_relpath,
638251881Speter                                 int op_depth,
639251881Speter                                 apr_pool_t *scratch_pool)
640251881Speter{
641251881Speter  svn_sqlite__stmt_t *stmt;
642269847Speter  svn_boolean_t have_row;
643269847Speter  int working_depth;
644269847Speter  svn_wc__db_status_t presence;
645269847Speter  const char *moved_to;
646251881Speter
647251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
648269847Speter                                    STMT_SELECT_LOWEST_WORKING_NODE));
649251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
650251881Speter                            op_depth));
651269847Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
652251881Speter
653269847Speter  if (!have_row)
654269847Speter    return svn_error_trace(svn_sqlite__reset(stmt));
655269847Speter
656269847Speter  working_depth = svn_sqlite__column_int(stmt, 0);
657269847Speter  presence = svn_sqlite__column_token(stmt, 1, presence_map);
658269847Speter  moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
659269847Speter
660269847Speter  SVN_ERR(svn_sqlite__reset(stmt));
661269847Speter
662269847Speter  if (moved_to)
663269847Speter    {
664269847Speter      /* Turn the move into a copy to keep the NODES table valid */
665269847Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
666269847Speter                                        STMT_CLEAR_MOVED_HERE_RECURSIVE));
667269847Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
668269847Speter                                moved_to, relpath_depth(moved_to)));
669269847Speter      SVN_ERR(svn_sqlite__step_done(stmt));
670269847Speter
671269847Speter      /* This leaves just the moved_to information on the origin,
672269847Speter         which we will remove in the next step */
673269847Speter    }
674269847Speter
675269847Speter  if (presence == svn_wc__db_status_base_deleted)
676269847Speter    {
677269847Speter      /* Nothing left to shadow; remove the base-deleted node */
678269847Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
679269847Speter    }
680269847Speter  else if (moved_to)
681269847Speter    {
682269847Speter      /* Clear moved to information, as this node is no longer base-deleted */
683269847Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
684269847Speter                                        STMT_CLEAR_MOVED_TO_RELPATH));
685269847Speter      }
686269847Speter  else
687269847Speter    {
688269847Speter      /* Nothing to update */
689269847Speter      return SVN_NO_ERROR;
690269847Speter    }
691269847Speter
692269847Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
693269847Speter                            working_depth));
694269847Speter
695269847Speter  return svn_error_trace(svn_sqlite__update(NULL, stmt));
696251881Speter}
697251881Speter
698251881Speter
699251881Speter
700251881Speter/* Insert the base row represented by (insert_base_baton_t *) BATON. */
701251881Speterstatic svn_error_t *
702251881Speterinsert_base_node(const insert_base_baton_t *pibb,
703251881Speter                 svn_wc__db_wcroot_t *wcroot,
704251881Speter                 const char *local_relpath,
705251881Speter                 apr_pool_t *scratch_pool)
706251881Speter{
707251881Speter  apr_int64_t repos_id = pibb->repos_id;
708251881Speter  svn_sqlite__stmt_t *stmt;
709251881Speter  svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
710251881Speter  apr_int64_t recorded_time;
711251881Speter
712251881Speter  /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
713251881Speter     bind the appropriate parent_relpath. */
714251881Speter  const char *parent_relpath =
715251881Speter    (*local_relpath == '\0') ? NULL
716251881Speter    : svn_relpath_dirname(local_relpath, scratch_pool);
717251881Speter
718251881Speter  if (pibb->repos_id == INVALID_REPOS_ID)
719251881Speter    SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
720251881Speter                            wcroot->sdb, scratch_pool));
721251881Speter
722251881Speter  SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
723251881Speter  SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
724251881Speter
725251881Speter  if (pibb->keep_recorded_info)
726251881Speter    {
727251881Speter      svn_boolean_t have_row;
728251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
729251881Speter                                        STMT_SELECT_BASE_NODE));
730251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
731251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
732251881Speter      if (have_row)
733251881Speter        {
734251881Speter          /* Preserve size and modification time if caller asked us to. */
735251881Speter          recorded_size = get_recorded_size(stmt, 6);
736251881Speter          recorded_time = svn_sqlite__column_int64(stmt, 12);
737251881Speter        }
738251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
739251881Speter    }
740251881Speter
741251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
742251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
743251881Speter                            "tstr"               /* 8 - 11 */
744251881Speter                            "isnnnnns",          /* 12 - 19 */
745251881Speter                            wcroot->wc_id,       /* 1 */
746251881Speter                            local_relpath,       /* 2 */
747251881Speter                            0,              /* op_depth is 0 for base */
748251881Speter                            parent_relpath,      /* 4 */
749251881Speter                            repos_id,
750251881Speter                            pibb->repos_relpath,
751251881Speter                            pibb->revision,
752251881Speter                            presence_map, pibb->status, /* 8 */
753251881Speter                            (pibb->kind == svn_node_dir) ? /* 9 */
754251881Speter                             svn_token__to_word(depth_map, pibb->depth) : NULL,
755251881Speter                            kind_map, pibb->kind, /* 10 */
756251881Speter                            pibb->changed_rev,    /* 11 */
757251881Speter                            pibb->changed_date,   /* 12 */
758251881Speter                            pibb->changed_author, /* 13 */
759251881Speter                            (pibb->kind == svn_node_symlink) ?
760251881Speter                                pibb->target : NULL)); /* 19 */
761251881Speter  if (pibb->kind == svn_node_file)
762251881Speter    {
763251881Speter      if (!pibb->checksum
764251881Speter          && pibb->status != svn_wc__db_status_not_present
765251881Speter          && pibb->status != svn_wc__db_status_excluded
766251881Speter          && pibb->status != svn_wc__db_status_server_excluded)
767251881Speter        return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
768251881Speter                                 _("The file '%s' has no checksum."),
769251881Speter                                 path_for_error_message(wcroot, local_relpath,
770251881Speter                                                        scratch_pool));
771251881Speter
772251881Speter      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
773251881Speter                                        scratch_pool));
774251881Speter
775251881Speter      if (recorded_size != SVN_INVALID_FILESIZE)
776251881Speter        {
777251881Speter          SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
778251881Speter          SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
779251881Speter        }
780251881Speter    }
781251881Speter
782251881Speter  /* Set properties.  Must be null if presence not normal or incomplete. */
783251881Speter  assert(pibb->status == svn_wc__db_status_normal
784251881Speter         || pibb->status == svn_wc__db_status_incomplete
785251881Speter         || pibb->props == NULL);
786251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
787251881Speter                                      scratch_pool));
788251881Speter
789251881Speter  SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
790251881Speter                                      scratch_pool));
791251881Speter
792251881Speter  if (pibb->dav_cache)
793251881Speter    SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
794251881Speter                                        scratch_pool));
795251881Speter
796251881Speter  if (pibb->file_external)
797251881Speter    SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
798251881Speter
799251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
800251881Speter
801251881Speter  if (pibb->update_actual_props)
802251881Speter    {
803251881Speter      /* Cast away const, to allow calling property helpers */
804251881Speter      apr_hash_t *base_props = (apr_hash_t *)pibb->props;
805251881Speter      apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
806251881Speter
807251881Speter      if (base_props != NULL
808251881Speter          && new_actual_props != NULL
809251881Speter          && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
810251881Speter        {
811251881Speter          apr_array_header_t *diffs;
812251881Speter
813251881Speter          SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
814251881Speter                                 scratch_pool));
815251881Speter
816251881Speter          if (diffs->nelts == 0)
817251881Speter            new_actual_props = NULL;
818251881Speter        }
819251881Speter
820251881Speter      SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
821251881Speter                               wcroot->sdb, scratch_pool));
822251881Speter    }
823251881Speter
824251881Speter  if (pibb->kind == svn_node_dir && pibb->children)
825251881Speter    SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
826251881Speter                                       local_relpath,
827251881Speter                                       repos_id,
828251881Speter                                       pibb->repos_relpath,
829251881Speter                                       pibb->revision,
830251881Speter                                       pibb->children,
831251881Speter                                       0 /* BASE */,
832251881Speter                                       scratch_pool));
833251881Speter
834251881Speter  /* When this is not the root node, check shadowing behavior */
835251881Speter  if (*local_relpath)
836251881Speter    {
837251881Speter      if (parent_relpath
838251881Speter          && ((pibb->status == svn_wc__db_status_normal)
839251881Speter              || (pibb->status == svn_wc__db_status_incomplete))
840251881Speter          && ! pibb->file_external)
841251881Speter        {
842251881Speter          SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
843251881Speter                                                  pibb->kind, 0,
844251881Speter                                                  scratch_pool));
845251881Speter        }
846251881Speter      else if (pibb->status == svn_wc__db_status_not_present
847251881Speter               || pibb->status == svn_wc__db_status_server_excluded
848251881Speter               || pibb->status == svn_wc__db_status_excluded)
849251881Speter        {
850251881Speter          SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
851251881Speter                                                   scratch_pool));
852251881Speter        }
853251881Speter    }
854251881Speter
855251881Speter  if (pibb->delete_working)
856251881Speter    {
857251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
858251881Speter                                    STMT_DELETE_WORKING_NODE));
859251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
860251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
861251881Speter    }
862251881Speter  if (pibb->insert_base_deleted)
863251881Speter    {
864251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
865251881Speter                                        STMT_INSERT_DELETE_FROM_BASE));
866251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
867251881Speter                                wcroot->wc_id, local_relpath,
868251881Speter                                relpath_depth(local_relpath)));
869251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
870251881Speter    }
871251881Speter
872251881Speter  SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
873251881Speter  if (pibb->conflict)
874251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
875251881Speter                                              pibb->conflict, scratch_pool));
876251881Speter
877251881Speter  return SVN_NO_ERROR;
878251881Speter}
879251881Speter
880251881Speter
881251881Speter/* Initialize the baton with appropriate "blank" values. This allows the
882251881Speter   insertion function to leave certain columns null.  */
883251881Speterstatic void
884251881Speterblank_iwb(insert_working_baton_t *piwb)
885251881Speter{
886251881Speter  memset(piwb, 0, sizeof(*piwb));
887251881Speter  piwb->changed_rev = SVN_INVALID_REVNUM;
888251881Speter  piwb->depth = svn_depth_infinity;
889251881Speter
890251881Speter  /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
891251881Speter     value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
892251881Speter}
893251881Speter
894251881Speter
895251881Speter/* Insert a row in NODES for each (const char *) child name in CHILDREN,
896251881Speter   whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH.  Set each
897251881Speter   child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
898251881Speter   repos_path by appending the child name to REPOS_PATH, and revision to
899251881Speter   REVISION (which should match the parent's revision).
900251881Speter
901251881Speter   If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
902251881Speterstatic svn_error_t *
903251881Speterinsert_incomplete_children(svn_sqlite__db_t *sdb,
904251881Speter                           apr_int64_t wc_id,
905251881Speter                           const char *local_relpath,
906251881Speter                           apr_int64_t repos_id,
907251881Speter                           const char *repos_path,
908251881Speter                           svn_revnum_t revision,
909251881Speter                           const apr_array_header_t *children,
910251881Speter                           int op_depth,
911251881Speter                           apr_pool_t *scratch_pool)
912251881Speter{
913251881Speter  svn_sqlite__stmt_t *stmt;
914251881Speter  int i;
915251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
916251881Speter  apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
917251881Speter
918251881Speter  SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
919251881Speter  SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
920251881Speter                 == (repos_path != NULL));
921251881Speter
922251881Speter  /* If we're inserting WORKING nodes, we might be replacing existing
923251881Speter   * nodes which were moved-away. We need to retain the moved-to relpath of
924251881Speter   * such nodes in order not to lose move information during replace. */
925251881Speter  if (op_depth > 0)
926251881Speter    {
927251881Speter      for (i = children->nelts; i--; )
928251881Speter        {
929251881Speter          const char *name = APR_ARRAY_IDX(children, i, const char *);
930251881Speter          svn_boolean_t have_row;
931251881Speter
932251881Speter          svn_pool_clear(iterpool);
933251881Speter
934251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
935251881Speter                                            STMT_SELECT_WORKING_NODE));
936251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
937251881Speter                                    svn_relpath_join(local_relpath, name,
938251881Speter                                                     iterpool)));
939251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
940251881Speter          if (have_row && !svn_sqlite__column_is_null(stmt, 14))
941251881Speter            svn_hash_sets(moved_to_relpaths, name,
942251881Speter                          svn_sqlite__column_text(stmt, 14, scratch_pool));
943251881Speter
944251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
945251881Speter        }
946251881Speter    }
947251881Speter
948251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
949251881Speter
950251881Speter  for (i = children->nelts; i--; )
951251881Speter    {
952251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
953251881Speter
954251881Speter      svn_pool_clear(iterpool);
955251881Speter
956251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
957251881Speter                                wc_id,
958251881Speter                                svn_relpath_join(local_relpath, name,
959251881Speter                                                 iterpool),
960251881Speter                                op_depth,
961251881Speter                                local_relpath,
962251881Speter                                revision,
963251881Speter                                "incomplete", /* 8, presence */
964251881Speter                                "unknown",    /* 10, kind */
965251881Speter                                /* 21, moved_to */
966251881Speter                                svn_hash_gets(moved_to_relpaths, name)));
967251881Speter      if (repos_id != INVALID_REPOS_ID)
968251881Speter        {
969251881Speter          SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
970251881Speter          SVN_ERR(svn_sqlite__bind_text(stmt, 6,
971251881Speter                                        svn_relpath_join(repos_path, name,
972251881Speter                                                         iterpool)));
973251881Speter        }
974251881Speter
975251881Speter      SVN_ERR(svn_sqlite__insert(NULL, stmt));
976251881Speter    }
977251881Speter
978251881Speter  svn_pool_destroy(iterpool);
979251881Speter
980251881Speter  return SVN_NO_ERROR;
981251881Speter}
982251881Speter
983251881Speter
984251881Speter/* Insert the working row represented by (insert_working_baton_t *) BATON. */
985251881Speterstatic svn_error_t *
986251881Speterinsert_working_node(const insert_working_baton_t *piwb,
987251881Speter                    svn_wc__db_wcroot_t *wcroot,
988251881Speter                    const char *local_relpath,
989251881Speter                    apr_pool_t *scratch_pool)
990251881Speter{
991251881Speter  const char *parent_relpath;
992251881Speter  const char *moved_to_relpath = NULL;
993251881Speter  svn_sqlite__stmt_t *stmt;
994251881Speter  svn_boolean_t have_row;
995251881Speter
996251881Speter  SVN_ERR_ASSERT(piwb->op_depth > 0);
997251881Speter
998251881Speter  /* We cannot insert a WORKING_NODE row at the wcroot.  */
999251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0');
1000251881Speter  parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1001251881Speter
1002251881Speter  /* Preserve existing moved-to information for this relpath,
1003251881Speter   * which might exist in case we're replacing an existing base-deleted
1004251881Speter   * node. */
1005251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1006251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1007251881Speter                            piwb->op_depth));
1008251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1009251881Speter  if (have_row)
1010251881Speter    moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1011251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1012251881Speter
1013251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1014251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1015251881Speter                "nnnn" /* properties translated_size last_mod_time dav_cache */
1016251881Speter                "sns", /* symlink_target, file_external, moved_to */
1017251881Speter                wcroot->wc_id, local_relpath,
1018251881Speter                piwb->op_depth,
1019251881Speter                parent_relpath,
1020251881Speter                presence_map, piwb->presence,
1021251881Speter                (piwb->kind == svn_node_dir)
1022251881Speter                            ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1023251881Speter                kind_map, piwb->kind,
1024251881Speter                piwb->changed_rev,
1025251881Speter                piwb->changed_date,
1026251881Speter                piwb->changed_author,
1027251881Speter                /* Note: incomplete nodes may have a NULL target.  */
1028251881Speter                (piwb->kind == svn_node_symlink)
1029251881Speter                            ? piwb->target : NULL,
1030251881Speter                moved_to_relpath));
1031251881Speter
1032251881Speter  if (piwb->moved_here)
1033251881Speter    {
1034251881Speter      SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1035251881Speter    }
1036251881Speter
1037251881Speter  if (piwb->kind == svn_node_file)
1038251881Speter    {
1039251881Speter      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1040251881Speter                                        scratch_pool));
1041251881Speter    }
1042251881Speter
1043251881Speter  if (piwb->original_repos_relpath != NULL)
1044251881Speter    {
1045251881Speter      SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1046251881Speter      SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1047251881Speter      SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1048251881Speter    }
1049251881Speter
1050251881Speter  /* Set properties.  Must be null if presence not normal or incomplete. */
1051251881Speter  assert(piwb->presence == svn_wc__db_status_normal
1052251881Speter         || piwb->presence == svn_wc__db_status_incomplete
1053251881Speter         || piwb->props == NULL);
1054251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1055251881Speter
1056251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
1057251881Speter
1058251881Speter  /* Insert incomplete children, if specified.
1059251881Speter     The children are part of the same op and so have the same op_depth.
1060251881Speter     (The only time we'd want a different depth is during a recursive
1061251881Speter     simple add, but we never insert children here during a simple add.) */
1062251881Speter  if (piwb->kind == svn_node_dir && piwb->children)
1063251881Speter    SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1064251881Speter                                       local_relpath,
1065251881Speter                                       INVALID_REPOS_ID /* inherit repos_id */,
1066251881Speter                                       NULL /* inherit repos_path */,
1067251881Speter                                       piwb->original_revnum,
1068251881Speter                                       piwb->children,
1069251881Speter                                       piwb->op_depth,
1070251881Speter                                       scratch_pool));
1071251881Speter
1072251881Speter  if (piwb->update_actual_props)
1073251881Speter    {
1074251881Speter      /* Cast away const, to allow calling property helpers */
1075251881Speter      apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1076251881Speter      apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1077251881Speter
1078251881Speter      if (base_props != NULL
1079251881Speter          && new_actual_props != NULL
1080251881Speter          && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1081251881Speter        {
1082251881Speter          apr_array_header_t *diffs;
1083251881Speter
1084251881Speter          SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1085251881Speter                                 scratch_pool));
1086251881Speter
1087251881Speter          if (diffs->nelts == 0)
1088251881Speter            new_actual_props = NULL;
1089251881Speter        }
1090251881Speter
1091251881Speter      SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
1092251881Speter                               wcroot->sdb, scratch_pool));
1093251881Speter    }
1094251881Speter
1095251881Speter  if (piwb->kind == svn_node_dir)
1096251881Speter    {
1097251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1098251881Speter                                        STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1099251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1100251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1101251881Speter
1102251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1103251881Speter                                        STMT_DELETE_ACTUAL_EMPTY));
1104251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1105251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1106251881Speter    }
1107251881Speter
1108251881Speter  if (piwb->not_present_op_depth > 0
1109251881Speter      && piwb->not_present_op_depth < piwb->op_depth)
1110251881Speter    {
1111251881Speter      /* And also insert a not-present node to tell the commit processing that
1112251881Speter         a child of the parent node was not copied. */
1113251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1114251881Speter                                        STMT_INSERT_NODE));
1115251881Speter
1116251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1117251881Speter                                wcroot->wc_id, local_relpath,
1118251881Speter                                piwb->not_present_op_depth, parent_relpath,
1119251881Speter                                piwb->original_repos_id,
1120251881Speter                                piwb->original_repos_relpath,
1121251881Speter                                piwb->original_revnum,
1122251881Speter                                presence_map, svn_wc__db_status_not_present,
1123251881Speter                                /* NULL */
1124251881Speter                                kind_map, piwb->kind));
1125251881Speter
1126251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1127251881Speter    }
1128251881Speter
1129251881Speter  SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1130251881Speter  if (piwb->conflict)
1131251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1132251881Speter                                              piwb->conflict, scratch_pool));
1133251881Speter
1134251881Speter  return SVN_NO_ERROR;
1135251881Speter}
1136251881Speter
1137251881Speter
1138251881Speter/* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
1139251881Speter   pointed to the same name.  */
1140251881Speterstatic svn_error_t *
1141251881Speteradd_children_to_hash(apr_hash_t *children,
1142251881Speter                     int stmt_idx,
1143251881Speter                     svn_sqlite__db_t *sdb,
1144251881Speter                     apr_int64_t wc_id,
1145251881Speter                     const char *parent_relpath,
1146251881Speter                     apr_pool_t *result_pool)
1147251881Speter{
1148251881Speter  svn_sqlite__stmt_t *stmt;
1149251881Speter  svn_boolean_t have_row;
1150251881Speter
1151251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
1152251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
1153251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1154251881Speter  while (have_row)
1155251881Speter    {
1156251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1157251881Speter      const char *name = svn_relpath_basename(child_relpath, result_pool);
1158251881Speter
1159251881Speter      svn_hash_sets(children, name, name);
1160251881Speter
1161251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1162251881Speter    }
1163251881Speter
1164251881Speter  return svn_sqlite__reset(stmt);
1165251881Speter}
1166251881Speter
1167251881Speter
1168251881Speter/* Set *CHILDREN to a new array of the (const char *) basenames of the
1169251881Speter   immediate children, whatever their status, of the working node at
1170251881Speter   LOCAL_RELPATH. */
1171251881Speterstatic svn_error_t *
1172251881Spetergather_children2(const apr_array_header_t **children,
1173251881Speter                 svn_wc__db_wcroot_t *wcroot,
1174251881Speter                 const char *local_relpath,
1175251881Speter                 apr_pool_t *result_pool,
1176251881Speter                 apr_pool_t *scratch_pool)
1177251881Speter{
1178251881Speter  apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1179251881Speter  apr_array_header_t *names_array;
1180251881Speter
1181251881Speter  /* All of the names get allocated in RESULT_POOL.  It
1182251881Speter     appears to be faster to use the hash to remove duplicates than to
1183251881Speter     use DISTINCT in the SQL query. */
1184251881Speter  SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
1185251881Speter                               wcroot->sdb, wcroot->wc_id,
1186251881Speter                               local_relpath, result_pool));
1187251881Speter
1188251881Speter  SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1189251881Speter  *children = names_array;
1190251881Speter  return SVN_NO_ERROR;
1191251881Speter}
1192251881Speter
1193251881Speter/* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1194251881Speter   of any status, in all op-depths in the NODES table. */
1195251881Speterstatic svn_error_t *
1196251881Spetergather_children(const apr_array_header_t **children,
1197251881Speter                svn_wc__db_wcroot_t *wcroot,
1198251881Speter                const char *local_relpath,
1199251881Speter                apr_pool_t *result_pool,
1200251881Speter                apr_pool_t *scratch_pool)
1201251881Speter{
1202251881Speter  apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1203251881Speter  apr_array_header_t *names_array;
1204251881Speter
1205251881Speter  /* All of the names get allocated in RESULT_POOL.  It
1206251881Speter     appears to be faster to use the hash to remove duplicates than to
1207251881Speter     use DISTINCT in the SQL query. */
1208251881Speter  SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
1209251881Speter                               wcroot->sdb, wcroot->wc_id,
1210251881Speter                               local_relpath, result_pool));
1211251881Speter
1212251881Speter  SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1213251881Speter  *children = names_array;
1214251881Speter  return SVN_NO_ERROR;
1215251881Speter}
1216251881Speter
1217251881Speter
1218251881Speter/* Set *CHILDREN to a new array of (const char *) names of the children of
1219251881Speter   the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
1220251881Speter   that is, only the children that are at the same op-depth as their parent. */
1221251881Speterstatic svn_error_t *
1222251881Spetergather_repo_children(const apr_array_header_t **children,
1223251881Speter                     svn_wc__db_wcroot_t *wcroot,
1224251881Speter                     const char *local_relpath,
1225251881Speter                     int op_depth,
1226251881Speter                     apr_pool_t *result_pool,
1227251881Speter                     apr_pool_t *scratch_pool)
1228251881Speter{
1229251881Speter  apr_array_header_t *result
1230251881Speter    = apr_array_make(result_pool, 0, sizeof(const char *));
1231251881Speter  svn_sqlite__stmt_t *stmt;
1232251881Speter  svn_boolean_t have_row;
1233251881Speter
1234251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1235251881Speter                                    STMT_SELECT_OP_DEPTH_CHILDREN));
1236251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1237251881Speter                            op_depth));
1238251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1239251881Speter  while (have_row)
1240251881Speter    {
1241251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1242251881Speter
1243251881Speter      /* Allocate the name in RESULT_POOL so we won't have to copy it. */
1244251881Speter      APR_ARRAY_PUSH(result, const char *)
1245251881Speter        = svn_relpath_basename(child_relpath, result_pool);
1246251881Speter
1247251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1248251881Speter    }
1249251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1250251881Speter
1251251881Speter  *children = result;
1252251881Speter  return SVN_NO_ERROR;
1253251881Speter}
1254251881Speter
1255251881Spetersvn_error_t *
1256251881Spetersvn_wc__db_get_children_op_depth(apr_hash_t **children,
1257251881Speter                                 svn_wc__db_wcroot_t *wcroot,
1258251881Speter                                 const char *local_relpath,
1259251881Speter                                 int op_depth,
1260251881Speter                                 apr_pool_t *result_pool,
1261251881Speter                                 apr_pool_t *scratch_pool)
1262251881Speter{
1263251881Speter  svn_sqlite__stmt_t *stmt;
1264251881Speter  svn_boolean_t have_row;
1265251881Speter
1266251881Speter  *children = apr_hash_make(result_pool);
1267251881Speter
1268251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1269251881Speter                                    STMT_SELECT_OP_DEPTH_CHILDREN));
1270251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1271251881Speter                            op_depth));
1272251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1273251881Speter  while (have_row)
1274251881Speter    {
1275251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1276251881Speter      svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
1277251881Speter
1278251881Speter      *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1279251881Speter      svn_hash_sets(*children,
1280251881Speter                    svn_relpath_basename(child_relpath, result_pool),
1281251881Speter                    child_kind);
1282251881Speter
1283251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1284251881Speter    }
1285251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1286251881Speter
1287251881Speter  return SVN_NO_ERROR;
1288251881Speter}
1289251881Speter
1290251881Speter
1291251881Speter/* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1292251881Speter * Else, return FALSE. */
1293251881Speterstatic svn_boolean_t
1294251881Speteris_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1295251881Speter{
1296251881Speter  const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1297251881Speter                                                       child_abspath);
1298251881Speter
1299251881Speter  /* To be an immediate child local_relpath should have one (not empty)
1300251881Speter     component */
1301251881Speter  return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1302251881Speter}
1303251881Speter
1304251881Speter
1305251881Speter/* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1306251881Speterstatic void
1307251881Speterremove_from_access_cache(apr_hash_t *access_cache,
1308251881Speter                         const char *local_abspath)
1309251881Speter{
1310251881Speter  svn_wc_adm_access_t *adm_access;
1311251881Speter
1312251881Speter  adm_access = svn_hash_gets(access_cache, local_abspath);
1313251881Speter  if (adm_access)
1314251881Speter    svn_wc__adm_access_set_entries(adm_access, NULL);
1315251881Speter}
1316251881Speter
1317251881Speter
1318251881Speter/* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1319251881Speter * the specified DEPTH, from the access baton cache in WCROOT.
1320251881Speter * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1321251881Speter *
1322251881Speter * This function must be called when the access baton cache goes stale,
1323251881Speter * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1324251881Speter *
1325251881Speter * Use SCRATCH_POOL for temporary allocations. */
1326251881Speterstatic svn_error_t *
1327251881Speterflush_entries(svn_wc__db_wcroot_t *wcroot,
1328251881Speter              const char *local_abspath,
1329251881Speter              svn_depth_t depth,
1330251881Speter              apr_pool_t *scratch_pool)
1331251881Speter{
1332251881Speter  const char *parent_abspath;
1333251881Speter
1334251881Speter  if (apr_hash_count(wcroot->access_cache) == 0)
1335251881Speter    return SVN_NO_ERROR;
1336251881Speter
1337251881Speter  remove_from_access_cache(wcroot->access_cache, local_abspath);
1338251881Speter
1339251881Speter  if (depth > svn_depth_empty)
1340251881Speter    {
1341251881Speter      apr_hash_index_t *hi;
1342251881Speter
1343251881Speter      /* Flush access batons of children within the specified depth. */
1344251881Speter      for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1345251881Speter           hi;
1346251881Speter           hi = apr_hash_next(hi))
1347251881Speter        {
1348251881Speter          const char *item_abspath = svn__apr_hash_index_key(hi);
1349251881Speter
1350251881Speter          if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1351251881Speter              is_immediate_child_path(local_abspath, item_abspath))
1352251881Speter            {
1353251881Speter              remove_from_access_cache(wcroot->access_cache, item_abspath);
1354251881Speter            }
1355251881Speter          else if (depth == svn_depth_infinity &&
1356251881Speter                   svn_dirent_is_ancestor(local_abspath, item_abspath))
1357251881Speter            {
1358251881Speter              remove_from_access_cache(wcroot->access_cache, item_abspath);
1359251881Speter            }
1360251881Speter        }
1361251881Speter    }
1362251881Speter
1363251881Speter  /* We're going to be overly aggressive here and just flush the parent
1364251881Speter     without doing much checking.  This may hurt performance for
1365251881Speter     legacy API consumers, but that's not our problem. :) */
1366251881Speter  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1367251881Speter  remove_from_access_cache(wcroot->access_cache, parent_abspath);
1368251881Speter
1369251881Speter  return SVN_NO_ERROR;
1370251881Speter}
1371251881Speter
1372251881Speter
1373251881Speter/* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1374251881Speter   not perform its work within a transaction, assuming the caller will
1375251881Speter   manage that.  */
1376251881Speterstatic svn_error_t *
1377251881Speteradd_single_work_item(svn_sqlite__db_t *sdb,
1378251881Speter                     const svn_skel_t *work_item,
1379251881Speter                     apr_pool_t *scratch_pool)
1380251881Speter{
1381251881Speter  svn_stringbuf_t *serialized;
1382251881Speter  svn_sqlite__stmt_t *stmt;
1383251881Speter
1384251881Speter  serialized = svn_skel__unparse(work_item, scratch_pool);
1385251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1386251881Speter  SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1387251881Speter  return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1388251881Speter}
1389251881Speter
1390251881Speter
1391251881Speter/* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1392251881Speter   SKEL is usually passed to the various wc_db operation functions. It may
1393251881Speter   be NULL, indicating no additional work items are needed, it may be a
1394251881Speter   single work item, or it may be a list of work items.  */
1395251881Speterstatic svn_error_t *
1396251881Speteradd_work_items(svn_sqlite__db_t *sdb,
1397251881Speter               const svn_skel_t *skel,
1398251881Speter               apr_pool_t *scratch_pool)
1399251881Speter{
1400251881Speter  apr_pool_t *iterpool;
1401251881Speter
1402251881Speter  /* Maybe there are no work items to insert.  */
1403251881Speter  if (skel == NULL)
1404251881Speter    return SVN_NO_ERROR;
1405251881Speter
1406251881Speter  /* Should have a list.  */
1407251881Speter  SVN_ERR_ASSERT(!skel->is_atom);
1408251881Speter
1409251881Speter  /* Is the list a single work item? Or a list of work items?  */
1410251881Speter  if (SVN_WC__SINGLE_WORK_ITEM(skel))
1411251881Speter    return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1412251881Speter
1413251881Speter  /* SKEL is a list-of-lists, aka list of work items.  */
1414251881Speter
1415251881Speter  iterpool = svn_pool_create(scratch_pool);
1416251881Speter  for (skel = skel->children; skel; skel = skel->next)
1417251881Speter    {
1418251881Speter      svn_pool_clear(iterpool);
1419251881Speter
1420251881Speter      SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1421251881Speter    }
1422251881Speter  svn_pool_destroy(iterpool);
1423251881Speter
1424251881Speter  return SVN_NO_ERROR;
1425251881Speter}
1426251881Speter
1427251881Speter
1428251881Speter/* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH.  */
1429251881Speterstatic svn_error_t *
1430251881Speterdoes_node_exist(svn_boolean_t *exists,
1431251881Speter                const svn_wc__db_wcroot_t *wcroot,
1432251881Speter                const char *local_relpath)
1433251881Speter{
1434251881Speter  svn_sqlite__stmt_t *stmt;
1435251881Speter
1436251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1437251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1438251881Speter  SVN_ERR(svn_sqlite__step(exists, stmt));
1439251881Speter
1440251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
1441251881Speter}
1442251881Speter
1443262253Spetersvn_error_t *
1444262253Spetersvn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1445262253Speter                                     apr_pool_t *scratch_pool)
1446262253Speter{
1447262253Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1448262253Speter
1449262253Speter  return SVN_NO_ERROR;
1450262253Speter}
1451262253Speter
1452251881Speter/* Helper for create_db(). Initializes our wc.db schema.
1453251881Speter */
1454251881Speterstatic svn_error_t *
1455251881Speterinit_db(/* output values */
1456251881Speter        apr_int64_t *repos_id,
1457251881Speter        apr_int64_t *wc_id,
1458251881Speter        /* input values */
1459251881Speter        svn_sqlite__db_t *db,
1460251881Speter        const char *repos_root_url,
1461251881Speter        const char *repos_uuid,
1462251881Speter        const char *root_node_repos_relpath,
1463251881Speter        svn_revnum_t root_node_revision,
1464251881Speter        svn_depth_t root_node_depth,
1465251881Speter        apr_pool_t *scratch_pool)
1466251881Speter{
1467251881Speter  svn_sqlite__stmt_t *stmt;
1468251881Speter
1469251881Speter  /* Create the database's schema.  */
1470251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1471251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1472251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1473251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1474251881Speter
1475251881Speter  /* Insert the repository. */
1476251881Speter  SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1477251881Speter                          db, scratch_pool));
1478251881Speter
1479262253Speter  SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1480262253Speter
1481251881Speter  /* Insert the wcroot. */
1482251881Speter  /* ### Right now, this just assumes wc metadata is being stored locally. */
1483251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1484251881Speter  SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1485251881Speter
1486251881Speter  if (root_node_repos_relpath)
1487251881Speter    {
1488251881Speter      svn_wc__db_status_t status = svn_wc__db_status_normal;
1489251881Speter
1490251881Speter      if (root_node_revision > 0)
1491251881Speter        status = svn_wc__db_status_incomplete; /* Will be filled by update */
1492251881Speter
1493251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1494251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1495251881Speter                                *wc_id,              /* 1 */
1496251881Speter                                "",                  /* 2 */
1497251881Speter                                0,                   /* op_depth is 0 for base */
1498251881Speter                                NULL,                /* 4 */
1499251881Speter                                *repos_id,
1500251881Speter                                root_node_repos_relpath,
1501251881Speter                                root_node_revision,
1502251881Speter                                presence_map, status, /* 8 */
1503251881Speter                                svn_token__to_word(depth_map,
1504251881Speter                                                   root_node_depth),
1505251881Speter                                kind_map, svn_node_dir /* 10 */));
1506251881Speter
1507251881Speter      SVN_ERR(svn_sqlite__insert(NULL, stmt));
1508251881Speter    }
1509251881Speter
1510251881Speter  return SVN_NO_ERROR;
1511251881Speter}
1512251881Speter
1513251881Speter/* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1514251881Speter   records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1515251881Speter   REPOSITORY and for WC_ID into WCROOT.  Return the DB connection
1516251881Speter   in *SDB.
1517251881Speter
1518251881Speter   If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1519251881Speter   the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1520251881Speter   revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1521251881Speter   */
1522251881Speterstatic svn_error_t *
1523251881Spetercreate_db(svn_sqlite__db_t **sdb,
1524251881Speter          apr_int64_t *repos_id,
1525251881Speter          apr_int64_t *wc_id,
1526251881Speter          const char *dir_abspath,
1527251881Speter          const char *repos_root_url,
1528251881Speter          const char *repos_uuid,
1529251881Speter          const char *sdb_fname,
1530251881Speter          const char *root_node_repos_relpath,
1531251881Speter          svn_revnum_t root_node_revision,
1532251881Speter          svn_depth_t root_node_depth,
1533251881Speter          svn_boolean_t exclusive,
1534251881Speter          apr_pool_t *result_pool,
1535251881Speter          apr_pool_t *scratch_pool)
1536251881Speter{
1537251881Speter  SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1538251881Speter                                  svn_sqlite__mode_rwcreate, exclusive,
1539251881Speter                                  NULL /* my_statements */,
1540251881Speter                                  result_pool, scratch_pool));
1541251881Speter
1542251881Speter  SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1543251881Speter                                *sdb, repos_root_url, repos_uuid,
1544251881Speter                                root_node_repos_relpath, root_node_revision,
1545251881Speter                                root_node_depth, scratch_pool),
1546251881Speter                        *sdb);
1547251881Speter
1548251881Speter  return SVN_NO_ERROR;
1549251881Speter}
1550251881Speter
1551251881Speter
1552251881Spetersvn_error_t *
1553251881Spetersvn_wc__db_init(svn_wc__db_t *db,
1554251881Speter                const char *local_abspath,
1555251881Speter                const char *repos_relpath,
1556251881Speter                const char *repos_root_url,
1557251881Speter                const char *repos_uuid,
1558251881Speter                svn_revnum_t initial_rev,
1559251881Speter                svn_depth_t depth,
1560251881Speter                apr_pool_t *scratch_pool)
1561251881Speter{
1562251881Speter  svn_sqlite__db_t *sdb;
1563251881Speter  apr_int64_t repos_id;
1564251881Speter  apr_int64_t wc_id;
1565251881Speter  svn_wc__db_wcroot_t *wcroot;
1566251881Speter  svn_boolean_t sqlite_exclusive = FALSE;
1567251881Speter
1568251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1569251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1570251881Speter  SVN_ERR_ASSERT(depth == svn_depth_empty
1571251881Speter                 || depth == svn_depth_files
1572251881Speter                 || depth == svn_depth_immediates
1573251881Speter                 || depth == svn_depth_infinity);
1574251881Speter
1575251881Speter  /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
1576251881Speter
1577251881Speter  SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1578251881Speter                              SVN_CONFIG_SECTION_WORKING_COPY,
1579251881Speter                              SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1580251881Speter                              FALSE));
1581251881Speter
1582251881Speter  /* Create the SDB and insert the basic rows.  */
1583251881Speter  SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1584251881Speter                    repos_uuid, SDB_FILE,
1585251881Speter                    repos_relpath, initial_rev, depth, sqlite_exclusive,
1586251881Speter                    db->state_pool, scratch_pool));
1587251881Speter
1588251881Speter  /* Create the WCROOT for this directory.  */
1589251881Speter  SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1590251881Speter                        apr_pstrdup(db->state_pool, local_abspath),
1591251881Speter                        sdb, wc_id, FORMAT_FROM_SDB,
1592251881Speter                        FALSE /* auto-upgrade */,
1593251881Speter                        FALSE /* enforce_empty_wq */,
1594251881Speter                        db->state_pool, scratch_pool));
1595251881Speter
1596251881Speter  /* The WCROOT is complete. Stash it into DB.  */
1597251881Speter  svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1598251881Speter
1599251881Speter  return SVN_NO_ERROR;
1600251881Speter}
1601251881Speter
1602251881Speter
1603251881Spetersvn_error_t *
1604251881Spetersvn_wc__db_to_relpath(const char **local_relpath,
1605251881Speter                      svn_wc__db_t *db,
1606251881Speter                      const char *wri_abspath,
1607251881Speter                      const char *local_abspath,
1608251881Speter                      apr_pool_t *result_pool,
1609251881Speter                      apr_pool_t *scratch_pool)
1610251881Speter{
1611251881Speter  svn_wc__db_wcroot_t *wcroot;
1612251881Speter  const char *relpath;
1613251881Speter
1614251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1615251881Speter
1616251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1617251881Speter                              wri_abspath, result_pool, scratch_pool));
1618251881Speter
1619251881Speter  /* This function is indirectly called from the upgrade code, so we
1620251881Speter     can't verify the wcroot here. Just check that it is not NULL */
1621251881Speter  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1622251881Speter
1623251881Speter  if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1624251881Speter    {
1625251881Speter      *local_relpath = apr_pstrdup(result_pool,
1626251881Speter                                   svn_dirent_skip_ancestor(wcroot->abspath,
1627251881Speter                                                            local_abspath));
1628251881Speter    }
1629251881Speter  else
1630251881Speter    /* Probably moving from $TMP. Should we allow this? */
1631251881Speter    *local_relpath = apr_pstrdup(result_pool, local_abspath);
1632251881Speter
1633251881Speter  return SVN_NO_ERROR;
1634251881Speter}
1635251881Speter
1636251881Speter
1637251881Spetersvn_error_t *
1638251881Spetersvn_wc__db_from_relpath(const char **local_abspath,
1639251881Speter                        svn_wc__db_t *db,
1640251881Speter                        const char *wri_abspath,
1641251881Speter                        const char *local_relpath,
1642251881Speter                        apr_pool_t *result_pool,
1643251881Speter                        apr_pool_t *scratch_pool)
1644251881Speter{
1645251881Speter  svn_wc__db_wcroot_t *wcroot;
1646251881Speter  const char *unused_relpath;
1647251881Speter#if 0
1648251881Speter  SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1649251881Speter#endif
1650251881Speter
1651251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1652251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1653251881Speter
1654251881Speter  /* This function is indirectly called from the upgrade code, so we
1655251881Speter     can't verify the wcroot here. Just check that it is not NULL */
1656251881Speter  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1657251881Speter
1658251881Speter
1659251881Speter  *local_abspath = svn_dirent_join(wcroot->abspath,
1660251881Speter                                   local_relpath,
1661251881Speter                                   result_pool);
1662251881Speter  return SVN_NO_ERROR;
1663251881Speter}
1664251881Speter
1665251881Speter
1666251881Spetersvn_error_t *
1667251881Spetersvn_wc__db_get_wcroot(const char **wcroot_abspath,
1668251881Speter                      svn_wc__db_t *db,
1669251881Speter                      const char *wri_abspath,
1670251881Speter                      apr_pool_t *result_pool,
1671251881Speter                      apr_pool_t *scratch_pool)
1672251881Speter{
1673251881Speter  svn_wc__db_wcroot_t *wcroot;
1674251881Speter  const char *unused_relpath;
1675251881Speter
1676251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1677251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1678251881Speter
1679251881Speter  /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1680251881Speter     where call upgrade */
1681251881Speter  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1682251881Speter
1683251881Speter  *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1684251881Speter
1685251881Speter  return SVN_NO_ERROR;
1686251881Speter}
1687251881Speter
1688251881Speter
1689251881Spetersvn_error_t *
1690251881Spetersvn_wc__db_base_add_directory(svn_wc__db_t *db,
1691251881Speter                              const char *local_abspath,
1692251881Speter                              const char *wri_abspath,
1693251881Speter                              const char *repos_relpath,
1694251881Speter                              const char *repos_root_url,
1695251881Speter                              const char *repos_uuid,
1696251881Speter                              svn_revnum_t revision,
1697251881Speter                              const apr_hash_t *props,
1698251881Speter                              svn_revnum_t changed_rev,
1699251881Speter                              apr_time_t changed_date,
1700251881Speter                              const char *changed_author,
1701251881Speter                              const apr_array_header_t *children,
1702251881Speter                              svn_depth_t depth,
1703251881Speter                              apr_hash_t *dav_cache,
1704251881Speter                              const svn_skel_t *conflict,
1705251881Speter                              svn_boolean_t update_actual_props,
1706251881Speter                              apr_hash_t *new_actual_props,
1707251881Speter                              apr_array_header_t *new_iprops,
1708251881Speter                              const svn_skel_t *work_items,
1709251881Speter                              apr_pool_t *scratch_pool)
1710251881Speter{
1711251881Speter  svn_wc__db_wcroot_t *wcroot;
1712251881Speter  const char *local_relpath;
1713251881Speter  insert_base_baton_t ibb;
1714251881Speter
1715251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1716251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1717251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1718251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1719251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1720251881Speter  SVN_ERR_ASSERT(props != NULL);
1721251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1722251881Speter#if 0
1723251881Speter  SVN_ERR_ASSERT(children != NULL);
1724251881Speter#endif
1725251881Speter
1726251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1727251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1728251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1729251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1730251881Speter
1731251881Speter  blank_ibb(&ibb);
1732251881Speter
1733251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1734251881Speter  ibb.repos_root_url = repos_root_url;
1735251881Speter  ibb.repos_uuid = repos_uuid;
1736251881Speter
1737251881Speter  ibb.status = svn_wc__db_status_normal;
1738251881Speter  ibb.kind = svn_node_dir;
1739251881Speter  ibb.repos_relpath = repos_relpath;
1740251881Speter  ibb.revision = revision;
1741251881Speter
1742251881Speter  ibb.iprops = new_iprops;
1743251881Speter  ibb.props = props;
1744251881Speter  ibb.changed_rev = changed_rev;
1745251881Speter  ibb.changed_date = changed_date;
1746251881Speter  ibb.changed_author = changed_author;
1747251881Speter
1748251881Speter  ibb.children = children;
1749251881Speter  ibb.depth = depth;
1750251881Speter
1751251881Speter  ibb.dav_cache = dav_cache;
1752251881Speter  ibb.conflict = conflict;
1753251881Speter  ibb.work_items = work_items;
1754251881Speter
1755251881Speter  if (update_actual_props)
1756251881Speter    {
1757251881Speter      ibb.update_actual_props = TRUE;
1758251881Speter      ibb.new_actual_props = new_actual_props;
1759251881Speter    }
1760251881Speter
1761251881Speter  /* Insert the directory and all its children transactionally.
1762251881Speter
1763251881Speter     Note: old children can stick around, even if they are no longer present
1764251881Speter     in this directory's revision.  */
1765251881Speter  SVN_WC__DB_WITH_TXN(
1766251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1767251881Speter            wcroot);
1768251881Speter
1769251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1770251881Speter  return SVN_NO_ERROR;
1771251881Speter}
1772251881Speter
1773251881Spetersvn_error_t *
1774251881Spetersvn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1775251881Speter                                         const char *local_abspath,
1776251881Speter                                         const char *repos_relpath,
1777251881Speter                                         const char *repos_root_url,
1778251881Speter                                         const char *repos_uuid,
1779251881Speter                                         svn_revnum_t revision,
1780251881Speter                                         svn_depth_t depth,
1781251881Speter                                         svn_boolean_t insert_base_deleted,
1782251881Speter                                         svn_boolean_t delete_working,
1783251881Speter                                         svn_skel_t *conflict,
1784251881Speter                                         svn_skel_t *work_items,
1785251881Speter                                         apr_pool_t *scratch_pool)
1786251881Speter{
1787251881Speter  svn_wc__db_wcroot_t *wcroot;
1788251881Speter  const char *local_relpath;
1789251881Speter  struct insert_base_baton_t ibb;
1790251881Speter
1791251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1792251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1793251881Speter  SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1794251881Speter
1795251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1796251881Speter                                                db, local_abspath,
1797251881Speter                                                scratch_pool, scratch_pool));
1798251881Speter
1799251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1800251881Speter
1801251881Speter  blank_ibb(&ibb);
1802251881Speter
1803251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1804251881Speter  ibb.repos_root_url = repos_root_url;
1805251881Speter  ibb.repos_uuid = repos_uuid;
1806251881Speter
1807251881Speter  ibb.status = svn_wc__db_status_incomplete;
1808251881Speter  ibb.kind = svn_node_dir;
1809251881Speter  ibb.repos_relpath = repos_relpath;
1810251881Speter  ibb.revision = revision;
1811251881Speter  ibb.depth = depth;
1812251881Speter  ibb.insert_base_deleted = insert_base_deleted;
1813251881Speter  ibb.delete_working = delete_working;
1814251881Speter
1815251881Speter  ibb.conflict = conflict;
1816251881Speter  ibb.work_items = work_items;
1817251881Speter
1818251881Speter  SVN_WC__DB_WITH_TXN(
1819251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1820251881Speter            wcroot);
1821251881Speter
1822251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1823251881Speter
1824251881Speter  return SVN_NO_ERROR;
1825251881Speter}
1826251881Speter
1827251881Speter
1828251881Spetersvn_error_t *
1829251881Spetersvn_wc__db_base_add_file(svn_wc__db_t *db,
1830251881Speter                         const char *local_abspath,
1831251881Speter                         const char *wri_abspath,
1832251881Speter                         const char *repos_relpath,
1833251881Speter                         const char *repos_root_url,
1834251881Speter                         const char *repos_uuid,
1835251881Speter                         svn_revnum_t revision,
1836251881Speter                         const apr_hash_t *props,
1837251881Speter                         svn_revnum_t changed_rev,
1838251881Speter                         apr_time_t changed_date,
1839251881Speter                         const char *changed_author,
1840251881Speter                         const svn_checksum_t *checksum,
1841251881Speter                         apr_hash_t *dav_cache,
1842251881Speter                         svn_boolean_t delete_working,
1843251881Speter                         svn_boolean_t update_actual_props,
1844251881Speter                         apr_hash_t *new_actual_props,
1845251881Speter                         apr_array_header_t *new_iprops,
1846251881Speter                         svn_boolean_t keep_recorded_info,
1847251881Speter                         svn_boolean_t insert_base_deleted,
1848251881Speter                         const svn_skel_t *conflict,
1849251881Speter                         const svn_skel_t *work_items,
1850251881Speter                         apr_pool_t *scratch_pool)
1851251881Speter{
1852251881Speter  svn_wc__db_wcroot_t *wcroot;
1853251881Speter  const char *local_relpath;
1854251881Speter  insert_base_baton_t ibb;
1855251881Speter
1856251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1857251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1858251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1859251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1860251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1861251881Speter  SVN_ERR_ASSERT(props != NULL);
1862251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1863251881Speter  SVN_ERR_ASSERT(checksum != NULL);
1864251881Speter
1865251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1866251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1867251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1868251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1869251881Speter
1870251881Speter  blank_ibb(&ibb);
1871251881Speter
1872251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1873251881Speter  ibb.repos_root_url = repos_root_url;
1874251881Speter  ibb.repos_uuid = repos_uuid;
1875251881Speter
1876251881Speter  ibb.status = svn_wc__db_status_normal;
1877251881Speter  ibb.kind = svn_node_file;
1878251881Speter  ibb.repos_relpath = repos_relpath;
1879251881Speter  ibb.revision = revision;
1880251881Speter
1881251881Speter  ibb.props = props;
1882251881Speter  ibb.changed_rev = changed_rev;
1883251881Speter  ibb.changed_date = changed_date;
1884251881Speter  ibb.changed_author = changed_author;
1885251881Speter
1886251881Speter  ibb.checksum = checksum;
1887251881Speter
1888251881Speter  ibb.dav_cache = dav_cache;
1889251881Speter  ibb.iprops = new_iprops;
1890251881Speter
1891251881Speter  if (update_actual_props)
1892251881Speter    {
1893251881Speter      ibb.update_actual_props = TRUE;
1894251881Speter      ibb.new_actual_props = new_actual_props;
1895251881Speter    }
1896251881Speter
1897251881Speter  ibb.keep_recorded_info = keep_recorded_info;
1898251881Speter  ibb.insert_base_deleted = insert_base_deleted;
1899251881Speter  ibb.delete_working = delete_working;
1900251881Speter
1901251881Speter  ibb.conflict = conflict;
1902251881Speter  ibb.work_items = work_items;
1903251881Speter
1904251881Speter  SVN_WC__DB_WITH_TXN(
1905251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1906251881Speter            wcroot);
1907251881Speter
1908251881Speter  /* If this used to be a directory we should remove children so pass
1909251881Speter   * depth infinity. */
1910251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1911251881Speter                        scratch_pool));
1912251881Speter  return SVN_NO_ERROR;
1913251881Speter}
1914251881Speter
1915251881Speter
1916251881Spetersvn_error_t *
1917251881Spetersvn_wc__db_base_add_symlink(svn_wc__db_t *db,
1918251881Speter                            const char *local_abspath,
1919251881Speter                            const char *wri_abspath,
1920251881Speter                            const char *repos_relpath,
1921251881Speter                            const char *repos_root_url,
1922251881Speter                            const char *repos_uuid,
1923251881Speter                            svn_revnum_t revision,
1924251881Speter                            const apr_hash_t *props,
1925251881Speter                            svn_revnum_t changed_rev,
1926251881Speter                            apr_time_t changed_date,
1927251881Speter                            const char *changed_author,
1928251881Speter                            const char *target,
1929251881Speter                            apr_hash_t *dav_cache,
1930251881Speter                            svn_boolean_t delete_working,
1931251881Speter                            svn_boolean_t update_actual_props,
1932251881Speter                            apr_hash_t *new_actual_props,
1933251881Speter                            apr_array_header_t *new_iprops,
1934251881Speter                            svn_boolean_t keep_recorded_info,
1935251881Speter                            svn_boolean_t insert_base_deleted,
1936251881Speter                            const svn_skel_t *conflict,
1937251881Speter                            const svn_skel_t *work_items,
1938251881Speter                            apr_pool_t *scratch_pool)
1939251881Speter{
1940251881Speter  svn_wc__db_wcroot_t *wcroot;
1941251881Speter  const char *local_relpath;
1942251881Speter  insert_base_baton_t ibb;
1943251881Speter
1944251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1945251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1946251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1947251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1948251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1949251881Speter  SVN_ERR_ASSERT(props != NULL);
1950251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1951251881Speter  SVN_ERR_ASSERT(target != NULL);
1952251881Speter
1953251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1954251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1955251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1956251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1957251881Speter  blank_ibb(&ibb);
1958251881Speter
1959251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1960251881Speter  ibb.repos_root_url = repos_root_url;
1961251881Speter  ibb.repos_uuid = repos_uuid;
1962251881Speter
1963251881Speter  ibb.status = svn_wc__db_status_normal;
1964251881Speter  ibb.kind = svn_node_symlink;
1965251881Speter  ibb.repos_relpath = repos_relpath;
1966251881Speter  ibb.revision = revision;
1967251881Speter
1968251881Speter  ibb.props = props;
1969251881Speter  ibb.changed_rev = changed_rev;
1970251881Speter  ibb.changed_date = changed_date;
1971251881Speter  ibb.changed_author = changed_author;
1972251881Speter
1973251881Speter  ibb.target = target;
1974251881Speter
1975251881Speter  ibb.dav_cache = dav_cache;
1976251881Speter  ibb.iprops = new_iprops;
1977251881Speter
1978251881Speter  if (update_actual_props)
1979251881Speter    {
1980251881Speter      ibb.update_actual_props = TRUE;
1981251881Speter      ibb.new_actual_props = new_actual_props;
1982251881Speter    }
1983251881Speter
1984251881Speter  ibb.keep_recorded_info = keep_recorded_info;
1985251881Speter  ibb.insert_base_deleted = insert_base_deleted;
1986251881Speter  ibb.delete_working = delete_working;
1987251881Speter
1988251881Speter  ibb.conflict = conflict;
1989251881Speter  ibb.work_items = work_items;
1990251881Speter
1991251881Speter  SVN_WC__DB_WITH_TXN(
1992251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1993251881Speter            wcroot);
1994251881Speter
1995251881Speter  /* If this used to be a directory we should remove children so pass
1996251881Speter   * depth infinity. */
1997251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1998251881Speter                        scratch_pool));
1999251881Speter  return SVN_NO_ERROR;
2000251881Speter}
2001251881Speter
2002251881Speter
2003251881Speterstatic svn_error_t *
2004251881Speteradd_excluded_or_not_present_node(svn_wc__db_t *db,
2005251881Speter                                 const char *local_abspath,
2006251881Speter                                 const char *repos_relpath,
2007251881Speter                                 const char *repos_root_url,
2008251881Speter                                 const char *repos_uuid,
2009251881Speter                                 svn_revnum_t revision,
2010251881Speter                                 svn_node_kind_t kind,
2011251881Speter                                 svn_wc__db_status_t status,
2012251881Speter                                 const svn_skel_t *conflict,
2013251881Speter                                 const svn_skel_t *work_items,
2014251881Speter                                 apr_pool_t *scratch_pool)
2015251881Speter{
2016251881Speter  svn_wc__db_wcroot_t *wcroot;
2017251881Speter  const char *local_relpath;
2018251881Speter  insert_base_baton_t ibb;
2019251881Speter  const char *dir_abspath, *name;
2020251881Speter
2021251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2022251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
2023251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
2024251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
2025251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
2026251881Speter  SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2027251881Speter                 || status == svn_wc__db_status_excluded
2028251881Speter                 || status == svn_wc__db_status_not_present);
2029251881Speter
2030251881Speter  /* These absent presence nodes are only useful below a parent node that is
2031251881Speter     present. To avoid problems with working copies obstructing the child
2032251881Speter     we calculate the wcroot and local_relpath of the parent and then add
2033251881Speter     our own relpath. */
2034251881Speter
2035251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
2036251881Speter
2037251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2038251881Speter                              dir_abspath, scratch_pool, scratch_pool));
2039251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2040251881Speter
2041251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
2042251881Speter
2043251881Speter  blank_ibb(&ibb);
2044251881Speter
2045251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
2046251881Speter  ibb.repos_root_url = repos_root_url;
2047251881Speter  ibb.repos_uuid = repos_uuid;
2048251881Speter
2049251881Speter  ibb.status = status;
2050251881Speter  ibb.kind = kind;
2051251881Speter  ibb.repos_relpath = repos_relpath;
2052251881Speter  ibb.revision = revision;
2053251881Speter
2054251881Speter  /* Depending upon KIND, any of these might get used. */
2055251881Speter  ibb.children = NULL;
2056251881Speter  ibb.depth = svn_depth_unknown;
2057251881Speter  ibb.checksum = NULL;
2058251881Speter  ibb.target = NULL;
2059251881Speter
2060251881Speter  ibb.conflict = conflict;
2061251881Speter  ibb.work_items = work_items;
2062251881Speter
2063251881Speter  SVN_WC__DB_WITH_TXN(
2064251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2065251881Speter            wcroot);
2066251881Speter
2067251881Speter  /* If this used to be a directory we should remove children so pass
2068251881Speter   * depth infinity. */
2069251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2070251881Speter                        scratch_pool));
2071251881Speter
2072251881Speter  return SVN_NO_ERROR;
2073251881Speter}
2074251881Speter
2075251881Speter
2076251881Spetersvn_error_t *
2077251881Spetersvn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2078251881Speter                                  const char *local_abspath,
2079251881Speter                                  const char *repos_relpath,
2080251881Speter                                  const char *repos_root_url,
2081251881Speter                                  const char *repos_uuid,
2082251881Speter                                  svn_revnum_t revision,
2083251881Speter                                  svn_node_kind_t kind,
2084251881Speter                                  svn_wc__db_status_t status,
2085251881Speter                                  const svn_skel_t *conflict,
2086251881Speter                                  const svn_skel_t *work_items,
2087251881Speter                                  apr_pool_t *scratch_pool)
2088251881Speter{
2089251881Speter  SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2090251881Speter                 || status == svn_wc__db_status_excluded);
2091251881Speter
2092251881Speter  return add_excluded_or_not_present_node(
2093251881Speter    db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2094251881Speter    kind, status, conflict, work_items, scratch_pool);
2095251881Speter}
2096251881Speter
2097251881Speter
2098251881Spetersvn_error_t *
2099251881Spetersvn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2100251881Speter                                     const char *local_abspath,
2101251881Speter                                     const char *repos_relpath,
2102251881Speter                                     const char *repos_root_url,
2103251881Speter                                     const char *repos_uuid,
2104251881Speter                                     svn_revnum_t revision,
2105251881Speter                                     svn_node_kind_t kind,
2106251881Speter                                     const svn_skel_t *conflict,
2107251881Speter                                     const svn_skel_t *work_items,
2108251881Speter                                     apr_pool_t *scratch_pool)
2109251881Speter{
2110251881Speter  return add_excluded_or_not_present_node(
2111251881Speter    db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2112251881Speter    kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2113251881Speter}
2114251881Speter
2115251881Speter/* Recursively clear moved-here information at the copy-half of the move
2116251881Speter * which moved the node at SRC_RELPATH away. This transforms the move into
2117251881Speter * a simple copy. */
2118251881Speterstatic svn_error_t *
2119251881Speterclear_moved_here(const char *src_relpath,
2120251881Speter                 svn_wc__db_wcroot_t *wcroot,
2121251881Speter                 apr_pool_t *scratch_pool)
2122251881Speter{
2123251881Speter  svn_sqlite__stmt_t *stmt;
2124251881Speter  const char *dst_relpath;
2125251881Speter
2126251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2127251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2128251881Speter                            src_relpath, relpath_depth(src_relpath)));
2129251881Speter  SVN_ERR(svn_sqlite__step_row(stmt));
2130251881Speter  dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2131251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
2132251881Speter
2133251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2134251881Speter                                    STMT_CLEAR_MOVED_HERE_RECURSIVE));
2135251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2136251881Speter                            dst_relpath, relpath_depth(dst_relpath)));
2137251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2138251881Speter
2139251881Speter  return SVN_NO_ERROR;
2140251881Speter}
2141251881Speter
2142251881Speter/* The body of svn_wc__db_base_remove().
2143251881Speter */
2144251881Speterstatic svn_error_t *
2145251881Speterdb_base_remove(svn_wc__db_wcroot_t *wcroot,
2146251881Speter               const char *local_relpath,
2147251881Speter               svn_wc__db_t *db, /* For checking conflicts */
2148251881Speter               svn_boolean_t keep_as_working,
2149251881Speter               svn_boolean_t queue_deletes,
2150253734Speter               svn_boolean_t remove_locks,
2151251881Speter               svn_revnum_t not_present_revision,
2152251881Speter               svn_skel_t *conflict,
2153251881Speter               svn_skel_t *work_items,
2154251881Speter               apr_pool_t *scratch_pool)
2155251881Speter{
2156251881Speter  svn_sqlite__stmt_t *stmt;
2157251881Speter  svn_boolean_t have_row;
2158251881Speter  svn_wc__db_status_t status;
2159251881Speter  apr_int64_t repos_id;
2160251881Speter  const char *repos_relpath;
2161251881Speter  svn_node_kind_t kind;
2162251881Speter  svn_boolean_t keep_working;
2163251881Speter
2164251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2165251881Speter                                            &repos_relpath, &repos_id,
2166251881Speter                                            NULL, NULL, NULL, NULL, NULL,
2167251881Speter                                            NULL, NULL, NULL, NULL, NULL,
2168251881Speter                                            wcroot, local_relpath,
2169251881Speter                                            scratch_pool, scratch_pool));
2170251881Speter
2171253734Speter  if (remove_locks)
2172253734Speter    {
2173253734Speter      svn_sqlite__stmt_t *lock_stmt;
2174253734Speter
2175253734Speter      SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2176253734Speter                                        STMT_DELETE_LOCK_RECURSIVELY));
2177253734Speter      SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2178253734Speter      SVN_ERR(svn_sqlite__step_done(lock_stmt));
2179253734Speter    }
2180253734Speter
2181251881Speter  if (status == svn_wc__db_status_normal
2182251881Speter      && keep_as_working)
2183251881Speter    {
2184251881Speter      SVN_ERR(svn_wc__db_op_make_copy(db,
2185251881Speter                                      svn_dirent_join(wcroot->abspath,
2186251881Speter                                                      local_relpath,
2187251881Speter                                                      scratch_pool),
2188251881Speter                                      NULL, NULL,
2189251881Speter                                      scratch_pool));
2190251881Speter      keep_working = TRUE;
2191251881Speter    }
2192251881Speter  else
2193251881Speter    {
2194251881Speter      /* Check if there is already a working node */
2195251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2196251881Speter                                        STMT_SELECT_WORKING_NODE));
2197251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2198251881Speter      SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2199251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
2200251881Speter    }
2201251881Speter
2202251881Speter  /* Step 1: Create workqueue operations to remove files and dirs in the
2203251881Speter     local-wc */
2204251881Speter  if (!keep_working
2205251881Speter      && queue_deletes
2206251881Speter      && (status == svn_wc__db_status_normal
2207251881Speter          || status == svn_wc__db_status_incomplete))
2208251881Speter    {
2209251881Speter      svn_skel_t *work_item;
2210251881Speter      const char *local_abspath;
2211251881Speter
2212251881Speter      local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2213251881Speter                                      scratch_pool);
2214251881Speter      if (kind == svn_node_dir)
2215251881Speter        {
2216251881Speter          apr_pool_t *iterpool;
2217251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2218251881Speter                                            STMT_SELECT_BASE_PRESENT));
2219251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2220251881Speter
2221251881Speter          iterpool = svn_pool_create(scratch_pool);
2222251881Speter
2223251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2224251881Speter
2225251881Speter          while (have_row)
2226251881Speter            {
2227251881Speter              const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2228251881Speter              svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2229251881Speter                                                              kind_map);
2230251881Speter              const char *node_abspath;
2231251881Speter              svn_error_t *err;
2232251881Speter
2233251881Speter              svn_pool_clear(iterpool);
2234251881Speter
2235251881Speter              node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2236251881Speter                                             iterpool);
2237251881Speter
2238251881Speter              if (node_kind == svn_node_dir)
2239251881Speter                err = svn_wc__wq_build_dir_remove(&work_item,
2240251881Speter                                                  db, wcroot->abspath,
2241251881Speter                                                  node_abspath, FALSE,
2242251881Speter                                                  iterpool, iterpool);
2243251881Speter              else
2244251881Speter                err = svn_wc__wq_build_file_remove(&work_item,
2245251881Speter                                                   db,
2246251881Speter                                                   wcroot->abspath,
2247251881Speter                                                   node_abspath,
2248251881Speter                                                   iterpool, iterpool);
2249251881Speter
2250251881Speter              if (!err)
2251251881Speter                err = add_work_items(wcroot->sdb, work_item, iterpool);
2252251881Speter              if (err)
2253251881Speter                return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2254251881Speter
2255251881Speter              SVN_ERR(svn_sqlite__step(&have_row, stmt));
2256251881Speter           }
2257251881Speter
2258251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
2259251881Speter
2260251881Speter          SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2261251881Speter                                              db, wcroot->abspath,
2262251881Speter                                              local_abspath, FALSE,
2263251881Speter                                              scratch_pool, iterpool));
2264251881Speter          svn_pool_destroy(iterpool);
2265251881Speter        }
2266251881Speter      else
2267251881Speter        SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2268251881Speter                                             db, wcroot->abspath,
2269251881Speter                                             local_abspath,
2270251881Speter                                             scratch_pool, scratch_pool));
2271251881Speter
2272251881Speter      SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2273251881Speter    }
2274251881Speter
2275251881Speter  /* Step 2: Delete ACTUAL nodes */
2276251881Speter  if (! keep_working)
2277251881Speter    {
2278251881Speter      /* There won't be a record in NODE left for this node, so we want
2279251881Speter         to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2280251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2281251881Speter                                        STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2282251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2283251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2284251881Speter    }
2285251881Speter  else if (! keep_as_working)
2286251881Speter    {
2287251881Speter      /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2288251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2289251881Speter                                       STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2290251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2291251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2292251881Speter    }
2293251881Speter  /* Else: Everything has been turned into a copy, so we want to keep all
2294251881Speter           ACTUAL_NODE records */
2295251881Speter
2296251881Speter  /* Step 3: Delete WORKING nodes */
2297251881Speter  if (conflict)
2298251881Speter    {
2299251881Speter      apr_pool_t *iterpool;
2300251881Speter
2301251881Speter      /*
2302251881Speter       * When deleting a conflicted node, moves of any moved-outside children
2303251881Speter       * of the node must be broken. Else, the destination will still be marked
2304251881Speter       * moved-here after the move source disappears from the working copy.
2305251881Speter       *
2306251881Speter       * ### FIXME: It would be nicer to have the conflict resolver
2307251881Speter       * break the move instead. It might also be a good idea to
2308251881Speter       * flag a tree conflict on each moved-away child. But doing so
2309251881Speter       * might introduce actual-only nodes without direct parents,
2310251881Speter       * and we're not yet sure if other existing code is prepared
2311251881Speter       * to handle such nodes. To be revisited post-1.8.
2312262253Speter       *
2313262253Speter       * ### In case of a conflict we are most likely creating WORKING nodes
2314262253Speter       *     describing a copy of what was in BASE. The move information
2315262253Speter       *     should be updated to describe a move from the WORKING layer.
2316262253Speter       *     When stored that way the resolver of the tree conflict still has
2317262253Speter       *     the knowledge of what was moved.
2318251881Speter       */
2319251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2320251881Speter                                        STMT_SELECT_MOVED_OUTSIDE));
2321251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2322251881Speter                                             local_relpath,
2323251881Speter                                             relpath_depth(local_relpath)));
2324251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2325251881Speter      iterpool = svn_pool_create(scratch_pool);
2326251881Speter      while (have_row)
2327251881Speter        {
2328251881Speter          const char *child_relpath;
2329251881Speter          svn_error_t *err;
2330251881Speter
2331251881Speter          svn_pool_clear(iterpool);
2332251881Speter          child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2333251881Speter          err = clear_moved_here(child_relpath, wcroot, iterpool);
2334251881Speter          if (err)
2335251881Speter            return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2336251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2337251881Speter        }
2338251881Speter      svn_pool_destroy(iterpool);
2339251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
2340251881Speter    }
2341251881Speter  if (keep_working)
2342251881Speter    {
2343251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2344251881Speter                                        STMT_DELETE_WORKING_BASE_DELETE));
2345251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2346251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2347251881Speter    }
2348251881Speter  else
2349251881Speter    {
2350251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2351251881Speter                                        STMT_DELETE_WORKING_RECURSIVE));
2352251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2353251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2354251881Speter    }
2355251881Speter
2356251881Speter  /* Step 4: Delete the BASE node descendants */
2357251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2358251881Speter                                    STMT_DELETE_BASE_RECURSIVE));
2359251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2360251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2361251881Speter
2362251881Speter  /* Step 5: handle the BASE node itself */
2363251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2364251881Speter                                    STMT_DELETE_BASE_NODE));
2365251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2366251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2367251881Speter
2368251881Speter  SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2369251881Speter                                           scratch_pool));
2370251881Speter
2371251881Speter  /* Step 6: Delete actual node if we don't keep working */
2372251881Speter  if (! keep_working)
2373251881Speter    {
2374251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2375251881Speter                                        STMT_DELETE_ACTUAL_NODE));
2376251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2377251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2378251881Speter    }
2379251881Speter
2380251881Speter  if (SVN_IS_VALID_REVNUM(not_present_revision))
2381251881Speter    {
2382251881Speter      struct insert_base_baton_t ibb;
2383251881Speter      blank_ibb(&ibb);
2384251881Speter
2385251881Speter      ibb.repos_id = repos_id;
2386251881Speter      ibb.status = svn_wc__db_status_not_present;
2387251881Speter      ibb.kind = kind;
2388251881Speter      ibb.repos_relpath = repos_relpath;
2389251881Speter      ibb.revision = not_present_revision;
2390251881Speter
2391251881Speter      /* Depending upon KIND, any of these might get used. */
2392251881Speter      ibb.children = NULL;
2393251881Speter      ibb.depth = svn_depth_unknown;
2394251881Speter      ibb.checksum = NULL;
2395251881Speter      ibb.target = NULL;
2396251881Speter
2397251881Speter      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2398251881Speter    }
2399251881Speter
2400251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2401251881Speter  if (conflict)
2402251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2403251881Speter                                              conflict, scratch_pool));
2404251881Speter
2405251881Speter  return SVN_NO_ERROR;
2406251881Speter}
2407251881Speter
2408251881Speter
2409251881Spetersvn_error_t *
2410251881Spetersvn_wc__db_base_remove(svn_wc__db_t *db,
2411251881Speter                       const char *local_abspath,
2412251881Speter                       svn_boolean_t keep_as_working,
2413251881Speter                       svn_boolean_t queue_deletes,
2414253734Speter                       svn_boolean_t remove_locks,
2415251881Speter                       svn_revnum_t not_present_revision,
2416251881Speter                       svn_skel_t *conflict,
2417251881Speter                       svn_skel_t *work_items,
2418251881Speter                       apr_pool_t *scratch_pool)
2419251881Speter{
2420251881Speter  svn_wc__db_wcroot_t *wcroot;
2421251881Speter  const char *local_relpath;
2422251881Speter
2423251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2424251881Speter
2425251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2426251881Speter                              local_abspath, scratch_pool, scratch_pool));
2427251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2428251881Speter
2429251881Speter  SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2430251881Speter                                     db, keep_as_working, queue_deletes,
2431253734Speter                                     remove_locks, not_present_revision,
2432251881Speter                                     conflict, work_items, scratch_pool),
2433251881Speter                      wcroot);
2434251881Speter
2435251881Speter  /* If this used to be a directory we should remove children so pass
2436251881Speter   * depth infinity. */
2437251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2438251881Speter                        scratch_pool));
2439251881Speter
2440251881Speter  return SVN_NO_ERROR;
2441251881Speter}
2442251881Speter
2443251881Speter
2444251881Spetersvn_error_t *
2445251881Spetersvn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2446251881Speter                                  svn_node_kind_t *kind,
2447251881Speter                                  svn_revnum_t *revision,
2448251881Speter                                  const char **repos_relpath,
2449251881Speter                                  apr_int64_t *repos_id,
2450251881Speter                                  svn_revnum_t *changed_rev,
2451251881Speter                                  apr_time_t *changed_date,
2452251881Speter                                  const char **changed_author,
2453251881Speter                                  svn_depth_t *depth,
2454251881Speter                                  const svn_checksum_t **checksum,
2455251881Speter                                  const char **target,
2456251881Speter                                  svn_wc__db_lock_t **lock,
2457251881Speter                                  svn_boolean_t *had_props,
2458251881Speter                                  apr_hash_t **props,
2459251881Speter                                  svn_boolean_t *update_root,
2460251881Speter                                  svn_wc__db_wcroot_t *wcroot,
2461251881Speter                                  const char *local_relpath,
2462251881Speter                                  apr_pool_t *result_pool,
2463251881Speter                                  apr_pool_t *scratch_pool)
2464251881Speter{
2465251881Speter  svn_sqlite__stmt_t *stmt;
2466251881Speter  svn_boolean_t have_row;
2467251881Speter  svn_error_t *err = SVN_NO_ERROR;
2468251881Speter
2469251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2470251881Speter                                    lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2471251881Speter                                         : STMT_SELECT_BASE_NODE));
2472251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2473251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2474251881Speter
2475251881Speter  if (have_row)
2476251881Speter    {
2477251881Speter      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2478251881Speter                                                                 presence_map);
2479251881Speter      svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2480251881Speter
2481251881Speter      if (kind)
2482251881Speter        {
2483251881Speter          *kind = node_kind;
2484251881Speter        }
2485251881Speter      if (status)
2486251881Speter        {
2487251881Speter          *status = node_status;
2488251881Speter        }
2489251881Speter      repos_location_from_columns(repos_id, revision, repos_relpath,
2490251881Speter                                  stmt, 0, 4, 1, result_pool);
2491251881Speter      SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2492251881Speter      SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2493251881Speter      if (lock)
2494251881Speter        {
2495251881Speter          *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2496251881Speter        }
2497251881Speter      if (changed_rev)
2498251881Speter        {
2499251881Speter          *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2500251881Speter        }
2501251881Speter      if (changed_date)
2502251881Speter        {
2503251881Speter          *changed_date = svn_sqlite__column_int64(stmt, 8);
2504251881Speter        }
2505251881Speter      if (changed_author)
2506251881Speter        {
2507251881Speter          /* Result may be NULL. */
2508251881Speter          *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2509251881Speter        }
2510251881Speter      if (depth)
2511251881Speter        {
2512251881Speter          if (node_kind != svn_node_dir)
2513251881Speter            {
2514251881Speter              *depth = svn_depth_unknown;
2515251881Speter            }
2516251881Speter          else
2517251881Speter            {
2518251881Speter              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2519251881Speter                                                     svn_depth_unknown);
2520251881Speter            }
2521251881Speter        }
2522251881Speter      if (checksum)
2523251881Speter        {
2524251881Speter          if (node_kind != svn_node_file)
2525251881Speter            {
2526251881Speter              *checksum = NULL;
2527251881Speter            }
2528251881Speter          else
2529251881Speter            {
2530251881Speter              err = svn_sqlite__column_checksum(checksum, stmt, 5,
2531251881Speter                                                result_pool);
2532251881Speter              if (err != NULL)
2533251881Speter                err = svn_error_createf(
2534251881Speter                        err->apr_err, err,
2535251881Speter                        _("The node '%s' has a corrupt checksum value."),
2536251881Speter                        path_for_error_message(wcroot, local_relpath,
2537251881Speter                                               scratch_pool));
2538251881Speter            }
2539251881Speter        }
2540251881Speter      if (target)
2541251881Speter        {
2542251881Speter          if (node_kind != svn_node_symlink)
2543251881Speter            *target = NULL;
2544251881Speter          else
2545251881Speter            *target = svn_sqlite__column_text(stmt, 11, result_pool);
2546251881Speter        }
2547251881Speter      if (had_props)
2548251881Speter        {
2549251881Speter          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2550251881Speter        }
2551251881Speter      if (props)
2552251881Speter        {
2553251881Speter          if (node_status == svn_wc__db_status_normal
2554251881Speter              || node_status == svn_wc__db_status_incomplete)
2555251881Speter            {
2556251881Speter              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2557251881Speter                                                    result_pool, scratch_pool));
2558251881Speter              if (*props == NULL)
2559251881Speter                *props = apr_hash_make(result_pool);
2560251881Speter            }
2561251881Speter          else
2562251881Speter            {
2563251881Speter              assert(svn_sqlite__column_is_null(stmt, 13));
2564251881Speter              *props = NULL;
2565251881Speter            }
2566251881Speter        }
2567251881Speter      if (update_root)
2568251881Speter        {
2569251881Speter          /* It's an update root iff it's a file external. */
2570251881Speter          *update_root = svn_sqlite__column_boolean(stmt, 14);
2571251881Speter        }
2572251881Speter    }
2573251881Speter  else
2574251881Speter    {
2575251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2576251881Speter                              _("The node '%s' was not found."),
2577251881Speter                              path_for_error_message(wcroot, local_relpath,
2578251881Speter                                                     scratch_pool));
2579251881Speter    }
2580251881Speter
2581251881Speter  /* Note: given the composition, no need to wrap for tracing.  */
2582251881Speter  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2583251881Speter}
2584251881Speter
2585251881Speter
2586251881Spetersvn_error_t *
2587251881Spetersvn_wc__db_base_get_info(svn_wc__db_status_t *status,
2588251881Speter                         svn_node_kind_t *kind,
2589251881Speter                         svn_revnum_t *revision,
2590251881Speter                         const char **repos_relpath,
2591251881Speter                         const char **repos_root_url,
2592251881Speter                         const char **repos_uuid,
2593251881Speter                         svn_revnum_t *changed_rev,
2594251881Speter                         apr_time_t *changed_date,
2595251881Speter                         const char **changed_author,
2596251881Speter                         svn_depth_t *depth,
2597251881Speter                         const svn_checksum_t **checksum,
2598251881Speter                         const char **target,
2599251881Speter                         svn_wc__db_lock_t **lock,
2600251881Speter                         svn_boolean_t *had_props,
2601251881Speter                         apr_hash_t **props,
2602251881Speter                         svn_boolean_t *update_root,
2603251881Speter                         svn_wc__db_t *db,
2604251881Speter                         const char *local_abspath,
2605251881Speter                         apr_pool_t *result_pool,
2606251881Speter                         apr_pool_t *scratch_pool)
2607251881Speter{
2608251881Speter  svn_wc__db_wcroot_t *wcroot;
2609251881Speter  const char *local_relpath;
2610251881Speter  apr_int64_t repos_id;
2611251881Speter
2612251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2613251881Speter
2614251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2615251881Speter                              local_abspath, scratch_pool, scratch_pool));
2616251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2617251881Speter
2618251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
2619251881Speter                                            repos_relpath, &repos_id,
2620251881Speter                                            changed_rev, changed_date,
2621251881Speter                                            changed_author, depth,
2622251881Speter                                            checksum, target, lock,
2623251881Speter                                            had_props, props, update_root,
2624251881Speter                                            wcroot, local_relpath,
2625251881Speter                                            result_pool, scratch_pool));
2626251881Speter  SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2627251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2628251881Speter                                      wcroot->sdb, repos_id, result_pool));
2629251881Speter
2630251881Speter  return SVN_NO_ERROR;
2631251881Speter}
2632251881Speter
2633251881Spetersvn_error_t *
2634251881Spetersvn_wc__db_base_get_children_info(apr_hash_t **nodes,
2635251881Speter                                  svn_wc__db_t *db,
2636251881Speter                                  const char *dir_abspath,
2637251881Speter                                  apr_pool_t *result_pool,
2638251881Speter                                  apr_pool_t *scratch_pool)
2639251881Speter{
2640251881Speter  svn_wc__db_wcroot_t *wcroot;
2641251881Speter  const char *local_relpath;
2642251881Speter  svn_sqlite__stmt_t *stmt;
2643251881Speter  svn_boolean_t have_row;
2644251881Speter
2645251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2646251881Speter
2647251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2648251881Speter                              dir_abspath, scratch_pool, scratch_pool));
2649251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2650251881Speter
2651251881Speter  *nodes = apr_hash_make(result_pool);
2652251881Speter
2653251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2654251881Speter                                    STMT_SELECT_BASE_CHILDREN_INFO));
2655251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2656251881Speter
2657251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2658251881Speter
2659251881Speter  while (have_row)
2660251881Speter    {
2661251881Speter      struct svn_wc__db_base_info_t *info;
2662251881Speter      svn_error_t *err;
2663251881Speter      apr_int64_t repos_id;
2664251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2665251881Speter      const char *name = svn_relpath_basename(child_relpath, result_pool);
2666251881Speter
2667251881Speter      info = apr_pcalloc(result_pool, sizeof(*info));
2668251881Speter
2669251881Speter      repos_id = svn_sqlite__column_int64(stmt, 1);
2670251881Speter      info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2671251881Speter      info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2672251881Speter      info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2673251881Speter      info->revnum = svn_sqlite__column_revnum(stmt, 5);
2674251881Speter
2675251881Speter      info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2676251881Speter                                                  svn_depth_unknown);
2677251881Speter
2678251881Speter      info->update_root = svn_sqlite__column_boolean(stmt, 7);
2679251881Speter
2680251881Speter      info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2681251881Speter
2682251881Speter      err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2683251881Speter                                        wcroot->sdb, repos_id, result_pool);
2684251881Speter
2685251881Speter      if (err)
2686251881Speter        return svn_error_trace(
2687251881Speter                 svn_error_compose_create(err,
2688251881Speter                                          svn_sqlite__reset(stmt)));
2689251881Speter
2690251881Speter
2691251881Speter      svn_hash_sets(*nodes, name, info);
2692251881Speter
2693251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2694251881Speter    }
2695251881Speter
2696251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
2697251881Speter
2698251881Speter  return SVN_NO_ERROR;
2699251881Speter}
2700251881Speter
2701251881Speter
2702251881Spetersvn_error_t *
2703251881Spetersvn_wc__db_base_get_props(apr_hash_t **props,
2704251881Speter                          svn_wc__db_t *db,
2705251881Speter                          const char *local_abspath,
2706251881Speter                          apr_pool_t *result_pool,
2707251881Speter                          apr_pool_t *scratch_pool)
2708251881Speter{
2709251881Speter  svn_wc__db_status_t presence;
2710251881Speter
2711251881Speter  SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2712251881Speter                                   NULL, NULL, NULL, NULL, NULL,
2713251881Speter                                   NULL, NULL, NULL, NULL, props, NULL,
2714251881Speter                                   db, local_abspath,
2715251881Speter                                   result_pool, scratch_pool));
2716251881Speter  if (presence != svn_wc__db_status_normal
2717251881Speter      && presence != svn_wc__db_status_incomplete)
2718251881Speter    {
2719251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2720251881Speter                               _("The node '%s' has a BASE status that"
2721251881Speter                                  " has no properties."),
2722251881Speter                               svn_dirent_local_style(local_abspath,
2723251881Speter                                                      scratch_pool));
2724251881Speter    }
2725251881Speter
2726251881Speter  return SVN_NO_ERROR;
2727251881Speter}
2728251881Speter
2729251881Speter
2730251881Spetersvn_error_t *
2731251881Spetersvn_wc__db_base_get_children(const apr_array_header_t **children,
2732251881Speter                             svn_wc__db_t *db,
2733251881Speter                             const char *local_abspath,
2734251881Speter                             apr_pool_t *result_pool,
2735251881Speter                             apr_pool_t *scratch_pool)
2736251881Speter{
2737251881Speter  svn_wc__db_wcroot_t *wcroot;
2738251881Speter  const char *local_relpath;
2739251881Speter
2740251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2741251881Speter
2742251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2743251881Speter                                             local_abspath,
2744251881Speter                                             scratch_pool, scratch_pool));
2745251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2746251881Speter
2747251881Speter  return gather_repo_children(children, wcroot, local_relpath, 0,
2748251881Speter                              result_pool, scratch_pool);
2749251881Speter}
2750251881Speter
2751251881Speter
2752251881Spetersvn_error_t *
2753251881Spetersvn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2754251881Speter                              const char *local_abspath,
2755251881Speter                              const apr_hash_t *props,
2756251881Speter                              apr_pool_t *scratch_pool)
2757251881Speter{
2758251881Speter  svn_sqlite__stmt_t *stmt;
2759251881Speter  int affected_rows;
2760251881Speter
2761251881Speter  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2762251881Speter                                 STMT_UPDATE_BASE_NODE_DAV_CACHE,
2763251881Speter                                 scratch_pool));
2764251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2765251881Speter
2766251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2767251881Speter
2768251881Speter  if (affected_rows != 1)
2769251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2770251881Speter                             _("The node '%s' was not found."),
2771251881Speter                             svn_dirent_local_style(local_abspath,
2772251881Speter                                                    scratch_pool));
2773251881Speter
2774251881Speter  return SVN_NO_ERROR;
2775251881Speter}
2776251881Speter
2777251881Speter
2778251881Spetersvn_error_t *
2779251881Spetersvn_wc__db_base_get_dav_cache(apr_hash_t **props,
2780251881Speter                              svn_wc__db_t *db,
2781251881Speter                              const char *local_abspath,
2782251881Speter                              apr_pool_t *result_pool,
2783251881Speter                              apr_pool_t *scratch_pool)
2784251881Speter{
2785251881Speter  svn_sqlite__stmt_t *stmt;
2786251881Speter  svn_boolean_t have_row;
2787251881Speter
2788251881Speter  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2789251881Speter                                 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2790251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2791251881Speter  if (!have_row)
2792251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2793251881Speter                             svn_sqlite__reset(stmt),
2794251881Speter                             _("The node '%s' was not found."),
2795251881Speter                             svn_dirent_local_style(local_abspath,
2796251881Speter                                                    scratch_pool));
2797251881Speter
2798251881Speter  SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2799251881Speter                                        scratch_pool));
2800251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
2801251881Speter}
2802251881Speter
2803251881Speter
2804251881Spetersvn_error_t *
2805251881Spetersvn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2806251881Speter                                          const char *local_abspath,
2807251881Speter                                          apr_pool_t *scratch_pool)
2808251881Speter{
2809251881Speter  svn_wc__db_wcroot_t *wcroot;
2810251881Speter  const char *local_relpath;
2811251881Speter  svn_sqlite__stmt_t *stmt;
2812251881Speter
2813251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2814251881Speter                                             db, local_abspath,
2815251881Speter                                             scratch_pool, scratch_pool));
2816251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2817251881Speter
2818251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2819251881Speter                                    STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2820251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2821251881Speter
2822251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2823251881Speter
2824251881Speter  return SVN_NO_ERROR;
2825251881Speter}
2826251881Speter
2827251881Speter
2828251881Spetersvn_error_t *
2829251881Spetersvn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2830251881Speter                          svn_node_kind_t *kind,
2831251881Speter                          svn_revnum_t *revision,
2832251881Speter                          const char **repos_relpath,
2833251881Speter                          apr_int64_t *repos_id,
2834251881Speter                          svn_revnum_t *changed_rev,
2835251881Speter                          apr_time_t *changed_date,
2836251881Speter                          const char **changed_author,
2837251881Speter                          svn_depth_t *depth,
2838251881Speter                          const svn_checksum_t **checksum,
2839251881Speter                          const char **target,
2840251881Speter                          svn_boolean_t *had_props,
2841251881Speter                          apr_hash_t **props,
2842251881Speter                          svn_wc__db_wcroot_t *wcroot,
2843251881Speter                          const char *local_relpath,
2844251881Speter                          int op_depth,
2845251881Speter                          apr_pool_t *result_pool,
2846251881Speter                          apr_pool_t *scratch_pool)
2847251881Speter{
2848251881Speter  svn_sqlite__stmt_t *stmt;
2849251881Speter  svn_boolean_t have_row;
2850251881Speter  svn_error_t *err = SVN_NO_ERROR;
2851251881Speter
2852251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2853251881Speter                                    STMT_SELECT_DEPTH_NODE));
2854251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2855251881Speter                            wcroot->wc_id, local_relpath, op_depth));
2856251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2857251881Speter
2858251881Speter  if (have_row)
2859251881Speter    {
2860251881Speter      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2861251881Speter                                                                 presence_map);
2862251881Speter      svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2863251881Speter
2864251881Speter      if (kind)
2865251881Speter        {
2866251881Speter          *kind = node_kind;
2867251881Speter        }
2868251881Speter      if (status)
2869251881Speter        {
2870251881Speter          *status = node_status;
2871251881Speter
2872251881Speter          if (op_depth > 0)
2873251881Speter            SVN_ERR(convert_to_working_status(status, *status));
2874251881Speter        }
2875251881Speter      repos_location_from_columns(repos_id, revision, repos_relpath,
2876251881Speter                                  stmt, 0, 4, 1, result_pool);
2877251881Speter
2878251881Speter      if (changed_rev)
2879251881Speter        {
2880251881Speter          *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2881251881Speter        }
2882251881Speter      if (changed_date)
2883251881Speter        {
2884251881Speter          *changed_date = svn_sqlite__column_int64(stmt, 8);
2885251881Speter        }
2886251881Speter      if (changed_author)
2887251881Speter        {
2888251881Speter          /* Result may be NULL. */
2889251881Speter          *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2890251881Speter        }
2891251881Speter      if (depth)
2892251881Speter        {
2893251881Speter          if (node_kind != svn_node_dir)
2894251881Speter            {
2895251881Speter              *depth = svn_depth_unknown;
2896251881Speter            }
2897251881Speter          else
2898251881Speter            {
2899251881Speter              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2900251881Speter                                                     svn_depth_unknown);
2901251881Speter            }
2902251881Speter        }
2903251881Speter      if (checksum)
2904251881Speter        {
2905251881Speter          if (node_kind != svn_node_file)
2906251881Speter            {
2907251881Speter              *checksum = NULL;
2908251881Speter            }
2909251881Speter          else
2910251881Speter            {
2911251881Speter              err = svn_sqlite__column_checksum(checksum, stmt, 5,
2912251881Speter                                                result_pool);
2913251881Speter              if (err != NULL)
2914251881Speter                err = svn_error_createf(
2915251881Speter                        err->apr_err, err,
2916251881Speter                        _("The node '%s' has a corrupt checksum value."),
2917251881Speter                        path_for_error_message(wcroot, local_relpath,
2918251881Speter                                               scratch_pool));
2919251881Speter            }
2920251881Speter        }
2921251881Speter      if (target)
2922251881Speter        {
2923251881Speter          if (node_kind != svn_node_symlink)
2924251881Speter            *target = NULL;
2925251881Speter          else
2926251881Speter            *target = svn_sqlite__column_text(stmt, 11, result_pool);
2927251881Speter        }
2928251881Speter      if (had_props)
2929251881Speter        {
2930251881Speter          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2931251881Speter        }
2932251881Speter      if (props)
2933251881Speter        {
2934251881Speter          if (node_status == svn_wc__db_status_normal
2935251881Speter              || node_status == svn_wc__db_status_incomplete)
2936251881Speter            {
2937251881Speter              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2938251881Speter                                                    result_pool, scratch_pool));
2939251881Speter              if (*props == NULL)
2940251881Speter                *props = apr_hash_make(result_pool);
2941251881Speter            }
2942251881Speter          else
2943251881Speter            {
2944251881Speter              assert(svn_sqlite__column_is_null(stmt, 13));
2945251881Speter              *props = NULL;
2946251881Speter            }
2947251881Speter        }
2948251881Speter    }
2949251881Speter  else
2950251881Speter    {
2951251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2952251881Speter                              _("The node '%s' was not found."),
2953251881Speter                              path_for_error_message(wcroot, local_relpath,
2954251881Speter                                                     scratch_pool));
2955251881Speter    }
2956251881Speter
2957251881Speter  /* Note: given the composition, no need to wrap for tracing.  */
2958251881Speter  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2959251881Speter}
2960251881Speter
2961251881Speter
2962251881Speter/* Baton for passing args to with_triggers(). */
2963251881Speterstruct with_triggers_baton_t {
2964251881Speter  int create_trigger;
2965251881Speter  int drop_trigger;
2966251881Speter  svn_wc__db_txn_callback_t cb_func;
2967251881Speter  void *cb_baton;
2968251881Speter};
2969251881Speter
2970251881Speter/* Helper for creating SQLite triggers, running the main transaction
2971251881Speter   callback, and then dropping the triggers.  It guarantees that the
2972251881Speter   triggers will not survive the transaction.  This could be used for
2973251881Speter   any general prefix/postscript statements where the postscript
2974251881Speter   *must* be executed if the transaction completes.
2975251881Speter
2976251881Speter   Implements svn_wc__db_txn_callback_t. */
2977251881Speterstatic svn_error_t *
2978251881Speterwith_triggers(void *baton,
2979251881Speter              svn_wc__db_wcroot_t *wcroot,
2980251881Speter              const char *local_relpath,
2981251881Speter              apr_pool_t *scratch_pool)
2982251881Speter{
2983251881Speter  struct with_triggers_baton_t *b = baton;
2984251881Speter  svn_error_t *err1;
2985251881Speter  svn_error_t *err2;
2986251881Speter
2987251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
2988251881Speter
2989251881Speter  err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
2990251881Speter
2991251881Speter  err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
2992251881Speter
2993251881Speter  return svn_error_trace(svn_error_compose_create(err1, err2));
2994251881Speter}
2995251881Speter
2996251881Speter
2997251881Speter/* Prototype for the "work callback" used by with_finalization().  */
2998251881Spetertypedef svn_error_t * (*work_callback_t)(
2999251881Speter                          void *baton,
3000251881Speter                          svn_wc__db_wcroot_t *wcroot,
3001251881Speter                          svn_cancel_func_t cancel_func,
3002251881Speter                          void *cancel_baton,
3003251881Speter                          svn_wc_notify_func2_t notify_func,
3004251881Speter                          void *notify_baton,
3005251881Speter                          apr_pool_t *scratch_pool);
3006251881Speter
3007251881Speter/* Utility function to provide several features, with a guaranteed
3008251881Speter   finalization (ie. to drop temporary tables).
3009251881Speter
3010251881Speter   1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3011251881Speter      sqlite transaction
3012251881Speter   2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3013251881Speter      the "work" step: WORK_CB(WORK_BATON).
3014251881Speter   3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3015251881Speter      from the above two steps.
3016251881Speter
3017251881Speter   CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3018251881Speter   typical values. These are passed to the work callback, which typically
3019251881Speter   provides notification about the work done by TXN_CB.  */
3020251881Speterstatic svn_error_t *
3021251881Speterwith_finalization(svn_wc__db_wcroot_t *wcroot,
3022251881Speter                  const char *local_relpath,
3023251881Speter                  svn_wc__db_txn_callback_t txn_cb,
3024251881Speter                  void *txn_baton,
3025251881Speter                  work_callback_t work_cb,
3026251881Speter                  void *work_baton,
3027251881Speter                  svn_cancel_func_t cancel_func,
3028251881Speter                  void *cancel_baton,
3029251881Speter                  svn_wc_notify_func2_t notify_func,
3030251881Speter                  void *notify_baton,
3031251881Speter                  int finalize_stmt_idx,
3032251881Speter                  apr_pool_t *scratch_pool)
3033251881Speter{
3034251881Speter  svn_error_t *err1;
3035251881Speter  svn_error_t *err2;
3036251881Speter
3037251881Speter  err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
3038251881Speter                             scratch_pool);
3039251881Speter
3040251881Speter  if (err1 == NULL && notify_func != NULL)
3041251881Speter    {
3042251881Speter      err2 = work_cb(work_baton, wcroot,
3043251881Speter                     cancel_func, cancel_baton,
3044251881Speter                     notify_func, notify_baton,
3045251881Speter                     scratch_pool);
3046251881Speter      err1 = svn_error_compose_create(err1, err2);
3047251881Speter    }
3048251881Speter
3049251881Speter  err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3050251881Speter
3051251881Speter  return svn_error_trace(svn_error_compose_create(err1, err2));
3052251881Speter}
3053251881Speter
3054251881Speter
3055251881Speter/* Initialize the baton with appropriate "blank" values. This allows the
3056251881Speter   insertion function to leave certain columns null.  */
3057251881Speterstatic void
3058251881Speterblank_ieb(insert_external_baton_t *ieb)
3059251881Speter{
3060251881Speter  memset(ieb, 0, sizeof(*ieb));
3061251881Speter  ieb->revision = SVN_INVALID_REVNUM;
3062251881Speter  ieb->changed_rev = SVN_INVALID_REVNUM;
3063251881Speter  ieb->repos_id = INVALID_REPOS_ID;
3064251881Speter
3065251881Speter  ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3066251881Speter  ieb->recorded_revision = SVN_INVALID_REVNUM;
3067251881Speter}
3068251881Speter
3069251881Speter/* Insert the externals row represented by (insert_external_baton_t *) BATON.
3070251881Speter *
3071251881Speter * Implements svn_wc__db_txn_callback_t. */
3072251881Speterstatic svn_error_t *
3073251881Speterinsert_external_node(const insert_external_baton_t *ieb,
3074251881Speter                     svn_wc__db_wcroot_t *wcroot,
3075251881Speter                     const char *local_relpath,
3076251881Speter                     apr_pool_t *scratch_pool)
3077251881Speter{
3078251881Speter  svn_wc__db_status_t status;
3079251881Speter  svn_error_t *err;
3080251881Speter  svn_boolean_t update_root;
3081251881Speter  apr_int64_t repos_id;
3082251881Speter  svn_sqlite__stmt_t *stmt;
3083251881Speter
3084251881Speter  if (ieb->repos_id != INVALID_REPOS_ID)
3085251881Speter    repos_id = ieb->repos_id;
3086251881Speter  else
3087251881Speter    SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3088251881Speter                            wcroot->sdb, scratch_pool));
3089251881Speter
3090251881Speter  /* And there must be no existing BASE node or it must be a file external */
3091251881Speter  err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3092251881Speter                                          NULL, NULL, NULL, NULL, NULL,
3093251881Speter                                          NULL, NULL, NULL, NULL, &update_root,
3094251881Speter                                          wcroot, local_relpath,
3095251881Speter                                          scratch_pool, scratch_pool);
3096251881Speter  if (err)
3097251881Speter    {
3098251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3099251881Speter        return svn_error_trace(err);
3100251881Speter
3101251881Speter      svn_error_clear(err);
3102251881Speter    }
3103251881Speter  else if (status == svn_wc__db_status_normal && !update_root)
3104251881Speter    return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3105251881Speter
3106251881Speter  if (ieb->kind == svn_node_file
3107251881Speter      || ieb->kind == svn_node_symlink)
3108251881Speter    {
3109251881Speter      struct insert_base_baton_t ibb;
3110251881Speter
3111251881Speter      blank_ibb(&ibb);
3112251881Speter
3113251881Speter      ibb.status          = svn_wc__db_status_normal;
3114251881Speter      ibb.kind            = ieb->kind;
3115251881Speter
3116251881Speter      ibb.repos_id        = repos_id;
3117251881Speter      ibb.repos_relpath   = ieb->repos_relpath;
3118251881Speter      ibb.revision        = ieb->revision;
3119251881Speter
3120251881Speter      ibb.props           = ieb->props;
3121251881Speter      ibb.iprops          = ieb->iprops;
3122251881Speter      ibb.changed_rev     = ieb->changed_rev;
3123251881Speter      ibb.changed_date    = ieb->changed_date;
3124251881Speter      ibb.changed_author  = ieb->changed_author;
3125251881Speter
3126251881Speter      ibb.dav_cache       = ieb->dav_cache;
3127251881Speter
3128251881Speter      ibb.checksum        = ieb->checksum;
3129251881Speter      ibb.target          = ieb->target;
3130251881Speter
3131251881Speter      ibb.conflict        = ieb->conflict;
3132251881Speter
3133251881Speter      ibb.update_actual_props = ieb->update_actual_props;
3134251881Speter      ibb.new_actual_props    = ieb->new_actual_props;
3135251881Speter
3136251881Speter      ibb.keep_recorded_info  = ieb->keep_recorded_info;
3137251881Speter
3138251881Speter      ibb.work_items      = ieb->work_items;
3139251881Speter
3140251881Speter      ibb.file_external = TRUE;
3141251881Speter
3142251881Speter      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3143251881Speter    }
3144251881Speter  else
3145251881Speter    SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3146251881Speter
3147251881Speter  /* The externals table only support presence normal and excluded */
3148251881Speter  SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3149251881Speter                 || ieb->presence == svn_wc__db_status_excluded);
3150251881Speter
3151251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3152251881Speter
3153251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3154251881Speter                            wcroot->wc_id,
3155251881Speter                            local_relpath,
3156251881Speter                            svn_relpath_dirname(local_relpath,
3157251881Speter                                                scratch_pool),
3158251881Speter                            presence_map, ieb->presence,
3159251881Speter                            kind_map, ieb->kind,
3160251881Speter                            ieb->record_ancestor_relpath,
3161251881Speter                            repos_id,
3162251881Speter                            ieb->recorded_repos_relpath));
3163251881Speter
3164251881Speter  if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3165251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3166251881Speter
3167251881Speter  if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3168251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3169251881Speter
3170251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
3171251881Speter
3172251881Speter  return SVN_NO_ERROR;
3173251881Speter}
3174251881Speter
3175251881Spetersvn_error_t *
3176251881Spetersvn_wc__db_external_add_file(svn_wc__db_t *db,
3177251881Speter                             const char *local_abspath,
3178251881Speter                             const char *wri_abspath,
3179251881Speter
3180251881Speter                             const char *repos_relpath,
3181251881Speter                             const char *repos_root_url,
3182251881Speter                             const char *repos_uuid,
3183251881Speter                             svn_revnum_t revision,
3184251881Speter
3185251881Speter                             const apr_hash_t *props,
3186251881Speter                             apr_array_header_t *iprops,
3187251881Speter
3188251881Speter                             svn_revnum_t changed_rev,
3189251881Speter                             apr_time_t changed_date,
3190251881Speter                             const char *changed_author,
3191251881Speter
3192251881Speter                             const svn_checksum_t *checksum,
3193251881Speter
3194251881Speter                             const apr_hash_t *dav_cache,
3195251881Speter
3196251881Speter                             const char *record_ancestor_abspath,
3197251881Speter                             const char *recorded_repos_relpath,
3198251881Speter                             svn_revnum_t recorded_peg_revision,
3199251881Speter                             svn_revnum_t recorded_revision,
3200251881Speter
3201251881Speter                             svn_boolean_t update_actual_props,
3202251881Speter                             apr_hash_t *new_actual_props,
3203251881Speter
3204251881Speter                             svn_boolean_t keep_recorded_info,
3205251881Speter                             const svn_skel_t *conflict,
3206251881Speter                             const svn_skel_t *work_items,
3207251881Speter                             apr_pool_t *scratch_pool)
3208251881Speter{
3209251881Speter  svn_wc__db_wcroot_t *wcroot;
3210251881Speter  const char *local_relpath;
3211251881Speter  insert_external_baton_t ieb;
3212251881Speter
3213251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3214251881Speter
3215251881Speter  if (! wri_abspath)
3216251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3217251881Speter
3218251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3219251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3220251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3221251881Speter
3222251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3223251881Speter                                        record_ancestor_abspath));
3224251881Speter
3225251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3226251881Speter
3227251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3228251881Speter
3229251881Speter  blank_ieb(&ieb);
3230251881Speter
3231251881Speter  ieb.kind = svn_node_file;
3232251881Speter  ieb.presence = svn_wc__db_status_normal;
3233251881Speter
3234251881Speter  ieb.repos_root_url = repos_root_url;
3235251881Speter  ieb.repos_uuid = repos_uuid;
3236251881Speter
3237251881Speter  ieb.repos_relpath = repos_relpath;
3238251881Speter  ieb.revision = revision;
3239251881Speter
3240251881Speter  ieb.props = props;
3241251881Speter  ieb.iprops = iprops;
3242251881Speter
3243251881Speter  ieb.changed_rev = changed_rev;
3244251881Speter  ieb.changed_date = changed_date;
3245251881Speter  ieb.changed_author = changed_author;
3246251881Speter
3247251881Speter  ieb.checksum = checksum;
3248251881Speter
3249251881Speter  ieb.dav_cache = dav_cache;
3250251881Speter
3251251881Speter  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3252251881Speter                                                wcroot->abspath,
3253251881Speter                                                record_ancestor_abspath);
3254251881Speter  ieb.recorded_repos_relpath = recorded_repos_relpath;
3255251881Speter  ieb.recorded_peg_revision = recorded_peg_revision;
3256251881Speter  ieb.recorded_revision = recorded_revision;
3257251881Speter
3258251881Speter  ieb.update_actual_props = update_actual_props;
3259251881Speter  ieb.new_actual_props = new_actual_props;
3260251881Speter
3261251881Speter  ieb.keep_recorded_info = keep_recorded_info;
3262251881Speter
3263251881Speter  ieb.conflict = conflict;
3264251881Speter  ieb.work_items = work_items;
3265251881Speter
3266251881Speter  SVN_WC__DB_WITH_TXN(
3267251881Speter            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3268251881Speter            wcroot);
3269251881Speter
3270251881Speter  return SVN_NO_ERROR;
3271251881Speter}
3272251881Speter
3273251881Spetersvn_error_t *
3274251881Spetersvn_wc__db_external_add_symlink(svn_wc__db_t *db,
3275251881Speter                                const char *local_abspath,
3276251881Speter                                const char *wri_abspath,
3277251881Speter                                const char *repos_relpath,
3278251881Speter                                const char *repos_root_url,
3279251881Speter                                const char *repos_uuid,
3280251881Speter                                svn_revnum_t revision,
3281251881Speter                                const apr_hash_t *props,
3282251881Speter                                svn_revnum_t changed_rev,
3283251881Speter                                apr_time_t changed_date,
3284251881Speter                                const char *changed_author,
3285251881Speter                                const char *target,
3286251881Speter                                const apr_hash_t *dav_cache,
3287251881Speter                                const char *record_ancestor_abspath,
3288251881Speter                                const char *recorded_repos_relpath,
3289251881Speter                                svn_revnum_t recorded_peg_revision,
3290251881Speter                                svn_revnum_t recorded_revision,
3291251881Speter                                svn_boolean_t update_actual_props,
3292251881Speter                                apr_hash_t *new_actual_props,
3293251881Speter                                svn_boolean_t keep_recorded_info,
3294251881Speter                                const svn_skel_t *work_items,
3295251881Speter                                apr_pool_t *scratch_pool)
3296251881Speter{
3297251881Speter  svn_wc__db_wcroot_t *wcroot;
3298251881Speter  const char *local_relpath;
3299251881Speter  insert_external_baton_t ieb;
3300251881Speter
3301251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3302251881Speter
3303251881Speter  if (! wri_abspath)
3304251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3305251881Speter
3306251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3307251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3308251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3309251881Speter
3310251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3311251881Speter                                        record_ancestor_abspath));
3312251881Speter
3313251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3314251881Speter
3315251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3316251881Speter
3317251881Speter  blank_ieb(&ieb);
3318251881Speter
3319251881Speter  ieb.kind = svn_node_symlink;
3320251881Speter  ieb.presence = svn_wc__db_status_normal;
3321251881Speter
3322251881Speter  ieb.repos_root_url = repos_root_url;
3323251881Speter  ieb.repos_uuid = repos_uuid;
3324251881Speter
3325251881Speter  ieb.repos_relpath = repos_relpath;
3326251881Speter  ieb.revision = revision;
3327251881Speter
3328251881Speter  ieb.props = props;
3329251881Speter
3330251881Speter  ieb.changed_rev = changed_rev;
3331251881Speter  ieb.changed_date = changed_date;
3332251881Speter  ieb.changed_author = changed_author;
3333251881Speter
3334251881Speter  ieb.target = target;
3335251881Speter
3336251881Speter  ieb.dav_cache = dav_cache;
3337251881Speter
3338251881Speter  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3339251881Speter                                                wcroot->abspath,
3340251881Speter                                                record_ancestor_abspath);
3341251881Speter  ieb.recorded_repos_relpath = recorded_repos_relpath;
3342251881Speter  ieb.recorded_peg_revision = recorded_peg_revision;
3343251881Speter  ieb.recorded_revision = recorded_revision;
3344251881Speter
3345251881Speter  ieb.update_actual_props = update_actual_props;
3346251881Speter  ieb.new_actual_props = new_actual_props;
3347251881Speter
3348251881Speter  ieb.keep_recorded_info = keep_recorded_info;
3349251881Speter
3350251881Speter  ieb.work_items = work_items;
3351251881Speter
3352251881Speter  SVN_WC__DB_WITH_TXN(
3353251881Speter            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3354251881Speter            wcroot);
3355251881Speter
3356251881Speter  return SVN_NO_ERROR;
3357251881Speter}
3358251881Speter
3359251881Spetersvn_error_t *
3360251881Spetersvn_wc__db_external_add_dir(svn_wc__db_t *db,
3361251881Speter                            const char *local_abspath,
3362251881Speter                            const char *wri_abspath,
3363251881Speter                            const char *repos_root_url,
3364251881Speter                            const char *repos_uuid,
3365251881Speter                            const char *record_ancestor_abspath,
3366251881Speter                            const char *recorded_repos_relpath,
3367251881Speter                            svn_revnum_t recorded_peg_revision,
3368251881Speter                            svn_revnum_t recorded_revision,
3369251881Speter                            const svn_skel_t *work_items,
3370251881Speter                            apr_pool_t *scratch_pool)
3371251881Speter{
3372251881Speter  svn_wc__db_wcroot_t *wcroot;
3373251881Speter  const char *local_relpath;
3374251881Speter  insert_external_baton_t ieb;
3375251881Speter
3376251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3377251881Speter
3378251881Speter  if (! wri_abspath)
3379251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3380251881Speter
3381251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3382251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3383251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3384251881Speter
3385251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3386251881Speter                                        record_ancestor_abspath));
3387251881Speter
3388251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3389251881Speter
3390251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3391251881Speter
3392251881Speter  blank_ieb(&ieb);
3393251881Speter
3394251881Speter  ieb.kind = svn_node_dir;
3395251881Speter  ieb.presence = svn_wc__db_status_normal;
3396251881Speter
3397251881Speter  ieb.repos_root_url = repos_root_url;
3398251881Speter  ieb.repos_uuid = repos_uuid;
3399251881Speter
3400251881Speter  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3401251881Speter                                                wcroot->abspath,
3402251881Speter                                                record_ancestor_abspath);
3403251881Speter  ieb.recorded_repos_relpath = recorded_repos_relpath;
3404251881Speter  ieb.recorded_peg_revision = recorded_peg_revision;
3405251881Speter  ieb.recorded_revision = recorded_revision;
3406251881Speter
3407251881Speter  ieb.work_items = work_items;
3408251881Speter
3409251881Speter  SVN_WC__DB_WITH_TXN(
3410251881Speter            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3411251881Speter            wcroot);
3412251881Speter
3413251881Speter  return SVN_NO_ERROR;
3414251881Speter}
3415251881Speter
3416251881Speter/* The body of svn_wc__db_external_remove(). */
3417251881Speterstatic svn_error_t *
3418251881Speterdb_external_remove(const svn_skel_t *work_items,
3419251881Speter                   svn_wc__db_wcroot_t *wcroot,
3420251881Speter                   const char *local_relpath,
3421251881Speter                   apr_pool_t *scratch_pool)
3422251881Speter{
3423251881Speter  svn_sqlite__stmt_t *stmt;
3424251881Speter
3425251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3426251881Speter                                    STMT_DELETE_EXTERNAL));
3427251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3428251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
3429251881Speter
3430251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3431251881Speter
3432251881Speter  /* ### What about actual? */
3433251881Speter  return SVN_NO_ERROR;
3434251881Speter}
3435251881Speter
3436251881Spetersvn_error_t *
3437251881Spetersvn_wc__db_external_remove(svn_wc__db_t *db,
3438251881Speter                           const char *local_abspath,
3439251881Speter                           const char *wri_abspath,
3440251881Speter                           const svn_skel_t *work_items,
3441251881Speter                           apr_pool_t *scratch_pool)
3442251881Speter{
3443251881Speter  svn_wc__db_wcroot_t *wcroot;
3444251881Speter  const char *local_relpath;
3445251881Speter
3446251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3447251881Speter
3448251881Speter  if (! wri_abspath)
3449251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3450251881Speter
3451251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3452251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3453251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3454251881Speter
3455251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3456251881Speter
3457251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3458251881Speter
3459251881Speter  SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3460251881Speter                                         scratch_pool),
3461251881Speter                      wcroot);
3462251881Speter
3463251881Speter  return SVN_NO_ERROR;
3464251881Speter}
3465251881Speter
3466251881Spetersvn_error_t *
3467251881Spetersvn_wc__db_external_read(svn_wc__db_status_t *status,
3468251881Speter                         svn_node_kind_t *kind,
3469251881Speter                         const char **definining_abspath,
3470251881Speter                         const char **repos_root_url,
3471251881Speter                         const char **repos_uuid,
3472251881Speter                         const char **recorded_repos_relpath,
3473251881Speter                         svn_revnum_t *recorded_peg_revision,
3474251881Speter                         svn_revnum_t *recorded_revision,
3475251881Speter                         svn_wc__db_t *db,
3476251881Speter                         const char *local_abspath,
3477251881Speter                         const char *wri_abspath,
3478251881Speter                         apr_pool_t *result_pool,
3479251881Speter                         apr_pool_t *scratch_pool)
3480251881Speter{
3481251881Speter  svn_wc__db_wcroot_t *wcroot;
3482251881Speter  const char *local_relpath;
3483251881Speter  svn_sqlite__stmt_t *stmt;
3484251881Speter  svn_boolean_t have_info;
3485251881Speter  svn_error_t *err = NULL;
3486251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3487251881Speter
3488251881Speter  if (! wri_abspath)
3489251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3490251881Speter
3491251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3492251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3493251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3494251881Speter
3495251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3496251881Speter
3497251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3498251881Speter
3499251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3500251881Speter                                    STMT_SELECT_EXTERNAL_INFO));
3501251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3502251881Speter  SVN_ERR(svn_sqlite__step(&have_info, stmt));
3503251881Speter
3504251881Speter  if (have_info)
3505251881Speter    {
3506251881Speter      if (status)
3507251881Speter        *status = svn_sqlite__column_token(stmt, 0, presence_map);
3508251881Speter
3509251881Speter      if (kind)
3510251881Speter        *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3511251881Speter
3512251881Speter      if (definining_abspath)
3513251881Speter        {
3514251881Speter          const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3515251881Speter
3516251881Speter          *definining_abspath = svn_dirent_join(wcroot->abspath,
3517251881Speter                                                record_relpath, result_pool);
3518251881Speter        }
3519251881Speter
3520251881Speter      if (repos_root_url || repos_uuid)
3521251881Speter        {
3522251881Speter          apr_int64_t repos_id;
3523251881Speter
3524251881Speter          repos_id = svn_sqlite__column_int64(stmt, 3);
3525251881Speter
3526251881Speter          err = svn_error_compose_create(
3527251881Speter                        err,
3528251881Speter                        svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3529251881Speter                                                    wcroot->sdb, repos_id,
3530251881Speter                                                    result_pool));
3531251881Speter        }
3532251881Speter
3533251881Speter      if (recorded_repos_relpath)
3534251881Speter        *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3535251881Speter                                                          result_pool);
3536251881Speter
3537251881Speter      if (recorded_peg_revision)
3538251881Speter        *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3539251881Speter
3540251881Speter      if (recorded_revision)
3541251881Speter        *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3542251881Speter    }
3543251881Speter  else
3544251881Speter    {
3545251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3546251881Speter                              _("The node '%s' is not an external."),
3547251881Speter                              svn_dirent_local_style(local_abspath,
3548251881Speter                                                     scratch_pool));
3549251881Speter    }
3550251881Speter
3551251881Speter  return svn_error_trace(
3552251881Speter                svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3553251881Speter}
3554251881Speter
3555251881Spetersvn_error_t *
3556251881Spetersvn_wc__db_committable_externals_below(apr_array_header_t **externals,
3557251881Speter                                       svn_wc__db_t *db,
3558251881Speter                                       const char *local_abspath,
3559251881Speter                                       svn_boolean_t immediates_only,
3560251881Speter                                       apr_pool_t *result_pool,
3561251881Speter                                       apr_pool_t *scratch_pool)
3562251881Speter{
3563251881Speter  svn_wc__db_wcroot_t *wcroot;
3564251881Speter  svn_sqlite__stmt_t *stmt;
3565251881Speter  const char *local_relpath;
3566251881Speter  svn_boolean_t have_row;
3567251881Speter  svn_wc__committable_external_info_t *info;
3568251881Speter  svn_node_kind_t db_kind;
3569251881Speter  apr_array_header_t *result = NULL;
3570251881Speter
3571251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3572251881Speter
3573251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3574251881Speter                              local_abspath, scratch_pool, scratch_pool));
3575251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3576251881Speter
3577251881Speter  SVN_ERR(svn_sqlite__get_statement(
3578251881Speter                &stmt, wcroot->sdb,
3579251881Speter                immediates_only
3580251881Speter                    ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3581251881Speter                    : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3582251881Speter
3583251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3584251881Speter
3585251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3586251881Speter
3587251881Speter  if (have_row)
3588251881Speter    result = apr_array_make(result_pool, 0,
3589251881Speter                            sizeof(svn_wc__committable_external_info_t *));
3590251881Speter
3591251881Speter  while (have_row)
3592251881Speter    {
3593251881Speter      info = apr_palloc(result_pool, sizeof(*info));
3594251881Speter
3595251881Speter      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3596251881Speter      info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3597251881Speter                                            result_pool);
3598251881Speter
3599251881Speter      db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3600251881Speter      SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3601251881Speter      info->kind = db_kind;
3602251881Speter
3603251881Speter      info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3604251881Speter      info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3605251881Speter
3606251881Speter      APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3607251881Speter
3608251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3609251881Speter    }
3610251881Speter
3611251881Speter  *externals = result;
3612251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
3613251881Speter}
3614251881Speter
3615251881Spetersvn_error_t *
3616251881Spetersvn_wc__db_externals_defined_below(apr_hash_t **externals,
3617251881Speter                                   svn_wc__db_t *db,
3618251881Speter                                   const char *local_abspath,
3619251881Speter                                   apr_pool_t *result_pool,
3620251881Speter                                   apr_pool_t *scratch_pool)
3621251881Speter{
3622251881Speter  svn_wc__db_wcroot_t *wcroot;
3623251881Speter  svn_sqlite__stmt_t *stmt;
3624251881Speter  const char *local_relpath;
3625251881Speter  svn_boolean_t have_row;
3626251881Speter
3627251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3628251881Speter
3629251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3630251881Speter                              local_abspath, scratch_pool, scratch_pool));
3631251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3632251881Speter
3633251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3634251881Speter                                    STMT_SELECT_EXTERNALS_DEFINED));
3635251881Speter
3636251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3637251881Speter
3638251881Speter  *externals = apr_hash_make(result_pool);
3639251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3640251881Speter
3641251881Speter  while (have_row)
3642251881Speter    {
3643251881Speter      const char *def_local_relpath;
3644251881Speter
3645251881Speter      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3646251881Speter      def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3647251881Speter
3648251881Speter      svn_hash_sets(*externals,
3649251881Speter                    svn_dirent_join(wcroot->abspath, local_relpath,
3650251881Speter                                    result_pool),
3651251881Speter                    svn_dirent_join(wcroot->abspath, def_local_relpath,
3652251881Speter                                    result_pool));
3653251881Speter
3654251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3655251881Speter    }
3656251881Speter
3657251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
3658251881Speter}
3659251881Speter
3660251881Spetersvn_error_t *
3661251881Spetersvn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3662251881Speter                                        apr_hash_t **depths,
3663251881Speter                                        svn_wc__db_t *db,
3664251881Speter                                        const char *local_abspath,
3665251881Speter                                        apr_pool_t *result_pool,
3666251881Speter                                        apr_pool_t *scratch_pool)
3667251881Speter{
3668251881Speter  svn_wc__db_wcroot_t *wcroot;
3669251881Speter  svn_sqlite__stmt_t *stmt;
3670251881Speter  const char *local_relpath;
3671251881Speter  svn_boolean_t have_row;
3672251881Speter  svn_error_t *err = NULL;
3673251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3674251881Speter
3675251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3676251881Speter
3677251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3678251881Speter                              local_abspath, scratch_pool, iterpool));
3679251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3680251881Speter
3681251881Speter  *externals = apr_hash_make(result_pool);
3682251881Speter  if (depths != NULL)
3683251881Speter    *depths = apr_hash_make(result_pool);
3684251881Speter
3685251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3686251881Speter                                    STMT_SELECT_EXTERNAL_PROPERTIES));
3687251881Speter
3688251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3689251881Speter
3690251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3691251881Speter
3692251881Speter  while (have_row)
3693251881Speter    {
3694251881Speter      apr_hash_t *node_props;
3695251881Speter      const char *external_value;
3696251881Speter
3697251881Speter      svn_pool_clear(iterpool);
3698251881Speter      err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3699251881Speter                                          iterpool);
3700251881Speter
3701251881Speter      if (err)
3702251881Speter        break;
3703251881Speter
3704251881Speter      external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3705251881Speter
3706251881Speter      if (external_value)
3707251881Speter        {
3708251881Speter          const char *node_abspath;
3709251881Speter          const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3710251881Speter
3711251881Speter          node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3712251881Speter                                         result_pool);
3713251881Speter
3714251881Speter          svn_hash_sets(*externals, node_abspath,
3715251881Speter                        apr_pstrdup(result_pool, external_value));
3716251881Speter
3717251881Speter          if (depths)
3718251881Speter            {
3719251881Speter              svn_depth_t depth
3720251881Speter                = svn_sqlite__column_token_null(stmt, 2, depth_map,
3721251881Speter                                                svn_depth_unknown);
3722251881Speter
3723251881Speter              svn_hash_sets(*depths, node_abspath,
3724251881Speter                            /* Use static string */
3725251881Speter                            svn_token__to_word(depth_map, depth));
3726251881Speter            }
3727251881Speter        }
3728251881Speter
3729251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3730251881Speter    }
3731251881Speter
3732251881Speter  svn_pool_destroy(iterpool);
3733251881Speter
3734251881Speter  return svn_error_trace(svn_error_compose_create(err,
3735251881Speter                                                  svn_sqlite__reset(stmt)));
3736251881Speter}
3737251881Speter
3738251881Speter/* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3739251881Speter   The new ACTUAL data won't have any conflicts. */
3740251881Speterstatic svn_error_t *
3741251881Spetercopy_actual(svn_wc__db_wcroot_t *src_wcroot,
3742251881Speter            const char *src_relpath,
3743251881Speter            svn_wc__db_wcroot_t *dst_wcroot,
3744251881Speter            const char *dst_relpath,
3745251881Speter            apr_pool_t *scratch_pool)
3746251881Speter{
3747251881Speter  svn_sqlite__stmt_t *stmt;
3748251881Speter  svn_boolean_t have_row;
3749251881Speter
3750251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3751251881Speter                                    STMT_SELECT_ACTUAL_NODE));
3752251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3753251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3754251881Speter  if (have_row)
3755251881Speter    {
3756251881Speter      apr_size_t props_size;
3757251881Speter      const char *changelist;
3758251881Speter      const char *properties;
3759251881Speter
3760251881Speter      /* Skipping conflict data... */
3761251881Speter      changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3762251881Speter      /* No need to parse the properties when simply copying. */
3763251881Speter      properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3764251881Speter
3765251881Speter      if (changelist || properties)
3766251881Speter        {
3767251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
3768251881Speter
3769251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3770251881Speter                                            STMT_INSERT_ACTUAL_NODE));
3771251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3772251881Speter                                    dst_wcroot->wc_id, dst_relpath,
3773251881Speter                                svn_relpath_dirname(dst_relpath, scratch_pool),
3774251881Speter                                    properties, props_size, changelist));
3775251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
3776251881Speter        }
3777251881Speter    }
3778251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
3779251881Speter
3780251881Speter  return SVN_NO_ERROR;
3781251881Speter}
3782251881Speter
3783251881Speter/* Helper for svn_wc__db_op_copy to handle copying from one db to
3784251881Speter   another */
3785251881Speterstatic svn_error_t *
3786251881Spetercross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3787251881Speter              const char *src_relpath,
3788251881Speter              svn_wc__db_wcroot_t *dst_wcroot,
3789251881Speter              const char *dst_relpath,
3790251881Speter              svn_wc__db_status_t dst_status,
3791251881Speter              int dst_op_depth,
3792251881Speter              int dst_np_op_depth,
3793251881Speter              svn_node_kind_t kind,
3794251881Speter              const apr_array_header_t *children,
3795251881Speter              apr_int64_t copyfrom_id,
3796251881Speter              const char *copyfrom_relpath,
3797251881Speter              svn_revnum_t copyfrom_rev,
3798251881Speter              apr_pool_t *scratch_pool)
3799251881Speter{
3800251881Speter  insert_working_baton_t iwb;
3801251881Speter  svn_revnum_t changed_rev;
3802251881Speter  apr_time_t changed_date;
3803251881Speter  const char *changed_author;
3804251881Speter  const svn_checksum_t *checksum;
3805251881Speter  apr_hash_t *props;
3806251881Speter  svn_depth_t depth;
3807251881Speter
3808251881Speter  SVN_ERR_ASSERT(kind == svn_node_file
3809251881Speter                 || kind == svn_node_dir
3810251881Speter                 );
3811251881Speter
3812251881Speter  SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3813251881Speter                    &changed_rev, &changed_date, &changed_author, &depth,
3814251881Speter                    &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3815251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3816251881Speter                    src_wcroot, src_relpath, scratch_pool, scratch_pool));
3817251881Speter
3818269847Speter  if (dst_status != svn_wc__db_status_not_present
3819269847Speter      && dst_status != svn_wc__db_status_excluded
3820269847Speter      && dst_status != svn_wc__db_status_server_excluded)
3821269847Speter    {
3822269847Speter      SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3823269847Speter                                     scratch_pool, scratch_pool));
3824269847Speter    }
3825269847Speter  else
3826269847Speter    props = NULL;
3827251881Speter
3828251881Speter  blank_iwb(&iwb);
3829251881Speter  iwb.presence = dst_status;
3830251881Speter  iwb.kind = kind;
3831251881Speter
3832251881Speter  iwb.props = props;
3833251881Speter  iwb.changed_rev = changed_rev;
3834251881Speter  iwb.changed_date = changed_date;
3835251881Speter  iwb.changed_author = changed_author;
3836251881Speter  iwb.original_repos_id = copyfrom_id;
3837251881Speter  iwb.original_repos_relpath = copyfrom_relpath;
3838251881Speter  iwb.original_revnum = copyfrom_rev;
3839251881Speter  iwb.moved_here = FALSE;
3840251881Speter
3841251881Speter  iwb.op_depth = dst_op_depth;
3842251881Speter
3843251881Speter  iwb.checksum = checksum;
3844251881Speter  iwb.children = children;
3845251881Speter  iwb.depth = depth;
3846251881Speter
3847251881Speter  iwb.not_present_op_depth = dst_np_op_depth;
3848251881Speter
3849251881Speter  SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3850251881Speter
3851251881Speter  SVN_ERR(copy_actual(src_wcroot, src_relpath,
3852251881Speter                      dst_wcroot, dst_relpath, scratch_pool));
3853251881Speter
3854251881Speter  return SVN_NO_ERROR;
3855251881Speter}
3856251881Speter
3857251881Speter/* Helper for scan_deletion_txn. Extracts the moved-to information, if
3858251881Speter   any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
3859251881Speterstatic svn_error_t *
3860251881Speterget_moved_to(const char **moved_to_relpath_p,
3861251881Speter             const char **moved_to_op_root_relpath_p,
3862251881Speter             svn_boolean_t *scan,
3863251881Speter             svn_sqlite__stmt_t *stmt,
3864251881Speter             const char *current_relpath,
3865251881Speter             svn_wc__db_wcroot_t *wcroot,
3866251881Speter             const char *local_relpath,
3867251881Speter             apr_pool_t *result_pool,
3868251881Speter             apr_pool_t *scratch_pool)
3869251881Speter{
3870251881Speter  const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3871251881Speter
3872251881Speter  if (moved_to_relpath)
3873251881Speter    {
3874251881Speter      const char *moved_to_op_root_relpath = moved_to_relpath;
3875251881Speter
3876251881Speter      if (strcmp(current_relpath, local_relpath))
3877251881Speter        {
3878251881Speter          /* LOCAL_RELPATH is a child inside the move op-root. */
3879251881Speter          const char *moved_child_relpath;
3880251881Speter
3881251881Speter          /* The CURRENT_RELPATH is the op_root of the delete-half of
3882251881Speter           * the move. LOCAL_RELPATH is a child that was moved along.
3883251881Speter           * Compute the child's new location within the move target. */
3884251881Speter          moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3885251881Speter                                                          local_relpath);
3886251881Speter          SVN_ERR_ASSERT(moved_child_relpath &&
3887251881Speter                         strlen(moved_child_relpath) > 0);
3888251881Speter          moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3889251881Speter                                              moved_child_relpath,
3890251881Speter                                              result_pool);
3891251881Speter        }
3892251881Speter
3893251881Speter      if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3894251881Speter        *moved_to_op_root_relpath_p
3895251881Speter          = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3896251881Speter
3897251881Speter      if (moved_to_relpath && moved_to_relpath_p)
3898251881Speter        *moved_to_relpath_p
3899251881Speter          = apr_pstrdup(result_pool, moved_to_relpath);
3900251881Speter
3901251881Speter      *scan = FALSE;
3902251881Speter    }
3903251881Speter
3904251881Speter  return SVN_NO_ERROR;
3905251881Speter}
3906251881Speter
3907251881Speter
3908251881Speter/* The body of svn_wc__db_scan_deletion().
3909251881Speter */
3910251881Speterstatic svn_error_t *
3911251881Speterscan_deletion_txn(const char **base_del_relpath,
3912251881Speter                  const char **moved_to_relpath,
3913251881Speter                  const char **work_del_relpath,
3914251881Speter                  const char **moved_to_op_root_relpath,
3915251881Speter                  svn_wc__db_wcroot_t *wcroot,
3916251881Speter                  const char *local_relpath,
3917251881Speter                  apr_pool_t *result_pool,
3918251881Speter                  apr_pool_t *scratch_pool)
3919251881Speter{
3920251881Speter  const char *current_relpath = local_relpath;
3921251881Speter  svn_sqlite__stmt_t *stmt;
3922251881Speter  svn_wc__db_status_t work_presence;
3923251881Speter  svn_boolean_t have_row, scan, have_base;
3924251881Speter  int op_depth;
3925251881Speter
3926251881Speter  /* Initialize all the OUT parameters.  */
3927251881Speter  if (base_del_relpath != NULL)
3928251881Speter    *base_del_relpath = NULL;
3929251881Speter  if (moved_to_relpath != NULL)
3930251881Speter    *moved_to_relpath = NULL;
3931251881Speter  if (work_del_relpath != NULL)
3932251881Speter    *work_del_relpath = NULL;
3933251881Speter  if (moved_to_op_root_relpath != NULL)
3934251881Speter    *moved_to_op_root_relpath = NULL;
3935251881Speter
3936251881Speter  /* If looking for moved-to info then we need to scan every path
3937251881Speter     until we find it.  If not looking for moved-to we only need to
3938251881Speter     check op-roots and parents of op-roots. */
3939251881Speter  scan = (moved_to_op_root_relpath || moved_to_relpath);
3940251881Speter
3941251881Speter  SVN_ERR(svn_sqlite__get_statement(
3942251881Speter                    &stmt, wcroot->sdb,
3943251881Speter                    scan ? STMT_SELECT_DELETION_INFO_SCAN
3944251881Speter                         : STMT_SELECT_DELETION_INFO));
3945251881Speter
3946251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3947251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3948251881Speter  if (!have_row)
3949251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3950251881Speter                             _("The node '%s' was not found."),
3951251881Speter                             path_for_error_message(wcroot, local_relpath,
3952251881Speter                                                    scratch_pool));
3953251881Speter
3954251881Speter  work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3955251881Speter  have_base = !svn_sqlite__column_is_null(stmt, 0);
3956251881Speter  if (work_presence != svn_wc__db_status_not_present
3957251881Speter      && work_presence != svn_wc__db_status_base_deleted)
3958251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3959251881Speter                             svn_sqlite__reset(stmt),
3960251881Speter                             _("Expected node '%s' to be deleted."),
3961251881Speter                             path_for_error_message(wcroot, local_relpath,
3962251881Speter                                                    scratch_pool));
3963251881Speter
3964251881Speter  op_depth = svn_sqlite__column_int(stmt, 2);
3965251881Speter
3966251881Speter  /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3967251881Speter     treat this as an op-root.  At commit time we need to explicitly
3968251881Speter     delete such nodes otherwise they will be present in the
3969251881Speter     repository copy. */
3970251881Speter  if (work_presence == svn_wc__db_status_not_present
3971251881Speter      && work_del_relpath && !*work_del_relpath)
3972251881Speter    {
3973251881Speter      *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3974251881Speter
3975251881Speter      if (!scan && !base_del_relpath)
3976251881Speter        {
3977251881Speter          /* We have all we need, exit early */
3978251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
3979251881Speter          return SVN_NO_ERROR;
3980251881Speter        }
3981251881Speter    }
3982251881Speter
3983251881Speter
3984251881Speter  while (TRUE)
3985251881Speter    {
3986251881Speter      svn_error_t *err;
3987251881Speter      const char *parent_relpath;
3988251881Speter      int current_depth = relpath_depth(current_relpath);
3989251881Speter
3990251881Speter      /* Step CURRENT_RELPATH to op-root */
3991251881Speter
3992251881Speter      while (TRUE)
3993251881Speter        {
3994251881Speter          if (scan)
3995251881Speter            {
3996251881Speter              err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
3997251881Speter                                 &scan, stmt, current_relpath,
3998251881Speter                                 wcroot, local_relpath,
3999251881Speter                                 result_pool, scratch_pool);
4000251881Speter              if (err || (!scan
4001251881Speter                          && !base_del_relpath
4002251881Speter                          && !work_del_relpath))
4003251881Speter                {
4004251881Speter                  /* We have all we need (or an error occurred) */
4005251881Speter                  SVN_ERR(svn_sqlite__reset(stmt));
4006251881Speter                  return svn_error_trace(err);
4007251881Speter                }
4008251881Speter            }
4009251881Speter
4010251881Speter          if (current_depth <= op_depth)
4011251881Speter            break;
4012251881Speter
4013251881Speter          current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4014251881Speter          --current_depth;
4015251881Speter
4016251881Speter          if (scan || current_depth == op_depth)
4017251881Speter            {
4018251881Speter              SVN_ERR(svn_sqlite__reset(stmt));
4019251881Speter              SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4020251881Speter                                        current_relpath));
4021251881Speter              SVN_ERR(svn_sqlite__step(&have_row, stmt));
4022251881Speter              SVN_ERR_ASSERT(have_row);
4023251881Speter              have_base = !svn_sqlite__column_is_null(stmt, 0);
4024251881Speter            }
4025251881Speter        }
4026251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
4027251881Speter
4028251881Speter      /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4029251881Speter
4030251881Speter      SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4031251881Speter      parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4032251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4033251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
4034251881Speter      if (!have_row)
4035251881Speter        {
4036251881Speter          /* No row means no WORKING node which mean we just fell off
4037251881Speter             the WORKING tree, so CURRENT_RELPATH is the op-root
4038251881Speter             closest to the wc root. */
4039251881Speter          if (have_base && base_del_relpath)
4040251881Speter            *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4041251881Speter          break;
4042251881Speter        }
4043251881Speter
4044251881Speter      /* Still in the WORKING tree so the first time we get here
4045251881Speter         CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4046251881Speter      if (work_del_relpath && !*work_del_relpath)
4047251881Speter        {
4048251881Speter          *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4049251881Speter
4050251881Speter          if (!scan && !base_del_relpath)
4051251881Speter            break; /* We have all we need */
4052251881Speter        }
4053251881Speter
4054251881Speter      current_relpath = parent_relpath;
4055251881Speter      op_depth = svn_sqlite__column_int(stmt, 2);
4056251881Speter      have_base = !svn_sqlite__column_is_null(stmt, 0);
4057251881Speter    }
4058251881Speter
4059251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4060251881Speter
4061251881Speter  return SVN_NO_ERROR;
4062251881Speter}
4063251881Speter
4064251881Spetersvn_error_t *
4065251881Spetersvn_wc__db_scan_deletion(const char **base_del_abspath,
4066251881Speter                         const char **moved_to_abspath,
4067251881Speter                         const char **work_del_abspath,
4068251881Speter                         const char **moved_to_op_root_abspath,
4069251881Speter                         svn_wc__db_t *db,
4070251881Speter                         const char *local_abspath,
4071251881Speter                         apr_pool_t *result_pool,
4072251881Speter                         apr_pool_t *scratch_pool)
4073251881Speter{
4074251881Speter  svn_wc__db_wcroot_t *wcroot;
4075251881Speter  const char *local_relpath;
4076251881Speter  const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4077251881Speter  const char *moved_to_op_root_relpath;
4078251881Speter
4079251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4080251881Speter
4081251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4082251881Speter                              local_abspath, scratch_pool, scratch_pool));
4083251881Speter  VERIFY_USABLE_WCROOT(wcroot);
4084251881Speter
4085251881Speter  SVN_WC__DB_WITH_TXN(
4086251881Speter    scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4087251881Speter                      &work_del_relpath, &moved_to_op_root_relpath,
4088251881Speter                      wcroot, local_relpath, result_pool, scratch_pool),
4089251881Speter    wcroot);
4090251881Speter
4091251881Speter  if (base_del_abspath)
4092251881Speter    {
4093251881Speter      *base_del_abspath = (base_del_relpath
4094251881Speter                           ? svn_dirent_join(wcroot->abspath,
4095251881Speter                                             base_del_relpath, result_pool)
4096251881Speter                           : NULL);
4097251881Speter    }
4098251881Speter  if (moved_to_abspath)
4099251881Speter    {
4100251881Speter      *moved_to_abspath = (moved_to_relpath
4101251881Speter                           ? svn_dirent_join(wcroot->abspath,
4102251881Speter                                             moved_to_relpath, result_pool)
4103251881Speter                           : NULL);
4104251881Speter    }
4105251881Speter  if (work_del_abspath)
4106251881Speter    {
4107251881Speter      *work_del_abspath = (work_del_relpath
4108251881Speter                           ? svn_dirent_join(wcroot->abspath,
4109251881Speter                                             work_del_relpath, result_pool)
4110251881Speter                           : NULL);
4111251881Speter    }
4112251881Speter  if (moved_to_op_root_abspath)
4113251881Speter    {
4114251881Speter      *moved_to_op_root_abspath = (moved_to_op_root_relpath
4115251881Speter                           ? svn_dirent_join(wcroot->abspath,
4116251881Speter                                             moved_to_op_root_relpath,
4117251881Speter                                             result_pool)
4118251881Speter                           : NULL);
4119251881Speter    }
4120251881Speter
4121251881Speter  return SVN_NO_ERROR;
4122251881Speter}
4123251881Speter
4124251881Speter
4125251881Speter/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4126251881Speter   appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4127251881Speter   since they are available.  This is a helper for
4128251881Speter   svn_wc__db_op_copy. */
4129251881Speterstatic svn_error_t *
4130251881Speterget_info_for_copy(apr_int64_t *copyfrom_id,
4131251881Speter                  const char **copyfrom_relpath,
4132251881Speter                  svn_revnum_t *copyfrom_rev,
4133251881Speter                  svn_wc__db_status_t *status,
4134251881Speter                  svn_node_kind_t *kind,
4135251881Speter                  svn_boolean_t *op_root,
4136269847Speter                  svn_wc__db_wcroot_t *src_wcroot,
4137251881Speter                  const char *local_relpath,
4138269847Speter                  svn_wc__db_wcroot_t *dst_wcroot,
4139251881Speter                  apr_pool_t *result_pool,
4140251881Speter                  apr_pool_t *scratch_pool)
4141251881Speter{
4142251881Speter  const char *repos_relpath;
4143251881Speter  svn_revnum_t revision;
4144251881Speter  svn_wc__db_status_t node_status;
4145251881Speter  apr_int64_t repos_id;
4146251881Speter  svn_boolean_t is_op_root;
4147251881Speter
4148251881Speter  SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4149251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4150251881Speter                    copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4151251881Speter                    NULL, &is_op_root, NULL, NULL,
4152251881Speter                    NULL /* have_base */,
4153251881Speter                    NULL /* have_more_work */,
4154251881Speter                    NULL /* have_work */,
4155269847Speter                    src_wcroot, local_relpath, result_pool, scratch_pool));
4156251881Speter
4157251881Speter  if (op_root)
4158251881Speter    *op_root = is_op_root;
4159251881Speter
4160251881Speter  if (node_status == svn_wc__db_status_excluded)
4161251881Speter    {
4162251881Speter      /* The parent cannot be excluded, so look at the parent and then
4163251881Speter         adjust the relpath */
4164251881Speter      const char *parent_relpath, *base_name;
4165251881Speter
4166251881Speter      svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4167251881Speter                       scratch_pool);
4168251881Speter      SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4169251881Speter                                NULL, NULL, NULL,
4170269847Speter                                src_wcroot, parent_relpath, dst_wcroot,
4171251881Speter                                scratch_pool, scratch_pool));
4172251881Speter      if (*copyfrom_relpath)
4173251881Speter        *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4174251881Speter                                             result_pool);
4175251881Speter    }
4176251881Speter  else if (node_status == svn_wc__db_status_added)
4177251881Speter    {
4178251881Speter      SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4179269847Speter                            NULL, NULL, NULL, src_wcroot, local_relpath,
4180251881Speter                            scratch_pool, scratch_pool));
4181251881Speter    }
4182251881Speter  else if (node_status == svn_wc__db_status_deleted && is_op_root)
4183251881Speter    {
4184251881Speter      const char *base_del_relpath, *work_del_relpath;
4185251881Speter
4186251881Speter      SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4187251881Speter                                &work_del_relpath,
4188269847Speter                                NULL, src_wcroot, local_relpath,
4189251881Speter                                scratch_pool, scratch_pool));
4190251881Speter      if (work_del_relpath)
4191251881Speter        {
4192251881Speter          const char *op_root_relpath;
4193251881Speter          const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4194251881Speter                                                               scratch_pool);
4195251881Speter
4196251881Speter          /* Similar to, but not the same as, the _scan_addition and
4197251881Speter             _join above.  Can we use get_copyfrom here? */
4198251881Speter          SVN_ERR(scan_addition(NULL, &op_root_relpath,
4199251881Speter                                NULL, NULL, /* repos_* */
4200251881Speter                                copyfrom_relpath, copyfrom_id, copyfrom_rev,
4201269847Speter                                NULL, NULL, NULL,
4202269847Speter                                src_wcroot, parent_del_relpath,
4203251881Speter                                scratch_pool, scratch_pool));
4204251881Speter          *copyfrom_relpath
4205251881Speter            = svn_relpath_join(*copyfrom_relpath,
4206251881Speter                               svn_relpath_skip_ancestor(op_root_relpath,
4207251881Speter                                                         local_relpath),
4208251881Speter                               result_pool);
4209251881Speter        }
4210251881Speter      else if (base_del_relpath)
4211251881Speter        {
4212251881Speter          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4213251881Speter                                                    copyfrom_relpath,
4214251881Speter                                                    copyfrom_id, NULL, NULL,
4215251881Speter                                                    NULL, NULL, NULL, NULL,
4216251881Speter                                                    NULL, NULL, NULL, NULL,
4217269847Speter                                                    src_wcroot, local_relpath,
4218251881Speter                                                    result_pool,
4219251881Speter                                                    scratch_pool));
4220251881Speter        }
4221251881Speter      else
4222251881Speter        SVN_ERR_MALFUNCTION();
4223251881Speter    }
4224251881Speter  else if (node_status == svn_wc__db_status_deleted)
4225251881Speter    {
4226251881Speter      /* Keep original_* from read_info() to allow seeing the difference
4227251881Speter         between base-deleted and not present */
4228251881Speter    }
4229251881Speter  else
4230251881Speter    {
4231251881Speter      *copyfrom_relpath = repos_relpath;
4232251881Speter      *copyfrom_rev = revision;
4233251881Speter      *copyfrom_id = repos_id;
4234251881Speter    }
4235251881Speter
4236251881Speter  if (status)
4237251881Speter    *status = node_status;
4238251881Speter
4239269847Speter  if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4240269847Speter    {
4241269847Speter      const char *repos_root_url;
4242269847Speter      const char *repos_uuid;
4243269847Speter
4244269847Speter      /* Pass the right repos-id for the destination db. We can't just use
4245269847Speter         the id of the source database, as this value can change after
4246269847Speter         relocation (and perhaps also when we start storing multiple
4247269847Speter         working copies in a single db)! */
4248269847Speter
4249269847Speter      SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4250269847Speter                                          src_wcroot->sdb, *copyfrom_id,
4251269847Speter                                          scratch_pool));
4252269847Speter
4253269847Speter      SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4254269847Speter                              dst_wcroot->sdb, scratch_pool));
4255269847Speter    }
4256269847Speter
4257251881Speter  return SVN_NO_ERROR;
4258251881Speter}
4259251881Speter
4260251881Speter
4261251881Speter/* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4262251881Speterstatic svn_error_t *
4263251881Speterop_depth_of(int *op_depth,
4264251881Speter            svn_wc__db_wcroot_t *wcroot,
4265251881Speter            const char *local_relpath)
4266251881Speter{
4267251881Speter  svn_sqlite__stmt_t *stmt;
4268251881Speter  svn_boolean_t have_row;
4269251881Speter
4270251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4271251881Speter                                    STMT_SELECT_NODE_INFO));
4272251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4273251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4274251881Speter  SVN_ERR_ASSERT(have_row);
4275251881Speter  *op_depth = svn_sqlite__column_int(stmt, 0);
4276251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4277251881Speter
4278251881Speter  return SVN_NO_ERROR;
4279251881Speter}
4280251881Speter
4281251881Speter
4282251881Speter/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4283251881Speter   revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4284251881Speter   by checking if this would be a direct child of a copy of its parent
4285251881Speter   directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4286251881Speter
4287251881Speter   If the node is not a direct copy at the same revision of the parent
4288251881Speter   *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4289251881Speter   node should be inserted at this op_depth. This will be the case when the
4290251881Speter   parent already defined an incomplete child with the same name. Otherwise
4291251881Speter   *NP_OP_DEPTH will be set to -1.
4292251881Speter
4293251881Speter   If the parent node is not the parent of the to be copied node, then
4294251881Speter   *OP_DEPTH will be set to the proper op_depth for a new operation root.
4295251881Speter
4296251881Speter   Set *PARENT_OP_DEPTH to the op_depth of the parent.
4297251881Speter
4298251881Speter */
4299251881Speterstatic svn_error_t *
4300251881Speterop_depth_for_copy(int *op_depth,
4301251881Speter                  int *np_op_depth,
4302251881Speter                  int *parent_op_depth,
4303251881Speter                  apr_int64_t copyfrom_repos_id,
4304251881Speter                  const char *copyfrom_relpath,
4305251881Speter                  svn_revnum_t copyfrom_revision,
4306251881Speter                  svn_wc__db_wcroot_t *wcroot,
4307251881Speter                  const char *local_relpath,
4308251881Speter                  apr_pool_t *scratch_pool)
4309251881Speter{
4310251881Speter  const char *parent_relpath, *name;
4311251881Speter  svn_sqlite__stmt_t *stmt;
4312251881Speter  svn_boolean_t have_row;
4313251881Speter  int incomplete_op_depth = -1;
4314251881Speter  int min_op_depth = 1; /* Never touch BASE */
4315251881Speter
4316251881Speter  *op_depth = relpath_depth(local_relpath);
4317251881Speter  *np_op_depth = -1;
4318251881Speter
4319251881Speter  svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4320251881Speter  *parent_op_depth = relpath_depth(parent_relpath);
4321251881Speter
4322251881Speter  if (!copyfrom_relpath)
4323251881Speter    return SVN_NO_ERROR;
4324251881Speter
4325251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4326251881Speter                                    STMT_SELECT_WORKING_NODE));
4327251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4328251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4329251881Speter  if (have_row)
4330251881Speter    {
4331251881Speter      svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4332251881Speter                                                            presence_map);
4333251881Speter
4334251881Speter      min_op_depth = svn_sqlite__column_int(stmt, 0);
4335251881Speter      if (status == svn_wc__db_status_incomplete)
4336251881Speter        incomplete_op_depth = min_op_depth;
4337251881Speter    }
4338251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4339251881Speter
4340251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4341251881Speter                                    STMT_SELECT_WORKING_NODE));
4342251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4343251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4344251881Speter  if (have_row)
4345251881Speter    {
4346251881Speter      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4347251881Speter                                                              presence_map);
4348251881Speter
4349251881Speter      *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4350251881Speter      if (*parent_op_depth < min_op_depth)
4351251881Speter        {
4352251881Speter          /* We want to create a copy; not overwrite the lower layers */
4353251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
4354251881Speter          return SVN_NO_ERROR;
4355251881Speter        }
4356251881Speter
4357251881Speter      /* You can only add children below a node that exists.
4358251881Speter         In WORKING that must be status added, which is represented
4359251881Speter         as presence normal */
4360251881Speter      SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4361251881Speter
4362251881Speter      if ((incomplete_op_depth < 0)
4363251881Speter          || (incomplete_op_depth == *parent_op_depth))
4364251881Speter        {
4365251881Speter          apr_int64_t parent_copyfrom_repos_id
4366251881Speter            = svn_sqlite__column_int64(stmt, 10);
4367251881Speter          const char *parent_copyfrom_relpath
4368251881Speter            = svn_sqlite__column_text(stmt, 11, NULL);
4369251881Speter          svn_revnum_t parent_copyfrom_revision
4370251881Speter            = svn_sqlite__column_revnum(stmt, 12);
4371251881Speter
4372251881Speter          if (parent_copyfrom_repos_id == copyfrom_repos_id)
4373251881Speter            {
4374251881Speter              if (copyfrom_revision == parent_copyfrom_revision
4375251881Speter                  && !strcmp(copyfrom_relpath,
4376251881Speter                             svn_relpath_join(parent_copyfrom_relpath, name,
4377251881Speter                                              scratch_pool)))
4378251881Speter                *op_depth = *parent_op_depth;
4379251881Speter              else if (incomplete_op_depth > 0)
4380251881Speter                *np_op_depth = incomplete_op_depth;
4381251881Speter            }
4382251881Speter        }
4383251881Speter    }
4384251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4385251881Speter
4386251881Speter  return SVN_NO_ERROR;
4387251881Speter}
4388251881Speter
4389251881Speter
4390251881Speter/* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4391251881Speter * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4392251881Speter * copy operation is part of a move, and indicates the op-depth of the
4393251881Speter * move destination op-root. */
4394251881Speterstatic svn_error_t *
4395251881Speterdb_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4396251881Speter           const char *src_relpath,
4397251881Speter           svn_wc__db_wcroot_t *dst_wcroot,
4398251881Speter           const char *dst_relpath,
4399251881Speter           const svn_skel_t *work_items,
4400251881Speter           int move_op_depth,
4401251881Speter           apr_pool_t *scratch_pool)
4402251881Speter{
4403251881Speter  const char *copyfrom_relpath;
4404251881Speter  svn_revnum_t copyfrom_rev;
4405251881Speter  svn_wc__db_status_t status;
4406251881Speter  svn_wc__db_status_t dst_presence;
4407251881Speter  svn_boolean_t op_root;
4408251881Speter  apr_int64_t copyfrom_id;
4409251881Speter  int dst_op_depth;
4410251881Speter  int dst_np_op_depth;
4411251881Speter  int dst_parent_op_depth;
4412251881Speter  svn_node_kind_t kind;
4413251881Speter  const apr_array_header_t *children;
4414251881Speter
4415251881Speter  SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
4416269847Speter                            &status, &kind, &op_root,
4417269847Speter                            src_wcroot, src_relpath, dst_wcroot,
4418269847Speter                            scratch_pool, scratch_pool));
4419251881Speter
4420251881Speter  SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4421251881Speter                            &dst_parent_op_depth,
4422251881Speter                            copyfrom_id, copyfrom_relpath, copyfrom_rev,
4423251881Speter                            dst_wcroot, dst_relpath, scratch_pool));
4424251881Speter
4425251881Speter  SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4426251881Speter
4427251881Speter  /* ### New status, not finished, see notes/wc-ng/copying */
4428251881Speter  switch (status)
4429251881Speter    {
4430251881Speter    case svn_wc__db_status_normal:
4431251881Speter    case svn_wc__db_status_added:
4432251881Speter    case svn_wc__db_status_moved_here:
4433251881Speter    case svn_wc__db_status_copied:
4434251881Speter      dst_presence = svn_wc__db_status_normal;
4435251881Speter      break;
4436251881Speter    case svn_wc__db_status_deleted:
4437251881Speter      if (op_root)
4438251881Speter        {
4439251881Speter          /* If the lower layer is already shadowcopied we can skip adding
4440251881Speter             a not present node. */
4441251881Speter          svn_error_t *err;
4442251881Speter          svn_wc__db_status_t dst_status;
4443251881Speter
4444251881Speter          err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4445251881Speter                          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4446251881Speter                          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4447251881Speter                          dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4448251881Speter
4449251881Speter          if (err)
4450251881Speter            {
4451251881Speter              if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4452251881Speter                svn_error_clear(err);
4453251881Speter              else
4454251881Speter                return svn_error_trace(err);
4455251881Speter            }
4456251881Speter          else if (dst_status == svn_wc__db_status_deleted)
4457251881Speter            {
4458251881Speter              /* Node is already deleted; skip the NODES work, but do
4459251881Speter                 install wq items if requested */
4460251881Speter              SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4461251881Speter                                     scratch_pool));
4462251881Speter              return SVN_NO_ERROR;
4463251881Speter            }
4464251881Speter        }
4465251881Speter      else
4466251881Speter        {
4467251881Speter          /* This node is either a not-present node (which should be copied), or
4468251881Speter             a base-delete of some lower layer (which shouldn't).
4469251881Speter             Subversion <= 1.7 always added a not-present node here, which is
4470251881Speter             safe (as it postpones the hard work until commit time and then we
4471251881Speter             ask the repository), but it breaks some move scenarios.
4472251881Speter             */
4473251881Speter
4474251881Speter           if (! copyfrom_relpath)
4475251881Speter             {
4476251881Speter               SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4477251881Speter                                     scratch_pool));
4478251881Speter               return SVN_NO_ERROR;
4479251881Speter             }
4480251881Speter
4481251881Speter           /* Fall through. Install not present node */
4482251881Speter        }
4483251881Speter    case svn_wc__db_status_not_present:
4484251881Speter    case svn_wc__db_status_excluded:
4485251881Speter      /* These presence values should not create a new op depth */
4486251881Speter      if (dst_np_op_depth > 0)
4487251881Speter        {
4488251881Speter          dst_op_depth = dst_np_op_depth;
4489251881Speter          dst_np_op_depth = -1;
4490251881Speter        }
4491251881Speter      if (status == svn_wc__db_status_excluded)
4492251881Speter        dst_presence = svn_wc__db_status_excluded;
4493251881Speter      else
4494251881Speter        dst_presence = svn_wc__db_status_not_present;
4495251881Speter      break;
4496251881Speter    case svn_wc__db_status_server_excluded:
4497251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4498251881Speter                               _("Cannot copy '%s' excluded by server"),
4499251881Speter                               path_for_error_message(src_wcroot,
4500251881Speter                                                      src_relpath,
4501251881Speter                                                      scratch_pool));
4502251881Speter    default:
4503251881Speter      /* Perhaps we should allow incomplete to incomplete? We can't
4504251881Speter         avoid incomplete working nodes as one step in copying a
4505251881Speter         directory is to add incomplete children. */
4506251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4507251881Speter                               _("Cannot handle status of '%s'"),
4508251881Speter                               path_for_error_message(src_wcroot,
4509251881Speter                                                      src_relpath,
4510251881Speter                                                      scratch_pool));
4511251881Speter    }
4512251881Speter
4513251881Speter  if (kind == svn_node_dir)
4514251881Speter    {
4515251881Speter      int src_op_depth;
4516251881Speter
4517251881Speter      SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4518251881Speter      SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4519251881Speter                                   src_op_depth, scratch_pool, scratch_pool));
4520251881Speter    }
4521251881Speter  else
4522251881Speter    children = NULL;
4523251881Speter
4524251881Speter  if (src_wcroot == dst_wcroot)
4525251881Speter    {
4526251881Speter      svn_sqlite__stmt_t *stmt;
4527251881Speter      const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4528251881Speter                                                           scratch_pool);
4529251881Speter
4530251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4531251881Speter                                        STMT_INSERT_WORKING_NODE_COPY_FROM));
4532251881Speter
4533251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4534251881Speter                    src_wcroot->wc_id, src_relpath,
4535251881Speter                    dst_relpath,
4536251881Speter                    dst_op_depth,
4537251881Speter                    dst_parent_relpath,
4538251881Speter                    presence_map, dst_presence));
4539251881Speter
4540251881Speter      if (move_op_depth > 0)
4541251881Speter        {
4542251881Speter          if (relpath_depth(dst_relpath) == move_op_depth)
4543251881Speter            {
4544251881Speter              /* We're moving the root of the move operation.
4545251881Speter               *
4546251881Speter               * When an added node or the op-root of a copy is moved,
4547251881Speter               * there is no 'moved-from' corresponding to the moved-here
4548251881Speter               * node. So the net effect is the same as copy+delete.
4549251881Speter               * Perform a normal copy operation in these cases. */
4550251881Speter              if (!(status == svn_wc__db_status_added ||
4551251881Speter                    (status == svn_wc__db_status_copied && op_root)))
4552251881Speter                SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4553251881Speter            }
4554251881Speter          else
4555251881Speter            {
4556251881Speter              svn_sqlite__stmt_t *info_stmt;
4557251881Speter              svn_boolean_t have_row;
4558251881Speter
4559251881Speter              /* We're moving a child along with the root of the move.
4560251881Speter               *
4561251881Speter               * Set moved-here depending on dst_parent, propagating the
4562251881Speter               * above decision to moved-along children at the same op_depth.
4563251881Speter               * We can't use scan_addition() to detect moved-here because
4564251881Speter               * the delete-half of the move might not yet exist. */
4565251881Speter              SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4566251881Speter                                                STMT_SELECT_NODE_INFO));
4567251881Speter              SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4568251881Speter                                        dst_parent_relpath));
4569251881Speter              SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4570251881Speter              SVN_ERR_ASSERT(have_row);
4571251881Speter              if (svn_sqlite__column_boolean(info_stmt, 15) &&
4572251881Speter                  dst_op_depth == dst_parent_op_depth)
4573251881Speter                {
4574251881Speter                  SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4575251881Speter                  SVN_ERR(svn_sqlite__reset(info_stmt));
4576251881Speter                }
4577251881Speter              else
4578251881Speter                {
4579251881Speter                  SVN_ERR(svn_sqlite__reset(info_stmt));
4580251881Speter
4581251881Speter                  /* If the child has been moved into the tree we're moving,
4582251881Speter                   * keep its moved-here bit set. */
4583251881Speter                  SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4584251881Speter                                                    dst_wcroot->sdb,
4585251881Speter                                                    STMT_SELECT_NODE_INFO));
4586251881Speter                  SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4587251881Speter                                            dst_wcroot->wc_id, src_relpath));
4588251881Speter                  SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4589251881Speter                  SVN_ERR_ASSERT(have_row);
4590251881Speter                  if (svn_sqlite__column_boolean(info_stmt, 15))
4591251881Speter                    SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4592251881Speter                  SVN_ERR(svn_sqlite__reset(info_stmt));
4593251881Speter                }
4594251881Speter            }
4595251881Speter        }
4596251881Speter
4597251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
4598251881Speter
4599251881Speter      /* ### Copying changelist is OK for a move but what about a copy? */
4600251881Speter      SVN_ERR(copy_actual(src_wcroot, src_relpath,
4601251881Speter                          dst_wcroot, dst_relpath, scratch_pool));
4602251881Speter
4603251881Speter      if (dst_np_op_depth > 0)
4604251881Speter        {
4605251881Speter          /* We introduce a not-present node at the parent's op_depth to
4606251881Speter             properly start a new op-depth at our own op_depth. This marks
4607251881Speter             us as an op_root for commit and allows reverting just this
4608251881Speter             operation */
4609251881Speter
4610251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4611251881Speter                                            STMT_INSERT_NODE));
4612251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4613251881Speter                                    src_wcroot->wc_id, dst_relpath,
4614251881Speter                                    dst_np_op_depth, dst_parent_relpath,
4615251881Speter                                    copyfrom_id, copyfrom_relpath,
4616251881Speter                                    copyfrom_rev,
4617251881Speter                                    presence_map,
4618251881Speter                                       svn_wc__db_status_not_present,
4619251881Speter                                    /* NULL */
4620251881Speter                                    kind_map, kind));
4621251881Speter
4622251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
4623251881Speter        }
4624251881Speter      /* Insert incomplete children, if relevant.
4625251881Speter         The children are part of the same op and so have the same op_depth.
4626251881Speter         (The only time we'd want a different depth is during a recursive
4627251881Speter         simple add, but we never insert children here during a simple add.) */
4628251881Speter      if (kind == svn_node_dir
4629251881Speter          && dst_presence == svn_wc__db_status_normal)
4630251881Speter        SVN_ERR(insert_incomplete_children(
4631251881Speter                  dst_wcroot->sdb,
4632251881Speter                  dst_wcroot->wc_id,
4633251881Speter                  dst_relpath,
4634251881Speter                  copyfrom_id,
4635251881Speter                  copyfrom_relpath,
4636251881Speter                  copyfrom_rev,
4637251881Speter                  children,
4638251881Speter                  dst_op_depth,
4639251881Speter                  scratch_pool));
4640251881Speter    }
4641251881Speter  else
4642251881Speter    {
4643251881Speter      SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4644251881Speter                            dst_relpath, dst_presence, dst_op_depth,
4645251881Speter                            dst_np_op_depth, kind,
4646251881Speter                            children, copyfrom_id, copyfrom_relpath,
4647251881Speter                            copyfrom_rev, scratch_pool));
4648251881Speter    }
4649251881Speter
4650251881Speter  SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4651251881Speter
4652251881Speter  return SVN_NO_ERROR;
4653251881Speter}
4654251881Speter
4655251881Speter/* Baton for passing args to op_copy_txn(). */
4656251881Speterstruct op_copy_baton
4657251881Speter{
4658251881Speter  svn_wc__db_wcroot_t *src_wcroot;
4659251881Speter  const char *src_relpath;
4660251881Speter
4661251881Speter  svn_wc__db_wcroot_t *dst_wcroot;
4662251881Speter  const char *dst_relpath;
4663251881Speter
4664251881Speter  const svn_skel_t *work_items;
4665251881Speter
4666251881Speter  svn_boolean_t is_move;
4667251881Speter  const char *dst_op_root_relpath;
4668251881Speter};
4669251881Speter
4670251881Speter/* Helper for svn_wc__db_op_copy().
4671251881Speter *
4672251881Speter * Implements svn_sqlite__transaction_callback_t. */
4673251881Speterstatic svn_error_t *
4674251881Speterop_copy_txn(void * baton,
4675251881Speter            svn_sqlite__db_t *sdb,
4676251881Speter            apr_pool_t *scratch_pool)
4677251881Speter{
4678251881Speter  struct op_copy_baton *ocb = baton;
4679251881Speter  int move_op_depth;
4680251881Speter
4681251881Speter  if (sdb != ocb->dst_wcroot->sdb)
4682251881Speter    {
4683251881Speter       /* Source and destination databases differ; so also start a lock
4684251881Speter          in the destination database, by calling ourself in a lock. */
4685251881Speter
4686251881Speter      return svn_error_trace(
4687251881Speter                        svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4688251881Speter                                              op_copy_txn, ocb, scratch_pool));
4689251881Speter    }
4690251881Speter
4691251881Speter  /* From this point we can assume a lock in the src and dst databases */
4692251881Speter
4693251881Speter  if (ocb->is_move)
4694251881Speter    move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4695251881Speter  else
4696251881Speter    move_op_depth = 0;
4697251881Speter
4698251881Speter  SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4699251881Speter                     ocb->dst_wcroot, ocb->dst_relpath,
4700251881Speter                     ocb->work_items, move_op_depth, scratch_pool));
4701251881Speter
4702251881Speter  return SVN_NO_ERROR;
4703251881Speter}
4704251881Speter
4705251881Spetersvn_error_t *
4706251881Spetersvn_wc__db_op_copy(svn_wc__db_t *db,
4707251881Speter                   const char *src_abspath,
4708251881Speter                   const char *dst_abspath,
4709251881Speter                   const char *dst_op_root_abspath,
4710251881Speter                   svn_boolean_t is_move,
4711251881Speter                   const svn_skel_t *work_items,
4712251881Speter                   apr_pool_t *scratch_pool)
4713251881Speter{
4714251881Speter  struct op_copy_baton ocb = {0};
4715251881Speter
4716251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4717251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4718251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4719251881Speter
4720251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4721251881Speter                                                &ocb.src_relpath, db,
4722251881Speter                                                src_abspath,
4723251881Speter                                                scratch_pool, scratch_pool));
4724251881Speter  VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4725251881Speter
4726251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4727251881Speter                                                &ocb.dst_relpath,
4728251881Speter                                                db, dst_abspath,
4729251881Speter                                                scratch_pool, scratch_pool));
4730251881Speter  VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4731251881Speter
4732251881Speter  ocb.work_items = work_items;
4733251881Speter  ocb.is_move = is_move;
4734251881Speter  ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4735251881Speter                                                     dst_op_root_abspath);
4736251881Speter
4737251881Speter  /* Call with the sdb in src_wcroot. It might call itself again to
4738251881Speter     also obtain a lock in dst_wcroot */
4739251881Speter  SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4740251881Speter                                scratch_pool));
4741251881Speter
4742251881Speter  return SVN_NO_ERROR;
4743251881Speter}
4744251881Speter
4745251881Speter/* The txn body of svn_wc__db_op_handle_move_back */
4746251881Speterstatic svn_error_t *
4747251881Speterhandle_move_back(svn_boolean_t *moved_back,
4748251881Speter                 svn_wc__db_wcroot_t *wcroot,
4749251881Speter                 const char *local_relpath,
4750251881Speter                 const char *moved_from_relpath,
4751251881Speter                 const svn_skel_t *work_items,
4752251881Speter                 apr_pool_t *scratch_pool)
4753251881Speter{
4754251881Speter  svn_sqlite__stmt_t *stmt;
4755251881Speter  svn_wc__db_status_t status;
4756251881Speter  svn_boolean_t op_root;
4757251881Speter  svn_boolean_t have_more_work;
4758251881Speter  int from_op_depth = 0;
4759251881Speter  svn_boolean_t have_row;
4760251881Speter  svn_boolean_t different = FALSE;
4761251881Speter
4762251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4763251881Speter
4764251881Speter  SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4765251881Speter                                        NULL, NULL, NULL, NULL, NULL, NULL,
4766251881Speter                                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4767251881Speter                                        &op_root, NULL, NULL, NULL,
4768251881Speter                                        &have_more_work, NULL,
4769251881Speter                                        wcroot, local_relpath,
4770251881Speter                                        scratch_pool, scratch_pool));
4771251881Speter
4772251881Speter  if (status != svn_wc__db_status_added || !op_root)
4773251881Speter    return SVN_NO_ERROR;
4774251881Speter
4775251881Speter  /* We have two cases here: BASE-move-back and WORKING-move-back */
4776251881Speter  if (have_more_work)
4777251881Speter    SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4778251881Speter                        svn_relpath_dirname(local_relpath, scratch_pool)));
4779251881Speter  else
4780251881Speter    from_op_depth = 0;
4781251881Speter
4782251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4783251881Speter                                    STMT_SELECT_MOVED_BACK));
4784251881Speter
4785251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4786251881Speter                                          local_relpath,
4787251881Speter                                          from_op_depth,
4788251881Speter                                          relpath_depth(local_relpath)));
4789251881Speter
4790251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4791251881Speter
4792251881Speter  SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4793251881Speter
4794251881Speter  {
4795251881Speter    svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4796251881Speter    const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4797251881Speter
4798251881Speter    if (!moved_here
4799251881Speter        || !moved_to
4800251881Speter        || strcmp(moved_to, moved_from_relpath))
4801251881Speter      {
4802251881Speter        different = TRUE;
4803251881Speter        have_row = FALSE;
4804251881Speter      }
4805251881Speter  }
4806251881Speter
4807251881Speter  while (have_row)
4808251881Speter    {
4809251881Speter      svn_wc__db_status_t upper_status;
4810251881Speter      svn_wc__db_status_t lower_status;
4811251881Speter
4812251881Speter      upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4813251881Speter
4814251881Speter      if (svn_sqlite__column_is_null(stmt, 5))
4815251881Speter        {
4816251881Speter          /* No lower layer replaced. */
4817251881Speter          if (upper_status != svn_wc__db_status_not_present)
4818251881Speter            {
4819251881Speter              different = TRUE;
4820251881Speter              break;
4821251881Speter            }
4822251881Speter          continue;
4823251881Speter        }
4824251881Speter
4825251881Speter      lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4826251881Speter
4827251881Speter      if (upper_status != lower_status)
4828251881Speter        {
4829251881Speter          different = TRUE;
4830251881Speter          break;
4831251881Speter        }
4832251881Speter
4833251881Speter      if (upper_status == svn_wc__db_status_not_present
4834251881Speter          || upper_status == svn_wc__db_status_excluded)
4835251881Speter        {
4836251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
4837251881Speter          continue; /* Nothing to check */
4838251881Speter        }
4839251881Speter      else if (upper_status != svn_wc__db_status_normal)
4840251881Speter        {
4841251881Speter          /* Not a normal move. Mixed revision move? */
4842251881Speter          different = TRUE;
4843251881Speter          break;
4844251881Speter        }
4845251881Speter
4846251881Speter      {
4847251881Speter        const char *upper_repos_relpath;
4848251881Speter        const char *lower_repos_relpath;
4849251881Speter
4850251881Speter        upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4851251881Speter        lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4852251881Speter
4853251881Speter        if (! upper_repos_relpath
4854251881Speter            || strcmp(upper_repos_relpath, lower_repos_relpath))
4855251881Speter          {
4856251881Speter            different = TRUE;
4857251881Speter            break;
4858251881Speter          }
4859251881Speter      }
4860251881Speter
4861251881Speter      {
4862251881Speter        svn_revnum_t upper_rev;
4863251881Speter        svn_revnum_t lower_rev;
4864251881Speter
4865251881Speter        upper_rev = svn_sqlite__column_revnum(stmt, 4);
4866251881Speter        lower_rev = svn_sqlite__column_revnum(stmt, 8);
4867251881Speter
4868251881Speter        if (upper_rev != lower_rev)
4869251881Speter          {
4870251881Speter            different = TRUE;
4871251881Speter            break;
4872251881Speter          }
4873251881Speter      }
4874251881Speter
4875251881Speter      {
4876251881Speter        apr_int64_t upper_repos_id;
4877251881Speter        apr_int64_t lower_repos_id;
4878251881Speter
4879251881Speter        upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4880251881Speter        lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4881251881Speter
4882251881Speter        if (upper_repos_id != lower_repos_id)
4883251881Speter          {
4884251881Speter            different = TRUE;
4885251881Speter            break;
4886251881Speter          }
4887251881Speter      }
4888251881Speter
4889251881Speter      /* Check moved_here? */
4890251881Speter
4891251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
4892251881Speter    }
4893251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4894251881Speter
4895251881Speter  if (! different)
4896251881Speter    {
4897251881Speter      /* Ok, we can now safely remove this complete move, because we
4898251881Speter         determined that it 100% matches the layer below it. */
4899251881Speter
4900251881Speter      /* ### We could copy the recorded timestamps from the higher to the
4901251881Speter             lower layer in an attempt to improve status performance, but
4902251881Speter             generally these values should be the same anyway as it was
4903251881Speter             a no-op move. */
4904251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4905251881Speter                                        STMT_DELETE_MOVED_BACK));
4906251881Speter
4907251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4908251881Speter                                             local_relpath,
4909251881Speter                                             relpath_depth(local_relpath)));
4910251881Speter
4911251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
4912251881Speter
4913251881Speter      if (moved_back)
4914251881Speter        *moved_back = TRUE;
4915251881Speter    }
4916251881Speter
4917251881Speter  return SVN_NO_ERROR;
4918251881Speter}
4919251881Speter
4920251881Spetersvn_error_t *
4921251881Spetersvn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4922251881Speter                               svn_wc__db_t *db,
4923251881Speter                               const char *local_abspath,
4924251881Speter                               const char *moved_from_abspath,
4925251881Speter                               const svn_skel_t *work_items,
4926251881Speter                               apr_pool_t *scratch_pool)
4927251881Speter{
4928251881Speter  svn_wc__db_wcroot_t *wcroot;
4929251881Speter  const char *local_relpath;
4930251881Speter  const char *moved_from_relpath;
4931251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4932251881Speter
4933251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4934251881Speter                                                local_abspath,
4935251881Speter                                                scratch_pool, scratch_pool));
4936251881Speter  VERIFY_USABLE_WCROOT(wcroot);
4937251881Speter
4938251881Speter  if (moved_back)
4939251881Speter    *moved_back = FALSE;
4940251881Speter
4941251881Speter  moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4942251881Speter                                                moved_from_abspath);
4943251881Speter
4944251881Speter  if (! local_relpath[0]
4945251881Speter      || !moved_from_relpath)
4946251881Speter    {
4947251881Speter       /* WC-Roots can't be moved */
4948251881Speter      SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4949251881Speter      return SVN_NO_ERROR;
4950251881Speter    }
4951251881Speter
4952251881Speter  SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4953251881Speter                                       moved_from_relpath, work_items,
4954251881Speter                                       scratch_pool),
4955251881Speter                      wcroot);
4956251881Speter
4957251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4958251881Speter                        scratch_pool));
4959251881Speter
4960251881Speter  return SVN_NO_ERROR;
4961251881Speter}
4962251881Speter
4963251881Speter
4964251881Speter/* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4965251881Speter *
4966251881Speter * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4967251881Speter * a move, and indicates the op-depth of the move destination op-root. */
4968251881Speterstatic svn_error_t *
4969251881Speterdb_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4970251881Speter                          const char *src_relpath,
4971251881Speter                          int src_op_depth,
4972251881Speter                          svn_wc__db_wcroot_t *dst_wcroot,
4973251881Speter                          const char *dst_relpath,
4974251881Speter                          int dst_op_depth,
4975251881Speter                          int del_op_depth,
4976251881Speter                          apr_int64_t repos_id,
4977251881Speter                          const char *repos_relpath,
4978251881Speter                          svn_revnum_t revision,
4979251881Speter                          int move_op_depth,
4980251881Speter                          apr_pool_t *scratch_pool)
4981251881Speter{
4982251881Speter  const apr_array_header_t *children;
4983251881Speter  apr_pool_t *iterpool;
4984251881Speter  svn_wc__db_status_t status;
4985251881Speter  svn_node_kind_t kind;
4986251881Speter  svn_revnum_t node_revision;
4987251881Speter  const char *node_repos_relpath;
4988251881Speter  apr_int64_t node_repos_id;
4989251881Speter  svn_sqlite__stmt_t *stmt;
4990251881Speter  svn_wc__db_status_t dst_presence;
4991251881Speter  int i;
4992251881Speter
4993251881Speter  {
4994251881Speter    svn_error_t *err;
4995251881Speter    err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
4996251881Speter                                    &node_repos_relpath, &node_repos_id,
4997251881Speter                                    NULL, NULL, NULL, NULL, NULL, NULL,
4998251881Speter                                    NULL, NULL,
4999251881Speter                                    src_wcroot, src_relpath, src_op_depth,
5000251881Speter                                    scratch_pool, scratch_pool);
5001251881Speter
5002251881Speter    if (err)
5003251881Speter      {
5004251881Speter        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5005251881Speter          return svn_error_trace(err);
5006251881Speter
5007251881Speter        svn_error_clear(err);
5008251881Speter        return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5009251881Speter      }
5010251881Speter  }
5011251881Speter
5012251881Speter  if (src_op_depth == 0)
5013251881Speter    {
5014251881Speter      /* If the node is switched or has a different revision then its parent
5015251881Speter         we shouldn't copy it. (We can't as we would have to insert it at
5016251881Speter         an unshadowed depth) */
5017251881Speter      if (status == svn_wc__db_status_not_present
5018251881Speter          || status == svn_wc__db_status_excluded
5019251881Speter          || status == svn_wc__db_status_server_excluded
5020251881Speter          || node_revision != revision
5021251881Speter          || node_repos_id != repos_id
5022251881Speter          || strcmp(node_repos_relpath, repos_relpath))
5023251881Speter        {
5024251881Speter          /* Add a not-present node in the destination wcroot */
5025251881Speter          struct insert_working_baton_t iwb;
5026251881Speter          const char *repos_root_url;
5027251881Speter          const char *repos_uuid;
5028251881Speter
5029251881Speter          SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5030251881Speter                                              src_wcroot->sdb, node_repos_id,
5031251881Speter                                              scratch_pool));
5032251881Speter
5033251881Speter          SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5034251881Speter                                  dst_wcroot->sdb, scratch_pool));
5035251881Speter
5036251881Speter          blank_iwb(&iwb);
5037251881Speter
5038251881Speter          iwb.op_depth = dst_op_depth;
5039251881Speter          if (status != svn_wc__db_status_excluded)
5040251881Speter            iwb.presence = svn_wc__db_status_not_present;
5041251881Speter          else
5042251881Speter            iwb.presence = svn_wc__db_status_excluded;
5043251881Speter
5044251881Speter          iwb.kind = kind;
5045251881Speter
5046251881Speter          iwb.original_repos_id = node_repos_id;
5047251881Speter          iwb.original_revnum = node_revision;
5048251881Speter          iwb.original_repos_relpath = node_repos_relpath;
5049251881Speter
5050251881Speter          SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5051251881Speter                                      scratch_pool));
5052251881Speter
5053251881Speter          return SVN_NO_ERROR;
5054251881Speter        }
5055251881Speter    }
5056251881Speter
5057251881Speter  iterpool = svn_pool_create(scratch_pool);
5058251881Speter
5059251881Speter  switch (status)
5060251881Speter    {
5061251881Speter    case svn_wc__db_status_normal:
5062251881Speter    case svn_wc__db_status_added:
5063251881Speter    case svn_wc__db_status_moved_here:
5064251881Speter    case svn_wc__db_status_copied:
5065251881Speter      dst_presence = svn_wc__db_status_normal;
5066251881Speter      break;
5067251881Speter    case svn_wc__db_status_deleted:
5068251881Speter    case svn_wc__db_status_not_present:
5069251881Speter      dst_presence = svn_wc__db_status_not_present;
5070251881Speter      break;
5071251881Speter    case svn_wc__db_status_excluded:
5072251881Speter      dst_presence = svn_wc__db_status_excluded;
5073251881Speter      break;
5074251881Speter    case svn_wc__db_status_server_excluded:
5075251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5076251881Speter                               _("Cannot copy '%s' excluded by server"),
5077251881Speter                               path_for_error_message(src_wcroot,
5078251881Speter                                                      src_relpath,
5079251881Speter                                                      scratch_pool));
5080251881Speter    default:
5081251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5082251881Speter                               _("Cannot handle status of '%s'"),
5083251881Speter                               path_for_error_message(src_wcroot,
5084251881Speter                                                      src_relpath,
5085251881Speter                                                      scratch_pool));
5086251881Speter    }
5087251881Speter
5088251881Speter  if (dst_presence == svn_wc__db_status_normal
5089251881Speter      && src_wcroot == dst_wcroot) /* ### Remove limitation */
5090251881Speter    {
5091251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5092251881Speter                             STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5093251881Speter
5094251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5095251881Speter                        src_wcroot->wc_id, src_relpath,
5096251881Speter                        dst_relpath,
5097251881Speter                        dst_op_depth,
5098251881Speter                        svn_relpath_dirname(dst_relpath, iterpool),
5099251881Speter                        presence_map, dst_presence,
5100251881Speter                        src_op_depth));
5101251881Speter
5102251881Speter      /* moved_here */
5103251881Speter      if (dst_op_depth == move_op_depth)
5104251881Speter        SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5105251881Speter
5106251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
5107251881Speter
5108251881Speter      {
5109251881Speter        /* And mark it deleted to allow proper shadowing */
5110251881Speter        struct insert_working_baton_t iwb;
5111251881Speter
5112251881Speter        blank_iwb(&iwb);
5113251881Speter
5114251881Speter        iwb.op_depth = del_op_depth;
5115251881Speter        iwb.presence = svn_wc__db_status_base_deleted;
5116251881Speter
5117251881Speter        iwb.kind = kind;
5118251881Speter
5119251881Speter        SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5120251881Speter                                    scratch_pool));
5121251881Speter      }
5122251881Speter    }
5123251881Speter  else
5124251881Speter    {
5125251881Speter      struct insert_working_baton_t iwb;
5126251881Speter      if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5127251881Speter        dst_presence = svn_wc__db_status_not_present;
5128251881Speter
5129251881Speter      /* And mark it deleted to allow proper shadowing */
5130251881Speter
5131251881Speter      blank_iwb(&iwb);
5132251881Speter
5133251881Speter      iwb.op_depth = dst_op_depth;
5134251881Speter      iwb.presence = dst_presence;
5135251881Speter      iwb.kind = kind;
5136251881Speter
5137251881Speter      SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5138251881Speter                                    scratch_pool));
5139251881Speter    }
5140251881Speter
5141269847Speter  if (dst_presence == svn_wc__db_status_not_present)
5142269847Speter    {
5143269847Speter      /* Don't create descendants of a not present node! */
5144269847Speter
5145269847Speter      /* This code is currently still triggered by copying deleted nodes
5146269847Speter         between separate working copies. See ### comment above. */
5147269847Speter
5148269847Speter      svn_pool_destroy(iterpool);
5149269847Speter      return SVN_NO_ERROR;
5150269847Speter    }
5151269847Speter
5152251881Speter  SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5153251881Speter                               src_op_depth, scratch_pool, iterpool));
5154251881Speter
5155251881Speter  for (i = 0; i < children->nelts; i++)
5156251881Speter    {
5157251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
5158251881Speter      const char *child_src_relpath;
5159251881Speter      const char *child_dst_relpath;
5160251881Speter      const char *child_repos_relpath = NULL;
5161251881Speter
5162251881Speter      svn_pool_clear(iterpool);
5163251881Speter      child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5164251881Speter      child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5165251881Speter
5166251881Speter      if (repos_relpath)
5167251881Speter        child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5168251881Speter
5169251881Speter      SVN_ERR(db_op_copy_shadowed_layer(
5170251881Speter                         src_wcroot, child_src_relpath, src_op_depth,
5171251881Speter                         dst_wcroot, child_dst_relpath, dst_op_depth,
5172251881Speter                         del_op_depth,
5173251881Speter                         repos_id, child_repos_relpath, revision,
5174251881Speter                         move_op_depth, scratch_pool));
5175251881Speter    }
5176251881Speter
5177251881Speter  svn_pool_destroy(iterpool);
5178251881Speter
5179251881Speter  return SVN_NO_ERROR;
5180251881Speter}
5181251881Speter
5182251881Speter/* Helper for svn_wc__db_op_copy_shadowed_layer().
5183251881Speter *
5184251881Speter * Implements  svn_sqlite__transaction_callback_t. */
5185251881Speterstatic svn_error_t *
5186251881Speterop_copy_shadowed_layer_txn(void *baton,
5187251881Speter                           svn_sqlite__db_t *sdb,
5188251881Speter                           apr_pool_t *scratch_pool)
5189251881Speter{
5190251881Speter  struct op_copy_baton *ocb = baton;
5191251881Speter  const char *src_parent_relpath;
5192251881Speter  const char *dst_parent_relpath;
5193251881Speter  int src_op_depth;
5194251881Speter  int dst_op_depth;
5195251881Speter  int del_op_depth;
5196251881Speter  const char *repos_relpath = NULL;
5197251881Speter  apr_int64_t repos_id = INVALID_REPOS_ID;
5198251881Speter  svn_revnum_t revision = SVN_INVALID_REVNUM;
5199251881Speter
5200251881Speter  if (sdb != ocb->dst_wcroot->sdb)
5201251881Speter    {
5202251881Speter       /* Source and destination databases differ; so also start a lock
5203251881Speter          in the destination database, by calling ourself in a lock. */
5204251881Speter
5205251881Speter      return svn_error_trace(
5206251881Speter                        svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5207251881Speter                                              op_copy_shadowed_layer_txn,
5208251881Speter                                              ocb, scratch_pool));
5209251881Speter    }
5210251881Speter
5211251881Speter  /* From this point we can assume a lock in the src and dst databases */
5212251881Speter
5213251881Speter
5214251881Speter  /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5215251881Speter  SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5216251881Speter
5217251881Speter  src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5218251881Speter  dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5219251881Speter
5220251881Speter  /* src_parent must be status normal or added; get its op-depth */
5221251881Speter  SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5222251881Speter
5223251881Speter  /* dst_parent must be status added; get its op-depth */
5224251881Speter  SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5225251881Speter
5226251881Speter  del_op_depth = relpath_depth(ocb->dst_relpath);
5227251881Speter
5228251881Speter  /* Get some information from the parent */
5229251881Speter  SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5230251881Speter                                    &repos_id, NULL, NULL, NULL, NULL, NULL,
5231251881Speter                                    NULL, NULL, NULL,
5232251881Speter                                    ocb->src_wcroot,
5233251881Speter                                    src_parent_relpath, src_op_depth,
5234251881Speter                                    scratch_pool, scratch_pool));
5235251881Speter
5236251881Speter  if (repos_relpath == NULL)
5237251881Speter    {
5238251881Speter      /* The node is a local addition and has no shadowed information */
5239251881Speter      return SVN_NO_ERROR;
5240251881Speter    }
5241251881Speter
5242251881Speter  /* And calculate the child repos relpath */
5243251881Speter  repos_relpath = svn_relpath_join(repos_relpath,
5244251881Speter                                   svn_relpath_basename(ocb->src_relpath,
5245251881Speter                                                        NULL),
5246251881Speter                                   scratch_pool);
5247251881Speter
5248251881Speter  SVN_ERR(db_op_copy_shadowed_layer(
5249251881Speter                        ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5250251881Speter                        ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5251251881Speter                        del_op_depth,
5252251881Speter                        repos_id, repos_relpath, revision,
5253251881Speter                        (ocb->is_move ? dst_op_depth : 0),
5254251881Speter                        scratch_pool));
5255251881Speter
5256251881Speter  return SVN_NO_ERROR;
5257251881Speter}
5258251881Speter
5259251881Spetersvn_error_t *
5260251881Spetersvn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5261251881Speter                                  const char *src_abspath,
5262251881Speter                                  const char *dst_abspath,
5263251881Speter                                  svn_boolean_t is_move,
5264251881Speter                                  apr_pool_t *scratch_pool)
5265251881Speter{
5266251881Speter  struct op_copy_baton ocb = {0};
5267251881Speter
5268251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5269251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5270251881Speter
5271251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5272251881Speter                                                &ocb.src_relpath, db,
5273251881Speter                                                src_abspath,
5274251881Speter                                                scratch_pool, scratch_pool));
5275251881Speter  VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5276251881Speter
5277251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5278251881Speter                                                &ocb.dst_relpath,
5279251881Speter                                                db, dst_abspath,
5280251881Speter                                                scratch_pool, scratch_pool));
5281251881Speter  VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5282251881Speter
5283251881Speter  ocb.is_move = is_move;
5284251881Speter  ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5285251881Speter
5286251881Speter  ocb.work_items = NULL;
5287251881Speter
5288251881Speter  /* Call with the sdb in src_wcroot. It might call itself again to
5289251881Speter     also obtain a lock in dst_wcroot */
5290251881Speter  SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5291251881Speter                                op_copy_shadowed_layer_txn,
5292251881Speter                                &ocb, scratch_pool));
5293251881Speter
5294251881Speter  return SVN_NO_ERROR;
5295251881Speter}
5296251881Speter
5297251881Speter
5298251881Speter/* If there are any server-excluded base nodes then the copy must fail
5299251881Speter   as it's not possible to commit such a copy.
5300251881Speter   Return an error if there are any server-excluded nodes. */
5301251881Speterstatic svn_error_t *
5302251881Spetercatch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5303251881Speter                              const char *local_relpath,
5304251881Speter                              apr_pool_t *scratch_pool)
5305251881Speter{
5306251881Speter  svn_sqlite__stmt_t *stmt;
5307251881Speter  svn_boolean_t have_row;
5308251881Speter  const char *server_excluded_relpath;
5309251881Speter
5310251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5311251881Speter                                    STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5312251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
5313251881Speter                            wcroot->wc_id,
5314251881Speter                            local_relpath));
5315251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
5316251881Speter  if (have_row)
5317251881Speter    server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5318251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
5319251881Speter  if (have_row)
5320251881Speter    return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5321251881Speter                             _("Cannot copy '%s' excluded by server"),
5322251881Speter                             path_for_error_message(wcroot,
5323251881Speter                                                    server_excluded_relpath,
5324251881Speter                                                    scratch_pool));
5325251881Speter
5326251881Speter  return SVN_NO_ERROR;
5327251881Speter}
5328251881Speter
5329251881Speter
5330251881Spetersvn_error_t *
5331251881Spetersvn_wc__db_op_copy_dir(svn_wc__db_t *db,
5332251881Speter                       const char *local_abspath,
5333251881Speter                       const apr_hash_t *props,
5334251881Speter                       svn_revnum_t changed_rev,
5335251881Speter                       apr_time_t changed_date,
5336251881Speter                       const char *changed_author,
5337251881Speter                       const char *original_repos_relpath,
5338251881Speter                       const char *original_root_url,
5339251881Speter                       const char *original_uuid,
5340251881Speter                       svn_revnum_t original_revision,
5341251881Speter                       const apr_array_header_t *children,
5342251881Speter                       svn_boolean_t is_move,
5343251881Speter                       svn_depth_t depth,
5344251881Speter                       const svn_skel_t *conflict,
5345251881Speter                       const svn_skel_t *work_items,
5346251881Speter                       apr_pool_t *scratch_pool)
5347251881Speter{
5348251881Speter  svn_wc__db_wcroot_t *wcroot;
5349251881Speter  const char *local_relpath;
5350251881Speter  insert_working_baton_t iwb;
5351251881Speter  int parent_op_depth;
5352251881Speter
5353251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5354251881Speter  SVN_ERR_ASSERT(props != NULL);
5355251881Speter  /* ### any assertions for CHANGED_* ?  */
5356251881Speter  /* ### any assertions for ORIGINAL_* ?  */
5357251881Speter#if 0
5358251881Speter  SVN_ERR_ASSERT(children != NULL);
5359251881Speter#endif
5360251881Speter
5361251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5362251881Speter                              local_abspath, scratch_pool, scratch_pool));
5363251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5364251881Speter
5365251881Speter  blank_iwb(&iwb);
5366251881Speter
5367251881Speter  iwb.presence = svn_wc__db_status_normal;
5368251881Speter  iwb.kind = svn_node_dir;
5369251881Speter
5370251881Speter  iwb.props = props;
5371251881Speter  iwb.changed_rev = changed_rev;
5372251881Speter  iwb.changed_date = changed_date;
5373251881Speter  iwb.changed_author = changed_author;
5374251881Speter
5375251881Speter  if (original_root_url != NULL)
5376251881Speter    {
5377251881Speter      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5378251881Speter                              original_root_url, original_uuid,
5379251881Speter                              wcroot->sdb, scratch_pool));
5380251881Speter      iwb.original_repos_relpath = original_repos_relpath;
5381251881Speter      iwb.original_revnum = original_revision;
5382251881Speter    }
5383251881Speter
5384251881Speter  /* ### Should we do this inside the transaction? */
5385251881Speter  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5386251881Speter                            &parent_op_depth, iwb.original_repos_id,
5387251881Speter                            original_repos_relpath, original_revision,
5388251881Speter                            wcroot, local_relpath, scratch_pool));
5389251881Speter
5390251881Speter  iwb.children = children;
5391251881Speter  iwb.depth = depth;
5392251881Speter  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5393251881Speter                               iwb.op_depth == parent_op_depth);
5394251881Speter
5395251881Speter  iwb.work_items = work_items;
5396251881Speter  iwb.conflict = conflict;
5397251881Speter
5398251881Speter  SVN_WC__DB_WITH_TXN(
5399251881Speter                insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5400251881Speter                wcroot);
5401251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5402251881Speter
5403251881Speter  return SVN_NO_ERROR;
5404251881Speter}
5405251881Speter
5406251881Speter
5407251881Spetersvn_error_t *
5408251881Spetersvn_wc__db_op_copy_file(svn_wc__db_t *db,
5409251881Speter                        const char *local_abspath,
5410251881Speter                        const apr_hash_t *props,
5411251881Speter                        svn_revnum_t changed_rev,
5412251881Speter                        apr_time_t changed_date,
5413251881Speter                        const char *changed_author,
5414251881Speter                        const char *original_repos_relpath,
5415251881Speter                        const char *original_root_url,
5416251881Speter                        const char *original_uuid,
5417251881Speter                        svn_revnum_t original_revision,
5418251881Speter                        const svn_checksum_t *checksum,
5419251881Speter                        svn_boolean_t update_actual_props,
5420251881Speter                        const apr_hash_t *new_actual_props,
5421251881Speter                        svn_boolean_t is_move,
5422251881Speter                        const svn_skel_t *conflict,
5423251881Speter                        const svn_skel_t *work_items,
5424251881Speter                        apr_pool_t *scratch_pool)
5425251881Speter{
5426251881Speter  svn_wc__db_wcroot_t *wcroot;
5427251881Speter  const char *local_relpath;
5428251881Speter  insert_working_baton_t iwb;
5429251881Speter  int parent_op_depth;
5430251881Speter
5431251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5432251881Speter  SVN_ERR_ASSERT(props != NULL);
5433251881Speter  /* ### any assertions for CHANGED_* ?  */
5434251881Speter  SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5435251881Speter                  && ! original_uuid && ! checksum
5436251881Speter                  && original_revision == SVN_INVALID_REVNUM)
5437251881Speter                 || (original_repos_relpath && original_root_url
5438251881Speter                     && original_uuid && checksum
5439251881Speter                     && original_revision != SVN_INVALID_REVNUM));
5440251881Speter
5441251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5442251881Speter                              local_abspath, scratch_pool, scratch_pool));
5443251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5444251881Speter
5445251881Speter  blank_iwb(&iwb);
5446251881Speter
5447251881Speter  iwb.presence = svn_wc__db_status_normal;
5448251881Speter  iwb.kind = svn_node_file;
5449251881Speter
5450251881Speter  iwb.props = props;
5451251881Speter  iwb.changed_rev = changed_rev;
5452251881Speter  iwb.changed_date = changed_date;
5453251881Speter  iwb.changed_author = changed_author;
5454251881Speter
5455251881Speter  if (original_root_url != NULL)
5456251881Speter    {
5457251881Speter      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5458251881Speter                              original_root_url, original_uuid,
5459251881Speter                              wcroot->sdb, scratch_pool));
5460251881Speter      iwb.original_repos_relpath = original_repos_relpath;
5461251881Speter      iwb.original_revnum = original_revision;
5462251881Speter    }
5463251881Speter
5464251881Speter  /* ### Should we do this inside the transaction? */
5465251881Speter  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5466251881Speter                            &parent_op_depth, iwb.original_repos_id,
5467251881Speter                            original_repos_relpath, original_revision,
5468251881Speter                            wcroot, local_relpath, scratch_pool));
5469251881Speter
5470251881Speter  iwb.checksum = checksum;
5471251881Speter  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5472251881Speter                               iwb.op_depth == parent_op_depth);
5473251881Speter
5474251881Speter  if (update_actual_props)
5475251881Speter    {
5476251881Speter      iwb.update_actual_props = update_actual_props;
5477251881Speter      iwb.new_actual_props = new_actual_props;
5478251881Speter    }
5479251881Speter
5480251881Speter  iwb.work_items = work_items;
5481251881Speter  iwb.conflict = conflict;
5482251881Speter
5483251881Speter  SVN_WC__DB_WITH_TXN(
5484251881Speter          insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5485251881Speter          wcroot);
5486251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5487251881Speter
5488251881Speter  return SVN_NO_ERROR;
5489251881Speter}
5490251881Speter
5491251881Speter
5492251881Spetersvn_error_t *
5493251881Spetersvn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5494251881Speter                           const char *local_abspath,
5495251881Speter                           const apr_hash_t *props,
5496251881Speter                           svn_revnum_t changed_rev,
5497251881Speter                           apr_time_t changed_date,
5498251881Speter                           const char *changed_author,
5499251881Speter                           const char *original_repos_relpath,
5500251881Speter                           const char *original_root_url,
5501251881Speter                           const char *original_uuid,
5502251881Speter                           svn_revnum_t original_revision,
5503251881Speter                           const char *target,
5504251881Speter                           const svn_skel_t *conflict,
5505251881Speter                           const svn_skel_t *work_items,
5506251881Speter                           apr_pool_t *scratch_pool)
5507251881Speter{
5508251881Speter  svn_wc__db_wcroot_t *wcroot;
5509251881Speter  const char *local_relpath;
5510251881Speter  insert_working_baton_t iwb;
5511251881Speter  int parent_op_depth;
5512251881Speter
5513251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5514251881Speter  SVN_ERR_ASSERT(props != NULL);
5515251881Speter  /* ### any assertions for CHANGED_* ?  */
5516251881Speter  /* ### any assertions for ORIGINAL_* ?  */
5517251881Speter  SVN_ERR_ASSERT(target != NULL);
5518251881Speter
5519251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5520251881Speter                              local_abspath, scratch_pool, scratch_pool));
5521251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5522251881Speter
5523251881Speter  blank_iwb(&iwb);
5524251881Speter
5525251881Speter  iwb.presence = svn_wc__db_status_normal;
5526251881Speter  iwb.kind = svn_node_symlink;
5527251881Speter
5528251881Speter  iwb.props = props;
5529251881Speter  iwb.changed_rev = changed_rev;
5530251881Speter  iwb.changed_date = changed_date;
5531251881Speter  iwb.changed_author = changed_author;
5532251881Speter  iwb.moved_here = FALSE;
5533251881Speter
5534251881Speter  if (original_root_url != NULL)
5535251881Speter    {
5536251881Speter      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5537251881Speter                              original_root_url, original_uuid,
5538251881Speter                              wcroot->sdb, scratch_pool));
5539251881Speter      iwb.original_repos_relpath = original_repos_relpath;
5540251881Speter      iwb.original_revnum = original_revision;
5541251881Speter    }
5542251881Speter
5543251881Speter  /* ### Should we do this inside the transaction? */
5544251881Speter  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5545251881Speter                            &parent_op_depth, iwb.original_repos_id,
5546251881Speter                            original_repos_relpath, original_revision,
5547251881Speter                            wcroot, local_relpath, scratch_pool));
5548251881Speter
5549251881Speter  iwb.target = target;
5550251881Speter
5551251881Speter  iwb.work_items = work_items;
5552251881Speter  iwb.conflict = conflict;
5553251881Speter
5554251881Speter  SVN_WC__DB_WITH_TXN(
5555251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5556251881Speter            wcroot);
5557251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5558251881Speter
5559251881Speter  return SVN_NO_ERROR;
5560251881Speter}
5561251881Speter
5562251881Speter
5563251881Spetersvn_error_t *
5564251881Spetersvn_wc__db_op_add_directory(svn_wc__db_t *db,
5565251881Speter                            const char *local_abspath,
5566251881Speter                            const apr_hash_t *props,
5567251881Speter                            const svn_skel_t *work_items,
5568251881Speter                            apr_pool_t *scratch_pool)
5569251881Speter{
5570251881Speter  svn_wc__db_wcroot_t *wcroot;
5571251881Speter  const char *local_relpath;
5572251881Speter  const char *dir_abspath;
5573251881Speter  const char *name;
5574251881Speter  insert_working_baton_t iwb;
5575251881Speter
5576251881Speter  /* Resolve wcroot via parent directory to avoid obstruction handling */
5577251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5578251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5579251881Speter
5580251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5581251881Speter                              dir_abspath, scratch_pool, scratch_pool));
5582251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5583251881Speter
5584251881Speter  blank_iwb(&iwb);
5585251881Speter
5586251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5587251881Speter  iwb.presence = svn_wc__db_status_normal;
5588251881Speter  iwb.kind = svn_node_dir;
5589251881Speter  iwb.op_depth = relpath_depth(local_relpath);
5590251881Speter  if (props && apr_hash_count((apr_hash_t *)props))
5591251881Speter    {
5592251881Speter      iwb.update_actual_props = TRUE;
5593251881Speter      iwb.new_actual_props = props;
5594251881Speter    }
5595251881Speter
5596251881Speter  iwb.work_items = work_items;
5597251881Speter
5598251881Speter  SVN_WC__DB_WITH_TXN(
5599251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5600251881Speter            wcroot);
5601251881Speter  /* Use depth infinity to make sure we have no invalid cached information
5602251881Speter   * about children of this dir. */
5603251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5604251881Speter                        scratch_pool));
5605251881Speter
5606251881Speter  return SVN_NO_ERROR;
5607251881Speter}
5608251881Speter
5609251881Speter
5610251881Spetersvn_error_t *
5611251881Spetersvn_wc__db_op_add_file(svn_wc__db_t *db,
5612251881Speter                       const char *local_abspath,
5613251881Speter                       const apr_hash_t *props,
5614251881Speter                       const svn_skel_t *work_items,
5615251881Speter                       apr_pool_t *scratch_pool)
5616251881Speter{
5617251881Speter  svn_wc__db_wcroot_t *wcroot;
5618251881Speter  const char *local_relpath;
5619251881Speter  insert_working_baton_t iwb;
5620251881Speter  const char *dir_abspath;
5621251881Speter  const char *name;
5622251881Speter
5623251881Speter  /* Resolve wcroot via parent directory to avoid obstruction handling */
5624251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5625251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5626251881Speter
5627251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5628251881Speter                              dir_abspath, scratch_pool, scratch_pool));
5629251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5630251881Speter
5631251881Speter  blank_iwb(&iwb);
5632251881Speter
5633251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5634251881Speter  iwb.presence = svn_wc__db_status_normal;
5635251881Speter  iwb.kind = svn_node_file;
5636251881Speter  iwb.op_depth = relpath_depth(local_relpath);
5637251881Speter  if (props && apr_hash_count((apr_hash_t *)props))
5638251881Speter    {
5639251881Speter      iwb.update_actual_props = TRUE;
5640251881Speter      iwb.new_actual_props = props;
5641251881Speter    }
5642251881Speter
5643251881Speter  iwb.work_items = work_items;
5644251881Speter
5645251881Speter  SVN_WC__DB_WITH_TXN(
5646251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5647251881Speter            wcroot);
5648251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5649251881Speter
5650251881Speter  return SVN_NO_ERROR;
5651251881Speter}
5652251881Speter
5653251881Speter
5654251881Spetersvn_error_t *
5655251881Spetersvn_wc__db_op_add_symlink(svn_wc__db_t *db,
5656251881Speter                          const char *local_abspath,
5657251881Speter                          const char *target,
5658251881Speter                          const apr_hash_t *props,
5659251881Speter                          const svn_skel_t *work_items,
5660251881Speter                          apr_pool_t *scratch_pool)
5661251881Speter{
5662251881Speter  svn_wc__db_wcroot_t *wcroot;
5663251881Speter  const char *local_relpath;
5664251881Speter  insert_working_baton_t iwb;
5665251881Speter  const char *dir_abspath;
5666251881Speter  const char *name;
5667251881Speter
5668251881Speter  /* Resolve wcroot via parent directory to avoid obstruction handling */
5669251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5670251881Speter  SVN_ERR_ASSERT(target != NULL);
5671251881Speter
5672251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5673251881Speter
5674251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5675251881Speter                              dir_abspath, scratch_pool, scratch_pool));
5676251881Speter
5677251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5678251881Speter
5679251881Speter  blank_iwb(&iwb);
5680251881Speter
5681251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5682251881Speter  iwb.presence = svn_wc__db_status_normal;
5683251881Speter  iwb.kind = svn_node_symlink;
5684251881Speter  iwb.op_depth = relpath_depth(local_relpath);
5685251881Speter  if (props && apr_hash_count((apr_hash_t *)props))
5686251881Speter    {
5687251881Speter      iwb.update_actual_props = TRUE;
5688251881Speter      iwb.new_actual_props = props;
5689251881Speter    }
5690251881Speter
5691251881Speter  iwb.target = target;
5692251881Speter
5693251881Speter  iwb.work_items = work_items;
5694251881Speter
5695251881Speter  SVN_WC__DB_WITH_TXN(
5696251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5697251881Speter            wcroot);
5698251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5699251881Speter
5700251881Speter  return SVN_NO_ERROR;
5701251881Speter}
5702251881Speter
5703251881Speter/* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5704251881Speterstatic svn_error_t *
5705251881Speterdb_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5706251881Speter                   const char *local_relpath,
5707251881Speter                   apr_int64_t recorded_size,
5708251881Speter                   apr_int64_t recorded_time,
5709251881Speter                   apr_pool_t *scratch_pool)
5710251881Speter{
5711251881Speter  svn_sqlite__stmt_t *stmt;
5712251881Speter  int affected_rows;
5713251881Speter
5714251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5715251881Speter                                    STMT_UPDATE_NODE_FILEINFO));
5716251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5717251881Speter                            recorded_size, recorded_time));
5718251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5719251881Speter
5720251881Speter  SVN_ERR_ASSERT(affected_rows == 1);
5721251881Speter
5722251881Speter  return SVN_NO_ERROR;
5723251881Speter}
5724251881Speter
5725251881Speter
5726251881Spetersvn_error_t *
5727251881Spetersvn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5728251881Speter                                  const char *local_abspath,
5729251881Speter                                  svn_filesize_t recorded_size,
5730251881Speter                                  apr_time_t recorded_time,
5731251881Speter                                  apr_pool_t *scratch_pool)
5732251881Speter{
5733251881Speter  svn_wc__db_wcroot_t *wcroot;
5734251881Speter  const char *local_relpath;
5735251881Speter
5736251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5737251881Speter
5738251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5739251881Speter                              local_abspath, scratch_pool, scratch_pool));
5740251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5741251881Speter
5742251881Speter  SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5743251881Speter                             recorded_size, recorded_time, scratch_pool));
5744251881Speter
5745251881Speter  /* We *totally* monkeyed the entries. Toss 'em.  */
5746251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5747251881Speter
5748251881Speter  return SVN_NO_ERROR;
5749251881Speter}
5750251881Speter
5751251881Speter
5752251881Speter/* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5753251881Speter * PROPS.
5754251881Speter *
5755251881Speter * Note: PROPS=NULL means the actual props are the same as the pristine
5756251881Speter * props; to indicate no properties when the pristine has some props,
5757251881Speter * PROPS must be an empty hash. */
5758251881Speterstatic svn_error_t *
5759251881Speterset_actual_props(apr_int64_t wc_id,
5760251881Speter                 const char *local_relpath,
5761251881Speter                 apr_hash_t *props,
5762251881Speter                 svn_sqlite__db_t *db,
5763251881Speter                 apr_pool_t *scratch_pool)
5764251881Speter{
5765251881Speter  svn_sqlite__stmt_t *stmt;
5766251881Speter  int affected_rows;
5767251881Speter
5768251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5769251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5770251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5771251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5772251881Speter
5773251881Speter  if (affected_rows == 1 || !props)
5774251881Speter    return SVN_NO_ERROR; /* We are done */
5775251881Speter
5776251881Speter  /* We have to insert a row in ACTUAL */
5777251881Speter
5778251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5779251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5780251881Speter  if (*local_relpath != '\0')
5781251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5782251881Speter                                  svn_relpath_dirname(local_relpath,
5783251881Speter                                                      scratch_pool)));
5784251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5785251881Speter  return svn_error_trace(svn_sqlite__step_done(stmt));
5786251881Speter}
5787251881Speter
5788251881Speter
5789251881Speter/* The body of svn_wc__db_op_set_props().
5790251881Speter
5791251881Speter   Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5792251881Speter   Create an entry in the ACTUAL table for the node if it does not yet
5793251881Speter   have one.
5794251881Speter   To specify no properties, BATON->props must be an empty hash, not NULL.
5795251881Speter   BATON is of type 'struct set_props_baton_t'.
5796251881Speter*/
5797251881Speterstatic svn_error_t *
5798251881Speterset_props_txn(svn_wc__db_wcroot_t *wcroot,
5799251881Speter              const char *local_relpath,
5800251881Speter              apr_hash_t *props,
5801251881Speter              svn_boolean_t clear_recorded_info,
5802251881Speter              const svn_skel_t *conflict,
5803251881Speter              const svn_skel_t *work_items,
5804251881Speter              apr_pool_t *scratch_pool)
5805251881Speter{
5806251881Speter  apr_hash_t *pristine_props;
5807251881Speter
5808251881Speter  /* Check if the props are modified. If no changes, then wipe out the
5809251881Speter     ACTUAL props.  PRISTINE_PROPS==NULL means that any
5810251881Speter     ACTUAL props are okay as provided, so go ahead and set them.  */
5811251881Speter  SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5812251881Speter                                 scratch_pool, scratch_pool));
5813251881Speter  if (props && pristine_props)
5814251881Speter    {
5815251881Speter      apr_array_header_t *prop_diffs;
5816251881Speter
5817251881Speter      SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5818251881Speter                             scratch_pool));
5819251881Speter      if (prop_diffs->nelts == 0)
5820251881Speter        props = NULL;
5821251881Speter    }
5822251881Speter
5823251881Speter  SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5824251881Speter                           props, wcroot->sdb, scratch_pool));
5825251881Speter
5826251881Speter  if (clear_recorded_info)
5827251881Speter    {
5828251881Speter      SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5829251881Speter                                 SVN_INVALID_FILESIZE, 0,
5830251881Speter                                 scratch_pool));
5831251881Speter    }
5832251881Speter
5833251881Speter  /* And finally.  */
5834251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5835251881Speter  if (conflict)
5836251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5837251881Speter                                              conflict, scratch_pool));
5838251881Speter
5839251881Speter  return SVN_NO_ERROR;
5840251881Speter}
5841251881Speter
5842251881Speter
5843251881Spetersvn_error_t *
5844251881Spetersvn_wc__db_op_set_props(svn_wc__db_t *db,
5845251881Speter                        const char *local_abspath,
5846251881Speter                        apr_hash_t *props,
5847251881Speter                        svn_boolean_t clear_recorded_info,
5848251881Speter                        const svn_skel_t *conflict,
5849251881Speter                        const svn_skel_t *work_items,
5850251881Speter                        apr_pool_t *scratch_pool)
5851251881Speter{
5852251881Speter  svn_wc__db_wcroot_t *wcroot;
5853251881Speter  const char *local_relpath;
5854251881Speter
5855251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5856251881Speter
5857251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5858251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
5859251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5860251881Speter
5861251881Speter  SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5862251881Speter                                    clear_recorded_info, conflict, work_items,
5863251881Speter                                    scratch_pool),
5864251881Speter                      wcroot);
5865251881Speter  return SVN_NO_ERROR;
5866251881Speter}
5867251881Speter
5868251881Speter
5869251881Spetersvn_error_t *
5870251881Spetersvn_wc__db_op_modified(svn_wc__db_t *db,
5871251881Speter                       const char *local_abspath,
5872251881Speter                       apr_pool_t *scratch_pool)
5873251881Speter{
5874251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5875251881Speter
5876251881Speter  NOT_IMPLEMENTED();
5877251881Speter}
5878251881Speter
5879251881Speter/* */
5880251881Speterstatic svn_error_t *
5881251881Speterpopulate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5882251881Speter                      const char *local_relpath,
5883251881Speter                      svn_depth_t depth,
5884251881Speter                      const apr_array_header_t *changelist_filter,
5885251881Speter                      apr_pool_t *scratch_pool)
5886251881Speter{
5887251881Speter  svn_sqlite__stmt_t *stmt;
5888251881Speter  int affected_rows = 0;
5889251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5890251881Speter                                      STMT_CREATE_TARGETS_LIST));
5891251881Speter
5892251881Speter  if (changelist_filter && changelist_filter->nelts > 0)
5893251881Speter    {
5894251881Speter      /* Iterate over the changelists, adding the nodes which match.
5895251881Speter         Common case: we only have one changelist, so this only
5896251881Speter         happens once. */
5897251881Speter      int i;
5898251881Speter      int stmt_idx;
5899251881Speter
5900251881Speter      switch (depth)
5901251881Speter        {
5902251881Speter          case svn_depth_empty:
5903251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5904251881Speter            break;
5905251881Speter
5906251881Speter          case svn_depth_files:
5907251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5908251881Speter            break;
5909251881Speter
5910251881Speter          case svn_depth_immediates:
5911251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5912251881Speter            break;
5913251881Speter
5914251881Speter          case svn_depth_infinity:
5915251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5916251881Speter            break;
5917251881Speter
5918251881Speter          default:
5919251881Speter            /* We don't know how to handle unknown or exclude. */
5920251881Speter            SVN_ERR_MALFUNCTION();
5921251881Speter            break;
5922251881Speter        }
5923251881Speter
5924251881Speter      for (i = 0; i < changelist_filter->nelts; i++)
5925251881Speter        {
5926251881Speter          int sub_affected;
5927251881Speter          const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5928251881Speter                                                 const char *);
5929251881Speter
5930251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5931251881Speter                                        STMT_INSERT_TARGET_WITH_CHANGELIST));
5932251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5933251881Speter                                    local_relpath, changelist));
5934251881Speter          SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5935251881Speter
5936251881Speter          /* If the root is matched by the changelist, we don't have to match
5937251881Speter             the children. As that tells us the root is a file */
5938251881Speter          if (!sub_affected && depth > svn_depth_empty)
5939251881Speter            {
5940251881Speter              SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5941251881Speter              SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5942251881Speter                                        local_relpath, changelist));
5943251881Speter              SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5944251881Speter            }
5945251881Speter
5946251881Speter          affected_rows += sub_affected;
5947251881Speter        }
5948251881Speter    }
5949251881Speter  else /* No changelist filtering */
5950251881Speter    {
5951251881Speter      int stmt_idx;
5952251881Speter      int sub_affected;
5953251881Speter
5954251881Speter      switch (depth)
5955251881Speter        {
5956251881Speter          case svn_depth_empty:
5957251881Speter            stmt_idx = STMT_INSERT_TARGET;
5958251881Speter            break;
5959251881Speter
5960251881Speter          case svn_depth_files:
5961251881Speter            stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5962251881Speter            break;
5963251881Speter
5964251881Speter          case svn_depth_immediates:
5965251881Speter            stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5966251881Speter            break;
5967251881Speter
5968251881Speter          case svn_depth_infinity:
5969251881Speter            stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5970251881Speter            break;
5971251881Speter
5972251881Speter          default:
5973251881Speter            /* We don't know how to handle unknown or exclude. */
5974251881Speter            SVN_ERR_MALFUNCTION();
5975251881Speter            break;
5976251881Speter        }
5977251881Speter
5978251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5979251881Speter                                        STMT_INSERT_TARGET));
5980251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5981251881Speter      SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5982251881Speter      affected_rows += sub_affected;
5983251881Speter
5984251881Speter      if (depth > svn_depth_empty)
5985251881Speter        {
5986251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5987251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5988251881Speter          SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5989251881Speter          affected_rows += sub_affected;
5990251881Speter        }
5991251881Speter    }
5992251881Speter
5993251881Speter  /* Does the target exist? */
5994251881Speter  if (affected_rows == 0)
5995251881Speter    {
5996251881Speter      svn_boolean_t exists;
5997251881Speter      SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
5998251881Speter
5999251881Speter      if (!exists)
6000251881Speter        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6001251881Speter                                 _("The node '%s' was not found."),
6002251881Speter                                 path_for_error_message(wcroot,
6003251881Speter                                                        local_relpath,
6004251881Speter                                                        scratch_pool));
6005251881Speter    }
6006251881Speter
6007251881Speter  return SVN_NO_ERROR;
6008251881Speter}
6009251881Speter
6010251881Speter
6011251881Speter#if 0
6012251881Speterstatic svn_error_t *
6013251881Speterdump_targets(svn_wc__db_wcroot_t *wcroot,
6014251881Speter             apr_pool_t *scratch_pool)
6015251881Speter{
6016251881Speter  svn_sqlite__stmt_t *stmt;
6017251881Speter  svn_boolean_t have_row;
6018251881Speter
6019251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6020251881Speter                                    STMT_SELECT_TARGETS));
6021251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6022251881Speter  while (have_row)
6023251881Speter    {
6024251881Speter      const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6025251881Speter      SVN_DBG(("Target: '%s'\n", target));
6026251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6027251881Speter    }
6028251881Speter
6029251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6030251881Speter
6031251881Speter  return SVN_NO_ERROR;
6032251881Speter}
6033251881Speter#endif
6034251881Speter
6035251881Speter
6036251881Speterstruct set_changelist_baton_t
6037251881Speter{
6038251881Speter  const char *new_changelist;
6039251881Speter  const apr_array_header_t *changelist_filter;
6040251881Speter  svn_depth_t depth;
6041251881Speter};
6042251881Speter
6043251881Speter
6044251881Speter/* The main part of svn_wc__db_op_set_changelist().
6045251881Speter *
6046251881Speter * Implements svn_wc__db_txn_callback_t. */
6047251881Speterstatic svn_error_t *
6048251881Speterset_changelist_txn(void *baton,
6049251881Speter                   svn_wc__db_wcroot_t *wcroot,
6050251881Speter                   const char *local_relpath,
6051251881Speter                   apr_pool_t *scratch_pool)
6052251881Speter{
6053251881Speter  struct set_changelist_baton_t *scb = baton;
6054251881Speter  svn_sqlite__stmt_t *stmt;
6055251881Speter
6056251881Speter  SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6057251881Speter                                scb->changelist_filter, scratch_pool));
6058251881Speter
6059251881Speter  /* Ensure we have actual nodes for our targets. */
6060251881Speter  if (scb->new_changelist)
6061251881Speter    {
6062251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6063251881Speter                                        STMT_INSERT_ACTUAL_EMPTIES));
6064251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6065251881Speter    }
6066251881Speter
6067251881Speter  /* Now create our notification table. */
6068251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6069251881Speter                                      STMT_CREATE_CHANGELIST_LIST));
6070251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6071251881Speter                                      STMT_CREATE_CHANGELIST_TRIGGER));
6072251881Speter
6073251881Speter  /* Update our changelists. */
6074251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6075251881Speter                                    STMT_UPDATE_ACTUAL_CHANGELISTS));
6076251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6077251881Speter                            scb->new_changelist));
6078251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6079251881Speter
6080251881Speter  if (scb->new_changelist)
6081251881Speter    {
6082251881Speter      /* We have to notify that we skipped directories, so do that now. */
6083251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6084251881Speter                                        STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6085251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6086251881Speter                                scb->new_changelist));
6087251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6088251881Speter    }
6089251881Speter
6090251881Speter  /* We may have left empty ACTUAL nodes, so remove them.  This is only a
6091251881Speter     potential problem if we removed changelists. */
6092251881Speter  if (!scb->new_changelist)
6093251881Speter    {
6094251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6095251881Speter                                        STMT_DELETE_ACTUAL_EMPTIES));
6096251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6097251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6098251881Speter    }
6099251881Speter
6100251881Speter  return SVN_NO_ERROR;
6101251881Speter}
6102251881Speter
6103251881Speter
6104251881Speter/* Send notifications for svn_wc__db_op_set_changelist().
6105251881Speter *
6106251881Speter * Implements work_callback_t. */
6107251881Speterstatic svn_error_t *
6108251881Speterdo_changelist_notify(void *baton,
6109251881Speter                     svn_wc__db_wcroot_t *wcroot,
6110251881Speter                     svn_cancel_func_t cancel_func,
6111251881Speter                     void *cancel_baton,
6112251881Speter                     svn_wc_notify_func2_t notify_func,
6113251881Speter                     void *notify_baton,
6114251881Speter                     apr_pool_t *scratch_pool)
6115251881Speter{
6116251881Speter  svn_sqlite__stmt_t *stmt;
6117251881Speter  svn_boolean_t have_row;
6118251881Speter  apr_pool_t *iterpool;
6119251881Speter
6120251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6121251881Speter                                    STMT_SELECT_CHANGELIST_LIST));
6122251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6123251881Speter
6124251881Speter  iterpool = svn_pool_create(scratch_pool);
6125251881Speter  while (have_row)
6126251881Speter    {
6127251881Speter      /* ### wc_id is column 0. use it one day...  */
6128251881Speter      const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6129251881Speter      svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6130251881Speter      svn_wc_notify_t *notify;
6131251881Speter      const char *notify_abspath;
6132251881Speter
6133251881Speter      svn_pool_clear(iterpool);
6134251881Speter
6135251881Speter      if (cancel_func)
6136251881Speter        {
6137251881Speter          svn_error_t *err = cancel_func(cancel_baton);
6138251881Speter
6139251881Speter          if (err)
6140251881Speter            return svn_error_trace(svn_error_compose_create(
6141251881Speter                                                    err,
6142251881Speter                                                    svn_sqlite__reset(stmt)));
6143251881Speter        }
6144251881Speter
6145251881Speter      notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6146251881Speter                                       iterpool);
6147251881Speter      notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6148251881Speter      notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6149251881Speter      notify_func(notify_baton, notify, iterpool);
6150251881Speter
6151251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6152251881Speter    }
6153251881Speter  svn_pool_destroy(iterpool);
6154251881Speter
6155251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
6156251881Speter}
6157251881Speter
6158251881Speter
6159251881Spetersvn_error_t *
6160251881Spetersvn_wc__db_op_set_changelist(svn_wc__db_t *db,
6161251881Speter                             const char *local_abspath,
6162251881Speter                             const char *new_changelist,
6163251881Speter                             const apr_array_header_t *changelist_filter,
6164251881Speter                             svn_depth_t depth,
6165251881Speter                             svn_wc_notify_func2_t notify_func,
6166251881Speter                             void *notify_baton,
6167251881Speter                             svn_cancel_func_t cancel_func,
6168251881Speter                             void *cancel_baton,
6169251881Speter                             apr_pool_t *scratch_pool)
6170251881Speter{
6171251881Speter  svn_wc__db_wcroot_t *wcroot;
6172251881Speter  const char *local_relpath;
6173251881Speter  struct set_changelist_baton_t scb;
6174251881Speter
6175251881Speter  scb.new_changelist = new_changelist;
6176251881Speter  scb.changelist_filter = changelist_filter;
6177251881Speter  scb.depth = depth;
6178251881Speter
6179251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6180251881Speter
6181251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6182251881Speter                                                db, local_abspath,
6183251881Speter                                                scratch_pool, scratch_pool));
6184251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6185251881Speter
6186251881Speter  /* Flush the entries before we do the work. Even if no work is performed,
6187251881Speter     the flush isn't a problem. */
6188251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6189251881Speter
6190251881Speter  /* Perform the set-changelist operation (transactionally), perform any
6191251881Speter     notifications necessary, and then clean out our temporary tables.  */
6192251881Speter  return svn_error_trace(with_finalization(wcroot, local_relpath,
6193251881Speter                                           set_changelist_txn, &scb,
6194251881Speter                                           do_changelist_notify, NULL,
6195251881Speter                                           cancel_func, cancel_baton,
6196251881Speter                                           notify_func, notify_baton,
6197251881Speter                                           STMT_FINALIZE_CHANGELIST,
6198251881Speter                                           scratch_pool));
6199251881Speter}
6200251881Speter
6201251881Speter/* Implementation of svn_wc__db_op_mark_conflict() */
6202251881Spetersvn_error_t *
6203251881Spetersvn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6204251881Speter                                  const char *local_relpath,
6205251881Speter                                  const svn_skel_t *conflict_skel,
6206251881Speter                                  apr_pool_t *scratch_pool)
6207251881Speter{
6208251881Speter  svn_sqlite__stmt_t *stmt;
6209251881Speter  svn_boolean_t got_row;
6210251881Speter  svn_boolean_t is_complete;
6211251881Speter
6212251881Speter  SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6213251881Speter  SVN_ERR_ASSERT(is_complete);
6214251881Speter
6215251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6216251881Speter                                    STMT_SELECT_ACTUAL_NODE));
6217251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6218251881Speter  SVN_ERR(svn_sqlite__step(&got_row, stmt));
6219251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6220251881Speter
6221251881Speter  if (got_row)
6222251881Speter    {
6223251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6224251881Speter                                        STMT_UPDATE_ACTUAL_CONFLICT));
6225251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6226251881Speter    }
6227251881Speter  else
6228251881Speter    {
6229251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6230251881Speter                                        STMT_INSERT_ACTUAL_CONFLICT));
6231251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6232251881Speter      if (*local_relpath != '\0')
6233251881Speter        SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6234251881Speter                                      svn_relpath_dirname(local_relpath,
6235251881Speter                                                          scratch_pool)));
6236251881Speter    }
6237251881Speter
6238251881Speter  {
6239251881Speter    svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6240251881Speter
6241251881Speter    SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6242251881Speter  }
6243251881Speter
6244251881Speter  SVN_ERR(svn_sqlite__update(NULL, stmt));
6245251881Speter
6246251881Speter  return SVN_NO_ERROR;
6247251881Speter}
6248251881Speter
6249251881Spetersvn_error_t *
6250251881Spetersvn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6251251881Speter                            const char *local_abspath,
6252251881Speter                            const svn_skel_t *conflict_skel,
6253251881Speter                            const svn_skel_t *work_items,
6254251881Speter                            apr_pool_t *scratch_pool)
6255251881Speter{
6256251881Speter  svn_wc__db_wcroot_t *wcroot;
6257251881Speter  const char *local_relpath;
6258251881Speter
6259251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6260251881Speter
6261251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6262251881Speter                              local_abspath, scratch_pool, scratch_pool));
6263251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6264251881Speter
6265251881Speter  SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6266251881Speter                                            conflict_skel, scratch_pool));
6267251881Speter
6268251881Speter  /* ### Should be handled in the same transaction as setting the conflict */
6269251881Speter  if (work_items)
6270251881Speter    SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6271251881Speter
6272251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6273251881Speter
6274251881Speter  return SVN_NO_ERROR;
6275251881Speter
6276251881Speter}
6277251881Speter
6278251881Speter/* The body of svn_wc__db_op_mark_resolved().
6279251881Speter */
6280251881Speterstatic svn_error_t *
6281251881Speterdb_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6282251881Speter                    const char *local_relpath,
6283251881Speter                    svn_wc__db_t *db,
6284251881Speter                    svn_boolean_t resolved_text,
6285251881Speter                    svn_boolean_t resolved_props,
6286251881Speter                    svn_boolean_t resolved_tree,
6287251881Speter                    const svn_skel_t *work_items,
6288251881Speter                    apr_pool_t *scratch_pool)
6289251881Speter{
6290251881Speter  svn_sqlite__stmt_t *stmt;
6291251881Speter  svn_boolean_t have_row;
6292251881Speter  int total_affected_rows = 0;
6293251881Speter  svn_boolean_t resolved_all;
6294251881Speter  apr_size_t conflict_len;
6295251881Speter  const void *conflict_data;
6296251881Speter  svn_skel_t *conflicts;
6297251881Speter
6298251881Speter  /* Check if we have a conflict in ACTUAL */
6299251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6300251881Speter                                    STMT_SELECT_ACTUAL_NODE));
6301251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6302251881Speter
6303251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6304251881Speter
6305251881Speter  if (! have_row)
6306251881Speter    {
6307251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6308251881Speter
6309251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6310251881Speter                                        STMT_SELECT_NODE_INFO));
6311251881Speter
6312251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6313251881Speter
6314251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6315251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6316251881Speter
6317251881Speter      if (have_row)
6318251881Speter        return SVN_NO_ERROR;
6319251881Speter
6320251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6321251881Speter                               _("The node '%s' was not found."),
6322251881Speter                                   path_for_error_message(wcroot,
6323251881Speter                                                          local_relpath,
6324251881Speter                                                          scratch_pool));
6325251881Speter    }
6326251881Speter
6327251881Speter  conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6328251881Speter                                          scratch_pool);
6329251881Speter  conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6330251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6331251881Speter
6332251881Speter  SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6333251881Speter                                        db, wcroot->abspath,
6334251881Speter                                        resolved_text,
6335251881Speter                                        resolved_props ? "" : NULL,
6336251881Speter                                        resolved_tree,
6337251881Speter                                        scratch_pool, scratch_pool));
6338251881Speter
6339251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6340251881Speter                                    STMT_UPDATE_ACTUAL_CONFLICT));
6341251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6342251881Speter
6343251881Speter  if (! resolved_all)
6344251881Speter    {
6345251881Speter      svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6346251881Speter
6347251881Speter      SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6348251881Speter    }
6349251881Speter
6350251881Speter  SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6351251881Speter
6352251881Speter  /* Now, remove the actual node if it doesn't have any more useful
6353251881Speter     information.  We only need to do this if we've remove data ourselves. */
6354251881Speter  if (total_affected_rows > 0)
6355251881Speter    {
6356251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6357251881Speter                                        STMT_DELETE_ACTUAL_EMPTY));
6358251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6359251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6360251881Speter    }
6361251881Speter
6362251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6363251881Speter
6364251881Speter  return SVN_NO_ERROR;
6365251881Speter}
6366251881Speter
6367251881Spetersvn_error_t *
6368251881Spetersvn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6369251881Speter                            const char *local_abspath,
6370251881Speter                            svn_boolean_t resolved_text,
6371251881Speter                            svn_boolean_t resolved_props,
6372251881Speter                            svn_boolean_t resolved_tree,
6373251881Speter                            const svn_skel_t *work_items,
6374251881Speter                            apr_pool_t *scratch_pool)
6375251881Speter{
6376251881Speter  svn_wc__db_wcroot_t *wcroot;
6377251881Speter  const char *local_relpath;
6378251881Speter
6379251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6380251881Speter
6381251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6382251881Speter                              local_abspath, scratch_pool, scratch_pool));
6383251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6384251881Speter
6385251881Speter  SVN_WC__DB_WITH_TXN(
6386251881Speter    db_op_mark_resolved(wcroot, local_relpath, db,
6387251881Speter                        resolved_text, resolved_props, resolved_tree,
6388251881Speter                        work_items, scratch_pool),
6389251881Speter    wcroot);
6390251881Speter
6391251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6392251881Speter  return SVN_NO_ERROR;
6393251881Speter}
6394251881Speter
6395251881Speter/* Clear moved-to information at the delete-half of the move which
6396251881Speter * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6397251881Speterstatic svn_error_t *
6398251881Speterclear_moved_to(const char *local_relpath,
6399251881Speter               svn_wc__db_wcroot_t *wcroot,
6400251881Speter               apr_pool_t *scratch_pool)
6401251881Speter{
6402251881Speter  svn_sqlite__stmt_t *stmt;
6403251881Speter  svn_boolean_t have_row;
6404251881Speter  const char *moved_from_relpath;
6405251881Speter
6406251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6407251881Speter                                    STMT_SELECT_MOVED_FROM_RELPATH));
6408251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6409251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6410251881Speter  if (!have_row)
6411251881Speter    {
6412251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6413251881Speter      return SVN_NO_ERROR;
6414251881Speter    }
6415251881Speter
6416251881Speter  moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6417251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6418251881Speter
6419251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6420251881Speter                                    STMT_CLEAR_MOVED_TO_RELPATH));
6421251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6422251881Speter                            moved_from_relpath,
6423251881Speter                            relpath_depth(moved_from_relpath)));
6424251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6425251881Speter
6426251881Speter  return SVN_NO_ERROR;
6427251881Speter}
6428251881Speter
6429251881Speter/* One of the two alternative bodies of svn_wc__db_op_revert().
6430251881Speter *
6431251881Speter * Implements svn_wc__db_txn_callback_t. */
6432251881Speterstatic svn_error_t *
6433251881Speterop_revert_txn(void *baton,
6434251881Speter              svn_wc__db_wcroot_t *wcroot,
6435251881Speter              const char *local_relpath,
6436251881Speter              apr_pool_t *scratch_pool)
6437251881Speter{
6438251881Speter  svn_wc__db_t *db = baton;
6439251881Speter  svn_sqlite__stmt_t *stmt;
6440251881Speter  svn_boolean_t have_row;
6441251881Speter  int op_depth;
6442251881Speter  svn_boolean_t moved_here;
6443251881Speter  int affected_rows;
6444251881Speter  const char *moved_to;
6445251881Speter
6446251881Speter  /* ### Similar structure to op_revert_recursive_txn, should they be
6447251881Speter         combined? */
6448251881Speter
6449251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6450251881Speter                                    STMT_SELECT_NODE_INFO));
6451251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6452251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6453251881Speter  if (!have_row)
6454251881Speter    {
6455251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6456251881Speter
6457251881Speter      /* There was no NODE row, so attempt to delete an ACTUAL_NODE row.  */
6458251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6459251881Speter                                        STMT_DELETE_ACTUAL_NODE));
6460251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6461251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6462251881Speter      if (affected_rows)
6463251881Speter        {
6464251881Speter          /* Can't do non-recursive actual-only revert if actual-only
6465251881Speter             children exist. Raise an error to cancel the transaction.  */
6466251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467251881Speter                                            STMT_ACTUAL_HAS_CHILDREN));
6468251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
6470251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
6471251881Speter          if (have_row)
6472251881Speter            return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6473251881Speter                                     _("Can't revert '%s' without"
6474251881Speter                                       " reverting children"),
6475251881Speter                                     path_for_error_message(wcroot,
6476251881Speter                                                            local_relpath,
6477251881Speter                                                            scratch_pool));
6478251881Speter          return SVN_NO_ERROR;
6479251881Speter        }
6480251881Speter
6481251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6482251881Speter                               _("The node '%s' was not found."),
6483251881Speter                               path_for_error_message(wcroot,
6484251881Speter                                                      local_relpath,
6485251881Speter                                                      scratch_pool));
6486251881Speter    }
6487251881Speter
6488251881Speter  op_depth = svn_sqlite__column_int(stmt, 0);
6489251881Speter  moved_here = svn_sqlite__column_boolean(stmt, 15);
6490251881Speter  moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6491251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6492251881Speter
6493251881Speter  if (moved_to)
6494251881Speter    {
6495251881Speter      SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6496251881Speter                                                           local_relpath,
6497262253Speter                                                           op_depth,
6498251881Speter                                                           scratch_pool));
6499251881Speter    }
6500251881Speter  else
6501251881Speter    {
6502251881Speter      svn_skel_t *conflict;
6503251881Speter
6504251881Speter      SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6505251881Speter                                                local_relpath,
6506251881Speter                                                scratch_pool, scratch_pool));
6507251881Speter      if (conflict)
6508251881Speter        {
6509251881Speter          svn_wc_operation_t operation;
6510251881Speter          svn_boolean_t tree_conflicted;
6511251881Speter
6512251881Speter          SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6513251881Speter                                             &tree_conflicted,
6514251881Speter                                             db, wcroot->abspath,
6515251881Speter                                             conflict,
6516251881Speter                                             scratch_pool, scratch_pool));
6517251881Speter          if (tree_conflicted
6518251881Speter              && (operation == svn_wc_operation_update
6519251881Speter                  || operation == svn_wc_operation_switch))
6520251881Speter            {
6521251881Speter              svn_wc_conflict_reason_t reason;
6522251881Speter              svn_wc_conflict_action_t action;
6523251881Speter
6524251881Speter              SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6525251881Speter                                                          NULL,
6526251881Speter                                                          db, wcroot->abspath,
6527251881Speter                                                          conflict,
6528251881Speter                                                          scratch_pool,
6529251881Speter                                                          scratch_pool));
6530251881Speter
6531251881Speter              if (reason == svn_wc_conflict_reason_deleted)
6532251881Speter                SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6533251881Speter                          db, svn_dirent_join(wcroot->abspath, local_relpath,
6534251881Speter                                              scratch_pool),
6535251881Speter                          NULL, NULL /* ### How do we notify this? */,
6536251881Speter                          scratch_pool));
6537251881Speter            }
6538251881Speter        }
6539251881Speter    }
6540251881Speter
6541251881Speter  if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6542251881Speter    {
6543251881Speter      /* Can't do non-recursive revert if children exist */
6544251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6545251881Speter                                        STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6546251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6547251881Speter                                local_relpath, op_depth));
6548251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6549251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6550251881Speter      if (have_row)
6551251881Speter        return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6552251881Speter                                 _("Can't revert '%s' without"
6553251881Speter                                   " reverting children"),
6554251881Speter                                 path_for_error_message(wcroot,
6555251881Speter                                                        local_relpath,
6556251881Speter                                                        scratch_pool));
6557251881Speter
6558251881Speter      /* Rewrite the op-depth of all deleted children making the
6559251881Speter         direct children into roots of deletes. */
6560251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6561251881Speter                                     STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6562251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6563251881Speter                                local_relpath,
6564251881Speter                                op_depth));
6565251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6566251881Speter
6567251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6568251881Speter                                        STMT_DELETE_WORKING_NODE));
6569251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6570251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6571251881Speter
6572251881Speter      /* ### This removes the lock, but what about the access baton? */
6573251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6574251881Speter                                        STMT_DELETE_WC_LOCK_ORPHAN));
6575251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6576251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6577251881Speter
6578251881Speter      /* If this node was moved-here, clear moved-to at the move source. */
6579251881Speter      if (moved_here)
6580251881Speter        SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6581251881Speter    }
6582251881Speter
6583251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6584251881Speter                                  STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6585251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6586251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6587251881Speter  if (!affected_rows)
6588251881Speter    {
6589251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6590251881Speter                                    STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6591251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6592251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6593251881Speter    }
6594251881Speter
6595251881Speter  return SVN_NO_ERROR;
6596251881Speter}
6597251881Speter
6598251881Speter
6599251881Speter/* One of the two alternative bodies of svn_wc__db_op_revert().
6600251881Speter *
6601251881Speter * Implements svn_wc__db_txn_callback_t. */
6602251881Speterstatic svn_error_t *
6603251881Speterop_revert_recursive_txn(void *baton,
6604251881Speter                        svn_wc__db_wcroot_t *wcroot,
6605251881Speter                        const char *local_relpath,
6606251881Speter                        apr_pool_t *scratch_pool)
6607251881Speter{
6608251881Speter  svn_sqlite__stmt_t *stmt;
6609251881Speter  svn_boolean_t have_row;
6610251881Speter  int op_depth;
6611251881Speter  int select_op_depth;
6612251881Speter  svn_boolean_t moved_here;
6613251881Speter  int affected_rows;
6614251881Speter  apr_pool_t *iterpool;
6615251881Speter
6616251881Speter  /* ### Similar structure to op_revert_txn, should they be
6617251881Speter         combined? */
6618251881Speter
6619251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6620251881Speter                                    STMT_SELECT_NODE_INFO));
6621251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6622251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6623251881Speter  if (!have_row)
6624251881Speter    {
6625251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6626251881Speter
6627251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6628251881Speter                                        STMT_DELETE_ACTUAL_NODE));
6629251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6630251881Speter                                local_relpath));
6631251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6632251881Speter
6633251881Speter      if (affected_rows)
6634251881Speter        return SVN_NO_ERROR;  /* actual-only revert */
6635251881Speter
6636251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6637251881Speter                               _("The node '%s' was not found."),
6638251881Speter                               path_for_error_message(wcroot,
6639251881Speter                                                      local_relpath,
6640251881Speter                                                      scratch_pool));
6641251881Speter    }
6642251881Speter
6643251881Speter  op_depth = svn_sqlite__column_int(stmt, 0);
6644251881Speter  moved_here = svn_sqlite__column_boolean(stmt, 15);
6645251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6646251881Speter
6647251881Speter  if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6648251881Speter    return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6649251881Speter                             _("Can't revert '%s' without"
6650251881Speter                               " reverting parent"),
6651251881Speter                             path_for_error_message(wcroot,
6652251881Speter                                                    local_relpath,
6653251881Speter                                                    scratch_pool));
6654251881Speter
6655251881Speter  /* Remove moved-here from move destinations outside the tree. */
6656251881Speter  SVN_ERR(svn_sqlite__get_statement(
6657251881Speter                    &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6658251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6659251881Speter                            op_depth));
6660251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6661251881Speter  while (have_row)
6662251881Speter    {
6663251881Speter      const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6664262253Speter      int move_op_depth = svn_sqlite__column_int(stmt, 2);
6665251881Speter      svn_error_t *err;
6666251881Speter
6667251881Speter      err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6668251881Speter                                                         move_src_relpath,
6669262253Speter                                                         move_op_depth,
6670251881Speter                                                         scratch_pool);
6671251881Speter      if (err)
6672251881Speter        return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6673251881Speter
6674251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6675251881Speter    }
6676251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6677251881Speter
6678251881Speter  /* Don't delete BASE nodes */
6679251881Speter  select_op_depth = op_depth ? op_depth : 1;
6680251881Speter
6681251881Speter  /* Reverting any non wc-root node */
6682251881Speter  SVN_ERR(svn_sqlite__get_statement(
6683251881Speter                    &stmt, wcroot->sdb,
6684251881Speter                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6685251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6686251881Speter                            local_relpath, select_op_depth));
6687251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6688251881Speter
6689251881Speter  SVN_ERR(svn_sqlite__get_statement(
6690251881Speter                    &stmt, wcroot->sdb,
6691251881Speter                    STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6692251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6693251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6694251881Speter
6695251881Speter  SVN_ERR(svn_sqlite__get_statement(
6696251881Speter                    &stmt, wcroot->sdb,
6697251881Speter                    STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6698251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6699251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6700251881Speter
6701251881Speter  /* ### This removes the locks, but what about the access batons? */
6702251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6703251881Speter                                    STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6704251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6705251881Speter                            local_relpath));
6706251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6707251881Speter
6708251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6709251881Speter                                    STMT_SELECT_MOVED_HERE_CHILDREN));
6710251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6711251881Speter
6712251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6713251881Speter
6714251881Speter  iterpool = svn_pool_create(scratch_pool);
6715251881Speter  while (have_row)
6716251881Speter    {
6717251881Speter      const char *moved_here_child_relpath;
6718251881Speter      svn_error_t *err;
6719251881Speter
6720251881Speter      svn_pool_clear(iterpool);
6721251881Speter
6722251881Speter      moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6723251881Speter      err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6724251881Speter      if (err)
6725251881Speter        return svn_error_trace(svn_error_compose_create(
6726251881Speter                                        err,
6727251881Speter                                        svn_sqlite__reset(stmt)));
6728251881Speter
6729251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6730251881Speter    }
6731251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6732251881Speter  svn_pool_destroy(iterpool);
6733251881Speter
6734251881Speter  /* Clear potential moved-to pointing at the target node itself. */
6735251881Speter  if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6736251881Speter      && moved_here)
6737251881Speter    SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6738251881Speter
6739251881Speter  return SVN_NO_ERROR;
6740251881Speter}
6741251881Speter
6742251881Spetersvn_error_t *
6743251881Spetersvn_wc__db_op_revert(svn_wc__db_t *db,
6744251881Speter                     const char *local_abspath,
6745251881Speter                     svn_depth_t depth,
6746251881Speter                     apr_pool_t *result_pool,
6747251881Speter                     apr_pool_t *scratch_pool)
6748251881Speter{
6749251881Speter  svn_wc__db_wcroot_t *wcroot;
6750251881Speter  const char *local_relpath;
6751251881Speter  struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6752251881Speter                                       STMT_DROP_REVERT_LIST_TRIGGERS,
6753251881Speter                                       NULL, NULL};
6754251881Speter
6755251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6756251881Speter
6757251881Speter  switch (depth)
6758251881Speter    {
6759251881Speter    case svn_depth_empty:
6760251881Speter      wtb.cb_func = op_revert_txn;
6761251881Speter      wtb.cb_baton = db;
6762251881Speter      break;
6763251881Speter    case svn_depth_infinity:
6764251881Speter      wtb.cb_func = op_revert_recursive_txn;
6765251881Speter      break;
6766251881Speter    default:
6767251881Speter      return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6768251881Speter                               _("Unsupported depth for revert of '%s'"),
6769251881Speter                               svn_dirent_local_style(local_abspath,
6770251881Speter                                                      scratch_pool));
6771251881Speter    }
6772251881Speter
6773251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6774251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6775251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6776251881Speter
6777251881Speter  SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6778251881Speter                      wcroot);
6779251881Speter
6780251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6781251881Speter
6782251881Speter  return SVN_NO_ERROR;
6783251881Speter}
6784251881Speter
6785251881Speter/* The body of svn_wc__db_revert_list_read().
6786251881Speter */
6787251881Speterstatic svn_error_t *
6788251881Speterrevert_list_read(svn_boolean_t *reverted,
6789251881Speter                 const apr_array_header_t **marker_paths,
6790251881Speter                 svn_boolean_t *copied_here,
6791251881Speter                 svn_node_kind_t *kind,
6792251881Speter                 svn_wc__db_wcroot_t *wcroot,
6793251881Speter                 const char *local_relpath,
6794251881Speter                 svn_wc__db_t *db,
6795251881Speter                 apr_pool_t *result_pool,
6796251881Speter                 apr_pool_t *scratch_pool)
6797251881Speter{
6798251881Speter  svn_sqlite__stmt_t *stmt;
6799251881Speter  svn_boolean_t have_row;
6800251881Speter
6801251881Speter  *reverted = FALSE;
6802251881Speter  *marker_paths = NULL;
6803251881Speter  *copied_here = FALSE;
6804251881Speter  *kind = svn_node_unknown;
6805251881Speter
6806251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6807251881Speter                                    STMT_SELECT_REVERT_LIST));
6808251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6809251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6810251881Speter  if (have_row)
6811251881Speter    {
6812251881Speter      svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6813251881Speter      svn_boolean_t another_row = FALSE;
6814251881Speter
6815251881Speter      if (is_actual)
6816251881Speter        {
6817251881Speter          apr_size_t conflict_len;
6818251881Speter          const void *conflict_data;
6819251881Speter
6820251881Speter          conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6821251881Speter                                                  scratch_pool);
6822251881Speter          if (conflict_data)
6823251881Speter            {
6824251881Speter              svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6825251881Speter                                                      conflict_len,
6826251881Speter                                                      scratch_pool);
6827251881Speter
6828251881Speter              SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6829251881Speter                                                    db, wcroot->abspath,
6830251881Speter                                                    conflicts,
6831251881Speter                                                    result_pool,
6832251881Speter                                                    scratch_pool));
6833251881Speter            }
6834251881Speter
6835251881Speter          if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6836251881Speter            *reverted = TRUE;
6837251881Speter
6838251881Speter          SVN_ERR(svn_sqlite__step(&another_row, stmt));
6839251881Speter        }
6840251881Speter
6841251881Speter      if (!is_actual || another_row)
6842251881Speter        {
6843251881Speter          *reverted = TRUE;
6844251881Speter          if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6845251881Speter            {
6846251881Speter              int op_depth = svn_sqlite__column_int(stmt, 3);
6847251881Speter              *copied_here = (op_depth == relpath_depth(local_relpath));
6848251881Speter            }
6849251881Speter          *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6850251881Speter        }
6851251881Speter
6852251881Speter    }
6853251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6854251881Speter
6855251881Speter  if (have_row)
6856251881Speter    {
6857251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6858251881Speter                                        STMT_DELETE_REVERT_LIST));
6859251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6860251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6861251881Speter    }
6862251881Speter
6863251881Speter  return SVN_NO_ERROR;
6864251881Speter}
6865251881Speter
6866251881Spetersvn_error_t *
6867251881Spetersvn_wc__db_revert_list_read(svn_boolean_t *reverted,
6868251881Speter                            const apr_array_header_t **marker_files,
6869251881Speter                            svn_boolean_t *copied_here,
6870251881Speter                            svn_node_kind_t *kind,
6871251881Speter                            svn_wc__db_t *db,
6872251881Speter                            const char *local_abspath,
6873251881Speter                            apr_pool_t *result_pool,
6874251881Speter                            apr_pool_t *scratch_pool)
6875251881Speter{
6876251881Speter  svn_wc__db_wcroot_t *wcroot;
6877251881Speter  const char *local_relpath;
6878251881Speter
6879251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6880251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6881251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6882251881Speter
6883251881Speter  SVN_WC__DB_WITH_TXN(
6884251881Speter    revert_list_read(reverted, marker_files, copied_here, kind,
6885251881Speter                     wcroot, local_relpath, db,
6886251881Speter                     result_pool, scratch_pool),
6887251881Speter    wcroot);
6888251881Speter  return SVN_NO_ERROR;
6889251881Speter}
6890251881Speter
6891251881Speter
6892251881Speter/* The body of svn_wc__db_revert_list_read_copied_children().
6893251881Speter */
6894251881Speterstatic svn_error_t *
6895251881Speterrevert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6896251881Speter                                 const char *local_relpath,
6897251881Speter                                 const apr_array_header_t **children_p,
6898251881Speter                                 apr_pool_t *result_pool,
6899251881Speter                                 apr_pool_t *scratch_pool)
6900251881Speter{
6901251881Speter  svn_sqlite__stmt_t *stmt;
6902251881Speter  svn_boolean_t have_row;
6903251881Speter  apr_array_header_t *children;
6904251881Speter
6905251881Speter  children =
6906251881Speter    apr_array_make(result_pool, 0,
6907251881Speter                  sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6908251881Speter
6909251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6910251881Speter                                    STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6911251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6912251881Speter                            local_relpath, relpath_depth(local_relpath)));
6913251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6914251881Speter  while (have_row)
6915251881Speter    {
6916251881Speter      svn_wc__db_revert_list_copied_child_info_t *child_info;
6917251881Speter      const char *child_relpath;
6918251881Speter
6919251881Speter      child_info = apr_palloc(result_pool, sizeof(*child_info));
6920251881Speter
6921251881Speter      child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6922251881Speter      child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6923251881Speter                                            result_pool);
6924251881Speter      child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6925251881Speter      APR_ARRAY_PUSH(
6926251881Speter        children,
6927251881Speter        svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6928251881Speter
6929251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6930251881Speter    }
6931251881Speter   SVN_ERR(svn_sqlite__reset(stmt));
6932251881Speter
6933251881Speter  *children_p = children;
6934251881Speter
6935251881Speter  return SVN_NO_ERROR;
6936251881Speter}
6937251881Speter
6938251881Speter
6939251881Spetersvn_error_t *
6940251881Spetersvn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6941251881Speter                                            svn_wc__db_t *db,
6942251881Speter                                            const char *local_abspath,
6943251881Speter                                            apr_pool_t *result_pool,
6944251881Speter                                            apr_pool_t *scratch_pool)
6945251881Speter{
6946251881Speter  svn_wc__db_wcroot_t *wcroot;
6947251881Speter  const char *local_relpath;
6948251881Speter
6949251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6950251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6951251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6952251881Speter
6953251881Speter  SVN_WC__DB_WITH_TXN(
6954251881Speter    revert_list_read_copied_children(wcroot, local_relpath, children,
6955251881Speter                                     result_pool, scratch_pool),
6956251881Speter    wcroot);
6957251881Speter  return SVN_NO_ERROR;
6958251881Speter}
6959251881Speter
6960251881Speter
6961251881Spetersvn_error_t *
6962251881Spetersvn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6963251881Speter                              void *notify_baton,
6964251881Speter                              svn_wc__db_t *db,
6965251881Speter                              const char *local_abspath,
6966251881Speter                              apr_pool_t *scratch_pool)
6967251881Speter{
6968251881Speter  svn_wc__db_wcroot_t *wcroot;
6969251881Speter  const char *local_relpath;
6970251881Speter  svn_sqlite__stmt_t *stmt;
6971251881Speter  svn_boolean_t have_row;
6972251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6973251881Speter
6974251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6975251881Speter                              db, local_abspath, scratch_pool, iterpool));
6976251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6977251881Speter
6978251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6979251881Speter                                    STMT_SELECT_REVERT_LIST_RECURSIVE));
6980251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6981251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6982251881Speter  if (!have_row)
6983251881Speter    return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
6984251881Speter  while (have_row)
6985251881Speter    {
6986251881Speter      const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6987251881Speter
6988251881Speter      svn_pool_clear(iterpool);
6989251881Speter
6990251881Speter      notify_func(notify_baton,
6991251881Speter                  svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
6992251881Speter                                                       notify_relpath,
6993251881Speter                                                       iterpool),
6994251881Speter                                       svn_wc_notify_revert,
6995251881Speter                                       iterpool),
6996251881Speter                  iterpool);
6997251881Speter
6998251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6999251881Speter    }
7000251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
7001251881Speter
7002251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7003251881Speter                                    STMT_DELETE_REVERT_LIST_RECURSIVE));
7004251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7005251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7006251881Speter
7007251881Speter  svn_pool_destroy(iterpool);
7008251881Speter
7009251881Speter  return SVN_NO_ERROR;
7010251881Speter}
7011251881Speter
7012251881Spetersvn_error_t *
7013251881Spetersvn_wc__db_revert_list_done(svn_wc__db_t *db,
7014251881Speter                            const char *local_abspath,
7015251881Speter                            apr_pool_t *scratch_pool)
7016251881Speter{
7017251881Speter  svn_wc__db_wcroot_t *wcroot;
7018251881Speter  const char *local_relpath;
7019251881Speter
7020251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7021251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
7022251881Speter  VERIFY_USABLE_WCROOT(wcroot);
7023251881Speter
7024251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7025251881Speter
7026251881Speter  return SVN_NO_ERROR;
7027251881Speter}
7028251881Speter
7029251881Speter/* The body of svn_wc__db_op_remove_node().
7030251881Speter */
7031251881Speterstatic svn_error_t *
7032251881Speterremove_node_txn(svn_boolean_t *left_changes,
7033251881Speter                svn_wc__db_wcroot_t *wcroot,
7034251881Speter                const char *local_relpath,
7035251881Speter                svn_wc__db_t *db,
7036251881Speter                svn_boolean_t destroy_wc,
7037251881Speter                svn_boolean_t destroy_changes,
7038251881Speter                svn_revnum_t not_present_rev,
7039251881Speter                svn_wc__db_status_t not_present_status,
7040251881Speter                svn_node_kind_t not_present_kind,
7041251881Speter                const svn_skel_t *conflict,
7042251881Speter                const svn_skel_t *work_items,
7043251881Speter                svn_cancel_func_t cancel_func,
7044251881Speter                void *cancel_baton,
7045251881Speter                apr_pool_t *scratch_pool)
7046251881Speter{
7047251881Speter  svn_sqlite__stmt_t *stmt;
7048251881Speter
7049251881Speter  apr_int64_t repos_id;
7050251881Speter  const char *repos_relpath;
7051251881Speter
7052251881Speter  /* Note that unlike many similar functions it is a valid scenario for this
7053251881Speter     function to be called on a wcroot! */
7054251881Speter
7055251881Speter   /* db set when destroying wc */
7056251881Speter  SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7057251881Speter
7058251881Speter  if (left_changes)
7059251881Speter    *left_changes = FALSE;
7060251881Speter
7061251881Speter  /* Need info for not_present node? */
7062251881Speter  if (SVN_IS_VALID_REVNUM(not_present_rev))
7063251881Speter    SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
7064251881Speter                                              &repos_relpath, &repos_id,
7065251881Speter                                              NULL, NULL, NULL, NULL, NULL,
7066251881Speter                                              NULL, NULL, NULL, NULL, NULL,
7067251881Speter                                              wcroot, local_relpath,
7068251881Speter                                              scratch_pool, scratch_pool));
7069251881Speter
7070251881Speter  if (destroy_wc
7071251881Speter      && (!destroy_changes || *local_relpath == '\0'))
7072251881Speter    {
7073251881Speter      svn_boolean_t have_row;
7074251881Speter      apr_pool_t *iterpool;
7075251881Speter      svn_error_t *err = NULL;
7076251881Speter
7077251881Speter      /* Install WQ items for deleting the unmodified files and all dirs */
7078251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7079251881Speter                                        STMT_SELECT_WORKING_PRESENT));
7080251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is",
7081251881Speter                                wcroot->wc_id, local_relpath));
7082251881Speter
7083251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7084251881Speter
7085251881Speter      iterpool = svn_pool_create(scratch_pool);
7086251881Speter
7087251881Speter      while (have_row)
7088251881Speter        {
7089251881Speter          const char *child_relpath;
7090251881Speter          const char *child_abspath;
7091251881Speter          svn_node_kind_t child_kind;
7092251881Speter          svn_boolean_t have_checksum;
7093251881Speter          svn_filesize_t recorded_size;
7094251881Speter          apr_int64_t recorded_time;
7095251881Speter          const svn_io_dirent2_t *dirent;
7096251881Speter          svn_boolean_t modified_p = TRUE;
7097251881Speter          svn_skel_t *work_item = NULL;
7098251881Speter
7099251881Speter          svn_pool_clear(iterpool);
7100251881Speter
7101251881Speter          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7102251881Speter          child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7103251881Speter
7104251881Speter          child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7105251881Speter                                          iterpool);
7106251881Speter
7107251881Speter          if (child_kind == svn_node_file)
7108251881Speter            {
7109251881Speter              have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7110251881Speter              recorded_size = get_recorded_size(stmt, 3);
7111251881Speter              recorded_time = svn_sqlite__column_int64(stmt, 4);
7112251881Speter            }
7113251881Speter
7114251881Speter          if (cancel_func)
7115251881Speter            err = cancel_func(cancel_baton);
7116251881Speter
7117251881Speter          if (err)
7118251881Speter            break;
7119251881Speter
7120251881Speter          err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7121251881Speter                                    iterpool, iterpool);
7122251881Speter
7123251881Speter          if (err)
7124251881Speter            break;
7125251881Speter
7126251881Speter          if (destroy_changes
7127251881Speter              || dirent->kind != svn_node_file
7128251881Speter              || child_kind != svn_node_file)
7129251881Speter            {
7130251881Speter              /* Not interested in keeping changes */
7131251881Speter              modified_p = FALSE;
7132251881Speter            }
7133251881Speter          else if (child_kind == svn_node_file
7134251881Speter                   && dirent->kind == svn_node_file
7135251881Speter                   && dirent->filesize == recorded_size
7136251881Speter                   && dirent->mtime == recorded_time)
7137251881Speter            {
7138251881Speter              modified_p = FALSE; /* File matches recorded state */
7139251881Speter            }
7140251881Speter          else if (have_checksum)
7141251881Speter            err = svn_wc__internal_file_modified_p(&modified_p,
7142251881Speter                                                   db, child_abspath,
7143251881Speter                                                   FALSE, iterpool);
7144251881Speter
7145251881Speter          if (err)
7146251881Speter            break;
7147251881Speter
7148251881Speter          if (modified_p)
7149251881Speter            {
7150251881Speter              if (left_changes)
7151251881Speter                *left_changes = TRUE;
7152251881Speter            }
7153251881Speter          else if (child_kind == svn_node_dir)
7154251881Speter            {
7155251881Speter              err = svn_wc__wq_build_dir_remove(&work_item,
7156251881Speter                                                db, wcroot->abspath,
7157251881Speter                                                child_abspath, FALSE,
7158251881Speter                                                iterpool, iterpool);
7159251881Speter            }
7160251881Speter          else /* svn_node_file || svn_node_symlink */
7161251881Speter            {
7162251881Speter              err = svn_wc__wq_build_file_remove(&work_item,
7163251881Speter                                                 db, wcroot->abspath,
7164251881Speter                                                 child_abspath,
7165251881Speter                                                 iterpool, iterpool);
7166251881Speter            }
7167251881Speter
7168251881Speter          if (err)
7169251881Speter            break;
7170251881Speter
7171251881Speter          if (work_item)
7172251881Speter            {
7173251881Speter              err = add_work_items(wcroot->sdb, work_item, iterpool);
7174251881Speter              if (err)
7175251881Speter                break;
7176251881Speter            }
7177251881Speter
7178251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7179251881Speter        }
7180251881Speter      svn_pool_destroy(iterpool);
7181251881Speter
7182251881Speter      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7183251881Speter    }
7184251881Speter
7185251881Speter  if (destroy_wc && *local_relpath != '\0')
7186251881Speter    {
7187251881Speter      /* Create work item for destroying the root */
7188251881Speter      svn_wc__db_status_t status;
7189251881Speter      svn_node_kind_t kind;
7190251881Speter      SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7191251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7192251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7193251881Speter                        wcroot, local_relpath,
7194251881Speter                        scratch_pool, scratch_pool));
7195251881Speter
7196251881Speter      if (status == svn_wc__db_status_normal
7197251881Speter          || status == svn_wc__db_status_added
7198251881Speter          || status == svn_wc__db_status_incomplete)
7199251881Speter        {
7200251881Speter          svn_skel_t *work_item = NULL;
7201251881Speter          const char *local_abspath = svn_dirent_join(wcroot->abspath,
7202251881Speter                                                          local_relpath,
7203251881Speter                                                          scratch_pool);
7204251881Speter
7205251881Speter          if (kind == svn_node_dir)
7206251881Speter            {
7207251881Speter              SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7208251881Speter                                                  db, wcroot->abspath,
7209251881Speter                                                  local_abspath,
7210251881Speter                                                  destroy_changes
7211251881Speter                                                      /* recursive */,
7212251881Speter                                                  scratch_pool, scratch_pool));
7213251881Speter            }
7214251881Speter          else
7215251881Speter            {
7216251881Speter              svn_boolean_t modified_p = FALSE;
7217251881Speter
7218251881Speter              if (!destroy_changes)
7219251881Speter                {
7220251881Speter                  SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7221251881Speter                                                           db, local_abspath,
7222251881Speter                                                           FALSE,
7223251881Speter                                                           scratch_pool));
7224251881Speter                }
7225251881Speter
7226251881Speter              if (!modified_p)
7227251881Speter                SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7228251881Speter                                                     db, wcroot->abspath,
7229251881Speter                                                     local_abspath,
7230251881Speter                                                     scratch_pool,
7231251881Speter                                                     scratch_pool));
7232251881Speter              else
7233251881Speter                {
7234251881Speter                  if (left_changes)
7235251881Speter                    *left_changes = TRUE;
7236251881Speter                }
7237251881Speter            }
7238251881Speter
7239251881Speter          SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7240251881Speter        }
7241251881Speter    }
7242251881Speter
7243251881Speter  /* Remove all nodes below local_relpath */
7244251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7245251881Speter                                    STMT_DELETE_NODE_RECURSIVE));
7246251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7247251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7248251881Speter
7249251881Speter  /* Delete the root NODE when this is not the working copy root */
7250251881Speter  if (local_relpath[0] != '\0')
7251251881Speter    {
7252251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7253269847Speter                                        STMT_DELETE_NODE_ALL_LAYERS));
7254251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7255251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
7256251881Speter    }
7257251881Speter
7258251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7259251881Speter                                    STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7260251881Speter
7261251881Speter  /* Delete all actual nodes at or below local_relpath */
7262251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7263251881Speter                                         local_relpath));
7264251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7265251881Speter
7266251881Speter  /* Should we leave a not-present node? */
7267251881Speter  if (SVN_IS_VALID_REVNUM(not_present_rev))
7268251881Speter    {
7269251881Speter      insert_base_baton_t ibb;
7270251881Speter      blank_ibb(&ibb);
7271251881Speter
7272251881Speter      ibb.repos_id = repos_id;
7273251881Speter
7274251881Speter      SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7275251881Speter                     || not_present_status == svn_wc__db_status_excluded);
7276251881Speter
7277251881Speter      ibb.status = not_present_status;
7278251881Speter      ibb.kind = not_present_kind;
7279251881Speter
7280251881Speter      ibb.repos_relpath = repos_relpath;
7281251881Speter      ibb.revision = not_present_rev;
7282251881Speter
7283251881Speter      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7284251881Speter    }
7285251881Speter
7286251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7287251881Speter  if (conflict)
7288251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7289251881Speter                                              conflict, scratch_pool));
7290251881Speter
7291251881Speter  return SVN_NO_ERROR;
7292251881Speter}
7293251881Speter
7294251881Spetersvn_error_t *
7295251881Spetersvn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7296251881Speter                          svn_wc__db_t *db,
7297251881Speter                          const char *local_abspath,
7298251881Speter                          svn_boolean_t destroy_wc,
7299251881Speter                          svn_boolean_t destroy_changes,
7300251881Speter                          svn_revnum_t not_present_revision,
7301251881Speter                          svn_wc__db_status_t not_present_status,
7302251881Speter                          svn_node_kind_t not_present_kind,
7303251881Speter                          const svn_skel_t *conflict,
7304251881Speter                          const svn_skel_t *work_items,
7305251881Speter                          svn_cancel_func_t cancel_func,
7306251881Speter                          void *cancel_baton,
7307251881Speter                          apr_pool_t *scratch_pool)
7308251881Speter{
7309251881Speter  svn_wc__db_wcroot_t *wcroot;
7310251881Speter  const char *local_relpath;
7311251881Speter
7312251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7313251881Speter
7314251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7315251881Speter                              local_abspath, scratch_pool, scratch_pool));
7316251881Speter  VERIFY_USABLE_WCROOT(wcroot);
7317251881Speter
7318251881Speter  SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7319251881Speter                                      wcroot, local_relpath, db,
7320251881Speter                                      destroy_wc, destroy_changes,
7321251881Speter                                      not_present_revision, not_present_status,
7322251881Speter                                      not_present_kind, conflict, work_items,
7323251881Speter                                      cancel_func, cancel_baton, scratch_pool),
7324251881Speter                      wcroot);
7325251881Speter
7326251881Speter  /* Flush everything below this node in all ways */
7327251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7328251881Speter                        scratch_pool));
7329251881Speter
7330251881Speter  return SVN_NO_ERROR;
7331251881Speter}
7332251881Speter
7333251881Speter
7334251881Speter/* The body of svn_wc__db_op_set_base_depth().
7335251881Speter */
7336251881Speterstatic svn_error_t *
7337251881Speterdb_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7338251881Speter                     const char *local_relpath,
7339251881Speter                     svn_depth_t depth,
7340251881Speter                     apr_pool_t *scratch_pool)
7341251881Speter{
7342251881Speter  svn_sqlite__stmt_t *stmt;
7343251881Speter  int affected_rows;
7344251881Speter
7345251881Speter  /* Flush any entries before we start monkeying the database.  */
7346251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7347251881Speter                                    STMT_UPDATE_NODE_BASE_DEPTH));
7348251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7349251881Speter                            svn_token__to_word(depth_map, depth)));
7350251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7351251881Speter
7352251881Speter  if (affected_rows == 0)
7353251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7354251881Speter                             "The node '%s' is not a committed directory",
7355251881Speter                             path_for_error_message(wcroot, local_relpath,
7356251881Speter                                                    scratch_pool));
7357251881Speter
7358251881Speter  return SVN_NO_ERROR;
7359251881Speter}
7360251881Speter
7361251881Speter
7362251881Spetersvn_error_t *
7363251881Spetersvn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7364251881Speter                             const char *local_abspath,
7365251881Speter                             svn_depth_t depth,
7366251881Speter                             apr_pool_t *scratch_pool)
7367251881Speter{
7368251881Speter  svn_wc__db_wcroot_t *wcroot;
7369251881Speter  const char *local_relpath;
7370251881Speter
7371251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7372251881Speter  SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7373251881Speter
7374251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7375251881Speter                              local_abspath, scratch_pool, scratch_pool));
7376251881Speter  VERIFY_USABLE_WCROOT(wcroot);
7377251881Speter
7378251881Speter  /* ### We set depth on working and base to match entry behavior.
7379251881Speter         Maybe these should be separated later? */
7380251881Speter  SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7381251881Speter                                           scratch_pool),
7382251881Speter                      wcroot);
7383251881Speter
7384251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7385251881Speter
7386251881Speter  return SVN_NO_ERROR;
7387251881Speter}
7388251881Speter
7389251881Speter
7390251881Speterstatic svn_error_t *
7391251881Speterinfo_below_working(svn_boolean_t *have_base,
7392251881Speter                   svn_boolean_t *have_work,
7393251881Speter                   svn_wc__db_status_t *status,
7394251881Speter                   svn_wc__db_wcroot_t *wcroot,
7395251881Speter                   const char *local_relpath,
7396251881Speter                   int below_op_depth, /* < 0 is ignored */
7397251881Speter                   apr_pool_t *scratch_pool);
7398251881Speter
7399251881Speter
7400251881Speter/* Convert STATUS, the raw status obtained from the presence map, to
7401251881Speter   the status appropriate for a working (op_depth > 0) node and return
7402251881Speter   it in *WORKING_STATUS. */
7403251881Speterstatic svn_error_t *
7404251881Speterconvert_to_working_status(svn_wc__db_status_t *working_status,
7405251881Speter                          svn_wc__db_status_t status)
7406251881Speter{
7407251881Speter  svn_wc__db_status_t work_status = status;
7408251881Speter
7409251881Speter  SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7410251881Speter                 || work_status == svn_wc__db_status_not_present
7411251881Speter                 || work_status == svn_wc__db_status_base_deleted
7412251881Speter                 || work_status == svn_wc__db_status_incomplete
7413251881Speter                 || work_status == svn_wc__db_status_excluded);
7414251881Speter
7415251881Speter  if (work_status == svn_wc__db_status_excluded)
7416251881Speter    {
7417251881Speter      *working_status = svn_wc__db_status_excluded;
7418251881Speter    }
7419251881Speter  else if (work_status == svn_wc__db_status_not_present
7420251881Speter           || work_status == svn_wc__db_status_base_deleted)
7421251881Speter    {
7422251881Speter      /* The caller should scan upwards to detect whether this
7423251881Speter         deletion has occurred because this node has been moved
7424251881Speter         away, or it is a regular deletion. Also note that the
7425251881Speter         deletion could be of the BASE tree, or a child of
7426251881Speter         something that has been copied/moved here. */
7427251881Speter
7428251881Speter      *working_status = svn_wc__db_status_deleted;
7429251881Speter    }
7430251881Speter  else /* normal or incomplete */
7431251881Speter    {
7432251881Speter      /* The caller should scan upwards to detect whether this
7433251881Speter         addition has occurred because of a simple addition,
7434251881Speter         a copy, or is the destination of a move. */
7435251881Speter      *working_status = svn_wc__db_status_added;
7436251881Speter    }
7437251881Speter
7438251881Speter  return SVN_NO_ERROR;
7439251881Speter}
7440251881Speter
7441251881Speter
7442251881Speter/* Return the status of the node, if any, below the "working" node (or
7443251881Speter   below BELOW_OP_DEPTH if >= 0).
7444251881Speter   Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7445251881Speter   working node is present, and *STATUS to the status of the first
7446251881Speter   layer below the selected node. */
7447251881Speterstatic svn_error_t *
7448251881Speterinfo_below_working(svn_boolean_t *have_base,
7449251881Speter                   svn_boolean_t *have_work,
7450251881Speter                   svn_wc__db_status_t *status,
7451251881Speter                   svn_wc__db_wcroot_t *wcroot,
7452251881Speter                   const char *local_relpath,
7453251881Speter                   int below_op_depth,
7454251881Speter                   apr_pool_t *scratch_pool)
7455251881Speter{
7456251881Speter  svn_sqlite__stmt_t *stmt;
7457251881Speter  svn_boolean_t have_row;
7458251881Speter
7459251881Speter  *have_base = *have_work =  FALSE;
7460251881Speter  *status = svn_wc__db_status_normal;
7461251881Speter
7462251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7463251881Speter                                    STMT_SELECT_NODE_INFO));
7464251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7465251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7466251881Speter
7467251881Speter  if (below_op_depth >= 0)
7468251881Speter    {
7469251881Speter      while (have_row &&
7470251881Speter             (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7471251881Speter        {
7472251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7473251881Speter        }
7474251881Speter    }
7475251881Speter  if (have_row)
7476251881Speter    {
7477251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7478251881Speter      if (have_row)
7479251881Speter        *status = svn_sqlite__column_token(stmt, 3, presence_map);
7480251881Speter
7481251881Speter      while (have_row)
7482251881Speter        {
7483251881Speter          int op_depth = svn_sqlite__column_int(stmt, 0);
7484251881Speter
7485251881Speter          if (op_depth > 0)
7486251881Speter            *have_work = TRUE;
7487251881Speter          else
7488251881Speter            *have_base = TRUE;
7489251881Speter
7490251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7491251881Speter        }
7492251881Speter    }
7493251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
7494251881Speter
7495251881Speter  if (*have_work)
7496251881Speter    SVN_ERR(convert_to_working_status(status, *status));
7497251881Speter
7498251881Speter  return SVN_NO_ERROR;
7499251881Speter}
7500251881Speter
7501251881Speter/* Helper function for op_delete_txn */
7502251881Speterstatic svn_error_t *
7503251881Speterdelete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7504251881Speter                      const char *child_moved_from_relpath,
7505251881Speter                      int op_depth,
7506251881Speter                      const char *new_moved_to_relpath,
7507251881Speter                      apr_pool_t *scratch_pool)
7508251881Speter{
7509251881Speter  svn_sqlite__stmt_t *stmt;
7510251881Speter  int affected;
7511251881Speter
7512251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7513251881Speter                                    STMT_UPDATE_MOVED_TO_RELPATH));
7514251881Speter
7515251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7516251881Speter                            wcroot->wc_id,
7517251881Speter                            child_moved_from_relpath,
7518251881Speter                            op_depth,
7519251881Speter                            new_moved_to_relpath));
7520251881Speter  SVN_ERR(svn_sqlite__update(&affected, stmt));
7521251881Speter  assert(affected == 1);
7522251881Speter
7523251881Speter  return SVN_NO_ERROR;
7524251881Speter}
7525251881Speter
7526251881Speter
7527251881Speterstruct op_delete_baton_t {
7528251881Speter  const char *moved_to_relpath; /* NULL if delete is not part of a move */
7529251881Speter  svn_skel_t *conflict;
7530251881Speter  svn_skel_t *work_items;
7531251881Speter  svn_boolean_t delete_dir_externals;
7532251881Speter  svn_boolean_t notify;
7533251881Speter};
7534251881Speter
7535251881Speter/* This structure is used while rewriting move information for nodes.
7536251881Speter *
7537251881Speter * The most simple case of rewriting move information happens when
7538251881Speter * a moved-away subtree is moved again:  mv A B; mv B C
7539251881Speter * The second move requires rewriting moved-to info at or within A.
7540251881Speter *
7541251881Speter * Another example is a move of a subtree which had nodes moved into it:
7542251881Speter *   mv A B/F; mv B G
7543251881Speter * This requires rewriting such that A/F is marked has having moved to G/F.
7544251881Speter *
7545251881Speter * Another case is where a node becomes a nested moved node.
7546251881Speter * A nested move happens when a subtree child is moved before or after
7547251881Speter * the subtree itself is moved. For example:
7548251881Speter *   mv A/F A/G; mv A B
7549251881Speter * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7550251881Speter * Note that the following sequence results in the same DB state:
7551251881Speter *   mv A B; mv B/F B/G
7552251881Speter * We do not care about the order the moves were performed in.
7553251881Speter * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7554251881Speter */
7555251881Speterstruct moved_node_t {
7556251881Speter  /* The source of the move. */
7557251881Speter  const char *local_relpath;
7558251881Speter
7559251881Speter  /* The move destination. */
7560251881Speter  const char *moved_to_relpath;
7561251881Speter
7562251881Speter  /* The op-depth of the deleted node at the source of the move. */
7563251881Speter  int op_depth;
7564269847Speter
7565269847Speter  /* When >= 1 the op_depth at which local_relpath was moved to its
7566269847Speter     location. Used to find its original location outside the delete */
7567269847Speter  int moved_from_depth;
7568251881Speter};
7569251881Speter
7570269847Speter/* Helper function to resolve the original location of local_relpath at OP_DEPTH
7571269847Speter   before it was moved into the tree rooted at ROOT_RELPATH. */
7572251881Speterstatic svn_error_t *
7573269847Speterresolve_moved_from(const char **moved_from_relpath,
7574269847Speter                   int *moved_from_op_depth,
7575269847Speter                   svn_wc__db_wcroot_t *wcroot,
7576269847Speter                   const char *root_relpath,
7577269847Speter                   const char *local_relpath,
7578269847Speter                   int op_depth,
7579269847Speter                   apr_pool_t *result_pool,
7580269847Speter                   apr_pool_t *scratch_pool)
7581269847Speter{
7582269847Speter  const char *suffix = "";
7583269847Speter  svn_sqlite__stmt_t *stmt;
7584269847Speter  const char *m_from_relpath;
7585269847Speter  int m_from_op_depth;
7586269847Speter  int m_move_from_depth;
7587269847Speter  svn_boolean_t have_row;
7588269847Speter
7589269847Speter  while (relpath_depth(local_relpath) > op_depth)
7590269847Speter    {
7591269847Speter      const char *name;
7592269847Speter      svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
7593269847Speter      suffix = svn_relpath_join(suffix, name, scratch_pool);
7594269847Speter    }
7595269847Speter
7596269847Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7597269847Speter                                    STMT_SELECT_MOVED_FROM_FOR_DELETE));
7598269847Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
7599269847Speter                            wcroot->wc_id, local_relpath));
7600269847Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7601269847Speter
7602269847Speter  if (!have_row)
7603269847Speter    {
7604269847Speter      /* assert(have_row); */
7605269847Speter      *moved_from_relpath = NULL;
7606269847Speter      *moved_from_op_depth = -1;
7607269847Speter
7608269847Speter      SVN_ERR(svn_sqlite__reset(stmt));
7609269847Speter
7610269847Speter      return SVN_NO_ERROR;
7611269847Speter    }
7612269847Speter
7613269847Speter  m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
7614269847Speter  m_from_op_depth = svn_sqlite__column_int(stmt, 1);
7615269847Speter  m_move_from_depth = svn_sqlite__column_int(stmt, 2);
7616269847Speter
7617269847Speter  SVN_ERR(svn_sqlite__reset(stmt));
7618269847Speter
7619269847Speter  if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
7620269847Speter    {
7621269847Speter      *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
7622269847Speter                                             result_pool);
7623269847Speter      *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
7624269847Speter      return SVN_NO_ERROR;
7625269847Speter    }
7626269847Speter  else if (!m_move_from_depth)
7627269847Speter    {
7628269847Speter      *moved_from_relpath = NULL;
7629269847Speter      *moved_from_op_depth = -1;
7630269847Speter      return SVN_NO_ERROR;
7631269847Speter    }
7632269847Speter
7633269847Speter  return svn_error_trace(
7634269847Speter        resolve_moved_from(moved_from_relpath,
7635269847Speter                           moved_from_op_depth,
7636269847Speter                           wcroot,
7637269847Speter                           root_relpath,
7638269847Speter                           svn_relpath_join(m_from_relpath, suffix,
7639269847Speter                                            scratch_pool),
7640269847Speter                           m_move_from_depth,
7641269847Speter                           result_pool, scratch_pool));
7642269847Speter}
7643269847Speter
7644269847Speterstatic svn_error_t *
7645251881Speterdelete_node(void *baton,
7646251881Speter            svn_wc__db_wcroot_t *wcroot,
7647251881Speter            const char *local_relpath,
7648251881Speter            apr_pool_t *scratch_pool)
7649251881Speter{
7650251881Speter  struct op_delete_baton_t *b = baton;
7651251881Speter  svn_wc__db_status_t status;
7652251881Speter  svn_boolean_t have_row, op_root;
7653251881Speter  svn_boolean_t add_work = FALSE;
7654251881Speter  svn_sqlite__stmt_t *stmt;
7655269847Speter  int working_op_depth; /* Depth of what is to be deleted */
7656269847Speter  int keep_op_depth = 0; /* Depth of what is below what is deleted */
7657251881Speter  svn_node_kind_t kind;
7658251881Speter  apr_array_header_t *moved_nodes = NULL;
7659269847Speter  int delete_op_depth = relpath_depth(local_relpath);
7660251881Speter
7661269847Speter  assert(*local_relpath); /* Can't delete wcroot */
7662251881Speter
7663269847Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7664269847Speter                                    STMT_SELECT_NODE_INFO));
7665269847Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7666269847Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7667269847Speter
7668269847Speter  if (!have_row)
7669269847Speter    {
7670269847Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
7671269847Speter                               svn_sqlite__reset(stmt),
7672269847Speter                               _("The node '%s' was not found."),
7673269847Speter                               path_for_error_message(wcroot,
7674269847Speter                                                      local_relpath,
7675269847Speter                                                      scratch_pool));
7676269847Speter    }
7677269847Speter
7678269847Speter  working_op_depth = svn_sqlite__column_int(stmt, 0);
7679269847Speter  status = svn_sqlite__column_token(stmt, 3, presence_map);
7680269847Speter  kind = svn_sqlite__column_token(stmt, 4, kind_map);
7681269847Speter
7682269847Speter  if (working_op_depth < delete_op_depth)
7683269847Speter    {
7684269847Speter      op_root = FALSE;
7685269847Speter      add_work = TRUE;
7686269847Speter      keep_op_depth = working_op_depth;
7687269847Speter    }
7688269847Speter  else
7689269847Speter    {
7690269847Speter      op_root = TRUE;
7691269847Speter
7692269847Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7693269847Speter
7694269847Speter      if (have_row)
7695269847Speter        {
7696269847Speter          svn_wc__db_status_t below_status;
7697269847Speter          int below_op_depth;
7698269847Speter
7699269847Speter          below_op_depth = svn_sqlite__column_int(stmt, 0);
7700269847Speter          below_status = svn_sqlite__column_token(stmt, 3, presence_map);
7701269847Speter
7702269847Speter          if (below_status != svn_wc__db_status_not_present
7703269847Speter              && below_status != svn_wc__db_status_base_deleted)
7704269847Speter            {
7705269847Speter              add_work = TRUE;
7706269847Speter              keep_op_depth = below_op_depth;
7707269847Speter            }
7708269847Speter          else
7709269847Speter            keep_op_depth = 0;
7710269847Speter        }
7711269847Speter      else
7712269847Speter        keep_op_depth = -1;
7713269847Speter    }
7714269847Speter
7715269847Speter  SVN_ERR(svn_sqlite__reset(stmt));
7716269847Speter
7717269847Speter  if (working_op_depth != 0) /* WORKING */
7718269847Speter    SVN_ERR(convert_to_working_status(&status, status));
7719269847Speter
7720251881Speter  if (status == svn_wc__db_status_deleted
7721251881Speter      || status == svn_wc__db_status_not_present)
7722251881Speter    return SVN_NO_ERROR;
7723251881Speter
7724251881Speter  /* Don't copy BASE directories with server excluded nodes */
7725251881Speter  if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7726251881Speter    {
7727251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7728251881Speter                                        STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7729251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is",
7730251881Speter                                wcroot->wc_id, local_relpath));
7731251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7732251881Speter      if (have_row)
7733251881Speter        {
7734251881Speter          const char *absent_path = svn_sqlite__column_text(stmt, 0,
7735251881Speter                                                            scratch_pool);
7736251881Speter
7737251881Speter          return svn_error_createf(
7738251881Speter                               SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7739251881Speter                               svn_sqlite__reset(stmt),
7740251881Speter                          _("Cannot delete '%s' as '%s' is excluded by server"),
7741251881Speter                               path_for_error_message(wcroot, local_relpath,
7742251881Speter                                                      scratch_pool),
7743251881Speter                               path_for_error_message(wcroot, absent_path,
7744251881Speter                                                      scratch_pool));
7745251881Speter        }
7746251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
7747251881Speter    }
7748251881Speter  else if (status == svn_wc__db_status_server_excluded)
7749251881Speter    {
7750251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7751251881Speter                          _("Cannot delete '%s' as it is excluded by server"),
7752251881Speter                               path_for_error_message(wcroot, local_relpath,
7753251881Speter                                                      scratch_pool));
7754251881Speter    }
7755251881Speter  else if (status == svn_wc__db_status_excluded)
7756251881Speter    {
7757251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7758251881Speter                          _("Cannot delete '%s' as it is excluded"),
7759251881Speter                               path_for_error_message(wcroot, local_relpath,
7760251881Speter                                                      scratch_pool));
7761251881Speter    }
7762251881Speter
7763251881Speter  if (b->moved_to_relpath)
7764251881Speter    {
7765251881Speter      const char *moved_from_relpath = NULL;
7766251881Speter      struct moved_node_t *moved_node;
7767251881Speter      int move_op_depth;
7768251881Speter
7769251881Speter      moved_nodes = apr_array_make(scratch_pool, 1,
7770251881Speter                                   sizeof(struct moved_node_t *));
7771251881Speter
7772251881Speter      /* The node is being moved-away.
7773251881Speter       * Figure out if the node was moved-here before, or whether this
7774251881Speter       * is the first time the node is moved. */
7775251881Speter      if (status == svn_wc__db_status_added)
7776251881Speter        SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7777251881Speter                              &moved_from_relpath,
7778251881Speter                              NULL,
7779251881Speter                              &move_op_depth,
7780251881Speter                              wcroot, local_relpath,
7781251881Speter                              scratch_pool, scratch_pool));
7782251881Speter
7783251881Speter      if (op_root && moved_from_relpath)
7784251881Speter        {
7785251881Speter          const char *part = svn_relpath_skip_ancestor(local_relpath,
7786251881Speter                                                       moved_from_relpath);
7787251881Speter
7788251881Speter          /* Existing move-root is moved to another location */
7789251881Speter          moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7790251881Speter          if (!part)
7791251881Speter            moved_node->local_relpath = moved_from_relpath;
7792251881Speter          else
7793251881Speter            moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7794251881Speter                                                         part, scratch_pool);
7795251881Speter          moved_node->op_depth = move_op_depth;
7796251881Speter          moved_node->moved_to_relpath = b->moved_to_relpath;
7797269847Speter          moved_node->moved_from_depth = -1;
7798251881Speter
7799251881Speter          APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7800251881Speter        }
7801251881Speter      else if (!op_root && (status == svn_wc__db_status_normal
7802251881Speter                            || status == svn_wc__db_status_copied
7803251881Speter                            || status == svn_wc__db_status_moved_here))
7804251881Speter        {
7805251881Speter          /* The node is becoming a move-root for the first time,
7806251881Speter           * possibly because of a nested move operation. */
7807251881Speter          moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7808251881Speter          moved_node->local_relpath = local_relpath;
7809269847Speter          moved_node->op_depth = delete_op_depth;
7810251881Speter          moved_node->moved_to_relpath = b->moved_to_relpath;
7811269847Speter          moved_node->moved_from_depth = -1;
7812251881Speter
7813251881Speter          APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7814251881Speter        }
7815251881Speter      /* Else: We can't track history of local additions and/or of things we are
7816251881Speter               about to delete. */
7817251881Speter
7818251881Speter      /* And update all moved_to values still pointing to this location */
7819251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7820251881Speter                                        STMT_UPDATE_MOVED_TO_DESCENDANTS));
7821251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7822251881Speter                                             local_relpath,
7823251881Speter                                             b->moved_to_relpath));
7824251881Speter      SVN_ERR(svn_sqlite__update(NULL, stmt));
7825251881Speter    }
7826251881Speter
7827251881Speter  /* Find children that were moved out of the subtree rooted at this node.
7828251881Speter   * We'll need to update their op-depth columns because their deletion
7829251881Speter   * is now implied by the deletion of their parent (i.e. this node). */
7830251881Speter    {
7831251881Speter      apr_pool_t *iterpool;
7832269847Speter      int i;
7833251881Speter
7834251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7835251881Speter                                        STMT_SELECT_MOVED_FOR_DELETE));
7836269847Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7837269847Speter                                delete_op_depth));
7838251881Speter
7839251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7840251881Speter      iterpool = svn_pool_create(scratch_pool);
7841251881Speter      while (have_row)
7842251881Speter        {
7843251881Speter          struct moved_node_t *mn;
7844251881Speter          const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7845251881Speter          const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7846251881Speter          int child_op_depth = svn_sqlite__column_int(stmt, 2);
7847269847Speter          int moved_from_depth = -1;
7848251881Speter          svn_boolean_t fixup = FALSE;
7849251881Speter
7850269847Speter          if (! b->moved_to_relpath
7851251881Speter              && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7852251881Speter            {
7853269847Speter              /* a NULL moved_here_depth will be reported as 0 */
7854269847Speter              int moved_here_depth = svn_sqlite__column_int(stmt, 3);
7855251881Speter
7856269847Speter              /* Plain delete. Fixup move information of descendants that were
7857269847Speter                 moved here, or that were moved out */
7858251881Speter
7859269847Speter              if (moved_here_depth >= delete_op_depth)
7860251881Speter                {
7861269847Speter                  /* The move we recorded here must be moved to the location
7862269847Speter                     this node had before it was moved here.
7863251881Speter
7864269847Speter                     This might contain multiple steps when the node was moved
7865269847Speter                     in several places within the to be deleted tree */
7866251881Speter
7867269847Speter                  /* ### TODO: Add logic */
7868251881Speter                  fixup = TRUE;
7869269847Speter                  moved_from_depth = moved_here_depth;
7870251881Speter                }
7871269847Speter              else
7872269847Speter                {
7873269847Speter                  /* Update the op-depth of an moved away node that was
7874269847Speter                     registered as moved by the records that we are about
7875269847Speter                     to delete */
7876269847Speter                  fixup = TRUE;
7877269847Speter                  child_op_depth = delete_op_depth;
7878269847Speter                }
7879251881Speter            }
7880269847Speter          else if (b->moved_to_relpath)
7881269847Speter            {
7882269847Speter              /* The node is moved to a new location */
7883251881Speter
7884269847Speter              if (delete_op_depth == child_op_depth)
7885269847Speter                {
7886269847Speter                  /* Update the op-depth of a tree shadowed by this tree */
7887269847Speter                  fixup = TRUE;
7888269847Speter                  /*child_op_depth = delete_depth;*/
7889269847Speter                }
7890269847Speter              else if (child_op_depth >= delete_op_depth
7891269847Speter                       && !svn_relpath_skip_ancestor(local_relpath,
7892269847Speter                                                     mv_to_relpath))
7893269847Speter                {
7894269847Speter                  /* Update the move destination of something that is now moved
7895269847Speter                     away further */
7896269847Speter
7897269847Speter                  child_relpath = svn_relpath_skip_ancestor(local_relpath,
7898269847Speter                                                            child_relpath);
7899269847Speter
7900269847Speter                  if (child_relpath)
7901269847Speter                    {
7902269847Speter                      child_relpath = svn_relpath_join(b->moved_to_relpath,
7903269847Speter                                                       child_relpath,
7904269847Speter                                                       scratch_pool);
7905269847Speter
7906269847Speter                      if (child_op_depth > delete_op_depth
7907269847Speter                           && svn_relpath_skip_ancestor(local_relpath,
7908269847Speter                                                        child_relpath))
7909269847Speter                        child_op_depth = delete_op_depth;
7910269847Speter                      else
7911269847Speter                        child_op_depth = relpath_depth(child_relpath);
7912269847Speter
7913269847Speter                      fixup = TRUE;
7914269847Speter                    }
7915269847Speter                }
7916269847Speter            }
7917269847Speter
7918251881Speter          if (fixup)
7919251881Speter            {
7920269847Speter              mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7921251881Speter
7922251881Speter              mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7923251881Speter              mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7924251881Speter              mn->op_depth = child_op_depth;
7925269847Speter              mn->moved_from_depth = moved_from_depth;
7926251881Speter
7927251881Speter              if (!moved_nodes)
7928251881Speter                moved_nodes = apr_array_make(scratch_pool, 1,
7929251881Speter                                             sizeof(struct moved_node_t *));
7930251881Speter              APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7931251881Speter            }
7932251881Speter
7933251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7934251881Speter        }
7935269847Speter      SVN_ERR(svn_sqlite__reset(stmt));
7936269847Speter
7937269847Speter      for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
7938269847Speter        {
7939269847Speter          struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
7940269847Speter                                                  struct moved_node_t *);
7941269847Speter
7942269847Speter          if (mn->moved_from_depth > 0)
7943269847Speter            {
7944269847Speter              svn_pool_clear(iterpool);
7945269847Speter
7946269847Speter              SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
7947269847Speter                                         wcroot, local_relpath,
7948269847Speter                                         mn->local_relpath,
7949269847Speter                                         mn->moved_from_depth,
7950269847Speter                                         scratch_pool, iterpool));
7951269847Speter
7952269847Speter              if (!mn->local_relpath)
7953269847Speter                svn_sort__array_delete(moved_nodes, i--, 1);
7954269847Speter            }
7955269847Speter        }
7956269847Speter
7957251881Speter      svn_pool_destroy(iterpool);
7958251881Speter    }
7959251881Speter
7960269847Speter  if (!b->moved_to_relpath)
7961251881Speter    {
7962269847Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7963269847Speter                                        STMT_CLEAR_MOVED_TO_DESCENDANTS));
7964269847Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7965269847Speter                                local_relpath));
7966269847Speter      SVN_ERR(svn_sqlite__update(NULL, stmt));
7967251881Speter
7968269847Speter      if (op_root)
7969251881Speter        {
7970269847Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7971269847Speter                                            STMT_CLEAR_MOVED_TO_FROM_DEST));
7972269847Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7973269847Speter                                    local_relpath));
7974269847Speter
7975269847Speter          SVN_ERR(svn_sqlite__update(NULL, stmt));
7976251881Speter        }
7977251881Speter    }
7978251881Speter
7979269847Speter
7980251881Speter  /* ### Put actual-only nodes into the list? */
7981251881Speter  if (b->notify)
7982251881Speter    {
7983251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7984251881Speter                                        STMT_INSERT_DELETE_LIST));
7985251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7986269847Speter                                wcroot->wc_id, local_relpath, working_op_depth));
7987251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
7988251881Speter    }
7989251881Speter
7990251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7991251881Speter                                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7992251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7993269847Speter                            wcroot->wc_id, local_relpath, delete_op_depth));
7994251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7995251881Speter
7996251881Speter  /* Delete ACTUAL_NODE rows, but leave those that have changelist
7997251881Speter     and a NODES row. */
7998251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7999251881Speter                         STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8000251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
8001251881Speter                            wcroot->wc_id, local_relpath));
8002251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
8003251881Speter
8004251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8005251881Speter                         STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8006251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
8007251881Speter                            wcroot->wc_id, local_relpath));
8008251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
8009251881Speter
8010251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8011251881Speter                                    STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8012251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8013251881Speter                            local_relpath));
8014251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
8015251881Speter
8016251881Speter  if (add_work)
8017251881Speter    {
8018251881Speter      /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8019251881Speter
8020251881Speter      /* Delete the node and possible descendants. */
8021251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8022251881Speter                                 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8023251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8024251881Speter                                wcroot->wc_id, local_relpath,
8025269847Speter                                keep_op_depth, delete_op_depth));
8026251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
8027251881Speter    }
8028251881Speter
8029251881Speter  if (moved_nodes)
8030251881Speter    {
8031251881Speter      int i;
8032251881Speter
8033251881Speter      for (i = 0; i < moved_nodes->nelts; ++i)
8034251881Speter        {
8035251881Speter          const struct moved_node_t *moved_node
8036251881Speter            = APR_ARRAY_IDX(moved_nodes, i, void *);
8037251881Speter
8038251881Speter          SVN_ERR(delete_update_movedto(wcroot,
8039251881Speter                                        moved_node->local_relpath,
8040251881Speter                                        moved_node->op_depth,
8041251881Speter                                        moved_node->moved_to_relpath,
8042251881Speter                                        scratch_pool));
8043251881Speter        }
8044251881Speter    }
8045251881Speter
8046251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8047251881Speter                                    STMT_DELETE_FILE_EXTERNALS));
8048251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8049251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
8050251881Speter
8051251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8052251881Speter                                    b->delete_dir_externals
8053251881Speter                                    ? STMT_DELETE_EXTERNAL_REGISTATIONS
8054251881Speter                                    : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8055251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8056251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
8057251881Speter
8058251881Speter  SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8059251881Speter  if (b->conflict)
8060251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8061251881Speter                                              b->conflict, scratch_pool));
8062251881Speter
8063251881Speter  return SVN_NO_ERROR;
8064251881Speter}
8065251881Speter
8066251881Speterstatic svn_error_t *
8067251881Speterop_delete_txn(void *baton,
8068251881Speter              svn_wc__db_wcroot_t *wcroot,
8069251881Speter              const char *local_relpath,
8070251881Speter              apr_pool_t *scratch_pool)
8071251881Speter{
8072251881Speter
8073251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8074251881Speter  SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8075251881Speter  return SVN_NO_ERROR;
8076251881Speter}
8077251881Speter
8078251881Speter
8079251881Speterstruct op_delete_many_baton_t {
8080251881Speter  apr_array_header_t *rel_targets;
8081251881Speter  svn_boolean_t delete_dir_externals;
8082251881Speter  const svn_skel_t *work_items;
8083251881Speter} op_delete_many_baton_t;
8084251881Speter
8085251881Speterstatic svn_error_t *
8086251881Speterop_delete_many_txn(void *baton,
8087251881Speter                   svn_wc__db_wcroot_t *wcroot,
8088251881Speter                   const char *local_relpath,
8089251881Speter                   apr_pool_t *scratch_pool)
8090251881Speter{
8091251881Speter  struct op_delete_many_baton_t *odmb = baton;
8092251881Speter  struct op_delete_baton_t odb;
8093251881Speter  int i;
8094251881Speter  apr_pool_t *iterpool;
8095251881Speter
8096251881Speter  odb.moved_to_relpath = NULL;
8097251881Speter  odb.conflict = NULL;
8098251881Speter  odb.work_items = NULL;
8099251881Speter  odb.delete_dir_externals = odmb->delete_dir_externals;
8100251881Speter  odb.notify = TRUE;
8101251881Speter
8102251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8103251881Speter  iterpool = svn_pool_create(scratch_pool);
8104251881Speter  for (i = 0; i < odmb->rel_targets->nelts; i++)
8105251881Speter    {
8106251881Speter      const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8107251881Speter                                                 const char *);
8108251881Speter
8109251881Speter
8110251881Speter      svn_pool_clear(iterpool);
8111251881Speter      SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8112251881Speter    }
8113251881Speter  svn_pool_destroy(iterpool);
8114251881Speter
8115251881Speter  SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8116251881Speter
8117251881Speter  return SVN_NO_ERROR;
8118251881Speter}
8119251881Speter
8120251881Speter
8121251881Speterstatic svn_error_t *
8122251881Speterdo_delete_notify(void *baton,
8123251881Speter                 svn_wc__db_wcroot_t *wcroot,
8124251881Speter                 svn_cancel_func_t cancel_func,
8125251881Speter                 void *cancel_baton,
8126251881Speter                 svn_wc_notify_func2_t notify_func,
8127251881Speter                 void *notify_baton,
8128251881Speter                 apr_pool_t *scratch_pool)
8129251881Speter{
8130251881Speter  svn_sqlite__stmt_t *stmt;
8131251881Speter  svn_boolean_t have_row;
8132251881Speter  apr_pool_t *iterpool;
8133251881Speter
8134251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8135251881Speter                                    STMT_SELECT_DELETE_LIST));
8136251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8137251881Speter
8138251881Speter  iterpool = svn_pool_create(scratch_pool);
8139251881Speter  while (have_row)
8140251881Speter    {
8141251881Speter      const char *notify_relpath;
8142251881Speter      const char *notify_abspath;
8143251881Speter
8144251881Speter      svn_pool_clear(iterpool);
8145251881Speter
8146251881Speter      notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8147251881Speter      notify_abspath = svn_dirent_join(wcroot->abspath,
8148251881Speter                                       notify_relpath,
8149251881Speter                                       iterpool);
8150251881Speter
8151251881Speter      notify_func(notify_baton,
8152251881Speter                  svn_wc_create_notify(notify_abspath,
8153251881Speter                                       svn_wc_notify_delete,
8154251881Speter                                       iterpool),
8155251881Speter                  iterpool);
8156251881Speter
8157251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8158251881Speter    }
8159251881Speter  svn_pool_destroy(iterpool);
8160251881Speter
8161251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
8162251881Speter
8163251881Speter  /* We only allow cancellation after notification for all deleted nodes
8164251881Speter   * has happened. The nodes are already deleted so we should notify for
8165251881Speter   * all of them. */
8166251881Speter  if (cancel_func)
8167251881Speter    SVN_ERR(cancel_func(cancel_baton));
8168251881Speter
8169251881Speter  return SVN_NO_ERROR;
8170251881Speter}
8171251881Speter
8172251881Speter
8173251881Spetersvn_error_t *
8174251881Spetersvn_wc__db_op_delete(svn_wc__db_t *db,
8175251881Speter                     const char *local_abspath,
8176251881Speter                     const char *moved_to_abspath,
8177251881Speter                     svn_boolean_t delete_dir_externals,
8178251881Speter                     svn_skel_t *conflict,
8179251881Speter                     svn_skel_t *work_items,
8180251881Speter                     svn_cancel_func_t cancel_func,
8181251881Speter                     void *cancel_baton,
8182251881Speter                     svn_wc_notify_func2_t notify_func,
8183251881Speter                     void *notify_baton,
8184251881Speter                     apr_pool_t *scratch_pool)
8185251881Speter{
8186251881Speter  svn_wc__db_wcroot_t *wcroot;
8187251881Speter  svn_wc__db_wcroot_t *moved_to_wcroot;
8188251881Speter  const char *local_relpath;
8189251881Speter  const char *moved_to_relpath;
8190251881Speter  struct op_delete_baton_t odb;
8191251881Speter
8192251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8193251881Speter
8194251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8195251881Speter                                                db, local_abspath,
8196251881Speter                                                scratch_pool, scratch_pool));
8197251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8198251881Speter
8199251881Speter  if (moved_to_abspath)
8200251881Speter    {
8201251881Speter      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8202251881Speter                                                    &moved_to_relpath,
8203251881Speter                                                    db, moved_to_abspath,
8204251881Speter                                                    scratch_pool,
8205251881Speter                                                    scratch_pool));
8206251881Speter      VERIFY_USABLE_WCROOT(moved_to_wcroot);
8207251881Speter
8208251881Speter      if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8209251881Speter        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8210251881Speter                                 _("Cannot move '%s' to '%s' because they "
8211251881Speter                                   "are not in the same working copy"),
8212251881Speter                                 svn_dirent_local_style(local_abspath,
8213251881Speter                                                        scratch_pool),
8214251881Speter                                 svn_dirent_local_style(moved_to_abspath,
8215251881Speter                                                        scratch_pool));
8216251881Speter    }
8217251881Speter  else
8218251881Speter    moved_to_relpath = NULL;
8219251881Speter
8220251881Speter  odb.moved_to_relpath = moved_to_relpath;
8221251881Speter  odb.conflict = conflict;
8222251881Speter  odb.work_items = work_items;
8223251881Speter  odb.delete_dir_externals = delete_dir_externals;
8224251881Speter
8225251881Speter  if (notify_func)
8226251881Speter    {
8227251881Speter      /* Perform the deletion operation (transactionally), perform any
8228251881Speter         notifications necessary, and then clean out our temporary tables.  */
8229251881Speter      odb.notify = TRUE;
8230251881Speter      SVN_ERR(with_finalization(wcroot, local_relpath,
8231251881Speter                                op_delete_txn, &odb,
8232251881Speter                                do_delete_notify, NULL,
8233251881Speter                                cancel_func, cancel_baton,
8234251881Speter                                notify_func, notify_baton,
8235251881Speter                                STMT_FINALIZE_DELETE,
8236251881Speter                                scratch_pool));
8237251881Speter    }
8238251881Speter  else
8239251881Speter    {
8240251881Speter      /* Avoid the trigger work */
8241251881Speter      odb.notify = FALSE;
8242251881Speter      SVN_WC__DB_WITH_TXN(
8243251881Speter                    delete_node(&odb, wcroot, local_relpath, scratch_pool),
8244251881Speter                    wcroot);
8245251881Speter    }
8246251881Speter
8247251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8248251881Speter                        scratch_pool));
8249251881Speter
8250251881Speter  return SVN_NO_ERROR;
8251251881Speter}
8252251881Speter
8253251881Speter
8254251881Spetersvn_error_t *
8255251881Spetersvn_wc__db_op_delete_many(svn_wc__db_t *db,
8256251881Speter                          apr_array_header_t *targets,
8257251881Speter                          svn_boolean_t delete_dir_externals,
8258251881Speter                          const svn_skel_t *work_items,
8259251881Speter                          svn_cancel_func_t cancel_func,
8260251881Speter                          void *cancel_baton,
8261251881Speter                          svn_wc_notify_func2_t notify_func,
8262251881Speter                          void *notify_baton,
8263251881Speter                          apr_pool_t *scratch_pool)
8264251881Speter{
8265251881Speter  svn_wc__db_wcroot_t *wcroot;
8266251881Speter  const char *local_relpath;
8267251881Speter  struct op_delete_many_baton_t odmb;
8268251881Speter  int i;
8269251881Speter  apr_pool_t *iterpool;
8270251881Speter
8271251881Speter  odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8272251881Speter                                    sizeof(const char *));
8273251881Speter  odmb.work_items = work_items;
8274251881Speter  odmb.delete_dir_externals = delete_dir_externals;
8275251881Speter  iterpool = svn_pool_create(scratch_pool);
8276251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8277251881Speter                                                db,
8278251881Speter                                                APR_ARRAY_IDX(targets, 0,
8279251881Speter                                                              const char *),
8280251881Speter                                                scratch_pool, iterpool));
8281251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8282251881Speter  for (i = 0; i < targets->nelts; i++)
8283251881Speter    {
8284251881Speter      const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8285251881Speter      svn_wc__db_wcroot_t *target_wcroot;
8286251881Speter
8287251881Speter      svn_pool_clear(iterpool);
8288251881Speter
8289251881Speter      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8290251881Speter                                                    &local_relpath, db,
8291251881Speter                                                    APR_ARRAY_IDX(targets, i,
8292251881Speter                                                                  const char *),
8293251881Speter                                                    scratch_pool, iterpool));
8294251881Speter      VERIFY_USABLE_WCROOT(target_wcroot);
8295251881Speter      SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8296251881Speter
8297251881Speter      /* Assert that all targets are within the same working copy. */
8298251881Speter      SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8299251881Speter
8300251881Speter      APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8301251881Speter      SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8302251881Speter                            iterpool));
8303251881Speter
8304251881Speter    }
8305251881Speter  svn_pool_destroy(iterpool);
8306251881Speter
8307251881Speter  /* Perform the deletion operation (transactionally), perform any
8308251881Speter     notifications necessary, and then clean out our temporary tables.  */
8309251881Speter  return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8310251881Speter                                           op_delete_many_txn, &odmb,
8311251881Speter                                           do_delete_notify, NULL,
8312251881Speter                                           cancel_func, cancel_baton,
8313251881Speter                                           notify_func, notify_baton,
8314251881Speter                                           STMT_FINALIZE_DELETE,
8315251881Speter                                           scratch_pool));
8316251881Speter}
8317251881Speter
8318251881Speter
8319251881Speter/* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8320251881Speter   DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8321251881Speterstatic svn_error_t *
8322251881Speterread_info(svn_wc__db_status_t *status,
8323251881Speter          svn_node_kind_t *kind,
8324251881Speter          svn_revnum_t *revision,
8325251881Speter          const char **repos_relpath,
8326251881Speter          apr_int64_t *repos_id,
8327251881Speter          svn_revnum_t *changed_rev,
8328251881Speter          apr_time_t *changed_date,
8329251881Speter          const char **changed_author,
8330251881Speter          svn_depth_t *depth,
8331251881Speter          const svn_checksum_t **checksum,
8332251881Speter          const char **target,
8333251881Speter          const char **original_repos_relpath,
8334251881Speter          apr_int64_t *original_repos_id,
8335251881Speter          svn_revnum_t *original_revision,
8336251881Speter          svn_wc__db_lock_t **lock,
8337251881Speter          svn_filesize_t *recorded_size,
8338251881Speter          apr_time_t *recorded_time,
8339251881Speter          const char **changelist,
8340251881Speter          svn_boolean_t *conflicted,
8341251881Speter          svn_boolean_t *op_root,
8342251881Speter          svn_boolean_t *had_props,
8343251881Speter          svn_boolean_t *props_mod,
8344251881Speter          svn_boolean_t *have_base,
8345251881Speter          svn_boolean_t *have_more_work,
8346251881Speter          svn_boolean_t *have_work,
8347251881Speter          svn_wc__db_wcroot_t *wcroot,
8348251881Speter          const char *local_relpath,
8349251881Speter          apr_pool_t *result_pool,
8350251881Speter          apr_pool_t *scratch_pool)
8351251881Speter{
8352251881Speter  svn_sqlite__stmt_t *stmt_info;
8353251881Speter  svn_sqlite__stmt_t *stmt_act;
8354251881Speter  svn_boolean_t have_info;
8355251881Speter  svn_boolean_t have_act;
8356251881Speter  svn_error_t *err = NULL;
8357251881Speter
8358251881Speter  /* Obtain the most likely to exist record first, to make sure we don't
8359251881Speter     have to obtain the SQLite read-lock multiple times */
8360251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8361251881Speter                                    lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8362251881Speter                                         : STMT_SELECT_NODE_INFO));
8363251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8364251881Speter  SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8365251881Speter
8366251881Speter  if (changelist || conflicted || props_mod)
8367251881Speter    {
8368251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8369251881Speter                                        STMT_SELECT_ACTUAL_NODE));
8370251881Speter      SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8371251881Speter      SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8372251881Speter    }
8373251881Speter  else
8374251881Speter    {
8375251881Speter      have_act = FALSE;
8376251881Speter      stmt_act = NULL;
8377251881Speter    }
8378251881Speter
8379251881Speter  if (have_info)
8380251881Speter    {
8381251881Speter      int op_depth;
8382251881Speter      svn_node_kind_t node_kind;
8383251881Speter
8384251881Speter      op_depth = svn_sqlite__column_int(stmt_info, 0);
8385251881Speter      node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8386251881Speter
8387251881Speter      if (status)
8388251881Speter        {
8389251881Speter          *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8390251881Speter
8391251881Speter          if (op_depth != 0) /* WORKING */
8392251881Speter            err = svn_error_compose_create(err,
8393251881Speter                                           convert_to_working_status(status,
8394251881Speter                                                                     *status));
8395251881Speter        }
8396251881Speter      if (kind)
8397251881Speter        {
8398251881Speter          *kind = node_kind;
8399251881Speter        }
8400251881Speter      if (op_depth != 0)
8401251881Speter        {
8402251881Speter          if (repos_id)
8403251881Speter            *repos_id = INVALID_REPOS_ID;
8404251881Speter          if (revision)
8405251881Speter            *revision = SVN_INVALID_REVNUM;
8406251881Speter          if (repos_relpath)
8407251881Speter            /* Our path is implied by our parent somewhere up the tree.
8408251881Speter               With the NULL value and status, the caller will know to
8409251881Speter               search up the tree for the base of our path.  */
8410251881Speter            *repos_relpath = NULL;
8411251881Speter        }
8412251881Speter      else
8413251881Speter        {
8414251881Speter          /* Fetch repository information. If we have a
8415251881Speter             WORKING_NODE (and have been added), then the repository
8416251881Speter             we're being added to will be dependent upon a parent. The
8417251881Speter             caller can scan upwards to locate the repository.  */
8418251881Speter          repos_location_from_columns(repos_id, revision, repos_relpath,
8419251881Speter                                      stmt_info, 1, 5, 2, result_pool);
8420251881Speter        }
8421251881Speter      if (changed_rev)
8422251881Speter        {
8423251881Speter          *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8424251881Speter        }
8425251881Speter      if (changed_date)
8426251881Speter        {
8427251881Speter          *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8428251881Speter        }
8429251881Speter      if (changed_author)
8430251881Speter        {
8431251881Speter          *changed_author = svn_sqlite__column_text(stmt_info, 10,
8432251881Speter                                                    result_pool);
8433251881Speter        }
8434251881Speter      if (recorded_time)
8435251881Speter        {
8436251881Speter          *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8437251881Speter        }
8438251881Speter      if (depth)
8439251881Speter        {
8440251881Speter          if (node_kind != svn_node_dir)
8441251881Speter            {
8442251881Speter              *depth = svn_depth_unknown;
8443251881Speter            }
8444251881Speter          else
8445251881Speter            {
8446251881Speter              *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8447251881Speter                                                     svn_depth_unknown);
8448251881Speter            }
8449251881Speter        }
8450251881Speter      if (checksum)
8451251881Speter        {
8452251881Speter          if (node_kind != svn_node_file)
8453251881Speter            {
8454251881Speter              *checksum = NULL;
8455251881Speter            }
8456251881Speter          else
8457251881Speter            {
8458251881Speter
8459251881Speter              err = svn_error_compose_create(
8460251881Speter                        err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8461251881Speter                                                         result_pool));
8462251881Speter            }
8463251881Speter        }
8464251881Speter      if (recorded_size)
8465251881Speter        {
8466251881Speter          *recorded_size = get_recorded_size(stmt_info, 7);
8467251881Speter        }
8468251881Speter      if (target)
8469251881Speter        {
8470251881Speter          if (node_kind != svn_node_symlink)
8471251881Speter            *target = NULL;
8472251881Speter          else
8473251881Speter            *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8474251881Speter        }
8475251881Speter      if (changelist)
8476251881Speter        {
8477251881Speter          if (have_act)
8478251881Speter            *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8479251881Speter          else
8480251881Speter            *changelist = NULL;
8481251881Speter        }
8482251881Speter      if (op_depth == 0)
8483251881Speter        {
8484251881Speter          if (original_repos_id)
8485251881Speter            *original_repos_id = INVALID_REPOS_ID;
8486251881Speter          if (original_revision)
8487251881Speter            *original_revision = SVN_INVALID_REVNUM;
8488251881Speter          if (original_repos_relpath)
8489251881Speter            *original_repos_relpath = NULL;
8490251881Speter        }
8491251881Speter      else
8492251881Speter        {
8493251881Speter          repos_location_from_columns(original_repos_id,
8494251881Speter                                      original_revision,
8495251881Speter                                      original_repos_relpath,
8496251881Speter                                      stmt_info, 1, 5, 2, result_pool);
8497251881Speter        }
8498251881Speter      if (props_mod)
8499251881Speter        {
8500251881Speter          *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8501251881Speter        }
8502251881Speter      if (had_props)
8503251881Speter        {
8504251881Speter          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8505251881Speter        }
8506251881Speter      if (conflicted)
8507251881Speter        {
8508251881Speter          if (have_act)
8509251881Speter            {
8510251881Speter              *conflicted =
8511251881Speter                 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8512251881Speter            }
8513251881Speter          else
8514251881Speter            *conflicted = FALSE;
8515251881Speter        }
8516251881Speter
8517251881Speter      if (lock)
8518251881Speter        {
8519251881Speter          if (op_depth != 0)
8520251881Speter            *lock = NULL;
8521251881Speter          else
8522251881Speter            *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8523251881Speter        }
8524251881Speter
8525251881Speter      if (have_work)
8526251881Speter        *have_work = (op_depth != 0);
8527251881Speter
8528251881Speter      if (op_root)
8529251881Speter        {
8530251881Speter          *op_root = ((op_depth > 0)
8531251881Speter                      && (op_depth == relpath_depth(local_relpath)));
8532251881Speter        }
8533251881Speter
8534251881Speter      if (have_base || have_more_work)
8535251881Speter        {
8536251881Speter          if (have_more_work)
8537251881Speter            *have_more_work = FALSE;
8538251881Speter
8539251881Speter          while (!err && op_depth != 0)
8540251881Speter            {
8541251881Speter              err = svn_sqlite__step(&have_info, stmt_info);
8542251881Speter
8543251881Speter              if (err || !have_info)
8544251881Speter                break;
8545251881Speter
8546251881Speter              op_depth = svn_sqlite__column_int(stmt_info, 0);
8547251881Speter
8548251881Speter              if (have_more_work)
8549251881Speter                {
8550251881Speter                  if (op_depth > 0)
8551251881Speter                    *have_more_work = TRUE;
8552251881Speter
8553251881Speter                  if (!have_base)
8554251881Speter                   break;
8555251881Speter                }
8556251881Speter            }
8557251881Speter
8558251881Speter          if (have_base)
8559251881Speter            *have_base = (op_depth == 0);
8560251881Speter        }
8561251881Speter    }
8562251881Speter  else if (have_act)
8563251881Speter    {
8564251881Speter      /* A row in ACTUAL_NODE should never exist without a corresponding
8565251881Speter         node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8566251881Speter      if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8567251881Speter          err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8568251881Speter                                  _("Corrupt data for '%s'"),
8569251881Speter                                  path_for_error_message(wcroot, local_relpath,
8570251881Speter                                                         scratch_pool));
8571251881Speter      /* ### What should we return?  Should we have a separate
8572251881Speter             function for reading actual-only nodes? */
8573251881Speter
8574251881Speter      /* As a safety measure, until we decide if we want to use
8575251881Speter         read_info for actual-only nodes, make sure the caller asked
8576251881Speter         for the conflict status. */
8577251881Speter      SVN_ERR_ASSERT(conflicted);
8578251881Speter
8579251881Speter      if (status)
8580251881Speter        *status = svn_wc__db_status_normal;  /* What! No it's not! */
8581251881Speter      if (kind)
8582251881Speter        *kind = svn_node_unknown;
8583251881Speter      if (revision)
8584251881Speter        *revision = SVN_INVALID_REVNUM;
8585251881Speter      if (repos_relpath)
8586251881Speter        *repos_relpath = NULL;
8587251881Speter      if (repos_id)
8588251881Speter        *repos_id = INVALID_REPOS_ID;
8589251881Speter      if (changed_rev)
8590251881Speter        *changed_rev = SVN_INVALID_REVNUM;
8591251881Speter      if (changed_date)
8592251881Speter        *changed_date = 0;
8593251881Speter      if (depth)
8594251881Speter        *depth = svn_depth_unknown;
8595251881Speter      if (checksum)
8596251881Speter        *checksum = NULL;
8597251881Speter      if (target)
8598251881Speter        *target = NULL;
8599251881Speter      if (original_repos_relpath)
8600251881Speter        *original_repos_relpath = NULL;
8601251881Speter      if (original_repos_id)
8602251881Speter        *original_repos_id = INVALID_REPOS_ID;
8603251881Speter      if (original_revision)
8604251881Speter        *original_revision = SVN_INVALID_REVNUM;
8605251881Speter      if (lock)
8606251881Speter        *lock = NULL;
8607251881Speter      if (recorded_size)
8608251881Speter        *recorded_size = 0;
8609251881Speter      if (recorded_time)
8610251881Speter        *recorded_time = 0;
8611251881Speter      if (changelist)
8612251881Speter        *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8613251881Speter      if (op_root)
8614251881Speter        *op_root = FALSE;
8615251881Speter      if (had_props)
8616251881Speter        *had_props = FALSE;
8617251881Speter      if (props_mod)
8618251881Speter        *props_mod = FALSE;
8619251881Speter      if (conflicted)
8620251881Speter        *conflicted = TRUE;
8621251881Speter      if (have_base)
8622251881Speter        *have_base = FALSE;
8623251881Speter      if (have_more_work)
8624251881Speter        *have_more_work = FALSE;
8625251881Speter      if (have_work)
8626251881Speter        *have_work = FALSE;
8627251881Speter    }
8628251881Speter  else
8629251881Speter    {
8630251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8631251881Speter                              _("The node '%s' was not found."),
8632251881Speter                              path_for_error_message(wcroot, local_relpath,
8633251881Speter                                                     scratch_pool));
8634251881Speter    }
8635251881Speter
8636251881Speter  if (stmt_act != NULL)
8637251881Speter    err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8638251881Speter
8639251881Speter  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8640251881Speter    err = svn_error_quick_wrap(err,
8641251881Speter                               apr_psprintf(scratch_pool,
8642251881Speter                                            "Error reading node '%s'",
8643251881Speter                                            local_relpath));
8644251881Speter
8645251881Speter  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8646251881Speter
8647251881Speter  return SVN_NO_ERROR;
8648251881Speter}
8649251881Speter
8650251881Speter
8651251881Spetersvn_error_t *
8652251881Spetersvn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8653251881Speter                              svn_node_kind_t *kind,
8654251881Speter                              svn_revnum_t *revision,
8655251881Speter                              const char **repos_relpath,
8656251881Speter                              apr_int64_t *repos_id,
8657251881Speter                              svn_revnum_t *changed_rev,
8658251881Speter                              apr_time_t *changed_date,
8659251881Speter                              const char **changed_author,
8660251881Speter                              svn_depth_t *depth,
8661251881Speter                              const svn_checksum_t **checksum,
8662251881Speter                              const char **target,
8663251881Speter                              const char **original_repos_relpath,
8664251881Speter                              apr_int64_t *original_repos_id,
8665251881Speter                              svn_revnum_t *original_revision,
8666251881Speter                              svn_wc__db_lock_t **lock,
8667251881Speter                              svn_filesize_t *recorded_size,
8668251881Speter                              apr_time_t *recorded_time,
8669251881Speter                              const char **changelist,
8670251881Speter                              svn_boolean_t *conflicted,
8671251881Speter                              svn_boolean_t *op_root,
8672251881Speter                              svn_boolean_t *had_props,
8673251881Speter                              svn_boolean_t *props_mod,
8674251881Speter                              svn_boolean_t *have_base,
8675251881Speter                              svn_boolean_t *have_more_work,
8676251881Speter                              svn_boolean_t *have_work,
8677251881Speter                              svn_wc__db_wcroot_t *wcroot,
8678251881Speter                              const char *local_relpath,
8679251881Speter                              apr_pool_t *result_pool,
8680251881Speter                              apr_pool_t *scratch_pool)
8681251881Speter{
8682251881Speter  return svn_error_trace(
8683251881Speter           read_info(status, kind, revision, repos_relpath, repos_id,
8684251881Speter                     changed_rev, changed_date, changed_author,
8685251881Speter                     depth, checksum, target, original_repos_relpath,
8686251881Speter                     original_repos_id, original_revision, lock,
8687251881Speter                     recorded_size, recorded_time, changelist, conflicted,
8688251881Speter                     op_root, had_props, props_mod,
8689251881Speter                     have_base, have_more_work, have_work,
8690251881Speter                     wcroot, local_relpath, result_pool, scratch_pool));
8691251881Speter}
8692251881Speter
8693251881Speter
8694251881Spetersvn_error_t *
8695251881Spetersvn_wc__db_read_info(svn_wc__db_status_t *status,
8696251881Speter                     svn_node_kind_t *kind,
8697251881Speter                     svn_revnum_t *revision,
8698251881Speter                     const char **repos_relpath,
8699251881Speter                     const char **repos_root_url,
8700251881Speter                     const char **repos_uuid,
8701251881Speter                     svn_revnum_t *changed_rev,
8702251881Speter                     apr_time_t *changed_date,
8703251881Speter                     const char **changed_author,
8704251881Speter                     svn_depth_t *depth,
8705251881Speter                     const svn_checksum_t **checksum,
8706251881Speter                     const char **target,
8707251881Speter                     const char **original_repos_relpath,
8708251881Speter                     const char **original_root_url,
8709251881Speter                     const char **original_uuid,
8710251881Speter                     svn_revnum_t *original_revision,
8711251881Speter                     svn_wc__db_lock_t **lock,
8712251881Speter                     svn_filesize_t *recorded_size,
8713251881Speter                     apr_time_t *recorded_time,
8714251881Speter                     const char **changelist,
8715251881Speter                     svn_boolean_t *conflicted,
8716251881Speter                     svn_boolean_t *op_root,
8717251881Speter                     svn_boolean_t *have_props,
8718251881Speter                     svn_boolean_t *props_mod,
8719251881Speter                     svn_boolean_t *have_base,
8720251881Speter                     svn_boolean_t *have_more_work,
8721251881Speter                     svn_boolean_t *have_work,
8722251881Speter                     svn_wc__db_t *db,
8723251881Speter                     const char *local_abspath,
8724251881Speter                     apr_pool_t *result_pool,
8725251881Speter                     apr_pool_t *scratch_pool)
8726251881Speter{
8727251881Speter  svn_wc__db_wcroot_t *wcroot;
8728251881Speter  const char *local_relpath;
8729251881Speter  apr_int64_t repos_id, original_repos_id;
8730251881Speter
8731251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8732251881Speter
8733251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8734251881Speter                              local_abspath, scratch_pool, scratch_pool));
8735251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8736251881Speter
8737251881Speter  SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
8738251881Speter                    changed_rev, changed_date, changed_author,
8739251881Speter                    depth, checksum, target, original_repos_relpath,
8740251881Speter                    &original_repos_id, original_revision, lock,
8741251881Speter                    recorded_size, recorded_time, changelist, conflicted,
8742251881Speter                    op_root, have_props, props_mod,
8743251881Speter                    have_base, have_more_work, have_work,
8744251881Speter                    wcroot, local_relpath, result_pool, scratch_pool));
8745251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8746251881Speter                                      wcroot->sdb, repos_id, result_pool));
8747251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8748251881Speter                                      wcroot->sdb, original_repos_id,
8749251881Speter                                      result_pool));
8750251881Speter
8751251881Speter  return SVN_NO_ERROR;
8752251881Speter}
8753251881Speter
8754251881Speterstatic svn_error_t *
8755251881Speteris_wclocked(svn_boolean_t *locked,
8756251881Speter            svn_wc__db_wcroot_t *wcroot,
8757251881Speter            const char *dir_relpath,
8758251881Speter            apr_pool_t *scratch_pool);
8759251881Speter
8760251881Speter/* What we really want to store about a node.  This relies on the
8761251881Speter   offset of svn_wc__db_info_t being zero. */
8762251881Speterstruct read_children_info_item_t
8763251881Speter{
8764251881Speter  struct svn_wc__db_info_t info;
8765251881Speter  int op_depth;
8766251881Speter  int nr_layers;
8767251881Speter};
8768251881Speter
8769251881Speterstatic svn_error_t *
8770251881Speterread_children_info(svn_wc__db_wcroot_t *wcroot,
8771251881Speter                   const char *dir_relpath,
8772251881Speter                   apr_hash_t *conflicts,
8773251881Speter                   apr_hash_t *nodes,
8774251881Speter                   apr_pool_t *result_pool,
8775251881Speter                   apr_pool_t *scratch_pool)
8776251881Speter{
8777251881Speter  svn_sqlite__stmt_t *stmt;
8778251881Speter  svn_boolean_t have_row;
8779251881Speter  const char *repos_root_url = NULL;
8780251881Speter  const char *repos_uuid = NULL;
8781251881Speter  apr_int64_t last_repos_id = INVALID_REPOS_ID;
8782251881Speter
8783251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8784251881Speter                                    STMT_SELECT_NODE_CHILDREN_INFO));
8785251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8786251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8787251881Speter
8788251881Speter  while (have_row)
8789251881Speter    {
8790251881Speter      /* CHILD item points to what we have about the node. We only provide
8791251881Speter         CHILD->item to our caller. */
8792251881Speter      struct read_children_info_item_t *child_item;
8793251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8794251881Speter      const char *name = svn_relpath_basename(child_relpath, NULL);
8795251881Speter      svn_error_t *err;
8796251881Speter      int op_depth;
8797251881Speter      svn_boolean_t new_child;
8798251881Speter
8799251881Speter      child_item = svn_hash_gets(nodes, name);
8800251881Speter      if (child_item)
8801251881Speter        new_child = FALSE;
8802251881Speter      else
8803251881Speter        {
8804251881Speter          child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8805251881Speter          new_child = TRUE;
8806251881Speter        }
8807251881Speter
8808251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
8809251881Speter
8810251881Speter      /* Do we have new or better information? */
8811251881Speter      if (new_child || op_depth > child_item->op_depth)
8812251881Speter        {
8813251881Speter          struct svn_wc__db_info_t *child = &child_item->info;
8814251881Speter          child_item->op_depth = op_depth;
8815251881Speter
8816251881Speter          child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8817251881Speter
8818251881Speter          child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8819251881Speter          if (op_depth != 0)
8820251881Speter            {
8821251881Speter              if (child->status == svn_wc__db_status_incomplete)
8822251881Speter                child->incomplete = TRUE;
8823251881Speter              err = convert_to_working_status(&child->status, child->status);
8824251881Speter              if (err)
8825251881Speter                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8826251881Speter            }
8827251881Speter
8828251881Speter          if (op_depth != 0)
8829251881Speter            child->revnum = SVN_INVALID_REVNUM;
8830251881Speter          else
8831251881Speter            child->revnum = svn_sqlite__column_revnum(stmt, 5);
8832251881Speter
8833251881Speter          if (op_depth != 0)
8834251881Speter            child->repos_relpath = NULL;
8835251881Speter          else
8836251881Speter            child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8837251881Speter                                                           result_pool);
8838251881Speter
8839251881Speter          if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8840251881Speter            {
8841251881Speter              child->repos_root_url = NULL;
8842251881Speter              child->repos_uuid = NULL;
8843251881Speter            }
8844251881Speter          else
8845251881Speter            {
8846251881Speter              const char *last_repos_root_url = NULL;
8847251881Speter
8848251881Speter              apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8849251881Speter              if (!repos_root_url ||
8850251881Speter                  (last_repos_id != INVALID_REPOS_ID &&
8851251881Speter                   repos_id != last_repos_id))
8852251881Speter                {
8853251881Speter                  last_repos_root_url = repos_root_url;
8854251881Speter                  err = svn_wc__db_fetch_repos_info(&repos_root_url,
8855251881Speter                                                    &repos_uuid,
8856251881Speter                                                    wcroot->sdb, repos_id,
8857251881Speter                                                    result_pool);
8858251881Speter                  if (err)
8859251881Speter                    SVN_ERR(svn_error_compose_create(err,
8860251881Speter                                                 svn_sqlite__reset(stmt)));
8861251881Speter                }
8862251881Speter
8863251881Speter              if (last_repos_id == INVALID_REPOS_ID)
8864251881Speter                last_repos_id = repos_id;
8865251881Speter
8866251881Speter              /* Assume working copy is all one repos_id so that a
8867251881Speter                 single cached value is sufficient. */
8868251881Speter              if (repos_id != last_repos_id)
8869251881Speter                {
8870251881Speter                  err= svn_error_createf(
8871251881Speter                         SVN_ERR_WC_DB_ERROR, NULL,
8872251881Speter                         _("The node '%s' comes from unexpected repository "
8873251881Speter                           "'%s', expected '%s'; if this node is a file "
8874251881Speter                           "external using the correct URL in the external "
8875251881Speter                           "definition can fix the problem, see issue #4087"),
8876251881Speter                         child_relpath, repos_root_url, last_repos_root_url);
8877251881Speter                  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8878251881Speter                }
8879251881Speter              child->repos_root_url = repos_root_url;
8880251881Speter              child->repos_uuid = repos_uuid;
8881251881Speter            }
8882251881Speter
8883251881Speter          child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8884251881Speter
8885251881Speter          child->changed_date = svn_sqlite__column_int64(stmt, 9);
8886251881Speter
8887251881Speter          child->changed_author = svn_sqlite__column_text(stmt, 10,
8888251881Speter                                                          result_pool);
8889251881Speter
8890251881Speter          if (child->kind != svn_node_dir)
8891251881Speter            child->depth = svn_depth_unknown;
8892251881Speter          else
8893251881Speter            {
8894251881Speter              child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8895251881Speter                                                           svn_depth_unknown);
8896251881Speter              if (new_child)
8897251881Speter                SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8898251881Speter                                    scratch_pool));
8899251881Speter            }
8900251881Speter
8901251881Speter          child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8902251881Speter          child->recorded_size = get_recorded_size(stmt, 7);
8903251881Speter          child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8904251881Speter          child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8905251881Speter          child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8906251881Speter#ifdef HAVE_SYMLINK
8907251881Speter          if (child->had_props)
8908251881Speter            {
8909251881Speter              apr_hash_t *properties;
8910251881Speter              err = svn_sqlite__column_properties(&properties, stmt, 14,
8911251881Speter                                                  scratch_pool, scratch_pool);
8912251881Speter              if (err)
8913251881Speter                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8914251881Speter
8915251881Speter              child->special = (child->had_props
8916251881Speter                                && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8917251881Speter            }
8918251881Speter#endif
8919251881Speter          if (op_depth == 0)
8920251881Speter            child->op_root = FALSE;
8921251881Speter          else
8922251881Speter            child->op_root = (op_depth == relpath_depth(child_relpath));
8923251881Speter
8924269847Speter          if (op_depth && child->op_root)
8925269847Speter            child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8926269847Speter
8927269847Speter          if (new_child)
8928269847Speter            svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8929251881Speter        }
8930251881Speter
8931251881Speter      if (op_depth == 0)
8932251881Speter        {
8933251881Speter          child_item->info.have_base = TRUE;
8934251881Speter
8935251881Speter          /* Get the lock info, available only at op_depth 0. */
8936251881Speter          child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8937251881Speter                                                    result_pool);
8938251881Speter
8939251881Speter          /* FILE_EXTERNAL flag only on op_depth 0. */
8940251881Speter          child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8941251881Speter                                                                      22);
8942251881Speter        }
8943251881Speter      else
8944251881Speter        {
8945251881Speter          const char *moved_to_relpath;
8946251881Speter
8947251881Speter          child_item->nr_layers++;
8948251881Speter          child_item->info.have_more_work = (child_item->nr_layers > 1);
8949251881Speter
8950269847Speter
8951269847Speter          /* A local_relpath can be moved multiple times at different op
8952269847Speter             depths and it really depends on the caller what is interesting.
8953269847Speter             We provide a simple linked list with the moved_from information */
8954269847Speter
8955251881Speter          moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8956251881Speter          if (moved_to_relpath)
8957269847Speter            {
8958269847Speter              struct svn_wc__db_moved_to_info_t *moved_to;
8959269847Speter              struct svn_wc__db_moved_to_info_t **next;
8960269847Speter              const char *shadow_op_relpath;
8961269847Speter              int cur_op_depth;
8962251881Speter
8963269847Speter              moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
8964269847Speter              moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
8965269847Speter                                                           moved_to_relpath,
8966269847Speter                                                           result_pool);
8967269847Speter
8968269847Speter              cur_op_depth = relpath_depth(child_relpath);
8969269847Speter              shadow_op_relpath = child_relpath;
8970269847Speter
8971269847Speter              while (cur_op_depth > op_depth)
8972269847Speter                {
8973269847Speter                  shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
8974269847Speter                                                          scratch_pool);
8975269847Speter                  cur_op_depth--;
8976269847Speter                }
8977269847Speter
8978269847Speter              moved_to->shadow_op_root_abspath =
8979269847Speter                        svn_dirent_join(wcroot->abspath, shadow_op_relpath,
8980269847Speter                                        result_pool);
8981269847Speter
8982269847Speter              next = &child_item->info.moved_to;
8983269847Speter
8984269847Speter              while (*next &&
8985269847Speter                     0 < strcmp((*next)->shadow_op_root_abspath,
8986269847Speter                                moved_to->shadow_op_root_abspath))
8987269847Speter                next = &((*next)->next);
8988269847Speter
8989269847Speter              moved_to->next = *next;
8990269847Speter              *next = moved_to;
8991269847Speter            }
8992251881Speter        }
8993251881Speter
8994251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8995251881Speter    }
8996251881Speter
8997251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
8998251881Speter
8999251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9000251881Speter                                    STMT_SELECT_ACTUAL_CHILDREN_INFO));
9001251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9002251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9003251881Speter
9004251881Speter  while (have_row)
9005251881Speter    {
9006251881Speter      struct read_children_info_item_t *child_item;
9007251881Speter      struct svn_wc__db_info_t *child;
9008251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9009251881Speter      const char *name = svn_relpath_basename(child_relpath, NULL);
9010251881Speter
9011251881Speter      child_item = svn_hash_gets(nodes, name);
9012251881Speter      if (!child_item)
9013251881Speter        {
9014251881Speter          child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9015251881Speter          child_item->info.status = svn_wc__db_status_not_present;
9016251881Speter        }
9017251881Speter
9018251881Speter      child = &child_item->info;
9019251881Speter
9020251881Speter      child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9021251881Speter
9022251881Speter      child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9023251881Speter#ifdef HAVE_SYMLINK
9024251881Speter      if (child->props_mod)
9025251881Speter        {
9026251881Speter          svn_error_t *err;
9027251881Speter          apr_hash_t *properties;
9028251881Speter
9029251881Speter          err = svn_sqlite__column_properties(&properties, stmt, 2,
9030251881Speter                                              scratch_pool, scratch_pool);
9031251881Speter          if (err)
9032251881Speter            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9033251881Speter          child->special = (NULL != svn_hash_gets(properties,
9034251881Speter                                                  SVN_PROP_SPECIAL));
9035251881Speter        }
9036251881Speter#endif
9037251881Speter
9038251881Speter      child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
9039251881Speter
9040251881Speter      if (child->conflicted)
9041251881Speter        svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9042251881Speter
9043251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9044251881Speter    }
9045251881Speter
9046251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
9047251881Speter
9048251881Speter  return SVN_NO_ERROR;
9049251881Speter}
9050251881Speter
9051251881Spetersvn_error_t *
9052251881Spetersvn_wc__db_read_children_info(apr_hash_t **nodes,
9053251881Speter                              apr_hash_t **conflicts,
9054251881Speter                              svn_wc__db_t *db,
9055251881Speter                              const char *dir_abspath,
9056251881Speter                              apr_pool_t *result_pool,
9057251881Speter                              apr_pool_t *scratch_pool)
9058251881Speter{
9059251881Speter  svn_wc__db_wcroot_t *wcroot;
9060251881Speter  const char *dir_relpath;
9061251881Speter
9062251881Speter  *conflicts = apr_hash_make(result_pool);
9063251881Speter  *nodes = apr_hash_make(result_pool);
9064251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9065251881Speter
9066251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9067251881Speter                                                dir_abspath,
9068251881Speter                                                scratch_pool, scratch_pool));
9069251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9070251881Speter
9071251881Speter  SVN_WC__DB_WITH_TXN(
9072251881Speter    read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9073251881Speter                       result_pool, scratch_pool),
9074251881Speter    wcroot);
9075251881Speter
9076251881Speter  return SVN_NO_ERROR;
9077251881Speter}
9078251881Speter
9079269847Speterstatic svn_error_t *
9080269847Speterdb_read_props(apr_hash_t **props,
9081269847Speter              svn_wc__db_wcroot_t *wcroot,
9082269847Speter              const char *local_relpath,
9083269847Speter              apr_pool_t *result_pool,
9084269847Speter              apr_pool_t *scratch_pool);
9085269847Speter
9086269847Speterstatic svn_error_t *
9087269847Speterread_single_info(const struct svn_wc__db_info_t **info,
9088269847Speter                 svn_wc__db_wcroot_t *wcroot,
9089269847Speter                 const char *local_relpath,
9090269847Speter                 apr_pool_t *result_pool,
9091269847Speter                 apr_pool_t *scratch_pool)
9092269847Speter{
9093269847Speter  struct svn_wc__db_info_t *mtb;
9094269847Speter  apr_int64_t repos_id;
9095269847Speter  const svn_checksum_t *checksum;
9096269847Speter  const char *original_repos_relpath;
9097269847Speter  svn_boolean_t have_work;
9098269847Speter
9099269847Speter  mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9100269847Speter
9101269847Speter  SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9102269847Speter                    &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9103269847Speter                    &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9104269847Speter                    &checksum, NULL, &original_repos_relpath, NULL, NULL,
9105269847Speter                    &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9106269847Speter                    &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9107269847Speter                    &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9108269847Speter                    &mtb->have_more_work, &have_work,
9109269847Speter                    wcroot, local_relpath,
9110269847Speter                    result_pool, scratch_pool));
9111269847Speter
9112269847Speter  /* Query the same rows in the database again for move information */
9113269847Speter  if (have_work && (mtb->have_base || mtb->have_more_work))
9114269847Speter    {
9115269847Speter      svn_sqlite__stmt_t *stmt;
9116269847Speter      svn_boolean_t have_row;
9117269847Speter      const char *cur_relpath = NULL;
9118269847Speter      int cur_op_depth;
9119269847Speter
9120269847Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9121269847Speter                                        STMT_SELECT_MOVED_TO_NODE));
9122269847Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9123269847Speter
9124269847Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9125269847Speter
9126269847Speter      while (have_row)
9127269847Speter        {
9128269847Speter          struct svn_wc__db_moved_to_info_t *move;
9129269847Speter          int op_depth = svn_sqlite__column_int(stmt, 0);
9130269847Speter          const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9131269847Speter
9132269847Speter          move = apr_pcalloc(result_pool, sizeof(*move));
9133269847Speter          move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9134269847Speter                                                   moved_to_relpath,
9135269847Speter                                                   result_pool);
9136269847Speter
9137269847Speter          if (!cur_relpath)
9138269847Speter            {
9139269847Speter              cur_relpath = local_relpath;
9140269847Speter              cur_op_depth = relpath_depth(cur_relpath);
9141269847Speter            }
9142269847Speter          while (cur_op_depth > op_depth)
9143269847Speter            {
9144269847Speter              cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
9145269847Speter              cur_op_depth--;
9146269847Speter            }
9147269847Speter          move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9148269847Speter                                                         cur_relpath,
9149269847Speter                                                         result_pool);
9150269847Speter
9151269847Speter          move->next = mtb->moved_to;
9152269847Speter          mtb->moved_to = move;
9153269847Speter
9154269847Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
9155269847Speter        }
9156269847Speter
9157269847Speter      SVN_ERR(svn_sqlite__reset(stmt));
9158269847Speter    }
9159269847Speter
9160269847Speter  /* Maybe we have to get some shadowed lock from BASE to make our test suite
9161269847Speter     happy... (It might be completely unrelated, but...)
9162269847Speter     This queries the same BASE row again, joined to the lock table */
9163269847Speter  if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
9164269847Speter    {
9165269847Speter      svn_boolean_t update_root;
9166269847Speter      svn_wc__db_lock_t **lock_arg = NULL;
9167269847Speter
9168269847Speter      if (have_work)
9169269847Speter        lock_arg = &mtb->lock;
9170269847Speter
9171269847Speter      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9172269847Speter                                                NULL, NULL, NULL, NULL, NULL,
9173269847Speter                                                NULL, lock_arg, NULL, NULL,
9174269847Speter                                                &update_root,
9175269847Speter                                                wcroot, local_relpath,
9176269847Speter                                                result_pool, scratch_pool));
9177269847Speter
9178269847Speter      mtb->file_external = (update_root && mtb->kind == svn_node_file);
9179269847Speter    }
9180269847Speter
9181269847Speter  if (mtb->status == svn_wc__db_status_added)
9182269847Speter    {
9183269847Speter      svn_wc__db_status_t status;
9184269847Speter
9185269847Speter      SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9186269847Speter                            NULL, NULL,
9187269847Speter                            wcroot, local_relpath,
9188269847Speter                            result_pool, scratch_pool));
9189269847Speter
9190269847Speter      mtb->moved_here = (status == svn_wc__db_status_moved_here);
9191269847Speter      mtb->incomplete = (status == svn_wc__db_status_incomplete);
9192269847Speter    }
9193269847Speter
9194269847Speter#ifdef HAVE_SYMLINK
9195269847Speter  if (mtb->kind == svn_node_file
9196269847Speter      && (mtb->had_props || mtb->props_mod))
9197269847Speter    {
9198269847Speter      apr_hash_t *properties;
9199269847Speter
9200269847Speter      if (mtb->props_mod)
9201269847Speter        SVN_ERR(db_read_props(&properties,
9202269847Speter                              wcroot, local_relpath,
9203269847Speter                              scratch_pool, scratch_pool));
9204269847Speter      else
9205269847Speter        SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9206269847Speter                                       TRUE /* deleted_ok */,
9207269847Speter                                       scratch_pool, scratch_pool));
9208269847Speter
9209269847Speter      mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9210269847Speter    }
9211269847Speter#endif
9212269847Speter
9213269847Speter  mtb->has_checksum = (checksum != NULL);
9214269847Speter  mtb->copied = (original_repos_relpath != NULL);
9215269847Speter
9216269847Speter  SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9217269847Speter                                      wcroot->sdb, repos_id, result_pool));
9218269847Speter
9219269847Speter  if (mtb->kind == svn_node_dir)
9220269847Speter    SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9221269847Speter
9222269847Speter  *info = mtb;
9223269847Speter
9224269847Speter  return SVN_NO_ERROR;
9225269847Speter}
9226269847Speter
9227251881Spetersvn_error_t *
9228269847Spetersvn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9229269847Speter                            svn_wc__db_t *db,
9230269847Speter                            const char *local_abspath,
9231269847Speter                            apr_pool_t *result_pool,
9232269847Speter                            apr_pool_t *scratch_pool)
9233269847Speter{
9234269847Speter  svn_wc__db_wcroot_t *wcroot;
9235269847Speter  const char *local_relpath;
9236269847Speter
9237269847Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9238269847Speter
9239269847Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9240269847Speter                                                local_abspath,
9241269847Speter                                                scratch_pool, scratch_pool));
9242269847Speter  VERIFY_USABLE_WCROOT(wcroot);
9243269847Speter
9244269847Speter  SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9245269847Speter                                       result_pool, scratch_pool),
9246269847Speter                      wcroot);
9247269847Speter
9248269847Speter  return SVN_NO_ERROR;
9249269847Speter}
9250269847Speter
9251269847Spetersvn_error_t *
9252251881Spetersvn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9253251881Speter                              svn_node_kind_t *kind,
9254251881Speter                              svn_revnum_t *changed_rev,
9255251881Speter                              apr_time_t *changed_date,
9256251881Speter                              const char **changed_author,
9257251881Speter                              svn_depth_t *depth,  /* dirs only */
9258251881Speter                              const svn_checksum_t **checksum, /* files only */
9259251881Speter                              const char **target, /* symlinks only */
9260251881Speter                              svn_boolean_t *had_props,
9261251881Speter                              apr_hash_t **props,
9262251881Speter                              svn_wc__db_t *db,
9263251881Speter                              const char *local_abspath,
9264251881Speter                              apr_pool_t *result_pool,
9265251881Speter                              apr_pool_t *scratch_pool)
9266251881Speter{
9267251881Speter  svn_wc__db_wcroot_t *wcroot;
9268251881Speter  const char *local_relpath;
9269251881Speter  svn_sqlite__stmt_t *stmt;
9270251881Speter  svn_boolean_t have_row;
9271251881Speter  svn_error_t *err = NULL;
9272251881Speter  int op_depth;
9273251881Speter  svn_wc__db_status_t raw_status;
9274251881Speter  svn_node_kind_t node_kind;
9275251881Speter
9276251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9277251881Speter
9278251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9279251881Speter                                                local_abspath,
9280251881Speter                                                scratch_pool, scratch_pool));
9281251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9282251881Speter
9283251881Speter  /* Obtain the most likely to exist record first, to make sure we don't
9284251881Speter     have to obtain the SQLite read-lock multiple times */
9285251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9286251881Speter                                    STMT_SELECT_NODE_INFO));
9287251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9288251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9289251881Speter
9290251881Speter  if (!have_row)
9291251881Speter    {
9292251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9293251881Speter                               svn_sqlite__reset(stmt),
9294251881Speter                               _("The node '%s' was not found."),
9295251881Speter                               path_for_error_message(wcroot,
9296251881Speter                                                      local_relpath,
9297251881Speter                                                      scratch_pool));
9298251881Speter    }
9299251881Speter
9300251881Speter  op_depth = svn_sqlite__column_int(stmt, 0);
9301251881Speter  raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9302251881Speter
9303251881Speter  if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9304251881Speter    {
9305251881Speter      SVN_ERR(svn_sqlite__step_row(stmt));
9306251881Speter
9307251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
9308251881Speter      raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9309251881Speter    }
9310251881Speter
9311251881Speter  node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9312251881Speter
9313251881Speter  if (status)
9314251881Speter    {
9315251881Speter      if (op_depth > 0)
9316251881Speter        {
9317251881Speter          err = svn_error_compose_create(err,
9318251881Speter                                         convert_to_working_status(
9319251881Speter                                                    status,
9320251881Speter                                                    raw_status));
9321251881Speter        }
9322251881Speter      else
9323251881Speter        *status = raw_status;
9324251881Speter    }
9325251881Speter  if (kind)
9326251881Speter    {
9327251881Speter      *kind = node_kind;
9328251881Speter    }
9329251881Speter  if (changed_rev)
9330251881Speter    {
9331251881Speter      *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9332251881Speter    }
9333251881Speter  if (changed_date)
9334251881Speter    {
9335251881Speter      *changed_date = svn_sqlite__column_int64(stmt, 9);
9336251881Speter    }
9337251881Speter  if (changed_author)
9338251881Speter    {
9339251881Speter      *changed_author = svn_sqlite__column_text(stmt, 10,
9340251881Speter                                                result_pool);
9341251881Speter    }
9342251881Speter  if (depth)
9343251881Speter    {
9344251881Speter      if (node_kind != svn_node_dir)
9345251881Speter        {
9346251881Speter          *depth = svn_depth_unknown;
9347251881Speter        }
9348251881Speter      else
9349251881Speter        {
9350251881Speter          *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9351251881Speter                                                 svn_depth_unknown);
9352251881Speter        }
9353251881Speter    }
9354251881Speter  if (checksum)
9355251881Speter    {
9356251881Speter      if (node_kind != svn_node_file)
9357251881Speter        {
9358251881Speter          *checksum = NULL;
9359251881Speter        }
9360251881Speter      else
9361251881Speter        {
9362251881Speter          svn_error_t *err2;
9363251881Speter          err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9364251881Speter
9365251881Speter          if (err2 != NULL)
9366251881Speter            {
9367251881Speter              if (err)
9368251881Speter                err = svn_error_compose_create(
9369251881Speter                         err,
9370251881Speter                         svn_error_createf(
9371251881Speter                               err->apr_err, err2,
9372251881Speter                              _("The node '%s' has a corrupt checksum value."),
9373251881Speter                              path_for_error_message(wcroot, local_relpath,
9374251881Speter                                                     scratch_pool)));
9375251881Speter              else
9376251881Speter                err = err2;
9377251881Speter            }
9378251881Speter        }
9379251881Speter    }
9380251881Speter  if (target)
9381251881Speter    {
9382251881Speter      if (node_kind != svn_node_symlink)
9383251881Speter        *target = NULL;
9384251881Speter      else
9385251881Speter        *target = svn_sqlite__column_text(stmt, 12, result_pool);
9386251881Speter    }
9387251881Speter  if (had_props)
9388251881Speter    {
9389251881Speter      *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9390251881Speter    }
9391251881Speter  if (props)
9392251881Speter    {
9393251881Speter      if (raw_status == svn_wc__db_status_normal
9394251881Speter          || raw_status == svn_wc__db_status_incomplete)
9395251881Speter        {
9396251881Speter          SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9397251881Speter                                                result_pool, scratch_pool));
9398251881Speter          if (*props == NULL)
9399251881Speter            *props = apr_hash_make(result_pool);
9400251881Speter        }
9401251881Speter      else
9402251881Speter        {
9403251881Speter          assert(svn_sqlite__column_is_null(stmt, 14));
9404251881Speter          *props = NULL;
9405251881Speter        }
9406251881Speter    }
9407251881Speter
9408251881Speter  return svn_error_trace(
9409251881Speter            svn_error_compose_create(err,
9410251881Speter                                     svn_sqlite__reset(stmt)));
9411251881Speter}
9412251881Speter
9413251881Spetersvn_error_t *
9414251881Spetersvn_wc__db_read_children_walker_info(apr_hash_t **nodes,
9415251881Speter                                     svn_wc__db_t *db,
9416251881Speter                                     const char *dir_abspath,
9417251881Speter                                     apr_pool_t *result_pool,
9418251881Speter                                     apr_pool_t *scratch_pool)
9419251881Speter{
9420251881Speter  svn_wc__db_wcroot_t *wcroot;
9421251881Speter  const char *dir_relpath;
9422251881Speter  svn_sqlite__stmt_t *stmt;
9423251881Speter  svn_boolean_t have_row;
9424251881Speter
9425251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9426251881Speter
9427251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9428251881Speter                                             dir_abspath,
9429251881Speter                                             scratch_pool, scratch_pool));
9430251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9431251881Speter
9432251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9433251881Speter                                    STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9434251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9435251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9436251881Speter
9437251881Speter  *nodes = apr_hash_make(result_pool);
9438251881Speter  while (have_row)
9439251881Speter    {
9440251881Speter      struct svn_wc__db_walker_info_t *child;
9441251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9442251881Speter      const char *name = svn_relpath_basename(child_relpath, NULL);
9443251881Speter      int op_depth = svn_sqlite__column_int(stmt, 1);
9444251881Speter      svn_error_t *err;
9445251881Speter
9446251881Speter      child = apr_palloc(result_pool, sizeof(*child));
9447251881Speter      child->status = svn_sqlite__column_token(stmt, 2, presence_map);
9448251881Speter      if (op_depth > 0)
9449251881Speter        {
9450251881Speter          err = convert_to_working_status(&child->status, child->status);
9451251881Speter          if (err)
9452251881Speter            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9453251881Speter        }
9454251881Speter      child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
9455251881Speter      svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
9456251881Speter
9457251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9458251881Speter    }
9459251881Speter
9460251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
9461251881Speter
9462251881Speter  return SVN_NO_ERROR;
9463251881Speter}
9464251881Speter
9465251881Spetersvn_error_t *
9466251881Spetersvn_wc__db_read_node_install_info(const char **wcroot_abspath,
9467251881Speter                                  const svn_checksum_t **sha1_checksum,
9468251881Speter                                  apr_hash_t **pristine_props,
9469251881Speter                                  apr_time_t *changed_date,
9470251881Speter                                  svn_wc__db_t *db,
9471251881Speter                                  const char *local_abspath,
9472251881Speter                                  const char *wri_abspath,
9473251881Speter                                  apr_pool_t *result_pool,
9474251881Speter                                  apr_pool_t *scratch_pool)
9475251881Speter{
9476251881Speter  svn_wc__db_wcroot_t *wcroot;
9477251881Speter  const char *local_relpath;
9478251881Speter  svn_sqlite__stmt_t *stmt;
9479251881Speter  svn_error_t *err = NULL;
9480251881Speter  svn_boolean_t have_row;
9481251881Speter
9482251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9483251881Speter
9484251881Speter  if (!wri_abspath)
9485251881Speter    wri_abspath = local_abspath;
9486251881Speter
9487251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9488251881Speter                              wri_abspath, scratch_pool, scratch_pool));
9489251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9490251881Speter
9491251881Speter  if (local_abspath != wri_abspath
9492251881Speter      && strcmp(local_abspath, wri_abspath))
9493251881Speter    {
9494251881Speter      if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9495251881Speter        return svn_error_createf(
9496251881Speter                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9497251881Speter                    _("The node '%s' is not in working copy '%s'"),
9498251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool),
9499251881Speter                    svn_dirent_local_style(wcroot->abspath, scratch_pool));
9500251881Speter
9501251881Speter      local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9502251881Speter    }
9503251881Speter
9504251881Speter  if (wcroot_abspath != NULL)
9505251881Speter    *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9506251881Speter
9507251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9508251881Speter                                    STMT_SELECT_NODE_INFO));
9509251881Speter
9510251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9511251881Speter
9512251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9513251881Speter
9514251881Speter  if (have_row)
9515251881Speter    {
9516251881Speter      if (!err && sha1_checksum)
9517251881Speter        err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9518251881Speter
9519251881Speter      if (!err && pristine_props)
9520251881Speter        {
9521251881Speter          err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9522251881Speter                                              result_pool, scratch_pool);
9523251881Speter          /* Null means no props (assuming presence normal or incomplete). */
9524251881Speter          if (*pristine_props == NULL)
9525251881Speter            *pristine_props = apr_hash_make(result_pool);
9526251881Speter        }
9527251881Speter
9528251881Speter      if (changed_date)
9529251881Speter        *changed_date = svn_sqlite__column_int64(stmt, 9);
9530251881Speter    }
9531251881Speter  else
9532251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9533251881Speter                             svn_sqlite__reset(stmt),
9534251881Speter                             _("The node '%s' is not installable"),
9535251881Speter                             svn_dirent_local_style(local_abspath,
9536251881Speter                                                    scratch_pool));
9537251881Speter
9538251881Speter  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9539251881Speter
9540251881Speter  return SVN_NO_ERROR;
9541251881Speter}
9542251881Speter
9543251881Speter
9544251881Speter
9545251881Speter/* The body of svn_wc__db_read_url().
9546251881Speter */
9547251881Speterstatic svn_error_t *
9548251881Speterread_url_txn(const char **url,
9549251881Speter             svn_wc__db_wcroot_t *wcroot,
9550251881Speter             const char *local_relpath,
9551251881Speter             apr_pool_t *result_pool,
9552251881Speter             apr_pool_t *scratch_pool)
9553251881Speter{
9554251881Speter  svn_wc__db_status_t status;
9555251881Speter  const char *repos_relpath;
9556251881Speter  const char *repos_root_url;
9557251881Speter  apr_int64_t repos_id;
9558251881Speter  svn_boolean_t have_base;
9559251881Speter
9560251881Speter  SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9561251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9562251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9563251881Speter                    &have_base, NULL, NULL,
9564251881Speter                    wcroot, local_relpath, scratch_pool, scratch_pool));
9565251881Speter
9566251881Speter  if (repos_relpath == NULL)
9567251881Speter    {
9568251881Speter      if (status == svn_wc__db_status_added)
9569251881Speter        {
9570251881Speter          SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9571251881Speter                                NULL, NULL, NULL, NULL, NULL,
9572251881Speter                                wcroot, local_relpath,
9573251881Speter                                scratch_pool, scratch_pool));
9574251881Speter        }
9575251881Speter      else if (status == svn_wc__db_status_deleted)
9576251881Speter        {
9577251881Speter          const char *base_del_relpath;
9578251881Speter          const char *work_del_relpath;
9579251881Speter
9580251881Speter          SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9581251881Speter                                    &work_del_relpath,
9582251881Speter                                    NULL, wcroot,
9583251881Speter                                    local_relpath,
9584251881Speter                                    scratch_pool,
9585251881Speter                                    scratch_pool));
9586251881Speter
9587251881Speter          if (base_del_relpath)
9588251881Speter            {
9589251881Speter              SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9590251881Speter                                                        &repos_relpath,
9591251881Speter                                                        &repos_id,
9592251881Speter                                                        NULL, NULL, NULL,
9593251881Speter                                                        NULL, NULL, NULL,
9594251881Speter                                                        NULL, NULL, NULL, NULL,
9595251881Speter                                                        wcroot,
9596251881Speter                                                        base_del_relpath,
9597251881Speter                                                        scratch_pool,
9598251881Speter                                                        scratch_pool));
9599251881Speter
9600251881Speter              repos_relpath = svn_relpath_join(
9601251881Speter                                    repos_relpath,
9602251881Speter                                    svn_dirent_skip_ancestor(base_del_relpath,
9603251881Speter                                                             local_relpath),
9604251881Speter                                    scratch_pool);
9605251881Speter            }
9606251881Speter          else
9607251881Speter            {
9608251881Speter              /* The parent of the WORKING delete, must be an addition */
9609251881Speter              const char *work_relpath = NULL;
9610251881Speter
9611251881Speter              /* work_del_relpath should not be NULL. However, we have
9612251881Speter               * observed instances where that assumption was not met.
9613251881Speter               * Bail out in that case instead of crashing with a segfault.
9614251881Speter               */
9615251881Speter              SVN_ERR_ASSERT(work_del_relpath != NULL);
9616251881Speter              work_relpath = svn_relpath_dirname(work_del_relpath,
9617251881Speter                                                 scratch_pool);
9618251881Speter
9619251881Speter              SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9620251881Speter                                    NULL, NULL, NULL, NULL, NULL, NULL,
9621251881Speter                                    wcroot, work_relpath,
9622251881Speter                                    scratch_pool, scratch_pool));
9623251881Speter
9624251881Speter              repos_relpath = svn_relpath_join(
9625251881Speter                                    repos_relpath,
9626251881Speter                                    svn_dirent_skip_ancestor(work_relpath,
9627251881Speter                                                             local_relpath),
9628251881Speter                                    scratch_pool);
9629251881Speter            }
9630251881Speter        }
9631251881Speter      else if (status == svn_wc__db_status_excluded)
9632251881Speter        {
9633251881Speter          const char *parent_relpath;
9634251881Speter          const char *name;
9635251881Speter          const char *url2;
9636251881Speter
9637251881Speter          /* Set 'url' to the *full URL* of the parent WC dir,
9638251881Speter           * and 'name' to the *single path component* that is the
9639251881Speter           * basename of this WC directory, so that joining them will result
9640251881Speter           * in the correct full URL. */
9641251881Speter          svn_relpath_split(&parent_relpath, &name, local_relpath,
9642251881Speter                            scratch_pool);
9643251881Speter          SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9644251881Speter                               scratch_pool, scratch_pool));
9645251881Speter
9646251881Speter          *url = svn_path_url_add_component2(url2, name, result_pool);
9647251881Speter
9648251881Speter          return SVN_NO_ERROR;
9649251881Speter        }
9650251881Speter      else
9651251881Speter        {
9652251881Speter          /* All working statee are explicitly handled and all base statee
9653251881Speter             have a repos_relpath */
9654251881Speter          SVN_ERR_MALFUNCTION();
9655251881Speter        }
9656251881Speter    }
9657251881Speter
9658251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9659251881Speter                                      repos_id, scratch_pool));
9660251881Speter
9661251881Speter  SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9662251881Speter  *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9663251881Speter                                     result_pool);
9664251881Speter
9665251881Speter  return SVN_NO_ERROR;
9666251881Speter}
9667251881Speter
9668251881Speter
9669251881Spetersvn_error_t *
9670251881Spetersvn_wc__db_read_url(const char **url,
9671251881Speter                    svn_wc__db_t *db,
9672251881Speter                    const char *local_abspath,
9673251881Speter                    apr_pool_t *result_pool,
9674251881Speter                    apr_pool_t *scratch_pool)
9675251881Speter{
9676251881Speter  svn_wc__db_wcroot_t *wcroot;
9677251881Speter  const char *local_relpath;
9678251881Speter
9679251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9680251881Speter
9681251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9682251881Speter                                                local_abspath,
9683251881Speter                                                scratch_pool, scratch_pool));
9684251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9685251881Speter
9686251881Speter  SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9687251881Speter                                   result_pool, scratch_pool),
9688251881Speter                      wcroot);
9689251881Speter
9690251881Speter  return SVN_NO_ERROR;
9691251881Speter}
9692251881Speter
9693251881Speter
9694251881Speter/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9695251881Speter   a hash table mapping <tt>char *</tt> names onto svn_string_t *
9696251881Speter   values for any properties of immediate or recursive child nodes of
9697251881Speter   LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9698251881Speter   If FILES_ONLY is true, only report properties for file child nodes.
9699251881Speter   Check for cancellation between calls of RECEIVER_FUNC.
9700251881Speter*/
9701251881Spetertypedef struct cache_props_baton_t
9702251881Speter{
9703251881Speter  svn_depth_t depth;
9704251881Speter  svn_boolean_t pristine;
9705251881Speter  const apr_array_header_t *changelists;
9706251881Speter  svn_cancel_func_t cancel_func;
9707251881Speter  void *cancel_baton;
9708251881Speter} cache_props_baton_t;
9709251881Speter
9710251881Speter
9711251881Speterstatic svn_error_t *
9712251881Spetercache_props_recursive(void *cb_baton,
9713251881Speter                      svn_wc__db_wcroot_t *wcroot,
9714251881Speter                      const char *local_relpath,
9715251881Speter                      apr_pool_t *scratch_pool)
9716251881Speter{
9717251881Speter  cache_props_baton_t *baton = cb_baton;
9718251881Speter  svn_sqlite__stmt_t *stmt;
9719251881Speter  int stmt_idx;
9720251881Speter
9721251881Speter  SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9722251881Speter                                baton->changelists, scratch_pool));
9723251881Speter
9724251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9725251881Speter                                      STMT_CREATE_TARGET_PROP_CACHE));
9726251881Speter
9727251881Speter  if (baton->pristine)
9728251881Speter    stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9729251881Speter  else
9730251881Speter    stmt_idx = STMT_CACHE_TARGET_PROPS;
9731251881Speter
9732251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9733251881Speter  SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9734251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
9735251881Speter
9736251881Speter  return SVN_NO_ERROR;
9737251881Speter}
9738251881Speter
9739251881Speter
9740251881Spetersvn_error_t *
9741251881Spetersvn_wc__db_read_props_streamily(svn_wc__db_t *db,
9742251881Speter                                const char *local_abspath,
9743251881Speter                                svn_depth_t depth,
9744251881Speter                                svn_boolean_t pristine,
9745251881Speter                                const apr_array_header_t *changelists,
9746251881Speter                                svn_wc__proplist_receiver_t receiver_func,
9747251881Speter                                void *receiver_baton,
9748251881Speter                                svn_cancel_func_t cancel_func,
9749251881Speter                                void *cancel_baton,
9750251881Speter                                apr_pool_t *scratch_pool)
9751251881Speter{
9752251881Speter  svn_wc__db_wcroot_t *wcroot;
9753251881Speter  const char *local_relpath;
9754251881Speter  svn_sqlite__stmt_t *stmt;
9755251881Speter  cache_props_baton_t baton;
9756251881Speter  svn_boolean_t have_row;
9757251881Speter  apr_pool_t *iterpool;
9758251881Speter  svn_error_t *err = NULL;
9759251881Speter
9760251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9761251881Speter  SVN_ERR_ASSERT(receiver_func);
9762251881Speter  SVN_ERR_ASSERT((depth == svn_depth_files) ||
9763251881Speter                 (depth == svn_depth_immediates) ||
9764251881Speter                 (depth == svn_depth_infinity));
9765251881Speter
9766251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9767251881Speter                                                db, local_abspath,
9768251881Speter                                                scratch_pool, scratch_pool));
9769251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9770251881Speter
9771251881Speter  baton.depth = depth;
9772251881Speter  baton.pristine = pristine;
9773251881Speter  baton.changelists = changelists;
9774251881Speter  baton.cancel_func = cancel_func;
9775251881Speter  baton.cancel_baton = cancel_baton;
9776251881Speter
9777251881Speter  SVN_ERR(with_finalization(wcroot, local_relpath,
9778251881Speter                            cache_props_recursive, &baton,
9779251881Speter                            NULL, NULL,
9780251881Speter                            cancel_func, cancel_baton,
9781251881Speter                            NULL, NULL,
9782251881Speter                            STMT_DROP_TARGETS_LIST,
9783251881Speter                            scratch_pool));
9784251881Speter
9785251881Speter  iterpool = svn_pool_create(scratch_pool);
9786251881Speter
9787251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9788251881Speter                                    STMT_SELECT_ALL_TARGET_PROP_CACHE));
9789251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9790251881Speter  while (!err && have_row)
9791251881Speter    {
9792251881Speter      apr_hash_t *props;
9793251881Speter
9794251881Speter      svn_pool_clear(iterpool);
9795251881Speter
9796251881Speter      SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9797251881Speter                                            iterpool));
9798251881Speter
9799251881Speter      /* see if someone wants to cancel this operation. */
9800251881Speter      if (cancel_func)
9801251881Speter        err = cancel_func(cancel_baton);
9802251881Speter
9803251881Speter      if (!err && props && apr_hash_count(props) != 0)
9804251881Speter        {
9805251881Speter          const char *child_relpath;
9806251881Speter          const char *child_abspath;
9807251881Speter
9808251881Speter          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9809251881Speter          child_abspath = svn_dirent_join(wcroot->abspath,
9810251881Speter                                          child_relpath, iterpool);
9811251881Speter
9812251881Speter          err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9813251881Speter        }
9814251881Speter
9815251881Speter      err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9816251881Speter    }
9817251881Speter
9818251881Speter  err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9819251881Speter
9820251881Speter  svn_pool_destroy(iterpool);
9821251881Speter
9822251881Speter  SVN_ERR(svn_error_compose_create(
9823251881Speter                    err,
9824251881Speter                    svn_sqlite__exec_statements(wcroot->sdb,
9825251881Speter                                                STMT_DROP_TARGET_PROP_CACHE)));
9826251881Speter  return SVN_NO_ERROR;
9827251881Speter}
9828251881Speter
9829251881Speter
9830251881Speter/* Helper for svn_wc__db_read_props().
9831251881Speter */
9832251881Speterstatic svn_error_t *
9833251881Speterdb_read_props(apr_hash_t **props,
9834251881Speter              svn_wc__db_wcroot_t *wcroot,
9835251881Speter              const char *local_relpath,
9836251881Speter              apr_pool_t *result_pool,
9837251881Speter              apr_pool_t *scratch_pool)
9838251881Speter{
9839251881Speter  svn_sqlite__stmt_t *stmt;
9840251881Speter  svn_boolean_t have_row;
9841251881Speter  svn_error_t *err = NULL;
9842251881Speter
9843251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9844251881Speter                                    STMT_SELECT_ACTUAL_PROPS));
9845251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9846251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9847251881Speter
9848251881Speter  if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9849251881Speter    {
9850251881Speter      err = svn_sqlite__column_properties(props, stmt, 0,
9851251881Speter                                          result_pool, scratch_pool);
9852251881Speter    }
9853251881Speter  else
9854251881Speter    have_row = FALSE;
9855251881Speter
9856251881Speter  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9857251881Speter
9858251881Speter  if (have_row)
9859251881Speter    return SVN_NO_ERROR;
9860251881Speter
9861251881Speter  /* No local changes. Return the pristine props for this node.  */
9862251881Speter  SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9863251881Speter                                 result_pool, scratch_pool));
9864251881Speter  if (*props == NULL)
9865251881Speter    {
9866251881Speter      /* Pristine properties are not defined for this node.
9867251881Speter         ### we need to determine whether this node is in a state that
9868251881Speter         ### allows for ACTUAL properties (ie. not deleted). for now,
9869251881Speter         ### just say all nodes, no matter the state, have at least an
9870251881Speter         ### empty set of props.  */
9871251881Speter      *props = apr_hash_make(result_pool);
9872251881Speter    }
9873251881Speter
9874251881Speter  return SVN_NO_ERROR;
9875251881Speter}
9876251881Speter
9877251881Speter
9878251881Spetersvn_error_t *
9879251881Spetersvn_wc__db_read_props(apr_hash_t **props,
9880251881Speter                      svn_wc__db_t *db,
9881251881Speter                      const char *local_abspath,
9882251881Speter                      apr_pool_t *result_pool,
9883251881Speter                      apr_pool_t *scratch_pool)
9884251881Speter{
9885251881Speter  svn_wc__db_wcroot_t *wcroot;
9886251881Speter  const char *local_relpath;
9887251881Speter
9888251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9889251881Speter
9890251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9891251881Speter                              local_abspath, scratch_pool, scratch_pool));
9892251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9893251881Speter
9894251881Speter  SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9895251881Speter                                    result_pool, scratch_pool),
9896251881Speter                      wcroot);
9897251881Speter
9898251881Speter  return SVN_NO_ERROR;
9899251881Speter}
9900251881Speter
9901251881Speter
9902251881Speterstatic svn_error_t *
9903251881Speterdb_read_pristine_props(apr_hash_t **props,
9904251881Speter                       svn_wc__db_wcroot_t *wcroot,
9905251881Speter                       const char *local_relpath,
9906251881Speter                       svn_boolean_t deleted_ok,
9907251881Speter                       apr_pool_t *result_pool,
9908251881Speter                       apr_pool_t *scratch_pool)
9909251881Speter{
9910251881Speter  svn_sqlite__stmt_t *stmt;
9911251881Speter  svn_boolean_t have_row;
9912251881Speter  svn_wc__db_status_t presence;
9913251881Speter
9914251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9915251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9916251881Speter
9917251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9918251881Speter
9919251881Speter  if (!have_row)
9920251881Speter    {
9921251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9922251881Speter                               svn_sqlite__reset(stmt),
9923251881Speter                               _("The node '%s' was not found."),
9924251881Speter                               path_for_error_message(wcroot,
9925251881Speter                                                      local_relpath,
9926251881Speter                                                      scratch_pool));
9927251881Speter    }
9928251881Speter
9929251881Speter
9930251881Speter  /* Examine the presence: */
9931251881Speter  presence = svn_sqlite__column_token(stmt, 1, presence_map);
9932251881Speter
9933251881Speter  /* For "base-deleted", it is obvious the pristine props are located
9934251881Speter     below the current node. Fetch the NODE from the next record. */
9935251881Speter  if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9936251881Speter    {
9937251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9938251881Speter
9939251881Speter      SVN_ERR_ASSERT(have_row);
9940251881Speter
9941251881Speter      presence = svn_sqlite__column_token(stmt, 1, presence_map);
9942251881Speter    }
9943251881Speter
9944251881Speter  /* normal or copied: Fetch properties (during update we want
9945251881Speter     properties for incomplete as well) */
9946251881Speter  if (presence == svn_wc__db_status_normal
9947251881Speter      || presence == svn_wc__db_status_incomplete)
9948251881Speter    {
9949251881Speter      svn_error_t *err;
9950251881Speter
9951251881Speter      err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9952251881Speter                                          scratch_pool);
9953251881Speter      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9954251881Speter
9955251881Speter      if (!*props)
9956251881Speter        *props = apr_hash_make(result_pool);
9957251881Speter
9958251881Speter      return SVN_NO_ERROR;
9959251881Speter    }
9960251881Speter
9961251881Speter  return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9962251881Speter                           svn_sqlite__reset(stmt),
9963251881Speter                           _("The node '%s' has a status that"
9964251881Speter                             " has no properties."),
9965251881Speter                           path_for_error_message(wcroot,
9966251881Speter                                                  local_relpath,
9967251881Speter                                                  scratch_pool));
9968251881Speter}
9969251881Speter
9970251881Speter
9971251881Spetersvn_error_t *
9972251881Spetersvn_wc__db_read_pristine_props(apr_hash_t **props,
9973251881Speter                               svn_wc__db_t *db,
9974251881Speter                               const char *local_abspath,
9975251881Speter                               apr_pool_t *result_pool,
9976251881Speter                               apr_pool_t *scratch_pool)
9977251881Speter{
9978251881Speter  svn_wc__db_wcroot_t *wcroot;
9979251881Speter  const char *local_relpath;
9980251881Speter
9981251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9982251881Speter
9983251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9984251881Speter                              local_abspath, scratch_pool, scratch_pool));
9985251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9986251881Speter
9987251881Speter  SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
9988251881Speter                                 result_pool, scratch_pool));
9989251881Speter  return SVN_NO_ERROR;
9990251881Speter}
9991251881Speter
9992251881Spetersvn_error_t *
9993251881Spetersvn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
9994251881Speter                                   svn_wc__db_t *db,
9995251881Speter                                   const char *local_abspath,
9996251881Speter                                   const char *propname,
9997251881Speter                                   apr_pool_t *result_pool,
9998251881Speter                                   apr_pool_t *scratch_pool)
9999251881Speter{
10000251881Speter  svn_wc__db_wcroot_t *wcroot;
10001251881Speter  const char *local_relpath;
10002251881Speter  svn_sqlite__stmt_t *stmt;
10003251881Speter  svn_boolean_t have_row;
10004251881Speter  apr_pool_t *iterpool;
10005251881Speter
10006251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10007251881Speter
10008251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10009251881Speter                              local_abspath, scratch_pool, scratch_pool));
10010251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10011251881Speter
10012251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10013251881Speter                                    STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10014251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10015251881Speter
10016251881Speter  *values = apr_hash_make(result_pool);
10017251881Speter
10018251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10019251881Speter  iterpool = svn_pool_create(scratch_pool);
10020251881Speter  while (have_row)
10021251881Speter  {
10022251881Speter    apr_hash_t *node_props;
10023251881Speter    svn_string_t *value;
10024251881Speter
10025251881Speter    svn_pool_clear(iterpool);
10026251881Speter
10027251881Speter    SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10028251881Speter                                          iterpool, iterpool));
10029251881Speter
10030251881Speter    value = (node_props
10031251881Speter                ? svn_hash_gets(node_props, propname)
10032251881Speter                : NULL);
10033251881Speter
10034251881Speter    if (value)
10035251881Speter      {
10036251881Speter        svn_hash_sets(*values,
10037251881Speter                      svn_dirent_join(wcroot->abspath,
10038251881Speter                                      svn_sqlite__column_text(stmt, 1, NULL),
10039251881Speter                                      result_pool),
10040251881Speter                      svn_string_dup(value, result_pool));
10041251881Speter      }
10042251881Speter
10043251881Speter    SVN_ERR(svn_sqlite__step(&have_row, stmt));
10044251881Speter  }
10045251881Speter
10046251881Speter  svn_pool_destroy(iterpool);
10047251881Speter
10048251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
10049251881Speter}
10050251881Speter
10051251881Speter/* The body of svn_wc__db_read_cached_iprops(). */
10052251881Speterstatic svn_error_t *
10053251881Speterdb_read_cached_iprops(apr_array_header_t **iprops,
10054251881Speter                      svn_wc__db_wcroot_t *wcroot,
10055251881Speter                      const char *local_relpath,
10056251881Speter                      apr_pool_t *result_pool,
10057251881Speter                      apr_pool_t *scratch_pool)
10058251881Speter{
10059251881Speter  svn_sqlite__stmt_t *stmt;
10060251881Speter  svn_boolean_t have_row;
10061251881Speter
10062251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10063251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10064251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10065251881Speter
10066251881Speter  if (!have_row)
10067251881Speter    {
10068251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10069251881Speter                               svn_sqlite__reset(stmt),
10070251881Speter                               _("The node '%s' was not found."),
10071251881Speter                               path_for_error_message(wcroot, local_relpath,
10072251881Speter                                                      scratch_pool));
10073251881Speter    }
10074251881Speter
10075251881Speter  SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10076251881Speter                                    result_pool, scratch_pool));
10077251881Speter
10078251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10079251881Speter
10080251881Speter  return SVN_NO_ERROR;
10081251881Speter}
10082251881Speter
10083251881Spetersvn_error_t *
10084251881Spetersvn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10085251881Speter                              svn_wc__db_t *db,
10086251881Speter                              const char *local_abspath,
10087251881Speter                              apr_pool_t *result_pool,
10088251881Speter                              apr_pool_t *scratch_pool)
10089251881Speter{
10090251881Speter  svn_wc__db_wcroot_t *wcroot;
10091251881Speter  const char *local_relpath;
10092251881Speter
10093251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10094251881Speter
10095251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10096251881Speter                                                db, local_abspath,
10097251881Speter                                                scratch_pool, scratch_pool));
10098251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10099251881Speter
10100251881Speter  /* Don't use with_txn yet, as we perform just a single transaction */
10101251881Speter  SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10102251881Speter                                result_pool, scratch_pool));
10103251881Speter
10104251881Speter  if (!*iprops)
10105251881Speter    {
10106251881Speter      *iprops = apr_array_make(result_pool, 0,
10107251881Speter                               sizeof(svn_prop_inherited_item_t *));
10108251881Speter    }
10109251881Speter
10110251881Speter  return SVN_NO_ERROR;
10111251881Speter}
10112251881Speter
10113251881Speter/* Remove all prop name value pairs from PROP_HASH where the property
10114251881Speter   name is not PROPNAME. */
10115251881Speterstatic void
10116251881Speterfilter_unwanted_props(apr_hash_t *prop_hash,
10117251881Speter                      const char * propname,
10118251881Speter                      apr_pool_t *scratch_pool)
10119251881Speter{
10120251881Speter  apr_hash_index_t *hi;
10121251881Speter
10122251881Speter  for (hi = apr_hash_first(scratch_pool, prop_hash);
10123251881Speter       hi;
10124251881Speter       hi = apr_hash_next(hi))
10125251881Speter    {
10126251881Speter      const char *ipropname = svn__apr_hash_index_key(hi);
10127251881Speter
10128251881Speter      if (strcmp(ipropname, propname) != 0)
10129251881Speter        svn_hash_sets(prop_hash, ipropname, NULL);
10130251881Speter    }
10131251881Speter  return;
10132251881Speter}
10133251881Speter
10134251881Speter/* Get the changed properties as stored in the ACTUAL table */
10135251881Speterstatic svn_error_t *
10136251881Speterdb_get_changed_props(apr_hash_t **actual_props,
10137251881Speter                     svn_wc__db_wcroot_t *wcroot,
10138251881Speter                     const char *local_relpath,
10139251881Speter                     apr_pool_t *result_pool,
10140251881Speter                     apr_pool_t *scratch_pool)
10141251881Speter{
10142251881Speter  svn_sqlite__stmt_t *stmt;
10143251881Speter  svn_boolean_t have_row;
10144251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10145251881Speter                                STMT_SELECT_ACTUAL_PROPS));
10146251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10147251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10148251881Speter
10149251881Speter  if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10150251881Speter    SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10151251881Speter                                          result_pool, scratch_pool));
10152251881Speter  else
10153251881Speter    *actual_props = NULL; /* Cached when we read that record */
10154251881Speter
10155251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
10156251881Speter}
10157251881Speter
10158251881Speter/* The body of svn_wc__db_read_inherited_props().  */
10159251881Speterstatic svn_error_t *
10160251881Speterdb_read_inherited_props(apr_array_header_t **inherited_props,
10161251881Speter                        apr_hash_t **actual_props,
10162251881Speter                        svn_wc__db_wcroot_t *wcroot,
10163251881Speter                        const char *local_relpath,
10164251881Speter                        const char *propname,
10165251881Speter                        apr_pool_t *result_pool,
10166251881Speter                        apr_pool_t *scratch_pool)
10167251881Speter{
10168251881Speter  int i;
10169251881Speter  apr_array_header_t *cached_iprops = NULL;
10170251881Speter  apr_array_header_t *iprops;
10171251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10172251881Speter  svn_sqlite__stmt_t *stmt;
10173251881Speter  const char *relpath;
10174251881Speter  const char *expected_parent_repos_relpath = NULL;
10175251881Speter  const char *parent_relpath;
10176251881Speter
10177251881Speter  iprops = apr_array_make(result_pool, 1,
10178251881Speter                           sizeof(svn_prop_inherited_item_t *));
10179251881Speter  *inherited_props = iprops;
10180251881Speter
10181251881Speter  if (actual_props)
10182251881Speter    *actual_props = NULL;
10183251881Speter
10184251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10185251881Speter                                    STMT_SELECT_NODE_INFO));
10186251881Speter
10187251881Speter  relpath = local_relpath;
10188251881Speter
10189251881Speter  /* Walk up to the root of the WC looking for inherited properties.  When we
10190251881Speter     reach the WC root also check for cached inherited properties. */
10191251881Speter  for (relpath = local_relpath; relpath; relpath = parent_relpath)
10192251881Speter    {
10193251881Speter      svn_boolean_t have_row;
10194251881Speter      int op_depth;
10195251881Speter      svn_wc__db_status_t status;
10196251881Speter      apr_hash_t *node_props;
10197251881Speter
10198251881Speter      parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10199251881Speter                                  : NULL;
10200251881Speter
10201251881Speter      svn_pool_clear(iterpool);
10202251881Speter
10203251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10204251881Speter
10205251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10206251881Speter
10207251881Speter      if (!have_row)
10208251881Speter        return svn_error_createf(
10209251881Speter                    SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10210251881Speter                    _("The node '%s' was not found."),
10211251881Speter                    path_for_error_message(wcroot, relpath,
10212251881Speter                                           scratch_pool));
10213251881Speter
10214251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
10215251881Speter
10216251881Speter      status = svn_sqlite__column_token(stmt, 3, presence_map);
10217251881Speter
10218251881Speter      if (status != svn_wc__db_status_normal
10219251881Speter          && status != svn_wc__db_status_incomplete)
10220251881Speter        return svn_error_createf(
10221251881Speter                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10222251881Speter                    _("The node '%s' has a status that has no properties."),
10223251881Speter                    path_for_error_message(wcroot, relpath,
10224251881Speter                                           scratch_pool));
10225251881Speter
10226251881Speter      if (op_depth > 0)
10227251881Speter        {
10228251881Speter          /* WORKING node. Nothing to check */
10229251881Speter        }
10230251881Speter      else if (expected_parent_repos_relpath)
10231251881Speter        {
10232251881Speter          const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10233251881Speter
10234251881Speter          if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10235251881Speter            {
10236251881Speter              /* The child of this node has a different parent than this node
10237251881Speter                 (It is "switched"), so we can stop here. Note that switched
10238251881Speter                 with the same parent is not interesting for us here. */
10239251881Speter              SVN_ERR(svn_sqlite__reset(stmt));
10240251881Speter              break;
10241251881Speter            }
10242251881Speter
10243251881Speter          expected_parent_repos_relpath =
10244251881Speter              svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10245251881Speter        }
10246251881Speter      else
10247251881Speter        {
10248251881Speter          const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10249251881Speter
10250251881Speter          expected_parent_repos_relpath =
10251251881Speter              svn_relpath_dirname(repos_relpath, scratch_pool);
10252251881Speter        }
10253251881Speter
10254251881Speter      if (op_depth == 0
10255251881Speter          && !svn_sqlite__column_is_null(stmt, 16))
10256251881Speter        {
10257251881Speter          /* The node contains a cache. No reason to look further */
10258251881Speter          SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10259251881Speter                                            result_pool, iterpool));
10260251881Speter
10261251881Speter          parent_relpath = NULL; /* Stop after this */
10262251881Speter        }
10263251881Speter
10264251881Speter      SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10265251881Speter                                            iterpool, iterpool));
10266251881Speter
10267251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
10268251881Speter
10269251881Speter      /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10270251881Speter         can inherit properties from it. */
10271251881Speter      if (relpath != local_relpath)
10272251881Speter        {
10273251881Speter          apr_hash_t *changed_props;
10274251881Speter
10275251881Speter          SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10276251881Speter                                       result_pool, iterpool));
10277251881Speter
10278251881Speter          if (changed_props)
10279251881Speter            node_props = changed_props;
10280251881Speter          else if (node_props)
10281251881Speter            node_props = svn_prop_hash_dup(node_props, result_pool);
10282251881Speter
10283251881Speter          if (node_props && apr_hash_count(node_props))
10284251881Speter            {
10285251881Speter              /* If we only want PROPNAME filter out any other properties. */
10286251881Speter              if (propname)
10287251881Speter                filter_unwanted_props(node_props, propname, iterpool);
10288251881Speter
10289251881Speter              if (apr_hash_count(node_props))
10290251881Speter                {
10291251881Speter                  svn_prop_inherited_item_t *iprop_elt =
10292251881Speter                    apr_pcalloc(result_pool,
10293251881Speter                                sizeof(svn_prop_inherited_item_t));
10294251881Speter                  iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10295251881Speter                                                           relpath,
10296251881Speter                                                           result_pool);
10297251881Speter
10298251881Speter                  iprop_elt->prop_hash = node_props;
10299251881Speter                  /* Build the output array in depth-first order. */
10300251881Speter                  svn_sort__array_insert(&iprop_elt, iprops, 0);
10301251881Speter                }
10302251881Speter            }
10303251881Speter        }
10304251881Speter      else if (actual_props)
10305251881Speter        {
10306251881Speter          apr_hash_t *changed_props;
10307251881Speter
10308251881Speter          SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10309251881Speter                                       result_pool, iterpool));
10310251881Speter
10311251881Speter          if (changed_props)
10312251881Speter            *actual_props = changed_props;
10313251881Speter          else if (node_props)
10314251881Speter            *actual_props = svn_prop_hash_dup(node_props, result_pool);
10315251881Speter        }
10316251881Speter    }
10317251881Speter
10318251881Speter  if (cached_iprops)
10319251881Speter    {
10320251881Speter      for (i = cached_iprops->nelts - 1; i >= 0; i--)
10321251881Speter        {
10322251881Speter          svn_prop_inherited_item_t *cached_iprop =
10323251881Speter            APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10324251881Speter
10325251881Speter          /* An empty property hash in the iprops cache means there are no
10326251881Speter             inherited properties. */
10327251881Speter          if (apr_hash_count(cached_iprop->prop_hash) == 0)
10328251881Speter            continue;
10329251881Speter
10330251881Speter          if (propname)
10331251881Speter            filter_unwanted_props(cached_iprop->prop_hash, propname,
10332251881Speter                                  scratch_pool);
10333251881Speter
10334251881Speter          /* If we didn't filter everything then keep this iprop. */
10335251881Speter          if (apr_hash_count(cached_iprop->prop_hash))
10336251881Speter            svn_sort__array_insert(&cached_iprop, iprops, 0);
10337251881Speter        }
10338251881Speter    }
10339251881Speter
10340251881Speter  if (actual_props && !*actual_props)
10341251881Speter    *actual_props = apr_hash_make(result_pool);
10342251881Speter
10343251881Speter  svn_pool_destroy(iterpool);
10344251881Speter  return SVN_NO_ERROR;
10345251881Speter}
10346251881Speter
10347251881Spetersvn_error_t *
10348251881Spetersvn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10349251881Speter                                apr_hash_t **actual_props,
10350251881Speter                                svn_wc__db_t *db,
10351251881Speter                                const char *local_abspath,
10352251881Speter                                const char *propname,
10353251881Speter                                apr_pool_t *result_pool,
10354251881Speter                                apr_pool_t *scratch_pool)
10355251881Speter{
10356251881Speter  svn_wc__db_wcroot_t *wcroot;
10357251881Speter  const char *local_relpath;
10358251881Speter
10359251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10360251881Speter
10361251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10362251881Speter                                                db, local_abspath,
10363251881Speter                                                scratch_pool, scratch_pool));
10364251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10365251881Speter
10366251881Speter  SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10367251881Speter                                              wcroot, local_relpath, propname,
10368251881Speter                                              result_pool, scratch_pool),
10369251881Speter                      wcroot);
10370251881Speter
10371251881Speter  return SVN_NO_ERROR;
10372251881Speter}
10373251881Speter
10374251881Speter/* The body of svn_wc__db_get_children_with_cached_iprops().
10375251881Speter */
10376251881Speterstatic svn_error_t *
10377251881Speterget_children_with_cached_iprops(apr_hash_t **iprop_paths,
10378251881Speter                                svn_wc__db_wcroot_t *wcroot,
10379251881Speter                                const char *local_relpath,
10380251881Speter                                svn_depth_t depth,
10381251881Speter                                apr_pool_t *result_pool,
10382251881Speter                                apr_pool_t *scratch_pool)
10383251881Speter{
10384251881Speter  svn_sqlite__stmt_t *stmt;
10385251881Speter  svn_boolean_t have_row;
10386251881Speter
10387251881Speter  *iprop_paths = apr_hash_make(result_pool);
10388251881Speter
10389251881Speter  /* First check if LOCAL_RELPATH itself has iprops */
10390251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10391251881Speter                                    STMT_SELECT_IPROPS_NODE));
10392251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10393251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10394251881Speter
10395251881Speter  if (have_row)
10396251881Speter   {
10397251881Speter      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10398251881Speter                                                               NULL);
10399251881Speter      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10400251881Speter                                                       relpath_with_cache,
10401251881Speter                                                       result_pool);
10402251881Speter      svn_hash_sets(*iprop_paths, abspath_with_cache,
10403251881Speter                    svn_sqlite__column_text(stmt, 1, result_pool));
10404251881Speter    }
10405251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10406251881Speter
10407251881Speter  if (depth == svn_depth_empty)
10408251881Speter    return SVN_NO_ERROR;
10409251881Speter
10410251881Speter  /* Now fetch information for children or all descendants */
10411251881Speter  if (depth == svn_depth_files
10412251881Speter      || depth == svn_depth_immediates)
10413251881Speter    {
10414251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10415251881Speter                                        STMT_SELECT_IPROPS_CHILDREN));
10416251881Speter    }
10417251881Speter  else /* Default to svn_depth_infinity. */
10418251881Speter    {
10419251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10420251881Speter                                        STMT_SELECT_IPROPS_RECURSIVE));
10421251881Speter    }
10422251881Speter
10423251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10424251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10425251881Speter
10426251881Speter  while (have_row)
10427251881Speter    {
10428251881Speter      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10429251881Speter                                                               NULL);
10430251881Speter      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10431251881Speter                                                       relpath_with_cache,
10432251881Speter                                                       result_pool);
10433251881Speter      svn_hash_sets(*iprop_paths, abspath_with_cache,
10434251881Speter                    svn_sqlite__column_text(stmt, 1, result_pool));
10435251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10436251881Speter    }
10437251881Speter
10438251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10439251881Speter
10440251881Speter  /* For depth files we should filter non files */
10441251881Speter  if (depth == svn_depth_files)
10442251881Speter    {
10443251881Speter      apr_hash_index_t *hi;
10444251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10445251881Speter
10446251881Speter      for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10447251881Speter           hi;
10448251881Speter           hi = apr_hash_next(hi))
10449251881Speter        {
10450251881Speter          const char *child_abspath = svn__apr_hash_index_key(hi);
10451251881Speter          const char *child_relpath;
10452251881Speter          svn_node_kind_t child_kind;
10453251881Speter
10454251881Speter          svn_pool_clear(iterpool);
10455251881Speter
10456251881Speter          child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10457251881Speter                                              NULL);
10458251881Speter
10459251881Speter          if (! child_relpath)
10460251881Speter            {
10461251881Speter              continue; /* local_relpath itself */
10462251881Speter            }
10463251881Speter
10464251881Speter          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10465251881Speter                                                    NULL, NULL, NULL, NULL,
10466251881Speter                                                    NULL, NULL, NULL, NULL,
10467251881Speter                                                    NULL, NULL, NULL, NULL,
10468251881Speter                                                    wcroot, child_relpath,
10469251881Speter                                                    scratch_pool,
10470251881Speter                                                    scratch_pool));
10471251881Speter
10472251881Speter          /* Filter if not a file */
10473251881Speter          if (child_kind != svn_node_file)
10474251881Speter            {
10475251881Speter              svn_hash_sets(*iprop_paths, child_abspath, NULL);
10476251881Speter            }
10477251881Speter        }
10478251881Speter
10479251881Speter      svn_pool_destroy(iterpool);
10480251881Speter    }
10481251881Speter
10482251881Speter  return SVN_NO_ERROR;
10483251881Speter}
10484251881Speter
10485251881Spetersvn_error_t *
10486251881Spetersvn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10487251881Speter                                           svn_depth_t depth,
10488251881Speter                                           const char *local_abspath,
10489251881Speter                                           svn_wc__db_t *db,
10490251881Speter                                           apr_pool_t *result_pool,
10491251881Speter                                           apr_pool_t *scratch_pool)
10492251881Speter{
10493251881Speter  svn_wc__db_wcroot_t *wcroot;
10494251881Speter  const char *local_relpath;
10495251881Speter
10496251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10497251881Speter
10498251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10499251881Speter                                                local_abspath, scratch_pool,
10500251881Speter                                                scratch_pool));
10501251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10502251881Speter
10503251881Speter  SVN_WC__DB_WITH_TXN(
10504251881Speter    get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10505251881Speter                                    depth, result_pool, scratch_pool),
10506251881Speter    wcroot);
10507251881Speter
10508251881Speter  return SVN_NO_ERROR;
10509251881Speter}
10510251881Speter
10511251881Spetersvn_error_t *
10512251881Spetersvn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10513251881Speter                                         svn_wc__db_t *db,
10514251881Speter                                         const char *local_abspath,
10515251881Speter                                         apr_pool_t *result_pool,
10516251881Speter                                         apr_pool_t *scratch_pool)
10517251881Speter{
10518251881Speter  svn_wc__db_wcroot_t *wcroot;
10519251881Speter  const char *local_relpath;
10520251881Speter
10521251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10522251881Speter
10523251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10524251881Speter                                             local_abspath,
10525251881Speter                                             scratch_pool, scratch_pool));
10526251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10527251881Speter
10528251881Speter  return gather_children2(children, wcroot, local_relpath,
10529251881Speter                          result_pool, scratch_pool);
10530251881Speter}
10531251881Speter
10532251881Speter/* Helper for svn_wc__db_node_check_replace().
10533251881Speter */
10534251881Speterstatic svn_error_t *
10535251881Spetercheck_replace_txn(svn_boolean_t *is_replace_root_p,
10536251881Speter                  svn_boolean_t *base_replace_p,
10537251881Speter                  svn_boolean_t *is_replace_p,
10538251881Speter                  svn_wc__db_wcroot_t *wcroot,
10539251881Speter                  const char *local_relpath,
10540251881Speter                  apr_pool_t *scratch_pool)
10541251881Speter{
10542251881Speter  svn_sqlite__stmt_t *stmt;
10543251881Speter  svn_boolean_t have_row;
10544251881Speter  svn_boolean_t is_replace = FALSE;
10545251881Speter  int replaced_op_depth;
10546251881Speter  svn_wc__db_status_t replaced_status;
10547251881Speter
10548251881Speter  /* Our caller initialized the output values to FALSE */
10549251881Speter
10550251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10551251881Speter                                    STMT_SELECT_NODE_INFO));
10552251881Speter
10553251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10554251881Speter
10555251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10556251881Speter
10557251881Speter  if (!have_row)
10558251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10559251881Speter                             svn_sqlite__reset(stmt),
10560251881Speter                             _("The node '%s' was not found."),
10561251881Speter                             path_for_error_message(wcroot, local_relpath,
10562251881Speter                                                    scratch_pool));
10563251881Speter
10564251881Speter  {
10565251881Speter    svn_wc__db_status_t status;
10566251881Speter
10567251881Speter    status = svn_sqlite__column_token(stmt, 3, presence_map);
10568251881Speter
10569251881Speter    if (status != svn_wc__db_status_normal)
10570251881Speter      return svn_error_trace(svn_sqlite__reset(stmt));
10571251881Speter  }
10572251881Speter
10573251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10574251881Speter
10575251881Speter  if (!have_row)
10576251881Speter    return svn_error_trace(svn_sqlite__reset(stmt));
10577251881Speter
10578251881Speter  replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10579251881Speter
10580251881Speter  /* If the layer below the add describes a not present or a deleted node,
10581251881Speter     this is not a replacement. Deleted can only occur if an ancestor is
10582251881Speter     the delete root. */
10583251881Speter  if (replaced_status != svn_wc__db_status_not_present
10584251881Speter      && replaced_status != svn_wc__db_status_excluded
10585251881Speter      && replaced_status != svn_wc__db_status_server_excluded
10586251881Speter      && replaced_status != svn_wc__db_status_base_deleted)
10587251881Speter    {
10588251881Speter      is_replace = TRUE;
10589251881Speter      if (is_replace_p)
10590251881Speter        *is_replace_p = TRUE;
10591251881Speter    }
10592251881Speter
10593251881Speter  replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10594251881Speter
10595251881Speter  if (base_replace_p)
10596251881Speter    {
10597251881Speter      int op_depth = svn_sqlite__column_int(stmt, 0);
10598251881Speter
10599251881Speter      while (op_depth != 0 && have_row)
10600251881Speter        {
10601251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
10602251881Speter
10603251881Speter          if (have_row)
10604251881Speter            op_depth = svn_sqlite__column_int(stmt, 0);
10605251881Speter        }
10606251881Speter
10607251881Speter      if (have_row && op_depth == 0)
10608251881Speter        {
10609251881Speter          svn_wc__db_status_t base_status;
10610251881Speter
10611251881Speter          base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10612251881Speter
10613251881Speter          *base_replace_p = (base_status != svn_wc__db_status_not_present);
10614251881Speter        }
10615251881Speter    }
10616251881Speter
10617251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10618251881Speter
10619251881Speter  if (!is_replace_root_p || !is_replace)
10620251881Speter    return SVN_NO_ERROR;
10621251881Speter
10622251881Speter  if (replaced_status != svn_wc__db_status_base_deleted)
10623251881Speter    {
10624251881Speter      int parent_op_depth;
10625251881Speter
10626251881Speter      /* Check the current op-depth of the parent to see if we are a replacement
10627251881Speter         root */
10628251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10629251881Speter                                svn_relpath_dirname(local_relpath,
10630251881Speter                                                    scratch_pool)));
10631251881Speter
10632251881Speter      SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10633251881Speter
10634251881Speter      parent_op_depth = svn_sqlite__column_int(stmt, 0);
10635251881Speter
10636251881Speter      if (parent_op_depth >= replaced_op_depth)
10637251881Speter        {
10638251881Speter          /* Did we replace inside our directory? */
10639251881Speter
10640251881Speter          *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10641251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
10642251881Speter          return SVN_NO_ERROR;
10643251881Speter        }
10644251881Speter
10645251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10646251881Speter
10647251881Speter      if (have_row)
10648251881Speter        parent_op_depth = svn_sqlite__column_int(stmt, 0);
10649251881Speter
10650251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
10651251881Speter
10652251881Speter      if (!have_row)
10653251881Speter        *is_replace_root_p = TRUE; /* Parent is no replacement */
10654251881Speter      else if (parent_op_depth < replaced_op_depth)
10655251881Speter        *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10656251881Speter      /*else // No replacement root */
10657251881Speter  }
10658251881Speter
10659251881Speter  return SVN_NO_ERROR;
10660251881Speter}
10661251881Speter
10662251881Spetersvn_error_t *
10663251881Spetersvn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10664251881Speter                              svn_boolean_t *base_replace,
10665251881Speter                              svn_boolean_t *is_replace,
10666251881Speter                              svn_wc__db_t *db,
10667251881Speter                              const char *local_abspath,
10668251881Speter                              apr_pool_t *scratch_pool)
10669251881Speter{
10670251881Speter  svn_wc__db_wcroot_t *wcroot;
10671251881Speter  const char *local_relpath;
10672251881Speter
10673251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10674251881Speter
10675251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10676251881Speter                                             local_abspath,
10677251881Speter                                             scratch_pool, scratch_pool));
10678251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10679251881Speter
10680251881Speter  if (is_replace_root)
10681251881Speter    *is_replace_root = FALSE;
10682251881Speter  if (base_replace)
10683251881Speter    *base_replace = FALSE;
10684251881Speter  if (is_replace)
10685251881Speter    *is_replace = FALSE;
10686251881Speter
10687251881Speter  if (local_relpath[0] == '\0')
10688251881Speter    return SVN_NO_ERROR; /* Working copy root can't be replaced */
10689251881Speter
10690251881Speter  SVN_WC__DB_WITH_TXN(
10691251881Speter    check_replace_txn(is_replace_root, base_replace, is_replace,
10692251881Speter                      wcroot, local_relpath, scratch_pool),
10693251881Speter    wcroot);
10694251881Speter
10695251881Speter  return SVN_NO_ERROR;
10696251881Speter}
10697251881Speter
10698251881Spetersvn_error_t *
10699251881Spetersvn_wc__db_read_children(const apr_array_header_t **children,
10700251881Speter                         svn_wc__db_t *db,
10701251881Speter                         const char *local_abspath,
10702251881Speter                         apr_pool_t *result_pool,
10703251881Speter                         apr_pool_t *scratch_pool)
10704251881Speter{
10705251881Speter  svn_wc__db_wcroot_t *wcroot;
10706251881Speter  const char *local_relpath;
10707251881Speter
10708251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10709251881Speter
10710251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10711251881Speter                                             local_abspath,
10712251881Speter                                             scratch_pool, scratch_pool));
10713251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10714251881Speter
10715251881Speter  return gather_children(children, wcroot, local_relpath,
10716251881Speter                         result_pool, scratch_pool);
10717251881Speter}
10718251881Speter
10719251881Speter
10720251881Speter/* */
10721251881Speterstatic svn_error_t *
10722251881Speterrelocate_txn(svn_wc__db_wcroot_t *wcroot,
10723251881Speter             const char *local_relpath,
10724251881Speter             const char *repos_root_url,
10725251881Speter             const char *repos_uuid,
10726251881Speter             svn_boolean_t have_base_node,
10727251881Speter             apr_int64_t old_repos_id,
10728251881Speter             apr_pool_t *scratch_pool)
10729251881Speter{
10730251881Speter  svn_sqlite__stmt_t *stmt;
10731251881Speter  apr_int64_t new_repos_id;
10732251881Speter
10733251881Speter  /* This function affects all the children of the given local_relpath,
10734251881Speter     but the way that it does this is through the repos inheritance mechanism.
10735251881Speter     So, we only need to rewrite the repos_id of the given local_relpath,
10736251881Speter     as well as any children with a non-null repos_id, as well as various
10737251881Speter     repos_id fields in the locks and working_node tables.
10738251881Speter   */
10739251881Speter
10740251881Speter  /* Get the repos_id for the new repository. */
10741251881Speter  SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10742251881Speter                          wcroot->sdb, scratch_pool));
10743251881Speter
10744251881Speter  /* Set the (base and working) repos_ids and clear the dav_caches */
10745251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10746251881Speter                                    STMT_RECURSIVE_UPDATE_NODE_REPO));
10747251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10748251881Speter                            old_repos_id, new_repos_id));
10749251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
10750251881Speter
10751251881Speter  if (have_base_node)
10752251881Speter    {
10753251881Speter      /* Update any locks for the root or its children. */
10754251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10755251881Speter                                        STMT_UPDATE_LOCK_REPOS_ID));
10756251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10757251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
10758251881Speter    }
10759251881Speter
10760251881Speter  return SVN_NO_ERROR;
10761251881Speter}
10762251881Speter
10763251881Speter
10764251881Spetersvn_error_t *
10765251881Spetersvn_wc__db_global_relocate(svn_wc__db_t *db,
10766251881Speter                           const char *local_dir_abspath,
10767251881Speter                           const char *repos_root_url,
10768251881Speter                           apr_pool_t *scratch_pool)
10769251881Speter{
10770251881Speter  svn_wc__db_wcroot_t *wcroot;
10771251881Speter  const char *local_relpath;
10772251881Speter  const char *local_dir_relpath;
10773251881Speter  svn_wc__db_status_t status;
10774251881Speter  const char *repos_uuid;
10775251881Speter  svn_boolean_t have_base_node;
10776251881Speter  apr_int64_t old_repos_id;
10777251881Speter
10778251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10779251881Speter  /* ### assert that we were passed a directory?  */
10780251881Speter
10781251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10782251881Speter                           db, local_dir_abspath, scratch_pool, scratch_pool));
10783251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10784251881Speter  local_relpath = local_dir_relpath;
10785251881Speter
10786251881Speter  SVN_ERR(read_info(&status,
10787251881Speter                    NULL, NULL, NULL, &old_repos_id,
10788251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10789251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10790251881Speter                    NULL,
10791251881Speter                    &have_base_node, NULL, NULL,
10792251881Speter                    wcroot, local_relpath,
10793251881Speter                    scratch_pool, scratch_pool));
10794251881Speter
10795251881Speter  if (status == svn_wc__db_status_excluded)
10796251881Speter    {
10797251881Speter      /* The parent cannot be excluded, so look at the parent and then
10798251881Speter         adjust the relpath */
10799251881Speter      const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10800251881Speter                                                       scratch_pool);
10801251881Speter      SVN_ERR(read_info(&status,
10802251881Speter                        NULL, NULL, NULL, &old_repos_id,
10803251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10804251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10805251881Speter                        NULL, NULL, NULL,
10806251881Speter                        NULL, NULL, NULL,
10807251881Speter                        wcroot, parent_relpath,
10808251881Speter                        scratch_pool, scratch_pool));
10809251881Speter      local_dir_relpath = parent_relpath;
10810251881Speter    }
10811251881Speter
10812251881Speter  if (old_repos_id == INVALID_REPOS_ID)
10813251881Speter    {
10814251881Speter      /* Do we need to support relocating something that is
10815251881Speter         added/deleted/excluded without relocating the parent?  If not
10816251881Speter         then perhaps relpath, root_url and uuid should be passed down
10817251881Speter         to the children so that they don't have to scan? */
10818251881Speter
10819251881Speter      if (status == svn_wc__db_status_deleted)
10820251881Speter        {
10821251881Speter          const char *work_del_relpath;
10822251881Speter
10823251881Speter          SVN_ERR(scan_deletion_txn(NULL, NULL,
10824251881Speter                                    &work_del_relpath, NULL,
10825251881Speter                                    wcroot, local_dir_relpath,
10826251881Speter                                    scratch_pool,
10827251881Speter                                    scratch_pool));
10828251881Speter          if (work_del_relpath)
10829251881Speter            {
10830251881Speter              /* Deleted within a copy/move */
10831251881Speter
10832251881Speter              /* The parent of the delete is added. */
10833251881Speter              status = svn_wc__db_status_added;
10834251881Speter              local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10835251881Speter                                                      scratch_pool);
10836251881Speter            }
10837251881Speter        }
10838251881Speter
10839251881Speter      if (status == svn_wc__db_status_added)
10840251881Speter        {
10841251881Speter          SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10842251881Speter                                NULL, NULL, NULL, NULL, NULL, NULL,
10843251881Speter                                wcroot, local_dir_relpath,
10844251881Speter                                scratch_pool, scratch_pool));
10845251881Speter        }
10846251881Speter      else
10847251881Speter        SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10848251881Speter                                                  &old_repos_id,
10849251881Speter                                                  NULL, NULL, NULL, NULL, NULL,
10850251881Speter                                                  NULL, NULL, NULL, NULL, NULL,
10851251881Speter                                                  wcroot, local_dir_relpath,
10852251881Speter                                                  scratch_pool, scratch_pool));
10853251881Speter    }
10854251881Speter
10855251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10856251881Speter                                      old_repos_id, scratch_pool));
10857251881Speter  SVN_ERR_ASSERT(repos_uuid);
10858251881Speter
10859251881Speter  SVN_WC__DB_WITH_TXN(
10860251881Speter    relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10861251881Speter                 have_base_node, old_repos_id, scratch_pool),
10862251881Speter    wcroot);
10863251881Speter
10864251881Speter  return SVN_NO_ERROR;
10865251881Speter}
10866251881Speter
10867251881Speter
10868251881Speter/* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10869251881Speter   (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10870251881Speter   its parent's BASE row if not. In the latter case, error if the parent
10871251881Speter   BASE row does not exist.  */
10872251881Speterstatic svn_error_t *
10873251881Speterdetermine_repos_info(apr_int64_t *repos_id,
10874251881Speter                     const char **repos_relpath,
10875251881Speter                     svn_wc__db_wcroot_t *wcroot,
10876251881Speter                     const char *local_relpath,
10877251881Speter                     apr_pool_t *result_pool,
10878251881Speter                     apr_pool_t *scratch_pool)
10879251881Speter{
10880251881Speter  svn_sqlite__stmt_t *stmt;
10881251881Speter  svn_boolean_t have_row;
10882251881Speter  const char *repos_parent_relpath;
10883251881Speter  const char *local_parent_relpath, *name;
10884251881Speter
10885251881Speter  /* ### is it faster to fetch fewer columns? */
10886251881Speter
10887251881Speter  /* Prefer the current node's repository information.  */
10888251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10889251881Speter                                    STMT_SELECT_BASE_NODE));
10890251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10891251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10892251881Speter
10893251881Speter  if (have_row)
10894251881Speter    {
10895251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
10896251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10897251881Speter
10898251881Speter      *repos_id = svn_sqlite__column_int64(stmt, 0);
10899251881Speter      *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
10900251881Speter
10901251881Speter      return svn_error_trace(svn_sqlite__reset(stmt));
10902251881Speter    }
10903251881Speter
10904251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10905251881Speter
10906251881Speter  /* This was a child node within this wcroot. We want to look at the
10907251881Speter     BASE node of the directory.  */
10908251881Speter  svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
10909251881Speter
10910251881Speter  /* The REPOS_ID will be the same (### until we support mixed-repos)  */
10911251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
10912251881Speter                                            &repos_parent_relpath, repos_id,
10913251881Speter                                            NULL, NULL, NULL, NULL, NULL,
10914251881Speter                                            NULL, NULL, NULL, NULL, NULL,
10915251881Speter                                            wcroot, local_parent_relpath,
10916251881Speter                                            scratch_pool, scratch_pool));
10917251881Speter
10918251881Speter  *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
10919251881Speter
10920251881Speter  return SVN_NO_ERROR;
10921251881Speter}
10922251881Speter
10923251881Speter/* Helper for svn_wc__db_global_commit()
10924251881Speter
10925251881Speter   Makes local_relpath and all its descendants at the same op-depth represent
10926251881Speter   the copy origin repos_id:repos_relpath@revision.
10927251881Speter
10928251881Speter   This code is only valid to fix-up a move from an old location, to a new
10929251881Speter   location during a commit.
10930251881Speter
10931251881Speter   Assumptions:
10932251881Speter     * local_relpath is not the working copy root (can't be moved)
10933251881Speter     * repos_relpath is not the repository root (can't be moved)
10934251881Speter   */
10935251881Speterstatic svn_error_t *
10936251881Spetermoved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10937251881Speter                        const char *local_relpath,
10938251881Speter                        int op_depth,
10939251881Speter                        apr_int64_t repos_id,
10940251881Speter                        const char *repos_relpath,
10941251881Speter                        svn_revnum_t revision,
10942251881Speter                        apr_pool_t *scratch_pool)
10943251881Speter{
10944251881Speter  apr_hash_t *children;
10945251881Speter  apr_pool_t *iterpool;
10946251881Speter  svn_sqlite__stmt_t *stmt;
10947251881Speter  svn_boolean_t have_row;
10948251881Speter  apr_hash_index_t *hi;
10949251881Speter
10950251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0'
10951251881Speter                 && *repos_relpath != '\0');
10952251881Speter
10953251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10954251881Speter                                    STMT_SELECT_MOVED_DESCENDANTS));
10955251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
10956251881Speter                                         local_relpath,
10957251881Speter                                         op_depth));
10958251881Speter
10959251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10960251881Speter  if (! have_row)
10961251881Speter    return svn_error_trace(svn_sqlite__reset(stmt));
10962251881Speter
10963251881Speter  children = apr_hash_make(scratch_pool);
10964251881Speter
10965251881Speter  /* First, obtain all moved children */
10966251881Speter  /* To keep error handling simple, first cache them in a hashtable */
10967251881Speter  while (have_row)
10968251881Speter    {
10969251881Speter      const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
10970251881Speter      const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
10971251881Speter
10972251881Speter      svn_hash_sets(children, src_relpath, to_relpath);
10973251881Speter
10974251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10975251881Speter    }
10976251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10977251881Speter
10978251881Speter  /* Then update them */
10979251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10980251881Speter                                    STMT_COMMIT_UPDATE_ORIGIN));
10981251881Speter
10982251881Speter  iterpool = svn_pool_create(scratch_pool);
10983251881Speter  for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
10984251881Speter    {
10985251881Speter      const char *src_relpath = svn__apr_hash_index_key(hi);
10986251881Speter      const char *to_relpath = svn__apr_hash_index_val(hi);
10987251881Speter      const char *new_repos_relpath;
10988251881Speter      int to_op_depth = relpath_depth(to_relpath);
10989251881Speter      int affected;
10990251881Speter
10991251881Speter      svn_pool_clear(iterpool);
10992251881Speter
10993251881Speter      SVN_ERR_ASSERT(to_op_depth > 0);
10994251881Speter
10995251881Speter      new_repos_relpath = svn_relpath_join(
10996251881Speter                            repos_relpath,
10997251881Speter                            svn_relpath_skip_ancestor(local_relpath,
10998251881Speter                                                      src_relpath),
10999251881Speter                            iterpool);
11000251881Speter
11001251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11002251881Speter                                                to_relpath,
11003251881Speter                                                to_op_depth,
11004251881Speter                                                repos_id,
11005251881Speter                                                new_repos_relpath,
11006251881Speter                                                revision));
11007251881Speter      SVN_ERR(svn_sqlite__update(&affected, stmt));
11008251881Speter
11009251881Speter#ifdef SVN_DEBUG
11010251881Speter      /* Enable in release code?
11011251881Speter         Broken moves are not fatal yet, but this assertion would break
11012251881Speter         committing them */
11013251881Speter      SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11014251881Speter#endif
11015251881Speter
11016251881Speter      SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
11017251881Speter                                      repos_id, new_repos_relpath, revision,
11018251881Speter                                      iterpool));
11019251881Speter    }
11020251881Speter
11021251881Speter  svn_pool_destroy(iterpool);
11022251881Speter  return SVN_NO_ERROR;
11023251881Speter}
11024251881Speter
11025251881Speter/* Helper for svn_wc__db_global_commit()
11026251881Speter
11027251881Speter   Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11028251881Speter   (BASE), setting their presence to 'not-present' if their presence wasn't
11029251881Speter   'normal'.
11030251881Speter
11031251881Speter   Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11032251881Speter   location repos_id:repos_relpath@revision.
11033251881Speter
11034251881Speter   Assumptions:
11035251881Speter     * local_relpath is not the working copy root (can't be replaced)
11036251881Speter     * repos_relpath is not the repository root (can't be replaced)
11037251881Speter   */
11038251881Speterstatic svn_error_t *
11039251881Speterdescendant_commit(svn_wc__db_wcroot_t *wcroot,
11040251881Speter                  const char *local_relpath,
11041251881Speter                  int op_depth,
11042251881Speter                  apr_int64_t repos_id,
11043251881Speter                  const char *repos_relpath,
11044251881Speter                  svn_revnum_t revision,
11045251881Speter                  apr_pool_t *scratch_pool)
11046251881Speter{
11047251881Speter  svn_sqlite__stmt_t *stmt;
11048251881Speter
11049251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0'
11050251881Speter                 && *repos_relpath != '\0');
11051251881Speter
11052251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11053251881Speter                                    STMT_COMMIT_DESCENDANTS_TO_BASE));
11054251881Speter
11055251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11056251881Speter                                            local_relpath,
11057251881Speter                                            op_depth,
11058251881Speter                                            repos_id,
11059251881Speter                                            repos_relpath,
11060251881Speter                                            revision));
11061251881Speter
11062251881Speter  SVN_ERR(svn_sqlite__update(NULL, stmt));
11063251881Speter
11064251881Speter  return SVN_NO_ERROR;
11065251881Speter}
11066251881Speter
11067251881Speter/* The body of svn_wc__db_global_commit().
11068251881Speter */
11069251881Speterstatic svn_error_t *
11070251881Spetercommit_node(svn_wc__db_wcroot_t *wcroot,
11071251881Speter            const char *local_relpath,
11072251881Speter            svn_revnum_t new_revision,
11073251881Speter            svn_revnum_t changed_rev,
11074251881Speter            apr_time_t changed_date,
11075251881Speter            const char *changed_author,
11076251881Speter            const svn_checksum_t *new_checksum,
11077251881Speter            const apr_array_header_t *new_children,
11078251881Speter            apr_hash_t *new_dav_cache,
11079251881Speter            svn_boolean_t keep_changelist,
11080251881Speter            svn_boolean_t no_unlock,
11081251881Speter            const svn_skel_t *work_items,
11082251881Speter            apr_pool_t *scratch_pool)
11083251881Speter{
11084251881Speter  svn_sqlite__stmt_t *stmt_info;
11085251881Speter  svn_sqlite__stmt_t *stmt_act;
11086251881Speter  svn_boolean_t have_act;
11087251881Speter  svn_string_t prop_blob = { 0 };
11088251881Speter  svn_string_t inherited_prop_blob = { 0 };
11089251881Speter  const char *changelist = NULL;
11090251881Speter  const char *parent_relpath;
11091251881Speter  svn_wc__db_status_t new_presence;
11092251881Speter  svn_node_kind_t new_kind;
11093251881Speter  const char *new_depth_str = NULL;
11094251881Speter  svn_sqlite__stmt_t *stmt;
11095251881Speter  apr_int64_t repos_id;
11096251881Speter  const char *repos_relpath;
11097251881Speter  int op_depth;
11098251881Speter  svn_wc__db_status_t old_presence;
11099251881Speter
11100251881Speter    /* If we are adding a file or directory, then we need to get
11101251881Speter     repository information from the parent node since "this node" does
11102251881Speter     not have a BASE).
11103251881Speter
11104251881Speter     For existing nodes, we should retain the (potentially-switched)
11105251881Speter     repository information.  */
11106251881Speter  SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
11107251881Speter                               wcroot, local_relpath,
11108251881Speter                               scratch_pool, scratch_pool));
11109251881Speter
11110251881Speter  /* ### is it better to select only the data needed?  */
11111251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11112251881Speter                                    STMT_SELECT_NODE_INFO));
11113251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11114251881Speter  SVN_ERR(svn_sqlite__step_row(stmt_info));
11115251881Speter
11116251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11117251881Speter                                    STMT_SELECT_ACTUAL_NODE));
11118251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11119251881Speter                            wcroot->wc_id, local_relpath));
11120251881Speter  SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11121251881Speter
11122251881Speter  /* There should be something to commit!  */
11123251881Speter
11124251881Speter  op_depth = svn_sqlite__column_int(stmt_info, 0);
11125251881Speter
11126251881Speter  /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11127251881Speter     or there will be a BASE_NODE that has it.  */
11128251881Speter  new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11129251881Speter
11130251881Speter  /* What will the new depth be?  */
11131251881Speter  if (new_kind == svn_node_dir)
11132251881Speter    new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11133251881Speter
11134251881Speter  /* Check that the repository information is not being changed.  */
11135251881Speter  if (op_depth == 0)
11136251881Speter    {
11137251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11138251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11139251881Speter
11140251881Speter      /* A commit cannot change these values.  */
11141251881Speter      SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11142251881Speter      SVN_ERR_ASSERT(strcmp(repos_relpath,
11143251881Speter                            svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11144251881Speter    }
11145251881Speter
11146251881Speter  /* Find the appropriate new properties -- ACTUAL overrides any properties
11147251881Speter     in WORKING that arrived as part of a copy/move.
11148251881Speter
11149251881Speter     Note: we'll keep them as a big blob of data, rather than
11150251881Speter     deserialize/serialize them.  */
11151251881Speter  if (have_act)
11152251881Speter    prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11153251881Speter                                             scratch_pool);
11154251881Speter  if (prop_blob.data == NULL)
11155251881Speter    prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11156251881Speter                                             scratch_pool);
11157251881Speter
11158251881Speter  inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
11159251881Speter                                                     &inherited_prop_blob.len,
11160251881Speter                                                     scratch_pool);
11161251881Speter
11162251881Speter  if (keep_changelist && have_act)
11163251881Speter    changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11164251881Speter
11165251881Speter  old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11166251881Speter
11167251881Speter  /* ### other stuff?  */
11168251881Speter
11169251881Speter  SVN_ERR(svn_sqlite__reset(stmt_info));
11170251881Speter  SVN_ERR(svn_sqlite__reset(stmt_act));
11171251881Speter
11172251881Speter  if (op_depth > 0)
11173251881Speter    {
11174251881Speter      int affected_rows;
11175251881Speter
11176251881Speter      /* This removes all layers of this node and at the same time determines
11177251881Speter         if we need to remove shadowed layers below our descendants. */
11178251881Speter
11179251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11180269847Speter                                        STMT_DELETE_NODE_ALL_LAYERS));
11181251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11182251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11183251881Speter
11184251881Speter      if (affected_rows > 1)
11185251881Speter        {
11186251881Speter          /* We commit a shadowing operation
11187251881Speter
11188251881Speter           1) Remove all shadowed nodes
11189251881Speter           2) And remove all nodes that have a base-deleted as lowest layer,
11190251881Speter              because 1) removed that layer */
11191251881Speter
11192251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11193251881Speter                                            STMT_DELETE_SHADOWED_RECURSIVE));
11194251881Speter
11195251881Speter          SVN_ERR(svn_sqlite__bindf(stmt,
11196251881Speter                                    "isd",
11197251881Speter                                    wcroot->wc_id,
11198251881Speter                                    local_relpath,
11199251881Speter                                    op_depth));
11200251881Speter
11201251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
11202251881Speter        }
11203251881Speter
11204251881Speter      /* Note that while these two calls look so similar that they might
11205251881Speter         be integrated, they really affect a different op-depth and
11206251881Speter         completely different nodes (via a different recursion pattern). */
11207251881Speter
11208251881Speter      /* Collapse descendants of the current op_depth in layer 0 */
11209251881Speter      SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11210251881Speter                                repos_id, repos_relpath, new_revision,
11211251881Speter                                scratch_pool));
11212251881Speter
11213251881Speter      /* And make the recorded local moves represent moves of the node we just
11214251881Speter         committed. */
11215251881Speter      SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
11216251881Speter                                      repos_id, repos_relpath, new_revision,
11217251881Speter                                      scratch_pool));
11218251881Speter
11219251881Speter      /* This node is no longer modified, so no node was moved here */
11220251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11221251881Speter                                        STMT_CLEAR_MOVED_TO_FROM_DEST));
11222251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11223251881Speter                                            local_relpath));
11224251881Speter
11225251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
11226251881Speter    }
11227251881Speter
11228251881Speter  /* Update or add the BASE_NODE row with all the new information.  */
11229251881Speter
11230251881Speter  if (*local_relpath == '\0')
11231251881Speter    parent_relpath = NULL;
11232251881Speter  else
11233251881Speter    parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11234251881Speter
11235251881Speter  /* Preserve any incomplete status */
11236251881Speter  new_presence = (old_presence == svn_wc__db_status_incomplete
11237251881Speter                  ? svn_wc__db_status_incomplete
11238251881Speter                  : svn_wc__db_status_normal);
11239251881Speter
11240251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11241251881Speter                                    STMT_APPLY_CHANGES_TO_BASE_NODE));
11242251881Speter  /* symlink_target not yet used */
11243251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11244251881Speter                            wcroot->wc_id, local_relpath,
11245251881Speter                            parent_relpath,
11246251881Speter                            repos_id,
11247251881Speter                            repos_relpath,
11248251881Speter                            new_revision,
11249251881Speter                            presence_map, new_presence,
11250251881Speter                            new_depth_str,
11251251881Speter                            kind_map, new_kind,
11252251881Speter                            changed_rev,
11253251881Speter                            changed_date,
11254251881Speter                            changed_author,
11255251881Speter                            prop_blob.data, prop_blob.len));
11256251881Speter
11257251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11258251881Speter                                    scratch_pool));
11259251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11260251881Speter                                      scratch_pool));
11261251881Speter  if (inherited_prop_blob.data != NULL)
11262251881Speter    {
11263251881Speter      SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11264251881Speter                                    inherited_prop_blob.len));
11265251881Speter    }
11266251881Speter
11267251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
11268251881Speter
11269251881Speter  if (have_act)
11270251881Speter    {
11271251881Speter      if (keep_changelist && changelist != NULL)
11272251881Speter        {
11273251881Speter          /* The user told us to keep the changelist. Replace the row in
11274251881Speter             ACTUAL_NODE with the basic keys and the changelist.  */
11275251881Speter          SVN_ERR(svn_sqlite__get_statement(
11276251881Speter                    &stmt, wcroot->sdb,
11277251881Speter                    STMT_RESET_ACTUAL_WITH_CHANGELIST));
11278251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11279251881Speter                                    wcroot->wc_id, local_relpath,
11280251881Speter                                    svn_relpath_dirname(local_relpath,
11281251881Speter                                                        scratch_pool),
11282251881Speter                                    changelist));
11283251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
11284251881Speter        }
11285251881Speter      else
11286251881Speter        {
11287251881Speter          /* Toss the ACTUAL_NODE row.  */
11288251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11289251881Speter                                            STMT_DELETE_ACTUAL_NODE));
11290251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11291251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
11292251881Speter        }
11293251881Speter    }
11294251881Speter
11295251881Speter  if (new_kind == svn_node_dir)
11296251881Speter    {
11297251881Speter      /* When committing a directory, we should have its new children.  */
11298251881Speter      /* ### one day. just not today.  */
11299251881Speter#if 0
11300251881Speter      SVN_ERR_ASSERT(new_children != NULL);
11301251881Speter#endif
11302251881Speter
11303251881Speter      /* ### process the children  */
11304251881Speter    }
11305251881Speter
11306251881Speter  if (!no_unlock)
11307251881Speter    {
11308251881Speter      svn_sqlite__stmt_t *lock_stmt;
11309251881Speter
11310251881Speter      SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11311253734Speter                                        STMT_DELETE_LOCK_RECURSIVELY));
11312251881Speter      SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11313251881Speter      SVN_ERR(svn_sqlite__step_done(lock_stmt));
11314251881Speter    }
11315251881Speter
11316251881Speter  /* Install any work items into the queue, as part of this transaction.  */
11317251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11318251881Speter
11319251881Speter  return SVN_NO_ERROR;
11320251881Speter}
11321251881Speter
11322251881Speter
11323251881Spetersvn_error_t *
11324251881Spetersvn_wc__db_global_commit(svn_wc__db_t *db,
11325251881Speter                         const char *local_abspath,
11326251881Speter                         svn_revnum_t new_revision,
11327251881Speter                         svn_revnum_t changed_revision,
11328251881Speter                         apr_time_t changed_date,
11329251881Speter                         const char *changed_author,
11330251881Speter                         const svn_checksum_t *new_checksum,
11331251881Speter                         const apr_array_header_t *new_children,
11332251881Speter                         apr_hash_t *new_dav_cache,
11333251881Speter                         svn_boolean_t keep_changelist,
11334251881Speter                         svn_boolean_t no_unlock,
11335251881Speter                         const svn_skel_t *work_items,
11336251881Speter                         apr_pool_t *scratch_pool)
11337251881Speter{
11338251881Speter  const char *local_relpath;
11339251881Speter  svn_wc__db_wcroot_t *wcroot;
11340251881Speter
11341251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11342251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11343251881Speter  SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
11344251881Speter
11345251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11346251881Speter                              local_abspath, scratch_pool, scratch_pool));
11347251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11348251881Speter
11349251881Speter  SVN_WC__DB_WITH_TXN(
11350251881Speter    commit_node(wcroot, local_relpath,
11351251881Speter                new_revision, changed_revision, changed_date, changed_author,
11352251881Speter                new_checksum, new_children, new_dav_cache, keep_changelist,
11353251881Speter                no_unlock, work_items, scratch_pool),
11354251881Speter    wcroot);
11355251881Speter
11356251881Speter  /* We *totally* monkeyed the entries. Toss 'em.  */
11357251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11358251881Speter
11359251881Speter  return SVN_NO_ERROR;
11360251881Speter}
11361251881Speter
11362251881Speter
11363251881Spetersvn_error_t *
11364251881Spetersvn_wc__db_global_update(svn_wc__db_t *db,
11365251881Speter                         const char *local_abspath,
11366251881Speter                         svn_node_kind_t new_kind,
11367251881Speter                         const char *new_repos_relpath,
11368251881Speter                         svn_revnum_t new_revision,
11369251881Speter                         const apr_hash_t *new_props,
11370251881Speter                         svn_revnum_t new_changed_rev,
11371251881Speter                         apr_time_t new_changed_date,
11372251881Speter                         const char *new_changed_author,
11373251881Speter                         const apr_array_header_t *new_children,
11374251881Speter                         const svn_checksum_t *new_checksum,
11375251881Speter                         const char *new_target,
11376251881Speter                         const apr_hash_t *new_dav_cache,
11377251881Speter                         const svn_skel_t *conflict,
11378251881Speter                         const svn_skel_t *work_items,
11379251881Speter                         apr_pool_t *scratch_pool)
11380251881Speter{
11381251881Speter  NOT_IMPLEMENTED();
11382251881Speter
11383251881Speter#if 0
11384251881Speter  svn_wc__db_wcroot_t *wcroot;
11385251881Speter  const char *local_relpath;
11386251881Speter
11387251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11388251881Speter  /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"?  */
11389251881Speter  SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
11390251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11391251881Speter  SVN_ERR_ASSERT(new_props != NULL);
11392251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
11393251881Speter  SVN_ERR_ASSERT((new_children != NULL
11394251881Speter                  && new_checksum == NULL
11395251881Speter                  && new_target == NULL)
11396251881Speter                 || (new_children == NULL
11397251881Speter                     && new_checksum != NULL
11398251881Speter                     && new_target == NULL)
11399251881Speter                 || (new_children == NULL
11400251881Speter                     && new_checksum == NULL
11401251881Speter                     && new_target != NULL));
11402251881Speter
11403251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11404251881Speter                              local_abspath, scratch_pool, scratch_pool));
11405251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11406251881Speter
11407251881Speter  SVN_WC__DB_WITH_TXN(
11408251881Speter    update_node(wcroot, local_relpath,
11409251881Speter                new_repos_relpath, new_revision, new_props,
11410251881Speter                new_changed_rev, new_changed_date, new_changed_author,
11411251881Speter                new_children, new_checksum, new_target,
11412251881Speter                conflict, work_items, scratch_pool),
11413251881Speter    wcroot);
11414251881Speter
11415251881Speter  /* We *totally* monkeyed the entries. Toss 'em.  */
11416251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
11417251881Speter
11418251881Speter  return SVN_NO_ERROR;
11419251881Speter#endif
11420251881Speter}
11421251881Speter
11422251881Speter/* Sets a base nodes revision, repository relative path, and/or inherited
11423251881Speter   propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
11424251881Speter   SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
11425251881Speter   (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
11426251881Speter   inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
11427251881Speter   cache for the base node.
11428251881Speter */
11429251881Speterstatic svn_error_t *
11430251881Speterdb_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
11431251881Speter                                   const char *local_relpath,
11432251881Speter                                   apr_array_header_t *iprops,
11433251881Speter                                   svn_revnum_t rev,
11434251881Speter                                   svn_boolean_t set_repos_relpath,
11435251881Speter                                   const char *repos_relpath,
11436251881Speter                                   apr_int64_t repos_id,
11437251881Speter                                   apr_pool_t *scratch_pool)
11438251881Speter{
11439251881Speter  svn_sqlite__stmt_t *stmt;
11440251881Speter
11441251881Speter  SVN_ERR(flush_entries(wcroot,
11442251881Speter                        svn_dirent_join(wcroot->abspath, local_relpath,
11443251881Speter                                        scratch_pool),
11444251881Speter                        svn_depth_empty, scratch_pool));
11445251881Speter
11446251881Speter
11447251881Speter  if (SVN_IS_VALID_REVNUM(rev))
11448251881Speter    {
11449251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11450251881Speter                                        STMT_UPDATE_BASE_REVISION));
11451251881Speter
11452251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
11453251881Speter                                rev));
11454251881Speter
11455251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
11456251881Speter    }
11457251881Speter
11458251881Speter  if (set_repos_relpath)
11459251881Speter    {
11460251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11461251881Speter                                        STMT_UPDATE_BASE_REPOS));
11462251881Speter
11463251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
11464251881Speter                                repos_id, repos_relpath));
11465251881Speter
11466251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
11467251881Speter    }
11468251881Speter
11469251881Speter  /* Set or clear iprops. */
11470251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11471251881Speter                                    STMT_UPDATE_IPROP));
11472251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
11473251881Speter                            wcroot->wc_id,
11474251881Speter                            local_relpath));
11475251881Speter  SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
11476251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
11477251881Speter
11478251881Speter  return SVN_NO_ERROR;
11479251881Speter}
11480251881Speter
11481251881Speter/* The main body of bump_revisions_post_update().
11482251881Speter *
11483251881Speter * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
11484251881Speter * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11485251881Speter * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
11486251881Speter *
11487251881Speter * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11488251881Speter * working copy paths to depth-first ordered arrays of
11489251881Speter * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
11490251881Speter * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11491251881Speter * node's inherited properties.
11492251881Speter *
11493251881Speter * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11494251881Speter * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11495251881Speter */
11496251881Speterstatic svn_error_t *
11497251881Speterbump_node_revision(svn_wc__db_wcroot_t *wcroot,
11498251881Speter                   const char *local_relpath,
11499251881Speter                   apr_int64_t new_repos_id,
11500251881Speter                   const char *new_repos_relpath,
11501251881Speter                   svn_revnum_t new_rev,
11502251881Speter                   svn_depth_t depth,
11503251881Speter                   apr_hash_t *exclude_relpaths,
11504251881Speter                   apr_hash_t *wcroot_iprops,
11505251881Speter                   svn_boolean_t is_root,
11506251881Speter                   svn_boolean_t skip_when_dir,
11507251881Speter                   svn_wc__db_t *db,
11508251881Speter                   apr_pool_t *scratch_pool)
11509251881Speter{
11510251881Speter  apr_pool_t *iterpool;
11511251881Speter  const apr_array_header_t *children;
11512251881Speter  int i;
11513251881Speter  svn_wc__db_status_t status;
11514251881Speter  svn_node_kind_t db_kind;
11515251881Speter  svn_revnum_t revision;
11516251881Speter  const char *repos_relpath;
11517251881Speter  apr_int64_t repos_id;
11518251881Speter  svn_boolean_t set_repos_relpath = FALSE;
11519251881Speter  svn_boolean_t update_root;
11520251881Speter  svn_depth_t depth_below_here = depth;
11521251881Speter  apr_array_header_t *iprops = NULL;
11522251881Speter
11523251881Speter  /* Skip an excluded path and its descendants. */
11524251881Speter  if (svn_hash_gets(exclude_relpaths, local_relpath))
11525251881Speter    return SVN_NO_ERROR;
11526251881Speter
11527251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11528251881Speter                                            &repos_relpath, &repos_id,
11529251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11530251881Speter                                            NULL, NULL, NULL, NULL, &update_root,
11531251881Speter                                            wcroot, local_relpath,
11532251881Speter                                            scratch_pool, scratch_pool));
11533251881Speter
11534251881Speter  /* Skip file externals */
11535251881Speter  if (update_root
11536251881Speter      && db_kind == svn_node_file
11537251881Speter      && !is_root)
11538251881Speter    return SVN_NO_ERROR;
11539251881Speter
11540251881Speter  if (skip_when_dir && db_kind == svn_node_dir)
11541251881Speter    return SVN_NO_ERROR;
11542251881Speter
11543251881Speter  /* If the node is still marked 'not-present', then the server did not
11544251881Speter     re-add it.  So it's really gone in this revision, thus we remove the node.
11545251881Speter
11546251881Speter     If the node is still marked 'server-excluded' and yet is not the same
11547251881Speter     revision as new_rev, then the server did not re-add it, nor
11548251881Speter     re-server-exclude it, so we can remove the node. */
11549251881Speter  if (!is_root
11550251881Speter      && (status == svn_wc__db_status_not_present
11551251881Speter          || (status == svn_wc__db_status_server_excluded &&
11552251881Speter              revision != new_rev)))
11553251881Speter    {
11554251881Speter      return svn_error_trace(db_base_remove(wcroot, local_relpath,
11555253734Speter                                            db, FALSE, FALSE, FALSE,
11556251881Speter                                            SVN_INVALID_REVNUM,
11557251881Speter                                            NULL, NULL, scratch_pool));
11558251881Speter    }
11559251881Speter
11560251881Speter  if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11561251881Speter    set_repos_relpath = TRUE;
11562251881Speter
11563251881Speter  if (wcroot_iprops)
11564251881Speter    iprops = svn_hash_gets(wcroot_iprops,
11565251881Speter                           svn_dirent_join(wcroot->abspath, local_relpath,
11566251881Speter                                           scratch_pool));
11567251881Speter
11568251881Speter  if (iprops
11569251881Speter      || set_repos_relpath
11570251881Speter      || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11571251881Speter    {
11572251881Speter      SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11573251881Speter                                                 iprops, new_rev,
11574251881Speter                                                 set_repos_relpath,
11575251881Speter                                                 new_repos_relpath,
11576251881Speter                                                 new_repos_id,
11577251881Speter                                                 scratch_pool));
11578251881Speter    }
11579251881Speter
11580251881Speter  /* Early out */
11581251881Speter  if (depth <= svn_depth_empty
11582251881Speter      || db_kind != svn_node_dir
11583251881Speter      || status == svn_wc__db_status_server_excluded
11584251881Speter      || status == svn_wc__db_status_excluded
11585251881Speter      || status == svn_wc__db_status_not_present)
11586251881Speter    return SVN_NO_ERROR;
11587251881Speter
11588251881Speter  /* And now recurse over the children */
11589251881Speter
11590251881Speter  depth_below_here = depth;
11591251881Speter
11592251881Speter  if (depth == svn_depth_immediates || depth == svn_depth_files)
11593251881Speter    depth_below_here = svn_depth_empty;
11594251881Speter
11595251881Speter  iterpool = svn_pool_create(scratch_pool);
11596251881Speter
11597251881Speter  SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11598251881Speter                               scratch_pool, iterpool));
11599251881Speter  for (i = 0; i < children->nelts; i++)
11600251881Speter    {
11601251881Speter      const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11602251881Speter      const char *child_local_relpath;
11603251881Speter      const char *child_repos_relpath = NULL;
11604251881Speter
11605251881Speter      svn_pool_clear(iterpool);
11606251881Speter
11607251881Speter      /* Derive the new URL for the current (child) entry */
11608251881Speter      if (new_repos_relpath)
11609251881Speter        child_repos_relpath = svn_relpath_join(new_repos_relpath,
11610251881Speter                                               child_basename, iterpool);
11611251881Speter
11612251881Speter      child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11613251881Speter                                             iterpool);
11614251881Speter
11615251881Speter      SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11616251881Speter                                 child_repos_relpath, new_rev,
11617251881Speter                                 depth_below_here,
11618251881Speter                                 exclude_relpaths, wcroot_iprops,
11619251881Speter                                 FALSE /* is_root */,
11620251881Speter                                 (depth < svn_depth_immediates), db,
11621251881Speter                                 iterpool));
11622251881Speter    }
11623251881Speter
11624251881Speter  /* Cleanup */
11625251881Speter  svn_pool_destroy(iterpool);
11626251881Speter
11627251881Speter  return SVN_NO_ERROR;
11628251881Speter}
11629251881Speter
11630251881Speter/* Helper for svn_wc__db_op_bump_revisions_post_update().
11631251881Speter */
11632251881Speterstatic svn_error_t *
11633251881Speterbump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11634251881Speter                           const char *local_relpath,
11635251881Speter                           svn_wc__db_t *db,
11636251881Speter                           svn_depth_t depth,
11637251881Speter                           const char *new_repos_relpath,
11638251881Speter                           const char *new_repos_root_url,
11639251881Speter                           const char *new_repos_uuid,
11640251881Speter                           svn_revnum_t new_revision,
11641251881Speter                           apr_hash_t *exclude_relpaths,
11642251881Speter                           apr_hash_t *wcroot_iprops,
11643251881Speter                           svn_wc_notify_func2_t notify_func,
11644251881Speter                           void *notify_baton,
11645251881Speter                           apr_pool_t *scratch_pool)
11646251881Speter{
11647251881Speter  svn_wc__db_status_t status;
11648251881Speter  svn_node_kind_t kind;
11649251881Speter  svn_error_t *err;
11650251881Speter  apr_int64_t new_repos_id = INVALID_REPOS_ID;
11651251881Speter
11652251881Speter  err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11653251881Speter                                          NULL, NULL, NULL, NULL, NULL, NULL,
11654251881Speter                                          NULL, NULL, NULL, NULL,
11655251881Speter                                          wcroot, local_relpath,
11656251881Speter                                          scratch_pool, scratch_pool);
11657251881Speter  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11658251881Speter    {
11659251881Speter      svn_error_clear(err);
11660251881Speter      return SVN_NO_ERROR;
11661251881Speter    }
11662251881Speter  else
11663251881Speter    SVN_ERR(err);
11664251881Speter
11665251881Speter  switch (status)
11666251881Speter    {
11667251881Speter      case svn_wc__db_status_excluded:
11668251881Speter      case svn_wc__db_status_server_excluded:
11669251881Speter      case svn_wc__db_status_not_present:
11670251881Speter        return SVN_NO_ERROR;
11671251881Speter
11672251881Speter      /* Explicitly ignore other statii */
11673251881Speter      default:
11674251881Speter        break;
11675251881Speter    }
11676251881Speter
11677251881Speter  if (new_repos_root_url != NULL)
11678251881Speter    SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11679251881Speter                            new_repos_uuid,
11680251881Speter                            wcroot->sdb, scratch_pool));
11681251881Speter
11682251881Speter  SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11683251881Speter                             new_repos_relpath, new_revision,
11684251881Speter                             depth, exclude_relpaths,
11685251881Speter                             wcroot_iprops,
11686251881Speter                             TRUE /* is_root */, FALSE, db,
11687251881Speter                             scratch_pool));
11688251881Speter
11689251881Speter  SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11690251881Speter                                     scratch_pool));
11691251881Speter
11692251881Speter  SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11693251881Speter                                             SVN_INVALID_REVNUM, notify_func,
11694251881Speter                                             notify_baton, scratch_pool));
11695251881Speter
11696251881Speter  return SVN_NO_ERROR;
11697251881Speter}
11698251881Speter
11699251881Spetersvn_error_t *
11700251881Spetersvn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11701251881Speter                                         const char *local_abspath,
11702251881Speter                                         svn_depth_t depth,
11703251881Speter                                         const char *new_repos_relpath,
11704251881Speter                                         const char *new_repos_root_url,
11705251881Speter                                         const char *new_repos_uuid,
11706251881Speter                                         svn_revnum_t new_revision,
11707251881Speter                                         apr_hash_t *exclude_relpaths,
11708251881Speter                                         apr_hash_t *wcroot_iprops,
11709251881Speter                                         svn_wc_notify_func2_t notify_func,
11710251881Speter                                         void *notify_baton,
11711251881Speter                                         apr_pool_t *scratch_pool)
11712251881Speter{
11713251881Speter  const char *local_relpath;
11714251881Speter  svn_wc__db_wcroot_t *wcroot;
11715251881Speter
11716251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11717251881Speter                              local_abspath, scratch_pool, scratch_pool));
11718251881Speter
11719251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11720251881Speter
11721251881Speter  if (svn_hash_gets(exclude_relpaths, local_relpath))
11722251881Speter    return SVN_NO_ERROR;
11723251881Speter
11724251881Speter  if (depth == svn_depth_unknown)
11725251881Speter    depth = svn_depth_infinity;
11726251881Speter
11727251881Speter  SVN_WC__DB_WITH_TXN(
11728251881Speter    bump_revisions_post_update(wcroot, local_relpath, db,
11729251881Speter                               depth, new_repos_relpath, new_repos_root_url,
11730251881Speter                               new_repos_uuid, new_revision,
11731251881Speter                               exclude_relpaths, wcroot_iprops,
11732251881Speter                               notify_func, notify_baton, scratch_pool),
11733251881Speter    wcroot);
11734251881Speter
11735251881Speter  return SVN_NO_ERROR;
11736251881Speter}
11737251881Speter
11738251881Speter/* The body of svn_wc__db_lock_add().
11739251881Speter */
11740251881Speterstatic svn_error_t *
11741251881Speterlock_add_txn(svn_wc__db_wcroot_t *wcroot,
11742251881Speter             const char *local_relpath,
11743251881Speter             const svn_wc__db_lock_t *lock,
11744251881Speter             apr_pool_t *scratch_pool)
11745251881Speter{
11746251881Speter  svn_sqlite__stmt_t *stmt;
11747251881Speter  const char *repos_relpath;
11748251881Speter  apr_int64_t repos_id;
11749251881Speter
11750251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11751251881Speter                                            &repos_relpath, &repos_id,
11752251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11753251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11754251881Speter                                            wcroot, local_relpath,
11755251881Speter                                            scratch_pool, scratch_pool));
11756251881Speter
11757251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11758251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11759251881Speter                            repos_id, repos_relpath, lock->token));
11760251881Speter
11761251881Speter  if (lock->owner != NULL)
11762251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11763251881Speter
11764251881Speter  if (lock->comment != NULL)
11765251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11766251881Speter
11767251881Speter  if (lock->date != 0)
11768251881Speter    SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11769251881Speter
11770251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
11771251881Speter
11772251881Speter  return SVN_NO_ERROR;
11773251881Speter}
11774251881Speter
11775251881Speter
11776251881Spetersvn_error_t *
11777251881Spetersvn_wc__db_lock_add(svn_wc__db_t *db,
11778251881Speter                    const char *local_abspath,
11779251881Speter                    const svn_wc__db_lock_t *lock,
11780251881Speter                    apr_pool_t *scratch_pool)
11781251881Speter{
11782251881Speter  svn_wc__db_wcroot_t *wcroot;
11783251881Speter  const char *local_relpath;
11784251881Speter
11785251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11786251881Speter  SVN_ERR_ASSERT(lock != NULL);
11787251881Speter
11788251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11789251881Speter                              local_abspath, scratch_pool, scratch_pool));
11790251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11791251881Speter
11792251881Speter  SVN_WC__DB_WITH_TXN(
11793251881Speter    lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11794251881Speter    wcroot);
11795251881Speter
11796251881Speter  /* There may be some entries, and the lock info is now out of date.  */
11797251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11798251881Speter
11799251881Speter  return SVN_NO_ERROR;
11800251881Speter}
11801251881Speter
11802251881Speter
11803251881Speter/* The body of svn_wc__db_lock_remove().
11804251881Speter */
11805251881Speterstatic svn_error_t *
11806251881Speterlock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11807251881Speter                const char *local_relpath,
11808251881Speter                apr_pool_t *scratch_pool)
11809251881Speter{
11810251881Speter  const char *repos_relpath;
11811251881Speter  apr_int64_t repos_id;
11812251881Speter  svn_sqlite__stmt_t *stmt;
11813251881Speter
11814251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11815251881Speter                                            &repos_relpath, &repos_id,
11816251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11817251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11818251881Speter                                            wcroot, local_relpath,
11819251881Speter                                            scratch_pool, scratch_pool));
11820251881Speter
11821251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11822251881Speter                                    STMT_DELETE_LOCK));
11823251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11824251881Speter
11825251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
11826251881Speter
11827251881Speter  return SVN_NO_ERROR;
11828251881Speter}
11829251881Speter
11830251881Speter
11831251881Spetersvn_error_t *
11832251881Spetersvn_wc__db_lock_remove(svn_wc__db_t *db,
11833251881Speter                       const char *local_abspath,
11834251881Speter                       apr_pool_t *scratch_pool)
11835251881Speter{
11836251881Speter  svn_wc__db_wcroot_t *wcroot;
11837251881Speter  const char *local_relpath;
11838251881Speter
11839251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11840251881Speter
11841251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11842251881Speter                              local_abspath, scratch_pool, scratch_pool));
11843251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11844251881Speter
11845251881Speter  SVN_WC__DB_WITH_TXN(
11846251881Speter    lock_remove_txn(wcroot, local_relpath, scratch_pool),
11847251881Speter    wcroot);
11848251881Speter
11849251881Speter  /* There may be some entries, and the lock info is now out of date.  */
11850251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11851251881Speter
11852251881Speter  return SVN_NO_ERROR;
11853251881Speter}
11854251881Speter
11855251881Speter
11856251881Spetersvn_error_t *
11857251881Spetersvn_wc__db_scan_base_repos(const char **repos_relpath,
11858251881Speter                           const char **repos_root_url,
11859251881Speter                           const char **repos_uuid,
11860251881Speter                           svn_wc__db_t *db,
11861251881Speter                           const char *local_abspath,
11862251881Speter                           apr_pool_t *result_pool,
11863251881Speter                           apr_pool_t *scratch_pool)
11864251881Speter{
11865251881Speter  svn_wc__db_wcroot_t *wcroot;
11866251881Speter  const char *local_relpath;
11867251881Speter  apr_int64_t repos_id;
11868251881Speter
11869251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11870251881Speter
11871251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11872251881Speter                              local_abspath, scratch_pool, scratch_pool));
11873251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11874251881Speter
11875251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11876251881Speter                                            repos_relpath, &repos_id,
11877251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11878251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11879251881Speter                                            wcroot, local_relpath,
11880251881Speter                                            result_pool, scratch_pool));
11881251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11882251881Speter                                      repos_id, result_pool));
11883251881Speter
11884251881Speter  return SVN_NO_ERROR;
11885251881Speter}
11886251881Speter
11887251881Speter
11888251881Speter/* A helper for scan_addition().
11889251881Speter * Compute moved-from information for the node at LOCAL_RELPATH which
11890251881Speter * has been determined as having been moved-here.
11891251881Speter * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11892251881Speter * path of the move-source node in *MOVED_FROM_RELPATH.
11893251881Speter * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11894251881Speter * to the path of the op-root of the delete-half of the move.
11895251881Speter * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11896251881Speter * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11897251881Speter * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11898251881Speter * of the move. */
11899251881Speterstatic svn_error_t *
11900251881Speterget_moved_from_info(const char **moved_from_relpath,
11901251881Speter                    const char **moved_from_op_root_relpath,
11902251881Speter                    const char *moved_to_op_root_relpath,
11903251881Speter                    int *op_depth,
11904251881Speter                    svn_wc__db_wcroot_t *wcroot,
11905251881Speter                    const char *local_relpath,
11906251881Speter                    apr_pool_t *result_pool,
11907251881Speter                    apr_pool_t *scratch_pool)
11908251881Speter{
11909251881Speter  svn_sqlite__stmt_t *stmt;
11910251881Speter  svn_boolean_t have_row;
11911251881Speter
11912251881Speter  /* Run a query to get the moved-from path from the DB. */
11913251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11914251881Speter                                    STMT_SELECT_MOVED_FROM_RELPATH));
11915251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
11916251881Speter                            wcroot->wc_id, moved_to_op_root_relpath));
11917251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11918251881Speter
11919251881Speter  if (!have_row)
11920251881Speter    {
11921251881Speter      /* The move was only recorded at the copy-half, possibly because
11922251881Speter       * the move operation was interrupted mid-way between the copy
11923251881Speter       * and the delete. Treat this node as a normal copy. */
11924251881Speter      if (moved_from_relpath)
11925251881Speter        *moved_from_relpath = NULL;
11926251881Speter      if (moved_from_op_root_relpath)
11927251881Speter        *moved_from_op_root_relpath = NULL;
11928251881Speter
11929251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
11930251881Speter      return SVN_NO_ERROR;
11931251881Speter    }
11932251881Speter
11933251881Speter  if (op_depth)
11934251881Speter    *op_depth = svn_sqlite__column_int(stmt, 1);
11935251881Speter
11936251881Speter  if (moved_from_relpath || moved_from_op_root_relpath)
11937251881Speter    {
11938251881Speter      const char *db_delete_op_root_relpath;
11939251881Speter
11940251881Speter      /* The moved-from path from the DB is the relpath of
11941251881Speter       * the op_root of the delete-half of the move. */
11942251881Speter      db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
11943251881Speter                                                          result_pool);
11944251881Speter      if (moved_from_op_root_relpath)
11945251881Speter        *moved_from_op_root_relpath = db_delete_op_root_relpath;
11946251881Speter
11947251881Speter      if (moved_from_relpath)
11948251881Speter        {
11949251881Speter          if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
11950251881Speter            {
11951251881Speter              /* LOCAL_RELPATH is the op_root of the copied-half of the
11952251881Speter               * move, so the correct MOVED_FROM_ABSPATH is the op-root
11953251881Speter               * of the delete-half. */
11954251881Speter              *moved_from_relpath = db_delete_op_root_relpath;
11955251881Speter            }
11956251881Speter          else
11957251881Speter            {
11958251881Speter              const char *child_relpath;
11959251881Speter
11960251881Speter              /* LOCAL_RELPATH is a child that was copied along with the
11961251881Speter               * op_root of the copied-half of the move. Construct the
11962251881Speter               * corresponding path beneath the op_root of the delete-half. */
11963251881Speter
11964251881Speter              /* Grab the child path relative to the op_root of the move
11965251881Speter               * destination. */
11966251881Speter              child_relpath = svn_relpath_skip_ancestor(
11967251881Speter                                moved_to_op_root_relpath, local_relpath);
11968251881Speter
11969251881Speter              SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
11970251881Speter
11971251881Speter              /* This join is valid because LOCAL_RELPATH has not been moved
11972251881Speter               * within the copied-half of the move yet -- else, it would
11973251881Speter               * be its own op_root. */
11974251881Speter              *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
11975251881Speter                                                     child_relpath,
11976251881Speter                                                     result_pool);
11977251881Speter            }
11978251881Speter        }
11979251881Speter    }
11980251881Speter
11981251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
11982251881Speter
11983251881Speter  return SVN_NO_ERROR;
11984251881Speter}
11985251881Speter
11986251881Speter/* The body of scan_addition().
11987251881Speter */
11988251881Speterstatic svn_error_t *
11989251881Speterscan_addition_txn(svn_wc__db_status_t *status,
11990251881Speter                  const char **op_root_relpath_p,
11991251881Speter                  const char **repos_relpath,
11992251881Speter                  apr_int64_t *repos_id,
11993251881Speter                  const char **original_repos_relpath,
11994251881Speter                  apr_int64_t *original_repos_id,
11995251881Speter                  svn_revnum_t *original_revision,
11996251881Speter                  const char **moved_from_relpath,
11997251881Speter                  const char **moved_from_op_root_relpath,
11998251881Speter                  int *moved_from_op_depth,
11999251881Speter                  svn_wc__db_wcroot_t *wcroot,
12000251881Speter                  const char *local_relpath,
12001251881Speter                  apr_pool_t *result_pool,
12002251881Speter                  apr_pool_t *scratch_pool)
12003251881Speter{
12004251881Speter  const char *op_root_relpath;
12005251881Speter  const char *build_relpath = "";
12006251881Speter
12007251881Speter  /* Initialize most of the OUT parameters. Generally, we'll only be filling
12008251881Speter     in a subset of these, so it is easier to init all up front. Note that
12009251881Speter     the STATUS parameter will be initialized once we read the status of
12010251881Speter     the specified node.  */
12011251881Speter  if (op_root_relpath_p)
12012251881Speter    *op_root_relpath_p = NULL;
12013251881Speter  if (original_repos_relpath)
12014251881Speter    *original_repos_relpath = NULL;
12015251881Speter  if (original_repos_id)
12016251881Speter    *original_repos_id = INVALID_REPOS_ID;
12017251881Speter  if (original_revision)
12018251881Speter    *original_revision = SVN_INVALID_REVNUM;
12019251881Speter  if (moved_from_relpath)
12020251881Speter    *moved_from_relpath = NULL;
12021251881Speter  if (moved_from_op_root_relpath)
12022251881Speter    *moved_from_op_root_relpath = NULL;
12023251881Speter  if (moved_from_op_depth)
12024251881Speter    *moved_from_op_depth = 0;
12025251881Speter
12026251881Speter  {
12027251881Speter    svn_sqlite__stmt_t *stmt;
12028251881Speter    svn_boolean_t have_row;
12029251881Speter    svn_wc__db_status_t presence;
12030251881Speter    int op_depth;
12031251881Speter    const char *repos_prefix_path = "";
12032251881Speter    int i;
12033251881Speter
12034251881Speter    /* ### is it faster to fetch fewer columns? */
12035251881Speter    SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12036251881Speter                                      STMT_SELECT_WORKING_NODE));
12037251881Speter    SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12038251881Speter    SVN_ERR(svn_sqlite__step(&have_row, stmt));
12039251881Speter
12040251881Speter    if (!have_row)
12041251881Speter      {
12042251881Speter        /* Reset statement before returning */
12043251881Speter        SVN_ERR(svn_sqlite__reset(stmt));
12044251881Speter
12045251881Speter        /* ### maybe we should return a usage error instead?  */
12046251881Speter        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12047251881Speter                                 _("The node '%s' was not found."),
12048251881Speter                                 path_for_error_message(wcroot,
12049251881Speter                                                        local_relpath,
12050251881Speter                                                        scratch_pool));
12051251881Speter      }
12052251881Speter
12053251881Speter    presence = svn_sqlite__column_token(stmt, 1, presence_map);
12054251881Speter
12055251881Speter    /* The starting node should exist normally.  */
12056251881Speter    op_depth = svn_sqlite__column_int(stmt, 0);
12057251881Speter    if (op_depth == 0 || (presence != svn_wc__db_status_normal
12058251881Speter                          && presence != svn_wc__db_status_incomplete))
12059251881Speter      /* reset the statement as part of the error generation process */
12060251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12061251881Speter                               svn_sqlite__reset(stmt),
12062251881Speter                               _("Expected node '%s' to be added."),
12063251881Speter                               path_for_error_message(wcroot,
12064251881Speter                                                      local_relpath,
12065251881Speter                                                      scratch_pool));
12066251881Speter
12067251881Speter    if (original_revision)
12068251881Speter      *original_revision = svn_sqlite__column_revnum(stmt, 12);
12069251881Speter
12070251881Speter    /* Provide the default status; we'll override as appropriate. */
12071251881Speter    if (status)
12072251881Speter      {
12073251881Speter        if (presence == svn_wc__db_status_normal)
12074251881Speter          *status = svn_wc__db_status_added;
12075251881Speter        else
12076251881Speter          *status = svn_wc__db_status_incomplete;
12077251881Speter      }
12078251881Speter
12079251881Speter
12080251881Speter    /* Calculate the op root local path components */
12081251881Speter    op_root_relpath = local_relpath;
12082251881Speter
12083251881Speter    for (i = relpath_depth(local_relpath); i > op_depth; --i)
12084251881Speter      {
12085251881Speter        /* Calculate the path of the operation root */
12086251881Speter        repos_prefix_path =
12087251881Speter          svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12088251881Speter                           repos_prefix_path,
12089251881Speter                           scratch_pool);
12090251881Speter        op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12091251881Speter      }
12092251881Speter
12093251881Speter    if (op_root_relpath_p)
12094251881Speter      *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12095251881Speter
12096251881Speter    /* ### This if-statement is quite redundant.
12097251881Speter     * ### We're checking all these values again within the body anyway.
12098251881Speter     * ### The body should be broken up appropriately and move into the
12099251881Speter     * ### outer scope. */
12100251881Speter    if (original_repos_relpath
12101251881Speter        || original_repos_id
12102251881Speter        || (original_revision
12103251881Speter                && *original_revision == SVN_INVALID_REVNUM)
12104251881Speter        || status
12105251881Speter        || moved_from_relpath || moved_from_op_root_relpath)
12106251881Speter      {
12107251881Speter        if (local_relpath != op_root_relpath)
12108251881Speter          /* requery to get the add/copy root */
12109251881Speter          {
12110251881Speter            SVN_ERR(svn_sqlite__reset(stmt));
12111251881Speter
12112251881Speter            SVN_ERR(svn_sqlite__bindf(stmt, "is",
12113251881Speter                                      wcroot->wc_id, op_root_relpath));
12114251881Speter            SVN_ERR(svn_sqlite__step(&have_row, stmt));
12115251881Speter
12116251881Speter            if (!have_row)
12117251881Speter              {
12118251881Speter                /* Reset statement before returning */
12119251881Speter                SVN_ERR(svn_sqlite__reset(stmt));
12120251881Speter
12121251881Speter                /* ### maybe we should return a usage error instead?  */
12122251881Speter                return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12123251881Speter                                         _("The node '%s' was not found."),
12124251881Speter                                         path_for_error_message(wcroot,
12125251881Speter                                                                op_root_relpath,
12126251881Speter                                                                scratch_pool));
12127251881Speter              }
12128251881Speter
12129251881Speter            if (original_revision
12130251881Speter                    && *original_revision == SVN_INVALID_REVNUM)
12131251881Speter              *original_revision = svn_sqlite__column_revnum(stmt, 12);
12132251881Speter          }
12133251881Speter
12134251881Speter        if (original_repos_relpath)
12135251881Speter          *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12136251881Speter                                                            result_pool);
12137251881Speter
12138251881Speter        if (!svn_sqlite__column_is_null(stmt, 10)
12139251881Speter            && (status
12140251881Speter                || original_repos_id
12141251881Speter                || moved_from_relpath || moved_from_op_root_relpath))
12142251881Speter          /* If column 10 (original_repos_id) is NULL,
12143251881Speter             this is a plain add, not a copy or a move */
12144251881Speter          {
12145251881Speter            svn_boolean_t moved_here;
12146251881Speter            if (original_repos_id)
12147251881Speter              *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12148251881Speter
12149251881Speter            moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12150251881Speter            if (status)
12151251881Speter              *status = moved_here ? svn_wc__db_status_moved_here
12152251881Speter                                   : svn_wc__db_status_copied;
12153251881Speter
12154251881Speter            if (moved_here
12155251881Speter                && (moved_from_relpath || moved_from_op_root_relpath))
12156251881Speter              {
12157251881Speter                svn_error_t *err;
12158251881Speter
12159251881Speter                err = get_moved_from_info(moved_from_relpath,
12160251881Speter                                          moved_from_op_root_relpath,
12161251881Speter                                          op_root_relpath,
12162251881Speter                                          moved_from_op_depth,
12163251881Speter                                          wcroot, local_relpath,
12164251881Speter                                          result_pool,
12165251881Speter                                          scratch_pool);
12166251881Speter
12167251881Speter                if (err)
12168251881Speter                  return svn_error_compose_create(
12169251881Speter                                err, svn_sqlite__reset(stmt));
12170251881Speter              }
12171251881Speter          }
12172251881Speter      }
12173251881Speter
12174251881Speter
12175251881Speter    /* ### This loop here is to skip up to the first node which is a BASE node,
12176251881Speter       because base_get_info() doesn't accommodate the scenario that
12177251881Speter       we're looking at here; we found the true op_root, which may be inside
12178251881Speter       further changed trees. */
12179251881Speter    if (repos_relpath || repos_id)
12180251881Speter      {
12181251881Speter        const char *base_relpath;
12182251881Speter
12183251881Speter    while (TRUE)
12184251881Speter      {
12185251881Speter
12186251881Speter        SVN_ERR(svn_sqlite__reset(stmt));
12187251881Speter
12188251881Speter        /* Pointing at op_depth, look at the parent */
12189251881Speter        repos_prefix_path =
12190251881Speter          svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12191251881Speter                           repos_prefix_path,
12192251881Speter                           scratch_pool);
12193251881Speter        op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12194251881Speter
12195251881Speter
12196251881Speter        SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12197251881Speter        SVN_ERR(svn_sqlite__step(&have_row, stmt));
12198251881Speter
12199251881Speter        if (! have_row)
12200251881Speter          break;
12201251881Speter
12202251881Speter        op_depth = svn_sqlite__column_int(stmt, 0);
12203251881Speter
12204251881Speter        /* Skip to op_depth */
12205251881Speter        for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
12206251881Speter          {
12207251881Speter            /* Calculate the path of the operation root */
12208251881Speter            repos_prefix_path =
12209251881Speter              svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12210251881Speter                               repos_prefix_path,
12211251881Speter                               scratch_pool);
12212251881Speter            op_root_relpath =
12213251881Speter              svn_relpath_dirname(op_root_relpath, scratch_pool);
12214251881Speter          }
12215251881Speter      }
12216251881Speter
12217251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12218251881Speter
12219251881Speter      build_relpath = repos_prefix_path;
12220251881Speter
12221251881Speter      /* If we're here, then we have an added/copied/moved (start) node, and
12222251881Speter         CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12223251881Speter         information for the current node, and use that to compute the start
12224251881Speter         node's repository information.  */
12225251881Speter      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12226251881Speter                                                &base_relpath, repos_id,
12227251881Speter                                                NULL, NULL, NULL, NULL, NULL,
12228251881Speter                                                NULL, NULL, NULL, NULL, NULL,
12229251881Speter                                                wcroot, op_root_relpath,
12230251881Speter                                                scratch_pool, scratch_pool));
12231251881Speter
12232251881Speter        if (repos_relpath)
12233251881Speter          *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12234251881Speter                                            result_pool);
12235251881Speter      }
12236251881Speter    else
12237251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12238251881Speter  }
12239251881Speter  /* Postconditions */
12240251881Speter#ifdef SVN_DEBUG
12241251881Speter  if (status)
12242251881Speter    {
12243251881Speter      SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12244251881Speter                     || *status == svn_wc__db_status_copied
12245251881Speter                     || *status == svn_wc__db_status_incomplete
12246251881Speter                     || *status == svn_wc__db_status_moved_here);
12247251881Speter      if (*status == svn_wc__db_status_added)
12248251881Speter        {
12249251881Speter          SVN_ERR_ASSERT(!original_repos_relpath
12250251881Speter                         || *original_repos_relpath == NULL);
12251251881Speter          SVN_ERR_ASSERT(!original_revision
12252251881Speter                         || *original_revision == SVN_INVALID_REVNUM);
12253251881Speter          SVN_ERR_ASSERT(!original_repos_id
12254251881Speter                         || *original_repos_id == INVALID_REPOS_ID);
12255251881Speter        }
12256251881Speter      /* An upgrade with a missing directory can leave INCOMPLETE working
12257251881Speter         op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12258251881Speter       */
12259251881Speter      else if (*status != svn_wc__db_status_incomplete)
12260251881Speter        {
12261251881Speter          SVN_ERR_ASSERT(!original_repos_relpath
12262251881Speter                         || *original_repos_relpath != NULL);
12263251881Speter          SVN_ERR_ASSERT(!original_revision
12264251881Speter                         || *original_revision != SVN_INVALID_REVNUM);
12265251881Speter          SVN_ERR_ASSERT(!original_repos_id
12266251881Speter                         || *original_repos_id != INVALID_REPOS_ID);
12267251881Speter        }
12268251881Speter    }
12269251881Speter  SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12270251881Speter#endif
12271251881Speter
12272251881Speter  return SVN_NO_ERROR;
12273251881Speter}
12274251881Speter
12275251881Speter
12276251881Speter/* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12277251881Speter   DB+LOCAL_ABSPATH.
12278251881Speter
12279251881Speter   The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12280251881Speter   is no 'copy-from' repository.  */
12281251881Speterstatic svn_error_t *
12282251881Speterscan_addition(svn_wc__db_status_t *status,
12283251881Speter              const char **op_root_relpath,
12284251881Speter              const char **repos_relpath,
12285251881Speter              apr_int64_t *repos_id,
12286251881Speter              const char **original_repos_relpath,
12287251881Speter              apr_int64_t *original_repos_id,
12288251881Speter              svn_revnum_t *original_revision,
12289251881Speter              const char **moved_from_relpath,
12290251881Speter              const char **moved_from_op_root_relpath,
12291251881Speter              int *moved_from_op_depth,
12292251881Speter              svn_wc__db_wcroot_t *wcroot,
12293251881Speter              const char *local_relpath,
12294251881Speter              apr_pool_t *result_pool,
12295251881Speter              apr_pool_t *scratch_pool)
12296251881Speter{
12297251881Speter  SVN_WC__DB_WITH_TXN(
12298251881Speter    scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
12299251881Speter                      original_repos_relpath, original_repos_id,
12300251881Speter                      original_revision, moved_from_relpath,
12301251881Speter                      moved_from_op_root_relpath, moved_from_op_depth,
12302251881Speter                      wcroot, local_relpath, result_pool, scratch_pool),
12303251881Speter    wcroot);
12304251881Speter  return SVN_NO_ERROR;
12305251881Speter}
12306251881Speter
12307251881Speter
12308251881Spetersvn_error_t *
12309251881Spetersvn_wc__db_scan_addition(svn_wc__db_status_t *status,
12310251881Speter                         const char **op_root_abspath,
12311251881Speter                         const char **repos_relpath,
12312251881Speter                         const char **repos_root_url,
12313251881Speter                         const char **repos_uuid,
12314251881Speter                         const char **original_repos_relpath,
12315251881Speter                         const char **original_root_url,
12316251881Speter                         const char **original_uuid,
12317251881Speter                         svn_revnum_t *original_revision,
12318251881Speter                         svn_wc__db_t *db,
12319251881Speter                         const char *local_abspath,
12320251881Speter                         apr_pool_t *result_pool,
12321251881Speter                         apr_pool_t *scratch_pool)
12322251881Speter{
12323251881Speter  svn_wc__db_wcroot_t *wcroot;
12324251881Speter  const char *local_relpath;
12325251881Speter  const char *op_root_relpath = NULL;
12326251881Speter  apr_int64_t repos_id = INVALID_REPOS_ID;
12327251881Speter  apr_int64_t original_repos_id = INVALID_REPOS_ID;
12328251881Speter  apr_int64_t *repos_id_p
12329251881Speter    = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12330251881Speter  apr_int64_t *original_repos_id_p
12331251881Speter    = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12332251881Speter
12333251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12334251881Speter
12335251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12336251881Speter                              local_abspath, scratch_pool, scratch_pool));
12337251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12338251881Speter
12339251881Speter  SVN_ERR(scan_addition(status,
12340251881Speter                        op_root_abspath
12341251881Speter                                ? &op_root_relpath
12342251881Speter                                : NULL,
12343251881Speter                        repos_relpath, repos_id_p,
12344251881Speter                        original_repos_relpath, original_repos_id_p,
12345251881Speter                        original_revision,
12346251881Speter                        NULL, NULL, NULL,
12347251881Speter                        wcroot, local_relpath, result_pool, scratch_pool));
12348251881Speter
12349251881Speter  if (op_root_abspath)
12350251881Speter    *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12351251881Speter                                       result_pool);
12352251881Speter  /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12353251881Speter  SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12354251881Speter
12355251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
12356251881Speter                                      repos_id, result_pool));
12357251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12358251881Speter                                      wcroot->sdb, original_repos_id,
12359251881Speter                                      result_pool));
12360251881Speter
12361251881Speter  return SVN_NO_ERROR;
12362251881Speter}
12363251881Speter
12364251881Spetersvn_error_t *
12365251881Spetersvn_wc__db_scan_moved(const char **moved_from_abspath,
12366251881Speter                      const char **op_root_abspath,
12367251881Speter                      const char **op_root_moved_from_abspath,
12368251881Speter                      const char **moved_from_delete_abspath,
12369251881Speter                      svn_wc__db_t *db,
12370251881Speter                      const char *local_abspath,
12371251881Speter                      apr_pool_t *result_pool,
12372251881Speter                      apr_pool_t *scratch_pool)
12373251881Speter{
12374251881Speter  svn_wc__db_wcroot_t *wcroot;
12375251881Speter  const char *local_relpath;
12376251881Speter  svn_wc__db_status_t status;
12377251881Speter  const char *op_root_relpath = NULL;
12378251881Speter  const char *moved_from_relpath = NULL;
12379251881Speter  const char *moved_from_op_root_relpath = NULL;
12380251881Speter  int moved_from_op_depth = -1;
12381251881Speter
12382251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12383251881Speter
12384251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12385251881Speter                              local_abspath, scratch_pool, scratch_pool));
12386251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12387251881Speter
12388251881Speter  SVN_ERR(scan_addition(&status,
12389251881Speter                        op_root_abspath
12390251881Speter                                ? &op_root_relpath
12391251881Speter                                : NULL,
12392251881Speter                        NULL, NULL,
12393251881Speter                        NULL, NULL, NULL,
12394251881Speter                        moved_from_abspath
12395251881Speter                            ? &moved_from_relpath
12396251881Speter                            : NULL,
12397251881Speter                        (op_root_moved_from_abspath
12398251881Speter                         || moved_from_delete_abspath)
12399251881Speter                            ? &moved_from_op_root_relpath
12400251881Speter                            : NULL,
12401251881Speter                        moved_from_delete_abspath
12402251881Speter                            ? &moved_from_op_depth
12403251881Speter                            : NULL,
12404251881Speter                        wcroot, local_relpath, scratch_pool, scratch_pool));
12405251881Speter
12406251881Speter  if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
12407251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
12408251881Speter                             _("Path '%s' was not moved here"),
12409251881Speter                             path_for_error_message(wcroot, local_relpath,
12410251881Speter                                                    scratch_pool));
12411251881Speter
12412251881Speter  if (op_root_abspath)
12413251881Speter    *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12414251881Speter                                       result_pool);
12415251881Speter
12416251881Speter  if (moved_from_abspath)
12417251881Speter    *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
12418251881Speter                                          result_pool);
12419251881Speter
12420251881Speter  if (op_root_moved_from_abspath)
12421251881Speter    *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
12422251881Speter                                                  moved_from_op_root_relpath,
12423251881Speter                                                  result_pool);
12424251881Speter
12425251881Speter  /* The deleted node is either where we moved from, or one of its ancestors */
12426251881Speter  if (moved_from_delete_abspath)
12427251881Speter    {
12428251881Speter      const char *tmp = moved_from_op_root_relpath;
12429251881Speter
12430251881Speter      SVN_ERR_ASSERT(moved_from_op_depth >= 0);
12431251881Speter
12432251881Speter      while (relpath_depth(tmp) > moved_from_op_depth)
12433251881Speter        tmp = svn_relpath_dirname(tmp, scratch_pool);
12434251881Speter
12435251881Speter      *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
12436251881Speter                                                   scratch_pool);
12437251881Speter    }
12438251881Speter
12439251881Speter  return SVN_NO_ERROR;
12440251881Speter}
12441251881Speter
12442251881Speter/* ###
12443251881Speter */
12444251881Speterstatic svn_error_t *
12445251881Speterfollow_moved_to(apr_array_header_t **moved_tos,
12446251881Speter                int op_depth,
12447251881Speter                const char *repos_path,
12448251881Speter                svn_revnum_t revision,
12449251881Speter                svn_wc__db_wcroot_t *wcroot,
12450251881Speter                const char *local_relpath,
12451251881Speter                apr_pool_t *result_pool,
12452251881Speter                apr_pool_t *scratch_pool)
12453251881Speter{
12454251881Speter  svn_sqlite__stmt_t *stmt;
12455251881Speter  svn_boolean_t have_row;
12456251881Speter  int working_op_depth;
12457251881Speter  const char *ancestor_relpath, *node_moved_to = NULL;
12458251881Speter  int i;
12459251881Speter
12460251881Speter  SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
12461251881Speter
12462251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12463251881Speter                                    STMT_SELECT_OP_DEPTH_MOVED_TO));
12464251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
12465251881Speter                            op_depth));
12466251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12467251881Speter  if (have_row)
12468251881Speter    {
12469251881Speter      working_op_depth = svn_sqlite__column_int(stmt, 0);
12470251881Speter      node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
12471251881Speter      if (!repos_path)
12472251881Speter        {
12473251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
12474251881Speter          if (!have_row || svn_sqlite__column_revnum(stmt, 0))
12475251881Speter            return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
12476251881Speter                                     svn_sqlite__reset(stmt),
12477251881Speter                                     _("The base node '%s' was not found."),
12478251881Speter                                     path_for_error_message(wcroot,
12479251881Speter                                                            local_relpath,
12480251881Speter                                                            scratch_pool));
12481251881Speter          repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12482251881Speter          revision = svn_sqlite__column_revnum(stmt, 3);
12483251881Speter        }
12484251881Speter    }
12485251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
12486251881Speter
12487251881Speter  if (node_moved_to)
12488251881Speter    {
12489251881Speter      svn_boolean_t have_row2;
12490251881Speter
12491251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12492251881Speter                                        STMT_SELECT_MOVED_HERE));
12493251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12494251881Speter                                relpath_depth(node_moved_to)));
12495251881Speter      SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12496251881Speter      if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12497251881Speter          || revision != svn_sqlite__column_revnum(stmt, 3)
12498251881Speter          || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12499251881Speter        node_moved_to = NULL;
12500251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12501251881Speter    }
12502251881Speter
12503251881Speter  if (node_moved_to)
12504251881Speter    {
12505251881Speter      struct svn_wc__db_moved_to_t *moved_to;
12506251881Speter
12507251881Speter      moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12508251881Speter      moved_to->op_depth = working_op_depth;
12509251881Speter      moved_to->local_relpath = node_moved_to;
12510251881Speter      APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12511251881Speter    }
12512251881Speter
12513251881Speter  /* A working row with moved_to, or no working row, and we are done. */
12514251881Speter  if (node_moved_to || !have_row)
12515251881Speter    return SVN_NO_ERROR;
12516251881Speter
12517251881Speter  /* Need to handle being moved via an ancestor. */
12518251881Speter  ancestor_relpath = local_relpath;
12519251881Speter  for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12520251881Speter    {
12521251881Speter      const char *ancestor_moved_to;
12522251881Speter
12523251881Speter      ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12524251881Speter
12525251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12526251881Speter                                        STMT_SELECT_MOVED_TO));
12527251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12528251881Speter                                working_op_depth));
12529251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12530251881Speter      SVN_ERR_ASSERT(have_row);
12531251881Speter      ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12532251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12533251881Speter      if (ancestor_moved_to)
12534251881Speter        {
12535251881Speter          node_moved_to
12536251881Speter            = svn_relpath_join(ancestor_moved_to,
12537251881Speter                               svn_relpath_skip_ancestor(ancestor_relpath,
12538251881Speter                                                         local_relpath),
12539251881Speter                               result_pool);
12540251881Speter
12541251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12542251881Speter                                            STMT_SELECT_MOVED_HERE));
12543251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12544251881Speter                                    relpath_depth(ancestor_moved_to)));
12545251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
12546251881Speter          if (!have_row)
12547251881Speter            ancestor_moved_to = NULL;
12548251881Speter          else if (!svn_sqlite__column_int(stmt, 0))
12549251881Speter            {
12550251881Speter              svn_wc__db_status_t presence
12551251881Speter                = svn_sqlite__column_token(stmt, 1, presence_map);
12552251881Speter              if (presence != svn_wc__db_status_not_present)
12553251881Speter                ancestor_moved_to = NULL;
12554251881Speter              else
12555251881Speter                {
12556251881Speter                  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12557251881Speter                  if (!have_row && !svn_sqlite__column_int(stmt, 0))
12558251881Speter                    ancestor_moved_to = NULL;
12559251881Speter                }
12560251881Speter            }
12561251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
12562251881Speter          if (!ancestor_moved_to)
12563251881Speter            break;
12564251881Speter          /* verify repos_path points back? */
12565251881Speter        }
12566251881Speter      if (ancestor_moved_to)
12567251881Speter        {
12568251881Speter          struct svn_wc__db_moved_to_t *moved_to;
12569251881Speter
12570251881Speter          moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12571251881Speter          moved_to->op_depth = working_op_depth;
12572251881Speter          moved_to->local_relpath = node_moved_to;
12573251881Speter          APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12574251881Speter
12575251881Speter          SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12576251881Speter                                  repos_path, revision, wcroot, node_moved_to,
12577251881Speter                                  result_pool, scratch_pool));
12578251881Speter          break;
12579251881Speter        }
12580251881Speter    }
12581251881Speter
12582251881Speter  return SVN_NO_ERROR;
12583251881Speter}
12584251881Speter
12585251881Spetersvn_error_t *
12586251881Spetersvn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12587251881Speter                           svn_wc__db_t *db,
12588251881Speter                           const char *local_abspath,
12589251881Speter                           apr_pool_t *result_pool,
12590251881Speter                           apr_pool_t *scratch_pool)
12591251881Speter{
12592251881Speter  svn_wc__db_wcroot_t *wcroot;
12593251881Speter  const char *local_relpath;
12594251881Speter
12595251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12596251881Speter
12597251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12598251881Speter                              local_abspath, scratch_pool, scratch_pool));
12599251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12600251881Speter
12601251881Speter  *moved_tos = apr_array_make(result_pool, 0,
12602251881Speter                              sizeof(struct svn_wc__db_moved_to_t *));
12603251881Speter
12604251881Speter  /* ### Wrap in a transaction */
12605251881Speter  SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12606251881Speter                          wcroot, local_relpath,
12607251881Speter                          result_pool, scratch_pool));
12608251881Speter
12609251881Speter  /* ### Convert moved_to to abspath */
12610251881Speter
12611251881Speter  return SVN_NO_ERROR;
12612251881Speter}
12613251881Speter
12614251881Speter/* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12615251881Speter   examining the lowest working node above OP_DEPTH.  The output paths
12616251881Speter   are NULL if there is no move, otherwise:
12617251881Speter
12618251881Speter   *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12619251881Speter
12620251881Speter   *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12621251881Speter   the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12622251881Speter   if LOCAL_RELPATH is the root of the move.
12623251881Speter
12624251881Speter   *MOVE_SRC_ROOT_RELPATH: the root of the move source.  For moves
12625251881Speter   inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12626251881Speter
12627251881Speter   *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12628251881Speter   contains the move.  For moves inside deletes this is the root of
12629251881Speter   the delete, for other moves this is the root of the move.
12630251881Speter
12631251881Speter   Given a path A/B/C with A/B moved to X then for A/B/C
12632251881Speter
12633251881Speter     MOVE_DST_RELPATH is X/C
12634251881Speter     MOVE_DST_OP_ROOT_RELPATH is X
12635251881Speter     MOVE_SRC_ROOT_RELPATH is A/B
12636251881Speter     MOVE_SRC_OP_ROOT_RELPATH is A/B
12637251881Speter
12638251881Speter   If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12639251881Speter   and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12640251881Speter   changes to A.
12641251881Speter
12642251881Speter   ### Think about combining with scan_deletion?  Also with
12643251881Speter   ### scan_addition to get moved-to for replaces?  Do we need to
12644251881Speter   ### return the op-root of the move source, i.e. A/B in the example
12645251881Speter   ### above?  */
12646251881Spetersvn_error_t *
12647251881Spetersvn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12648251881Speter                             const char **move_dst_op_root_relpath,
12649251881Speter                             const char **move_src_root_relpath,
12650251881Speter                             const char **move_src_op_root_relpath,
12651251881Speter                             int op_depth,
12652251881Speter                             svn_wc__db_wcroot_t *wcroot,
12653251881Speter                             const char *local_relpath,
12654251881Speter                             apr_pool_t *result_pool,
12655251881Speter                             apr_pool_t *scratch_pool)
12656251881Speter{
12657251881Speter  svn_sqlite__stmt_t *stmt;
12658251881Speter  svn_boolean_t have_row;
12659251881Speter  int delete_op_depth;
12660251881Speter  const char *relpath = local_relpath;
12661251881Speter
12662251881Speter  *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12663251881Speter  *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12664251881Speter
12665251881Speter  do
12666251881Speter    {
12667251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12668251881Speter                                        STMT_SELECT_LOWEST_WORKING_NODE));
12669251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12670251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12671251881Speter      if (have_row)
12672251881Speter        {
12673251881Speter          delete_op_depth = svn_sqlite__column_int(stmt, 0);
12674251881Speter          *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12675251881Speter                                                              result_pool);
12676251881Speter          if (*move_dst_op_root_relpath)
12677251881Speter            *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12678251881Speter        }
12679251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12680251881Speter      if (!*move_dst_op_root_relpath)
12681251881Speter        relpath = svn_relpath_dirname(relpath, scratch_pool);
12682251881Speter    }
12683251881Speter  while (!*move_dst_op_root_relpath
12684251881Speter        && have_row && delete_op_depth <= relpath_depth(relpath));
12685251881Speter
12686251881Speter  if (*move_dst_op_root_relpath)
12687251881Speter    {
12688251881Speter      *move_dst_relpath
12689251881Speter        = svn_relpath_join(*move_dst_op_root_relpath,
12690251881Speter                           svn_relpath_skip_ancestor(relpath, local_relpath),
12691251881Speter                           result_pool);
12692251881Speter      while (delete_op_depth < relpath_depth(relpath))
12693251881Speter        relpath = svn_relpath_dirname(relpath, scratch_pool);
12694251881Speter      *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12695251881Speter    }
12696251881Speter
12697251881Speter  return SVN_NO_ERROR;
12698251881Speter}
12699251881Speter
12700251881Speter/* Public (within libsvn_wc) absolute path version of
12701251881Speter   svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12702251881Speter   BASE. */
12703251881Spetersvn_error_t *
12704251881Spetersvn_wc__db_base_moved_to(const char **move_dst_abspath,
12705251881Speter                         const char **move_dst_op_root_abspath,
12706251881Speter                         const char **move_src_root_abspath,
12707251881Speter                         const char **move_src_op_root_abspath,
12708251881Speter                         svn_wc__db_t *db,
12709251881Speter                         const char *local_abspath,
12710251881Speter                         apr_pool_t *result_pool,
12711251881Speter                         apr_pool_t *scratch_pool)
12712251881Speter{
12713251881Speter  svn_wc__db_wcroot_t *wcroot;
12714251881Speter  const char *local_relpath;
12715251881Speter  const char *move_dst_relpath, *move_dst_op_root_relpath;
12716251881Speter  const char *move_src_root_relpath, *move_src_op_root_relpath;
12717251881Speter
12718251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12719251881Speter
12720251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12721251881Speter                              local_abspath, scratch_pool, scratch_pool));
12722251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12723251881Speter
12724251881Speter  SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12725251881Speter                                                   &move_dst_op_root_relpath,
12726251881Speter                                                   &move_src_root_relpath,
12727251881Speter                                                   &move_src_op_root_relpath,
12728251881Speter                                                   0 /* BASE op-depth */,
12729251881Speter                                                   wcroot, local_relpath,
12730251881Speter                                                   scratch_pool, scratch_pool),
12731251881Speter                      wcroot);
12732251881Speter
12733251881Speter  if (move_dst_abspath)
12734251881Speter    *move_dst_abspath
12735251881Speter      = move_dst_relpath
12736251881Speter      ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12737251881Speter      : NULL;
12738251881Speter
12739251881Speter  if (move_dst_op_root_abspath)
12740251881Speter    *move_dst_op_root_abspath
12741251881Speter      = move_dst_op_root_relpath
12742251881Speter      ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12743251881Speter      : NULL;
12744251881Speter
12745251881Speter  if (move_src_root_abspath)
12746251881Speter    *move_src_root_abspath
12747251881Speter      = move_src_root_relpath
12748251881Speter      ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12749251881Speter      : NULL;
12750251881Speter
12751251881Speter  if (move_src_op_root_abspath)
12752251881Speter    *move_src_op_root_abspath
12753251881Speter      = move_src_op_root_relpath
12754251881Speter      ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12755251881Speter      : NULL;
12756251881Speter
12757251881Speter  return SVN_NO_ERROR;
12758251881Speter}
12759251881Speter
12760251881Spetersvn_error_t *
12761251881Spetersvn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12762251881Speter                         apr_int64_t *repos_id,
12763251881Speter                         apr_int64_t *wc_id,
12764251881Speter                         svn_wc__db_t *wc_db,
12765251881Speter                         const char *dir_abspath,
12766251881Speter                         const char *repos_root_url,
12767251881Speter                         const char *repos_uuid,
12768251881Speter                         apr_pool_t *scratch_pool)
12769251881Speter{
12770251881Speter  svn_wc__db_wcroot_t *wcroot;
12771251881Speter
12772251881Speter  /* Upgrade is inherently exclusive so specify exclusive locking. */
12773251881Speter  SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12774251881Speter                    repos_root_url, repos_uuid,
12775251881Speter                    SDB_FILE,
12776251881Speter                    NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12777251881Speter                    TRUE /* exclusive */,
12778251881Speter                    wc_db->state_pool, scratch_pool));
12779251881Speter
12780251881Speter  SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12781251881Speter                                       apr_pstrdup(wc_db->state_pool,
12782251881Speter                                                   dir_abspath),
12783251881Speter                                       *sdb, *wc_id, FORMAT_FROM_SDB,
12784251881Speter                                       FALSE /* auto-upgrade */,
12785251881Speter                                       FALSE /* enforce_empty_wq */,
12786251881Speter                                       wc_db->state_pool, scratch_pool));
12787251881Speter
12788251881Speter  /* The WCROOT is complete. Stash it into DB.  */
12789251881Speter  svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12790251881Speter
12791251881Speter  return SVN_NO_ERROR;
12792251881Speter}
12793251881Speter
12794251881Speter
12795251881Spetersvn_error_t *
12796251881Spetersvn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12797251881Speter                                   const char *dir_relpath,
12798251881Speter                                   apr_hash_t *cache_values,
12799251881Speter                                   apr_pool_t *scratch_pool)
12800251881Speter{
12801251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12802251881Speter  apr_int64_t wc_id;
12803251881Speter  apr_hash_index_t *hi;
12804251881Speter  svn_sqlite__stmt_t *stmt;
12805251881Speter
12806251881Speter  SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12807251881Speter
12808251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12809251881Speter                                    STMT_UPDATE_BASE_NODE_DAV_CACHE));
12810251881Speter
12811251881Speter  /* Iterate over all the wcprops, writing each one to the wc_db. */
12812251881Speter  for (hi = apr_hash_first(scratch_pool, cache_values);
12813251881Speter       hi;
12814251881Speter       hi = apr_hash_next(hi))
12815251881Speter    {
12816251881Speter      const char *name = svn__apr_hash_index_key(hi);
12817251881Speter      apr_hash_t *props = svn__apr_hash_index_val(hi);
12818251881Speter      const char *local_relpath;
12819251881Speter
12820251881Speter      svn_pool_clear(iterpool);
12821251881Speter
12822251881Speter      local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12823251881Speter
12824251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12825251881Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12826251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
12827251881Speter    }
12828251881Speter
12829251881Speter  svn_pool_destroy(iterpool);
12830251881Speter
12831251881Speter  return SVN_NO_ERROR;
12832251881Speter}
12833251881Speter
12834251881Speter
12835251881Spetersvn_error_t *
12836251881Spetersvn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12837251881Speter                               const char *dir_abspath,
12838251881Speter                               const char *local_relpath,
12839251881Speter                               apr_hash_t *base_props,
12840251881Speter                               apr_hash_t *revert_props,
12841251881Speter                               apr_hash_t *working_props,
12842251881Speter                               int original_format,
12843251881Speter                               apr_int64_t wc_id,
12844251881Speter                               apr_pool_t *scratch_pool)
12845251881Speter{
12846251881Speter  svn_sqlite__stmt_t *stmt;
12847251881Speter  svn_boolean_t have_row;
12848251881Speter  int top_op_depth = -1;
12849251881Speter  int below_op_depth = -1;
12850251881Speter  svn_wc__db_status_t top_presence;
12851251881Speter  svn_wc__db_status_t below_presence;
12852251881Speter  int affected_rows;
12853251881Speter
12854251881Speter  /* ### working_props: use set_props_txn.
12855251881Speter     ### if working_props == NULL, then skip. what if they equal the
12856251881Speter     ### pristine props? we should probably do the compare here.
12857251881Speter     ###
12858251881Speter     ### base props go into WORKING_NODE if avail, otherwise BASE.
12859251881Speter     ###
12860251881Speter     ### revert only goes into BASE. (and WORKING better be there!)
12861251881Speter
12862251881Speter     Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12863251881Speter     file was deleted, then a copy (potentially with props) was disallowed
12864251881Speter     and could not replace the deletion. An addition *could* be performed,
12865251881Speter     but that would never bring its own props.
12866251881Speter
12867251881Speter     1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12868251881Speter     bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12869251881Speter     construct a REVERT_PROPS if the target had no props. Thus, reverting
12870251881Speter     the delete/copy would see no REVERT_PROPS to restore, leaving the
12871251881Speter     props from the copy source intact, and appearing as if they are (now)
12872251881Speter     the base props for the previously-deleted file. (wc corruption)
12873251881Speter
12874251881Speter     1.4.6 ensured that an empty REVERT_PROPS would be established at all
12875251881Speter     times. See issue 2530, and r861670 as starting points.
12876251881Speter
12877251881Speter     We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12878251881Speter     the handling of our inputs, relative to the state of this node.
12879251881Speter  */
12880251881Speter
12881251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12882251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12883251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12884251881Speter  if (have_row)
12885251881Speter    {
12886251881Speter      top_op_depth = svn_sqlite__column_int(stmt, 0);
12887251881Speter      top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12888251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12889251881Speter      if (have_row)
12890251881Speter        {
12891251881Speter          below_op_depth = svn_sqlite__column_int(stmt, 0);
12892251881Speter          below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12893251881Speter        }
12894251881Speter    }
12895251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
12896251881Speter
12897251881Speter  /* Detect the buggy scenario described above. We cannot upgrade this
12898251881Speter     working copy if we have no idea where BASE_PROPS should go.  */
12899251881Speter  if (original_format > SVN_WC__NO_REVERT_FILES
12900251881Speter      && revert_props == NULL
12901251881Speter      && top_op_depth != -1
12902251881Speter      && top_presence == svn_wc__db_status_normal
12903251881Speter      && below_op_depth != -1
12904251881Speter      && below_presence != svn_wc__db_status_not_present)
12905251881Speter    {
12906251881Speter      /* There should be REVERT_PROPS, so it appears that we just ran into
12907251881Speter         the described bug. Sigh.  */
12908251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12909251881Speter                               _("The properties of '%s' are in an "
12910251881Speter                                 "indeterminate state and cannot be "
12911251881Speter                                 "upgraded. See issue #2530."),
12912251881Speter                               svn_dirent_local_style(
12913251881Speter                                 svn_dirent_join(dir_abspath, local_relpath,
12914251881Speter                                                 scratch_pool), scratch_pool));
12915251881Speter    }
12916251881Speter
12917251881Speter  /* Need at least one row, or two rows if there are revert props */
12918251881Speter  if (top_op_depth == -1
12919251881Speter      || (below_op_depth == -1 && revert_props))
12920251881Speter    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12921251881Speter                             _("Insufficient NODES rows for '%s'"),
12922251881Speter                             svn_dirent_local_style(
12923251881Speter                               svn_dirent_join(dir_abspath, local_relpath,
12924251881Speter                                               scratch_pool), scratch_pool));
12925251881Speter
12926251881Speter  /* one row, base props only: upper row gets base props
12927251881Speter     two rows, base props only: lower row gets base props
12928251881Speter     two rows, revert props only: lower row gets revert props
12929251881Speter     two rows, base and revert props: upper row gets base, lower gets revert */
12930251881Speter
12931251881Speter
12932251881Speter  if (revert_props || below_op_depth == -1)
12933251881Speter    {
12934251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12935251881Speter                                        STMT_UPDATE_NODE_PROPS));
12936251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12937251881Speter                                wc_id, local_relpath, top_op_depth));
12938251881Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12939251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12940251881Speter
12941251881Speter      SVN_ERR_ASSERT(affected_rows == 1);
12942251881Speter    }
12943251881Speter
12944251881Speter  if (below_op_depth != -1)
12945251881Speter    {
12946251881Speter      apr_hash_t *props = revert_props ? revert_props : base_props;
12947251881Speter
12948251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12949251881Speter                                        STMT_UPDATE_NODE_PROPS));
12950251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12951251881Speter                                wc_id, local_relpath, below_op_depth));
12952251881Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
12953251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12954251881Speter
12955251881Speter      SVN_ERR_ASSERT(affected_rows == 1);
12956251881Speter    }
12957251881Speter
12958251881Speter  /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE.  */
12959251881Speter  if (working_props != NULL
12960251881Speter      && base_props != NULL)
12961251881Speter    {
12962251881Speter      apr_array_header_t *diffs;
12963251881Speter
12964251881Speter      SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
12965251881Speter
12966251881Speter      if (diffs->nelts == 0)
12967251881Speter        working_props = NULL; /* No differences */
12968251881Speter    }
12969251881Speter
12970251881Speter  if (working_props != NULL)
12971251881Speter    {
12972251881Speter      SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
12973251881Speter                               sdb, scratch_pool));
12974251881Speter    }
12975251881Speter
12976251881Speter  return SVN_NO_ERROR;
12977251881Speter}
12978251881Speter
12979251881Spetersvn_error_t *
12980251881Spetersvn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
12981251881Speter                                   const char *local_abspath,
12982251881Speter                                   svn_node_kind_t kind,
12983251881Speter                                   const char *parent_abspath,
12984251881Speter                                   const char *def_local_abspath,
12985251881Speter                                   const char *repos_relpath,
12986251881Speter                                   const char *repos_root_url,
12987251881Speter                                   const char *repos_uuid,
12988251881Speter                                   svn_revnum_t def_peg_revision,
12989251881Speter                                   svn_revnum_t def_revision,
12990251881Speter                                   apr_pool_t *scratch_pool)
12991251881Speter{
12992251881Speter  svn_wc__db_wcroot_t *wcroot;
12993251881Speter  const char *def_local_relpath;
12994251881Speter  svn_sqlite__stmt_t *stmt;
12995251881Speter  svn_boolean_t have_row;
12996251881Speter  apr_int64_t repos_id;
12997251881Speter
12998251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12999251881Speter
13000251881Speter  /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13001251881Speter   * WC, i.e. where the svn:externals prop is set. The external target path
13002251881Speter   * itself may be "hidden behind" other working copies. */
13003251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13004251881Speter                                                db, def_local_abspath,
13005251881Speter                                                scratch_pool, scratch_pool));
13006251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13007251881Speter
13008251881Speter
13009251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13010251881Speter                                    STMT_SELECT_REPOSITORY));
13011251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13012251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13013251881Speter
13014251881Speter  if (have_row)
13015251881Speter    repos_id = svn_sqlite__column_int64(stmt, 0);
13016251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
13017251881Speter
13018251881Speter  if (!have_row)
13019251881Speter    {
13020251881Speter      /* Need to set up a new repository row. */
13021251881Speter      SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13022251881Speter                              wcroot->sdb, scratch_pool));
13023251881Speter    }
13024251881Speter
13025251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13026251881Speter                                    STMT_INSERT_EXTERNAL));
13027251881Speter
13028251881Speter  /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13029251881Speter   * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13030251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13031251881Speter                            wcroot->wc_id,
13032251881Speter                            svn_dirent_skip_ancestor(wcroot->abspath,
13033251881Speter                                                     local_abspath),
13034251881Speter                            svn_dirent_skip_ancestor(wcroot->abspath,
13035251881Speter                                                     parent_abspath),
13036251881Speter                            "normal",
13037251881Speter                            kind_map, kind,
13038251881Speter                            def_local_relpath,
13039251881Speter                            repos_id,
13040251881Speter                            repos_relpath));
13041251881Speter
13042251881Speter  if (SVN_IS_VALID_REVNUM(def_peg_revision))
13043251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13044251881Speter
13045251881Speter  if (SVN_IS_VALID_REVNUM(def_revision))
13046251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13047251881Speter
13048251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
13049251881Speter
13050251881Speter  return SVN_NO_ERROR;
13051251881Speter}
13052251881Speter
13053251881Spetersvn_error_t *
13054251881Spetersvn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
13055251881Speter                                svn_sqlite__db_t *sdb,
13056251881Speter                                const char *repos_root_url,
13057251881Speter                                apr_pool_t *scratch_pool)
13058251881Speter{
13059251881Speter  svn_sqlite__stmt_t *stmt;
13060251881Speter  svn_boolean_t have_row;
13061251881Speter
13062251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
13063251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13064251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13065251881Speter
13066251881Speter  if (!have_row)
13067251881Speter    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
13068251881Speter                             _("Repository '%s' not found in the database"),
13069251881Speter                             repos_root_url);
13070251881Speter
13071251881Speter  *repos_id = svn_sqlite__column_int64(stmt, 0);
13072251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
13073251881Speter}
13074251881Speter
13075251881Speter
13076251881Spetersvn_error_t *
13077251881Spetersvn_wc__db_wq_add(svn_wc__db_t *db,
13078251881Speter                  const char *wri_abspath,
13079251881Speter                  const svn_skel_t *work_item,
13080251881Speter                  apr_pool_t *scratch_pool)
13081251881Speter{
13082251881Speter  svn_wc__db_wcroot_t *wcroot;
13083251881Speter  const char *local_relpath;
13084251881Speter
13085251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13086251881Speter
13087251881Speter  /* Quick exit, if there are no work items to queue up.  */
13088251881Speter  if (work_item == NULL)
13089251881Speter    return SVN_NO_ERROR;
13090251881Speter
13091251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13092251881Speter                              wri_abspath, scratch_pool, scratch_pool));
13093251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13094251881Speter
13095251881Speter  /* Add the work item(s) to the WORK_QUEUE.  */
13096251881Speter  return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13097251881Speter                                        scratch_pool));
13098251881Speter}
13099251881Speter
13100251881Speter/* The body of svn_wc__db_wq_fetch_next().
13101251881Speter */
13102251881Speterstatic svn_error_t *
13103251881Speterwq_fetch_next(apr_uint64_t *id,
13104251881Speter              svn_skel_t **work_item,
13105251881Speter              svn_wc__db_wcroot_t *wcroot,
13106251881Speter              const char *local_relpath,
13107251881Speter              apr_uint64_t completed_id,
13108251881Speter              apr_pool_t *result_pool,
13109251881Speter              apr_pool_t *scratch_pool)
13110251881Speter{
13111251881Speter  svn_sqlite__stmt_t *stmt;
13112251881Speter  svn_boolean_t have_row;
13113251881Speter
13114251881Speter  if (completed_id != 0)
13115251881Speter    {
13116251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13117251881Speter                                        STMT_DELETE_WORK_ITEM));
13118251881Speter      SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13119251881Speter
13120251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
13121251881Speter    }
13122251881Speter
13123251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13124251881Speter                                    STMT_SELECT_WORK_ITEM));
13125251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13126251881Speter
13127251881Speter  if (!have_row)
13128251881Speter    {
13129251881Speter      *id = 0;
13130251881Speter      *work_item = NULL;
13131251881Speter    }
13132251881Speter  else
13133251881Speter    {
13134251881Speter      apr_size_t len;
13135251881Speter      const void *val;
13136251881Speter
13137251881Speter      *id = svn_sqlite__column_int64(stmt, 0);
13138251881Speter
13139251881Speter      val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13140251881Speter
13141251881Speter      *work_item = svn_skel__parse(val, len, result_pool);
13142251881Speter    }
13143251881Speter
13144251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
13145251881Speter}
13146251881Speter
13147251881Spetersvn_error_t *
13148251881Spetersvn_wc__db_wq_fetch_next(apr_uint64_t *id,
13149251881Speter                         svn_skel_t **work_item,
13150251881Speter                         svn_wc__db_t *db,
13151251881Speter                         const char *wri_abspath,
13152251881Speter                         apr_uint64_t completed_id,
13153251881Speter                         apr_pool_t *result_pool,
13154251881Speter                         apr_pool_t *scratch_pool)
13155251881Speter{
13156251881Speter  svn_wc__db_wcroot_t *wcroot;
13157251881Speter  const char *local_relpath;
13158251881Speter
13159251881Speter  SVN_ERR_ASSERT(id != NULL);
13160251881Speter  SVN_ERR_ASSERT(work_item != NULL);
13161251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13162251881Speter
13163251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13164251881Speter                              wri_abspath, scratch_pool, scratch_pool));
13165251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13166251881Speter
13167251881Speter  SVN_WC__DB_WITH_TXN(
13168251881Speter    wq_fetch_next(id, work_item,
13169251881Speter                  wcroot, local_relpath, completed_id,
13170251881Speter                  result_pool, scratch_pool),
13171251881Speter    wcroot);
13172251881Speter
13173251881Speter  return SVN_NO_ERROR;
13174251881Speter}
13175251881Speter
13176251881Speter/* Records timestamp and date for one or more files in wcroot */
13177251881Speterstatic svn_error_t *
13178251881Speterwq_record(svn_wc__db_wcroot_t *wcroot,
13179251881Speter          apr_hash_t *record_map,
13180251881Speter          apr_pool_t *scratch_pool)
13181251881Speter{
13182251881Speter  apr_hash_index_t *hi;
13183251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13184251881Speter
13185251881Speter  for (hi = apr_hash_first(scratch_pool, record_map); hi;
13186251881Speter       hi = apr_hash_next(hi))
13187251881Speter    {
13188251881Speter      const char *local_abspath = svn__apr_hash_index_key(hi);
13189251881Speter      const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
13190251881Speter      const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13191251881Speter                                                           local_abspath);
13192251881Speter
13193251881Speter      svn_pool_clear(iterpool);
13194251881Speter
13195251881Speter      if (! local_relpath)
13196251881Speter        continue;
13197251881Speter
13198251881Speter      SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13199251881Speter                                 dirent->filesize, dirent->mtime,
13200251881Speter                                 iterpool));
13201251881Speter    }
13202251881Speter
13203251881Speter  svn_pool_destroy(iterpool);
13204251881Speter  return SVN_NO_ERROR;
13205251881Speter}
13206251881Speter
13207251881Spetersvn_error_t *
13208251881Spetersvn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13209251881Speter                                    svn_skel_t **work_item,
13210251881Speter                                    svn_wc__db_t *db,
13211251881Speter                                    const char *wri_abspath,
13212251881Speter                                    apr_uint64_t completed_id,
13213251881Speter                                    apr_hash_t *record_map,
13214251881Speter                                    apr_pool_t *result_pool,
13215251881Speter                                    apr_pool_t *scratch_pool)
13216251881Speter{
13217251881Speter  svn_wc__db_wcroot_t *wcroot;
13218251881Speter  const char *local_relpath;
13219251881Speter
13220251881Speter  SVN_ERR_ASSERT(id != NULL);
13221251881Speter  SVN_ERR_ASSERT(work_item != NULL);
13222251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13223251881Speter
13224251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13225251881Speter                              wri_abspath, scratch_pool, scratch_pool));
13226251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13227251881Speter
13228251881Speter  SVN_WC__DB_WITH_TXN(
13229251881Speter    svn_error_compose_create(
13230251881Speter            wq_fetch_next(id, work_item,
13231251881Speter                          wcroot, local_relpath, completed_id,
13232251881Speter                          result_pool, scratch_pool),
13233251881Speter            wq_record(wcroot, record_map, scratch_pool)),
13234251881Speter    wcroot);
13235251881Speter
13236251881Speter  return SVN_NO_ERROR;
13237251881Speter}
13238251881Speter
13239251881Speter
13240251881Speter
13241251881Speter/* ### temporary API. remove before release.  */
13242251881Spetersvn_error_t *
13243251881Spetersvn_wc__db_temp_get_format(int *format,
13244251881Speter                           svn_wc__db_t *db,
13245251881Speter                           const char *local_dir_abspath,
13246251881Speter                           apr_pool_t *scratch_pool)
13247251881Speter{
13248251881Speter  svn_wc__db_wcroot_t *wcroot;
13249251881Speter  const char *local_relpath;
13250251881Speter  svn_error_t *err;
13251251881Speter
13252251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13253251881Speter  /* ### assert that we were passed a directory?  */
13254251881Speter
13255251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13256251881Speter                                local_dir_abspath, scratch_pool, scratch_pool);
13257251881Speter
13258251881Speter  /* If we hit an error examining this directory, then declare this
13259251881Speter     directory to not be a working copy.  */
13260251881Speter  if (err)
13261251881Speter    {
13262251881Speter      if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13263251881Speter        return svn_error_trace(err);
13264251881Speter      svn_error_clear(err);
13265251881Speter
13266251881Speter      /* Remap the returned error.  */
13267251881Speter      *format = 0;
13268251881Speter      return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13269251881Speter                               _("'%s' is not a working copy"),
13270251881Speter                               svn_dirent_local_style(local_dir_abspath,
13271251881Speter                                                      scratch_pool));
13272251881Speter    }
13273251881Speter
13274251881Speter  SVN_ERR_ASSERT(wcroot != NULL);
13275251881Speter  SVN_ERR_ASSERT(wcroot->format >= 1);
13276251881Speter
13277251881Speter  *format = wcroot->format;
13278251881Speter
13279251881Speter  return SVN_NO_ERROR;
13280251881Speter}
13281251881Speter
13282251881Speter/* ### temporary API. remove before release.  */
13283251881Spetersvn_wc_adm_access_t *
13284251881Spetersvn_wc__db_temp_get_access(svn_wc__db_t *db,
13285251881Speter                           const char *local_dir_abspath,
13286251881Speter                           apr_pool_t *scratch_pool)
13287251881Speter{
13288251881Speter  const char *local_relpath;
13289251881Speter  svn_wc__db_wcroot_t *wcroot;
13290251881Speter  svn_error_t *err;
13291251881Speter
13292251881Speter  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13293251881Speter
13294251881Speter  /* ### we really need to assert that we were passed a directory. sometimes
13295251881Speter     ### adm_retrieve_internal is asked about a file, and then it asks us
13296251881Speter     ### for an access baton for it. we should definitely return NULL, but
13297251881Speter     ### ideally: the caller would never ask us about a non-directory.  */
13298251881Speter
13299251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13300251881Speter                            db, local_dir_abspath, scratch_pool, scratch_pool);
13301251881Speter  if (err)
13302251881Speter    {
13303251881Speter      svn_error_clear(err);
13304251881Speter      return NULL;
13305251881Speter    }
13306251881Speter
13307251881Speter  if (!wcroot)
13308251881Speter    return NULL;
13309251881Speter
13310251881Speter  return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13311251881Speter}
13312251881Speter
13313251881Speter
13314251881Speter/* ### temporary API. remove before release.  */
13315251881Spetervoid
13316251881Spetersvn_wc__db_temp_set_access(svn_wc__db_t *db,
13317251881Speter                           const char *local_dir_abspath,
13318251881Speter                           svn_wc_adm_access_t *adm_access,
13319251881Speter                           apr_pool_t *scratch_pool)
13320251881Speter{
13321251881Speter  const char *local_relpath;
13322251881Speter  svn_wc__db_wcroot_t *wcroot;
13323251881Speter  svn_error_t *err;
13324251881Speter
13325251881Speter  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13326251881Speter  /* ### assert that we were passed a directory?  */
13327251881Speter
13328251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13329251881Speter                            db, local_dir_abspath, scratch_pool, scratch_pool);
13330251881Speter  if (err)
13331251881Speter    {
13332251881Speter      /* We don't even have a wcroot, so just bail. */
13333251881Speter      svn_error_clear(err);
13334251881Speter      return;
13335251881Speter    }
13336251881Speter
13337251881Speter  /* Better not override something already there.  */
13338251881Speter  SVN_ERR_ASSERT_NO_RETURN(
13339251881Speter    svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13340251881Speter  );
13341251881Speter  svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13342251881Speter}
13343251881Speter
13344251881Speter
13345251881Speter/* ### temporary API. remove before release.  */
13346251881Spetersvn_error_t *
13347251881Spetersvn_wc__db_temp_close_access(svn_wc__db_t *db,
13348251881Speter                             const char *local_dir_abspath,
13349251881Speter                             svn_wc_adm_access_t *adm_access,
13350251881Speter                             apr_pool_t *scratch_pool)
13351251881Speter{
13352251881Speter  const char *local_relpath;
13353251881Speter  svn_wc__db_wcroot_t *wcroot;
13354251881Speter
13355251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13356251881Speter  /* ### assert that we were passed a directory?  */
13357251881Speter
13358251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13359251881Speter                              local_dir_abspath, scratch_pool, scratch_pool));
13360251881Speter  svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13361251881Speter
13362251881Speter  return SVN_NO_ERROR;
13363251881Speter}
13364251881Speter
13365251881Speter
13366251881Speter/* ### temporary API. remove before release.  */
13367251881Spetervoid
13368251881Spetersvn_wc__db_temp_clear_access(svn_wc__db_t *db,
13369251881Speter                             const char *local_dir_abspath,
13370251881Speter                             apr_pool_t *scratch_pool)
13371251881Speter{
13372251881Speter  const char *local_relpath;
13373251881Speter  svn_wc__db_wcroot_t *wcroot;
13374251881Speter  svn_error_t *err;
13375251881Speter
13376251881Speter  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13377251881Speter  /* ### assert that we were passed a directory?  */
13378251881Speter
13379251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13380251881Speter                            db, local_dir_abspath, scratch_pool, scratch_pool);
13381251881Speter  if (err)
13382251881Speter    {
13383251881Speter      svn_error_clear(err);
13384251881Speter      return;
13385251881Speter    }
13386251881Speter
13387251881Speter  svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13388251881Speter}
13389251881Speter
13390251881Speter
13391251881Speterapr_hash_t *
13392251881Spetersvn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13393251881Speter                               apr_pool_t *result_pool)
13394251881Speter{
13395251881Speter  apr_hash_t *result = apr_hash_make(result_pool);
13396251881Speter  apr_hash_index_t *hi;
13397251881Speter
13398251881Speter  for (hi = apr_hash_first(result_pool, db->dir_data);
13399251881Speter       hi;
13400251881Speter       hi = apr_hash_next(hi))
13401251881Speter    {
13402251881Speter      const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
13403251881Speter
13404251881Speter      /* This is highly redundant, 'cause the same WCROOT will appear many
13405251881Speter         times in dir_data. */
13406251881Speter      result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13407251881Speter    }
13408251881Speter
13409251881Speter  return result;
13410251881Speter}
13411251881Speter
13412251881Speter
13413251881Spetersvn_error_t *
13414251881Spetersvn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13415251881Speter                           svn_wc__db_t *db,
13416251881Speter                           const char *local_dir_abspath,
13417251881Speter                           apr_pool_t *scratch_pool)
13418251881Speter{
13419251881Speter  svn_wc__db_wcroot_t *wcroot;
13420251881Speter  const char *local_relpath;
13421251881Speter
13422251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13423251881Speter
13424251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13425251881Speter                            local_dir_abspath, scratch_pool, scratch_pool));
13426251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13427251881Speter
13428251881Speter  *sdb = wcroot->sdb;
13429251881Speter
13430251881Speter  return SVN_NO_ERROR;
13431251881Speter}
13432251881Speter
13433251881Speter
13434251881Spetersvn_error_t *
13435251881Spetersvn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13436251881Speter                                 svn_wc__db_t *db,
13437251881Speter                                 const char *local_abspath,
13438251881Speter                                 apr_pool_t *result_pool,
13439251881Speter                                 apr_pool_t *scratch_pool)
13440251881Speter{
13441251881Speter  svn_wc__db_wcroot_t *wcroot;
13442251881Speter  const char *local_relpath;
13443251881Speter  svn_sqlite__stmt_t *stmt;
13444251881Speter  svn_boolean_t have_row;
13445251881Speter  apr_array_header_t *new_victims;
13446251881Speter
13447251881Speter  /* The parent should be a working copy directory. */
13448251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13449251881Speter                              local_abspath, scratch_pool, scratch_pool));
13450251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13451251881Speter
13452251881Speter  /* ### This will be much easier once we have all conflicts in one
13453251881Speter         field of actual*/
13454251881Speter
13455251881Speter  /* Look for text, tree and property conflicts in ACTUAL */
13456251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13457251881Speter                                    STMT_SELECT_CONFLICT_VICTIMS));
13458251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13459251881Speter
13460251881Speter  new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13461251881Speter
13462251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13463251881Speter  while (have_row)
13464251881Speter    {
13465251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13466251881Speter
13467251881Speter      APR_ARRAY_PUSH(new_victims, const char *) =
13468251881Speter                            svn_relpath_basename(child_relpath, result_pool);
13469251881Speter
13470251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13471251881Speter    }
13472251881Speter
13473251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
13474251881Speter
13475251881Speter  *victims = new_victims;
13476251881Speter  return SVN_NO_ERROR;
13477251881Speter}
13478251881Speter
13479251881Speter/* The body of svn_wc__db_get_conflict_marker_files().
13480251881Speter */
13481251881Speterstatic svn_error_t *
13482251881Speterget_conflict_marker_files(apr_hash_t **marker_files_p,
13483251881Speter                          svn_wc__db_wcroot_t *wcroot,
13484251881Speter                          const char *local_relpath,
13485251881Speter                          svn_wc__db_t *db,
13486251881Speter                          apr_pool_t *result_pool,
13487251881Speter                          apr_pool_t *scratch_pool)
13488251881Speter{
13489251881Speter  svn_sqlite__stmt_t *stmt;
13490251881Speter  svn_boolean_t have_row;
13491251881Speter  apr_hash_t *marker_files = apr_hash_make(result_pool);
13492251881Speter
13493251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13494251881Speter                                    STMT_SELECT_ACTUAL_NODE));
13495251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13496251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13497251881Speter
13498251881Speter  if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13499251881Speter    {
13500251881Speter      apr_size_t len;
13501251881Speter      const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13502251881Speter      svn_skel_t *conflicts;
13503251881Speter      const apr_array_header_t *markers;
13504251881Speter      int i;
13505251881Speter
13506251881Speter      conflicts = svn_skel__parse(data, len, scratch_pool);
13507251881Speter
13508251881Speter      /* ### ADD markers to *marker_files */
13509251881Speter      SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13510251881Speter                                            conflicts,
13511251881Speter                                            result_pool, scratch_pool));
13512251881Speter
13513251881Speter      for (i = 0; markers && (i < markers->nelts); i++)
13514251881Speter        {
13515251881Speter          const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13516251881Speter
13517251881Speter          svn_hash_sets(marker_files, marker_abspath, "");
13518251881Speter        }
13519251881Speter    }
13520251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
13521251881Speter
13522251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13523251881Speter                                    STMT_SELECT_CONFLICT_VICTIMS));
13524251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13525251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13526251881Speter
13527251881Speter  while (have_row)
13528251881Speter    {
13529251881Speter      apr_size_t len;
13530251881Speter      const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13531251881Speter
13532251881Speter      const apr_array_header_t *markers;
13533251881Speter      int i;
13534251881Speter
13535251881Speter      if (data)
13536251881Speter        {
13537251881Speter          svn_skel_t *conflicts;
13538251881Speter          conflicts = svn_skel__parse(data, len, scratch_pool);
13539251881Speter
13540251881Speter          SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13541251881Speter                                                conflicts,
13542251881Speter                                                result_pool, scratch_pool));
13543251881Speter
13544251881Speter          for (i = 0; markers && (i < markers->nelts); i++)
13545251881Speter            {
13546251881Speter              const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13547251881Speter
13548251881Speter              svn_hash_sets(marker_files, marker_abspath, "");
13549251881Speter            }
13550251881Speter        }
13551251881Speter
13552251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13553251881Speter    }
13554251881Speter
13555251881Speter  if (apr_hash_count(marker_files))
13556251881Speter    *marker_files_p = marker_files;
13557251881Speter  else
13558251881Speter    *marker_files_p = NULL;
13559251881Speter
13560251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
13561251881Speter}
13562251881Speter
13563251881Spetersvn_error_t *
13564251881Spetersvn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13565251881Speter                                     svn_wc__db_t *db,
13566251881Speter                                     const char *local_abspath,
13567251881Speter                                     apr_pool_t *result_pool,
13568251881Speter                                     apr_pool_t *scratch_pool)
13569251881Speter{
13570251881Speter  svn_wc__db_wcroot_t *wcroot;
13571251881Speter  const char *local_relpath;
13572251881Speter
13573251881Speter  /* The parent should be a working copy directory. */
13574251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13575251881Speter                              local_abspath, scratch_pool, scratch_pool));
13576251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13577251881Speter
13578251881Speter  SVN_WC__DB_WITH_TXN(
13579251881Speter    get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13580251881Speter                              result_pool, scratch_pool),
13581251881Speter    wcroot);
13582251881Speter
13583251881Speter  return SVN_NO_ERROR;
13584251881Speter}
13585251881Speter
13586251881Speter
13587251881Spetersvn_error_t *
13588251881Spetersvn_wc__db_read_conflict(svn_skel_t **conflict,
13589251881Speter                         svn_wc__db_t *db,
13590251881Speter                         const char *local_abspath,
13591251881Speter                         apr_pool_t *result_pool,
13592251881Speter                         apr_pool_t *scratch_pool)
13593251881Speter{
13594251881Speter  svn_wc__db_wcroot_t *wcroot;
13595251881Speter  const char *local_relpath;
13596251881Speter
13597251881Speter  /* The parent should be a working copy directory. */
13598251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13599251881Speter                              local_abspath, scratch_pool, scratch_pool));
13600251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13601251881Speter
13602251881Speter  return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13603251881Speter                                                           local_relpath,
13604251881Speter                                                           result_pool,
13605251881Speter                                                           scratch_pool));
13606251881Speter}
13607251881Speter
13608251881Spetersvn_error_t *
13609251881Spetersvn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13610251881Speter                                  svn_wc__db_wcroot_t *wcroot,
13611251881Speter                                  const char *local_relpath,
13612251881Speter                                  apr_pool_t *result_pool,
13613251881Speter                                  apr_pool_t *scratch_pool)
13614251881Speter{
13615251881Speter  svn_sqlite__stmt_t *stmt;
13616251881Speter  svn_boolean_t have_row;
13617251881Speter
13618251881Speter  /* Check if we have a conflict in ACTUAL */
13619251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13620251881Speter                                    STMT_SELECT_ACTUAL_NODE));
13621251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13622251881Speter
13623251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13624251881Speter
13625251881Speter  if (! have_row)
13626251881Speter    {
13627251881Speter      /* Do this while stmt is still open to avoid closing the sqlite
13628251881Speter         transaction and then reopening. */
13629251881Speter      svn_sqlite__stmt_t *stmt_node;
13630251881Speter      svn_error_t *err;
13631251881Speter
13632251881Speter      err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13633251881Speter                                      STMT_SELECT_NODE_INFO);
13634251881Speter
13635251881Speter      if (err)
13636251881Speter        stmt_node = NULL;
13637251881Speter      else
13638251881Speter        err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13639251881Speter                                local_relpath);
13640251881Speter
13641251881Speter      if (!err)
13642251881Speter        err = svn_sqlite__step(&have_row, stmt_node);
13643251881Speter
13644251881Speter      if (stmt_node)
13645251881Speter        err = svn_error_compose_create(err,
13646251881Speter                                       svn_sqlite__reset(stmt_node));
13647251881Speter
13648251881Speter      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13649251881Speter
13650251881Speter      if (have_row)
13651251881Speter        {
13652251881Speter          *conflict = NULL;
13653251881Speter          return SVN_NO_ERROR;
13654251881Speter        }
13655251881Speter
13656251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13657251881Speter                               _("The node '%s' was not found."),
13658251881Speter                                   path_for_error_message(wcroot,
13659251881Speter                                                          local_relpath,
13660251881Speter                                                          scratch_pool));
13661251881Speter    }
13662251881Speter
13663251881Speter  {
13664251881Speter    apr_size_t cfl_len;
13665251881Speter    const void *cfl_data;
13666251881Speter
13667251881Speter    /* svn_skel__parse doesn't copy data, so store in result_pool */
13668251881Speter    cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13669251881Speter
13670251881Speter    if (cfl_data)
13671251881Speter      *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13672251881Speter    else
13673251881Speter      *conflict = NULL;
13674251881Speter
13675251881Speter    return svn_error_trace(svn_sqlite__reset(stmt));
13676251881Speter  }
13677251881Speter}
13678251881Speter
13679251881Speter
13680251881Spetersvn_error_t *
13681251881Spetersvn_wc__db_read_kind(svn_node_kind_t *kind,
13682251881Speter                     svn_wc__db_t *db,
13683251881Speter                     const char *local_abspath,
13684251881Speter                     svn_boolean_t allow_missing,
13685251881Speter                     svn_boolean_t show_deleted,
13686251881Speter                     svn_boolean_t show_hidden,
13687251881Speter                     apr_pool_t *scratch_pool)
13688251881Speter{
13689251881Speter  svn_wc__db_wcroot_t *wcroot;
13690251881Speter  const char *local_relpath;
13691251881Speter  svn_sqlite__stmt_t *stmt_info;
13692251881Speter  svn_boolean_t have_info;
13693251881Speter
13694251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13695251881Speter
13696251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13697251881Speter                              local_abspath, scratch_pool, scratch_pool));
13698251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13699251881Speter
13700251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13701251881Speter                                    STMT_SELECT_NODE_INFO));
13702251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13703251881Speter  SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13704251881Speter
13705251881Speter  if (!have_info)
13706251881Speter    {
13707251881Speter      if (allow_missing)
13708251881Speter        {
13709251881Speter          *kind = svn_node_unknown;
13710251881Speter          SVN_ERR(svn_sqlite__reset(stmt_info));
13711251881Speter          return SVN_NO_ERROR;
13712251881Speter        }
13713251881Speter      else
13714251881Speter        {
13715251881Speter          SVN_ERR(svn_sqlite__reset(stmt_info));
13716251881Speter          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13717251881Speter                                   _("The node '%s' was not found."),
13718251881Speter                                   path_for_error_message(wcroot,
13719251881Speter                                                          local_relpath,
13720251881Speter                                                          scratch_pool));
13721251881Speter        }
13722251881Speter    }
13723251881Speter
13724251881Speter  if (!(show_deleted && show_hidden))
13725251881Speter    {
13726251881Speter      int op_depth = svn_sqlite__column_int(stmt_info, 0);
13727251881Speter      svn_boolean_t report_none = FALSE;
13728251881Speter      svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13729251881Speter                                                            presence_map);
13730251881Speter
13731251881Speter      if (op_depth > 0)
13732251881Speter        SVN_ERR(convert_to_working_status(&status, status));
13733251881Speter
13734251881Speter      switch (status)
13735251881Speter        {
13736251881Speter          case svn_wc__db_status_not_present:
13737251881Speter            if (! (show_hidden && show_deleted))
13738251881Speter              report_none = TRUE;
13739251881Speter            break;
13740251881Speter          case svn_wc__db_status_excluded:
13741251881Speter          case svn_wc__db_status_server_excluded:
13742251881Speter            if (! show_hidden)
13743251881Speter              report_none = TRUE;
13744251881Speter            break;
13745251881Speter          case svn_wc__db_status_deleted:
13746251881Speter            if (! show_deleted)
13747251881Speter              report_none = TRUE;
13748251881Speter            break;
13749251881Speter          default:
13750251881Speter            break;
13751251881Speter        }
13752251881Speter
13753251881Speter      if (report_none)
13754251881Speter        {
13755251881Speter          *kind = svn_node_none;
13756251881Speter          return svn_error_trace(svn_sqlite__reset(stmt_info));
13757251881Speter        }
13758251881Speter    }
13759251881Speter
13760251881Speter  *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13761251881Speter
13762251881Speter  return svn_error_trace(svn_sqlite__reset(stmt_info));
13763251881Speter}
13764251881Speter
13765251881Speter
13766251881Spetersvn_error_t *
13767251881Spetersvn_wc__db_node_hidden(svn_boolean_t *hidden,
13768251881Speter                       svn_wc__db_t *db,
13769251881Speter                       const char *local_abspath,
13770251881Speter                       apr_pool_t *scratch_pool)
13771251881Speter{
13772251881Speter  svn_wc__db_wcroot_t *wcroot;
13773251881Speter  const char *local_relpath;
13774251881Speter  svn_wc__db_status_t status;
13775251881Speter
13776251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13777251881Speter
13778251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13779251881Speter                              local_abspath, scratch_pool, scratch_pool));
13780251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13781251881Speter
13782251881Speter  SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13783251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13784251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13785251881Speter                    NULL, NULL, NULL,
13786251881Speter                    wcroot, local_relpath,
13787251881Speter                    scratch_pool, scratch_pool));
13788251881Speter
13789251881Speter  *hidden = (status == svn_wc__db_status_server_excluded
13790251881Speter             || status == svn_wc__db_status_not_present
13791251881Speter             || status == svn_wc__db_status_excluded);
13792251881Speter
13793251881Speter  return SVN_NO_ERROR;
13794251881Speter}
13795251881Speter
13796251881Speter
13797251881Spetersvn_error_t *
13798251881Spetersvn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13799251881Speter                     svn_wc__db_t *db,
13800251881Speter                     const char *local_abspath,
13801251881Speter                     apr_pool_t *scratch_pool)
13802251881Speter{
13803251881Speter  svn_wc__db_wcroot_t *wcroot;
13804251881Speter  const char *local_relpath;
13805251881Speter
13806251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13807251881Speter
13808251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13809251881Speter                              local_abspath, scratch_pool, scratch_pool));
13810251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13811251881Speter
13812251881Speter  if (*local_relpath != '\0')
13813251881Speter    {
13814251881Speter      *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13815251881Speter                           the same wcroot */
13816251881Speter      return SVN_NO_ERROR;
13817251881Speter    }
13818251881Speter
13819251881Speter   *is_wcroot = TRUE;
13820251881Speter
13821251881Speter   return SVN_NO_ERROR;
13822251881Speter}
13823251881Speter
13824251881Speter/* Find a node's kind and whether it is switched, putting the outputs in
13825251881Speter * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13826251881Speter */
13827251881Speterstatic svn_error_t *
13828251881Speterdb_is_switched(svn_boolean_t *is_switched,
13829251881Speter               svn_node_kind_t *kind,
13830251881Speter               svn_wc__db_wcroot_t *wcroot,
13831251881Speter               const char *local_relpath,
13832251881Speter               apr_pool_t *scratch_pool)
13833251881Speter{
13834251881Speter  svn_wc__db_status_t status;
13835251881Speter  apr_int64_t repos_id;
13836251881Speter  const char *repos_relpath;
13837251881Speter  const char *name;
13838251881Speter  const char *parent_local_relpath;
13839251881Speter  apr_int64_t parent_repos_id;
13840251881Speter  const char *parent_repos_relpath;
13841251881Speter
13842251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13843251881Speter
13844251881Speter  SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13845251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13846251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13847251881Speter                    wcroot, local_relpath, scratch_pool, scratch_pool));
13848251881Speter
13849251881Speter  if (status == svn_wc__db_status_server_excluded
13850251881Speter      || status == svn_wc__db_status_excluded
13851251881Speter      || status == svn_wc__db_status_not_present)
13852251881Speter    {
13853251881Speter      return svn_error_createf(
13854251881Speter                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13855251881Speter                    _("The node '%s' was not found."),
13856251881Speter                    path_for_error_message(wcroot, local_relpath,
13857251881Speter                                           scratch_pool));
13858251881Speter    }
13859251881Speter  else if (! repos_relpath)
13860251881Speter    {
13861251881Speter      /* Node is shadowed; easy out */
13862251881Speter      if (is_switched)
13863251881Speter        *is_switched = FALSE;
13864251881Speter
13865251881Speter      return SVN_NO_ERROR;
13866251881Speter    }
13867251881Speter
13868251881Speter  if (! is_switched)
13869251881Speter    return SVN_NO_ERROR;
13870251881Speter
13871251881Speter  svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13872251881Speter
13873251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13874251881Speter                                            &parent_repos_relpath,
13875251881Speter                                            &parent_repos_id, NULL, NULL, NULL,
13876251881Speter                                            NULL, NULL, NULL, NULL, NULL,
13877251881Speter                                            NULL, NULL,
13878251881Speter                                            wcroot, parent_local_relpath,
13879251881Speter                                            scratch_pool, scratch_pool));
13880251881Speter
13881251881Speter  if (repos_id != parent_repos_id)
13882251881Speter    *is_switched = TRUE;
13883251881Speter  else
13884251881Speter    {
13885251881Speter      const char *expected_relpath;
13886251881Speter
13887251881Speter      expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13888251881Speter                                          scratch_pool);
13889251881Speter
13890251881Speter      *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13891251881Speter    }
13892251881Speter
13893251881Speter  return SVN_NO_ERROR;
13894251881Speter}
13895251881Speter
13896251881Spetersvn_error_t *
13897251881Spetersvn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13898251881Speter                       svn_boolean_t *is_switched,
13899251881Speter                       svn_node_kind_t *kind,
13900251881Speter                       svn_wc__db_t *db,
13901251881Speter                       const char *local_abspath,
13902251881Speter                       apr_pool_t *scratch_pool)
13903251881Speter{
13904251881Speter  svn_wc__db_wcroot_t *wcroot;
13905251881Speter  const char *local_relpath;
13906251881Speter
13907251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13908251881Speter
13909251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13910251881Speter                              local_abspath, scratch_pool, scratch_pool));
13911251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13912251881Speter
13913251881Speter  if (is_switched)
13914251881Speter    *is_switched = FALSE;
13915251881Speter
13916251881Speter  if (*local_relpath == '\0')
13917251881Speter    {
13918251881Speter      /* Easy out */
13919251881Speter      if (is_wcroot)
13920251881Speter        *is_wcroot = TRUE;
13921251881Speter
13922251881Speter      if (kind)
13923251881Speter        *kind = svn_node_dir;
13924251881Speter      return SVN_NO_ERROR;
13925251881Speter    }
13926251881Speter
13927251881Speter  if (is_wcroot)
13928251881Speter    *is_wcroot = FALSE;
13929251881Speter
13930251881Speter  if (! is_switched && ! kind)
13931251881Speter    return SVN_NO_ERROR;
13932251881Speter
13933251881Speter  SVN_WC__DB_WITH_TXN(
13934251881Speter    db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13935251881Speter    wcroot);
13936251881Speter  return SVN_NO_ERROR;
13937251881Speter}
13938251881Speter
13939251881Speter
13940251881Spetersvn_error_t *
13941251881Spetersvn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
13942251881Speter                               svn_wc__db_t *db,
13943251881Speter                               const char *wri_abspath,
13944251881Speter                               apr_pool_t *result_pool,
13945251881Speter                               apr_pool_t *scratch_pool)
13946251881Speter{
13947251881Speter  svn_wc__db_wcroot_t *wcroot;
13948251881Speter  const char *local_relpath;
13949251881Speter
13950251881Speter  SVN_ERR_ASSERT(temp_dir_abspath != NULL);
13951251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13952251881Speter
13953251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13954251881Speter                              wri_abspath, scratch_pool, scratch_pool));
13955251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13956251881Speter
13957251881Speter  *temp_dir_abspath = svn_dirent_join_many(result_pool,
13958251881Speter                                           wcroot->abspath,
13959251881Speter                                           svn_wc_get_adm_dir(scratch_pool),
13960251881Speter                                           WCROOT_TEMPDIR_RELPATH,
13961251881Speter                                           NULL);
13962251881Speter  return SVN_NO_ERROR;
13963251881Speter}
13964251881Speter
13965251881Speter
13966251881Speter/* Helper for wclock_obtain_cb() to steal an existing lock */
13967251881Speterstatic svn_error_t *
13968251881Speterwclock_steal(svn_wc__db_wcroot_t *wcroot,
13969251881Speter             const char *local_relpath,
13970251881Speter             apr_pool_t *scratch_pool)
13971251881Speter{
13972251881Speter  svn_sqlite__stmt_t *stmt;
13973251881Speter
13974251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
13975251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13976251881Speter
13977251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
13978251881Speter
13979251881Speter  return SVN_NO_ERROR;
13980251881Speter}
13981251881Speter
13982251881Speter
13983251881Speter/* The body of svn_wc__db_wclock_obtain().
13984251881Speter */
13985251881Speterstatic svn_error_t *
13986251881Speterwclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
13987251881Speter                 const char *local_relpath,
13988251881Speter                 int levels_to_lock,
13989251881Speter                 svn_boolean_t steal_lock,
13990251881Speter                 apr_pool_t *scratch_pool)
13991251881Speter{
13992251881Speter  svn_sqlite__stmt_t *stmt;
13993251881Speter  svn_error_t *err;
13994251881Speter  const char *lock_relpath;
13995251881Speter  int max_depth;
13996251881Speter  int lock_depth;
13997251881Speter  svn_boolean_t got_row;
13998251881Speter
13999251881Speter  svn_wc__db_wclock_t lock;
14000251881Speter
14001251881Speter  /* Upgrade locks the root before the node exists.  Apart from that
14002251881Speter     the root node always exists so we will just skip the check.
14003251881Speter
14004251881Speter     ### Perhaps the lock for upgrade should be created when the db is
14005251881Speter         created?  1.6 used to lock .svn on creation. */
14006251881Speter  if (local_relpath[0])
14007251881Speter    {
14008251881Speter      svn_boolean_t exists;
14009251881Speter
14010251881Speter      SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14011251881Speter      if (!exists)
14012251881Speter        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14013251881Speter                                 _("The node '%s' was not found."),
14014251881Speter                                 path_for_error_message(wcroot,
14015251881Speter                                                        local_relpath,
14016251881Speter                                                        scratch_pool));
14017251881Speter    }
14018251881Speter
14019251881Speter  /* Check if there are nodes locked below the new lock root */
14020251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14021251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14022251881Speter
14023251881Speter  lock_depth = relpath_depth(local_relpath);
14024251881Speter  max_depth = lock_depth + levels_to_lock;
14025251881Speter
14026251881Speter  SVN_ERR(svn_sqlite__step(&got_row, stmt));
14027251881Speter
14028251881Speter  while (got_row)
14029251881Speter    {
14030251881Speter      svn_boolean_t own_lock;
14031251881Speter
14032251881Speter      lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14033251881Speter
14034251881Speter      /* If we are not locking with depth infinity, check if this lock
14035251881Speter         voids our lock request */
14036251881Speter      if (levels_to_lock >= 0
14037251881Speter          && relpath_depth(lock_relpath) > max_depth)
14038251881Speter        {
14039251881Speter          SVN_ERR(svn_sqlite__step(&got_row, stmt));
14040251881Speter          continue;
14041251881Speter        }
14042251881Speter
14043251881Speter      /* Check if we are the lock owner, because we should be able to
14044251881Speter         extend our lock. */
14045251881Speter      err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
14046251881Speter                             TRUE, scratch_pool);
14047251881Speter
14048251881Speter      if (err)
14049251881Speter        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14050251881Speter
14051251881Speter      if (!own_lock && !steal_lock)
14052251881Speter        {
14053251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
14054251881Speter          err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14055251881Speter                                   _("'%s' is already locked."),
14056251881Speter                                   path_for_error_message(wcroot,
14057251881Speter                                                          lock_relpath,
14058251881Speter                                                          scratch_pool));
14059251881Speter          return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14060251881Speter                                   _("Working copy '%s' locked."),
14061251881Speter                                   path_for_error_message(wcroot,
14062251881Speter                                                          local_relpath,
14063251881Speter                                                          scratch_pool));
14064251881Speter        }
14065251881Speter      else if (!own_lock)
14066251881Speter        {
14067251881Speter          err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14068251881Speter
14069251881Speter          if (err)
14070251881Speter            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14071251881Speter        }
14072251881Speter
14073251881Speter      SVN_ERR(svn_sqlite__step(&got_row, stmt));
14074251881Speter    }
14075251881Speter
14076251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14077251881Speter
14078251881Speter  if (steal_lock)
14079251881Speter    SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14080251881Speter
14081251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14082251881Speter  lock_relpath = local_relpath;
14083251881Speter
14084251881Speter  while (TRUE)
14085251881Speter    {
14086251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14087251881Speter
14088251881Speter      SVN_ERR(svn_sqlite__step(&got_row, stmt));
14089251881Speter
14090251881Speter      if (got_row)
14091251881Speter        {
14092251881Speter          int levels = svn_sqlite__column_int(stmt, 0);
14093251881Speter          if (levels >= 0)
14094251881Speter            levels += relpath_depth(lock_relpath);
14095251881Speter
14096251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
14097251881Speter
14098251881Speter          if (levels == -1 || levels >= lock_depth)
14099251881Speter            {
14100251881Speter
14101251881Speter              err = svn_error_createf(
14102251881Speter                              SVN_ERR_WC_LOCKED, NULL,
14103251881Speter                              _("'%s' is already locked."),
14104251881Speter                              svn_dirent_local_style(
14105251881Speter                                       svn_dirent_join(wcroot->abspath,
14106251881Speter                                                       lock_relpath,
14107251881Speter                                                       scratch_pool),
14108251881Speter                              scratch_pool));
14109251881Speter              return svn_error_createf(
14110251881Speter                              SVN_ERR_WC_LOCKED, err,
14111251881Speter                              _("Working copy '%s' locked."),
14112251881Speter                              path_for_error_message(wcroot,
14113251881Speter                                                     local_relpath,
14114251881Speter                                                     scratch_pool));
14115251881Speter            }
14116251881Speter
14117251881Speter          break; /* There can't be interesting locks on higher nodes */
14118251881Speter        }
14119251881Speter      else
14120251881Speter        SVN_ERR(svn_sqlite__reset(stmt));
14121251881Speter
14122251881Speter      if (!*lock_relpath)
14123251881Speter        break;
14124251881Speter
14125251881Speter      lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14126251881Speter    }
14127251881Speter
14128251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14129251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14130251881Speter                            levels_to_lock));
14131251881Speter  err = svn_sqlite__insert(NULL, stmt);
14132251881Speter  if (err)
14133251881Speter    return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14134251881Speter                             _("Working copy '%s' locked"),
14135251881Speter                             path_for_error_message(wcroot,
14136251881Speter                                                    local_relpath,
14137251881Speter                                                    scratch_pool));
14138251881Speter
14139251881Speter  /* And finally store that we obtained the lock */
14140251881Speter  lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14141251881Speter  lock.levels = levels_to_lock;
14142251881Speter  APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14143251881Speter
14144251881Speter  return SVN_NO_ERROR;
14145251881Speter}
14146251881Speter
14147251881Speter
14148251881Spetersvn_error_t *
14149251881Spetersvn_wc__db_wclock_obtain(svn_wc__db_t *db,
14150251881Speter                         const char *local_abspath,
14151251881Speter                         int levels_to_lock,
14152251881Speter                         svn_boolean_t steal_lock,
14153251881Speter                         apr_pool_t *scratch_pool)
14154251881Speter{
14155251881Speter  svn_wc__db_wcroot_t *wcroot;
14156251881Speter  const char *local_relpath;
14157251881Speter
14158251881Speter  SVN_ERR_ASSERT(levels_to_lock >= -1);
14159251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14160251881Speter
14161251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14162251881Speter                                             db, local_abspath,
14163251881Speter                                             scratch_pool, scratch_pool));
14164251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14165251881Speter
14166251881Speter  if (!steal_lock)
14167251881Speter    {
14168251881Speter      int i;
14169251881Speter      int depth = relpath_depth(local_relpath);
14170251881Speter
14171251881Speter      for (i = 0; i < wcroot->owned_locks->nelts; i++)
14172251881Speter        {
14173251881Speter          svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14174251881Speter                                                     i, svn_wc__db_wclock_t);
14175251881Speter
14176251881Speter          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14177251881Speter              && (lock->levels == -1
14178251881Speter                  || (lock->levels + relpath_depth(lock->local_relpath))
14179251881Speter                            >= depth))
14180251881Speter            {
14181251881Speter              return svn_error_createf(
14182251881Speter                SVN_ERR_WC_LOCKED, NULL,
14183251881Speter                _("'%s' is already locked via '%s'."),
14184251881Speter                svn_dirent_local_style(local_abspath, scratch_pool),
14185251881Speter                path_for_error_message(wcroot, lock->local_relpath,
14186251881Speter                                       scratch_pool));
14187251881Speter            }
14188251881Speter        }
14189251881Speter    }
14190251881Speter
14191251881Speter  SVN_WC__DB_WITH_TXN(
14192251881Speter    wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14193251881Speter                     scratch_pool),
14194251881Speter    wcroot);
14195251881Speter  return SVN_NO_ERROR;
14196251881Speter}
14197251881Speter
14198251881Speter
14199251881Speter/* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14200251881Speterstatic svn_error_t *
14201251881Speterfind_wclock(const char **lock_relpath,
14202251881Speter            svn_wc__db_wcroot_t *wcroot,
14203251881Speter            const char *dir_relpath,
14204251881Speter            apr_pool_t *result_pool,
14205251881Speter            apr_pool_t *scratch_pool)
14206251881Speter{
14207251881Speter  svn_sqlite__stmt_t *stmt;
14208251881Speter  svn_boolean_t have_row;
14209251881Speter  int dir_depth = relpath_depth(dir_relpath);
14210251881Speter  const char *first_relpath;
14211251881Speter
14212251881Speter  /* Check for locks on all directories that might be ancestors.
14213251881Speter     As our new apis only use recursive locks the number of locks stored
14214251881Speter     in the DB will be very low */
14215251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14216251881Speter                                    STMT_SELECT_ANCESTOR_WCLOCKS));
14217251881Speter
14218251881Speter  /* Get the top level relpath to reduce the worst case number of results
14219251881Speter     to the number of directories below this node plus two.
14220251881Speter     (1: the node itself and 2: the wcroot). */
14221251881Speter  first_relpath = strchr(dir_relpath, '/');
14222251881Speter
14223251881Speter  if (first_relpath != NULL)
14224251881Speter    first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14225251881Speter                                 first_relpath - dir_relpath);
14226251881Speter  else
14227251881Speter    first_relpath = dir_relpath;
14228251881Speter
14229251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14230251881Speter                            wcroot->wc_id,
14231251881Speter                            dir_relpath,
14232251881Speter                            first_relpath));
14233251881Speter
14234251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14235251881Speter
14236251881Speter  while (have_row)
14237251881Speter    {
14238251881Speter      const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14239251881Speter
14240251881Speter      if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14241251881Speter        {
14242251881Speter          int locked_levels = svn_sqlite__column_int(stmt, 1);
14243251881Speter          int row_depth = relpath_depth(relpath);
14244251881Speter
14245251881Speter          if (locked_levels == -1
14246251881Speter              || locked_levels + row_depth >= dir_depth)
14247251881Speter            {
14248251881Speter              *lock_relpath = apr_pstrdup(result_pool, relpath);
14249251881Speter              SVN_ERR(svn_sqlite__reset(stmt));
14250251881Speter              return SVN_NO_ERROR;
14251251881Speter            }
14252251881Speter        }
14253251881Speter
14254251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
14255251881Speter    }
14256251881Speter
14257251881Speter  *lock_relpath = NULL;
14258251881Speter
14259251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
14260251881Speter}
14261251881Speter
14262251881Speterstatic svn_error_t *
14263251881Speteris_wclocked(svn_boolean_t *locked,
14264251881Speter            svn_wc__db_wcroot_t *wcroot,
14265251881Speter            const char *dir_relpath,
14266251881Speter            apr_pool_t *scratch_pool)
14267251881Speter{
14268251881Speter  const char *lock_relpath;
14269251881Speter
14270251881Speter  SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14271251881Speter                      scratch_pool, scratch_pool));
14272251881Speter  *locked = (lock_relpath != NULL);
14273251881Speter  return SVN_NO_ERROR;
14274251881Speter}
14275251881Speter
14276251881Speter
14277251881Spetersvn_error_t*
14278251881Spetersvn_wc__db_wclock_find_root(const char **lock_abspath,
14279251881Speter                            svn_wc__db_t *db,
14280251881Speter                            const char *local_abspath,
14281251881Speter                            apr_pool_t *result_pool,
14282251881Speter                            apr_pool_t *scratch_pool)
14283251881Speter{
14284251881Speter  svn_wc__db_wcroot_t *wcroot;
14285251881Speter  const char *local_relpath;
14286251881Speter  const char *lock_relpath;
14287251881Speter
14288251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14289251881Speter                              local_abspath, scratch_pool, scratch_pool));
14290251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14291251881Speter
14292251881Speter  SVN_WC__DB_WITH_TXN(
14293251881Speter    find_wclock(&lock_relpath, wcroot, local_relpath,
14294251881Speter                scratch_pool, scratch_pool),
14295251881Speter    wcroot);
14296251881Speter
14297251881Speter  if (!lock_relpath)
14298251881Speter    *lock_abspath = NULL;
14299251881Speter  else
14300251881Speter    SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14301251881Speter                                    lock_relpath, result_pool, scratch_pool));
14302251881Speter  return SVN_NO_ERROR;
14303251881Speter}
14304251881Speter
14305251881Speter
14306251881Spetersvn_error_t *
14307251881Spetersvn_wc__db_wclocked(svn_boolean_t *locked,
14308251881Speter                    svn_wc__db_t *db,
14309251881Speter                    const char *local_abspath,
14310251881Speter                    apr_pool_t *scratch_pool)
14311251881Speter{
14312251881Speter  svn_wc__db_wcroot_t *wcroot;
14313251881Speter  const char *local_relpath;
14314251881Speter
14315251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14316251881Speter                              local_abspath, scratch_pool, scratch_pool));
14317251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14318251881Speter
14319251881Speter  SVN_WC__DB_WITH_TXN(
14320251881Speter    is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14321251881Speter    wcroot);
14322251881Speter
14323251881Speter  return SVN_NO_ERROR;
14324251881Speter}
14325251881Speter
14326251881Speter
14327251881Spetersvn_error_t *
14328251881Spetersvn_wc__db_wclock_release(svn_wc__db_t *db,
14329251881Speter                          const char *local_abspath,
14330251881Speter                          apr_pool_t *scratch_pool)
14331251881Speter{
14332251881Speter  svn_sqlite__stmt_t *stmt;
14333251881Speter  svn_wc__db_wcroot_t *wcroot;
14334251881Speter  const char *local_relpath;
14335251881Speter  int i;
14336251881Speter  apr_array_header_t *owned_locks;
14337251881Speter
14338251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14339251881Speter                              local_abspath, scratch_pool, scratch_pool));
14340251881Speter
14341251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14342251881Speter
14343251881Speter  /* First check and remove the owns-lock information as failure in
14344251881Speter     removing the db record implies that we have to steal the lock later. */
14345251881Speter  owned_locks = wcroot->owned_locks;
14346251881Speter  for (i = 0; i < owned_locks->nelts; i++)
14347251881Speter    {
14348251881Speter      svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14349251881Speter                                                 svn_wc__db_wclock_t);
14350251881Speter
14351251881Speter      if (strcmp(lock->local_relpath, local_relpath) == 0)
14352251881Speter        break;
14353251881Speter    }
14354251881Speter
14355251881Speter  if (i >= owned_locks->nelts)
14356251881Speter    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14357251881Speter                             _("Working copy not locked at '%s'."),
14358251881Speter                             svn_dirent_local_style(local_abspath,
14359251881Speter                                                    scratch_pool));
14360251881Speter
14361251881Speter  if (i < owned_locks->nelts)
14362251881Speter    {
14363251881Speter      owned_locks->nelts--;
14364251881Speter
14365251881Speter      /* Move the last item in the array to the deleted place */
14366251881Speter      if (owned_locks->nelts > 0)
14367251881Speter        APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14368251881Speter           APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14369251881Speter    }
14370251881Speter
14371251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14372251881Speter                                    STMT_DELETE_WC_LOCK));
14373251881Speter
14374251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14375251881Speter
14376251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
14377251881Speter
14378251881Speter  return SVN_NO_ERROR;
14379251881Speter}
14380251881Speter
14381251881Speter
14382251881Speter/* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14383251881Speter   of DB+LOCAL_ABSPATH.  */
14384251881Speterstatic svn_error_t *
14385251881Speterwclock_owns_lock(svn_boolean_t *own_lock,
14386251881Speter                 svn_wc__db_wcroot_t *wcroot,
14387251881Speter                 const char *local_relpath,
14388251881Speter                 svn_boolean_t exact,
14389251881Speter                 apr_pool_t *scratch_pool)
14390251881Speter{
14391251881Speter  apr_array_header_t *owned_locks;
14392251881Speter  int lock_level;
14393251881Speter  int i;
14394251881Speter
14395251881Speter  *own_lock = FALSE;
14396251881Speter  owned_locks = wcroot->owned_locks;
14397251881Speter  lock_level = relpath_depth(local_relpath);
14398251881Speter
14399251881Speter  if (exact)
14400251881Speter    {
14401251881Speter      for (i = 0; i < owned_locks->nelts; i++)
14402251881Speter        {
14403251881Speter          svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14404251881Speter                                                     svn_wc__db_wclock_t);
14405251881Speter
14406251881Speter          if (strcmp(lock->local_relpath, local_relpath) == 0)
14407251881Speter            {
14408251881Speter              *own_lock = TRUE;
14409251881Speter              return SVN_NO_ERROR;
14410251881Speter            }
14411251881Speter        }
14412251881Speter    }
14413251881Speter  else
14414251881Speter    {
14415251881Speter      for (i = 0; i < owned_locks->nelts; i++)
14416251881Speter        {
14417251881Speter          svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14418251881Speter                                                     svn_wc__db_wclock_t);
14419251881Speter
14420251881Speter          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14421251881Speter              && (lock->levels == -1
14422251881Speter                  || ((relpath_depth(lock->local_relpath) + lock->levels)
14423251881Speter                      >= lock_level)))
14424251881Speter            {
14425251881Speter              *own_lock = TRUE;
14426251881Speter              return SVN_NO_ERROR;
14427251881Speter            }
14428251881Speter        }
14429251881Speter    }
14430251881Speter
14431251881Speter  return SVN_NO_ERROR;
14432251881Speter}
14433251881Speter
14434251881Speter
14435251881Spetersvn_error_t *
14436251881Spetersvn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14437251881Speter                            svn_wc__db_t *db,
14438251881Speter                            const char *local_abspath,
14439251881Speter                            svn_boolean_t exact,
14440251881Speter                            apr_pool_t *scratch_pool)
14441251881Speter{
14442251881Speter  svn_wc__db_wcroot_t *wcroot;
14443251881Speter  const char *local_relpath;
14444251881Speter
14445251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14446251881Speter                              local_abspath, scratch_pool, scratch_pool));
14447251881Speter
14448251881Speter  if (!wcroot)
14449251881Speter    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14450251881Speter                             _("The node '%s' was not found."),
14451251881Speter                             svn_dirent_local_style(local_abspath,
14452251881Speter                                                    scratch_pool));
14453251881Speter
14454251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14455251881Speter
14456251881Speter  SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
14457251881Speter                           scratch_pool));
14458251881Speter
14459251881Speter  return SVN_NO_ERROR;
14460251881Speter}
14461251881Speter
14462251881Speter/* The body of svn_wc__db_temp_op_end_directory_update().
14463251881Speter */
14464251881Speterstatic svn_error_t *
14465251881Speterend_directory_update(svn_wc__db_wcroot_t *wcroot,
14466251881Speter                     const char *local_relpath,
14467251881Speter                     apr_pool_t *scratch_pool)
14468251881Speter{
14469251881Speter  svn_sqlite__stmt_t *stmt;
14470251881Speter  svn_wc__db_status_t base_status;
14471251881Speter
14472251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14473251881Speter                                            NULL, NULL, NULL, NULL, NULL,
14474251881Speter                                            NULL, NULL, NULL, NULL, NULL, NULL,
14475251881Speter                                            wcroot, local_relpath,
14476251881Speter                                            scratch_pool, scratch_pool));
14477251881Speter
14478251881Speter  if (base_status == svn_wc__db_status_normal)
14479251881Speter    return SVN_NO_ERROR;
14480251881Speter
14481251881Speter  SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14482251881Speter
14483251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14484251881Speter                                    STMT_UPDATE_NODE_BASE_PRESENCE));
14485251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14486251881Speter                            presence_map, svn_wc__db_status_normal));
14487251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
14488251881Speter
14489251881Speter  return SVN_NO_ERROR;
14490251881Speter}
14491251881Speter
14492251881Spetersvn_error_t *
14493251881Spetersvn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14494251881Speter                                        const char *local_dir_abspath,
14495251881Speter                                        apr_pool_t *scratch_pool)
14496251881Speter{
14497251881Speter  svn_wc__db_wcroot_t *wcroot;
14498251881Speter  const char *local_relpath;
14499251881Speter
14500251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14501251881Speter
14502251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14503251881Speter                              local_dir_abspath, scratch_pool, scratch_pool));
14504251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14505251881Speter
14506251881Speter  SVN_WC__DB_WITH_TXN(
14507251881Speter    end_directory_update(wcroot, local_relpath, scratch_pool),
14508251881Speter    wcroot);
14509251881Speter
14510251881Speter  SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14511251881Speter                        scratch_pool));
14512251881Speter
14513251881Speter  return SVN_NO_ERROR;
14514251881Speter}
14515251881Speter
14516251881Speter
14517251881Speter/* The body of svn_wc__db_temp_op_start_directory_update().
14518251881Speter */
14519251881Speterstatic svn_error_t *
14520251881Speterstart_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14521251881Speter                           const char *local_relpath,
14522251881Speter                           const char *new_repos_relpath,
14523251881Speter                           svn_revnum_t new_rev,
14524251881Speter                           apr_pool_t *scratch_pool)
14525251881Speter{
14526251881Speter  svn_sqlite__stmt_t *stmt;
14527251881Speter
14528251881Speter  /* Note: In the majority of calls, the repos_relpath is unchanged. */
14529251881Speter  /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14530251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14531251881Speter                    STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14532251881Speter
14533251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14534251881Speter                            wcroot->wc_id,
14535251881Speter                            local_relpath,
14536251881Speter                            presence_map, svn_wc__db_status_incomplete,
14537251881Speter                            new_rev,
14538251881Speter                            new_repos_relpath));
14539251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
14540251881Speter
14541251881Speter  return SVN_NO_ERROR;
14542251881Speter
14543251881Speter}
14544251881Speter
14545251881Spetersvn_error_t *
14546251881Spetersvn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14547251881Speter                                          const char *local_abspath,
14548251881Speter                                          const char *new_repos_relpath,
14549251881Speter                                          svn_revnum_t new_rev,
14550251881Speter                                          apr_pool_t *scratch_pool)
14551251881Speter{
14552251881Speter  svn_wc__db_wcroot_t *wcroot;
14553251881Speter  const char *local_relpath;
14554251881Speter
14555251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14556251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14557251881Speter  SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14558251881Speter
14559251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14560251881Speter                              local_abspath, scratch_pool, scratch_pool));
14561251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14562251881Speter
14563251881Speter  SVN_WC__DB_WITH_TXN(
14564251881Speter    start_directory_update_txn(wcroot, local_relpath,
14565251881Speter                               new_repos_relpath, new_rev, scratch_pool),
14566251881Speter    wcroot);
14567251881Speter
14568251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14569251881Speter
14570251881Speter  return SVN_NO_ERROR;
14571251881Speter}
14572251881Speter
14573251881Speter
14574251881Speter/* The body of svn_wc__db_temp_op_make_copy().  This is
14575251881Speter   used by the update editor when deleting a base node tree would be a
14576251881Speter   tree-conflict because there are changes to subtrees.  This function
14577251881Speter   inserts a copy of the base node tree below any existing working
14578251881Speter   subtrees.  Given a tree:
14579251881Speter
14580251881Speter             0            1           2            3
14581251881Speter    /     normal          -
14582251881Speter    A     normal          -
14583251881Speter    A/B   normal          -         normal
14584251881Speter    A/B/C normal          -         base-del       normal
14585251881Speter    A/F   normal          -         normal
14586251881Speter    A/F/G normal          -         normal
14587251881Speter    A/F/H normal          -         base-deleted   normal
14588251881Speter    A/F/E normal          -         not-present
14589251881Speter    A/X   normal          -
14590251881Speter    A/X/Y incomplete      -
14591251881Speter
14592251881Speter    This function adds layers to A and some of its descendants in an attempt
14593251881Speter    to make the working copy look like as if it were a copy of the BASE nodes.
14594251881Speter
14595251881Speter             0            1              2            3
14596251881Speter    /     normal        -
14597251881Speter    A     normal        norm
14598251881Speter    A/B   normal        norm        norm
14599251881Speter    A/B/C normal        norm        base-del       normal
14600251881Speter    A/F   normal        norm        norm
14601251881Speter    A/F/G normal        norm        norm
14602251881Speter    A/F/H normal        norm        not-pres
14603251881Speter    A/F/E normal        norm        base-del
14604251881Speter    A/X   normal        norm
14605251881Speter    A/X/Y incomplete  incomplete
14606251881Speter */
14607251881Speterstatic svn_error_t *
14608251881Spetermake_copy_txn(svn_wc__db_wcroot_t *wcroot,
14609251881Speter              const char *local_relpath,
14610251881Speter              int op_depth,
14611251881Speter              const svn_skel_t *conflicts,
14612251881Speter              const svn_skel_t *work_items,
14613251881Speter              apr_pool_t *scratch_pool)
14614251881Speter{
14615251881Speter  svn_sqlite__stmt_t *stmt;
14616251881Speter  svn_boolean_t have_row;
14617251881Speter  svn_boolean_t add_working_base_deleted = FALSE;
14618251881Speter  svn_boolean_t remove_working = FALSE;
14619251881Speter  const apr_array_header_t *children;
14620251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14621251881Speter  int i;
14622251881Speter
14623251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14624251881Speter                                    STMT_SELECT_LOWEST_WORKING_NODE));
14625251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14626251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14627251881Speter
14628251881Speter  if (have_row)
14629251881Speter    {
14630251881Speter      svn_wc__db_status_t working_status;
14631251881Speter      int working_op_depth;
14632251881Speter
14633251881Speter      working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14634251881Speter      working_op_depth = svn_sqlite__column_int(stmt, 0);
14635251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
14636251881Speter
14637251881Speter      SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14638251881Speter                     || working_status == svn_wc__db_status_base_deleted
14639251881Speter                     || working_status == svn_wc__db_status_not_present
14640251881Speter                     || working_status == svn_wc__db_status_incomplete);
14641251881Speter
14642251881Speter      /* Only change nodes in the layers where we are creating the copy.
14643251881Speter         Deletes in higher layers will just apply to the copy */
14644251881Speter      if (working_op_depth <= op_depth)
14645251881Speter        {
14646251881Speter          add_working_base_deleted = TRUE;
14647251881Speter
14648251881Speter          if (working_status == svn_wc__db_status_base_deleted)
14649251881Speter            remove_working = TRUE;
14650251881Speter        }
14651251881Speter    }
14652251881Speter  else
14653251881Speter    SVN_ERR(svn_sqlite__reset(stmt));
14654251881Speter
14655251881Speter  if (remove_working)
14656251881Speter    {
14657251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14658251881Speter                                        STMT_DELETE_LOWEST_WORKING_NODE));
14659251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14660251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
14661251881Speter    }
14662251881Speter
14663251881Speter  if (add_working_base_deleted)
14664251881Speter    {
14665251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14666251881Speter                                        STMT_INSERT_DELETE_FROM_BASE));
14667251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14668251881Speter                                op_depth));
14669251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
14670251881Speter    }
14671251881Speter  else
14672251881Speter    {
14673251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14674251881Speter                                      STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14675251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14676251881Speter                                op_depth));
14677251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
14678251881Speter    }
14679251881Speter
14680251881Speter  /* Get the BASE children, as WORKING children don't need modifications */
14681251881Speter  SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14682251881Speter                               0, scratch_pool, iterpool));
14683251881Speter
14684251881Speter  for (i = 0; i < children->nelts; i++)
14685251881Speter    {
14686251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
14687251881Speter      const char *copy_relpath;
14688251881Speter
14689251881Speter      svn_pool_clear(iterpool);
14690251881Speter
14691251881Speter      copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14692251881Speter
14693251881Speter      SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14694251881Speter                            iterpool));
14695251881Speter    }
14696251881Speter
14697251881Speter  SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14698251881Speter                                                iterpool),
14699251881Speter                                                svn_depth_empty, iterpool));
14700251881Speter
14701251881Speter  if (conflicts)
14702251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14703251881Speter                                              conflicts, iterpool));
14704251881Speter
14705251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14706251881Speter
14707251881Speter  svn_pool_destroy(iterpool);
14708251881Speter
14709251881Speter  return SVN_NO_ERROR;
14710251881Speter}
14711251881Speter
14712251881Speter
14713251881Spetersvn_error_t *
14714251881Spetersvn_wc__db_op_make_copy(svn_wc__db_t *db,
14715251881Speter                        const char *local_abspath,
14716251881Speter                        const svn_skel_t *conflicts,
14717251881Speter                        const svn_skel_t *work_items,
14718251881Speter                        apr_pool_t *scratch_pool)
14719251881Speter{
14720251881Speter  svn_wc__db_wcroot_t *wcroot;
14721251881Speter  const char *local_relpath;
14722251881Speter  svn_sqlite__stmt_t *stmt;
14723251881Speter  svn_boolean_t have_row;
14724251881Speter
14725251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14726251881Speter
14727251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14728251881Speter                              local_abspath, scratch_pool, scratch_pool));
14729251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14730251881Speter
14731251881Speter  /* The update editor is supposed to call this function when there is
14732251881Speter     no working node for LOCAL_ABSPATH. */
14733251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14734251881Speter                                    STMT_SELECT_WORKING_NODE));
14735251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14736251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14737251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14738251881Speter  if (have_row)
14739251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14740251881Speter                             _("Modification of '%s' already exists"),
14741251881Speter                             path_for_error_message(wcroot,
14742251881Speter                                                    local_relpath,
14743251881Speter                                                    scratch_pool));
14744251881Speter
14745251881Speter  /* We don't allow copies to contain server-excluded nodes;
14746251881Speter     the update editor is going to have to bail out. */
14747251881Speter  SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14748251881Speter
14749251881Speter  SVN_WC__DB_WITH_TXN(
14750251881Speter    make_copy_txn(wcroot, local_relpath,
14751251881Speter                  relpath_depth(local_relpath), conflicts, work_items,
14752251881Speter                  scratch_pool),
14753251881Speter    wcroot);
14754251881Speter
14755251881Speter  return SVN_NO_ERROR;
14756251881Speter}
14757251881Speter
14758251881Spetersvn_error_t *
14759251881Spetersvn_wc__db_info_below_working(svn_boolean_t *have_base,
14760251881Speter                              svn_boolean_t *have_work,
14761251881Speter                              svn_wc__db_status_t *status,
14762251881Speter                              svn_wc__db_t *db,
14763251881Speter                              const char *local_abspath,
14764251881Speter                              apr_pool_t *scratch_pool)
14765251881Speter{
14766251881Speter  svn_wc__db_wcroot_t *wcroot;
14767251881Speter  const char *local_relpath;
14768251881Speter
14769251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14770251881Speter
14771251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14772251881Speter                              local_abspath, scratch_pool, scratch_pool));
14773251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14774251881Speter  SVN_ERR(info_below_working(have_base, have_work, status,
14775251881Speter                             wcroot, local_relpath, -1, scratch_pool));
14776251881Speter
14777251881Speter  return SVN_NO_ERROR;
14778251881Speter}
14779251881Speter
14780251881Spetersvn_error_t *
14781251881Spetersvn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14782251881Speter                                       svn_wc__db_t *db,
14783251881Speter                                       const char *local_abspath,
14784251881Speter                                       apr_pool_t *result_pool,
14785251881Speter                                       apr_pool_t *scratch_pool)
14786251881Speter{
14787251881Speter  svn_wc__db_wcroot_t *wcroot;
14788251881Speter  const char *local_relpath;
14789251881Speter  svn_sqlite__stmt_t *stmt;
14790251881Speter  svn_boolean_t have_row;
14791251881Speter
14792251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14793251881Speter
14794251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14795251881Speter                              local_abspath, scratch_pool, scratch_pool));
14796251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14797251881Speter
14798251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14799251881Speter                                    STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14800251881Speter
14801251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14802251881Speter                            wcroot->wc_id,
14803251881Speter                            local_relpath,
14804251881Speter                            relpath_depth(local_relpath)));
14805251881Speter
14806251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14807251881Speter
14808251881Speter  if (have_row)
14809251881Speter    {
14810251881Speter      apr_array_header_t *paths;
14811251881Speter
14812251881Speter      paths = apr_array_make(result_pool, 4, sizeof(const char*));
14813251881Speter      while (have_row)
14814251881Speter        {
14815251881Speter          const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14816251881Speter
14817251881Speter          APR_ARRAY_PUSH(paths, const char *)
14818251881Speter              = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14819251881Speter                                           local_relpath, found_relpath));
14820251881Speter
14821251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
14822251881Speter        }
14823251881Speter
14824251881Speter      *descendants = paths;
14825251881Speter    }
14826251881Speter  else
14827251881Speter    *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14828251881Speter
14829251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
14830251881Speter}
14831251881Speter
14832251881Speter
14833251881Speter/* Like svn_wc__db_min_max_revisions(),
14834251881Speter * but accepts a WCROOT/LOCAL_RELPATH pair. */
14835251881Speterstatic svn_error_t *
14836251881Speterget_min_max_revisions(svn_revnum_t *min_revision,
14837251881Speter                      svn_revnum_t *max_revision,
14838251881Speter                      svn_wc__db_wcroot_t *wcroot,
14839251881Speter                      const char *local_relpath,
14840251881Speter                      svn_boolean_t committed,
14841251881Speter                      apr_pool_t *scratch_pool)
14842251881Speter{
14843251881Speter  svn_sqlite__stmt_t *stmt;
14844251881Speter  svn_revnum_t min_rev, max_rev;
14845251881Speter
14846251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14847251881Speter                                    STMT_SELECT_MIN_MAX_REVISIONS));
14848251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14849251881Speter  SVN_ERR(svn_sqlite__step_row(stmt));
14850251881Speter
14851251881Speter  if (committed)
14852251881Speter    {
14853251881Speter      min_rev = svn_sqlite__column_revnum(stmt, 2);
14854251881Speter      max_rev = svn_sqlite__column_revnum(stmt, 3);
14855251881Speter    }
14856251881Speter  else
14857251881Speter    {
14858251881Speter      min_rev = svn_sqlite__column_revnum(stmt, 0);
14859251881Speter      max_rev = svn_sqlite__column_revnum(stmt, 1);
14860251881Speter    }
14861251881Speter
14862251881Speter  /* The statement returns exactly one row. */
14863251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14864251881Speter
14865251881Speter  if (min_revision)
14866251881Speter    *min_revision = min_rev;
14867251881Speter  if (max_revision)
14868251881Speter    *max_revision = max_rev;
14869251881Speter
14870251881Speter  return SVN_NO_ERROR;
14871251881Speter}
14872251881Speter
14873251881Speter
14874251881Spetersvn_error_t *
14875251881Spetersvn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14876251881Speter                             svn_revnum_t *max_revision,
14877251881Speter                             svn_wc__db_t *db,
14878251881Speter                             const char *local_abspath,
14879251881Speter                             svn_boolean_t committed,
14880251881Speter                             apr_pool_t *scratch_pool)
14881251881Speter{
14882251881Speter  svn_wc__db_wcroot_t *wcroot;
14883251881Speter  const char *local_relpath;
14884251881Speter
14885251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14886251881Speter
14887251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14888251881Speter                                                db, local_abspath,
14889251881Speter                                                scratch_pool, scratch_pool));
14890251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14891251881Speter
14892251881Speter  return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14893251881Speter                                               wcroot, local_relpath,
14894251881Speter                                               committed, scratch_pool));
14895251881Speter}
14896251881Speter
14897251881Speter
14898251881Speter/* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14899251881Speter * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14900251881Speterstatic svn_error_t *
14901251881Speteris_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14902251881Speter                            svn_wc__db_wcroot_t *wcroot,
14903251881Speter                            const char *local_relpath,
14904251881Speter                            apr_pool_t *scratch_pool)
14905251881Speter{
14906251881Speter  svn_sqlite__stmt_t *stmt;
14907251881Speter  svn_boolean_t have_row;
14908251881Speter
14909251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14910251881Speter                                    STMT_HAS_SPARSE_NODES));
14911251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
14912251881Speter                            wcroot->wc_id,
14913251881Speter                            local_relpath));
14914251881Speter  /* If this query returns a row, the working copy is sparse. */
14915251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14916251881Speter  *is_sparse_checkout = have_row;
14917251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14918251881Speter
14919251881Speter  return SVN_NO_ERROR;
14920251881Speter}
14921251881Speter
14922251881Speter
14923251881Speter/* Like svn_wc__db_has_switched_subtrees(),
14924251881Speter * but accepts a WCROOT/LOCAL_RELPATH pair. */
14925251881Speterstatic svn_error_t *
14926251881Speterhas_switched_subtrees(svn_boolean_t *is_switched,
14927251881Speter                      svn_wc__db_wcroot_t *wcroot,
14928251881Speter                      const char *local_relpath,
14929251881Speter                      const char *trail_url,
14930251881Speter                      apr_pool_t *scratch_pool)
14931251881Speter{
14932251881Speter  svn_sqlite__stmt_t *stmt;
14933251881Speter  svn_boolean_t have_row;
14934251881Speter  apr_int64_t repos_id;
14935251881Speter  const char *repos_relpath;
14936251881Speter
14937251881Speter  /* Optional argument handling for caller */
14938251881Speter  if (!is_switched)
14939251881Speter    return SVN_NO_ERROR;
14940251881Speter
14941251881Speter  *is_switched = FALSE;
14942251881Speter
14943251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14944251881Speter                                            &repos_relpath, &repos_id,
14945251881Speter                                            NULL, NULL, NULL, NULL, NULL,
14946251881Speter                                            NULL, NULL, NULL, NULL, NULL,
14947251881Speter                                            wcroot, local_relpath,
14948251881Speter                                            scratch_pool, scratch_pool));
14949251881Speter
14950251881Speter  /* First do the cheap check where we only need info on the origin itself */
14951251881Speter  if (trail_url != NULL)
14952251881Speter    {
14953251881Speter      const char *repos_root_url;
14954251881Speter      const char *url;
14955251881Speter      apr_size_t len1, len2;
14956251881Speter
14957251881Speter      /* If the trailing part of the URL of the working copy directory
14958251881Speter         does not match the given trailing URL then the whole working
14959251881Speter         copy is switched. */
14960251881Speter
14961251881Speter      SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
14962251881Speter                                          repos_id, scratch_pool));
14963251881Speter      url = svn_path_url_add_component2(repos_root_url, repos_relpath,
14964251881Speter                                        scratch_pool);
14965251881Speter
14966251881Speter      len1 = strlen(trail_url);
14967251881Speter      len2 = strlen(url);
14968251881Speter      if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
14969251881Speter        {
14970251881Speter          *is_switched = TRUE;
14971251881Speter          return SVN_NO_ERROR;
14972251881Speter        }
14973251881Speter    }
14974251881Speter
14975251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
14976251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
14977251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14978251881Speter  if (have_row)
14979251881Speter    *is_switched = TRUE;
14980251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14981251881Speter
14982251881Speter  return SVN_NO_ERROR;
14983251881Speter}
14984251881Speter
14985251881Speter
14986251881Spetersvn_error_t *
14987251881Spetersvn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
14988251881Speter                                 svn_wc__db_t *db,
14989251881Speter                                 const char *local_abspath,
14990251881Speter                                 const char *trail_url,
14991251881Speter                                 apr_pool_t *scratch_pool)
14992251881Speter{
14993251881Speter  svn_wc__db_wcroot_t *wcroot;
14994251881Speter  const char *local_relpath;
14995251881Speter
14996251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14997251881Speter
14998251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14999251881Speter                                                db, local_abspath,
15000251881Speter                                                scratch_pool, scratch_pool));
15001251881Speter  VERIFY_USABLE_WCROOT(wcroot);
15002251881Speter
15003251881Speter  return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15004251881Speter                                               local_relpath, trail_url,
15005251881Speter                                               scratch_pool));
15006251881Speter}
15007251881Speter
15008251881Spetersvn_error_t *
15009251881Spetersvn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15010251881Speter                                 svn_wc__db_t *db,
15011251881Speter                                 const char *local_abspath,
15012251881Speter                                 apr_pool_t *result_pool,
15013251881Speter                                 apr_pool_t *scratch_pool)
15014251881Speter{
15015251881Speter  svn_wc__db_wcroot_t *wcroot;
15016251881Speter  const char *local_relpath;
15017251881Speter  svn_sqlite__stmt_t *stmt;
15018251881Speter  svn_boolean_t have_row;
15019251881Speter
15020251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15021251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15022251881Speter                                                db, local_abspath,
15023251881Speter                                                scratch_pool, scratch_pool));
15024251881Speter  VERIFY_USABLE_WCROOT(wcroot);
15025251881Speter
15026251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15027251881Speter                                    STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15028251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
15029251881Speter                            wcroot->wc_id,
15030251881Speter                            local_relpath));
15031251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15032251881Speter
15033251881Speter  if (have_row)
15034251881Speter    *excluded_subtrees = apr_hash_make(result_pool);
15035251881Speter  else
15036251881Speter    *excluded_subtrees = NULL;
15037251881Speter
15038251881Speter  while (have_row)
15039251881Speter    {
15040251881Speter      const char *abs_path =
15041251881Speter        svn_dirent_join(wcroot->abspath,
15042251881Speter                        svn_sqlite__column_text(stmt, 0, NULL),
15043251881Speter                        result_pool);
15044251881Speter      svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15045251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15046251881Speter    }
15047251881Speter
15048251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
15049251881Speter  return SVN_NO_ERROR;
15050251881Speter}
15051251881Speter
15052251881Speter/* Like svn_wc__db_has_local_mods(),
15053251881Speter * but accepts a WCROOT/LOCAL_RELPATH pair.
15054251881Speter * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15055251881Speterstatic svn_error_t *
15056251881Speterhas_local_mods(svn_boolean_t *is_modified,
15057251881Speter               svn_wc__db_wcroot_t *wcroot,
15058251881Speter               const char *local_relpath,
15059251881Speter               svn_wc__db_t *db,
15060251881Speter               svn_cancel_func_t cancel_func,
15061251881Speter               void *cancel_baton,
15062251881Speter               apr_pool_t *scratch_pool)
15063251881Speter{
15064251881Speter  svn_sqlite__stmt_t *stmt;
15065251881Speter
15066251881Speter  /* Check for additions or deletions. */
15067251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15068251881Speter                                    STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15069251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15070251881Speter  /* If this query returns a row, the working copy is modified. */
15071251881Speter  SVN_ERR(svn_sqlite__step(is_modified, stmt));
15072251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
15073251881Speter
15074251881Speter  if (cancel_func)
15075251881Speter    SVN_ERR(cancel_func(cancel_baton));
15076251881Speter
15077251881Speter  if (! *is_modified)
15078251881Speter    {
15079251881Speter      /* Check for property modifications. */
15080251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15081251881Speter                                        STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15082251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15083251881Speter      /* If this query returns a row, the working copy is modified. */
15084251881Speter      SVN_ERR(svn_sqlite__step(is_modified, stmt));
15085251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
15086251881Speter
15087251881Speter      if (cancel_func)
15088251881Speter        SVN_ERR(cancel_func(cancel_baton));
15089251881Speter    }
15090251881Speter
15091251881Speter  if (! *is_modified)
15092251881Speter    {
15093251881Speter      apr_pool_t *iterpool = NULL;
15094251881Speter      svn_boolean_t have_row;
15095251881Speter
15096251881Speter      /* Check for text modifications. */
15097251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15098251881Speter                                        STMT_SELECT_BASE_FILES_RECURSIVE));
15099251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15100251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15101251881Speter      if (have_row)
15102251881Speter        iterpool = svn_pool_create(scratch_pool);
15103251881Speter      while (have_row)
15104251881Speter        {
15105251881Speter          const char *node_abspath;
15106251881Speter          svn_filesize_t recorded_size;
15107251881Speter          apr_time_t recorded_time;
15108251881Speter          svn_boolean_t skip_check = FALSE;
15109251881Speter          svn_error_t *err;
15110251881Speter
15111251881Speter          if (cancel_func)
15112251881Speter            {
15113251881Speter              err = cancel_func(cancel_baton);
15114251881Speter              if (err)
15115251881Speter                return svn_error_trace(svn_error_compose_create(
15116251881Speter                                                    err,
15117251881Speter                                                    svn_sqlite__reset(stmt)));
15118251881Speter            }
15119251881Speter
15120251881Speter          svn_pool_clear(iterpool);
15121251881Speter
15122251881Speter          node_abspath = svn_dirent_join(wcroot->abspath,
15123251881Speter                                         svn_sqlite__column_text(stmt, 0,
15124251881Speter                                                                 iterpool),
15125251881Speter                                         iterpool);
15126251881Speter
15127251881Speter          recorded_size = get_recorded_size(stmt, 1);
15128251881Speter          recorded_time = svn_sqlite__column_int64(stmt, 2);
15129251881Speter
15130251881Speter          if (recorded_size != SVN_INVALID_FILESIZE
15131251881Speter              && recorded_time != 0)
15132251881Speter            {
15133251881Speter              const svn_io_dirent2_t *dirent;
15134251881Speter
15135251881Speter              err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
15136251881Speter                                        iterpool, iterpool);
15137251881Speter              if (err)
15138251881Speter                return svn_error_trace(svn_error_compose_create(
15139251881Speter                                                    err,
15140251881Speter                                                    svn_sqlite__reset(stmt)));
15141251881Speter
15142251881Speter              if (dirent->kind != svn_node_file)
15143251881Speter                {
15144251881Speter                  *is_modified = TRUE; /* Missing or obstruction */
15145251881Speter                  break;
15146251881Speter                }
15147251881Speter              else if (dirent->filesize == recorded_size
15148251881Speter                       && dirent->mtime == recorded_time)
15149251881Speter                {
15150251881Speter                  /* The file is not modified */
15151251881Speter                  skip_check = TRUE;
15152251881Speter                }
15153251881Speter            }
15154251881Speter
15155251881Speter          if (! skip_check)
15156251881Speter            {
15157251881Speter              err = svn_wc__internal_file_modified_p(is_modified,
15158251881Speter                                                     db, node_abspath,
15159251881Speter                                                     FALSE, iterpool);
15160251881Speter
15161251881Speter              if (err)
15162251881Speter                return svn_error_trace(svn_error_compose_create(
15163251881Speter                                                    err,
15164251881Speter                                                    svn_sqlite__reset(stmt)));
15165251881Speter
15166251881Speter              if (*is_modified)
15167251881Speter                break;
15168251881Speter            }
15169251881Speter
15170251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
15171251881Speter        }
15172251881Speter      if (iterpool)
15173251881Speter        svn_pool_destroy(iterpool);
15174251881Speter
15175251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
15176251881Speter    }
15177251881Speter
15178251881Speter  return SVN_NO_ERROR;
15179251881Speter}
15180251881Speter
15181251881Speter
15182251881Spetersvn_error_t *
15183251881Spetersvn_wc__db_has_local_mods(svn_boolean_t *is_modified,
15184251881Speter                          svn_wc__db_t *db,
15185251881Speter                          const char *local_abspath,
15186251881Speter                          svn_cancel_func_t cancel_func,
15187251881Speter                          void *cancel_baton,
15188251881Speter                          apr_pool_t *scratch_pool)
15189251881Speter{
15190251881Speter  svn_wc__db_wcroot_t *wcroot;
15191251881Speter  const char *local_relpath;
15192251881Speter
15193251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15194251881Speter
15195251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15196251881Speter                                                db, local_abspath,
15197251881Speter                                                scratch_pool, scratch_pool));
15198251881Speter  VERIFY_USABLE_WCROOT(wcroot);
15199251881Speter
15200251881Speter  return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
15201251881Speter                                        db, cancel_func, cancel_baton,
15202251881Speter                                        scratch_pool));
15203251881Speter}
15204251881Speter
15205251881Speter
15206251881Speter/* The body of svn_wc__db_revision_status().
15207251881Speter */
15208251881Speterstatic svn_error_t *
15209251881Speterrevision_status_txn(svn_revnum_t *min_revision,
15210251881Speter                    svn_revnum_t *max_revision,
15211251881Speter                    svn_boolean_t *is_sparse_checkout,
15212251881Speter                    svn_boolean_t *is_modified,
15213251881Speter                    svn_boolean_t *is_switched,
15214251881Speter                    svn_wc__db_wcroot_t *wcroot,
15215251881Speter                    const char *local_relpath,
15216251881Speter                    svn_wc__db_t *db,
15217251881Speter                    const char *trail_url,
15218251881Speter                    svn_boolean_t committed,
15219251881Speter                    svn_cancel_func_t cancel_func,
15220251881Speter                    void *cancel_baton,
15221251881Speter                    apr_pool_t *scratch_pool)
15222251881Speter{
15223251881Speter  svn_error_t *err;
15224251881Speter  svn_boolean_t exists;
15225251881Speter
15226251881Speter  SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15227251881Speter
15228251881Speter  if (!exists)
15229251881Speter    {
15230251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15231251881Speter                               _("The node '%s' was not found."),
15232251881Speter                               path_for_error_message(wcroot, local_relpath,
15233251881Speter                                                      scratch_pool));
15234251881Speter    }
15235251881Speter
15236251881Speter  /* Determine mixed-revisionness. */
15237251881Speter  SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15238251881Speter                                local_relpath, committed, scratch_pool));
15239251881Speter
15240251881Speter  if (cancel_func)
15241251881Speter    SVN_ERR(cancel_func(cancel_baton));
15242251881Speter
15243251881Speter  /* Determine sparseness. */
15244251881Speter  SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15245251881Speter                                      local_relpath, scratch_pool));
15246251881Speter
15247251881Speter  if (cancel_func)
15248251881Speter    SVN_ERR(cancel_func(cancel_baton));
15249251881Speter
15250251881Speter  /* Check for switched nodes. */
15251251881Speter  {
15252251881Speter    err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15253251881Speter                                trail_url, scratch_pool);
15254251881Speter
15255251881Speter    if (err)
15256251881Speter      {
15257251881Speter        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15258251881Speter          return svn_error_trace(err);
15259251881Speter
15260251881Speter        svn_error_clear(err); /* No Base node, but no fatal error */
15261251881Speter        *is_switched = FALSE;
15262251881Speter      }
15263251881Speter  }
15264251881Speter
15265251881Speter  if (cancel_func)
15266251881Speter    SVN_ERR(cancel_func(cancel_baton));
15267251881Speter
15268251881Speter  /* Check for local mods. */
15269251881Speter  SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
15270251881Speter                         cancel_func, cancel_baton, scratch_pool));
15271251881Speter
15272251881Speter  return SVN_NO_ERROR;
15273251881Speter}
15274251881Speter
15275251881Speter
15276251881Spetersvn_error_t *
15277251881Spetersvn_wc__db_revision_status(svn_revnum_t *min_revision,
15278251881Speter                           svn_revnum_t *max_revision,
15279251881Speter                           svn_boolean_t *is_sparse_checkout,
15280251881Speter                           svn_boolean_t *is_modified,
15281251881Speter                           svn_boolean_t *is_switched,
15282251881Speter                           svn_wc__db_t *db,
15283251881Speter                           const char *local_abspath,
15284251881Speter                           const char *trail_url,
15285251881Speter                           svn_boolean_t committed,
15286251881Speter                           svn_cancel_func_t cancel_func,
15287251881Speter                           void *cancel_baton,
15288251881Speter                           apr_pool_t *scratch_pool)
15289251881Speter{
15290251881Speter  svn_wc__db_wcroot_t *wcroot;
15291251881Speter  const char *local_relpath;
15292251881Speter
15293251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15294251881Speter
15295251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15296251881Speter                                                db, local_abspath,
15297251881Speter                                                scratch_pool, scratch_pool));
15298251881Speter  VERIFY_USABLE_WCROOT(wcroot);
15299251881Speter
15300251881Speter  SVN_WC__DB_WITH_TXN(
15301251881Speter    revision_status_txn(min_revision, max_revision,
15302251881Speter                        is_sparse_checkout, is_modified, is_switched,
15303251881Speter                        wcroot, local_relpath, db,
15304251881Speter                        trail_url, committed, cancel_func, cancel_baton,
15305251881Speter                        scratch_pool),
15306251881Speter    wcroot);
15307251881Speter  return SVN_NO_ERROR;
15308251881Speter}
15309251881Speter
15310251881Speter
15311251881Spetersvn_error_t *
15312251881Spetersvn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15313251881Speter                                          svn_wc__db_t *db,
15314251881Speter                                          const char *local_abspath,
15315251881Speter                                          apr_pool_t *result_pool,
15316251881Speter                                          apr_pool_t *scratch_pool)
15317251881Speter{
15318251881Speter  svn_wc__db_wcroot_t *wcroot;
15319251881Speter  const char *local_relpath;
15320251881Speter  svn_sqlite__stmt_t *stmt;
15321251881Speter  svn_boolean_t have_row;
15322251881Speter  apr_int64_t last_repos_id = INVALID_REPOS_ID;
15323251881Speter  const char *last_repos_root_url = NULL;
15324251881Speter
15325251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15326251881Speter
15327251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15328251881Speter                                                db, local_abspath,
15329251881Speter                                                scratch_pool, scratch_pool));
15330251881Speter  VERIFY_USABLE_WCROOT(wcroot);
15331251881Speter
15332251881Speter  *lock_tokens = apr_hash_make(result_pool);
15333251881Speter
15334251881Speter  /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15335251881Speter  SVN_ERR(svn_sqlite__get_statement(
15336251881Speter              &stmt, wcroot->sdb,
15337251881Speter              STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15338251881Speter
15339251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15340251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
15341251881Speter  while (have_row)
15342251881Speter    {
15343251881Speter      apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15344251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15345251881Speter      const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15346251881Speter
15347251881Speter      if (child_repos_id != last_repos_id)
15348251881Speter        {
15349251881Speter          svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15350251881Speter                                                         NULL, wcroot->sdb,
15351251881Speter                                                         child_repos_id,
15352251881Speter                                                         scratch_pool);
15353251881Speter
15354251881Speter          if (err)
15355251881Speter            {
15356251881Speter              return svn_error_trace(
15357251881Speter                            svn_error_compose_create(err,
15358251881Speter                                                     svn_sqlite__reset(stmt)));
15359251881Speter            }
15360251881Speter
15361251881Speter          last_repos_id = child_repos_id;
15362251881Speter        }
15363251881Speter
15364251881Speter      SVN_ERR_ASSERT(last_repos_root_url != NULL);
15365251881Speter      svn_hash_sets(*lock_tokens,
15366251881Speter                    svn_path_url_add_component2(last_repos_root_url,
15367251881Speter                                                child_relpath, result_pool),
15368251881Speter                    lock_token);
15369251881Speter
15370251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15371251881Speter    }
15372251881Speter  return svn_sqlite__reset(stmt);
15373251881Speter}
15374251881Speter
15375251881Speter
15376251881Speter/* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15377251881Speter * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15378251881Speter#define VERIFY(expression) \
15379251881Speter  do { \
15380251881Speter    if (! (expression)) \
15381251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15382251881Speter        _("database inconsistency at local_relpath='%s' verifying " \
15383251881Speter          "expression '%s'"), local_relpath, #expression); \
15384251881Speter  } while (0)
15385251881Speter
15386251881Speter
15387251881Speter/* Verify consistency of the metadata concerning WCROOT.  This is intended
15388251881Speter * for use only during testing and debugging, so is not intended to be
15389251881Speter * blazingly fast.
15390251881Speter *
15391251881Speter * This code is a complement to any verification that we can do in SQLite
15392251881Speter * triggers.  See, for example, 'wc-checks.sql'.
15393251881Speter *
15394251881Speter * Some more verification steps we might want to add are:
15395251881Speter *
15396251881Speter *   * on every ACTUAL row (except root): a NODES row exists at its parent path
15397251881Speter *   * the op-depth root must always exist and every intermediate too
15398251881Speter */
15399251881Speterstatic svn_error_t *
15400251881Speterverify_wcroot(svn_wc__db_wcroot_t *wcroot,
15401251881Speter              apr_pool_t *scratch_pool)
15402251881Speter{
15403251881Speter  svn_sqlite__stmt_t *stmt;
15404251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15405251881Speter
15406251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15407251881Speter                                    STMT_SELECT_ALL_NODES));
15408251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15409251881Speter  while (TRUE)
15410251881Speter    {
15411251881Speter      svn_boolean_t have_row;
15412251881Speter      const char *local_relpath, *parent_relpath;
15413251881Speter      int op_depth;
15414251881Speter
15415251881Speter      svn_pool_clear(iterpool);
15416251881Speter
15417251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
15418251881Speter      if (!have_row)
15419251881Speter        break;
15420251881Speter
15421251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
15422251881Speter      local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15423251881Speter      parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15424251881Speter
15425251881Speter      /* Verify parent_relpath is the parent path of local_relpath */
15426251881Speter      VERIFY((parent_relpath == NULL)
15427251881Speter             ? (local_relpath[0] == '\0')
15428251881Speter             : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15429251881Speter                       parent_relpath) == 0));
15430251881Speter
15431251881Speter      /* Verify op_depth <= the tree depth of local_relpath */
15432251881Speter      VERIFY(op_depth <= relpath_depth(local_relpath));
15433251881Speter
15434251881Speter      /* Verify parent_relpath refers to a row that exists */
15435251881Speter      /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15436251881Speter       * the child's and a suitable presence */
15437251881Speter      if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15438251881Speter        {
15439251881Speter          svn_sqlite__stmt_t *stmt2;
15440251881Speter          svn_boolean_t have_a_parent_row;
15441251881Speter
15442251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15443251881Speter                                            STMT_SELECT_NODE_INFO));
15444251881Speter          SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15445251881Speter                                    parent_relpath));
15446251881Speter          SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15447251881Speter          VERIFY(have_a_parent_row);
15448251881Speter          SVN_ERR(svn_sqlite__reset(stmt2));
15449251881Speter        }
15450251881Speter    }
15451251881Speter  svn_pool_destroy(iterpool);
15452251881Speter
15453251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
15454251881Speter}
15455251881Speter
15456251881Spetersvn_error_t *
15457251881Spetersvn_wc__db_verify(svn_wc__db_t *db,
15458251881Speter                  const char *wri_abspath,
15459251881Speter                  apr_pool_t *scratch_pool)
15460251881Speter{
15461251881Speter  svn_wc__db_wcroot_t *wcroot;
15462251881Speter  const char *local_relpath;
15463251881Speter
15464251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15465251881Speter                                                db, wri_abspath,
15466251881Speter                                                scratch_pool, scratch_pool));
15467251881Speter  VERIFY_USABLE_WCROOT(wcroot);
15468251881Speter
15469251881Speter  SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15470251881Speter  return SVN_NO_ERROR;
15471251881Speter}
15472251881Speter
15473251881Spetersvn_error_t *
15474251881Spetersvn_wc__db_bump_format(int *result_format,
15475253734Speter                       svn_boolean_t *bumped_format,
15476253734Speter                       svn_wc__db_t *db,
15477251881Speter                       const char *wcroot_abspath,
15478251881Speter                       apr_pool_t *scratch_pool)
15479251881Speter{
15480251881Speter  svn_sqlite__db_t *sdb;
15481251881Speter  svn_error_t *err;
15482251881Speter  int format;
15483251881Speter
15484253734Speter  if (bumped_format)
15485253734Speter    *bumped_format = FALSE;
15486253734Speter
15487251881Speter  /* Do not scan upwards for a working copy root here to prevent accidental
15488251881Speter   * upgrades of any working copies the WCROOT might be nested in.
15489251881Speter   * Just try to open a DB at the specified path instead. */
15490251881Speter  err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15491251881Speter                                svn_sqlite__mode_readwrite,
15492251881Speter                                TRUE, /* exclusive */
15493251881Speter                                NULL, /* my statements */
15494251881Speter                                scratch_pool, scratch_pool);
15495251881Speter  if (err)
15496251881Speter    {
15497251881Speter      svn_error_t *err2;
15498251881Speter      apr_hash_t *entries;
15499251881Speter
15500251881Speter      /* Could not open an sdb. Check for an entries file instead. */
15501251881Speter      err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15502251881Speter                                      scratch_pool, scratch_pool);
15503251881Speter      if (err2 || apr_hash_count(entries) == 0)
15504251881Speter        return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15505251881Speter                  svn_error_compose_create(err, err2),
15506251881Speter                  _("Can't upgrade '%s' as it is not a working copy root"),
15507251881Speter                  svn_dirent_local_style(wcroot_abspath, scratch_pool));
15508251881Speter
15509251881Speter      /* An entries file was found. This is a pre-wc-ng working copy
15510251881Speter       * so suggest an upgrade. */
15511251881Speter      return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15512251881Speter                _("Working copy '%s' is too old and must be upgraded to "
15513251881Speter                  "at least format %d, as created by Subversion %s"),
15514251881Speter                svn_dirent_local_style(wcroot_abspath, scratch_pool),
15515251881Speter                SVN_WC__WC_NG_VERSION,
15516251881Speter                svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15517251881Speter    }
15518251881Speter
15519251881Speter  SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15520251881Speter  err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15521253734Speter                            sdb, format, scratch_pool);
15522251881Speter
15523253734Speter  if (err == SVN_NO_ERROR && bumped_format)
15524253734Speter    *bumped_format = (*result_format > format);
15525253734Speter
15526251881Speter  /* Make sure we return a different error than expected for upgrades from
15527251881Speter     entries */
15528251881Speter  if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15529251881Speter    err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15530251881Speter                           _("Working copy upgrade failed"));
15531251881Speter
15532251881Speter  err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15533251881Speter
15534251881Speter  return svn_error_trace(err);
15535251881Speter}
15536251881Speter
15537251881Spetersvn_error_t *
15538251881Spetersvn_wc__db_vacuum(svn_wc__db_t *db,
15539251881Speter                  const char *local_abspath,
15540251881Speter                  apr_pool_t *scratch_pool)
15541251881Speter{
15542251881Speter  svn_wc__db_wcroot_t *wcroot;
15543251881Speter  const char *local_relpath;
15544251881Speter
15545251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15546251881Speter                                                db, local_abspath,
15547251881Speter                                                scratch_pool, scratch_pool));
15548251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15549251881Speter
15550251881Speter  return SVN_NO_ERROR;
15551251881Speter}
15552