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.
630251881Speter */
631251881Spetersvn_error_t *
632251881Spetersvn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
633251881Speter                                 const char *local_relpath,
634251881Speter                                 int op_depth,
635251881Speter                                 apr_pool_t *scratch_pool)
636251881Speter{
637251881Speter  svn_sqlite__stmt_t *stmt;
638251881Speter
639251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
640251881Speter                                    STMT_DELETE_LOWEST_WORKING_NODE));
641251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
642251881Speter                            op_depth));
643251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
644251881Speter
645251881Speter  return SVN_NO_ERROR;
646251881Speter}
647251881Speter
648251881Speter
649251881Speter
650251881Speter/* Insert the base row represented by (insert_base_baton_t *) BATON. */
651251881Speterstatic svn_error_t *
652251881Speterinsert_base_node(const insert_base_baton_t *pibb,
653251881Speter                 svn_wc__db_wcroot_t *wcroot,
654251881Speter                 const char *local_relpath,
655251881Speter                 apr_pool_t *scratch_pool)
656251881Speter{
657251881Speter  apr_int64_t repos_id = pibb->repos_id;
658251881Speter  svn_sqlite__stmt_t *stmt;
659251881Speter  svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
660251881Speter  apr_int64_t recorded_time;
661251881Speter
662251881Speter  /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
663251881Speter     bind the appropriate parent_relpath. */
664251881Speter  const char *parent_relpath =
665251881Speter    (*local_relpath == '\0') ? NULL
666251881Speter    : svn_relpath_dirname(local_relpath, scratch_pool);
667251881Speter
668251881Speter  if (pibb->repos_id == INVALID_REPOS_ID)
669251881Speter    SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
670251881Speter                            wcroot->sdb, scratch_pool));
671251881Speter
672251881Speter  SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
673251881Speter  SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
674251881Speter
675251881Speter  if (pibb->keep_recorded_info)
676251881Speter    {
677251881Speter      svn_boolean_t have_row;
678251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
679251881Speter                                        STMT_SELECT_BASE_NODE));
680251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
681251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
682251881Speter      if (have_row)
683251881Speter        {
684251881Speter          /* Preserve size and modification time if caller asked us to. */
685251881Speter          recorded_size = get_recorded_size(stmt, 6);
686251881Speter          recorded_time = svn_sqlite__column_int64(stmt, 12);
687251881Speter        }
688251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
689251881Speter    }
690251881Speter
691251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
692251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
693251881Speter                            "tstr"               /* 8 - 11 */
694251881Speter                            "isnnnnns",          /* 12 - 19 */
695251881Speter                            wcroot->wc_id,       /* 1 */
696251881Speter                            local_relpath,       /* 2 */
697251881Speter                            0,              /* op_depth is 0 for base */
698251881Speter                            parent_relpath,      /* 4 */
699251881Speter                            repos_id,
700251881Speter                            pibb->repos_relpath,
701251881Speter                            pibb->revision,
702251881Speter                            presence_map, pibb->status, /* 8 */
703251881Speter                            (pibb->kind == svn_node_dir) ? /* 9 */
704251881Speter                             svn_token__to_word(depth_map, pibb->depth) : NULL,
705251881Speter                            kind_map, pibb->kind, /* 10 */
706251881Speter                            pibb->changed_rev,    /* 11 */
707251881Speter                            pibb->changed_date,   /* 12 */
708251881Speter                            pibb->changed_author, /* 13 */
709251881Speter                            (pibb->kind == svn_node_symlink) ?
710251881Speter                                pibb->target : NULL)); /* 19 */
711251881Speter  if (pibb->kind == svn_node_file)
712251881Speter    {
713251881Speter      if (!pibb->checksum
714251881Speter          && pibb->status != svn_wc__db_status_not_present
715251881Speter          && pibb->status != svn_wc__db_status_excluded
716251881Speter          && pibb->status != svn_wc__db_status_server_excluded)
717251881Speter        return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
718251881Speter                                 _("The file '%s' has no checksum."),
719251881Speter                                 path_for_error_message(wcroot, local_relpath,
720251881Speter                                                        scratch_pool));
721251881Speter
722251881Speter      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
723251881Speter                                        scratch_pool));
724251881Speter
725251881Speter      if (recorded_size != SVN_INVALID_FILESIZE)
726251881Speter        {
727251881Speter          SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
728251881Speter          SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
729251881Speter        }
730251881Speter    }
731251881Speter
732251881Speter  /* Set properties.  Must be null if presence not normal or incomplete. */
733251881Speter  assert(pibb->status == svn_wc__db_status_normal
734251881Speter         || pibb->status == svn_wc__db_status_incomplete
735251881Speter         || pibb->props == NULL);
736251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
737251881Speter                                      scratch_pool));
738251881Speter
739251881Speter  SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
740251881Speter                                      scratch_pool));
741251881Speter
742251881Speter  if (pibb->dav_cache)
743251881Speter    SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
744251881Speter                                        scratch_pool));
745251881Speter
746251881Speter  if (pibb->file_external)
747251881Speter    SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
748251881Speter
749251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
750251881Speter
751251881Speter  if (pibb->update_actual_props)
752251881Speter    {
753251881Speter      /* Cast away const, to allow calling property helpers */
754251881Speter      apr_hash_t *base_props = (apr_hash_t *)pibb->props;
755251881Speter      apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
756251881Speter
757251881Speter      if (base_props != NULL
758251881Speter          && new_actual_props != NULL
759251881Speter          && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
760251881Speter        {
761251881Speter          apr_array_header_t *diffs;
762251881Speter
763251881Speter          SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
764251881Speter                                 scratch_pool));
765251881Speter
766251881Speter          if (diffs->nelts == 0)
767251881Speter            new_actual_props = NULL;
768251881Speter        }
769251881Speter
770251881Speter      SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
771251881Speter                               wcroot->sdb, scratch_pool));
772251881Speter    }
773251881Speter
774251881Speter  if (pibb->kind == svn_node_dir && pibb->children)
775251881Speter    SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
776251881Speter                                       local_relpath,
777251881Speter                                       repos_id,
778251881Speter                                       pibb->repos_relpath,
779251881Speter                                       pibb->revision,
780251881Speter                                       pibb->children,
781251881Speter                                       0 /* BASE */,
782251881Speter                                       scratch_pool));
783251881Speter
784251881Speter  /* When this is not the root node, check shadowing behavior */
785251881Speter  if (*local_relpath)
786251881Speter    {
787251881Speter      if (parent_relpath
788251881Speter          && ((pibb->status == svn_wc__db_status_normal)
789251881Speter              || (pibb->status == svn_wc__db_status_incomplete))
790251881Speter          && ! pibb->file_external)
791251881Speter        {
792251881Speter          SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
793251881Speter                                                  pibb->kind, 0,
794251881Speter                                                  scratch_pool));
795251881Speter        }
796251881Speter      else if (pibb->status == svn_wc__db_status_not_present
797251881Speter               || pibb->status == svn_wc__db_status_server_excluded
798251881Speter               || pibb->status == svn_wc__db_status_excluded)
799251881Speter        {
800251881Speter          SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
801251881Speter                                                   scratch_pool));
802251881Speter        }
803251881Speter    }
804251881Speter
805251881Speter  if (pibb->delete_working)
806251881Speter    {
807251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
808251881Speter                                    STMT_DELETE_WORKING_NODE));
809251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
810251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
811251881Speter    }
812251881Speter  if (pibb->insert_base_deleted)
813251881Speter    {
814251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
815251881Speter                                        STMT_INSERT_DELETE_FROM_BASE));
816251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
817251881Speter                                wcroot->wc_id, local_relpath,
818251881Speter                                relpath_depth(local_relpath)));
819251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
820251881Speter    }
821251881Speter
822251881Speter  SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
823251881Speter  if (pibb->conflict)
824251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
825251881Speter                                              pibb->conflict, scratch_pool));
826251881Speter
827251881Speter  return SVN_NO_ERROR;
828251881Speter}
829251881Speter
830251881Speter
831251881Speter/* Initialize the baton with appropriate "blank" values. This allows the
832251881Speter   insertion function to leave certain columns null.  */
833251881Speterstatic void
834251881Speterblank_iwb(insert_working_baton_t *piwb)
835251881Speter{
836251881Speter  memset(piwb, 0, sizeof(*piwb));
837251881Speter  piwb->changed_rev = SVN_INVALID_REVNUM;
838251881Speter  piwb->depth = svn_depth_infinity;
839251881Speter
840251881Speter  /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
841251881Speter     value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
842251881Speter}
843251881Speter
844251881Speter
845251881Speter/* Insert a row in NODES for each (const char *) child name in CHILDREN,
846251881Speter   whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH.  Set each
847251881Speter   child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
848251881Speter   repos_path by appending the child name to REPOS_PATH, and revision to
849251881Speter   REVISION (which should match the parent's revision).
850251881Speter
851251881Speter   If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
852251881Speterstatic svn_error_t *
853251881Speterinsert_incomplete_children(svn_sqlite__db_t *sdb,
854251881Speter                           apr_int64_t wc_id,
855251881Speter                           const char *local_relpath,
856251881Speter                           apr_int64_t repos_id,
857251881Speter                           const char *repos_path,
858251881Speter                           svn_revnum_t revision,
859251881Speter                           const apr_array_header_t *children,
860251881Speter                           int op_depth,
861251881Speter                           apr_pool_t *scratch_pool)
862251881Speter{
863251881Speter  svn_sqlite__stmt_t *stmt;
864251881Speter  int i;
865251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
866251881Speter  apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
867251881Speter
868251881Speter  SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
869251881Speter  SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
870251881Speter                 == (repos_path != NULL));
871251881Speter
872251881Speter  /* If we're inserting WORKING nodes, we might be replacing existing
873251881Speter   * nodes which were moved-away. We need to retain the moved-to relpath of
874251881Speter   * such nodes in order not to lose move information during replace. */
875251881Speter  if (op_depth > 0)
876251881Speter    {
877251881Speter      for (i = children->nelts; i--; )
878251881Speter        {
879251881Speter          const char *name = APR_ARRAY_IDX(children, i, const char *);
880251881Speter          svn_boolean_t have_row;
881251881Speter
882251881Speter          svn_pool_clear(iterpool);
883251881Speter
884251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
885251881Speter                                            STMT_SELECT_WORKING_NODE));
886251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
887251881Speter                                    svn_relpath_join(local_relpath, name,
888251881Speter                                                     iterpool)));
889251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
890251881Speter          if (have_row && !svn_sqlite__column_is_null(stmt, 14))
891251881Speter            svn_hash_sets(moved_to_relpaths, name,
892251881Speter                          svn_sqlite__column_text(stmt, 14, scratch_pool));
893251881Speter
894251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
895251881Speter        }
896251881Speter    }
897251881Speter
898251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
899251881Speter
900251881Speter  for (i = children->nelts; i--; )
901251881Speter    {
902251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
903251881Speter
904251881Speter      svn_pool_clear(iterpool);
905251881Speter
906251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
907251881Speter                                wc_id,
908251881Speter                                svn_relpath_join(local_relpath, name,
909251881Speter                                                 iterpool),
910251881Speter                                op_depth,
911251881Speter                                local_relpath,
912251881Speter                                revision,
913251881Speter                                "incomplete", /* 8, presence */
914251881Speter                                "unknown",    /* 10, kind */
915251881Speter                                /* 21, moved_to */
916251881Speter                                svn_hash_gets(moved_to_relpaths, name)));
917251881Speter      if (repos_id != INVALID_REPOS_ID)
918251881Speter        {
919251881Speter          SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
920251881Speter          SVN_ERR(svn_sqlite__bind_text(stmt, 6,
921251881Speter                                        svn_relpath_join(repos_path, name,
922251881Speter                                                         iterpool)));
923251881Speter        }
924251881Speter
925251881Speter      SVN_ERR(svn_sqlite__insert(NULL, stmt));
926251881Speter    }
927251881Speter
928251881Speter  svn_pool_destroy(iterpool);
929251881Speter
930251881Speter  return SVN_NO_ERROR;
931251881Speter}
932251881Speter
933251881Speter
934251881Speter/* Insert the working row represented by (insert_working_baton_t *) BATON. */
935251881Speterstatic svn_error_t *
936251881Speterinsert_working_node(const insert_working_baton_t *piwb,
937251881Speter                    svn_wc__db_wcroot_t *wcroot,
938251881Speter                    const char *local_relpath,
939251881Speter                    apr_pool_t *scratch_pool)
940251881Speter{
941251881Speter  const char *parent_relpath;
942251881Speter  const char *moved_to_relpath = NULL;
943251881Speter  svn_sqlite__stmt_t *stmt;
944251881Speter  svn_boolean_t have_row;
945251881Speter
946251881Speter  SVN_ERR_ASSERT(piwb->op_depth > 0);
947251881Speter
948251881Speter  /* We cannot insert a WORKING_NODE row at the wcroot.  */
949251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0');
950251881Speter  parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
951251881Speter
952251881Speter  /* Preserve existing moved-to information for this relpath,
953251881Speter   * which might exist in case we're replacing an existing base-deleted
954251881Speter   * node. */
955251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
956251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
957251881Speter                            piwb->op_depth));
958251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
959251881Speter  if (have_row)
960251881Speter    moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
961251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
962251881Speter
963251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
964251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
965251881Speter                "nnnn" /* properties translated_size last_mod_time dav_cache */
966251881Speter                "sns", /* symlink_target, file_external, moved_to */
967251881Speter                wcroot->wc_id, local_relpath,
968251881Speter                piwb->op_depth,
969251881Speter                parent_relpath,
970251881Speter                presence_map, piwb->presence,
971251881Speter                (piwb->kind == svn_node_dir)
972251881Speter                            ? svn_token__to_word(depth_map, piwb->depth) : NULL,
973251881Speter                kind_map, piwb->kind,
974251881Speter                piwb->changed_rev,
975251881Speter                piwb->changed_date,
976251881Speter                piwb->changed_author,
977251881Speter                /* Note: incomplete nodes may have a NULL target.  */
978251881Speter                (piwb->kind == svn_node_symlink)
979251881Speter                            ? piwb->target : NULL,
980251881Speter                moved_to_relpath));
981251881Speter
982251881Speter  if (piwb->moved_here)
983251881Speter    {
984251881Speter      SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
985251881Speter    }
986251881Speter
987251881Speter  if (piwb->kind == svn_node_file)
988251881Speter    {
989251881Speter      SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
990251881Speter                                        scratch_pool));
991251881Speter    }
992251881Speter
993251881Speter  if (piwb->original_repos_relpath != NULL)
994251881Speter    {
995251881Speter      SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
996251881Speter      SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
997251881Speter      SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
998251881Speter    }
999251881Speter
1000251881Speter  /* Set properties.  Must be null if presence not normal or incomplete. */
1001251881Speter  assert(piwb->presence == svn_wc__db_status_normal
1002251881Speter         || piwb->presence == svn_wc__db_status_incomplete
1003251881Speter         || piwb->props == NULL);
1004251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1005251881Speter
1006251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
1007251881Speter
1008251881Speter  /* Insert incomplete children, if specified.
1009251881Speter     The children are part of the same op and so have the same op_depth.
1010251881Speter     (The only time we'd want a different depth is during a recursive
1011251881Speter     simple add, but we never insert children here during a simple add.) */
1012251881Speter  if (piwb->kind == svn_node_dir && piwb->children)
1013251881Speter    SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1014251881Speter                                       local_relpath,
1015251881Speter                                       INVALID_REPOS_ID /* inherit repos_id */,
1016251881Speter                                       NULL /* inherit repos_path */,
1017251881Speter                                       piwb->original_revnum,
1018251881Speter                                       piwb->children,
1019251881Speter                                       piwb->op_depth,
1020251881Speter                                       scratch_pool));
1021251881Speter
1022251881Speter  if (piwb->update_actual_props)
1023251881Speter    {
1024251881Speter      /* Cast away const, to allow calling property helpers */
1025251881Speter      apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1026251881Speter      apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1027251881Speter
1028251881Speter      if (base_props != NULL
1029251881Speter          && new_actual_props != NULL
1030251881Speter          && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1031251881Speter        {
1032251881Speter          apr_array_header_t *diffs;
1033251881Speter
1034251881Speter          SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1035251881Speter                                 scratch_pool));
1036251881Speter
1037251881Speter          if (diffs->nelts == 0)
1038251881Speter            new_actual_props = NULL;
1039251881Speter        }
1040251881Speter
1041251881Speter      SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
1042251881Speter                               wcroot->sdb, scratch_pool));
1043251881Speter    }
1044251881Speter
1045251881Speter  if (piwb->kind == svn_node_dir)
1046251881Speter    {
1047251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1048251881Speter                                        STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1049251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1050251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1051251881Speter
1052251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1053251881Speter                                        STMT_DELETE_ACTUAL_EMPTY));
1054251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1055251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1056251881Speter    }
1057251881Speter
1058251881Speter  if (piwb->not_present_op_depth > 0
1059251881Speter      && piwb->not_present_op_depth < piwb->op_depth)
1060251881Speter    {
1061251881Speter      /* And also insert a not-present node to tell the commit processing that
1062251881Speter         a child of the parent node was not copied. */
1063251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1064251881Speter                                        STMT_INSERT_NODE));
1065251881Speter
1066251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1067251881Speter                                wcroot->wc_id, local_relpath,
1068251881Speter                                piwb->not_present_op_depth, parent_relpath,
1069251881Speter                                piwb->original_repos_id,
1070251881Speter                                piwb->original_repos_relpath,
1071251881Speter                                piwb->original_revnum,
1072251881Speter                                presence_map, svn_wc__db_status_not_present,
1073251881Speter                                /* NULL */
1074251881Speter                                kind_map, piwb->kind));
1075251881Speter
1076251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1077251881Speter    }
1078251881Speter
1079251881Speter  SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1080251881Speter  if (piwb->conflict)
1081251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1082251881Speter                                              piwb->conflict, scratch_pool));
1083251881Speter
1084251881Speter  return SVN_NO_ERROR;
1085251881Speter}
1086251881Speter
1087251881Speter
1088251881Speter/* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
1089251881Speter   pointed to the same name.  */
1090251881Speterstatic svn_error_t *
1091251881Speteradd_children_to_hash(apr_hash_t *children,
1092251881Speter                     int stmt_idx,
1093251881Speter                     svn_sqlite__db_t *sdb,
1094251881Speter                     apr_int64_t wc_id,
1095251881Speter                     const char *parent_relpath,
1096251881Speter                     apr_pool_t *result_pool)
1097251881Speter{
1098251881Speter  svn_sqlite__stmt_t *stmt;
1099251881Speter  svn_boolean_t have_row;
1100251881Speter
1101251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
1102251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
1103251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1104251881Speter  while (have_row)
1105251881Speter    {
1106251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1107251881Speter      const char *name = svn_relpath_basename(child_relpath, result_pool);
1108251881Speter
1109251881Speter      svn_hash_sets(children, name, name);
1110251881Speter
1111251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1112251881Speter    }
1113251881Speter
1114251881Speter  return svn_sqlite__reset(stmt);
1115251881Speter}
1116251881Speter
1117251881Speter
1118251881Speter/* Set *CHILDREN to a new array of the (const char *) basenames of the
1119251881Speter   immediate children, whatever their status, of the working node at
1120251881Speter   LOCAL_RELPATH. */
1121251881Speterstatic svn_error_t *
1122251881Spetergather_children2(const apr_array_header_t **children,
1123251881Speter                 svn_wc__db_wcroot_t *wcroot,
1124251881Speter                 const char *local_relpath,
1125251881Speter                 apr_pool_t *result_pool,
1126251881Speter                 apr_pool_t *scratch_pool)
1127251881Speter{
1128251881Speter  apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1129251881Speter  apr_array_header_t *names_array;
1130251881Speter
1131251881Speter  /* All of the names get allocated in RESULT_POOL.  It
1132251881Speter     appears to be faster to use the hash to remove duplicates than to
1133251881Speter     use DISTINCT in the SQL query. */
1134251881Speter  SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
1135251881Speter                               wcroot->sdb, wcroot->wc_id,
1136251881Speter                               local_relpath, result_pool));
1137251881Speter
1138251881Speter  SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1139251881Speter  *children = names_array;
1140251881Speter  return SVN_NO_ERROR;
1141251881Speter}
1142251881Speter
1143251881Speter/* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1144251881Speter   of any status, in all op-depths in the NODES table. */
1145251881Speterstatic svn_error_t *
1146251881Spetergather_children(const apr_array_header_t **children,
1147251881Speter                svn_wc__db_wcroot_t *wcroot,
1148251881Speter                const char *local_relpath,
1149251881Speter                apr_pool_t *result_pool,
1150251881Speter                apr_pool_t *scratch_pool)
1151251881Speter{
1152251881Speter  apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1153251881Speter  apr_array_header_t *names_array;
1154251881Speter
1155251881Speter  /* All of the names get allocated in RESULT_POOL.  It
1156251881Speter     appears to be faster to use the hash to remove duplicates than to
1157251881Speter     use DISTINCT in the SQL query. */
1158251881Speter  SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
1159251881Speter                               wcroot->sdb, wcroot->wc_id,
1160251881Speter                               local_relpath, result_pool));
1161251881Speter
1162251881Speter  SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1163251881Speter  *children = names_array;
1164251881Speter  return SVN_NO_ERROR;
1165251881Speter}
1166251881Speter
1167251881Speter
1168251881Speter/* Set *CHILDREN to a new array of (const char *) names of the children of
1169251881Speter   the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
1170251881Speter   that is, only the children that are at the same op-depth as their parent. */
1171251881Speterstatic svn_error_t *
1172251881Spetergather_repo_children(const apr_array_header_t **children,
1173251881Speter                     svn_wc__db_wcroot_t *wcroot,
1174251881Speter                     const char *local_relpath,
1175251881Speter                     int op_depth,
1176251881Speter                     apr_pool_t *result_pool,
1177251881Speter                     apr_pool_t *scratch_pool)
1178251881Speter{
1179251881Speter  apr_array_header_t *result
1180251881Speter    = apr_array_make(result_pool, 0, sizeof(const char *));
1181251881Speter  svn_sqlite__stmt_t *stmt;
1182251881Speter  svn_boolean_t have_row;
1183251881Speter
1184251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1185251881Speter                                    STMT_SELECT_OP_DEPTH_CHILDREN));
1186251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1187251881Speter                            op_depth));
1188251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1189251881Speter  while (have_row)
1190251881Speter    {
1191251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1192251881Speter
1193251881Speter      /* Allocate the name in RESULT_POOL so we won't have to copy it. */
1194251881Speter      APR_ARRAY_PUSH(result, const char *)
1195251881Speter        = svn_relpath_basename(child_relpath, result_pool);
1196251881Speter
1197251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1198251881Speter    }
1199251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1200251881Speter
1201251881Speter  *children = result;
1202251881Speter  return SVN_NO_ERROR;
1203251881Speter}
1204251881Speter
1205251881Spetersvn_error_t *
1206251881Spetersvn_wc__db_get_children_op_depth(apr_hash_t **children,
1207251881Speter                                 svn_wc__db_wcroot_t *wcroot,
1208251881Speter                                 const char *local_relpath,
1209251881Speter                                 int op_depth,
1210251881Speter                                 apr_pool_t *result_pool,
1211251881Speter                                 apr_pool_t *scratch_pool)
1212251881Speter{
1213251881Speter  svn_sqlite__stmt_t *stmt;
1214251881Speter  svn_boolean_t have_row;
1215251881Speter
1216251881Speter  *children = apr_hash_make(result_pool);
1217251881Speter
1218251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1219251881Speter                                    STMT_SELECT_OP_DEPTH_CHILDREN));
1220251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1221251881Speter                            op_depth));
1222251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1223251881Speter  while (have_row)
1224251881Speter    {
1225251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1226251881Speter      svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
1227251881Speter
1228251881Speter      *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1229251881Speter      svn_hash_sets(*children,
1230251881Speter                    svn_relpath_basename(child_relpath, result_pool),
1231251881Speter                    child_kind);
1232251881Speter
1233251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1234251881Speter    }
1235251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1236251881Speter
1237251881Speter  return SVN_NO_ERROR;
1238251881Speter}
1239251881Speter
1240251881Speter
1241251881Speter/* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1242251881Speter * Else, return FALSE. */
1243251881Speterstatic svn_boolean_t
1244251881Speteris_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1245251881Speter{
1246251881Speter  const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1247251881Speter                                                       child_abspath);
1248251881Speter
1249251881Speter  /* To be an immediate child local_relpath should have one (not empty)
1250251881Speter     component */
1251251881Speter  return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1252251881Speter}
1253251881Speter
1254251881Speter
1255251881Speter/* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1256251881Speterstatic void
1257251881Speterremove_from_access_cache(apr_hash_t *access_cache,
1258251881Speter                         const char *local_abspath)
1259251881Speter{
1260251881Speter  svn_wc_adm_access_t *adm_access;
1261251881Speter
1262251881Speter  adm_access = svn_hash_gets(access_cache, local_abspath);
1263251881Speter  if (adm_access)
1264251881Speter    svn_wc__adm_access_set_entries(adm_access, NULL);
1265251881Speter}
1266251881Speter
1267251881Speter
1268251881Speter/* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1269251881Speter * the specified DEPTH, from the access baton cache in WCROOT.
1270251881Speter * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1271251881Speter *
1272251881Speter * This function must be called when the access baton cache goes stale,
1273251881Speter * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1274251881Speter *
1275251881Speter * Use SCRATCH_POOL for temporary allocations. */
1276251881Speterstatic svn_error_t *
1277251881Speterflush_entries(svn_wc__db_wcroot_t *wcroot,
1278251881Speter              const char *local_abspath,
1279251881Speter              svn_depth_t depth,
1280251881Speter              apr_pool_t *scratch_pool)
1281251881Speter{
1282251881Speter  const char *parent_abspath;
1283251881Speter
1284251881Speter  if (apr_hash_count(wcroot->access_cache) == 0)
1285251881Speter    return SVN_NO_ERROR;
1286251881Speter
1287251881Speter  remove_from_access_cache(wcroot->access_cache, local_abspath);
1288251881Speter
1289251881Speter  if (depth > svn_depth_empty)
1290251881Speter    {
1291251881Speter      apr_hash_index_t *hi;
1292251881Speter
1293251881Speter      /* Flush access batons of children within the specified depth. */
1294251881Speter      for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1295251881Speter           hi;
1296251881Speter           hi = apr_hash_next(hi))
1297251881Speter        {
1298251881Speter          const char *item_abspath = svn__apr_hash_index_key(hi);
1299251881Speter
1300251881Speter          if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1301251881Speter              is_immediate_child_path(local_abspath, item_abspath))
1302251881Speter            {
1303251881Speter              remove_from_access_cache(wcroot->access_cache, item_abspath);
1304251881Speter            }
1305251881Speter          else if (depth == svn_depth_infinity &&
1306251881Speter                   svn_dirent_is_ancestor(local_abspath, item_abspath))
1307251881Speter            {
1308251881Speter              remove_from_access_cache(wcroot->access_cache, item_abspath);
1309251881Speter            }
1310251881Speter        }
1311251881Speter    }
1312251881Speter
1313251881Speter  /* We're going to be overly aggressive here and just flush the parent
1314251881Speter     without doing much checking.  This may hurt performance for
1315251881Speter     legacy API consumers, but that's not our problem. :) */
1316251881Speter  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1317251881Speter  remove_from_access_cache(wcroot->access_cache, parent_abspath);
1318251881Speter
1319251881Speter  return SVN_NO_ERROR;
1320251881Speter}
1321251881Speter
1322251881Speter
1323251881Speter/* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1324251881Speter   not perform its work within a transaction, assuming the caller will
1325251881Speter   manage that.  */
1326251881Speterstatic svn_error_t *
1327251881Speteradd_single_work_item(svn_sqlite__db_t *sdb,
1328251881Speter                     const svn_skel_t *work_item,
1329251881Speter                     apr_pool_t *scratch_pool)
1330251881Speter{
1331251881Speter  svn_stringbuf_t *serialized;
1332251881Speter  svn_sqlite__stmt_t *stmt;
1333251881Speter
1334251881Speter  serialized = svn_skel__unparse(work_item, scratch_pool);
1335251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1336251881Speter  SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1337251881Speter  return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1338251881Speter}
1339251881Speter
1340251881Speter
1341251881Speter/* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1342251881Speter   SKEL is usually passed to the various wc_db operation functions. It may
1343251881Speter   be NULL, indicating no additional work items are needed, it may be a
1344251881Speter   single work item, or it may be a list of work items.  */
1345251881Speterstatic svn_error_t *
1346251881Speteradd_work_items(svn_sqlite__db_t *sdb,
1347251881Speter               const svn_skel_t *skel,
1348251881Speter               apr_pool_t *scratch_pool)
1349251881Speter{
1350251881Speter  apr_pool_t *iterpool;
1351251881Speter
1352251881Speter  /* Maybe there are no work items to insert.  */
1353251881Speter  if (skel == NULL)
1354251881Speter    return SVN_NO_ERROR;
1355251881Speter
1356251881Speter  /* Should have a list.  */
1357251881Speter  SVN_ERR_ASSERT(!skel->is_atom);
1358251881Speter
1359251881Speter  /* Is the list a single work item? Or a list of work items?  */
1360251881Speter  if (SVN_WC__SINGLE_WORK_ITEM(skel))
1361251881Speter    return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1362251881Speter
1363251881Speter  /* SKEL is a list-of-lists, aka list of work items.  */
1364251881Speter
1365251881Speter  iterpool = svn_pool_create(scratch_pool);
1366251881Speter  for (skel = skel->children; skel; skel = skel->next)
1367251881Speter    {
1368251881Speter      svn_pool_clear(iterpool);
1369251881Speter
1370251881Speter      SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1371251881Speter    }
1372251881Speter  svn_pool_destroy(iterpool);
1373251881Speter
1374251881Speter  return SVN_NO_ERROR;
1375251881Speter}
1376251881Speter
1377251881Speter
1378251881Speter/* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH.  */
1379251881Speterstatic svn_error_t *
1380251881Speterdoes_node_exist(svn_boolean_t *exists,
1381251881Speter                const svn_wc__db_wcroot_t *wcroot,
1382251881Speter                const char *local_relpath)
1383251881Speter{
1384251881Speter  svn_sqlite__stmt_t *stmt;
1385251881Speter
1386251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1387251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1388251881Speter  SVN_ERR(svn_sqlite__step(exists, stmt));
1389251881Speter
1390251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
1391251881Speter}
1392251881Speter
1393251881Speter/* Helper for create_db(). Initializes our wc.db schema.
1394251881Speter */
1395251881Speterstatic svn_error_t *
1396251881Speterinit_db(/* output values */
1397251881Speter        apr_int64_t *repos_id,
1398251881Speter        apr_int64_t *wc_id,
1399251881Speter        /* input values */
1400251881Speter        svn_sqlite__db_t *db,
1401251881Speter        const char *repos_root_url,
1402251881Speter        const char *repos_uuid,
1403251881Speter        const char *root_node_repos_relpath,
1404251881Speter        svn_revnum_t root_node_revision,
1405251881Speter        svn_depth_t root_node_depth,
1406251881Speter        apr_pool_t *scratch_pool)
1407251881Speter{
1408251881Speter  svn_sqlite__stmt_t *stmt;
1409251881Speter
1410251881Speter  /* Create the database's schema.  */
1411251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1412251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1413251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1414251881Speter  SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1415251881Speter
1416251881Speter  /* Insert the repository. */
1417251881Speter  SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1418251881Speter                          db, scratch_pool));
1419251881Speter
1420251881Speter  /* Insert the wcroot. */
1421251881Speter  /* ### Right now, this just assumes wc metadata is being stored locally. */
1422251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1423251881Speter  SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1424251881Speter
1425251881Speter  if (root_node_repos_relpath)
1426251881Speter    {
1427251881Speter      svn_wc__db_status_t status = svn_wc__db_status_normal;
1428251881Speter
1429251881Speter      if (root_node_revision > 0)
1430251881Speter        status = svn_wc__db_status_incomplete; /* Will be filled by update */
1431251881Speter
1432251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1433251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1434251881Speter                                *wc_id,              /* 1 */
1435251881Speter                                "",                  /* 2 */
1436251881Speter                                0,                   /* op_depth is 0 for base */
1437251881Speter                                NULL,                /* 4 */
1438251881Speter                                *repos_id,
1439251881Speter                                root_node_repos_relpath,
1440251881Speter                                root_node_revision,
1441251881Speter                                presence_map, status, /* 8 */
1442251881Speter                                svn_token__to_word(depth_map,
1443251881Speter                                                   root_node_depth),
1444251881Speter                                kind_map, svn_node_dir /* 10 */));
1445251881Speter
1446251881Speter      SVN_ERR(svn_sqlite__insert(NULL, stmt));
1447251881Speter    }
1448251881Speter
1449251881Speter  return SVN_NO_ERROR;
1450251881Speter}
1451251881Speter
1452251881Speter/* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1453251881Speter   records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1454251881Speter   REPOSITORY and for WC_ID into WCROOT.  Return the DB connection
1455251881Speter   in *SDB.
1456251881Speter
1457251881Speter   If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1458251881Speter   the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1459251881Speter   revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1460251881Speter   */
1461251881Speterstatic svn_error_t *
1462251881Spetercreate_db(svn_sqlite__db_t **sdb,
1463251881Speter          apr_int64_t *repos_id,
1464251881Speter          apr_int64_t *wc_id,
1465251881Speter          const char *dir_abspath,
1466251881Speter          const char *repos_root_url,
1467251881Speter          const char *repos_uuid,
1468251881Speter          const char *sdb_fname,
1469251881Speter          const char *root_node_repos_relpath,
1470251881Speter          svn_revnum_t root_node_revision,
1471251881Speter          svn_depth_t root_node_depth,
1472251881Speter          svn_boolean_t exclusive,
1473251881Speter          apr_pool_t *result_pool,
1474251881Speter          apr_pool_t *scratch_pool)
1475251881Speter{
1476251881Speter  SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1477251881Speter                                  svn_sqlite__mode_rwcreate, exclusive,
1478251881Speter                                  NULL /* my_statements */,
1479251881Speter                                  result_pool, scratch_pool));
1480251881Speter
1481251881Speter  SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1482251881Speter                                *sdb, repos_root_url, repos_uuid,
1483251881Speter                                root_node_repos_relpath, root_node_revision,
1484251881Speter                                root_node_depth, scratch_pool),
1485251881Speter                        *sdb);
1486251881Speter
1487251881Speter  return SVN_NO_ERROR;
1488251881Speter}
1489251881Speter
1490251881Speter
1491251881Spetersvn_error_t *
1492251881Spetersvn_wc__db_init(svn_wc__db_t *db,
1493251881Speter                const char *local_abspath,
1494251881Speter                const char *repos_relpath,
1495251881Speter                const char *repos_root_url,
1496251881Speter                const char *repos_uuid,
1497251881Speter                svn_revnum_t initial_rev,
1498251881Speter                svn_depth_t depth,
1499251881Speter                apr_pool_t *scratch_pool)
1500251881Speter{
1501251881Speter  svn_sqlite__db_t *sdb;
1502251881Speter  apr_int64_t repos_id;
1503251881Speter  apr_int64_t wc_id;
1504251881Speter  svn_wc__db_wcroot_t *wcroot;
1505251881Speter  svn_boolean_t sqlite_exclusive = FALSE;
1506251881Speter
1507251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1508251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1509251881Speter  SVN_ERR_ASSERT(depth == svn_depth_empty
1510251881Speter                 || depth == svn_depth_files
1511251881Speter                 || depth == svn_depth_immediates
1512251881Speter                 || depth == svn_depth_infinity);
1513251881Speter
1514251881Speter  /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
1515251881Speter
1516251881Speter  SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1517251881Speter                              SVN_CONFIG_SECTION_WORKING_COPY,
1518251881Speter                              SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1519251881Speter                              FALSE));
1520251881Speter
1521251881Speter  /* Create the SDB and insert the basic rows.  */
1522251881Speter  SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1523251881Speter                    repos_uuid, SDB_FILE,
1524251881Speter                    repos_relpath, initial_rev, depth, sqlite_exclusive,
1525251881Speter                    db->state_pool, scratch_pool));
1526251881Speter
1527251881Speter  /* Create the WCROOT for this directory.  */
1528251881Speter  SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1529251881Speter                        apr_pstrdup(db->state_pool, local_abspath),
1530251881Speter                        sdb, wc_id, FORMAT_FROM_SDB,
1531251881Speter                        FALSE /* auto-upgrade */,
1532251881Speter                        FALSE /* enforce_empty_wq */,
1533251881Speter                        db->state_pool, scratch_pool));
1534251881Speter
1535251881Speter  /* The WCROOT is complete. Stash it into DB.  */
1536251881Speter  svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1537251881Speter
1538251881Speter  return SVN_NO_ERROR;
1539251881Speter}
1540251881Speter
1541251881Speter
1542251881Spetersvn_error_t *
1543251881Spetersvn_wc__db_to_relpath(const char **local_relpath,
1544251881Speter                      svn_wc__db_t *db,
1545251881Speter                      const char *wri_abspath,
1546251881Speter                      const char *local_abspath,
1547251881Speter                      apr_pool_t *result_pool,
1548251881Speter                      apr_pool_t *scratch_pool)
1549251881Speter{
1550251881Speter  svn_wc__db_wcroot_t *wcroot;
1551251881Speter  const char *relpath;
1552251881Speter
1553251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1554251881Speter
1555251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1556251881Speter                              wri_abspath, result_pool, scratch_pool));
1557251881Speter
1558251881Speter  /* This function is indirectly called from the upgrade code, so we
1559251881Speter     can't verify the wcroot here. Just check that it is not NULL */
1560251881Speter  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1561251881Speter
1562251881Speter  if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1563251881Speter    {
1564251881Speter      *local_relpath = apr_pstrdup(result_pool,
1565251881Speter                                   svn_dirent_skip_ancestor(wcroot->abspath,
1566251881Speter                                                            local_abspath));
1567251881Speter    }
1568251881Speter  else
1569251881Speter    /* Probably moving from $TMP. Should we allow this? */
1570251881Speter    *local_relpath = apr_pstrdup(result_pool, local_abspath);
1571251881Speter
1572251881Speter  return SVN_NO_ERROR;
1573251881Speter}
1574251881Speter
1575251881Speter
1576251881Spetersvn_error_t *
1577251881Spetersvn_wc__db_from_relpath(const char **local_abspath,
1578251881Speter                        svn_wc__db_t *db,
1579251881Speter                        const char *wri_abspath,
1580251881Speter                        const char *local_relpath,
1581251881Speter                        apr_pool_t *result_pool,
1582251881Speter                        apr_pool_t *scratch_pool)
1583251881Speter{
1584251881Speter  svn_wc__db_wcroot_t *wcroot;
1585251881Speter  const char *unused_relpath;
1586251881Speter#if 0
1587251881Speter  SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1588251881Speter#endif
1589251881Speter
1590251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1591251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1592251881Speter
1593251881Speter  /* This function is indirectly called from the upgrade code, so we
1594251881Speter     can't verify the wcroot here. Just check that it is not NULL */
1595251881Speter  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1596251881Speter
1597251881Speter
1598251881Speter  *local_abspath = svn_dirent_join(wcroot->abspath,
1599251881Speter                                   local_relpath,
1600251881Speter                                   result_pool);
1601251881Speter  return SVN_NO_ERROR;
1602251881Speter}
1603251881Speter
1604251881Speter
1605251881Spetersvn_error_t *
1606251881Spetersvn_wc__db_get_wcroot(const char **wcroot_abspath,
1607251881Speter                      svn_wc__db_t *db,
1608251881Speter                      const char *wri_abspath,
1609251881Speter                      apr_pool_t *result_pool,
1610251881Speter                      apr_pool_t *scratch_pool)
1611251881Speter{
1612251881Speter  svn_wc__db_wcroot_t *wcroot;
1613251881Speter  const char *unused_relpath;
1614251881Speter
1615251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1616251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1617251881Speter
1618251881Speter  /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1619251881Speter     where call upgrade */
1620251881Speter  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1621251881Speter
1622251881Speter  *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1623251881Speter
1624251881Speter  return SVN_NO_ERROR;
1625251881Speter}
1626251881Speter
1627251881Speter
1628251881Spetersvn_error_t *
1629251881Spetersvn_wc__db_base_add_directory(svn_wc__db_t *db,
1630251881Speter                              const char *local_abspath,
1631251881Speter                              const char *wri_abspath,
1632251881Speter                              const char *repos_relpath,
1633251881Speter                              const char *repos_root_url,
1634251881Speter                              const char *repos_uuid,
1635251881Speter                              svn_revnum_t revision,
1636251881Speter                              const apr_hash_t *props,
1637251881Speter                              svn_revnum_t changed_rev,
1638251881Speter                              apr_time_t changed_date,
1639251881Speter                              const char *changed_author,
1640251881Speter                              const apr_array_header_t *children,
1641251881Speter                              svn_depth_t depth,
1642251881Speter                              apr_hash_t *dav_cache,
1643251881Speter                              const svn_skel_t *conflict,
1644251881Speter                              svn_boolean_t update_actual_props,
1645251881Speter                              apr_hash_t *new_actual_props,
1646251881Speter                              apr_array_header_t *new_iprops,
1647251881Speter                              const svn_skel_t *work_items,
1648251881Speter                              apr_pool_t *scratch_pool)
1649251881Speter{
1650251881Speter  svn_wc__db_wcroot_t *wcroot;
1651251881Speter  const char *local_relpath;
1652251881Speter  insert_base_baton_t ibb;
1653251881Speter
1654251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1655251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1656251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1657251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1658251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1659251881Speter  SVN_ERR_ASSERT(props != NULL);
1660251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1661251881Speter#if 0
1662251881Speter  SVN_ERR_ASSERT(children != NULL);
1663251881Speter#endif
1664251881Speter
1665251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1666251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1667251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1668251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1669251881Speter
1670251881Speter  blank_ibb(&ibb);
1671251881Speter
1672251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1673251881Speter  ibb.repos_root_url = repos_root_url;
1674251881Speter  ibb.repos_uuid = repos_uuid;
1675251881Speter
1676251881Speter  ibb.status = svn_wc__db_status_normal;
1677251881Speter  ibb.kind = svn_node_dir;
1678251881Speter  ibb.repos_relpath = repos_relpath;
1679251881Speter  ibb.revision = revision;
1680251881Speter
1681251881Speter  ibb.iprops = new_iprops;
1682251881Speter  ibb.props = props;
1683251881Speter  ibb.changed_rev = changed_rev;
1684251881Speter  ibb.changed_date = changed_date;
1685251881Speter  ibb.changed_author = changed_author;
1686251881Speter
1687251881Speter  ibb.children = children;
1688251881Speter  ibb.depth = depth;
1689251881Speter
1690251881Speter  ibb.dav_cache = dav_cache;
1691251881Speter  ibb.conflict = conflict;
1692251881Speter  ibb.work_items = work_items;
1693251881Speter
1694251881Speter  if (update_actual_props)
1695251881Speter    {
1696251881Speter      ibb.update_actual_props = TRUE;
1697251881Speter      ibb.new_actual_props = new_actual_props;
1698251881Speter    }
1699251881Speter
1700251881Speter  /* Insert the directory and all its children transactionally.
1701251881Speter
1702251881Speter     Note: old children can stick around, even if they are no longer present
1703251881Speter     in this directory's revision.  */
1704251881Speter  SVN_WC__DB_WITH_TXN(
1705251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1706251881Speter            wcroot);
1707251881Speter
1708251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1709251881Speter  return SVN_NO_ERROR;
1710251881Speter}
1711251881Speter
1712251881Spetersvn_error_t *
1713251881Spetersvn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1714251881Speter                                         const char *local_abspath,
1715251881Speter                                         const char *repos_relpath,
1716251881Speter                                         const char *repos_root_url,
1717251881Speter                                         const char *repos_uuid,
1718251881Speter                                         svn_revnum_t revision,
1719251881Speter                                         svn_depth_t depth,
1720251881Speter                                         svn_boolean_t insert_base_deleted,
1721251881Speter                                         svn_boolean_t delete_working,
1722251881Speter                                         svn_skel_t *conflict,
1723251881Speter                                         svn_skel_t *work_items,
1724251881Speter                                         apr_pool_t *scratch_pool)
1725251881Speter{
1726251881Speter  svn_wc__db_wcroot_t *wcroot;
1727251881Speter  const char *local_relpath;
1728251881Speter  struct insert_base_baton_t ibb;
1729251881Speter
1730251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1731251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1732251881Speter  SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1733251881Speter
1734251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1735251881Speter                                                db, local_abspath,
1736251881Speter                                                scratch_pool, scratch_pool));
1737251881Speter
1738251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1739251881Speter
1740251881Speter  blank_ibb(&ibb);
1741251881Speter
1742251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1743251881Speter  ibb.repos_root_url = repos_root_url;
1744251881Speter  ibb.repos_uuid = repos_uuid;
1745251881Speter
1746251881Speter  ibb.status = svn_wc__db_status_incomplete;
1747251881Speter  ibb.kind = svn_node_dir;
1748251881Speter  ibb.repos_relpath = repos_relpath;
1749251881Speter  ibb.revision = revision;
1750251881Speter  ibb.depth = depth;
1751251881Speter  ibb.insert_base_deleted = insert_base_deleted;
1752251881Speter  ibb.delete_working = delete_working;
1753251881Speter
1754251881Speter  ibb.conflict = conflict;
1755251881Speter  ibb.work_items = work_items;
1756251881Speter
1757251881Speter  SVN_WC__DB_WITH_TXN(
1758251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1759251881Speter            wcroot);
1760251881Speter
1761251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1762251881Speter
1763251881Speter  return SVN_NO_ERROR;
1764251881Speter}
1765251881Speter
1766251881Speter
1767251881Spetersvn_error_t *
1768251881Spetersvn_wc__db_base_add_file(svn_wc__db_t *db,
1769251881Speter                         const char *local_abspath,
1770251881Speter                         const char *wri_abspath,
1771251881Speter                         const char *repos_relpath,
1772251881Speter                         const char *repos_root_url,
1773251881Speter                         const char *repos_uuid,
1774251881Speter                         svn_revnum_t revision,
1775251881Speter                         const apr_hash_t *props,
1776251881Speter                         svn_revnum_t changed_rev,
1777251881Speter                         apr_time_t changed_date,
1778251881Speter                         const char *changed_author,
1779251881Speter                         const svn_checksum_t *checksum,
1780251881Speter                         apr_hash_t *dav_cache,
1781251881Speter                         svn_boolean_t delete_working,
1782251881Speter                         svn_boolean_t update_actual_props,
1783251881Speter                         apr_hash_t *new_actual_props,
1784251881Speter                         apr_array_header_t *new_iprops,
1785251881Speter                         svn_boolean_t keep_recorded_info,
1786251881Speter                         svn_boolean_t insert_base_deleted,
1787251881Speter                         const svn_skel_t *conflict,
1788251881Speter                         const svn_skel_t *work_items,
1789251881Speter                         apr_pool_t *scratch_pool)
1790251881Speter{
1791251881Speter  svn_wc__db_wcroot_t *wcroot;
1792251881Speter  const char *local_relpath;
1793251881Speter  insert_base_baton_t ibb;
1794251881Speter
1795251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1796251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1797251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1798251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1799251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1800251881Speter  SVN_ERR_ASSERT(props != NULL);
1801251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1802251881Speter  SVN_ERR_ASSERT(checksum != NULL);
1803251881Speter
1804251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1805251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1806251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1807251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1808251881Speter
1809251881Speter  blank_ibb(&ibb);
1810251881Speter
1811251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1812251881Speter  ibb.repos_root_url = repos_root_url;
1813251881Speter  ibb.repos_uuid = repos_uuid;
1814251881Speter
1815251881Speter  ibb.status = svn_wc__db_status_normal;
1816251881Speter  ibb.kind = svn_node_file;
1817251881Speter  ibb.repos_relpath = repos_relpath;
1818251881Speter  ibb.revision = revision;
1819251881Speter
1820251881Speter  ibb.props = props;
1821251881Speter  ibb.changed_rev = changed_rev;
1822251881Speter  ibb.changed_date = changed_date;
1823251881Speter  ibb.changed_author = changed_author;
1824251881Speter
1825251881Speter  ibb.checksum = checksum;
1826251881Speter
1827251881Speter  ibb.dav_cache = dav_cache;
1828251881Speter  ibb.iprops = new_iprops;
1829251881Speter
1830251881Speter  if (update_actual_props)
1831251881Speter    {
1832251881Speter      ibb.update_actual_props = TRUE;
1833251881Speter      ibb.new_actual_props = new_actual_props;
1834251881Speter    }
1835251881Speter
1836251881Speter  ibb.keep_recorded_info = keep_recorded_info;
1837251881Speter  ibb.insert_base_deleted = insert_base_deleted;
1838251881Speter  ibb.delete_working = delete_working;
1839251881Speter
1840251881Speter  ibb.conflict = conflict;
1841251881Speter  ibb.work_items = work_items;
1842251881Speter
1843251881Speter  SVN_WC__DB_WITH_TXN(
1844251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1845251881Speter            wcroot);
1846251881Speter
1847251881Speter  /* If this used to be a directory we should remove children so pass
1848251881Speter   * depth infinity. */
1849251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1850251881Speter                        scratch_pool));
1851251881Speter  return SVN_NO_ERROR;
1852251881Speter}
1853251881Speter
1854251881Speter
1855251881Spetersvn_error_t *
1856251881Spetersvn_wc__db_base_add_symlink(svn_wc__db_t *db,
1857251881Speter                            const char *local_abspath,
1858251881Speter                            const char *wri_abspath,
1859251881Speter                            const char *repos_relpath,
1860251881Speter                            const char *repos_root_url,
1861251881Speter                            const char *repos_uuid,
1862251881Speter                            svn_revnum_t revision,
1863251881Speter                            const apr_hash_t *props,
1864251881Speter                            svn_revnum_t changed_rev,
1865251881Speter                            apr_time_t changed_date,
1866251881Speter                            const char *changed_author,
1867251881Speter                            const char *target,
1868251881Speter                            apr_hash_t *dav_cache,
1869251881Speter                            svn_boolean_t delete_working,
1870251881Speter                            svn_boolean_t update_actual_props,
1871251881Speter                            apr_hash_t *new_actual_props,
1872251881Speter                            apr_array_header_t *new_iprops,
1873251881Speter                            svn_boolean_t keep_recorded_info,
1874251881Speter                            svn_boolean_t insert_base_deleted,
1875251881Speter                            const svn_skel_t *conflict,
1876251881Speter                            const svn_skel_t *work_items,
1877251881Speter                            apr_pool_t *scratch_pool)
1878251881Speter{
1879251881Speter  svn_wc__db_wcroot_t *wcroot;
1880251881Speter  const char *local_relpath;
1881251881Speter  insert_base_baton_t ibb;
1882251881Speter
1883251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1884251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1885251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1886251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1887251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1888251881Speter  SVN_ERR_ASSERT(props != NULL);
1889251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1890251881Speter  SVN_ERR_ASSERT(target != NULL);
1891251881Speter
1892251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1893251881Speter                              wri_abspath, scratch_pool, scratch_pool));
1894251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1895251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1896251881Speter  blank_ibb(&ibb);
1897251881Speter
1898251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1899251881Speter  ibb.repos_root_url = repos_root_url;
1900251881Speter  ibb.repos_uuid = repos_uuid;
1901251881Speter
1902251881Speter  ibb.status = svn_wc__db_status_normal;
1903251881Speter  ibb.kind = svn_node_symlink;
1904251881Speter  ibb.repos_relpath = repos_relpath;
1905251881Speter  ibb.revision = revision;
1906251881Speter
1907251881Speter  ibb.props = props;
1908251881Speter  ibb.changed_rev = changed_rev;
1909251881Speter  ibb.changed_date = changed_date;
1910251881Speter  ibb.changed_author = changed_author;
1911251881Speter
1912251881Speter  ibb.target = target;
1913251881Speter
1914251881Speter  ibb.dav_cache = dav_cache;
1915251881Speter  ibb.iprops = new_iprops;
1916251881Speter
1917251881Speter  if (update_actual_props)
1918251881Speter    {
1919251881Speter      ibb.update_actual_props = TRUE;
1920251881Speter      ibb.new_actual_props = new_actual_props;
1921251881Speter    }
1922251881Speter
1923251881Speter  ibb.keep_recorded_info = keep_recorded_info;
1924251881Speter  ibb.insert_base_deleted = insert_base_deleted;
1925251881Speter  ibb.delete_working = delete_working;
1926251881Speter
1927251881Speter  ibb.conflict = conflict;
1928251881Speter  ibb.work_items = work_items;
1929251881Speter
1930251881Speter  SVN_WC__DB_WITH_TXN(
1931251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1932251881Speter            wcroot);
1933251881Speter
1934251881Speter  /* If this used to be a directory we should remove children so pass
1935251881Speter   * depth infinity. */
1936251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1937251881Speter                        scratch_pool));
1938251881Speter  return SVN_NO_ERROR;
1939251881Speter}
1940251881Speter
1941251881Speter
1942251881Speterstatic svn_error_t *
1943251881Speteradd_excluded_or_not_present_node(svn_wc__db_t *db,
1944251881Speter                                 const char *local_abspath,
1945251881Speter                                 const char *repos_relpath,
1946251881Speter                                 const char *repos_root_url,
1947251881Speter                                 const char *repos_uuid,
1948251881Speter                                 svn_revnum_t revision,
1949251881Speter                                 svn_node_kind_t kind,
1950251881Speter                                 svn_wc__db_status_t status,
1951251881Speter                                 const svn_skel_t *conflict,
1952251881Speter                                 const svn_skel_t *work_items,
1953251881Speter                                 apr_pool_t *scratch_pool)
1954251881Speter{
1955251881Speter  svn_wc__db_wcroot_t *wcroot;
1956251881Speter  const char *local_relpath;
1957251881Speter  insert_base_baton_t ibb;
1958251881Speter  const char *dir_abspath, *name;
1959251881Speter
1960251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1961251881Speter  SVN_ERR_ASSERT(repos_relpath != NULL);
1962251881Speter  SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1963251881Speter  SVN_ERR_ASSERT(repos_uuid != NULL);
1964251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1965251881Speter  SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1966251881Speter                 || status == svn_wc__db_status_excluded
1967251881Speter                 || status == svn_wc__db_status_not_present);
1968251881Speter
1969251881Speter  /* These absent presence nodes are only useful below a parent node that is
1970251881Speter     present. To avoid problems with working copies obstructing the child
1971251881Speter     we calculate the wcroot and local_relpath of the parent and then add
1972251881Speter     our own relpath. */
1973251881Speter
1974251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1975251881Speter
1976251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1977251881Speter                              dir_abspath, scratch_pool, scratch_pool));
1978251881Speter  VERIFY_USABLE_WCROOT(wcroot);
1979251881Speter
1980251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1981251881Speter
1982251881Speter  blank_ibb(&ibb);
1983251881Speter
1984251881Speter  /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1985251881Speter  ibb.repos_root_url = repos_root_url;
1986251881Speter  ibb.repos_uuid = repos_uuid;
1987251881Speter
1988251881Speter  ibb.status = status;
1989251881Speter  ibb.kind = kind;
1990251881Speter  ibb.repos_relpath = repos_relpath;
1991251881Speter  ibb.revision = revision;
1992251881Speter
1993251881Speter  /* Depending upon KIND, any of these might get used. */
1994251881Speter  ibb.children = NULL;
1995251881Speter  ibb.depth = svn_depth_unknown;
1996251881Speter  ibb.checksum = NULL;
1997251881Speter  ibb.target = NULL;
1998251881Speter
1999251881Speter  ibb.conflict = conflict;
2000251881Speter  ibb.work_items = work_items;
2001251881Speter
2002251881Speter  SVN_WC__DB_WITH_TXN(
2003251881Speter            insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2004251881Speter            wcroot);
2005251881Speter
2006251881Speter  /* If this used to be a directory we should remove children so pass
2007251881Speter   * depth infinity. */
2008251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2009251881Speter                        scratch_pool));
2010251881Speter
2011251881Speter  return SVN_NO_ERROR;
2012251881Speter}
2013251881Speter
2014251881Speter
2015251881Spetersvn_error_t *
2016251881Spetersvn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2017251881Speter                                  const char *local_abspath,
2018251881Speter                                  const char *repos_relpath,
2019251881Speter                                  const char *repos_root_url,
2020251881Speter                                  const char *repos_uuid,
2021251881Speter                                  svn_revnum_t revision,
2022251881Speter                                  svn_node_kind_t kind,
2023251881Speter                                  svn_wc__db_status_t status,
2024251881Speter                                  const svn_skel_t *conflict,
2025251881Speter                                  const svn_skel_t *work_items,
2026251881Speter                                  apr_pool_t *scratch_pool)
2027251881Speter{
2028251881Speter  SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2029251881Speter                 || status == svn_wc__db_status_excluded);
2030251881Speter
2031251881Speter  return add_excluded_or_not_present_node(
2032251881Speter    db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2033251881Speter    kind, status, conflict, work_items, scratch_pool);
2034251881Speter}
2035251881Speter
2036251881Speter
2037251881Spetersvn_error_t *
2038251881Spetersvn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2039251881Speter                                     const char *local_abspath,
2040251881Speter                                     const char *repos_relpath,
2041251881Speter                                     const char *repos_root_url,
2042251881Speter                                     const char *repos_uuid,
2043251881Speter                                     svn_revnum_t revision,
2044251881Speter                                     svn_node_kind_t kind,
2045251881Speter                                     const svn_skel_t *conflict,
2046251881Speter                                     const svn_skel_t *work_items,
2047251881Speter                                     apr_pool_t *scratch_pool)
2048251881Speter{
2049251881Speter  return add_excluded_or_not_present_node(
2050251881Speter    db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2051251881Speter    kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2052251881Speter}
2053251881Speter
2054251881Speter/* Recursively clear moved-here information at the copy-half of the move
2055251881Speter * which moved the node at SRC_RELPATH away. This transforms the move into
2056251881Speter * a simple copy. */
2057251881Speterstatic svn_error_t *
2058251881Speterclear_moved_here(const char *src_relpath,
2059251881Speter                 svn_wc__db_wcroot_t *wcroot,
2060251881Speter                 apr_pool_t *scratch_pool)
2061251881Speter{
2062251881Speter  svn_sqlite__stmt_t *stmt;
2063251881Speter  const char *dst_relpath;
2064251881Speter
2065251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2066251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2067251881Speter                            src_relpath, relpath_depth(src_relpath)));
2068251881Speter  SVN_ERR(svn_sqlite__step_row(stmt));
2069251881Speter  dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2070251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
2071251881Speter
2072251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2073251881Speter                                    STMT_CLEAR_MOVED_HERE_RECURSIVE));
2074251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2075251881Speter                            dst_relpath, relpath_depth(dst_relpath)));
2076251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2077251881Speter
2078251881Speter  return SVN_NO_ERROR;
2079251881Speter}
2080251881Speter
2081251881Speter/* The body of svn_wc__db_base_remove().
2082251881Speter */
2083251881Speterstatic svn_error_t *
2084251881Speterdb_base_remove(svn_wc__db_wcroot_t *wcroot,
2085251881Speter               const char *local_relpath,
2086251881Speter               svn_wc__db_t *db, /* For checking conflicts */
2087251881Speter               svn_boolean_t keep_as_working,
2088251881Speter               svn_boolean_t queue_deletes,
2089253734Speter               svn_boolean_t remove_locks,
2090251881Speter               svn_revnum_t not_present_revision,
2091251881Speter               svn_skel_t *conflict,
2092251881Speter               svn_skel_t *work_items,
2093251881Speter               apr_pool_t *scratch_pool)
2094251881Speter{
2095251881Speter  svn_sqlite__stmt_t *stmt;
2096251881Speter  svn_boolean_t have_row;
2097251881Speter  svn_wc__db_status_t status;
2098251881Speter  apr_int64_t repos_id;
2099251881Speter  const char *repos_relpath;
2100251881Speter  svn_node_kind_t kind;
2101251881Speter  svn_boolean_t keep_working;
2102251881Speter
2103251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2104251881Speter                                            &repos_relpath, &repos_id,
2105251881Speter                                            NULL, NULL, NULL, NULL, NULL,
2106251881Speter                                            NULL, NULL, NULL, NULL, NULL,
2107251881Speter                                            wcroot, local_relpath,
2108251881Speter                                            scratch_pool, scratch_pool));
2109251881Speter
2110253734Speter  if (remove_locks)
2111253734Speter    {
2112253734Speter      svn_sqlite__stmt_t *lock_stmt;
2113253734Speter
2114253734Speter      SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2115253734Speter                                        STMT_DELETE_LOCK_RECURSIVELY));
2116253734Speter      SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2117253734Speter      SVN_ERR(svn_sqlite__step_done(lock_stmt));
2118253734Speter    }
2119253734Speter
2120251881Speter  if (status == svn_wc__db_status_normal
2121251881Speter      && keep_as_working)
2122251881Speter    {
2123251881Speter      SVN_ERR(svn_wc__db_op_make_copy(db,
2124251881Speter                                      svn_dirent_join(wcroot->abspath,
2125251881Speter                                                      local_relpath,
2126251881Speter                                                      scratch_pool),
2127251881Speter                                      NULL, NULL,
2128251881Speter                                      scratch_pool));
2129251881Speter      keep_working = TRUE;
2130251881Speter    }
2131251881Speter  else
2132251881Speter    {
2133251881Speter      /* Check if there is already a working node */
2134251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2135251881Speter                                        STMT_SELECT_WORKING_NODE));
2136251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2137251881Speter      SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2138251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
2139251881Speter    }
2140251881Speter
2141251881Speter  /* Step 1: Create workqueue operations to remove files and dirs in the
2142251881Speter     local-wc */
2143251881Speter  if (!keep_working
2144251881Speter      && queue_deletes
2145251881Speter      && (status == svn_wc__db_status_normal
2146251881Speter          || status == svn_wc__db_status_incomplete))
2147251881Speter    {
2148251881Speter      svn_skel_t *work_item;
2149251881Speter      const char *local_abspath;
2150251881Speter
2151251881Speter      local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2152251881Speter                                      scratch_pool);
2153251881Speter      if (kind == svn_node_dir)
2154251881Speter        {
2155251881Speter          apr_pool_t *iterpool;
2156251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2157251881Speter                                            STMT_SELECT_BASE_PRESENT));
2158251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2159251881Speter
2160251881Speter          iterpool = svn_pool_create(scratch_pool);
2161251881Speter
2162251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2163251881Speter
2164251881Speter          while (have_row)
2165251881Speter            {
2166251881Speter              const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2167251881Speter              svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2168251881Speter                                                              kind_map);
2169251881Speter              const char *node_abspath;
2170251881Speter              svn_error_t *err;
2171251881Speter
2172251881Speter              svn_pool_clear(iterpool);
2173251881Speter
2174251881Speter              node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2175251881Speter                                             iterpool);
2176251881Speter
2177251881Speter              if (node_kind == svn_node_dir)
2178251881Speter                err = svn_wc__wq_build_dir_remove(&work_item,
2179251881Speter                                                  db, wcroot->abspath,
2180251881Speter                                                  node_abspath, FALSE,
2181251881Speter                                                  iterpool, iterpool);
2182251881Speter              else
2183251881Speter                err = svn_wc__wq_build_file_remove(&work_item,
2184251881Speter                                                   db,
2185251881Speter                                                   wcroot->abspath,
2186251881Speter                                                   node_abspath,
2187251881Speter                                                   iterpool, iterpool);
2188251881Speter
2189251881Speter              if (!err)
2190251881Speter                err = add_work_items(wcroot->sdb, work_item, iterpool);
2191251881Speter              if (err)
2192251881Speter                return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2193251881Speter
2194251881Speter              SVN_ERR(svn_sqlite__step(&have_row, stmt));
2195251881Speter           }
2196251881Speter
2197251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
2198251881Speter
2199251881Speter          SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2200251881Speter                                              db, wcroot->abspath,
2201251881Speter                                              local_abspath, FALSE,
2202251881Speter                                              scratch_pool, iterpool));
2203251881Speter          svn_pool_destroy(iterpool);
2204251881Speter        }
2205251881Speter      else
2206251881Speter        SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2207251881Speter                                             db, wcroot->abspath,
2208251881Speter                                             local_abspath,
2209251881Speter                                             scratch_pool, scratch_pool));
2210251881Speter
2211251881Speter      SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2212251881Speter    }
2213251881Speter
2214251881Speter  /* Step 2: Delete ACTUAL nodes */
2215251881Speter  if (! keep_working)
2216251881Speter    {
2217251881Speter      /* There won't be a record in NODE left for this node, so we want
2218251881Speter         to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2219251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2220251881Speter                                        STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2221251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2222251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2223251881Speter    }
2224251881Speter  else if (! keep_as_working)
2225251881Speter    {
2226251881Speter      /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2227251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2228251881Speter                                       STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2229251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2230251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2231251881Speter    }
2232251881Speter  /* Else: Everything has been turned into a copy, so we want to keep all
2233251881Speter           ACTUAL_NODE records */
2234251881Speter
2235251881Speter  /* Step 3: Delete WORKING nodes */
2236251881Speter  if (conflict)
2237251881Speter    {
2238251881Speter      apr_pool_t *iterpool;
2239251881Speter
2240251881Speter      /*
2241251881Speter       * When deleting a conflicted node, moves of any moved-outside children
2242251881Speter       * of the node must be broken. Else, the destination will still be marked
2243251881Speter       * moved-here after the move source disappears from the working copy.
2244251881Speter       *
2245251881Speter       * ### FIXME: It would be nicer to have the conflict resolver
2246251881Speter       * break the move instead. It might also be a good idea to
2247251881Speter       * flag a tree conflict on each moved-away child. But doing so
2248251881Speter       * might introduce actual-only nodes without direct parents,
2249251881Speter       * and we're not yet sure if other existing code is prepared
2250251881Speter       * to handle such nodes. To be revisited post-1.8.
2251251881Speter       */
2252251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2253251881Speter                                        STMT_SELECT_MOVED_OUTSIDE));
2254251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2255251881Speter                                             local_relpath,
2256251881Speter                                             relpath_depth(local_relpath)));
2257251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2258251881Speter      iterpool = svn_pool_create(scratch_pool);
2259251881Speter      while (have_row)
2260251881Speter        {
2261251881Speter          const char *child_relpath;
2262251881Speter          svn_error_t *err;
2263251881Speter
2264251881Speter          svn_pool_clear(iterpool);
2265251881Speter          child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2266251881Speter          err = clear_moved_here(child_relpath, wcroot, iterpool);
2267251881Speter          if (err)
2268251881Speter            return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2269251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
2270251881Speter        }
2271251881Speter      svn_pool_destroy(iterpool);
2272251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
2273251881Speter    }
2274251881Speter  if (keep_working)
2275251881Speter    {
2276251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2277251881Speter                                        STMT_DELETE_WORKING_BASE_DELETE));
2278251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2279251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2280251881Speter    }
2281251881Speter  else
2282251881Speter    {
2283251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2284251881Speter                                        STMT_DELETE_WORKING_RECURSIVE));
2285251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2286251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2287251881Speter    }
2288251881Speter
2289251881Speter  /* Step 4: Delete the BASE node descendants */
2290251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2291251881Speter                                    STMT_DELETE_BASE_RECURSIVE));
2292251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2293251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2294251881Speter
2295251881Speter  /* Step 5: handle the BASE node itself */
2296251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2297251881Speter                                    STMT_DELETE_BASE_NODE));
2298251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2299251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2300251881Speter
2301251881Speter  SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2302251881Speter                                           scratch_pool));
2303251881Speter
2304251881Speter  /* Step 6: Delete actual node if we don't keep working */
2305251881Speter  if (! keep_working)
2306251881Speter    {
2307251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2308251881Speter                                        STMT_DELETE_ACTUAL_NODE));
2309251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2310251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
2311251881Speter    }
2312251881Speter
2313251881Speter  if (SVN_IS_VALID_REVNUM(not_present_revision))
2314251881Speter    {
2315251881Speter      struct insert_base_baton_t ibb;
2316251881Speter      blank_ibb(&ibb);
2317251881Speter
2318251881Speter      ibb.repos_id = repos_id;
2319251881Speter      ibb.status = svn_wc__db_status_not_present;
2320251881Speter      ibb.kind = kind;
2321251881Speter      ibb.repos_relpath = repos_relpath;
2322251881Speter      ibb.revision = not_present_revision;
2323251881Speter
2324251881Speter      /* Depending upon KIND, any of these might get used. */
2325251881Speter      ibb.children = NULL;
2326251881Speter      ibb.depth = svn_depth_unknown;
2327251881Speter      ibb.checksum = NULL;
2328251881Speter      ibb.target = NULL;
2329251881Speter
2330251881Speter      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2331251881Speter    }
2332251881Speter
2333251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2334251881Speter  if (conflict)
2335251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2336251881Speter                                              conflict, scratch_pool));
2337251881Speter
2338251881Speter  return SVN_NO_ERROR;
2339251881Speter}
2340251881Speter
2341251881Speter
2342251881Spetersvn_error_t *
2343251881Spetersvn_wc__db_base_remove(svn_wc__db_t *db,
2344251881Speter                       const char *local_abspath,
2345251881Speter                       svn_boolean_t keep_as_working,
2346251881Speter                       svn_boolean_t queue_deletes,
2347253734Speter                       svn_boolean_t remove_locks,
2348251881Speter                       svn_revnum_t not_present_revision,
2349251881Speter                       svn_skel_t *conflict,
2350251881Speter                       svn_skel_t *work_items,
2351251881Speter                       apr_pool_t *scratch_pool)
2352251881Speter{
2353251881Speter  svn_wc__db_wcroot_t *wcroot;
2354251881Speter  const char *local_relpath;
2355251881Speter
2356251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2357251881Speter
2358251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2359251881Speter                              local_abspath, scratch_pool, scratch_pool));
2360251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2361251881Speter
2362251881Speter  SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2363251881Speter                                     db, keep_as_working, queue_deletes,
2364253734Speter                                     remove_locks, not_present_revision,
2365251881Speter                                     conflict, work_items, scratch_pool),
2366251881Speter                      wcroot);
2367251881Speter
2368251881Speter  /* If this used to be a directory we should remove children so pass
2369251881Speter   * depth infinity. */
2370251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2371251881Speter                        scratch_pool));
2372251881Speter
2373251881Speter  return SVN_NO_ERROR;
2374251881Speter}
2375251881Speter
2376251881Speter
2377251881Spetersvn_error_t *
2378251881Spetersvn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2379251881Speter                                  svn_node_kind_t *kind,
2380251881Speter                                  svn_revnum_t *revision,
2381251881Speter                                  const char **repos_relpath,
2382251881Speter                                  apr_int64_t *repos_id,
2383251881Speter                                  svn_revnum_t *changed_rev,
2384251881Speter                                  apr_time_t *changed_date,
2385251881Speter                                  const char **changed_author,
2386251881Speter                                  svn_depth_t *depth,
2387251881Speter                                  const svn_checksum_t **checksum,
2388251881Speter                                  const char **target,
2389251881Speter                                  svn_wc__db_lock_t **lock,
2390251881Speter                                  svn_boolean_t *had_props,
2391251881Speter                                  apr_hash_t **props,
2392251881Speter                                  svn_boolean_t *update_root,
2393251881Speter                                  svn_wc__db_wcroot_t *wcroot,
2394251881Speter                                  const char *local_relpath,
2395251881Speter                                  apr_pool_t *result_pool,
2396251881Speter                                  apr_pool_t *scratch_pool)
2397251881Speter{
2398251881Speter  svn_sqlite__stmt_t *stmt;
2399251881Speter  svn_boolean_t have_row;
2400251881Speter  svn_error_t *err = SVN_NO_ERROR;
2401251881Speter
2402251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2403251881Speter                                    lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2404251881Speter                                         : STMT_SELECT_BASE_NODE));
2405251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2406251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2407251881Speter
2408251881Speter  if (have_row)
2409251881Speter    {
2410251881Speter      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2411251881Speter                                                                 presence_map);
2412251881Speter      svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2413251881Speter
2414251881Speter      if (kind)
2415251881Speter        {
2416251881Speter          *kind = node_kind;
2417251881Speter        }
2418251881Speter      if (status)
2419251881Speter        {
2420251881Speter          *status = node_status;
2421251881Speter        }
2422251881Speter      repos_location_from_columns(repos_id, revision, repos_relpath,
2423251881Speter                                  stmt, 0, 4, 1, result_pool);
2424251881Speter      SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2425251881Speter      SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2426251881Speter      if (lock)
2427251881Speter        {
2428251881Speter          *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2429251881Speter        }
2430251881Speter      if (changed_rev)
2431251881Speter        {
2432251881Speter          *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2433251881Speter        }
2434251881Speter      if (changed_date)
2435251881Speter        {
2436251881Speter          *changed_date = svn_sqlite__column_int64(stmt, 8);
2437251881Speter        }
2438251881Speter      if (changed_author)
2439251881Speter        {
2440251881Speter          /* Result may be NULL. */
2441251881Speter          *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2442251881Speter        }
2443251881Speter      if (depth)
2444251881Speter        {
2445251881Speter          if (node_kind != svn_node_dir)
2446251881Speter            {
2447251881Speter              *depth = svn_depth_unknown;
2448251881Speter            }
2449251881Speter          else
2450251881Speter            {
2451251881Speter              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2452251881Speter                                                     svn_depth_unknown);
2453251881Speter            }
2454251881Speter        }
2455251881Speter      if (checksum)
2456251881Speter        {
2457251881Speter          if (node_kind != svn_node_file)
2458251881Speter            {
2459251881Speter              *checksum = NULL;
2460251881Speter            }
2461251881Speter          else
2462251881Speter            {
2463251881Speter              err = svn_sqlite__column_checksum(checksum, stmt, 5,
2464251881Speter                                                result_pool);
2465251881Speter              if (err != NULL)
2466251881Speter                err = svn_error_createf(
2467251881Speter                        err->apr_err, err,
2468251881Speter                        _("The node '%s' has a corrupt checksum value."),
2469251881Speter                        path_for_error_message(wcroot, local_relpath,
2470251881Speter                                               scratch_pool));
2471251881Speter            }
2472251881Speter        }
2473251881Speter      if (target)
2474251881Speter        {
2475251881Speter          if (node_kind != svn_node_symlink)
2476251881Speter            *target = NULL;
2477251881Speter          else
2478251881Speter            *target = svn_sqlite__column_text(stmt, 11, result_pool);
2479251881Speter        }
2480251881Speter      if (had_props)
2481251881Speter        {
2482251881Speter          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2483251881Speter        }
2484251881Speter      if (props)
2485251881Speter        {
2486251881Speter          if (node_status == svn_wc__db_status_normal
2487251881Speter              || node_status == svn_wc__db_status_incomplete)
2488251881Speter            {
2489251881Speter              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2490251881Speter                                                    result_pool, scratch_pool));
2491251881Speter              if (*props == NULL)
2492251881Speter                *props = apr_hash_make(result_pool);
2493251881Speter            }
2494251881Speter          else
2495251881Speter            {
2496251881Speter              assert(svn_sqlite__column_is_null(stmt, 13));
2497251881Speter              *props = NULL;
2498251881Speter            }
2499251881Speter        }
2500251881Speter      if (update_root)
2501251881Speter        {
2502251881Speter          /* It's an update root iff it's a file external. */
2503251881Speter          *update_root = svn_sqlite__column_boolean(stmt, 14);
2504251881Speter        }
2505251881Speter    }
2506251881Speter  else
2507251881Speter    {
2508251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2509251881Speter                              _("The node '%s' was not found."),
2510251881Speter                              path_for_error_message(wcroot, local_relpath,
2511251881Speter                                                     scratch_pool));
2512251881Speter    }
2513251881Speter
2514251881Speter  /* Note: given the composition, no need to wrap for tracing.  */
2515251881Speter  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2516251881Speter}
2517251881Speter
2518251881Speter
2519251881Spetersvn_error_t *
2520251881Spetersvn_wc__db_base_get_info(svn_wc__db_status_t *status,
2521251881Speter                         svn_node_kind_t *kind,
2522251881Speter                         svn_revnum_t *revision,
2523251881Speter                         const char **repos_relpath,
2524251881Speter                         const char **repos_root_url,
2525251881Speter                         const char **repos_uuid,
2526251881Speter                         svn_revnum_t *changed_rev,
2527251881Speter                         apr_time_t *changed_date,
2528251881Speter                         const char **changed_author,
2529251881Speter                         svn_depth_t *depth,
2530251881Speter                         const svn_checksum_t **checksum,
2531251881Speter                         const char **target,
2532251881Speter                         svn_wc__db_lock_t **lock,
2533251881Speter                         svn_boolean_t *had_props,
2534251881Speter                         apr_hash_t **props,
2535251881Speter                         svn_boolean_t *update_root,
2536251881Speter                         svn_wc__db_t *db,
2537251881Speter                         const char *local_abspath,
2538251881Speter                         apr_pool_t *result_pool,
2539251881Speter                         apr_pool_t *scratch_pool)
2540251881Speter{
2541251881Speter  svn_wc__db_wcroot_t *wcroot;
2542251881Speter  const char *local_relpath;
2543251881Speter  apr_int64_t repos_id;
2544251881Speter
2545251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2546251881Speter
2547251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2548251881Speter                              local_abspath, scratch_pool, scratch_pool));
2549251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2550251881Speter
2551251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
2552251881Speter                                            repos_relpath, &repos_id,
2553251881Speter                                            changed_rev, changed_date,
2554251881Speter                                            changed_author, depth,
2555251881Speter                                            checksum, target, lock,
2556251881Speter                                            had_props, props, update_root,
2557251881Speter                                            wcroot, local_relpath,
2558251881Speter                                            result_pool, scratch_pool));
2559251881Speter  SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2560251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2561251881Speter                                      wcroot->sdb, repos_id, result_pool));
2562251881Speter
2563251881Speter  return SVN_NO_ERROR;
2564251881Speter}
2565251881Speter
2566251881Spetersvn_error_t *
2567251881Spetersvn_wc__db_base_get_children_info(apr_hash_t **nodes,
2568251881Speter                                  svn_wc__db_t *db,
2569251881Speter                                  const char *dir_abspath,
2570251881Speter                                  apr_pool_t *result_pool,
2571251881Speter                                  apr_pool_t *scratch_pool)
2572251881Speter{
2573251881Speter  svn_wc__db_wcroot_t *wcroot;
2574251881Speter  const char *local_relpath;
2575251881Speter  svn_sqlite__stmt_t *stmt;
2576251881Speter  svn_boolean_t have_row;
2577251881Speter
2578251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2579251881Speter
2580251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2581251881Speter                              dir_abspath, scratch_pool, scratch_pool));
2582251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2583251881Speter
2584251881Speter  *nodes = apr_hash_make(result_pool);
2585251881Speter
2586251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2587251881Speter                                    STMT_SELECT_BASE_CHILDREN_INFO));
2588251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2589251881Speter
2590251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2591251881Speter
2592251881Speter  while (have_row)
2593251881Speter    {
2594251881Speter      struct svn_wc__db_base_info_t *info;
2595251881Speter      svn_error_t *err;
2596251881Speter      apr_int64_t repos_id;
2597251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2598251881Speter      const char *name = svn_relpath_basename(child_relpath, result_pool);
2599251881Speter
2600251881Speter      info = apr_pcalloc(result_pool, sizeof(*info));
2601251881Speter
2602251881Speter      repos_id = svn_sqlite__column_int64(stmt, 1);
2603251881Speter      info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2604251881Speter      info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2605251881Speter      info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2606251881Speter      info->revnum = svn_sqlite__column_revnum(stmt, 5);
2607251881Speter
2608251881Speter      info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2609251881Speter                                                  svn_depth_unknown);
2610251881Speter
2611251881Speter      info->update_root = svn_sqlite__column_boolean(stmt, 7);
2612251881Speter
2613251881Speter      info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2614251881Speter
2615251881Speter      err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2616251881Speter                                        wcroot->sdb, repos_id, result_pool);
2617251881Speter
2618251881Speter      if (err)
2619251881Speter        return svn_error_trace(
2620251881Speter                 svn_error_compose_create(err,
2621251881Speter                                          svn_sqlite__reset(stmt)));
2622251881Speter
2623251881Speter
2624251881Speter      svn_hash_sets(*nodes, name, info);
2625251881Speter
2626251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2627251881Speter    }
2628251881Speter
2629251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
2630251881Speter
2631251881Speter  return SVN_NO_ERROR;
2632251881Speter}
2633251881Speter
2634251881Speter
2635251881Spetersvn_error_t *
2636251881Spetersvn_wc__db_base_get_props(apr_hash_t **props,
2637251881Speter                          svn_wc__db_t *db,
2638251881Speter                          const char *local_abspath,
2639251881Speter                          apr_pool_t *result_pool,
2640251881Speter                          apr_pool_t *scratch_pool)
2641251881Speter{
2642251881Speter  svn_wc__db_status_t presence;
2643251881Speter
2644251881Speter  SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2645251881Speter                                   NULL, NULL, NULL, NULL, NULL,
2646251881Speter                                   NULL, NULL, NULL, NULL, props, NULL,
2647251881Speter                                   db, local_abspath,
2648251881Speter                                   result_pool, scratch_pool));
2649251881Speter  if (presence != svn_wc__db_status_normal
2650251881Speter      && presence != svn_wc__db_status_incomplete)
2651251881Speter    {
2652251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2653251881Speter                               _("The node '%s' has a BASE status that"
2654251881Speter                                  " has no properties."),
2655251881Speter                               svn_dirent_local_style(local_abspath,
2656251881Speter                                                      scratch_pool));
2657251881Speter    }
2658251881Speter
2659251881Speter  return SVN_NO_ERROR;
2660251881Speter}
2661251881Speter
2662251881Speter
2663251881Spetersvn_error_t *
2664251881Spetersvn_wc__db_base_get_children(const apr_array_header_t **children,
2665251881Speter                             svn_wc__db_t *db,
2666251881Speter                             const char *local_abspath,
2667251881Speter                             apr_pool_t *result_pool,
2668251881Speter                             apr_pool_t *scratch_pool)
2669251881Speter{
2670251881Speter  svn_wc__db_wcroot_t *wcroot;
2671251881Speter  const char *local_relpath;
2672251881Speter
2673251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2674251881Speter
2675251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2676251881Speter                                             local_abspath,
2677251881Speter                                             scratch_pool, scratch_pool));
2678251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2679251881Speter
2680251881Speter  return gather_repo_children(children, wcroot, local_relpath, 0,
2681251881Speter                              result_pool, scratch_pool);
2682251881Speter}
2683251881Speter
2684251881Speter
2685251881Spetersvn_error_t *
2686251881Spetersvn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2687251881Speter                              const char *local_abspath,
2688251881Speter                              const apr_hash_t *props,
2689251881Speter                              apr_pool_t *scratch_pool)
2690251881Speter{
2691251881Speter  svn_sqlite__stmt_t *stmt;
2692251881Speter  int affected_rows;
2693251881Speter
2694251881Speter  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2695251881Speter                                 STMT_UPDATE_BASE_NODE_DAV_CACHE,
2696251881Speter                                 scratch_pool));
2697251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2698251881Speter
2699251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2700251881Speter
2701251881Speter  if (affected_rows != 1)
2702251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2703251881Speter                             _("The node '%s' was not found."),
2704251881Speter                             svn_dirent_local_style(local_abspath,
2705251881Speter                                                    scratch_pool));
2706251881Speter
2707251881Speter  return SVN_NO_ERROR;
2708251881Speter}
2709251881Speter
2710251881Speter
2711251881Spetersvn_error_t *
2712251881Spetersvn_wc__db_base_get_dav_cache(apr_hash_t **props,
2713251881Speter                              svn_wc__db_t *db,
2714251881Speter                              const char *local_abspath,
2715251881Speter                              apr_pool_t *result_pool,
2716251881Speter                              apr_pool_t *scratch_pool)
2717251881Speter{
2718251881Speter  svn_sqlite__stmt_t *stmt;
2719251881Speter  svn_boolean_t have_row;
2720251881Speter
2721251881Speter  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2722251881Speter                                 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2723251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2724251881Speter  if (!have_row)
2725251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2726251881Speter                             svn_sqlite__reset(stmt),
2727251881Speter                             _("The node '%s' was not found."),
2728251881Speter                             svn_dirent_local_style(local_abspath,
2729251881Speter                                                    scratch_pool));
2730251881Speter
2731251881Speter  SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2732251881Speter                                        scratch_pool));
2733251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
2734251881Speter}
2735251881Speter
2736251881Speter
2737251881Spetersvn_error_t *
2738251881Spetersvn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2739251881Speter                                          const char *local_abspath,
2740251881Speter                                          apr_pool_t *scratch_pool)
2741251881Speter{
2742251881Speter  svn_wc__db_wcroot_t *wcroot;
2743251881Speter  const char *local_relpath;
2744251881Speter  svn_sqlite__stmt_t *stmt;
2745251881Speter
2746251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2747251881Speter                                             db, local_abspath,
2748251881Speter                                             scratch_pool, scratch_pool));
2749251881Speter  VERIFY_USABLE_WCROOT(wcroot);
2750251881Speter
2751251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2752251881Speter                                    STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2753251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2754251881Speter
2755251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
2756251881Speter
2757251881Speter  return SVN_NO_ERROR;
2758251881Speter}
2759251881Speter
2760251881Speter
2761251881Spetersvn_error_t *
2762251881Spetersvn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2763251881Speter                          svn_node_kind_t *kind,
2764251881Speter                          svn_revnum_t *revision,
2765251881Speter                          const char **repos_relpath,
2766251881Speter                          apr_int64_t *repos_id,
2767251881Speter                          svn_revnum_t *changed_rev,
2768251881Speter                          apr_time_t *changed_date,
2769251881Speter                          const char **changed_author,
2770251881Speter                          svn_depth_t *depth,
2771251881Speter                          const svn_checksum_t **checksum,
2772251881Speter                          const char **target,
2773251881Speter                          svn_boolean_t *had_props,
2774251881Speter                          apr_hash_t **props,
2775251881Speter                          svn_wc__db_wcroot_t *wcroot,
2776251881Speter                          const char *local_relpath,
2777251881Speter                          int op_depth,
2778251881Speter                          apr_pool_t *result_pool,
2779251881Speter                          apr_pool_t *scratch_pool)
2780251881Speter{
2781251881Speter  svn_sqlite__stmt_t *stmt;
2782251881Speter  svn_boolean_t have_row;
2783251881Speter  svn_error_t *err = SVN_NO_ERROR;
2784251881Speter
2785251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2786251881Speter                                    STMT_SELECT_DEPTH_NODE));
2787251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2788251881Speter                            wcroot->wc_id, local_relpath, op_depth));
2789251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2790251881Speter
2791251881Speter  if (have_row)
2792251881Speter    {
2793251881Speter      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2794251881Speter                                                                 presence_map);
2795251881Speter      svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2796251881Speter
2797251881Speter      if (kind)
2798251881Speter        {
2799251881Speter          *kind = node_kind;
2800251881Speter        }
2801251881Speter      if (status)
2802251881Speter        {
2803251881Speter          *status = node_status;
2804251881Speter
2805251881Speter          if (op_depth > 0)
2806251881Speter            SVN_ERR(convert_to_working_status(status, *status));
2807251881Speter        }
2808251881Speter      repos_location_from_columns(repos_id, revision, repos_relpath,
2809251881Speter                                  stmt, 0, 4, 1, result_pool);
2810251881Speter
2811251881Speter      if (changed_rev)
2812251881Speter        {
2813251881Speter          *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2814251881Speter        }
2815251881Speter      if (changed_date)
2816251881Speter        {
2817251881Speter          *changed_date = svn_sqlite__column_int64(stmt, 8);
2818251881Speter        }
2819251881Speter      if (changed_author)
2820251881Speter        {
2821251881Speter          /* Result may be NULL. */
2822251881Speter          *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2823251881Speter        }
2824251881Speter      if (depth)
2825251881Speter        {
2826251881Speter          if (node_kind != svn_node_dir)
2827251881Speter            {
2828251881Speter              *depth = svn_depth_unknown;
2829251881Speter            }
2830251881Speter          else
2831251881Speter            {
2832251881Speter              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2833251881Speter                                                     svn_depth_unknown);
2834251881Speter            }
2835251881Speter        }
2836251881Speter      if (checksum)
2837251881Speter        {
2838251881Speter          if (node_kind != svn_node_file)
2839251881Speter            {
2840251881Speter              *checksum = NULL;
2841251881Speter            }
2842251881Speter          else
2843251881Speter            {
2844251881Speter              err = svn_sqlite__column_checksum(checksum, stmt, 5,
2845251881Speter                                                result_pool);
2846251881Speter              if (err != NULL)
2847251881Speter                err = svn_error_createf(
2848251881Speter                        err->apr_err, err,
2849251881Speter                        _("The node '%s' has a corrupt checksum value."),
2850251881Speter                        path_for_error_message(wcroot, local_relpath,
2851251881Speter                                               scratch_pool));
2852251881Speter            }
2853251881Speter        }
2854251881Speter      if (target)
2855251881Speter        {
2856251881Speter          if (node_kind != svn_node_symlink)
2857251881Speter            *target = NULL;
2858251881Speter          else
2859251881Speter            *target = svn_sqlite__column_text(stmt, 11, result_pool);
2860251881Speter        }
2861251881Speter      if (had_props)
2862251881Speter        {
2863251881Speter          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2864251881Speter        }
2865251881Speter      if (props)
2866251881Speter        {
2867251881Speter          if (node_status == svn_wc__db_status_normal
2868251881Speter              || node_status == svn_wc__db_status_incomplete)
2869251881Speter            {
2870251881Speter              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2871251881Speter                                                    result_pool, scratch_pool));
2872251881Speter              if (*props == NULL)
2873251881Speter                *props = apr_hash_make(result_pool);
2874251881Speter            }
2875251881Speter          else
2876251881Speter            {
2877251881Speter              assert(svn_sqlite__column_is_null(stmt, 13));
2878251881Speter              *props = NULL;
2879251881Speter            }
2880251881Speter        }
2881251881Speter    }
2882251881Speter  else
2883251881Speter    {
2884251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2885251881Speter                              _("The node '%s' was not found."),
2886251881Speter                              path_for_error_message(wcroot, local_relpath,
2887251881Speter                                                     scratch_pool));
2888251881Speter    }
2889251881Speter
2890251881Speter  /* Note: given the composition, no need to wrap for tracing.  */
2891251881Speter  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2892251881Speter}
2893251881Speter
2894251881Speter
2895251881Speter/* Baton for passing args to with_triggers(). */
2896251881Speterstruct with_triggers_baton_t {
2897251881Speter  int create_trigger;
2898251881Speter  int drop_trigger;
2899251881Speter  svn_wc__db_txn_callback_t cb_func;
2900251881Speter  void *cb_baton;
2901251881Speter};
2902251881Speter
2903251881Speter/* Helper for creating SQLite triggers, running the main transaction
2904251881Speter   callback, and then dropping the triggers.  It guarantees that the
2905251881Speter   triggers will not survive the transaction.  This could be used for
2906251881Speter   any general prefix/postscript statements where the postscript
2907251881Speter   *must* be executed if the transaction completes.
2908251881Speter
2909251881Speter   Implements svn_wc__db_txn_callback_t. */
2910251881Speterstatic svn_error_t *
2911251881Speterwith_triggers(void *baton,
2912251881Speter              svn_wc__db_wcroot_t *wcroot,
2913251881Speter              const char *local_relpath,
2914251881Speter              apr_pool_t *scratch_pool)
2915251881Speter{
2916251881Speter  struct with_triggers_baton_t *b = baton;
2917251881Speter  svn_error_t *err1;
2918251881Speter  svn_error_t *err2;
2919251881Speter
2920251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
2921251881Speter
2922251881Speter  err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
2923251881Speter
2924251881Speter  err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
2925251881Speter
2926251881Speter  return svn_error_trace(svn_error_compose_create(err1, err2));
2927251881Speter}
2928251881Speter
2929251881Speter
2930251881Speter/* Prototype for the "work callback" used by with_finalization().  */
2931251881Spetertypedef svn_error_t * (*work_callback_t)(
2932251881Speter                          void *baton,
2933251881Speter                          svn_wc__db_wcroot_t *wcroot,
2934251881Speter                          svn_cancel_func_t cancel_func,
2935251881Speter                          void *cancel_baton,
2936251881Speter                          svn_wc_notify_func2_t notify_func,
2937251881Speter                          void *notify_baton,
2938251881Speter                          apr_pool_t *scratch_pool);
2939251881Speter
2940251881Speter/* Utility function to provide several features, with a guaranteed
2941251881Speter   finalization (ie. to drop temporary tables).
2942251881Speter
2943251881Speter   1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
2944251881Speter      sqlite transaction
2945251881Speter   2) if (1) is successful and a NOTIFY_FUNC is provided, then run
2946251881Speter      the "work" step: WORK_CB(WORK_BATON).
2947251881Speter   3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
2948251881Speter      from the above two steps.
2949251881Speter
2950251881Speter   CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
2951251881Speter   typical values. These are passed to the work callback, which typically
2952251881Speter   provides notification about the work done by TXN_CB.  */
2953251881Speterstatic svn_error_t *
2954251881Speterwith_finalization(svn_wc__db_wcroot_t *wcroot,
2955251881Speter                  const char *local_relpath,
2956251881Speter                  svn_wc__db_txn_callback_t txn_cb,
2957251881Speter                  void *txn_baton,
2958251881Speter                  work_callback_t work_cb,
2959251881Speter                  void *work_baton,
2960251881Speter                  svn_cancel_func_t cancel_func,
2961251881Speter                  void *cancel_baton,
2962251881Speter                  svn_wc_notify_func2_t notify_func,
2963251881Speter                  void *notify_baton,
2964251881Speter                  int finalize_stmt_idx,
2965251881Speter                  apr_pool_t *scratch_pool)
2966251881Speter{
2967251881Speter  svn_error_t *err1;
2968251881Speter  svn_error_t *err2;
2969251881Speter
2970251881Speter  err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
2971251881Speter                             scratch_pool);
2972251881Speter
2973251881Speter  if (err1 == NULL && notify_func != NULL)
2974251881Speter    {
2975251881Speter      err2 = work_cb(work_baton, wcroot,
2976251881Speter                     cancel_func, cancel_baton,
2977251881Speter                     notify_func, notify_baton,
2978251881Speter                     scratch_pool);
2979251881Speter      err1 = svn_error_compose_create(err1, err2);
2980251881Speter    }
2981251881Speter
2982251881Speter  err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
2983251881Speter
2984251881Speter  return svn_error_trace(svn_error_compose_create(err1, err2));
2985251881Speter}
2986251881Speter
2987251881Speter
2988251881Speter/* Initialize the baton with appropriate "blank" values. This allows the
2989251881Speter   insertion function to leave certain columns null.  */
2990251881Speterstatic void
2991251881Speterblank_ieb(insert_external_baton_t *ieb)
2992251881Speter{
2993251881Speter  memset(ieb, 0, sizeof(*ieb));
2994251881Speter  ieb->revision = SVN_INVALID_REVNUM;
2995251881Speter  ieb->changed_rev = SVN_INVALID_REVNUM;
2996251881Speter  ieb->repos_id = INVALID_REPOS_ID;
2997251881Speter
2998251881Speter  ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
2999251881Speter  ieb->recorded_revision = SVN_INVALID_REVNUM;
3000251881Speter}
3001251881Speter
3002251881Speter/* Insert the externals row represented by (insert_external_baton_t *) BATON.
3003251881Speter *
3004251881Speter * Implements svn_wc__db_txn_callback_t. */
3005251881Speterstatic svn_error_t *
3006251881Speterinsert_external_node(const insert_external_baton_t *ieb,
3007251881Speter                     svn_wc__db_wcroot_t *wcroot,
3008251881Speter                     const char *local_relpath,
3009251881Speter                     apr_pool_t *scratch_pool)
3010251881Speter{
3011251881Speter  svn_wc__db_status_t status;
3012251881Speter  svn_error_t *err;
3013251881Speter  svn_boolean_t update_root;
3014251881Speter  apr_int64_t repos_id;
3015251881Speter  svn_sqlite__stmt_t *stmt;
3016251881Speter
3017251881Speter  if (ieb->repos_id != INVALID_REPOS_ID)
3018251881Speter    repos_id = ieb->repos_id;
3019251881Speter  else
3020251881Speter    SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3021251881Speter                            wcroot->sdb, scratch_pool));
3022251881Speter
3023251881Speter  /* And there must be no existing BASE node or it must be a file external */
3024251881Speter  err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3025251881Speter                                          NULL, NULL, NULL, NULL, NULL,
3026251881Speter                                          NULL, NULL, NULL, NULL, &update_root,
3027251881Speter                                          wcroot, local_relpath,
3028251881Speter                                          scratch_pool, scratch_pool);
3029251881Speter  if (err)
3030251881Speter    {
3031251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3032251881Speter        return svn_error_trace(err);
3033251881Speter
3034251881Speter      svn_error_clear(err);
3035251881Speter    }
3036251881Speter  else if (status == svn_wc__db_status_normal && !update_root)
3037251881Speter    return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3038251881Speter
3039251881Speter  if (ieb->kind == svn_node_file
3040251881Speter      || ieb->kind == svn_node_symlink)
3041251881Speter    {
3042251881Speter      struct insert_base_baton_t ibb;
3043251881Speter
3044251881Speter      blank_ibb(&ibb);
3045251881Speter
3046251881Speter      ibb.status          = svn_wc__db_status_normal;
3047251881Speter      ibb.kind            = ieb->kind;
3048251881Speter
3049251881Speter      ibb.repos_id        = repos_id;
3050251881Speter      ibb.repos_relpath   = ieb->repos_relpath;
3051251881Speter      ibb.revision        = ieb->revision;
3052251881Speter
3053251881Speter      ibb.props           = ieb->props;
3054251881Speter      ibb.iprops          = ieb->iprops;
3055251881Speter      ibb.changed_rev     = ieb->changed_rev;
3056251881Speter      ibb.changed_date    = ieb->changed_date;
3057251881Speter      ibb.changed_author  = ieb->changed_author;
3058251881Speter
3059251881Speter      ibb.dav_cache       = ieb->dav_cache;
3060251881Speter
3061251881Speter      ibb.checksum        = ieb->checksum;
3062251881Speter      ibb.target          = ieb->target;
3063251881Speter
3064251881Speter      ibb.conflict        = ieb->conflict;
3065251881Speter
3066251881Speter      ibb.update_actual_props = ieb->update_actual_props;
3067251881Speter      ibb.new_actual_props    = ieb->new_actual_props;
3068251881Speter
3069251881Speter      ibb.keep_recorded_info  = ieb->keep_recorded_info;
3070251881Speter
3071251881Speter      ibb.work_items      = ieb->work_items;
3072251881Speter
3073251881Speter      ibb.file_external = TRUE;
3074251881Speter
3075251881Speter      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3076251881Speter    }
3077251881Speter  else
3078251881Speter    SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3079251881Speter
3080251881Speter  /* The externals table only support presence normal and excluded */
3081251881Speter  SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3082251881Speter                 || ieb->presence == svn_wc__db_status_excluded);
3083251881Speter
3084251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3085251881Speter
3086251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3087251881Speter                            wcroot->wc_id,
3088251881Speter                            local_relpath,
3089251881Speter                            svn_relpath_dirname(local_relpath,
3090251881Speter                                                scratch_pool),
3091251881Speter                            presence_map, ieb->presence,
3092251881Speter                            kind_map, ieb->kind,
3093251881Speter                            ieb->record_ancestor_relpath,
3094251881Speter                            repos_id,
3095251881Speter                            ieb->recorded_repos_relpath));
3096251881Speter
3097251881Speter  if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3098251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3099251881Speter
3100251881Speter  if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3101251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3102251881Speter
3103251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
3104251881Speter
3105251881Speter  return SVN_NO_ERROR;
3106251881Speter}
3107251881Speter
3108251881Spetersvn_error_t *
3109251881Spetersvn_wc__db_external_add_file(svn_wc__db_t *db,
3110251881Speter                             const char *local_abspath,
3111251881Speter                             const char *wri_abspath,
3112251881Speter
3113251881Speter                             const char *repos_relpath,
3114251881Speter                             const char *repos_root_url,
3115251881Speter                             const char *repos_uuid,
3116251881Speter                             svn_revnum_t revision,
3117251881Speter
3118251881Speter                             const apr_hash_t *props,
3119251881Speter                             apr_array_header_t *iprops,
3120251881Speter
3121251881Speter                             svn_revnum_t changed_rev,
3122251881Speter                             apr_time_t changed_date,
3123251881Speter                             const char *changed_author,
3124251881Speter
3125251881Speter                             const svn_checksum_t *checksum,
3126251881Speter
3127251881Speter                             const apr_hash_t *dav_cache,
3128251881Speter
3129251881Speter                             const char *record_ancestor_abspath,
3130251881Speter                             const char *recorded_repos_relpath,
3131251881Speter                             svn_revnum_t recorded_peg_revision,
3132251881Speter                             svn_revnum_t recorded_revision,
3133251881Speter
3134251881Speter                             svn_boolean_t update_actual_props,
3135251881Speter                             apr_hash_t *new_actual_props,
3136251881Speter
3137251881Speter                             svn_boolean_t keep_recorded_info,
3138251881Speter                             const svn_skel_t *conflict,
3139251881Speter                             const svn_skel_t *work_items,
3140251881Speter                             apr_pool_t *scratch_pool)
3141251881Speter{
3142251881Speter  svn_wc__db_wcroot_t *wcroot;
3143251881Speter  const char *local_relpath;
3144251881Speter  insert_external_baton_t ieb;
3145251881Speter
3146251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3147251881Speter
3148251881Speter  if (! wri_abspath)
3149251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3150251881Speter
3151251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3152251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3153251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3154251881Speter
3155251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3156251881Speter                                        record_ancestor_abspath));
3157251881Speter
3158251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3159251881Speter
3160251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3161251881Speter
3162251881Speter  blank_ieb(&ieb);
3163251881Speter
3164251881Speter  ieb.kind = svn_node_file;
3165251881Speter  ieb.presence = svn_wc__db_status_normal;
3166251881Speter
3167251881Speter  ieb.repos_root_url = repos_root_url;
3168251881Speter  ieb.repos_uuid = repos_uuid;
3169251881Speter
3170251881Speter  ieb.repos_relpath = repos_relpath;
3171251881Speter  ieb.revision = revision;
3172251881Speter
3173251881Speter  ieb.props = props;
3174251881Speter  ieb.iprops = iprops;
3175251881Speter
3176251881Speter  ieb.changed_rev = changed_rev;
3177251881Speter  ieb.changed_date = changed_date;
3178251881Speter  ieb.changed_author = changed_author;
3179251881Speter
3180251881Speter  ieb.checksum = checksum;
3181251881Speter
3182251881Speter  ieb.dav_cache = dav_cache;
3183251881Speter
3184251881Speter  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3185251881Speter                                                wcroot->abspath,
3186251881Speter                                                record_ancestor_abspath);
3187251881Speter  ieb.recorded_repos_relpath = recorded_repos_relpath;
3188251881Speter  ieb.recorded_peg_revision = recorded_peg_revision;
3189251881Speter  ieb.recorded_revision = recorded_revision;
3190251881Speter
3191251881Speter  ieb.update_actual_props = update_actual_props;
3192251881Speter  ieb.new_actual_props = new_actual_props;
3193251881Speter
3194251881Speter  ieb.keep_recorded_info = keep_recorded_info;
3195251881Speter
3196251881Speter  ieb.conflict = conflict;
3197251881Speter  ieb.work_items = work_items;
3198251881Speter
3199251881Speter  SVN_WC__DB_WITH_TXN(
3200251881Speter            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3201251881Speter            wcroot);
3202251881Speter
3203251881Speter  return SVN_NO_ERROR;
3204251881Speter}
3205251881Speter
3206251881Spetersvn_error_t *
3207251881Spetersvn_wc__db_external_add_symlink(svn_wc__db_t *db,
3208251881Speter                                const char *local_abspath,
3209251881Speter                                const char *wri_abspath,
3210251881Speter                                const char *repos_relpath,
3211251881Speter                                const char *repos_root_url,
3212251881Speter                                const char *repos_uuid,
3213251881Speter                                svn_revnum_t revision,
3214251881Speter                                const apr_hash_t *props,
3215251881Speter                                svn_revnum_t changed_rev,
3216251881Speter                                apr_time_t changed_date,
3217251881Speter                                const char *changed_author,
3218251881Speter                                const char *target,
3219251881Speter                                const apr_hash_t *dav_cache,
3220251881Speter                                const char *record_ancestor_abspath,
3221251881Speter                                const char *recorded_repos_relpath,
3222251881Speter                                svn_revnum_t recorded_peg_revision,
3223251881Speter                                svn_revnum_t recorded_revision,
3224251881Speter                                svn_boolean_t update_actual_props,
3225251881Speter                                apr_hash_t *new_actual_props,
3226251881Speter                                svn_boolean_t keep_recorded_info,
3227251881Speter                                const svn_skel_t *work_items,
3228251881Speter                                apr_pool_t *scratch_pool)
3229251881Speter{
3230251881Speter  svn_wc__db_wcroot_t *wcroot;
3231251881Speter  const char *local_relpath;
3232251881Speter  insert_external_baton_t ieb;
3233251881Speter
3234251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3235251881Speter
3236251881Speter  if (! wri_abspath)
3237251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3238251881Speter
3239251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3240251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3241251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3242251881Speter
3243251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3244251881Speter                                        record_ancestor_abspath));
3245251881Speter
3246251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3247251881Speter
3248251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3249251881Speter
3250251881Speter  blank_ieb(&ieb);
3251251881Speter
3252251881Speter  ieb.kind = svn_node_symlink;
3253251881Speter  ieb.presence = svn_wc__db_status_normal;
3254251881Speter
3255251881Speter  ieb.repos_root_url = repos_root_url;
3256251881Speter  ieb.repos_uuid = repos_uuid;
3257251881Speter
3258251881Speter  ieb.repos_relpath = repos_relpath;
3259251881Speter  ieb.revision = revision;
3260251881Speter
3261251881Speter  ieb.props = props;
3262251881Speter
3263251881Speter  ieb.changed_rev = changed_rev;
3264251881Speter  ieb.changed_date = changed_date;
3265251881Speter  ieb.changed_author = changed_author;
3266251881Speter
3267251881Speter  ieb.target = target;
3268251881Speter
3269251881Speter  ieb.dav_cache = dav_cache;
3270251881Speter
3271251881Speter  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3272251881Speter                                                wcroot->abspath,
3273251881Speter                                                record_ancestor_abspath);
3274251881Speter  ieb.recorded_repos_relpath = recorded_repos_relpath;
3275251881Speter  ieb.recorded_peg_revision = recorded_peg_revision;
3276251881Speter  ieb.recorded_revision = recorded_revision;
3277251881Speter
3278251881Speter  ieb.update_actual_props = update_actual_props;
3279251881Speter  ieb.new_actual_props = new_actual_props;
3280251881Speter
3281251881Speter  ieb.keep_recorded_info = keep_recorded_info;
3282251881Speter
3283251881Speter  ieb.work_items = work_items;
3284251881Speter
3285251881Speter  SVN_WC__DB_WITH_TXN(
3286251881Speter            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3287251881Speter            wcroot);
3288251881Speter
3289251881Speter  return SVN_NO_ERROR;
3290251881Speter}
3291251881Speter
3292251881Spetersvn_error_t *
3293251881Spetersvn_wc__db_external_add_dir(svn_wc__db_t *db,
3294251881Speter                            const char *local_abspath,
3295251881Speter                            const char *wri_abspath,
3296251881Speter                            const char *repos_root_url,
3297251881Speter                            const char *repos_uuid,
3298251881Speter                            const char *record_ancestor_abspath,
3299251881Speter                            const char *recorded_repos_relpath,
3300251881Speter                            svn_revnum_t recorded_peg_revision,
3301251881Speter                            svn_revnum_t recorded_revision,
3302251881Speter                            const svn_skel_t *work_items,
3303251881Speter                            apr_pool_t *scratch_pool)
3304251881Speter{
3305251881Speter  svn_wc__db_wcroot_t *wcroot;
3306251881Speter  const char *local_relpath;
3307251881Speter  insert_external_baton_t ieb;
3308251881Speter
3309251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3310251881Speter
3311251881Speter  if (! wri_abspath)
3312251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3313251881Speter
3314251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3315251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3316251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3317251881Speter
3318251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3319251881Speter                                        record_ancestor_abspath));
3320251881Speter
3321251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3322251881Speter
3323251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3324251881Speter
3325251881Speter  blank_ieb(&ieb);
3326251881Speter
3327251881Speter  ieb.kind = svn_node_dir;
3328251881Speter  ieb.presence = svn_wc__db_status_normal;
3329251881Speter
3330251881Speter  ieb.repos_root_url = repos_root_url;
3331251881Speter  ieb.repos_uuid = repos_uuid;
3332251881Speter
3333251881Speter  ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3334251881Speter                                                wcroot->abspath,
3335251881Speter                                                record_ancestor_abspath);
3336251881Speter  ieb.recorded_repos_relpath = recorded_repos_relpath;
3337251881Speter  ieb.recorded_peg_revision = recorded_peg_revision;
3338251881Speter  ieb.recorded_revision = recorded_revision;
3339251881Speter
3340251881Speter  ieb.work_items = work_items;
3341251881Speter
3342251881Speter  SVN_WC__DB_WITH_TXN(
3343251881Speter            insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3344251881Speter            wcroot);
3345251881Speter
3346251881Speter  return SVN_NO_ERROR;
3347251881Speter}
3348251881Speter
3349251881Speter/* The body of svn_wc__db_external_remove(). */
3350251881Speterstatic svn_error_t *
3351251881Speterdb_external_remove(const svn_skel_t *work_items,
3352251881Speter                   svn_wc__db_wcroot_t *wcroot,
3353251881Speter                   const char *local_relpath,
3354251881Speter                   apr_pool_t *scratch_pool)
3355251881Speter{
3356251881Speter  svn_sqlite__stmt_t *stmt;
3357251881Speter
3358251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3359251881Speter                                    STMT_DELETE_EXTERNAL));
3360251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3361251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
3362251881Speter
3363251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3364251881Speter
3365251881Speter  /* ### What about actual? */
3366251881Speter  return SVN_NO_ERROR;
3367251881Speter}
3368251881Speter
3369251881Spetersvn_error_t *
3370251881Spetersvn_wc__db_external_remove(svn_wc__db_t *db,
3371251881Speter                           const char *local_abspath,
3372251881Speter                           const char *wri_abspath,
3373251881Speter                           const svn_skel_t *work_items,
3374251881Speter                           apr_pool_t *scratch_pool)
3375251881Speter{
3376251881Speter  svn_wc__db_wcroot_t *wcroot;
3377251881Speter  const char *local_relpath;
3378251881Speter
3379251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3380251881Speter
3381251881Speter  if (! wri_abspath)
3382251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3383251881Speter
3384251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3385251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3386251881Speter  VERIFY_USABLE_WCROOT(wcroot);
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  SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3393251881Speter                                         scratch_pool),
3394251881Speter                      wcroot);
3395251881Speter
3396251881Speter  return SVN_NO_ERROR;
3397251881Speter}
3398251881Speter
3399251881Spetersvn_error_t *
3400251881Spetersvn_wc__db_external_read(svn_wc__db_status_t *status,
3401251881Speter                         svn_node_kind_t *kind,
3402251881Speter                         const char **definining_abspath,
3403251881Speter                         const char **repos_root_url,
3404251881Speter                         const char **repos_uuid,
3405251881Speter                         const char **recorded_repos_relpath,
3406251881Speter                         svn_revnum_t *recorded_peg_revision,
3407251881Speter                         svn_revnum_t *recorded_revision,
3408251881Speter                         svn_wc__db_t *db,
3409251881Speter                         const char *local_abspath,
3410251881Speter                         const char *wri_abspath,
3411251881Speter                         apr_pool_t *result_pool,
3412251881Speter                         apr_pool_t *scratch_pool)
3413251881Speter{
3414251881Speter  svn_wc__db_wcroot_t *wcroot;
3415251881Speter  const char *local_relpath;
3416251881Speter  svn_sqlite__stmt_t *stmt;
3417251881Speter  svn_boolean_t have_info;
3418251881Speter  svn_error_t *err = NULL;
3419251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3420251881Speter
3421251881Speter  if (! wri_abspath)
3422251881Speter    wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3423251881Speter
3424251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3425251881Speter                              wri_abspath, scratch_pool, scratch_pool));
3426251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3427251881Speter
3428251881Speter  SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3429251881Speter
3430251881Speter  local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3431251881Speter
3432251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3433251881Speter                                    STMT_SELECT_EXTERNAL_INFO));
3434251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3435251881Speter  SVN_ERR(svn_sqlite__step(&have_info, stmt));
3436251881Speter
3437251881Speter  if (have_info)
3438251881Speter    {
3439251881Speter      if (status)
3440251881Speter        *status = svn_sqlite__column_token(stmt, 0, presence_map);
3441251881Speter
3442251881Speter      if (kind)
3443251881Speter        *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3444251881Speter
3445251881Speter      if (definining_abspath)
3446251881Speter        {
3447251881Speter          const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3448251881Speter
3449251881Speter          *definining_abspath = svn_dirent_join(wcroot->abspath,
3450251881Speter                                                record_relpath, result_pool);
3451251881Speter        }
3452251881Speter
3453251881Speter      if (repos_root_url || repos_uuid)
3454251881Speter        {
3455251881Speter          apr_int64_t repos_id;
3456251881Speter
3457251881Speter          repos_id = svn_sqlite__column_int64(stmt, 3);
3458251881Speter
3459251881Speter          err = svn_error_compose_create(
3460251881Speter                        err,
3461251881Speter                        svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3462251881Speter                                                    wcroot->sdb, repos_id,
3463251881Speter                                                    result_pool));
3464251881Speter        }
3465251881Speter
3466251881Speter      if (recorded_repos_relpath)
3467251881Speter        *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3468251881Speter                                                          result_pool);
3469251881Speter
3470251881Speter      if (recorded_peg_revision)
3471251881Speter        *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3472251881Speter
3473251881Speter      if (recorded_revision)
3474251881Speter        *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3475251881Speter    }
3476251881Speter  else
3477251881Speter    {
3478251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3479251881Speter                              _("The node '%s' is not an external."),
3480251881Speter                              svn_dirent_local_style(local_abspath,
3481251881Speter                                                     scratch_pool));
3482251881Speter    }
3483251881Speter
3484251881Speter  return svn_error_trace(
3485251881Speter                svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3486251881Speter}
3487251881Speter
3488251881Spetersvn_error_t *
3489251881Spetersvn_wc__db_committable_externals_below(apr_array_header_t **externals,
3490251881Speter                                       svn_wc__db_t *db,
3491251881Speter                                       const char *local_abspath,
3492251881Speter                                       svn_boolean_t immediates_only,
3493251881Speter                                       apr_pool_t *result_pool,
3494251881Speter                                       apr_pool_t *scratch_pool)
3495251881Speter{
3496251881Speter  svn_wc__db_wcroot_t *wcroot;
3497251881Speter  svn_sqlite__stmt_t *stmt;
3498251881Speter  const char *local_relpath;
3499251881Speter  svn_boolean_t have_row;
3500251881Speter  svn_wc__committable_external_info_t *info;
3501251881Speter  svn_node_kind_t db_kind;
3502251881Speter  apr_array_header_t *result = NULL;
3503251881Speter
3504251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3505251881Speter
3506251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3507251881Speter                              local_abspath, scratch_pool, scratch_pool));
3508251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3509251881Speter
3510251881Speter  SVN_ERR(svn_sqlite__get_statement(
3511251881Speter                &stmt, wcroot->sdb,
3512251881Speter                immediates_only
3513251881Speter                    ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3514251881Speter                    : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3515251881Speter
3516251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3517251881Speter
3518251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3519251881Speter
3520251881Speter  if (have_row)
3521251881Speter    result = apr_array_make(result_pool, 0,
3522251881Speter                            sizeof(svn_wc__committable_external_info_t *));
3523251881Speter
3524251881Speter  while (have_row)
3525251881Speter    {
3526251881Speter      info = apr_palloc(result_pool, sizeof(*info));
3527251881Speter
3528251881Speter      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3529251881Speter      info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3530251881Speter                                            result_pool);
3531251881Speter
3532251881Speter      db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3533251881Speter      SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3534251881Speter      info->kind = db_kind;
3535251881Speter
3536251881Speter      info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3537251881Speter      info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3538251881Speter
3539251881Speter      APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3540251881Speter
3541251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3542251881Speter    }
3543251881Speter
3544251881Speter  *externals = result;
3545251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
3546251881Speter}
3547251881Speter
3548251881Spetersvn_error_t *
3549251881Spetersvn_wc__db_externals_defined_below(apr_hash_t **externals,
3550251881Speter                                   svn_wc__db_t *db,
3551251881Speter                                   const char *local_abspath,
3552251881Speter                                   apr_pool_t *result_pool,
3553251881Speter                                   apr_pool_t *scratch_pool)
3554251881Speter{
3555251881Speter  svn_wc__db_wcroot_t *wcroot;
3556251881Speter  svn_sqlite__stmt_t *stmt;
3557251881Speter  const char *local_relpath;
3558251881Speter  svn_boolean_t have_row;
3559251881Speter
3560251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3561251881Speter
3562251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3563251881Speter                              local_abspath, scratch_pool, scratch_pool));
3564251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3565251881Speter
3566251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3567251881Speter                                    STMT_SELECT_EXTERNALS_DEFINED));
3568251881Speter
3569251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3570251881Speter
3571251881Speter  *externals = apr_hash_make(result_pool);
3572251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3573251881Speter
3574251881Speter  while (have_row)
3575251881Speter    {
3576251881Speter      const char *def_local_relpath;
3577251881Speter
3578251881Speter      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3579251881Speter      def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3580251881Speter
3581251881Speter      svn_hash_sets(*externals,
3582251881Speter                    svn_dirent_join(wcroot->abspath, local_relpath,
3583251881Speter                                    result_pool),
3584251881Speter                    svn_dirent_join(wcroot->abspath, def_local_relpath,
3585251881Speter                                    result_pool));
3586251881Speter
3587251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3588251881Speter    }
3589251881Speter
3590251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
3591251881Speter}
3592251881Speter
3593251881Spetersvn_error_t *
3594251881Spetersvn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3595251881Speter                                        apr_hash_t **depths,
3596251881Speter                                        svn_wc__db_t *db,
3597251881Speter                                        const char *local_abspath,
3598251881Speter                                        apr_pool_t *result_pool,
3599251881Speter                                        apr_pool_t *scratch_pool)
3600251881Speter{
3601251881Speter  svn_wc__db_wcroot_t *wcroot;
3602251881Speter  svn_sqlite__stmt_t *stmt;
3603251881Speter  const char *local_relpath;
3604251881Speter  svn_boolean_t have_row;
3605251881Speter  svn_error_t *err = NULL;
3606251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3607251881Speter
3608251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3609251881Speter
3610251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3611251881Speter                              local_abspath, scratch_pool, iterpool));
3612251881Speter  VERIFY_USABLE_WCROOT(wcroot);
3613251881Speter
3614251881Speter  *externals = apr_hash_make(result_pool);
3615251881Speter  if (depths != NULL)
3616251881Speter    *depths = apr_hash_make(result_pool);
3617251881Speter
3618251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3619251881Speter                                    STMT_SELECT_EXTERNAL_PROPERTIES));
3620251881Speter
3621251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3622251881Speter
3623251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3624251881Speter
3625251881Speter  while (have_row)
3626251881Speter    {
3627251881Speter      apr_hash_t *node_props;
3628251881Speter      const char *external_value;
3629251881Speter
3630251881Speter      svn_pool_clear(iterpool);
3631251881Speter      err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3632251881Speter                                          iterpool);
3633251881Speter
3634251881Speter      if (err)
3635251881Speter        break;
3636251881Speter
3637251881Speter      external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3638251881Speter
3639251881Speter      if (external_value)
3640251881Speter        {
3641251881Speter          const char *node_abspath;
3642251881Speter          const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3643251881Speter
3644251881Speter          node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3645251881Speter                                         result_pool);
3646251881Speter
3647251881Speter          svn_hash_sets(*externals, node_abspath,
3648251881Speter                        apr_pstrdup(result_pool, external_value));
3649251881Speter
3650251881Speter          if (depths)
3651251881Speter            {
3652251881Speter              svn_depth_t depth
3653251881Speter                = svn_sqlite__column_token_null(stmt, 2, depth_map,
3654251881Speter                                                svn_depth_unknown);
3655251881Speter
3656251881Speter              svn_hash_sets(*depths, node_abspath,
3657251881Speter                            /* Use static string */
3658251881Speter                            svn_token__to_word(depth_map, depth));
3659251881Speter            }
3660251881Speter        }
3661251881Speter
3662251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3663251881Speter    }
3664251881Speter
3665251881Speter  svn_pool_destroy(iterpool);
3666251881Speter
3667251881Speter  return svn_error_trace(svn_error_compose_create(err,
3668251881Speter                                                  svn_sqlite__reset(stmt)));
3669251881Speter}
3670251881Speter
3671251881Speter/* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3672251881Speter   The new ACTUAL data won't have any conflicts. */
3673251881Speterstatic svn_error_t *
3674251881Spetercopy_actual(svn_wc__db_wcroot_t *src_wcroot,
3675251881Speter            const char *src_relpath,
3676251881Speter            svn_wc__db_wcroot_t *dst_wcroot,
3677251881Speter            const char *dst_relpath,
3678251881Speter            apr_pool_t *scratch_pool)
3679251881Speter{
3680251881Speter  svn_sqlite__stmt_t *stmt;
3681251881Speter  svn_boolean_t have_row;
3682251881Speter
3683251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3684251881Speter                                    STMT_SELECT_ACTUAL_NODE));
3685251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3686251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3687251881Speter  if (have_row)
3688251881Speter    {
3689251881Speter      apr_size_t props_size;
3690251881Speter      const char *changelist;
3691251881Speter      const char *properties;
3692251881Speter
3693251881Speter      /* Skipping conflict data... */
3694251881Speter      changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3695251881Speter      /* No need to parse the properties when simply copying. */
3696251881Speter      properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3697251881Speter
3698251881Speter      if (changelist || properties)
3699251881Speter        {
3700251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
3701251881Speter
3702251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3703251881Speter                                            STMT_INSERT_ACTUAL_NODE));
3704251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3705251881Speter                                    dst_wcroot->wc_id, dst_relpath,
3706251881Speter                                svn_relpath_dirname(dst_relpath, scratch_pool),
3707251881Speter                                    properties, props_size, changelist));
3708251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
3709251881Speter        }
3710251881Speter    }
3711251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
3712251881Speter
3713251881Speter  return SVN_NO_ERROR;
3714251881Speter}
3715251881Speter
3716251881Speter/* Helper for svn_wc__db_op_copy to handle copying from one db to
3717251881Speter   another */
3718251881Speterstatic svn_error_t *
3719251881Spetercross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3720251881Speter              const char *src_relpath,
3721251881Speter              svn_wc__db_wcroot_t *dst_wcroot,
3722251881Speter              const char *dst_relpath,
3723251881Speter              svn_wc__db_status_t dst_status,
3724251881Speter              int dst_op_depth,
3725251881Speter              int dst_np_op_depth,
3726251881Speter              svn_node_kind_t kind,
3727251881Speter              const apr_array_header_t *children,
3728251881Speter              apr_int64_t copyfrom_id,
3729251881Speter              const char *copyfrom_relpath,
3730251881Speter              svn_revnum_t copyfrom_rev,
3731251881Speter              apr_pool_t *scratch_pool)
3732251881Speter{
3733251881Speter  insert_working_baton_t iwb;
3734251881Speter  svn_revnum_t changed_rev;
3735251881Speter  apr_time_t changed_date;
3736251881Speter  const char *changed_author;
3737251881Speter  const svn_checksum_t *checksum;
3738251881Speter  apr_hash_t *props;
3739251881Speter  svn_depth_t depth;
3740251881Speter
3741251881Speter  SVN_ERR_ASSERT(kind == svn_node_file
3742251881Speter                 || kind == svn_node_dir
3743251881Speter                 );
3744251881Speter
3745251881Speter  SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3746251881Speter                    &changed_rev, &changed_date, &changed_author, &depth,
3747251881Speter                    &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3748251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3749251881Speter                    src_wcroot, src_relpath, scratch_pool, scratch_pool));
3750251881Speter
3751251881Speter  SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3752251881Speter                                 scratch_pool, scratch_pool));
3753251881Speter
3754251881Speter  blank_iwb(&iwb);
3755251881Speter  iwb.presence = dst_status;
3756251881Speter  iwb.kind = kind;
3757251881Speter
3758251881Speter  iwb.props = props;
3759251881Speter  iwb.changed_rev = changed_rev;
3760251881Speter  iwb.changed_date = changed_date;
3761251881Speter  iwb.changed_author = changed_author;
3762251881Speter  iwb.original_repos_id = copyfrom_id;
3763251881Speter  iwb.original_repos_relpath = copyfrom_relpath;
3764251881Speter  iwb.original_revnum = copyfrom_rev;
3765251881Speter  iwb.moved_here = FALSE;
3766251881Speter
3767251881Speter  iwb.op_depth = dst_op_depth;
3768251881Speter
3769251881Speter  iwb.checksum = checksum;
3770251881Speter  iwb.children = children;
3771251881Speter  iwb.depth = depth;
3772251881Speter
3773251881Speter  iwb.not_present_op_depth = dst_np_op_depth;
3774251881Speter
3775251881Speter  SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3776251881Speter
3777251881Speter  SVN_ERR(copy_actual(src_wcroot, src_relpath,
3778251881Speter                      dst_wcroot, dst_relpath, scratch_pool));
3779251881Speter
3780251881Speter  return SVN_NO_ERROR;
3781251881Speter}
3782251881Speter
3783251881Speter/* Helper for scan_deletion_txn. Extracts the moved-to information, if
3784251881Speter   any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
3785251881Speterstatic svn_error_t *
3786251881Speterget_moved_to(const char **moved_to_relpath_p,
3787251881Speter             const char **moved_to_op_root_relpath_p,
3788251881Speter             svn_boolean_t *scan,
3789251881Speter             svn_sqlite__stmt_t *stmt,
3790251881Speter             const char *current_relpath,
3791251881Speter             svn_wc__db_wcroot_t *wcroot,
3792251881Speter             const char *local_relpath,
3793251881Speter             apr_pool_t *result_pool,
3794251881Speter             apr_pool_t *scratch_pool)
3795251881Speter{
3796251881Speter  const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3797251881Speter
3798251881Speter  if (moved_to_relpath)
3799251881Speter    {
3800251881Speter      const char *moved_to_op_root_relpath = moved_to_relpath;
3801251881Speter
3802251881Speter      if (strcmp(current_relpath, local_relpath))
3803251881Speter        {
3804251881Speter          /* LOCAL_RELPATH is a child inside the move op-root. */
3805251881Speter          const char *moved_child_relpath;
3806251881Speter
3807251881Speter          /* The CURRENT_RELPATH is the op_root of the delete-half of
3808251881Speter           * the move. LOCAL_RELPATH is a child that was moved along.
3809251881Speter           * Compute the child's new location within the move target. */
3810251881Speter          moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3811251881Speter                                                          local_relpath);
3812251881Speter          SVN_ERR_ASSERT(moved_child_relpath &&
3813251881Speter                         strlen(moved_child_relpath) > 0);
3814251881Speter          moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3815251881Speter                                              moved_child_relpath,
3816251881Speter                                              result_pool);
3817251881Speter        }
3818251881Speter
3819251881Speter      if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3820251881Speter        *moved_to_op_root_relpath_p
3821251881Speter          = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3822251881Speter
3823251881Speter      if (moved_to_relpath && moved_to_relpath_p)
3824251881Speter        *moved_to_relpath_p
3825251881Speter          = apr_pstrdup(result_pool, moved_to_relpath);
3826251881Speter
3827251881Speter      *scan = FALSE;
3828251881Speter    }
3829251881Speter
3830251881Speter  return SVN_NO_ERROR;
3831251881Speter}
3832251881Speter
3833251881Speter
3834251881Speter/* The body of svn_wc__db_scan_deletion().
3835251881Speter */
3836251881Speterstatic svn_error_t *
3837251881Speterscan_deletion_txn(const char **base_del_relpath,
3838251881Speter                  const char **moved_to_relpath,
3839251881Speter                  const char **work_del_relpath,
3840251881Speter                  const char **moved_to_op_root_relpath,
3841251881Speter                  svn_wc__db_wcroot_t *wcroot,
3842251881Speter                  const char *local_relpath,
3843251881Speter                  apr_pool_t *result_pool,
3844251881Speter                  apr_pool_t *scratch_pool)
3845251881Speter{
3846251881Speter  const char *current_relpath = local_relpath;
3847251881Speter  svn_sqlite__stmt_t *stmt;
3848251881Speter  svn_wc__db_status_t work_presence;
3849251881Speter  svn_boolean_t have_row, scan, have_base;
3850251881Speter  int op_depth;
3851251881Speter
3852251881Speter  /* Initialize all the OUT parameters.  */
3853251881Speter  if (base_del_relpath != NULL)
3854251881Speter    *base_del_relpath = NULL;
3855251881Speter  if (moved_to_relpath != NULL)
3856251881Speter    *moved_to_relpath = NULL;
3857251881Speter  if (work_del_relpath != NULL)
3858251881Speter    *work_del_relpath = NULL;
3859251881Speter  if (moved_to_op_root_relpath != NULL)
3860251881Speter    *moved_to_op_root_relpath = NULL;
3861251881Speter
3862251881Speter  /* If looking for moved-to info then we need to scan every path
3863251881Speter     until we find it.  If not looking for moved-to we only need to
3864251881Speter     check op-roots and parents of op-roots. */
3865251881Speter  scan = (moved_to_op_root_relpath || moved_to_relpath);
3866251881Speter
3867251881Speter  SVN_ERR(svn_sqlite__get_statement(
3868251881Speter                    &stmt, wcroot->sdb,
3869251881Speter                    scan ? STMT_SELECT_DELETION_INFO_SCAN
3870251881Speter                         : STMT_SELECT_DELETION_INFO));
3871251881Speter
3872251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3873251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
3874251881Speter  if (!have_row)
3875251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3876251881Speter                             _("The node '%s' was not found."),
3877251881Speter                             path_for_error_message(wcroot, local_relpath,
3878251881Speter                                                    scratch_pool));
3879251881Speter
3880251881Speter  work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3881251881Speter  have_base = !svn_sqlite__column_is_null(stmt, 0);
3882251881Speter  if (work_presence != svn_wc__db_status_not_present
3883251881Speter      && work_presence != svn_wc__db_status_base_deleted)
3884251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3885251881Speter                             svn_sqlite__reset(stmt),
3886251881Speter                             _("Expected node '%s' to be deleted."),
3887251881Speter                             path_for_error_message(wcroot, local_relpath,
3888251881Speter                                                    scratch_pool));
3889251881Speter
3890251881Speter  op_depth = svn_sqlite__column_int(stmt, 2);
3891251881Speter
3892251881Speter  /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3893251881Speter     treat this as an op-root.  At commit time we need to explicitly
3894251881Speter     delete such nodes otherwise they will be present in the
3895251881Speter     repository copy. */
3896251881Speter  if (work_presence == svn_wc__db_status_not_present
3897251881Speter      && work_del_relpath && !*work_del_relpath)
3898251881Speter    {
3899251881Speter      *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3900251881Speter
3901251881Speter      if (!scan && !base_del_relpath)
3902251881Speter        {
3903251881Speter          /* We have all we need, exit early */
3904251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
3905251881Speter          return SVN_NO_ERROR;
3906251881Speter        }
3907251881Speter    }
3908251881Speter
3909251881Speter
3910251881Speter  while (TRUE)
3911251881Speter    {
3912251881Speter      svn_error_t *err;
3913251881Speter      const char *parent_relpath;
3914251881Speter      int current_depth = relpath_depth(current_relpath);
3915251881Speter
3916251881Speter      /* Step CURRENT_RELPATH to op-root */
3917251881Speter
3918251881Speter      while (TRUE)
3919251881Speter        {
3920251881Speter          if (scan)
3921251881Speter            {
3922251881Speter              err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
3923251881Speter                                 &scan, stmt, current_relpath,
3924251881Speter                                 wcroot, local_relpath,
3925251881Speter                                 result_pool, scratch_pool);
3926251881Speter              if (err || (!scan
3927251881Speter                          && !base_del_relpath
3928251881Speter                          && !work_del_relpath))
3929251881Speter                {
3930251881Speter                  /* We have all we need (or an error occurred) */
3931251881Speter                  SVN_ERR(svn_sqlite__reset(stmt));
3932251881Speter                  return svn_error_trace(err);
3933251881Speter                }
3934251881Speter            }
3935251881Speter
3936251881Speter          if (current_depth <= op_depth)
3937251881Speter            break;
3938251881Speter
3939251881Speter          current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
3940251881Speter          --current_depth;
3941251881Speter
3942251881Speter          if (scan || current_depth == op_depth)
3943251881Speter            {
3944251881Speter              SVN_ERR(svn_sqlite__reset(stmt));
3945251881Speter              SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
3946251881Speter                                        current_relpath));
3947251881Speter              SVN_ERR(svn_sqlite__step(&have_row, stmt));
3948251881Speter              SVN_ERR_ASSERT(have_row);
3949251881Speter              have_base = !svn_sqlite__column_is_null(stmt, 0);
3950251881Speter            }
3951251881Speter        }
3952251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
3953251881Speter
3954251881Speter      /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
3955251881Speter
3956251881Speter      SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
3957251881Speter      parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
3958251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
3959251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
3960251881Speter      if (!have_row)
3961251881Speter        {
3962251881Speter          /* No row means no WORKING node which mean we just fell off
3963251881Speter             the WORKING tree, so CURRENT_RELPATH is the op-root
3964251881Speter             closest to the wc root. */
3965251881Speter          if (have_base && base_del_relpath)
3966251881Speter            *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
3967251881Speter          break;
3968251881Speter        }
3969251881Speter
3970251881Speter      /* Still in the WORKING tree so the first time we get here
3971251881Speter         CURRENT_RELPATH is a delete op-root in the WORKING tree. */
3972251881Speter      if (work_del_relpath && !*work_del_relpath)
3973251881Speter        {
3974251881Speter          *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3975251881Speter
3976251881Speter          if (!scan && !base_del_relpath)
3977251881Speter            break; /* We have all we need */
3978251881Speter        }
3979251881Speter
3980251881Speter      current_relpath = parent_relpath;
3981251881Speter      op_depth = svn_sqlite__column_int(stmt, 2);
3982251881Speter      have_base = !svn_sqlite__column_is_null(stmt, 0);
3983251881Speter    }
3984251881Speter
3985251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
3986251881Speter
3987251881Speter  return SVN_NO_ERROR;
3988251881Speter}
3989251881Speter
3990251881Spetersvn_error_t *
3991251881Spetersvn_wc__db_scan_deletion(const char **base_del_abspath,
3992251881Speter                         const char **moved_to_abspath,
3993251881Speter                         const char **work_del_abspath,
3994251881Speter                         const char **moved_to_op_root_abspath,
3995251881Speter                         svn_wc__db_t *db,
3996251881Speter                         const char *local_abspath,
3997251881Speter                         apr_pool_t *result_pool,
3998251881Speter                         apr_pool_t *scratch_pool)
3999251881Speter{
4000251881Speter  svn_wc__db_wcroot_t *wcroot;
4001251881Speter  const char *local_relpath;
4002251881Speter  const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4003251881Speter  const char *moved_to_op_root_relpath;
4004251881Speter
4005251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4006251881Speter
4007251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4008251881Speter                              local_abspath, scratch_pool, scratch_pool));
4009251881Speter  VERIFY_USABLE_WCROOT(wcroot);
4010251881Speter
4011251881Speter  SVN_WC__DB_WITH_TXN(
4012251881Speter    scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4013251881Speter                      &work_del_relpath, &moved_to_op_root_relpath,
4014251881Speter                      wcroot, local_relpath, result_pool, scratch_pool),
4015251881Speter    wcroot);
4016251881Speter
4017251881Speter  if (base_del_abspath)
4018251881Speter    {
4019251881Speter      *base_del_abspath = (base_del_relpath
4020251881Speter                           ? svn_dirent_join(wcroot->abspath,
4021251881Speter                                             base_del_relpath, result_pool)
4022251881Speter                           : NULL);
4023251881Speter    }
4024251881Speter  if (moved_to_abspath)
4025251881Speter    {
4026251881Speter      *moved_to_abspath = (moved_to_relpath
4027251881Speter                           ? svn_dirent_join(wcroot->abspath,
4028251881Speter                                             moved_to_relpath, result_pool)
4029251881Speter                           : NULL);
4030251881Speter    }
4031251881Speter  if (work_del_abspath)
4032251881Speter    {
4033251881Speter      *work_del_abspath = (work_del_relpath
4034251881Speter                           ? svn_dirent_join(wcroot->abspath,
4035251881Speter                                             work_del_relpath, result_pool)
4036251881Speter                           : NULL);
4037251881Speter    }
4038251881Speter  if (moved_to_op_root_abspath)
4039251881Speter    {
4040251881Speter      *moved_to_op_root_abspath = (moved_to_op_root_relpath
4041251881Speter                           ? svn_dirent_join(wcroot->abspath,
4042251881Speter                                             moved_to_op_root_relpath,
4043251881Speter                                             result_pool)
4044251881Speter                           : NULL);
4045251881Speter    }
4046251881Speter
4047251881Speter  return SVN_NO_ERROR;
4048251881Speter}
4049251881Speter
4050251881Speter
4051251881Speter/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4052251881Speter   appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4053251881Speter   since they are available.  This is a helper for
4054251881Speter   svn_wc__db_op_copy. */
4055251881Speterstatic svn_error_t *
4056251881Speterget_info_for_copy(apr_int64_t *copyfrom_id,
4057251881Speter                  const char **copyfrom_relpath,
4058251881Speter                  svn_revnum_t *copyfrom_rev,
4059251881Speter                  svn_wc__db_status_t *status,
4060251881Speter                  svn_node_kind_t *kind,
4061251881Speter                  svn_boolean_t *op_root,
4062251881Speter                  svn_wc__db_wcroot_t *wcroot,
4063251881Speter                  const char *local_relpath,
4064251881Speter                  apr_pool_t *result_pool,
4065251881Speter                  apr_pool_t *scratch_pool)
4066251881Speter{
4067251881Speter  const char *repos_relpath;
4068251881Speter  svn_revnum_t revision;
4069251881Speter  svn_wc__db_status_t node_status;
4070251881Speter  apr_int64_t repos_id;
4071251881Speter  svn_boolean_t is_op_root;
4072251881Speter
4073251881Speter  SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4074251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4075251881Speter                    copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4076251881Speter                    NULL, &is_op_root, NULL, NULL,
4077251881Speter                    NULL /* have_base */,
4078251881Speter                    NULL /* have_more_work */,
4079251881Speter                    NULL /* have_work */,
4080251881Speter                    wcroot, local_relpath, result_pool, scratch_pool));
4081251881Speter
4082251881Speter  if (op_root)
4083251881Speter    *op_root = is_op_root;
4084251881Speter
4085251881Speter  if (node_status == svn_wc__db_status_excluded)
4086251881Speter    {
4087251881Speter      /* The parent cannot be excluded, so look at the parent and then
4088251881Speter         adjust the relpath */
4089251881Speter      const char *parent_relpath, *base_name;
4090251881Speter
4091251881Speter      svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4092251881Speter                       scratch_pool);
4093251881Speter      SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4094251881Speter                                NULL, NULL, NULL,
4095251881Speter                                wcroot, parent_relpath,
4096251881Speter                                scratch_pool, scratch_pool));
4097251881Speter      if (*copyfrom_relpath)
4098251881Speter        *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4099251881Speter                                             result_pool);
4100251881Speter    }
4101251881Speter  else if (node_status == svn_wc__db_status_added)
4102251881Speter    {
4103251881Speter      SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4104251881Speter                            NULL, NULL, NULL, wcroot, local_relpath,
4105251881Speter                            scratch_pool, scratch_pool));
4106251881Speter    }
4107251881Speter  else if (node_status == svn_wc__db_status_deleted && is_op_root)
4108251881Speter    {
4109251881Speter      const char *base_del_relpath, *work_del_relpath;
4110251881Speter
4111251881Speter      SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4112251881Speter                                &work_del_relpath,
4113251881Speter                                NULL, wcroot, local_relpath,
4114251881Speter                                scratch_pool, scratch_pool));
4115251881Speter      if (work_del_relpath)
4116251881Speter        {
4117251881Speter          const char *op_root_relpath;
4118251881Speter          const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4119251881Speter                                                               scratch_pool);
4120251881Speter
4121251881Speter          /* Similar to, but not the same as, the _scan_addition and
4122251881Speter             _join above.  Can we use get_copyfrom here? */
4123251881Speter          SVN_ERR(scan_addition(NULL, &op_root_relpath,
4124251881Speter                                NULL, NULL, /* repos_* */
4125251881Speter                                copyfrom_relpath, copyfrom_id, copyfrom_rev,
4126251881Speter                                NULL, NULL, NULL, wcroot, parent_del_relpath,
4127251881Speter                                scratch_pool, scratch_pool));
4128251881Speter          *copyfrom_relpath
4129251881Speter            = svn_relpath_join(*copyfrom_relpath,
4130251881Speter                               svn_relpath_skip_ancestor(op_root_relpath,
4131251881Speter                                                         local_relpath),
4132251881Speter                               result_pool);
4133251881Speter        }
4134251881Speter      else if (base_del_relpath)
4135251881Speter        {
4136251881Speter          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4137251881Speter                                                    copyfrom_relpath,
4138251881Speter                                                    copyfrom_id, NULL, NULL,
4139251881Speter                                                    NULL, NULL, NULL, NULL,
4140251881Speter                                                    NULL, NULL, NULL, NULL,
4141251881Speter                                                    wcroot, local_relpath,
4142251881Speter                                                    result_pool,
4143251881Speter                                                    scratch_pool));
4144251881Speter        }
4145251881Speter      else
4146251881Speter        SVN_ERR_MALFUNCTION();
4147251881Speter    }
4148251881Speter  else if (node_status == svn_wc__db_status_deleted)
4149251881Speter    {
4150251881Speter      /* Keep original_* from read_info() to allow seeing the difference
4151251881Speter         between base-deleted and not present */
4152251881Speter    }
4153251881Speter  else
4154251881Speter    {
4155251881Speter      *copyfrom_relpath = repos_relpath;
4156251881Speter      *copyfrom_rev = revision;
4157251881Speter      *copyfrom_id = repos_id;
4158251881Speter    }
4159251881Speter
4160251881Speter  if (status)
4161251881Speter    *status = node_status;
4162251881Speter
4163251881Speter  return SVN_NO_ERROR;
4164251881Speter}
4165251881Speter
4166251881Speter
4167251881Speter/* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4168251881Speterstatic svn_error_t *
4169251881Speterop_depth_of(int *op_depth,
4170251881Speter            svn_wc__db_wcroot_t *wcroot,
4171251881Speter            const char *local_relpath)
4172251881Speter{
4173251881Speter  svn_sqlite__stmt_t *stmt;
4174251881Speter  svn_boolean_t have_row;
4175251881Speter
4176251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4177251881Speter                                    STMT_SELECT_NODE_INFO));
4178251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4179251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4180251881Speter  SVN_ERR_ASSERT(have_row);
4181251881Speter  *op_depth = svn_sqlite__column_int(stmt, 0);
4182251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4183251881Speter
4184251881Speter  return SVN_NO_ERROR;
4185251881Speter}
4186251881Speter
4187251881Speter
4188251881Speter/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4189251881Speter   revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4190251881Speter   by checking if this would be a direct child of a copy of its parent
4191251881Speter   directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4192251881Speter
4193251881Speter   If the node is not a direct copy at the same revision of the parent
4194251881Speter   *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4195251881Speter   node should be inserted at this op_depth. This will be the case when the
4196251881Speter   parent already defined an incomplete child with the same name. Otherwise
4197251881Speter   *NP_OP_DEPTH will be set to -1.
4198251881Speter
4199251881Speter   If the parent node is not the parent of the to be copied node, then
4200251881Speter   *OP_DEPTH will be set to the proper op_depth for a new operation root.
4201251881Speter
4202251881Speter   Set *PARENT_OP_DEPTH to the op_depth of the parent.
4203251881Speter
4204251881Speter */
4205251881Speterstatic svn_error_t *
4206251881Speterop_depth_for_copy(int *op_depth,
4207251881Speter                  int *np_op_depth,
4208251881Speter                  int *parent_op_depth,
4209251881Speter                  apr_int64_t copyfrom_repos_id,
4210251881Speter                  const char *copyfrom_relpath,
4211251881Speter                  svn_revnum_t copyfrom_revision,
4212251881Speter                  svn_wc__db_wcroot_t *wcroot,
4213251881Speter                  const char *local_relpath,
4214251881Speter                  apr_pool_t *scratch_pool)
4215251881Speter{
4216251881Speter  const char *parent_relpath, *name;
4217251881Speter  svn_sqlite__stmt_t *stmt;
4218251881Speter  svn_boolean_t have_row;
4219251881Speter  int incomplete_op_depth = -1;
4220251881Speter  int min_op_depth = 1; /* Never touch BASE */
4221251881Speter
4222251881Speter  *op_depth = relpath_depth(local_relpath);
4223251881Speter  *np_op_depth = -1;
4224251881Speter
4225251881Speter  svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4226251881Speter  *parent_op_depth = relpath_depth(parent_relpath);
4227251881Speter
4228251881Speter  if (!copyfrom_relpath)
4229251881Speter    return SVN_NO_ERROR;
4230251881Speter
4231251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4232251881Speter                                    STMT_SELECT_WORKING_NODE));
4233251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4234251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4235251881Speter  if (have_row)
4236251881Speter    {
4237251881Speter      svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4238251881Speter                                                            presence_map);
4239251881Speter
4240251881Speter      min_op_depth = svn_sqlite__column_int(stmt, 0);
4241251881Speter      if (status == svn_wc__db_status_incomplete)
4242251881Speter        incomplete_op_depth = min_op_depth;
4243251881Speter    }
4244251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4245251881Speter
4246251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4247251881Speter                                    STMT_SELECT_WORKING_NODE));
4248251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4249251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4250251881Speter  if (have_row)
4251251881Speter    {
4252251881Speter      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4253251881Speter                                                              presence_map);
4254251881Speter
4255251881Speter      *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4256251881Speter      if (*parent_op_depth < min_op_depth)
4257251881Speter        {
4258251881Speter          /* We want to create a copy; not overwrite the lower layers */
4259251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
4260251881Speter          return SVN_NO_ERROR;
4261251881Speter        }
4262251881Speter
4263251881Speter      /* You can only add children below a node that exists.
4264251881Speter         In WORKING that must be status added, which is represented
4265251881Speter         as presence normal */
4266251881Speter      SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4267251881Speter
4268251881Speter      if ((incomplete_op_depth < 0)
4269251881Speter          || (incomplete_op_depth == *parent_op_depth))
4270251881Speter        {
4271251881Speter          apr_int64_t parent_copyfrom_repos_id
4272251881Speter            = svn_sqlite__column_int64(stmt, 10);
4273251881Speter          const char *parent_copyfrom_relpath
4274251881Speter            = svn_sqlite__column_text(stmt, 11, NULL);
4275251881Speter          svn_revnum_t parent_copyfrom_revision
4276251881Speter            = svn_sqlite__column_revnum(stmt, 12);
4277251881Speter
4278251881Speter          if (parent_copyfrom_repos_id == copyfrom_repos_id)
4279251881Speter            {
4280251881Speter              if (copyfrom_revision == parent_copyfrom_revision
4281251881Speter                  && !strcmp(copyfrom_relpath,
4282251881Speter                             svn_relpath_join(parent_copyfrom_relpath, name,
4283251881Speter                                              scratch_pool)))
4284251881Speter                *op_depth = *parent_op_depth;
4285251881Speter              else if (incomplete_op_depth > 0)
4286251881Speter                *np_op_depth = incomplete_op_depth;
4287251881Speter            }
4288251881Speter        }
4289251881Speter    }
4290251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4291251881Speter
4292251881Speter  return SVN_NO_ERROR;
4293251881Speter}
4294251881Speter
4295251881Speter
4296251881Speter/* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4297251881Speter * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4298251881Speter * copy operation is part of a move, and indicates the op-depth of the
4299251881Speter * move destination op-root. */
4300251881Speterstatic svn_error_t *
4301251881Speterdb_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4302251881Speter           const char *src_relpath,
4303251881Speter           svn_wc__db_wcroot_t *dst_wcroot,
4304251881Speter           const char *dst_relpath,
4305251881Speter           const svn_skel_t *work_items,
4306251881Speter           int move_op_depth,
4307251881Speter           apr_pool_t *scratch_pool)
4308251881Speter{
4309251881Speter  const char *copyfrom_relpath;
4310251881Speter  svn_revnum_t copyfrom_rev;
4311251881Speter  svn_wc__db_status_t status;
4312251881Speter  svn_wc__db_status_t dst_presence;
4313251881Speter  svn_boolean_t op_root;
4314251881Speter  apr_int64_t copyfrom_id;
4315251881Speter  int dst_op_depth;
4316251881Speter  int dst_np_op_depth;
4317251881Speter  int dst_parent_op_depth;
4318251881Speter  svn_node_kind_t kind;
4319251881Speter  const apr_array_header_t *children;
4320251881Speter
4321251881Speter  SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
4322251881Speter                            &status, &kind, &op_root, src_wcroot,
4323251881Speter                            src_relpath, scratch_pool, scratch_pool));
4324251881Speter
4325251881Speter  SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4326251881Speter                            &dst_parent_op_depth,
4327251881Speter                            copyfrom_id, copyfrom_relpath, copyfrom_rev,
4328251881Speter                            dst_wcroot, dst_relpath, scratch_pool));
4329251881Speter
4330251881Speter  SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4331251881Speter
4332251881Speter  /* ### New status, not finished, see notes/wc-ng/copying */
4333251881Speter  switch (status)
4334251881Speter    {
4335251881Speter    case svn_wc__db_status_normal:
4336251881Speter    case svn_wc__db_status_added:
4337251881Speter    case svn_wc__db_status_moved_here:
4338251881Speter    case svn_wc__db_status_copied:
4339251881Speter      dst_presence = svn_wc__db_status_normal;
4340251881Speter      break;
4341251881Speter    case svn_wc__db_status_deleted:
4342251881Speter      if (op_root)
4343251881Speter        {
4344251881Speter          /* If the lower layer is already shadowcopied we can skip adding
4345251881Speter             a not present node. */
4346251881Speter          svn_error_t *err;
4347251881Speter          svn_wc__db_status_t dst_status;
4348251881Speter
4349251881Speter          err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4350251881Speter                          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4351251881Speter                          NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4352251881Speter                          dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4353251881Speter
4354251881Speter          if (err)
4355251881Speter            {
4356251881Speter              if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4357251881Speter                svn_error_clear(err);
4358251881Speter              else
4359251881Speter                return svn_error_trace(err);
4360251881Speter            }
4361251881Speter          else if (dst_status == svn_wc__db_status_deleted)
4362251881Speter            {
4363251881Speter              /* Node is already deleted; skip the NODES work, but do
4364251881Speter                 install wq items if requested */
4365251881Speter              SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4366251881Speter                                     scratch_pool));
4367251881Speter              return SVN_NO_ERROR;
4368251881Speter            }
4369251881Speter        }
4370251881Speter      else
4371251881Speter        {
4372251881Speter          /* This node is either a not-present node (which should be copied), or
4373251881Speter             a base-delete of some lower layer (which shouldn't).
4374251881Speter             Subversion <= 1.7 always added a not-present node here, which is
4375251881Speter             safe (as it postpones the hard work until commit time and then we
4376251881Speter             ask the repository), but it breaks some move scenarios.
4377251881Speter             */
4378251881Speter
4379251881Speter           if (! copyfrom_relpath)
4380251881Speter             {
4381251881Speter               SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4382251881Speter                                     scratch_pool));
4383251881Speter               return SVN_NO_ERROR;
4384251881Speter             }
4385251881Speter
4386251881Speter           /* Fall through. Install not present node */
4387251881Speter        }
4388251881Speter    case svn_wc__db_status_not_present:
4389251881Speter    case svn_wc__db_status_excluded:
4390251881Speter      /* These presence values should not create a new op depth */
4391251881Speter      if (dst_np_op_depth > 0)
4392251881Speter        {
4393251881Speter          dst_op_depth = dst_np_op_depth;
4394251881Speter          dst_np_op_depth = -1;
4395251881Speter        }
4396251881Speter      if (status == svn_wc__db_status_excluded)
4397251881Speter        dst_presence = svn_wc__db_status_excluded;
4398251881Speter      else
4399251881Speter        dst_presence = svn_wc__db_status_not_present;
4400251881Speter      break;
4401251881Speter    case svn_wc__db_status_server_excluded:
4402251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4403251881Speter                               _("Cannot copy '%s' excluded by server"),
4404251881Speter                               path_for_error_message(src_wcroot,
4405251881Speter                                                      src_relpath,
4406251881Speter                                                      scratch_pool));
4407251881Speter    default:
4408251881Speter      /* Perhaps we should allow incomplete to incomplete? We can't
4409251881Speter         avoid incomplete working nodes as one step in copying a
4410251881Speter         directory is to add incomplete children. */
4411251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4412251881Speter                               _("Cannot handle status of '%s'"),
4413251881Speter                               path_for_error_message(src_wcroot,
4414251881Speter                                                      src_relpath,
4415251881Speter                                                      scratch_pool));
4416251881Speter    }
4417251881Speter
4418251881Speter  if (kind == svn_node_dir)
4419251881Speter    {
4420251881Speter      int src_op_depth;
4421251881Speter
4422251881Speter      SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4423251881Speter      SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4424251881Speter                                   src_op_depth, scratch_pool, scratch_pool));
4425251881Speter    }
4426251881Speter  else
4427251881Speter    children = NULL;
4428251881Speter
4429251881Speter  if (src_wcroot == dst_wcroot)
4430251881Speter    {
4431251881Speter      svn_sqlite__stmt_t *stmt;
4432251881Speter      const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4433251881Speter                                                           scratch_pool);
4434251881Speter
4435251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4436251881Speter                                        STMT_INSERT_WORKING_NODE_COPY_FROM));
4437251881Speter
4438251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4439251881Speter                    src_wcroot->wc_id, src_relpath,
4440251881Speter                    dst_relpath,
4441251881Speter                    dst_op_depth,
4442251881Speter                    dst_parent_relpath,
4443251881Speter                    presence_map, dst_presence));
4444251881Speter
4445251881Speter      if (move_op_depth > 0)
4446251881Speter        {
4447251881Speter          if (relpath_depth(dst_relpath) == move_op_depth)
4448251881Speter            {
4449251881Speter              /* We're moving the root of the move operation.
4450251881Speter               *
4451251881Speter               * When an added node or the op-root of a copy is moved,
4452251881Speter               * there is no 'moved-from' corresponding to the moved-here
4453251881Speter               * node. So the net effect is the same as copy+delete.
4454251881Speter               * Perform a normal copy operation in these cases. */
4455251881Speter              if (!(status == svn_wc__db_status_added ||
4456251881Speter                    (status == svn_wc__db_status_copied && op_root)))
4457251881Speter                SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4458251881Speter            }
4459251881Speter          else
4460251881Speter            {
4461251881Speter              svn_sqlite__stmt_t *info_stmt;
4462251881Speter              svn_boolean_t have_row;
4463251881Speter
4464251881Speter              /* We're moving a child along with the root of the move.
4465251881Speter               *
4466251881Speter               * Set moved-here depending on dst_parent, propagating the
4467251881Speter               * above decision to moved-along children at the same op_depth.
4468251881Speter               * We can't use scan_addition() to detect moved-here because
4469251881Speter               * the delete-half of the move might not yet exist. */
4470251881Speter              SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4471251881Speter                                                STMT_SELECT_NODE_INFO));
4472251881Speter              SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4473251881Speter                                        dst_parent_relpath));
4474251881Speter              SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4475251881Speter              SVN_ERR_ASSERT(have_row);
4476251881Speter              if (svn_sqlite__column_boolean(info_stmt, 15) &&
4477251881Speter                  dst_op_depth == dst_parent_op_depth)
4478251881Speter                {
4479251881Speter                  SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4480251881Speter                  SVN_ERR(svn_sqlite__reset(info_stmt));
4481251881Speter                }
4482251881Speter              else
4483251881Speter                {
4484251881Speter                  SVN_ERR(svn_sqlite__reset(info_stmt));
4485251881Speter
4486251881Speter                  /* If the child has been moved into the tree we're moving,
4487251881Speter                   * keep its moved-here bit set. */
4488251881Speter                  SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4489251881Speter                                                    dst_wcroot->sdb,
4490251881Speter                                                    STMT_SELECT_NODE_INFO));
4491251881Speter                  SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4492251881Speter                                            dst_wcroot->wc_id, src_relpath));
4493251881Speter                  SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4494251881Speter                  SVN_ERR_ASSERT(have_row);
4495251881Speter                  if (svn_sqlite__column_boolean(info_stmt, 15))
4496251881Speter                    SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4497251881Speter                  SVN_ERR(svn_sqlite__reset(info_stmt));
4498251881Speter                }
4499251881Speter            }
4500251881Speter        }
4501251881Speter
4502251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
4503251881Speter
4504251881Speter      /* ### Copying changelist is OK for a move but what about a copy? */
4505251881Speter      SVN_ERR(copy_actual(src_wcroot, src_relpath,
4506251881Speter                          dst_wcroot, dst_relpath, scratch_pool));
4507251881Speter
4508251881Speter      if (dst_np_op_depth > 0)
4509251881Speter        {
4510251881Speter          /* We introduce a not-present node at the parent's op_depth to
4511251881Speter             properly start a new op-depth at our own op_depth. This marks
4512251881Speter             us as an op_root for commit and allows reverting just this
4513251881Speter             operation */
4514251881Speter
4515251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4516251881Speter                                            STMT_INSERT_NODE));
4517251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4518251881Speter                                    src_wcroot->wc_id, dst_relpath,
4519251881Speter                                    dst_np_op_depth, dst_parent_relpath,
4520251881Speter                                    copyfrom_id, copyfrom_relpath,
4521251881Speter                                    copyfrom_rev,
4522251881Speter                                    presence_map,
4523251881Speter                                       svn_wc__db_status_not_present,
4524251881Speter                                    /* NULL */
4525251881Speter                                    kind_map, kind));
4526251881Speter
4527251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
4528251881Speter        }
4529251881Speter      /* Insert incomplete children, if relevant.
4530251881Speter         The children are part of the same op and so have the same op_depth.
4531251881Speter         (The only time we'd want a different depth is during a recursive
4532251881Speter         simple add, but we never insert children here during a simple add.) */
4533251881Speter      if (kind == svn_node_dir
4534251881Speter          && dst_presence == svn_wc__db_status_normal)
4535251881Speter        SVN_ERR(insert_incomplete_children(
4536251881Speter                  dst_wcroot->sdb,
4537251881Speter                  dst_wcroot->wc_id,
4538251881Speter                  dst_relpath,
4539251881Speter                  copyfrom_id,
4540251881Speter                  copyfrom_relpath,
4541251881Speter                  copyfrom_rev,
4542251881Speter                  children,
4543251881Speter                  dst_op_depth,
4544251881Speter                  scratch_pool));
4545251881Speter    }
4546251881Speter  else
4547251881Speter    {
4548251881Speter      SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4549251881Speter                            dst_relpath, dst_presence, dst_op_depth,
4550251881Speter                            dst_np_op_depth, kind,
4551251881Speter                            children, copyfrom_id, copyfrom_relpath,
4552251881Speter                            copyfrom_rev, scratch_pool));
4553251881Speter    }
4554251881Speter
4555251881Speter  SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4556251881Speter
4557251881Speter  return SVN_NO_ERROR;
4558251881Speter}
4559251881Speter
4560251881Speter/* Baton for passing args to op_copy_txn(). */
4561251881Speterstruct op_copy_baton
4562251881Speter{
4563251881Speter  svn_wc__db_wcroot_t *src_wcroot;
4564251881Speter  const char *src_relpath;
4565251881Speter
4566251881Speter  svn_wc__db_wcroot_t *dst_wcroot;
4567251881Speter  const char *dst_relpath;
4568251881Speter
4569251881Speter  const svn_skel_t *work_items;
4570251881Speter
4571251881Speter  svn_boolean_t is_move;
4572251881Speter  const char *dst_op_root_relpath;
4573251881Speter};
4574251881Speter
4575251881Speter/* Helper for svn_wc__db_op_copy().
4576251881Speter *
4577251881Speter * Implements svn_sqlite__transaction_callback_t. */
4578251881Speterstatic svn_error_t *
4579251881Speterop_copy_txn(void * baton,
4580251881Speter            svn_sqlite__db_t *sdb,
4581251881Speter            apr_pool_t *scratch_pool)
4582251881Speter{
4583251881Speter  struct op_copy_baton *ocb = baton;
4584251881Speter  int move_op_depth;
4585251881Speter
4586251881Speter  if (sdb != ocb->dst_wcroot->sdb)
4587251881Speter    {
4588251881Speter       /* Source and destination databases differ; so also start a lock
4589251881Speter          in the destination database, by calling ourself in a lock. */
4590251881Speter
4591251881Speter      return svn_error_trace(
4592251881Speter                        svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4593251881Speter                                              op_copy_txn, ocb, scratch_pool));
4594251881Speter    }
4595251881Speter
4596251881Speter  /* From this point we can assume a lock in the src and dst databases */
4597251881Speter
4598251881Speter  if (ocb->is_move)
4599251881Speter    move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4600251881Speter  else
4601251881Speter    move_op_depth = 0;
4602251881Speter
4603251881Speter  SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4604251881Speter                     ocb->dst_wcroot, ocb->dst_relpath,
4605251881Speter                     ocb->work_items, move_op_depth, scratch_pool));
4606251881Speter
4607251881Speter  return SVN_NO_ERROR;
4608251881Speter}
4609251881Speter
4610251881Spetersvn_error_t *
4611251881Spetersvn_wc__db_op_copy(svn_wc__db_t *db,
4612251881Speter                   const char *src_abspath,
4613251881Speter                   const char *dst_abspath,
4614251881Speter                   const char *dst_op_root_abspath,
4615251881Speter                   svn_boolean_t is_move,
4616251881Speter                   const svn_skel_t *work_items,
4617251881Speter                   apr_pool_t *scratch_pool)
4618251881Speter{
4619251881Speter  struct op_copy_baton ocb = {0};
4620251881Speter
4621251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4622251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4623251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4624251881Speter
4625251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4626251881Speter                                                &ocb.src_relpath, db,
4627251881Speter                                                src_abspath,
4628251881Speter                                                scratch_pool, scratch_pool));
4629251881Speter  VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4630251881Speter
4631251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4632251881Speter                                                &ocb.dst_relpath,
4633251881Speter                                                db, dst_abspath,
4634251881Speter                                                scratch_pool, scratch_pool));
4635251881Speter  VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4636251881Speter
4637251881Speter  ocb.work_items = work_items;
4638251881Speter  ocb.is_move = is_move;
4639251881Speter  ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4640251881Speter                                                     dst_op_root_abspath);
4641251881Speter
4642251881Speter  /* Call with the sdb in src_wcroot. It might call itself again to
4643251881Speter     also obtain a lock in dst_wcroot */
4644251881Speter  SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4645251881Speter                                scratch_pool));
4646251881Speter
4647251881Speter  return SVN_NO_ERROR;
4648251881Speter}
4649251881Speter
4650251881Speter/* The txn body of svn_wc__db_op_handle_move_back */
4651251881Speterstatic svn_error_t *
4652251881Speterhandle_move_back(svn_boolean_t *moved_back,
4653251881Speter                 svn_wc__db_wcroot_t *wcroot,
4654251881Speter                 const char *local_relpath,
4655251881Speter                 const char *moved_from_relpath,
4656251881Speter                 const svn_skel_t *work_items,
4657251881Speter                 apr_pool_t *scratch_pool)
4658251881Speter{
4659251881Speter  svn_sqlite__stmt_t *stmt;
4660251881Speter  svn_wc__db_status_t status;
4661251881Speter  svn_boolean_t op_root;
4662251881Speter  svn_boolean_t have_more_work;
4663251881Speter  int from_op_depth = 0;
4664251881Speter  svn_boolean_t have_row;
4665251881Speter  svn_boolean_t different = FALSE;
4666251881Speter
4667251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4668251881Speter
4669251881Speter  SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4670251881Speter                                        NULL, NULL, NULL, NULL, NULL, NULL,
4671251881Speter                                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4672251881Speter                                        &op_root, NULL, NULL, NULL,
4673251881Speter                                        &have_more_work, NULL,
4674251881Speter                                        wcroot, local_relpath,
4675251881Speter                                        scratch_pool, scratch_pool));
4676251881Speter
4677251881Speter  if (status != svn_wc__db_status_added || !op_root)
4678251881Speter    return SVN_NO_ERROR;
4679251881Speter
4680251881Speter  /* We have two cases here: BASE-move-back and WORKING-move-back */
4681251881Speter  if (have_more_work)
4682251881Speter    SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4683251881Speter                        svn_relpath_dirname(local_relpath, scratch_pool)));
4684251881Speter  else
4685251881Speter    from_op_depth = 0;
4686251881Speter
4687251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4688251881Speter                                    STMT_SELECT_MOVED_BACK));
4689251881Speter
4690251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4691251881Speter                                          local_relpath,
4692251881Speter                                          from_op_depth,
4693251881Speter                                          relpath_depth(local_relpath)));
4694251881Speter
4695251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
4696251881Speter
4697251881Speter  SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4698251881Speter
4699251881Speter  {
4700251881Speter    svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4701251881Speter    const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4702251881Speter
4703251881Speter    if (!moved_here
4704251881Speter        || !moved_to
4705251881Speter        || strcmp(moved_to, moved_from_relpath))
4706251881Speter      {
4707251881Speter        different = TRUE;
4708251881Speter        have_row = FALSE;
4709251881Speter      }
4710251881Speter  }
4711251881Speter
4712251881Speter  while (have_row)
4713251881Speter    {
4714251881Speter      svn_wc__db_status_t upper_status;
4715251881Speter      svn_wc__db_status_t lower_status;
4716251881Speter
4717251881Speter      upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4718251881Speter
4719251881Speter      if (svn_sqlite__column_is_null(stmt, 5))
4720251881Speter        {
4721251881Speter          /* No lower layer replaced. */
4722251881Speter          if (upper_status != svn_wc__db_status_not_present)
4723251881Speter            {
4724251881Speter              different = TRUE;
4725251881Speter              break;
4726251881Speter            }
4727251881Speter          continue;
4728251881Speter        }
4729251881Speter
4730251881Speter      lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4731251881Speter
4732251881Speter      if (upper_status != lower_status)
4733251881Speter        {
4734251881Speter          different = TRUE;
4735251881Speter          break;
4736251881Speter        }
4737251881Speter
4738251881Speter      if (upper_status == svn_wc__db_status_not_present
4739251881Speter          || upper_status == svn_wc__db_status_excluded)
4740251881Speter        {
4741251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
4742251881Speter          continue; /* Nothing to check */
4743251881Speter        }
4744251881Speter      else if (upper_status != svn_wc__db_status_normal)
4745251881Speter        {
4746251881Speter          /* Not a normal move. Mixed revision move? */
4747251881Speter          different = TRUE;
4748251881Speter          break;
4749251881Speter        }
4750251881Speter
4751251881Speter      {
4752251881Speter        const char *upper_repos_relpath;
4753251881Speter        const char *lower_repos_relpath;
4754251881Speter
4755251881Speter        upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4756251881Speter        lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4757251881Speter
4758251881Speter        if (! upper_repos_relpath
4759251881Speter            || strcmp(upper_repos_relpath, lower_repos_relpath))
4760251881Speter          {
4761251881Speter            different = TRUE;
4762251881Speter            break;
4763251881Speter          }
4764251881Speter      }
4765251881Speter
4766251881Speter      {
4767251881Speter        svn_revnum_t upper_rev;
4768251881Speter        svn_revnum_t lower_rev;
4769251881Speter
4770251881Speter        upper_rev = svn_sqlite__column_revnum(stmt, 4);
4771251881Speter        lower_rev = svn_sqlite__column_revnum(stmt, 8);
4772251881Speter
4773251881Speter        if (upper_rev != lower_rev)
4774251881Speter          {
4775251881Speter            different = TRUE;
4776251881Speter            break;
4777251881Speter          }
4778251881Speter      }
4779251881Speter
4780251881Speter      {
4781251881Speter        apr_int64_t upper_repos_id;
4782251881Speter        apr_int64_t lower_repos_id;
4783251881Speter
4784251881Speter        upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4785251881Speter        lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4786251881Speter
4787251881Speter        if (upper_repos_id != lower_repos_id)
4788251881Speter          {
4789251881Speter            different = TRUE;
4790251881Speter            break;
4791251881Speter          }
4792251881Speter      }
4793251881Speter
4794251881Speter      /* Check moved_here? */
4795251881Speter
4796251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
4797251881Speter    }
4798251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
4799251881Speter
4800251881Speter  if (! different)
4801251881Speter    {
4802251881Speter      /* Ok, we can now safely remove this complete move, because we
4803251881Speter         determined that it 100% matches the layer below it. */
4804251881Speter
4805251881Speter      /* ### We could copy the recorded timestamps from the higher to the
4806251881Speter             lower layer in an attempt to improve status performance, but
4807251881Speter             generally these values should be the same anyway as it was
4808251881Speter             a no-op move. */
4809251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4810251881Speter                                        STMT_DELETE_MOVED_BACK));
4811251881Speter
4812251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4813251881Speter                                             local_relpath,
4814251881Speter                                             relpath_depth(local_relpath)));
4815251881Speter
4816251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
4817251881Speter
4818251881Speter      if (moved_back)
4819251881Speter        *moved_back = TRUE;
4820251881Speter    }
4821251881Speter
4822251881Speter  return SVN_NO_ERROR;
4823251881Speter}
4824251881Speter
4825251881Spetersvn_error_t *
4826251881Spetersvn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4827251881Speter                               svn_wc__db_t *db,
4828251881Speter                               const char *local_abspath,
4829251881Speter                               const char *moved_from_abspath,
4830251881Speter                               const svn_skel_t *work_items,
4831251881Speter                               apr_pool_t *scratch_pool)
4832251881Speter{
4833251881Speter  svn_wc__db_wcroot_t *wcroot;
4834251881Speter  const char *local_relpath;
4835251881Speter  const char *moved_from_relpath;
4836251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4837251881Speter
4838251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4839251881Speter                                                local_abspath,
4840251881Speter                                                scratch_pool, scratch_pool));
4841251881Speter  VERIFY_USABLE_WCROOT(wcroot);
4842251881Speter
4843251881Speter  if (moved_back)
4844251881Speter    *moved_back = FALSE;
4845251881Speter
4846251881Speter  moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4847251881Speter                                                moved_from_abspath);
4848251881Speter
4849251881Speter  if (! local_relpath[0]
4850251881Speter      || !moved_from_relpath)
4851251881Speter    {
4852251881Speter       /* WC-Roots can't be moved */
4853251881Speter      SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4854251881Speter      return SVN_NO_ERROR;
4855251881Speter    }
4856251881Speter
4857251881Speter  SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4858251881Speter                                       moved_from_relpath, work_items,
4859251881Speter                                       scratch_pool),
4860251881Speter                      wcroot);
4861251881Speter
4862251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4863251881Speter                        scratch_pool));
4864251881Speter
4865251881Speter  return SVN_NO_ERROR;
4866251881Speter}
4867251881Speter
4868251881Speter
4869251881Speter/* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4870251881Speter *
4871251881Speter * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4872251881Speter * a move, and indicates the op-depth of the move destination op-root. */
4873251881Speterstatic svn_error_t *
4874251881Speterdb_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4875251881Speter                          const char *src_relpath,
4876251881Speter                          int src_op_depth,
4877251881Speter                          svn_wc__db_wcroot_t *dst_wcroot,
4878251881Speter                          const char *dst_relpath,
4879251881Speter                          int dst_op_depth,
4880251881Speter                          int del_op_depth,
4881251881Speter                          apr_int64_t repos_id,
4882251881Speter                          const char *repos_relpath,
4883251881Speter                          svn_revnum_t revision,
4884251881Speter                          int move_op_depth,
4885251881Speter                          apr_pool_t *scratch_pool)
4886251881Speter{
4887251881Speter  const apr_array_header_t *children;
4888251881Speter  apr_pool_t *iterpool;
4889251881Speter  svn_wc__db_status_t status;
4890251881Speter  svn_node_kind_t kind;
4891251881Speter  svn_revnum_t node_revision;
4892251881Speter  const char *node_repos_relpath;
4893251881Speter  apr_int64_t node_repos_id;
4894251881Speter  svn_sqlite__stmt_t *stmt;
4895251881Speter  svn_wc__db_status_t dst_presence;
4896251881Speter  int i;
4897251881Speter
4898251881Speter  {
4899251881Speter    svn_error_t *err;
4900251881Speter    err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
4901251881Speter                                    &node_repos_relpath, &node_repos_id,
4902251881Speter                                    NULL, NULL, NULL, NULL, NULL, NULL,
4903251881Speter                                    NULL, NULL,
4904251881Speter                                    src_wcroot, src_relpath, src_op_depth,
4905251881Speter                                    scratch_pool, scratch_pool);
4906251881Speter
4907251881Speter    if (err)
4908251881Speter      {
4909251881Speter        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4910251881Speter          return svn_error_trace(err);
4911251881Speter
4912251881Speter        svn_error_clear(err);
4913251881Speter        return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
4914251881Speter      }
4915251881Speter  }
4916251881Speter
4917251881Speter  if (src_op_depth == 0)
4918251881Speter    {
4919251881Speter      /* If the node is switched or has a different revision then its parent
4920251881Speter         we shouldn't copy it. (We can't as we would have to insert it at
4921251881Speter         an unshadowed depth) */
4922251881Speter      if (status == svn_wc__db_status_not_present
4923251881Speter          || status == svn_wc__db_status_excluded
4924251881Speter          || status == svn_wc__db_status_server_excluded
4925251881Speter          || node_revision != revision
4926251881Speter          || node_repos_id != repos_id
4927251881Speter          || strcmp(node_repos_relpath, repos_relpath))
4928251881Speter        {
4929251881Speter          /* Add a not-present node in the destination wcroot */
4930251881Speter          struct insert_working_baton_t iwb;
4931251881Speter          const char *repos_root_url;
4932251881Speter          const char *repos_uuid;
4933251881Speter
4934251881Speter          SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4935251881Speter                                              src_wcroot->sdb, node_repos_id,
4936251881Speter                                              scratch_pool));
4937251881Speter
4938251881Speter          SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
4939251881Speter                                  dst_wcroot->sdb, scratch_pool));
4940251881Speter
4941251881Speter          blank_iwb(&iwb);
4942251881Speter
4943251881Speter          iwb.op_depth = dst_op_depth;
4944251881Speter          if (status != svn_wc__db_status_excluded)
4945251881Speter            iwb.presence = svn_wc__db_status_not_present;
4946251881Speter          else
4947251881Speter            iwb.presence = svn_wc__db_status_excluded;
4948251881Speter
4949251881Speter          iwb.kind = kind;
4950251881Speter
4951251881Speter          iwb.original_repos_id = node_repos_id;
4952251881Speter          iwb.original_revnum = node_revision;
4953251881Speter          iwb.original_repos_relpath = node_repos_relpath;
4954251881Speter
4955251881Speter          SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
4956251881Speter                                      scratch_pool));
4957251881Speter
4958251881Speter          return SVN_NO_ERROR;
4959251881Speter        }
4960251881Speter    }
4961251881Speter
4962251881Speter  iterpool = svn_pool_create(scratch_pool);
4963251881Speter
4964251881Speter  switch (status)
4965251881Speter    {
4966251881Speter    case svn_wc__db_status_normal:
4967251881Speter    case svn_wc__db_status_added:
4968251881Speter    case svn_wc__db_status_moved_here:
4969251881Speter    case svn_wc__db_status_copied:
4970251881Speter      dst_presence = svn_wc__db_status_normal;
4971251881Speter      break;
4972251881Speter    case svn_wc__db_status_deleted:
4973251881Speter    case svn_wc__db_status_not_present:
4974251881Speter      dst_presence = svn_wc__db_status_not_present;
4975251881Speter      break;
4976251881Speter    case svn_wc__db_status_excluded:
4977251881Speter      dst_presence = svn_wc__db_status_excluded;
4978251881Speter      break;
4979251881Speter    case svn_wc__db_status_server_excluded:
4980251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4981251881Speter                               _("Cannot copy '%s' excluded by server"),
4982251881Speter                               path_for_error_message(src_wcroot,
4983251881Speter                                                      src_relpath,
4984251881Speter                                                      scratch_pool));
4985251881Speter    default:
4986251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4987251881Speter                               _("Cannot handle status of '%s'"),
4988251881Speter                               path_for_error_message(src_wcroot,
4989251881Speter                                                      src_relpath,
4990251881Speter                                                      scratch_pool));
4991251881Speter    }
4992251881Speter
4993251881Speter  if (dst_presence == svn_wc__db_status_normal
4994251881Speter      && src_wcroot == dst_wcroot) /* ### Remove limitation */
4995251881Speter    {
4996251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4997251881Speter                             STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
4998251881Speter
4999251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5000251881Speter                        src_wcroot->wc_id, src_relpath,
5001251881Speter                        dst_relpath,
5002251881Speter                        dst_op_depth,
5003251881Speter                        svn_relpath_dirname(dst_relpath, iterpool),
5004251881Speter                        presence_map, dst_presence,
5005251881Speter                        src_op_depth));
5006251881Speter
5007251881Speter      /* moved_here */
5008251881Speter      if (dst_op_depth == move_op_depth)
5009251881Speter        SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5010251881Speter
5011251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
5012251881Speter
5013251881Speter      {
5014251881Speter        /* And mark it deleted to allow proper shadowing */
5015251881Speter        struct insert_working_baton_t iwb;
5016251881Speter
5017251881Speter        blank_iwb(&iwb);
5018251881Speter
5019251881Speter        iwb.op_depth = del_op_depth;
5020251881Speter        iwb.presence = svn_wc__db_status_base_deleted;
5021251881Speter
5022251881Speter        iwb.kind = kind;
5023251881Speter
5024251881Speter        SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5025251881Speter                                    scratch_pool));
5026251881Speter      }
5027251881Speter    }
5028251881Speter  else
5029251881Speter    {
5030251881Speter      struct insert_working_baton_t iwb;
5031251881Speter      if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5032251881Speter        dst_presence = svn_wc__db_status_not_present;
5033251881Speter
5034251881Speter      /* And mark it deleted to allow proper shadowing */
5035251881Speter
5036251881Speter      blank_iwb(&iwb);
5037251881Speter
5038251881Speter      iwb.op_depth = dst_op_depth;
5039251881Speter      iwb.presence = dst_presence;
5040251881Speter      iwb.kind = kind;
5041251881Speter
5042251881Speter      SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5043251881Speter                                    scratch_pool));
5044251881Speter    }
5045251881Speter
5046251881Speter  SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5047251881Speter                               src_op_depth, scratch_pool, iterpool));
5048251881Speter
5049251881Speter  for (i = 0; i < children->nelts; i++)
5050251881Speter    {
5051251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
5052251881Speter      const char *child_src_relpath;
5053251881Speter      const char *child_dst_relpath;
5054251881Speter      const char *child_repos_relpath = NULL;
5055251881Speter
5056251881Speter      svn_pool_clear(iterpool);
5057251881Speter      child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5058251881Speter      child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5059251881Speter
5060251881Speter      if (repos_relpath)
5061251881Speter        child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5062251881Speter
5063251881Speter      SVN_ERR(db_op_copy_shadowed_layer(
5064251881Speter                         src_wcroot, child_src_relpath, src_op_depth,
5065251881Speter                         dst_wcroot, child_dst_relpath, dst_op_depth,
5066251881Speter                         del_op_depth,
5067251881Speter                         repos_id, child_repos_relpath, revision,
5068251881Speter                         move_op_depth, scratch_pool));
5069251881Speter    }
5070251881Speter
5071251881Speter  svn_pool_destroy(iterpool);
5072251881Speter
5073251881Speter  return SVN_NO_ERROR;
5074251881Speter}
5075251881Speter
5076251881Speter/* Helper for svn_wc__db_op_copy_shadowed_layer().
5077251881Speter *
5078251881Speter * Implements  svn_sqlite__transaction_callback_t. */
5079251881Speterstatic svn_error_t *
5080251881Speterop_copy_shadowed_layer_txn(void *baton,
5081251881Speter                           svn_sqlite__db_t *sdb,
5082251881Speter                           apr_pool_t *scratch_pool)
5083251881Speter{
5084251881Speter  struct op_copy_baton *ocb = baton;
5085251881Speter  const char *src_parent_relpath;
5086251881Speter  const char *dst_parent_relpath;
5087251881Speter  int src_op_depth;
5088251881Speter  int dst_op_depth;
5089251881Speter  int del_op_depth;
5090251881Speter  const char *repos_relpath = NULL;
5091251881Speter  apr_int64_t repos_id = INVALID_REPOS_ID;
5092251881Speter  svn_revnum_t revision = SVN_INVALID_REVNUM;
5093251881Speter
5094251881Speter  if (sdb != ocb->dst_wcroot->sdb)
5095251881Speter    {
5096251881Speter       /* Source and destination databases differ; so also start a lock
5097251881Speter          in the destination database, by calling ourself in a lock. */
5098251881Speter
5099251881Speter      return svn_error_trace(
5100251881Speter                        svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5101251881Speter                                              op_copy_shadowed_layer_txn,
5102251881Speter                                              ocb, scratch_pool));
5103251881Speter    }
5104251881Speter
5105251881Speter  /* From this point we can assume a lock in the src and dst databases */
5106251881Speter
5107251881Speter
5108251881Speter  /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5109251881Speter  SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5110251881Speter
5111251881Speter  src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5112251881Speter  dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5113251881Speter
5114251881Speter  /* src_parent must be status normal or added; get its op-depth */
5115251881Speter  SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5116251881Speter
5117251881Speter  /* dst_parent must be status added; get its op-depth */
5118251881Speter  SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5119251881Speter
5120251881Speter  del_op_depth = relpath_depth(ocb->dst_relpath);
5121251881Speter
5122251881Speter  /* Get some information from the parent */
5123251881Speter  SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5124251881Speter                                    &repos_id, NULL, NULL, NULL, NULL, NULL,
5125251881Speter                                    NULL, NULL, NULL,
5126251881Speter                                    ocb->src_wcroot,
5127251881Speter                                    src_parent_relpath, src_op_depth,
5128251881Speter                                    scratch_pool, scratch_pool));
5129251881Speter
5130251881Speter  if (repos_relpath == NULL)
5131251881Speter    {
5132251881Speter      /* The node is a local addition and has no shadowed information */
5133251881Speter      return SVN_NO_ERROR;
5134251881Speter    }
5135251881Speter
5136251881Speter  /* And calculate the child repos relpath */
5137251881Speter  repos_relpath = svn_relpath_join(repos_relpath,
5138251881Speter                                   svn_relpath_basename(ocb->src_relpath,
5139251881Speter                                                        NULL),
5140251881Speter                                   scratch_pool);
5141251881Speter
5142251881Speter  SVN_ERR(db_op_copy_shadowed_layer(
5143251881Speter                        ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5144251881Speter                        ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5145251881Speter                        del_op_depth,
5146251881Speter                        repos_id, repos_relpath, revision,
5147251881Speter                        (ocb->is_move ? dst_op_depth : 0),
5148251881Speter                        scratch_pool));
5149251881Speter
5150251881Speter  return SVN_NO_ERROR;
5151251881Speter}
5152251881Speter
5153251881Spetersvn_error_t *
5154251881Spetersvn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5155251881Speter                                  const char *src_abspath,
5156251881Speter                                  const char *dst_abspath,
5157251881Speter                                  svn_boolean_t is_move,
5158251881Speter                                  apr_pool_t *scratch_pool)
5159251881Speter{
5160251881Speter  struct op_copy_baton ocb = {0};
5161251881Speter
5162251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5163251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5164251881Speter
5165251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5166251881Speter                                                &ocb.src_relpath, db,
5167251881Speter                                                src_abspath,
5168251881Speter                                                scratch_pool, scratch_pool));
5169251881Speter  VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5170251881Speter
5171251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5172251881Speter                                                &ocb.dst_relpath,
5173251881Speter                                                db, dst_abspath,
5174251881Speter                                                scratch_pool, scratch_pool));
5175251881Speter  VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5176251881Speter
5177251881Speter  ocb.is_move = is_move;
5178251881Speter  ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5179251881Speter
5180251881Speter  ocb.work_items = NULL;
5181251881Speter
5182251881Speter  /* Call with the sdb in src_wcroot. It might call itself again to
5183251881Speter     also obtain a lock in dst_wcroot */
5184251881Speter  SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5185251881Speter                                op_copy_shadowed_layer_txn,
5186251881Speter                                &ocb, scratch_pool));
5187251881Speter
5188251881Speter  return SVN_NO_ERROR;
5189251881Speter}
5190251881Speter
5191251881Speter
5192251881Speter/* If there are any server-excluded base nodes then the copy must fail
5193251881Speter   as it's not possible to commit such a copy.
5194251881Speter   Return an error if there are any server-excluded nodes. */
5195251881Speterstatic svn_error_t *
5196251881Spetercatch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5197251881Speter                              const char *local_relpath,
5198251881Speter                              apr_pool_t *scratch_pool)
5199251881Speter{
5200251881Speter  svn_sqlite__stmt_t *stmt;
5201251881Speter  svn_boolean_t have_row;
5202251881Speter  const char *server_excluded_relpath;
5203251881Speter
5204251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5205251881Speter                                    STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5206251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
5207251881Speter                            wcroot->wc_id,
5208251881Speter                            local_relpath));
5209251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
5210251881Speter  if (have_row)
5211251881Speter    server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5212251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
5213251881Speter  if (have_row)
5214251881Speter    return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5215251881Speter                             _("Cannot copy '%s' excluded by server"),
5216251881Speter                             path_for_error_message(wcroot,
5217251881Speter                                                    server_excluded_relpath,
5218251881Speter                                                    scratch_pool));
5219251881Speter
5220251881Speter  return SVN_NO_ERROR;
5221251881Speter}
5222251881Speter
5223251881Speter
5224251881Spetersvn_error_t *
5225251881Spetersvn_wc__db_op_copy_dir(svn_wc__db_t *db,
5226251881Speter                       const char *local_abspath,
5227251881Speter                       const apr_hash_t *props,
5228251881Speter                       svn_revnum_t changed_rev,
5229251881Speter                       apr_time_t changed_date,
5230251881Speter                       const char *changed_author,
5231251881Speter                       const char *original_repos_relpath,
5232251881Speter                       const char *original_root_url,
5233251881Speter                       const char *original_uuid,
5234251881Speter                       svn_revnum_t original_revision,
5235251881Speter                       const apr_array_header_t *children,
5236251881Speter                       svn_boolean_t is_move,
5237251881Speter                       svn_depth_t depth,
5238251881Speter                       const svn_skel_t *conflict,
5239251881Speter                       const svn_skel_t *work_items,
5240251881Speter                       apr_pool_t *scratch_pool)
5241251881Speter{
5242251881Speter  svn_wc__db_wcroot_t *wcroot;
5243251881Speter  const char *local_relpath;
5244251881Speter  insert_working_baton_t iwb;
5245251881Speter  int parent_op_depth;
5246251881Speter
5247251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5248251881Speter  SVN_ERR_ASSERT(props != NULL);
5249251881Speter  /* ### any assertions for CHANGED_* ?  */
5250251881Speter  /* ### any assertions for ORIGINAL_* ?  */
5251251881Speter#if 0
5252251881Speter  SVN_ERR_ASSERT(children != NULL);
5253251881Speter#endif
5254251881Speter
5255251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5256251881Speter                              local_abspath, scratch_pool, scratch_pool));
5257251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5258251881Speter
5259251881Speter  blank_iwb(&iwb);
5260251881Speter
5261251881Speter  iwb.presence = svn_wc__db_status_normal;
5262251881Speter  iwb.kind = svn_node_dir;
5263251881Speter
5264251881Speter  iwb.props = props;
5265251881Speter  iwb.changed_rev = changed_rev;
5266251881Speter  iwb.changed_date = changed_date;
5267251881Speter  iwb.changed_author = changed_author;
5268251881Speter
5269251881Speter  if (original_root_url != NULL)
5270251881Speter    {
5271251881Speter      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5272251881Speter                              original_root_url, original_uuid,
5273251881Speter                              wcroot->sdb, scratch_pool));
5274251881Speter      iwb.original_repos_relpath = original_repos_relpath;
5275251881Speter      iwb.original_revnum = original_revision;
5276251881Speter    }
5277251881Speter
5278251881Speter  /* ### Should we do this inside the transaction? */
5279251881Speter  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5280251881Speter                            &parent_op_depth, iwb.original_repos_id,
5281251881Speter                            original_repos_relpath, original_revision,
5282251881Speter                            wcroot, local_relpath, scratch_pool));
5283251881Speter
5284251881Speter  iwb.children = children;
5285251881Speter  iwb.depth = depth;
5286251881Speter  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5287251881Speter                               iwb.op_depth == parent_op_depth);
5288251881Speter
5289251881Speter  iwb.work_items = work_items;
5290251881Speter  iwb.conflict = conflict;
5291251881Speter
5292251881Speter  SVN_WC__DB_WITH_TXN(
5293251881Speter                insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5294251881Speter                wcroot);
5295251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5296251881Speter
5297251881Speter  return SVN_NO_ERROR;
5298251881Speter}
5299251881Speter
5300251881Speter
5301251881Spetersvn_error_t *
5302251881Spetersvn_wc__db_op_copy_file(svn_wc__db_t *db,
5303251881Speter                        const char *local_abspath,
5304251881Speter                        const apr_hash_t *props,
5305251881Speter                        svn_revnum_t changed_rev,
5306251881Speter                        apr_time_t changed_date,
5307251881Speter                        const char *changed_author,
5308251881Speter                        const char *original_repos_relpath,
5309251881Speter                        const char *original_root_url,
5310251881Speter                        const char *original_uuid,
5311251881Speter                        svn_revnum_t original_revision,
5312251881Speter                        const svn_checksum_t *checksum,
5313251881Speter                        svn_boolean_t update_actual_props,
5314251881Speter                        const apr_hash_t *new_actual_props,
5315251881Speter                        svn_boolean_t is_move,
5316251881Speter                        const svn_skel_t *conflict,
5317251881Speter                        const svn_skel_t *work_items,
5318251881Speter                        apr_pool_t *scratch_pool)
5319251881Speter{
5320251881Speter  svn_wc__db_wcroot_t *wcroot;
5321251881Speter  const char *local_relpath;
5322251881Speter  insert_working_baton_t iwb;
5323251881Speter  int parent_op_depth;
5324251881Speter
5325251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5326251881Speter  SVN_ERR_ASSERT(props != NULL);
5327251881Speter  /* ### any assertions for CHANGED_* ?  */
5328251881Speter  SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5329251881Speter                  && ! original_uuid && ! checksum
5330251881Speter                  && original_revision == SVN_INVALID_REVNUM)
5331251881Speter                 || (original_repos_relpath && original_root_url
5332251881Speter                     && original_uuid && checksum
5333251881Speter                     && original_revision != SVN_INVALID_REVNUM));
5334251881Speter
5335251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5336251881Speter                              local_abspath, scratch_pool, scratch_pool));
5337251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5338251881Speter
5339251881Speter  blank_iwb(&iwb);
5340251881Speter
5341251881Speter  iwb.presence = svn_wc__db_status_normal;
5342251881Speter  iwb.kind = svn_node_file;
5343251881Speter
5344251881Speter  iwb.props = props;
5345251881Speter  iwb.changed_rev = changed_rev;
5346251881Speter  iwb.changed_date = changed_date;
5347251881Speter  iwb.changed_author = changed_author;
5348251881Speter
5349251881Speter  if (original_root_url != NULL)
5350251881Speter    {
5351251881Speter      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5352251881Speter                              original_root_url, original_uuid,
5353251881Speter                              wcroot->sdb, scratch_pool));
5354251881Speter      iwb.original_repos_relpath = original_repos_relpath;
5355251881Speter      iwb.original_revnum = original_revision;
5356251881Speter    }
5357251881Speter
5358251881Speter  /* ### Should we do this inside the transaction? */
5359251881Speter  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5360251881Speter                            &parent_op_depth, iwb.original_repos_id,
5361251881Speter                            original_repos_relpath, original_revision,
5362251881Speter                            wcroot, local_relpath, scratch_pool));
5363251881Speter
5364251881Speter  iwb.checksum = checksum;
5365251881Speter  iwb.moved_here = is_move && (parent_op_depth == 0 ||
5366251881Speter                               iwb.op_depth == parent_op_depth);
5367251881Speter
5368251881Speter  if (update_actual_props)
5369251881Speter    {
5370251881Speter      iwb.update_actual_props = update_actual_props;
5371251881Speter      iwb.new_actual_props = new_actual_props;
5372251881Speter    }
5373251881Speter
5374251881Speter  iwb.work_items = work_items;
5375251881Speter  iwb.conflict = conflict;
5376251881Speter
5377251881Speter  SVN_WC__DB_WITH_TXN(
5378251881Speter          insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5379251881Speter          wcroot);
5380251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5381251881Speter
5382251881Speter  return SVN_NO_ERROR;
5383251881Speter}
5384251881Speter
5385251881Speter
5386251881Spetersvn_error_t *
5387251881Spetersvn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5388251881Speter                           const char *local_abspath,
5389251881Speter                           const apr_hash_t *props,
5390251881Speter                           svn_revnum_t changed_rev,
5391251881Speter                           apr_time_t changed_date,
5392251881Speter                           const char *changed_author,
5393251881Speter                           const char *original_repos_relpath,
5394251881Speter                           const char *original_root_url,
5395251881Speter                           const char *original_uuid,
5396251881Speter                           svn_revnum_t original_revision,
5397251881Speter                           const char *target,
5398251881Speter                           const svn_skel_t *conflict,
5399251881Speter                           const svn_skel_t *work_items,
5400251881Speter                           apr_pool_t *scratch_pool)
5401251881Speter{
5402251881Speter  svn_wc__db_wcroot_t *wcroot;
5403251881Speter  const char *local_relpath;
5404251881Speter  insert_working_baton_t iwb;
5405251881Speter  int parent_op_depth;
5406251881Speter
5407251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5408251881Speter  SVN_ERR_ASSERT(props != NULL);
5409251881Speter  /* ### any assertions for CHANGED_* ?  */
5410251881Speter  /* ### any assertions for ORIGINAL_* ?  */
5411251881Speter  SVN_ERR_ASSERT(target != NULL);
5412251881Speter
5413251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5414251881Speter                              local_abspath, scratch_pool, scratch_pool));
5415251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5416251881Speter
5417251881Speter  blank_iwb(&iwb);
5418251881Speter
5419251881Speter  iwb.presence = svn_wc__db_status_normal;
5420251881Speter  iwb.kind = svn_node_symlink;
5421251881Speter
5422251881Speter  iwb.props = props;
5423251881Speter  iwb.changed_rev = changed_rev;
5424251881Speter  iwb.changed_date = changed_date;
5425251881Speter  iwb.changed_author = changed_author;
5426251881Speter  iwb.moved_here = FALSE;
5427251881Speter
5428251881Speter  if (original_root_url != NULL)
5429251881Speter    {
5430251881Speter      SVN_ERR(create_repos_id(&iwb.original_repos_id,
5431251881Speter                              original_root_url, original_uuid,
5432251881Speter                              wcroot->sdb, scratch_pool));
5433251881Speter      iwb.original_repos_relpath = original_repos_relpath;
5434251881Speter      iwb.original_revnum = original_revision;
5435251881Speter    }
5436251881Speter
5437251881Speter  /* ### Should we do this inside the transaction? */
5438251881Speter  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5439251881Speter                            &parent_op_depth, iwb.original_repos_id,
5440251881Speter                            original_repos_relpath, original_revision,
5441251881Speter                            wcroot, local_relpath, scratch_pool));
5442251881Speter
5443251881Speter  iwb.target = target;
5444251881Speter
5445251881Speter  iwb.work_items = work_items;
5446251881Speter  iwb.conflict = conflict;
5447251881Speter
5448251881Speter  SVN_WC__DB_WITH_TXN(
5449251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5450251881Speter            wcroot);
5451251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5452251881Speter
5453251881Speter  return SVN_NO_ERROR;
5454251881Speter}
5455251881Speter
5456251881Speter
5457251881Spetersvn_error_t *
5458251881Spetersvn_wc__db_op_add_directory(svn_wc__db_t *db,
5459251881Speter                            const char *local_abspath,
5460251881Speter                            const apr_hash_t *props,
5461251881Speter                            const svn_skel_t *work_items,
5462251881Speter                            apr_pool_t *scratch_pool)
5463251881Speter{
5464251881Speter  svn_wc__db_wcroot_t *wcroot;
5465251881Speter  const char *local_relpath;
5466251881Speter  const char *dir_abspath;
5467251881Speter  const char *name;
5468251881Speter  insert_working_baton_t iwb;
5469251881Speter
5470251881Speter  /* Resolve wcroot via parent directory to avoid obstruction handling */
5471251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5472251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5473251881Speter
5474251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5475251881Speter                              dir_abspath, scratch_pool, scratch_pool));
5476251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5477251881Speter
5478251881Speter  blank_iwb(&iwb);
5479251881Speter
5480251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5481251881Speter  iwb.presence = svn_wc__db_status_normal;
5482251881Speter  iwb.kind = svn_node_dir;
5483251881Speter  iwb.op_depth = relpath_depth(local_relpath);
5484251881Speter  if (props && apr_hash_count((apr_hash_t *)props))
5485251881Speter    {
5486251881Speter      iwb.update_actual_props = TRUE;
5487251881Speter      iwb.new_actual_props = props;
5488251881Speter    }
5489251881Speter
5490251881Speter  iwb.work_items = work_items;
5491251881Speter
5492251881Speter  SVN_WC__DB_WITH_TXN(
5493251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5494251881Speter            wcroot);
5495251881Speter  /* Use depth infinity to make sure we have no invalid cached information
5496251881Speter   * about children of this dir. */
5497251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5498251881Speter                        scratch_pool));
5499251881Speter
5500251881Speter  return SVN_NO_ERROR;
5501251881Speter}
5502251881Speter
5503251881Speter
5504251881Spetersvn_error_t *
5505251881Spetersvn_wc__db_op_add_file(svn_wc__db_t *db,
5506251881Speter                       const char *local_abspath,
5507251881Speter                       const apr_hash_t *props,
5508251881Speter                       const svn_skel_t *work_items,
5509251881Speter                       apr_pool_t *scratch_pool)
5510251881Speter{
5511251881Speter  svn_wc__db_wcroot_t *wcroot;
5512251881Speter  const char *local_relpath;
5513251881Speter  insert_working_baton_t iwb;
5514251881Speter  const char *dir_abspath;
5515251881Speter  const char *name;
5516251881Speter
5517251881Speter  /* Resolve wcroot via parent directory to avoid obstruction handling */
5518251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5519251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5520251881Speter
5521251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5522251881Speter                              dir_abspath, scratch_pool, scratch_pool));
5523251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5524251881Speter
5525251881Speter  blank_iwb(&iwb);
5526251881Speter
5527251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5528251881Speter  iwb.presence = svn_wc__db_status_normal;
5529251881Speter  iwb.kind = svn_node_file;
5530251881Speter  iwb.op_depth = relpath_depth(local_relpath);
5531251881Speter  if (props && apr_hash_count((apr_hash_t *)props))
5532251881Speter    {
5533251881Speter      iwb.update_actual_props = TRUE;
5534251881Speter      iwb.new_actual_props = props;
5535251881Speter    }
5536251881Speter
5537251881Speter  iwb.work_items = work_items;
5538251881Speter
5539251881Speter  SVN_WC__DB_WITH_TXN(
5540251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5541251881Speter            wcroot);
5542251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5543251881Speter
5544251881Speter  return SVN_NO_ERROR;
5545251881Speter}
5546251881Speter
5547251881Speter
5548251881Spetersvn_error_t *
5549251881Spetersvn_wc__db_op_add_symlink(svn_wc__db_t *db,
5550251881Speter                          const char *local_abspath,
5551251881Speter                          const char *target,
5552251881Speter                          const apr_hash_t *props,
5553251881Speter                          const svn_skel_t *work_items,
5554251881Speter                          apr_pool_t *scratch_pool)
5555251881Speter{
5556251881Speter  svn_wc__db_wcroot_t *wcroot;
5557251881Speter  const char *local_relpath;
5558251881Speter  insert_working_baton_t iwb;
5559251881Speter  const char *dir_abspath;
5560251881Speter  const char *name;
5561251881Speter
5562251881Speter  /* Resolve wcroot via parent directory to avoid obstruction handling */
5563251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5564251881Speter  SVN_ERR_ASSERT(target != NULL);
5565251881Speter
5566251881Speter  svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5567251881Speter
5568251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5569251881Speter                              dir_abspath, scratch_pool, scratch_pool));
5570251881Speter
5571251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5572251881Speter
5573251881Speter  blank_iwb(&iwb);
5574251881Speter
5575251881Speter  local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5576251881Speter  iwb.presence = svn_wc__db_status_normal;
5577251881Speter  iwb.kind = svn_node_symlink;
5578251881Speter  iwb.op_depth = relpath_depth(local_relpath);
5579251881Speter  if (props && apr_hash_count((apr_hash_t *)props))
5580251881Speter    {
5581251881Speter      iwb.update_actual_props = TRUE;
5582251881Speter      iwb.new_actual_props = props;
5583251881Speter    }
5584251881Speter
5585251881Speter  iwb.target = target;
5586251881Speter
5587251881Speter  iwb.work_items = work_items;
5588251881Speter
5589251881Speter  SVN_WC__DB_WITH_TXN(
5590251881Speter            insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5591251881Speter            wcroot);
5592251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5593251881Speter
5594251881Speter  return SVN_NO_ERROR;
5595251881Speter}
5596251881Speter
5597251881Speter/* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5598251881Speterstatic svn_error_t *
5599251881Speterdb_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5600251881Speter                   const char *local_relpath,
5601251881Speter                   apr_int64_t recorded_size,
5602251881Speter                   apr_int64_t recorded_time,
5603251881Speter                   apr_pool_t *scratch_pool)
5604251881Speter{
5605251881Speter  svn_sqlite__stmt_t *stmt;
5606251881Speter  int affected_rows;
5607251881Speter
5608251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5609251881Speter                                    STMT_UPDATE_NODE_FILEINFO));
5610251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5611251881Speter                            recorded_size, recorded_time));
5612251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5613251881Speter
5614251881Speter  SVN_ERR_ASSERT(affected_rows == 1);
5615251881Speter
5616251881Speter  return SVN_NO_ERROR;
5617251881Speter}
5618251881Speter
5619251881Speter
5620251881Spetersvn_error_t *
5621251881Spetersvn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5622251881Speter                                  const char *local_abspath,
5623251881Speter                                  svn_filesize_t recorded_size,
5624251881Speter                                  apr_time_t recorded_time,
5625251881Speter                                  apr_pool_t *scratch_pool)
5626251881Speter{
5627251881Speter  svn_wc__db_wcroot_t *wcroot;
5628251881Speter  const char *local_relpath;
5629251881Speter
5630251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5631251881Speter
5632251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5633251881Speter                              local_abspath, scratch_pool, scratch_pool));
5634251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5635251881Speter
5636251881Speter  SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5637251881Speter                             recorded_size, recorded_time, scratch_pool));
5638251881Speter
5639251881Speter  /* We *totally* monkeyed the entries. Toss 'em.  */
5640251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5641251881Speter
5642251881Speter  return SVN_NO_ERROR;
5643251881Speter}
5644251881Speter
5645251881Speter
5646251881Speter/* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5647251881Speter * PROPS.
5648251881Speter *
5649251881Speter * Note: PROPS=NULL means the actual props are the same as the pristine
5650251881Speter * props; to indicate no properties when the pristine has some props,
5651251881Speter * PROPS must be an empty hash. */
5652251881Speterstatic svn_error_t *
5653251881Speterset_actual_props(apr_int64_t wc_id,
5654251881Speter                 const char *local_relpath,
5655251881Speter                 apr_hash_t *props,
5656251881Speter                 svn_sqlite__db_t *db,
5657251881Speter                 apr_pool_t *scratch_pool)
5658251881Speter{
5659251881Speter  svn_sqlite__stmt_t *stmt;
5660251881Speter  int affected_rows;
5661251881Speter
5662251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5663251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5664251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5665251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5666251881Speter
5667251881Speter  if (affected_rows == 1 || !props)
5668251881Speter    return SVN_NO_ERROR; /* We are done */
5669251881Speter
5670251881Speter  /* We have to insert a row in ACTUAL */
5671251881Speter
5672251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5673251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5674251881Speter  if (*local_relpath != '\0')
5675251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5676251881Speter                                  svn_relpath_dirname(local_relpath,
5677251881Speter                                                      scratch_pool)));
5678251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5679251881Speter  return svn_error_trace(svn_sqlite__step_done(stmt));
5680251881Speter}
5681251881Speter
5682251881Speter
5683251881Speter/* The body of svn_wc__db_op_set_props().
5684251881Speter
5685251881Speter   Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5686251881Speter   Create an entry in the ACTUAL table for the node if it does not yet
5687251881Speter   have one.
5688251881Speter   To specify no properties, BATON->props must be an empty hash, not NULL.
5689251881Speter   BATON is of type 'struct set_props_baton_t'.
5690251881Speter*/
5691251881Speterstatic svn_error_t *
5692251881Speterset_props_txn(svn_wc__db_wcroot_t *wcroot,
5693251881Speter              const char *local_relpath,
5694251881Speter              apr_hash_t *props,
5695251881Speter              svn_boolean_t clear_recorded_info,
5696251881Speter              const svn_skel_t *conflict,
5697251881Speter              const svn_skel_t *work_items,
5698251881Speter              apr_pool_t *scratch_pool)
5699251881Speter{
5700251881Speter  apr_hash_t *pristine_props;
5701251881Speter
5702251881Speter  /* Check if the props are modified. If no changes, then wipe out the
5703251881Speter     ACTUAL props.  PRISTINE_PROPS==NULL means that any
5704251881Speter     ACTUAL props are okay as provided, so go ahead and set them.  */
5705251881Speter  SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5706251881Speter                                 scratch_pool, scratch_pool));
5707251881Speter  if (props && pristine_props)
5708251881Speter    {
5709251881Speter      apr_array_header_t *prop_diffs;
5710251881Speter
5711251881Speter      SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5712251881Speter                             scratch_pool));
5713251881Speter      if (prop_diffs->nelts == 0)
5714251881Speter        props = NULL;
5715251881Speter    }
5716251881Speter
5717251881Speter  SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5718251881Speter                           props, wcroot->sdb, scratch_pool));
5719251881Speter
5720251881Speter  if (clear_recorded_info)
5721251881Speter    {
5722251881Speter      SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5723251881Speter                                 SVN_INVALID_FILESIZE, 0,
5724251881Speter                                 scratch_pool));
5725251881Speter    }
5726251881Speter
5727251881Speter  /* And finally.  */
5728251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5729251881Speter  if (conflict)
5730251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5731251881Speter                                              conflict, scratch_pool));
5732251881Speter
5733251881Speter  return SVN_NO_ERROR;
5734251881Speter}
5735251881Speter
5736251881Speter
5737251881Spetersvn_error_t *
5738251881Spetersvn_wc__db_op_set_props(svn_wc__db_t *db,
5739251881Speter                        const char *local_abspath,
5740251881Speter                        apr_hash_t *props,
5741251881Speter                        svn_boolean_t clear_recorded_info,
5742251881Speter                        const svn_skel_t *conflict,
5743251881Speter                        const svn_skel_t *work_items,
5744251881Speter                        apr_pool_t *scratch_pool)
5745251881Speter{
5746251881Speter  svn_wc__db_wcroot_t *wcroot;
5747251881Speter  const char *local_relpath;
5748251881Speter
5749251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5750251881Speter
5751251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5752251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
5753251881Speter  VERIFY_USABLE_WCROOT(wcroot);
5754251881Speter
5755251881Speter  SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5756251881Speter                                    clear_recorded_info, conflict, work_items,
5757251881Speter                                    scratch_pool),
5758251881Speter                      wcroot);
5759251881Speter  return SVN_NO_ERROR;
5760251881Speter}
5761251881Speter
5762251881Speter
5763251881Spetersvn_error_t *
5764251881Spetersvn_wc__db_op_modified(svn_wc__db_t *db,
5765251881Speter                       const char *local_abspath,
5766251881Speter                       apr_pool_t *scratch_pool)
5767251881Speter{
5768251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5769251881Speter
5770251881Speter  NOT_IMPLEMENTED();
5771251881Speter}
5772251881Speter
5773251881Speter/* */
5774251881Speterstatic svn_error_t *
5775251881Speterpopulate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5776251881Speter                      const char *local_relpath,
5777251881Speter                      svn_depth_t depth,
5778251881Speter                      const apr_array_header_t *changelist_filter,
5779251881Speter                      apr_pool_t *scratch_pool)
5780251881Speter{
5781251881Speter  svn_sqlite__stmt_t *stmt;
5782251881Speter  int affected_rows = 0;
5783251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5784251881Speter                                      STMT_CREATE_TARGETS_LIST));
5785251881Speter
5786251881Speter  if (changelist_filter && changelist_filter->nelts > 0)
5787251881Speter    {
5788251881Speter      /* Iterate over the changelists, adding the nodes which match.
5789251881Speter         Common case: we only have one changelist, so this only
5790251881Speter         happens once. */
5791251881Speter      int i;
5792251881Speter      int stmt_idx;
5793251881Speter
5794251881Speter      switch (depth)
5795251881Speter        {
5796251881Speter          case svn_depth_empty:
5797251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5798251881Speter            break;
5799251881Speter
5800251881Speter          case svn_depth_files:
5801251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5802251881Speter            break;
5803251881Speter
5804251881Speter          case svn_depth_immediates:
5805251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5806251881Speter            break;
5807251881Speter
5808251881Speter          case svn_depth_infinity:
5809251881Speter            stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5810251881Speter            break;
5811251881Speter
5812251881Speter          default:
5813251881Speter            /* We don't know how to handle unknown or exclude. */
5814251881Speter            SVN_ERR_MALFUNCTION();
5815251881Speter            break;
5816251881Speter        }
5817251881Speter
5818251881Speter      for (i = 0; i < changelist_filter->nelts; i++)
5819251881Speter        {
5820251881Speter          int sub_affected;
5821251881Speter          const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5822251881Speter                                                 const char *);
5823251881Speter
5824251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5825251881Speter                                        STMT_INSERT_TARGET_WITH_CHANGELIST));
5826251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5827251881Speter                                    local_relpath, changelist));
5828251881Speter          SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5829251881Speter
5830251881Speter          /* If the root is matched by the changelist, we don't have to match
5831251881Speter             the children. As that tells us the root is a file */
5832251881Speter          if (!sub_affected && depth > svn_depth_empty)
5833251881Speter            {
5834251881Speter              SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5835251881Speter              SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5836251881Speter                                        local_relpath, changelist));
5837251881Speter              SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5838251881Speter            }
5839251881Speter
5840251881Speter          affected_rows += sub_affected;
5841251881Speter        }
5842251881Speter    }
5843251881Speter  else /* No changelist filtering */
5844251881Speter    {
5845251881Speter      int stmt_idx;
5846251881Speter      int sub_affected;
5847251881Speter
5848251881Speter      switch (depth)
5849251881Speter        {
5850251881Speter          case svn_depth_empty:
5851251881Speter            stmt_idx = STMT_INSERT_TARGET;
5852251881Speter            break;
5853251881Speter
5854251881Speter          case svn_depth_files:
5855251881Speter            stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5856251881Speter            break;
5857251881Speter
5858251881Speter          case svn_depth_immediates:
5859251881Speter            stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5860251881Speter            break;
5861251881Speter
5862251881Speter          case svn_depth_infinity:
5863251881Speter            stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5864251881Speter            break;
5865251881Speter
5866251881Speter          default:
5867251881Speter            /* We don't know how to handle unknown or exclude. */
5868251881Speter            SVN_ERR_MALFUNCTION();
5869251881Speter            break;
5870251881Speter        }
5871251881Speter
5872251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5873251881Speter                                        STMT_INSERT_TARGET));
5874251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5875251881Speter      SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5876251881Speter      affected_rows += sub_affected;
5877251881Speter
5878251881Speter      if (depth > svn_depth_empty)
5879251881Speter        {
5880251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5881251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5882251881Speter          SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5883251881Speter          affected_rows += sub_affected;
5884251881Speter        }
5885251881Speter    }
5886251881Speter
5887251881Speter  /* Does the target exist? */
5888251881Speter  if (affected_rows == 0)
5889251881Speter    {
5890251881Speter      svn_boolean_t exists;
5891251881Speter      SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
5892251881Speter
5893251881Speter      if (!exists)
5894251881Speter        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
5895251881Speter                                 _("The node '%s' was not found."),
5896251881Speter                                 path_for_error_message(wcroot,
5897251881Speter                                                        local_relpath,
5898251881Speter                                                        scratch_pool));
5899251881Speter    }
5900251881Speter
5901251881Speter  return SVN_NO_ERROR;
5902251881Speter}
5903251881Speter
5904251881Speter
5905251881Speter#if 0
5906251881Speterstatic svn_error_t *
5907251881Speterdump_targets(svn_wc__db_wcroot_t *wcroot,
5908251881Speter             apr_pool_t *scratch_pool)
5909251881Speter{
5910251881Speter  svn_sqlite__stmt_t *stmt;
5911251881Speter  svn_boolean_t have_row;
5912251881Speter
5913251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5914251881Speter                                    STMT_SELECT_TARGETS));
5915251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
5916251881Speter  while (have_row)
5917251881Speter    {
5918251881Speter      const char *target = svn_sqlite__column_text(stmt, 0, NULL);
5919251881Speter      SVN_DBG(("Target: '%s'\n", target));
5920251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
5921251881Speter    }
5922251881Speter
5923251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
5924251881Speter
5925251881Speter  return SVN_NO_ERROR;
5926251881Speter}
5927251881Speter#endif
5928251881Speter
5929251881Speter
5930251881Speterstruct set_changelist_baton_t
5931251881Speter{
5932251881Speter  const char *new_changelist;
5933251881Speter  const apr_array_header_t *changelist_filter;
5934251881Speter  svn_depth_t depth;
5935251881Speter};
5936251881Speter
5937251881Speter
5938251881Speter/* The main part of svn_wc__db_op_set_changelist().
5939251881Speter *
5940251881Speter * Implements svn_wc__db_txn_callback_t. */
5941251881Speterstatic svn_error_t *
5942251881Speterset_changelist_txn(void *baton,
5943251881Speter                   svn_wc__db_wcroot_t *wcroot,
5944251881Speter                   const char *local_relpath,
5945251881Speter                   apr_pool_t *scratch_pool)
5946251881Speter{
5947251881Speter  struct set_changelist_baton_t *scb = baton;
5948251881Speter  svn_sqlite__stmt_t *stmt;
5949251881Speter
5950251881Speter  SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
5951251881Speter                                scb->changelist_filter, scratch_pool));
5952251881Speter
5953251881Speter  /* Ensure we have actual nodes for our targets. */
5954251881Speter  if (scb->new_changelist)
5955251881Speter    {
5956251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5957251881Speter                                        STMT_INSERT_ACTUAL_EMPTIES));
5958251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
5959251881Speter    }
5960251881Speter
5961251881Speter  /* Now create our notification table. */
5962251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5963251881Speter                                      STMT_CREATE_CHANGELIST_LIST));
5964251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5965251881Speter                                      STMT_CREATE_CHANGELIST_TRIGGER));
5966251881Speter
5967251881Speter  /* Update our changelists. */
5968251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5969251881Speter                                    STMT_UPDATE_ACTUAL_CHANGELISTS));
5970251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
5971251881Speter                            scb->new_changelist));
5972251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
5973251881Speter
5974251881Speter  if (scb->new_changelist)
5975251881Speter    {
5976251881Speter      /* We have to notify that we skipped directories, so do that now. */
5977251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5978251881Speter                                        STMT_MARK_SKIPPED_CHANGELIST_DIRS));
5979251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
5980251881Speter                                scb->new_changelist));
5981251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
5982251881Speter    }
5983251881Speter
5984251881Speter  /* We may have left empty ACTUAL nodes, so remove them.  This is only a
5985251881Speter     potential problem if we removed changelists. */
5986251881Speter  if (!scb->new_changelist)
5987251881Speter    {
5988251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5989251881Speter                                        STMT_DELETE_ACTUAL_EMPTIES));
5990251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5991251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
5992251881Speter    }
5993251881Speter
5994251881Speter  return SVN_NO_ERROR;
5995251881Speter}
5996251881Speter
5997251881Speter
5998251881Speter/* Send notifications for svn_wc__db_op_set_changelist().
5999251881Speter *
6000251881Speter * Implements work_callback_t. */
6001251881Speterstatic svn_error_t *
6002251881Speterdo_changelist_notify(void *baton,
6003251881Speter                     svn_wc__db_wcroot_t *wcroot,
6004251881Speter                     svn_cancel_func_t cancel_func,
6005251881Speter                     void *cancel_baton,
6006251881Speter                     svn_wc_notify_func2_t notify_func,
6007251881Speter                     void *notify_baton,
6008251881Speter                     apr_pool_t *scratch_pool)
6009251881Speter{
6010251881Speter  svn_sqlite__stmt_t *stmt;
6011251881Speter  svn_boolean_t have_row;
6012251881Speter  apr_pool_t *iterpool;
6013251881Speter
6014251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6015251881Speter                                    STMT_SELECT_CHANGELIST_LIST));
6016251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6017251881Speter
6018251881Speter  iterpool = svn_pool_create(scratch_pool);
6019251881Speter  while (have_row)
6020251881Speter    {
6021251881Speter      /* ### wc_id is column 0. use it one day...  */
6022251881Speter      const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6023251881Speter      svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6024251881Speter      svn_wc_notify_t *notify;
6025251881Speter      const char *notify_abspath;
6026251881Speter
6027251881Speter      svn_pool_clear(iterpool);
6028251881Speter
6029251881Speter      if (cancel_func)
6030251881Speter        {
6031251881Speter          svn_error_t *err = cancel_func(cancel_baton);
6032251881Speter
6033251881Speter          if (err)
6034251881Speter            return svn_error_trace(svn_error_compose_create(
6035251881Speter                                                    err,
6036251881Speter                                                    svn_sqlite__reset(stmt)));
6037251881Speter        }
6038251881Speter
6039251881Speter      notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6040251881Speter                                       iterpool);
6041251881Speter      notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6042251881Speter      notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6043251881Speter      notify_func(notify_baton, notify, iterpool);
6044251881Speter
6045251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6046251881Speter    }
6047251881Speter  svn_pool_destroy(iterpool);
6048251881Speter
6049251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
6050251881Speter}
6051251881Speter
6052251881Speter
6053251881Spetersvn_error_t *
6054251881Spetersvn_wc__db_op_set_changelist(svn_wc__db_t *db,
6055251881Speter                             const char *local_abspath,
6056251881Speter                             const char *new_changelist,
6057251881Speter                             const apr_array_header_t *changelist_filter,
6058251881Speter                             svn_depth_t depth,
6059251881Speter                             svn_wc_notify_func2_t notify_func,
6060251881Speter                             void *notify_baton,
6061251881Speter                             svn_cancel_func_t cancel_func,
6062251881Speter                             void *cancel_baton,
6063251881Speter                             apr_pool_t *scratch_pool)
6064251881Speter{
6065251881Speter  svn_wc__db_wcroot_t *wcroot;
6066251881Speter  const char *local_relpath;
6067251881Speter  struct set_changelist_baton_t scb;
6068251881Speter
6069251881Speter  scb.new_changelist = new_changelist;
6070251881Speter  scb.changelist_filter = changelist_filter;
6071251881Speter  scb.depth = depth;
6072251881Speter
6073251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6074251881Speter
6075251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6076251881Speter                                                db, local_abspath,
6077251881Speter                                                scratch_pool, scratch_pool));
6078251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6079251881Speter
6080251881Speter  /* Flush the entries before we do the work. Even if no work is performed,
6081251881Speter     the flush isn't a problem. */
6082251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6083251881Speter
6084251881Speter  /* Perform the set-changelist operation (transactionally), perform any
6085251881Speter     notifications necessary, and then clean out our temporary tables.  */
6086251881Speter  return svn_error_trace(with_finalization(wcroot, local_relpath,
6087251881Speter                                           set_changelist_txn, &scb,
6088251881Speter                                           do_changelist_notify, NULL,
6089251881Speter                                           cancel_func, cancel_baton,
6090251881Speter                                           notify_func, notify_baton,
6091251881Speter                                           STMT_FINALIZE_CHANGELIST,
6092251881Speter                                           scratch_pool));
6093251881Speter}
6094251881Speter
6095251881Speter/* Implementation of svn_wc__db_op_mark_conflict() */
6096251881Spetersvn_error_t *
6097251881Spetersvn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6098251881Speter                                  const char *local_relpath,
6099251881Speter                                  const svn_skel_t *conflict_skel,
6100251881Speter                                  apr_pool_t *scratch_pool)
6101251881Speter{
6102251881Speter  svn_sqlite__stmt_t *stmt;
6103251881Speter  svn_boolean_t got_row;
6104251881Speter  svn_boolean_t is_complete;
6105251881Speter
6106251881Speter  SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6107251881Speter  SVN_ERR_ASSERT(is_complete);
6108251881Speter
6109251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6110251881Speter                                    STMT_SELECT_ACTUAL_NODE));
6111251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6112251881Speter  SVN_ERR(svn_sqlite__step(&got_row, stmt));
6113251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6114251881Speter
6115251881Speter  if (got_row)
6116251881Speter    {
6117251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6118251881Speter                                        STMT_UPDATE_ACTUAL_CONFLICT));
6119251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6120251881Speter    }
6121251881Speter  else
6122251881Speter    {
6123251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6124251881Speter                                        STMT_INSERT_ACTUAL_CONFLICT));
6125251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6126251881Speter      if (*local_relpath != '\0')
6127251881Speter        SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6128251881Speter                                      svn_relpath_dirname(local_relpath,
6129251881Speter                                                          scratch_pool)));
6130251881Speter    }
6131251881Speter
6132251881Speter  {
6133251881Speter    svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6134251881Speter
6135251881Speter    SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6136251881Speter  }
6137251881Speter
6138251881Speter  SVN_ERR(svn_sqlite__update(NULL, stmt));
6139251881Speter
6140251881Speter  return SVN_NO_ERROR;
6141251881Speter}
6142251881Speter
6143251881Spetersvn_error_t *
6144251881Spetersvn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6145251881Speter                            const char *local_abspath,
6146251881Speter                            const svn_skel_t *conflict_skel,
6147251881Speter                            const svn_skel_t *work_items,
6148251881Speter                            apr_pool_t *scratch_pool)
6149251881Speter{
6150251881Speter  svn_wc__db_wcroot_t *wcroot;
6151251881Speter  const char *local_relpath;
6152251881Speter
6153251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6154251881Speter
6155251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6156251881Speter                              local_abspath, scratch_pool, scratch_pool));
6157251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6158251881Speter
6159251881Speter  SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6160251881Speter                                            conflict_skel, scratch_pool));
6161251881Speter
6162251881Speter  /* ### Should be handled in the same transaction as setting the conflict */
6163251881Speter  if (work_items)
6164251881Speter    SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6165251881Speter
6166251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6167251881Speter
6168251881Speter  return SVN_NO_ERROR;
6169251881Speter
6170251881Speter}
6171251881Speter
6172251881Speter/* The body of svn_wc__db_op_mark_resolved().
6173251881Speter */
6174251881Speterstatic svn_error_t *
6175251881Speterdb_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6176251881Speter                    const char *local_relpath,
6177251881Speter                    svn_wc__db_t *db,
6178251881Speter                    svn_boolean_t resolved_text,
6179251881Speter                    svn_boolean_t resolved_props,
6180251881Speter                    svn_boolean_t resolved_tree,
6181251881Speter                    const svn_skel_t *work_items,
6182251881Speter                    apr_pool_t *scratch_pool)
6183251881Speter{
6184251881Speter  svn_sqlite__stmt_t *stmt;
6185251881Speter  svn_boolean_t have_row;
6186251881Speter  int total_affected_rows = 0;
6187251881Speter  svn_boolean_t resolved_all;
6188251881Speter  apr_size_t conflict_len;
6189251881Speter  const void *conflict_data;
6190251881Speter  svn_skel_t *conflicts;
6191251881Speter
6192251881Speter  /* Check if we have a conflict in ACTUAL */
6193251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6194251881Speter                                    STMT_SELECT_ACTUAL_NODE));
6195251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6196251881Speter
6197251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6198251881Speter
6199251881Speter  if (! have_row)
6200251881Speter    {
6201251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6202251881Speter
6203251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6204251881Speter                                        STMT_SELECT_NODE_INFO));
6205251881Speter
6206251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6207251881Speter
6208251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6209251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6210251881Speter
6211251881Speter      if (have_row)
6212251881Speter        return SVN_NO_ERROR;
6213251881Speter
6214251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6215251881Speter                               _("The node '%s' was not found."),
6216251881Speter                                   path_for_error_message(wcroot,
6217251881Speter                                                          local_relpath,
6218251881Speter                                                          scratch_pool));
6219251881Speter    }
6220251881Speter
6221251881Speter  conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6222251881Speter                                          scratch_pool);
6223251881Speter  conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6224251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6225251881Speter
6226251881Speter  SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6227251881Speter                                        db, wcroot->abspath,
6228251881Speter                                        resolved_text,
6229251881Speter                                        resolved_props ? "" : NULL,
6230251881Speter                                        resolved_tree,
6231251881Speter                                        scratch_pool, scratch_pool));
6232251881Speter
6233251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6234251881Speter                                    STMT_UPDATE_ACTUAL_CONFLICT));
6235251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6236251881Speter
6237251881Speter  if (! resolved_all)
6238251881Speter    {
6239251881Speter      svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6240251881Speter
6241251881Speter      SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6242251881Speter    }
6243251881Speter
6244251881Speter  SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6245251881Speter
6246251881Speter  /* Now, remove the actual node if it doesn't have any more useful
6247251881Speter     information.  We only need to do this if we've remove data ourselves. */
6248251881Speter  if (total_affected_rows > 0)
6249251881Speter    {
6250251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6251251881Speter                                        STMT_DELETE_ACTUAL_EMPTY));
6252251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6253251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6254251881Speter    }
6255251881Speter
6256251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6257251881Speter
6258251881Speter  return SVN_NO_ERROR;
6259251881Speter}
6260251881Speter
6261251881Spetersvn_error_t *
6262251881Spetersvn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6263251881Speter                            const char *local_abspath,
6264251881Speter                            svn_boolean_t resolved_text,
6265251881Speter                            svn_boolean_t resolved_props,
6266251881Speter                            svn_boolean_t resolved_tree,
6267251881Speter                            const svn_skel_t *work_items,
6268251881Speter                            apr_pool_t *scratch_pool)
6269251881Speter{
6270251881Speter  svn_wc__db_wcroot_t *wcroot;
6271251881Speter  const char *local_relpath;
6272251881Speter
6273251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6274251881Speter
6275251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6276251881Speter                              local_abspath, scratch_pool, scratch_pool));
6277251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6278251881Speter
6279251881Speter  SVN_WC__DB_WITH_TXN(
6280251881Speter    db_op_mark_resolved(wcroot, local_relpath, db,
6281251881Speter                        resolved_text, resolved_props, resolved_tree,
6282251881Speter                        work_items, scratch_pool),
6283251881Speter    wcroot);
6284251881Speter
6285251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6286251881Speter  return SVN_NO_ERROR;
6287251881Speter}
6288251881Speter
6289251881Speter/* Clear moved-to information at the delete-half of the move which
6290251881Speter * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6291251881Speterstatic svn_error_t *
6292251881Speterclear_moved_to(const char *local_relpath,
6293251881Speter               svn_wc__db_wcroot_t *wcroot,
6294251881Speter               apr_pool_t *scratch_pool)
6295251881Speter{
6296251881Speter  svn_sqlite__stmt_t *stmt;
6297251881Speter  svn_boolean_t have_row;
6298251881Speter  const char *moved_from_relpath;
6299251881Speter
6300251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6301251881Speter                                    STMT_SELECT_MOVED_FROM_RELPATH));
6302251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6303251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6304251881Speter  if (!have_row)
6305251881Speter    {
6306251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6307251881Speter      return SVN_NO_ERROR;
6308251881Speter    }
6309251881Speter
6310251881Speter  moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6311251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6312251881Speter
6313251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6314251881Speter                                    STMT_CLEAR_MOVED_TO_RELPATH));
6315251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6316251881Speter                            moved_from_relpath,
6317251881Speter                            relpath_depth(moved_from_relpath)));
6318251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6319251881Speter
6320251881Speter  return SVN_NO_ERROR;
6321251881Speter}
6322251881Speter
6323251881Speter/* One of the two alternative bodies of svn_wc__db_op_revert().
6324251881Speter *
6325251881Speter * Implements svn_wc__db_txn_callback_t. */
6326251881Speterstatic svn_error_t *
6327251881Speterop_revert_txn(void *baton,
6328251881Speter              svn_wc__db_wcroot_t *wcroot,
6329251881Speter              const char *local_relpath,
6330251881Speter              apr_pool_t *scratch_pool)
6331251881Speter{
6332251881Speter  svn_wc__db_t *db = baton;
6333251881Speter  svn_sqlite__stmt_t *stmt;
6334251881Speter  svn_boolean_t have_row;
6335251881Speter  int op_depth;
6336251881Speter  svn_boolean_t moved_here;
6337251881Speter  int affected_rows;
6338251881Speter  const char *moved_to;
6339251881Speter
6340251881Speter  /* ### Similar structure to op_revert_recursive_txn, should they be
6341251881Speter         combined? */
6342251881Speter
6343251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6344251881Speter                                    STMT_SELECT_NODE_INFO));
6345251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6346251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6347251881Speter  if (!have_row)
6348251881Speter    {
6349251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6350251881Speter
6351251881Speter      /* There was no NODE row, so attempt to delete an ACTUAL_NODE row.  */
6352251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6353251881Speter                                        STMT_DELETE_ACTUAL_NODE));
6354251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6355251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6356251881Speter      if (affected_rows)
6357251881Speter        {
6358251881Speter          /* Can't do non-recursive actual-only revert if actual-only
6359251881Speter             children exist. Raise an error to cancel the transaction.  */
6360251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6361251881Speter                                            STMT_ACTUAL_HAS_CHILDREN));
6362251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6363251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
6364251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
6365251881Speter          if (have_row)
6366251881Speter            return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6367251881Speter                                     _("Can't revert '%s' without"
6368251881Speter                                       " reverting children"),
6369251881Speter                                     path_for_error_message(wcroot,
6370251881Speter                                                            local_relpath,
6371251881Speter                                                            scratch_pool));
6372251881Speter          return SVN_NO_ERROR;
6373251881Speter        }
6374251881Speter
6375251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6376251881Speter                               _("The node '%s' was not found."),
6377251881Speter                               path_for_error_message(wcroot,
6378251881Speter                                                      local_relpath,
6379251881Speter                                                      scratch_pool));
6380251881Speter    }
6381251881Speter
6382251881Speter  op_depth = svn_sqlite__column_int(stmt, 0);
6383251881Speter  moved_here = svn_sqlite__column_boolean(stmt, 15);
6384251881Speter  moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6385251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6386251881Speter
6387251881Speter  if (moved_to)
6388251881Speter    {
6389251881Speter      SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6390251881Speter                                                           local_relpath,
6391251881Speter                                                           scratch_pool));
6392251881Speter    }
6393251881Speter  else
6394251881Speter    {
6395251881Speter      svn_skel_t *conflict;
6396251881Speter
6397251881Speter      SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6398251881Speter                                                local_relpath,
6399251881Speter                                                scratch_pool, scratch_pool));
6400251881Speter      if (conflict)
6401251881Speter        {
6402251881Speter          svn_wc_operation_t operation;
6403251881Speter          svn_boolean_t tree_conflicted;
6404251881Speter
6405251881Speter          SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6406251881Speter                                             &tree_conflicted,
6407251881Speter                                             db, wcroot->abspath,
6408251881Speter                                             conflict,
6409251881Speter                                             scratch_pool, scratch_pool));
6410251881Speter          if (tree_conflicted
6411251881Speter              && (operation == svn_wc_operation_update
6412251881Speter                  || operation == svn_wc_operation_switch))
6413251881Speter            {
6414251881Speter              svn_wc_conflict_reason_t reason;
6415251881Speter              svn_wc_conflict_action_t action;
6416251881Speter
6417251881Speter              SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6418251881Speter                                                          NULL,
6419251881Speter                                                          db, wcroot->abspath,
6420251881Speter                                                          conflict,
6421251881Speter                                                          scratch_pool,
6422251881Speter                                                          scratch_pool));
6423251881Speter
6424251881Speter              if (reason == svn_wc_conflict_reason_deleted)
6425251881Speter                SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6426251881Speter                          db, svn_dirent_join(wcroot->abspath, local_relpath,
6427251881Speter                                              scratch_pool),
6428251881Speter                          NULL, NULL /* ### How do we notify this? */,
6429251881Speter                          scratch_pool));
6430251881Speter            }
6431251881Speter        }
6432251881Speter    }
6433251881Speter
6434251881Speter  if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6435251881Speter    {
6436251881Speter      /* Can't do non-recursive revert if children exist */
6437251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6438251881Speter                                        STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6439251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6440251881Speter                                local_relpath, op_depth));
6441251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6442251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6443251881Speter      if (have_row)
6444251881Speter        return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6445251881Speter                                 _("Can't revert '%s' without"
6446251881Speter                                   " reverting children"),
6447251881Speter                                 path_for_error_message(wcroot,
6448251881Speter                                                        local_relpath,
6449251881Speter                                                        scratch_pool));
6450251881Speter
6451251881Speter      /* Rewrite the op-depth of all deleted children making the
6452251881Speter         direct children into roots of deletes. */
6453251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6454251881Speter                                     STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6455251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6456251881Speter                                local_relpath,
6457251881Speter                                op_depth));
6458251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6459251881Speter
6460251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6461251881Speter                                        STMT_DELETE_WORKING_NODE));
6462251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6463251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6464251881Speter
6465251881Speter      /* ### This removes the lock, but what about the access baton? */
6466251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467251881Speter                                        STMT_DELETE_WC_LOCK_ORPHAN));
6468251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6470251881Speter
6471251881Speter      /* If this node was moved-here, clear moved-to at the move source. */
6472251881Speter      if (moved_here)
6473251881Speter        SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6474251881Speter    }
6475251881Speter
6476251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6477251881Speter                                  STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6478251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6479251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6480251881Speter  if (!affected_rows)
6481251881Speter    {
6482251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6483251881Speter                                    STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6484251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6485251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6486251881Speter    }
6487251881Speter
6488251881Speter  return SVN_NO_ERROR;
6489251881Speter}
6490251881Speter
6491251881Speter
6492251881Speter/* One of the two alternative bodies of svn_wc__db_op_revert().
6493251881Speter *
6494251881Speter * Implements svn_wc__db_txn_callback_t. */
6495251881Speterstatic svn_error_t *
6496251881Speterop_revert_recursive_txn(void *baton,
6497251881Speter                        svn_wc__db_wcroot_t *wcroot,
6498251881Speter                        const char *local_relpath,
6499251881Speter                        apr_pool_t *scratch_pool)
6500251881Speter{
6501251881Speter  svn_sqlite__stmt_t *stmt;
6502251881Speter  svn_boolean_t have_row;
6503251881Speter  int op_depth;
6504251881Speter  int select_op_depth;
6505251881Speter  svn_boolean_t moved_here;
6506251881Speter  int affected_rows;
6507251881Speter  apr_pool_t *iterpool;
6508251881Speter
6509251881Speter  /* ### Similar structure to op_revert_txn, should they be
6510251881Speter         combined? */
6511251881Speter
6512251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6513251881Speter                                    STMT_SELECT_NODE_INFO));
6514251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6515251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6516251881Speter  if (!have_row)
6517251881Speter    {
6518251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
6519251881Speter
6520251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6521251881Speter                                        STMT_DELETE_ACTUAL_NODE));
6522251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6523251881Speter                                local_relpath));
6524251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6525251881Speter
6526251881Speter      if (affected_rows)
6527251881Speter        return SVN_NO_ERROR;  /* actual-only revert */
6528251881Speter
6529251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6530251881Speter                               _("The node '%s' was not found."),
6531251881Speter                               path_for_error_message(wcroot,
6532251881Speter                                                      local_relpath,
6533251881Speter                                                      scratch_pool));
6534251881Speter    }
6535251881Speter
6536251881Speter  op_depth = svn_sqlite__column_int(stmt, 0);
6537251881Speter  moved_here = svn_sqlite__column_boolean(stmt, 15);
6538251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6539251881Speter
6540251881Speter  if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6541251881Speter    return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6542251881Speter                             _("Can't revert '%s' without"
6543251881Speter                               " reverting parent"),
6544251881Speter                             path_for_error_message(wcroot,
6545251881Speter                                                    local_relpath,
6546251881Speter                                                    scratch_pool));
6547251881Speter
6548251881Speter  /* Remove moved-here from move destinations outside the tree. */
6549251881Speter  SVN_ERR(svn_sqlite__get_statement(
6550251881Speter                    &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6551251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6552251881Speter                            op_depth));
6553251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6554251881Speter  while (have_row)
6555251881Speter    {
6556251881Speter      const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6557251881Speter      svn_error_t *err;
6558251881Speter
6559251881Speter      err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6560251881Speter                                                         move_src_relpath,
6561251881Speter                                                         scratch_pool);
6562251881Speter      if (err)
6563251881Speter        return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6564251881Speter
6565251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6566251881Speter    }
6567251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6568251881Speter
6569251881Speter  /* Don't delete BASE nodes */
6570251881Speter  select_op_depth = op_depth ? op_depth : 1;
6571251881Speter
6572251881Speter  /* Reverting any non wc-root node */
6573251881Speter  SVN_ERR(svn_sqlite__get_statement(
6574251881Speter                    &stmt, wcroot->sdb,
6575251881Speter                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6576251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6577251881Speter                            local_relpath, select_op_depth));
6578251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6579251881Speter
6580251881Speter  SVN_ERR(svn_sqlite__get_statement(
6581251881Speter                    &stmt, wcroot->sdb,
6582251881Speter                    STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6583251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6584251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6585251881Speter
6586251881Speter  SVN_ERR(svn_sqlite__get_statement(
6587251881Speter                    &stmt, wcroot->sdb,
6588251881Speter                    STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6589251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6590251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6591251881Speter
6592251881Speter  /* ### This removes the locks, but what about the access batons? */
6593251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6594251881Speter                                    STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6595251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6596251881Speter                            local_relpath));
6597251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6598251881Speter
6599251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6600251881Speter                                    STMT_SELECT_MOVED_HERE_CHILDREN));
6601251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6602251881Speter
6603251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6604251881Speter
6605251881Speter  iterpool = svn_pool_create(scratch_pool);
6606251881Speter  while (have_row)
6607251881Speter    {
6608251881Speter      const char *moved_here_child_relpath;
6609251881Speter      svn_error_t *err;
6610251881Speter
6611251881Speter      svn_pool_clear(iterpool);
6612251881Speter
6613251881Speter      moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6614251881Speter      err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6615251881Speter      if (err)
6616251881Speter        return svn_error_trace(svn_error_compose_create(
6617251881Speter                                        err,
6618251881Speter                                        svn_sqlite__reset(stmt)));
6619251881Speter
6620251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6621251881Speter    }
6622251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6623251881Speter  svn_pool_destroy(iterpool);
6624251881Speter
6625251881Speter  /* Clear potential moved-to pointing at the target node itself. */
6626251881Speter  if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6627251881Speter      && moved_here)
6628251881Speter    SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6629251881Speter
6630251881Speter  return SVN_NO_ERROR;
6631251881Speter}
6632251881Speter
6633251881Spetersvn_error_t *
6634251881Spetersvn_wc__db_op_revert(svn_wc__db_t *db,
6635251881Speter                     const char *local_abspath,
6636251881Speter                     svn_depth_t depth,
6637251881Speter                     apr_pool_t *result_pool,
6638251881Speter                     apr_pool_t *scratch_pool)
6639251881Speter{
6640251881Speter  svn_wc__db_wcroot_t *wcroot;
6641251881Speter  const char *local_relpath;
6642251881Speter  struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6643251881Speter                                       STMT_DROP_REVERT_LIST_TRIGGERS,
6644251881Speter                                       NULL, NULL};
6645251881Speter
6646251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6647251881Speter
6648251881Speter  switch (depth)
6649251881Speter    {
6650251881Speter    case svn_depth_empty:
6651251881Speter      wtb.cb_func = op_revert_txn;
6652251881Speter      wtb.cb_baton = db;
6653251881Speter      break;
6654251881Speter    case svn_depth_infinity:
6655251881Speter      wtb.cb_func = op_revert_recursive_txn;
6656251881Speter      break;
6657251881Speter    default:
6658251881Speter      return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6659251881Speter                               _("Unsupported depth for revert of '%s'"),
6660251881Speter                               svn_dirent_local_style(local_abspath,
6661251881Speter                                                      scratch_pool));
6662251881Speter    }
6663251881Speter
6664251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6665251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6666251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6667251881Speter
6668251881Speter  SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6669251881Speter                      wcroot);
6670251881Speter
6671251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6672251881Speter
6673251881Speter  return SVN_NO_ERROR;
6674251881Speter}
6675251881Speter
6676251881Speter/* The body of svn_wc__db_revert_list_read().
6677251881Speter */
6678251881Speterstatic svn_error_t *
6679251881Speterrevert_list_read(svn_boolean_t *reverted,
6680251881Speter                 const apr_array_header_t **marker_paths,
6681251881Speter                 svn_boolean_t *copied_here,
6682251881Speter                 svn_node_kind_t *kind,
6683251881Speter                 svn_wc__db_wcroot_t *wcroot,
6684251881Speter                 const char *local_relpath,
6685251881Speter                 svn_wc__db_t *db,
6686251881Speter                 apr_pool_t *result_pool,
6687251881Speter                 apr_pool_t *scratch_pool)
6688251881Speter{
6689251881Speter  svn_sqlite__stmt_t *stmt;
6690251881Speter  svn_boolean_t have_row;
6691251881Speter
6692251881Speter  *reverted = FALSE;
6693251881Speter  *marker_paths = NULL;
6694251881Speter  *copied_here = FALSE;
6695251881Speter  *kind = svn_node_unknown;
6696251881Speter
6697251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6698251881Speter                                    STMT_SELECT_REVERT_LIST));
6699251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6700251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6701251881Speter  if (have_row)
6702251881Speter    {
6703251881Speter      svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6704251881Speter      svn_boolean_t another_row = FALSE;
6705251881Speter
6706251881Speter      if (is_actual)
6707251881Speter        {
6708251881Speter          apr_size_t conflict_len;
6709251881Speter          const void *conflict_data;
6710251881Speter
6711251881Speter          conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6712251881Speter                                                  scratch_pool);
6713251881Speter          if (conflict_data)
6714251881Speter            {
6715251881Speter              svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6716251881Speter                                                      conflict_len,
6717251881Speter                                                      scratch_pool);
6718251881Speter
6719251881Speter              SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6720251881Speter                                                    db, wcroot->abspath,
6721251881Speter                                                    conflicts,
6722251881Speter                                                    result_pool,
6723251881Speter                                                    scratch_pool));
6724251881Speter            }
6725251881Speter
6726251881Speter          if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6727251881Speter            *reverted = TRUE;
6728251881Speter
6729251881Speter          SVN_ERR(svn_sqlite__step(&another_row, stmt));
6730251881Speter        }
6731251881Speter
6732251881Speter      if (!is_actual || another_row)
6733251881Speter        {
6734251881Speter          *reverted = TRUE;
6735251881Speter          if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6736251881Speter            {
6737251881Speter              int op_depth = svn_sqlite__column_int(stmt, 3);
6738251881Speter              *copied_here = (op_depth == relpath_depth(local_relpath));
6739251881Speter            }
6740251881Speter          *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6741251881Speter        }
6742251881Speter
6743251881Speter    }
6744251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6745251881Speter
6746251881Speter  if (have_row)
6747251881Speter    {
6748251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6749251881Speter                                        STMT_DELETE_REVERT_LIST));
6750251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6751251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
6752251881Speter    }
6753251881Speter
6754251881Speter  return SVN_NO_ERROR;
6755251881Speter}
6756251881Speter
6757251881Spetersvn_error_t *
6758251881Spetersvn_wc__db_revert_list_read(svn_boolean_t *reverted,
6759251881Speter                            const apr_array_header_t **marker_files,
6760251881Speter                            svn_boolean_t *copied_here,
6761251881Speter                            svn_node_kind_t *kind,
6762251881Speter                            svn_wc__db_t *db,
6763251881Speter                            const char *local_abspath,
6764251881Speter                            apr_pool_t *result_pool,
6765251881Speter                            apr_pool_t *scratch_pool)
6766251881Speter{
6767251881Speter  svn_wc__db_wcroot_t *wcroot;
6768251881Speter  const char *local_relpath;
6769251881Speter
6770251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6771251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6772251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6773251881Speter
6774251881Speter  SVN_WC__DB_WITH_TXN(
6775251881Speter    revert_list_read(reverted, marker_files, copied_here, kind,
6776251881Speter                     wcroot, local_relpath, db,
6777251881Speter                     result_pool, scratch_pool),
6778251881Speter    wcroot);
6779251881Speter  return SVN_NO_ERROR;
6780251881Speter}
6781251881Speter
6782251881Speter
6783251881Speter/* The body of svn_wc__db_revert_list_read_copied_children().
6784251881Speter */
6785251881Speterstatic svn_error_t *
6786251881Speterrevert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6787251881Speter                                 const char *local_relpath,
6788251881Speter                                 const apr_array_header_t **children_p,
6789251881Speter                                 apr_pool_t *result_pool,
6790251881Speter                                 apr_pool_t *scratch_pool)
6791251881Speter{
6792251881Speter  svn_sqlite__stmt_t *stmt;
6793251881Speter  svn_boolean_t have_row;
6794251881Speter  apr_array_header_t *children;
6795251881Speter
6796251881Speter  children =
6797251881Speter    apr_array_make(result_pool, 0,
6798251881Speter                  sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6799251881Speter
6800251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6801251881Speter                                    STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6802251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6803251881Speter                            local_relpath, relpath_depth(local_relpath)));
6804251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6805251881Speter  while (have_row)
6806251881Speter    {
6807251881Speter      svn_wc__db_revert_list_copied_child_info_t *child_info;
6808251881Speter      const char *child_relpath;
6809251881Speter
6810251881Speter      child_info = apr_palloc(result_pool, sizeof(*child_info));
6811251881Speter
6812251881Speter      child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6813251881Speter      child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6814251881Speter                                            result_pool);
6815251881Speter      child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6816251881Speter      APR_ARRAY_PUSH(
6817251881Speter        children,
6818251881Speter        svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6819251881Speter
6820251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6821251881Speter    }
6822251881Speter   SVN_ERR(svn_sqlite__reset(stmt));
6823251881Speter
6824251881Speter  *children_p = children;
6825251881Speter
6826251881Speter  return SVN_NO_ERROR;
6827251881Speter}
6828251881Speter
6829251881Speter
6830251881Spetersvn_error_t *
6831251881Spetersvn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6832251881Speter                                            svn_wc__db_t *db,
6833251881Speter                                            const char *local_abspath,
6834251881Speter                                            apr_pool_t *result_pool,
6835251881Speter                                            apr_pool_t *scratch_pool)
6836251881Speter{
6837251881Speter  svn_wc__db_wcroot_t *wcroot;
6838251881Speter  const char *local_relpath;
6839251881Speter
6840251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6841251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6842251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6843251881Speter
6844251881Speter  SVN_WC__DB_WITH_TXN(
6845251881Speter    revert_list_read_copied_children(wcroot, local_relpath, children,
6846251881Speter                                     result_pool, scratch_pool),
6847251881Speter    wcroot);
6848251881Speter  return SVN_NO_ERROR;
6849251881Speter}
6850251881Speter
6851251881Speter
6852251881Spetersvn_error_t *
6853251881Spetersvn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6854251881Speter                              void *notify_baton,
6855251881Speter                              svn_wc__db_t *db,
6856251881Speter                              const char *local_abspath,
6857251881Speter                              apr_pool_t *scratch_pool)
6858251881Speter{
6859251881Speter  svn_wc__db_wcroot_t *wcroot;
6860251881Speter  const char *local_relpath;
6861251881Speter  svn_sqlite__stmt_t *stmt;
6862251881Speter  svn_boolean_t have_row;
6863251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6864251881Speter
6865251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6866251881Speter                              db, local_abspath, scratch_pool, iterpool));
6867251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6868251881Speter
6869251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6870251881Speter                                    STMT_SELECT_REVERT_LIST_RECURSIVE));
6871251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6872251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
6873251881Speter  if (!have_row)
6874251881Speter    return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
6875251881Speter  while (have_row)
6876251881Speter    {
6877251881Speter      const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6878251881Speter
6879251881Speter      svn_pool_clear(iterpool);
6880251881Speter
6881251881Speter      notify_func(notify_baton,
6882251881Speter                  svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
6883251881Speter                                                       notify_relpath,
6884251881Speter                                                       iterpool),
6885251881Speter                                       svn_wc_notify_revert,
6886251881Speter                                       iterpool),
6887251881Speter                  iterpool);
6888251881Speter
6889251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6890251881Speter    }
6891251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
6892251881Speter
6893251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6894251881Speter                                    STMT_DELETE_REVERT_LIST_RECURSIVE));
6895251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6896251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
6897251881Speter
6898251881Speter  svn_pool_destroy(iterpool);
6899251881Speter
6900251881Speter  return SVN_NO_ERROR;
6901251881Speter}
6902251881Speter
6903251881Spetersvn_error_t *
6904251881Spetersvn_wc__db_revert_list_done(svn_wc__db_t *db,
6905251881Speter                            const char *local_abspath,
6906251881Speter                            apr_pool_t *scratch_pool)
6907251881Speter{
6908251881Speter  svn_wc__db_wcroot_t *wcroot;
6909251881Speter  const char *local_relpath;
6910251881Speter
6911251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6912251881Speter                              db, local_abspath, scratch_pool, scratch_pool));
6913251881Speter  VERIFY_USABLE_WCROOT(wcroot);
6914251881Speter
6915251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
6916251881Speter
6917251881Speter  return SVN_NO_ERROR;
6918251881Speter}
6919251881Speter
6920251881Speter/* The body of svn_wc__db_op_remove_node().
6921251881Speter */
6922251881Speterstatic svn_error_t *
6923251881Speterremove_node_txn(svn_boolean_t *left_changes,
6924251881Speter                svn_wc__db_wcroot_t *wcroot,
6925251881Speter                const char *local_relpath,
6926251881Speter                svn_wc__db_t *db,
6927251881Speter                svn_boolean_t destroy_wc,
6928251881Speter                svn_boolean_t destroy_changes,
6929251881Speter                svn_revnum_t not_present_rev,
6930251881Speter                svn_wc__db_status_t not_present_status,
6931251881Speter                svn_node_kind_t not_present_kind,
6932251881Speter                const svn_skel_t *conflict,
6933251881Speter                const svn_skel_t *work_items,
6934251881Speter                svn_cancel_func_t cancel_func,
6935251881Speter                void *cancel_baton,
6936251881Speter                apr_pool_t *scratch_pool)
6937251881Speter{
6938251881Speter  svn_sqlite__stmt_t *stmt;
6939251881Speter
6940251881Speter  apr_int64_t repos_id;
6941251881Speter  const char *repos_relpath;
6942251881Speter
6943251881Speter  /* Note that unlike many similar functions it is a valid scenario for this
6944251881Speter     function to be called on a wcroot! */
6945251881Speter
6946251881Speter   /* db set when destroying wc */
6947251881Speter  SVN_ERR_ASSERT(!destroy_wc || db != NULL);
6948251881Speter
6949251881Speter  if (left_changes)
6950251881Speter    *left_changes = FALSE;
6951251881Speter
6952251881Speter  /* Need info for not_present node? */
6953251881Speter  if (SVN_IS_VALID_REVNUM(not_present_rev))
6954251881Speter    SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
6955251881Speter                                              &repos_relpath, &repos_id,
6956251881Speter                                              NULL, NULL, NULL, NULL, NULL,
6957251881Speter                                              NULL, NULL, NULL, NULL, NULL,
6958251881Speter                                              wcroot, local_relpath,
6959251881Speter                                              scratch_pool, scratch_pool));
6960251881Speter
6961251881Speter  if (destroy_wc
6962251881Speter      && (!destroy_changes || *local_relpath == '\0'))
6963251881Speter    {
6964251881Speter      svn_boolean_t have_row;
6965251881Speter      apr_pool_t *iterpool;
6966251881Speter      svn_error_t *err = NULL;
6967251881Speter
6968251881Speter      /* Install WQ items for deleting the unmodified files and all dirs */
6969251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6970251881Speter                                        STMT_SELECT_WORKING_PRESENT));
6971251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is",
6972251881Speter                                wcroot->wc_id, local_relpath));
6973251881Speter
6974251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
6975251881Speter
6976251881Speter      iterpool = svn_pool_create(scratch_pool);
6977251881Speter
6978251881Speter      while (have_row)
6979251881Speter        {
6980251881Speter          const char *child_relpath;
6981251881Speter          const char *child_abspath;
6982251881Speter          svn_node_kind_t child_kind;
6983251881Speter          svn_boolean_t have_checksum;
6984251881Speter          svn_filesize_t recorded_size;
6985251881Speter          apr_int64_t recorded_time;
6986251881Speter          const svn_io_dirent2_t *dirent;
6987251881Speter          svn_boolean_t modified_p = TRUE;
6988251881Speter          svn_skel_t *work_item = NULL;
6989251881Speter
6990251881Speter          svn_pool_clear(iterpool);
6991251881Speter
6992251881Speter          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6993251881Speter          child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
6994251881Speter
6995251881Speter          child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6996251881Speter                                          iterpool);
6997251881Speter
6998251881Speter          if (child_kind == svn_node_file)
6999251881Speter            {
7000251881Speter              have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7001251881Speter              recorded_size = get_recorded_size(stmt, 3);
7002251881Speter              recorded_time = svn_sqlite__column_int64(stmt, 4);
7003251881Speter            }
7004251881Speter
7005251881Speter          if (cancel_func)
7006251881Speter            err = cancel_func(cancel_baton);
7007251881Speter
7008251881Speter          if (err)
7009251881Speter            break;
7010251881Speter
7011251881Speter          err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7012251881Speter                                    iterpool, iterpool);
7013251881Speter
7014251881Speter          if (err)
7015251881Speter            break;
7016251881Speter
7017251881Speter          if (destroy_changes
7018251881Speter              || dirent->kind != svn_node_file
7019251881Speter              || child_kind != svn_node_file)
7020251881Speter            {
7021251881Speter              /* Not interested in keeping changes */
7022251881Speter              modified_p = FALSE;
7023251881Speter            }
7024251881Speter          else if (child_kind == svn_node_file
7025251881Speter                   && dirent->kind == svn_node_file
7026251881Speter                   && dirent->filesize == recorded_size
7027251881Speter                   && dirent->mtime == recorded_time)
7028251881Speter            {
7029251881Speter              modified_p = FALSE; /* File matches recorded state */
7030251881Speter            }
7031251881Speter          else if (have_checksum)
7032251881Speter            err = svn_wc__internal_file_modified_p(&modified_p,
7033251881Speter                                                   db, child_abspath,
7034251881Speter                                                   FALSE, iterpool);
7035251881Speter
7036251881Speter          if (err)
7037251881Speter            break;
7038251881Speter
7039251881Speter          if (modified_p)
7040251881Speter            {
7041251881Speter              if (left_changes)
7042251881Speter                *left_changes = TRUE;
7043251881Speter            }
7044251881Speter          else if (child_kind == svn_node_dir)
7045251881Speter            {
7046251881Speter              err = svn_wc__wq_build_dir_remove(&work_item,
7047251881Speter                                                db, wcroot->abspath,
7048251881Speter                                                child_abspath, FALSE,
7049251881Speter                                                iterpool, iterpool);
7050251881Speter            }
7051251881Speter          else /* svn_node_file || svn_node_symlink */
7052251881Speter            {
7053251881Speter              err = svn_wc__wq_build_file_remove(&work_item,
7054251881Speter                                                 db, wcroot->abspath,
7055251881Speter                                                 child_abspath,
7056251881Speter                                                 iterpool, iterpool);
7057251881Speter            }
7058251881Speter
7059251881Speter          if (err)
7060251881Speter            break;
7061251881Speter
7062251881Speter          if (work_item)
7063251881Speter            {
7064251881Speter              err = add_work_items(wcroot->sdb, work_item, iterpool);
7065251881Speter              if (err)
7066251881Speter                break;
7067251881Speter            }
7068251881Speter
7069251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7070251881Speter        }
7071251881Speter      svn_pool_destroy(iterpool);
7072251881Speter
7073251881Speter      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7074251881Speter    }
7075251881Speter
7076251881Speter  if (destroy_wc && *local_relpath != '\0')
7077251881Speter    {
7078251881Speter      /* Create work item for destroying the root */
7079251881Speter      svn_wc__db_status_t status;
7080251881Speter      svn_node_kind_t kind;
7081251881Speter      SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7082251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7083251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7084251881Speter                        wcroot, local_relpath,
7085251881Speter                        scratch_pool, scratch_pool));
7086251881Speter
7087251881Speter      if (status == svn_wc__db_status_normal
7088251881Speter          || status == svn_wc__db_status_added
7089251881Speter          || status == svn_wc__db_status_incomplete)
7090251881Speter        {
7091251881Speter          svn_skel_t *work_item = NULL;
7092251881Speter          const char *local_abspath = svn_dirent_join(wcroot->abspath,
7093251881Speter                                                          local_relpath,
7094251881Speter                                                          scratch_pool);
7095251881Speter
7096251881Speter          if (kind == svn_node_dir)
7097251881Speter            {
7098251881Speter              SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7099251881Speter                                                  db, wcroot->abspath,
7100251881Speter                                                  local_abspath,
7101251881Speter                                                  destroy_changes
7102251881Speter                                                      /* recursive */,
7103251881Speter                                                  scratch_pool, scratch_pool));
7104251881Speter            }
7105251881Speter          else
7106251881Speter            {
7107251881Speter              svn_boolean_t modified_p = FALSE;
7108251881Speter
7109251881Speter              if (!destroy_changes)
7110251881Speter                {
7111251881Speter                  SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7112251881Speter                                                           db, local_abspath,
7113251881Speter                                                           FALSE,
7114251881Speter                                                           scratch_pool));
7115251881Speter                }
7116251881Speter
7117251881Speter              if (!modified_p)
7118251881Speter                SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7119251881Speter                                                     db, wcroot->abspath,
7120251881Speter                                                     local_abspath,
7121251881Speter                                                     scratch_pool,
7122251881Speter                                                     scratch_pool));
7123251881Speter              else
7124251881Speter                {
7125251881Speter                  if (left_changes)
7126251881Speter                    *left_changes = TRUE;
7127251881Speter                }
7128251881Speter            }
7129251881Speter
7130251881Speter          SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7131251881Speter        }
7132251881Speter    }
7133251881Speter
7134251881Speter  /* Remove all nodes below local_relpath */
7135251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7136251881Speter                                    STMT_DELETE_NODE_RECURSIVE));
7137251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7138251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7139251881Speter
7140251881Speter  /* Delete the root NODE when this is not the working copy root */
7141251881Speter  if (local_relpath[0] != '\0')
7142251881Speter    {
7143251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7144251881Speter                                        STMT_DELETE_NODE));
7145251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7146251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
7147251881Speter    }
7148251881Speter
7149251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7150251881Speter                                    STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7151251881Speter
7152251881Speter  /* Delete all actual nodes at or below local_relpath */
7153251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7154251881Speter                                         local_relpath));
7155251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7156251881Speter
7157251881Speter  /* Should we leave a not-present node? */
7158251881Speter  if (SVN_IS_VALID_REVNUM(not_present_rev))
7159251881Speter    {
7160251881Speter      insert_base_baton_t ibb;
7161251881Speter      blank_ibb(&ibb);
7162251881Speter
7163251881Speter      ibb.repos_id = repos_id;
7164251881Speter
7165251881Speter      SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7166251881Speter                     || not_present_status == svn_wc__db_status_excluded);
7167251881Speter
7168251881Speter      ibb.status = not_present_status;
7169251881Speter      ibb.kind = not_present_kind;
7170251881Speter
7171251881Speter      ibb.repos_relpath = repos_relpath;
7172251881Speter      ibb.revision = not_present_rev;
7173251881Speter
7174251881Speter      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7175251881Speter    }
7176251881Speter
7177251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7178251881Speter  if (conflict)
7179251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7180251881Speter                                              conflict, scratch_pool));
7181251881Speter
7182251881Speter  return SVN_NO_ERROR;
7183251881Speter}
7184251881Speter
7185251881Spetersvn_error_t *
7186251881Spetersvn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7187251881Speter                          svn_wc__db_t *db,
7188251881Speter                          const char *local_abspath,
7189251881Speter                          svn_boolean_t destroy_wc,
7190251881Speter                          svn_boolean_t destroy_changes,
7191251881Speter                          svn_revnum_t not_present_revision,
7192251881Speter                          svn_wc__db_status_t not_present_status,
7193251881Speter                          svn_node_kind_t not_present_kind,
7194251881Speter                          const svn_skel_t *conflict,
7195251881Speter                          const svn_skel_t *work_items,
7196251881Speter                          svn_cancel_func_t cancel_func,
7197251881Speter                          void *cancel_baton,
7198251881Speter                          apr_pool_t *scratch_pool)
7199251881Speter{
7200251881Speter  svn_wc__db_wcroot_t *wcroot;
7201251881Speter  const char *local_relpath;
7202251881Speter
7203251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7204251881Speter
7205251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7206251881Speter                              local_abspath, scratch_pool, scratch_pool));
7207251881Speter  VERIFY_USABLE_WCROOT(wcroot);
7208251881Speter
7209251881Speter  SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7210251881Speter                                      wcroot, local_relpath, db,
7211251881Speter                                      destroy_wc, destroy_changes,
7212251881Speter                                      not_present_revision, not_present_status,
7213251881Speter                                      not_present_kind, conflict, work_items,
7214251881Speter                                      cancel_func, cancel_baton, scratch_pool),
7215251881Speter                      wcroot);
7216251881Speter
7217251881Speter  /* Flush everything below this node in all ways */
7218251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7219251881Speter                        scratch_pool));
7220251881Speter
7221251881Speter  return SVN_NO_ERROR;
7222251881Speter}
7223251881Speter
7224251881Speter
7225251881Speter/* The body of svn_wc__db_op_set_base_depth().
7226251881Speter */
7227251881Speterstatic svn_error_t *
7228251881Speterdb_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7229251881Speter                     const char *local_relpath,
7230251881Speter                     svn_depth_t depth,
7231251881Speter                     apr_pool_t *scratch_pool)
7232251881Speter{
7233251881Speter  svn_sqlite__stmt_t *stmt;
7234251881Speter  int affected_rows;
7235251881Speter
7236251881Speter  /* Flush any entries before we start monkeying the database.  */
7237251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7238251881Speter                                    STMT_UPDATE_NODE_BASE_DEPTH));
7239251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7240251881Speter                            svn_token__to_word(depth_map, depth)));
7241251881Speter  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7242251881Speter
7243251881Speter  if (affected_rows == 0)
7244251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7245251881Speter                             "The node '%s' is not a committed directory",
7246251881Speter                             path_for_error_message(wcroot, local_relpath,
7247251881Speter                                                    scratch_pool));
7248251881Speter
7249251881Speter  return SVN_NO_ERROR;
7250251881Speter}
7251251881Speter
7252251881Speter
7253251881Spetersvn_error_t *
7254251881Spetersvn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7255251881Speter                             const char *local_abspath,
7256251881Speter                             svn_depth_t depth,
7257251881Speter                             apr_pool_t *scratch_pool)
7258251881Speter{
7259251881Speter  svn_wc__db_wcroot_t *wcroot;
7260251881Speter  const char *local_relpath;
7261251881Speter
7262251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7263251881Speter  SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7264251881Speter
7265251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7266251881Speter                              local_abspath, scratch_pool, scratch_pool));
7267251881Speter  VERIFY_USABLE_WCROOT(wcroot);
7268251881Speter
7269251881Speter  /* ### We set depth on working and base to match entry behavior.
7270251881Speter         Maybe these should be separated later? */
7271251881Speter  SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7272251881Speter                                           scratch_pool),
7273251881Speter                      wcroot);
7274251881Speter
7275251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7276251881Speter
7277251881Speter  return SVN_NO_ERROR;
7278251881Speter}
7279251881Speter
7280251881Speter
7281251881Speterstatic svn_error_t *
7282251881Speterinfo_below_working(svn_boolean_t *have_base,
7283251881Speter                   svn_boolean_t *have_work,
7284251881Speter                   svn_wc__db_status_t *status,
7285251881Speter                   svn_wc__db_wcroot_t *wcroot,
7286251881Speter                   const char *local_relpath,
7287251881Speter                   int below_op_depth, /* < 0 is ignored */
7288251881Speter                   apr_pool_t *scratch_pool);
7289251881Speter
7290251881Speter
7291251881Speter/* Convert STATUS, the raw status obtained from the presence map, to
7292251881Speter   the status appropriate for a working (op_depth > 0) node and return
7293251881Speter   it in *WORKING_STATUS. */
7294251881Speterstatic svn_error_t *
7295251881Speterconvert_to_working_status(svn_wc__db_status_t *working_status,
7296251881Speter                          svn_wc__db_status_t status)
7297251881Speter{
7298251881Speter  svn_wc__db_status_t work_status = status;
7299251881Speter
7300251881Speter  SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7301251881Speter                 || work_status == svn_wc__db_status_not_present
7302251881Speter                 || work_status == svn_wc__db_status_base_deleted
7303251881Speter                 || work_status == svn_wc__db_status_incomplete
7304251881Speter                 || work_status == svn_wc__db_status_excluded);
7305251881Speter
7306251881Speter  if (work_status == svn_wc__db_status_excluded)
7307251881Speter    {
7308251881Speter      *working_status = svn_wc__db_status_excluded;
7309251881Speter    }
7310251881Speter  else if (work_status == svn_wc__db_status_not_present
7311251881Speter           || work_status == svn_wc__db_status_base_deleted)
7312251881Speter    {
7313251881Speter      /* The caller should scan upwards to detect whether this
7314251881Speter         deletion has occurred because this node has been moved
7315251881Speter         away, or it is a regular deletion. Also note that the
7316251881Speter         deletion could be of the BASE tree, or a child of
7317251881Speter         something that has been copied/moved here. */
7318251881Speter
7319251881Speter      *working_status = svn_wc__db_status_deleted;
7320251881Speter    }
7321251881Speter  else /* normal or incomplete */
7322251881Speter    {
7323251881Speter      /* The caller should scan upwards to detect whether this
7324251881Speter         addition has occurred because of a simple addition,
7325251881Speter         a copy, or is the destination of a move. */
7326251881Speter      *working_status = svn_wc__db_status_added;
7327251881Speter    }
7328251881Speter
7329251881Speter  return SVN_NO_ERROR;
7330251881Speter}
7331251881Speter
7332251881Speter
7333251881Speter/* Return the status of the node, if any, below the "working" node (or
7334251881Speter   below BELOW_OP_DEPTH if >= 0).
7335251881Speter   Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7336251881Speter   working node is present, and *STATUS to the status of the first
7337251881Speter   layer below the selected node. */
7338251881Speterstatic svn_error_t *
7339251881Speterinfo_below_working(svn_boolean_t *have_base,
7340251881Speter                   svn_boolean_t *have_work,
7341251881Speter                   svn_wc__db_status_t *status,
7342251881Speter                   svn_wc__db_wcroot_t *wcroot,
7343251881Speter                   const char *local_relpath,
7344251881Speter                   int below_op_depth,
7345251881Speter                   apr_pool_t *scratch_pool)
7346251881Speter{
7347251881Speter  svn_sqlite__stmt_t *stmt;
7348251881Speter  svn_boolean_t have_row;
7349251881Speter
7350251881Speter  *have_base = *have_work =  FALSE;
7351251881Speter  *status = svn_wc__db_status_normal;
7352251881Speter
7353251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7354251881Speter                                    STMT_SELECT_NODE_INFO));
7355251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7356251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7357251881Speter
7358251881Speter  if (below_op_depth >= 0)
7359251881Speter    {
7360251881Speter      while (have_row &&
7361251881Speter             (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7362251881Speter        {
7363251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7364251881Speter        }
7365251881Speter    }
7366251881Speter  if (have_row)
7367251881Speter    {
7368251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7369251881Speter      if (have_row)
7370251881Speter        *status = svn_sqlite__column_token(stmt, 3, presence_map);
7371251881Speter
7372251881Speter      while (have_row)
7373251881Speter        {
7374251881Speter          int op_depth = svn_sqlite__column_int(stmt, 0);
7375251881Speter
7376251881Speter          if (op_depth > 0)
7377251881Speter            *have_work = TRUE;
7378251881Speter          else
7379251881Speter            *have_base = TRUE;
7380251881Speter
7381251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7382251881Speter        }
7383251881Speter    }
7384251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
7385251881Speter
7386251881Speter  if (*have_work)
7387251881Speter    SVN_ERR(convert_to_working_status(status, *status));
7388251881Speter
7389251881Speter  return SVN_NO_ERROR;
7390251881Speter}
7391251881Speter
7392251881Speter/* Helper function for op_delete_txn */
7393251881Speterstatic svn_error_t *
7394251881Speterdelete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7395251881Speter                      const char *child_moved_from_relpath,
7396251881Speter                      int op_depth,
7397251881Speter                      const char *new_moved_to_relpath,
7398251881Speter                      apr_pool_t *scratch_pool)
7399251881Speter{
7400251881Speter  svn_sqlite__stmt_t *stmt;
7401251881Speter  int affected;
7402251881Speter
7403251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7404251881Speter                                    STMT_UPDATE_MOVED_TO_RELPATH));
7405251881Speter
7406251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7407251881Speter                            wcroot->wc_id,
7408251881Speter                            child_moved_from_relpath,
7409251881Speter                            op_depth,
7410251881Speter                            new_moved_to_relpath));
7411251881Speter  SVN_ERR(svn_sqlite__update(&affected, stmt));
7412251881Speter  assert(affected == 1);
7413251881Speter
7414251881Speter  return SVN_NO_ERROR;
7415251881Speter}
7416251881Speter
7417251881Speter
7418251881Speterstruct op_delete_baton_t {
7419251881Speter  const char *moved_to_relpath; /* NULL if delete is not part of a move */
7420251881Speter  svn_skel_t *conflict;
7421251881Speter  svn_skel_t *work_items;
7422251881Speter  svn_boolean_t delete_dir_externals;
7423251881Speter  svn_boolean_t notify;
7424251881Speter};
7425251881Speter
7426251881Speter/* This structure is used while rewriting move information for nodes.
7427251881Speter *
7428251881Speter * The most simple case of rewriting move information happens when
7429251881Speter * a moved-away subtree is moved again:  mv A B; mv B C
7430251881Speter * The second move requires rewriting moved-to info at or within A.
7431251881Speter *
7432251881Speter * Another example is a move of a subtree which had nodes moved into it:
7433251881Speter *   mv A B/F; mv B G
7434251881Speter * This requires rewriting such that A/F is marked has having moved to G/F.
7435251881Speter *
7436251881Speter * Another case is where a node becomes a nested moved node.
7437251881Speter * A nested move happens when a subtree child is moved before or after
7438251881Speter * the subtree itself is moved. For example:
7439251881Speter *   mv A/F A/G; mv A B
7440251881Speter * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7441251881Speter * Note that the following sequence results in the same DB state:
7442251881Speter *   mv A B; mv B/F B/G
7443251881Speter * We do not care about the order the moves were performed in.
7444251881Speter * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7445251881Speter */
7446251881Speterstruct moved_node_t {
7447251881Speter  /* The source of the move. */
7448251881Speter  const char *local_relpath;
7449251881Speter
7450251881Speter  /* The move destination. */
7451251881Speter  const char *moved_to_relpath;
7452251881Speter
7453251881Speter  /* The op-depth of the deleted node at the source of the move. */
7454251881Speter  int op_depth;
7455251881Speter};
7456251881Speter
7457251881Speterstatic svn_error_t *
7458251881Speterdelete_node(void *baton,
7459251881Speter            svn_wc__db_wcroot_t *wcroot,
7460251881Speter            const char *local_relpath,
7461251881Speter            apr_pool_t *scratch_pool)
7462251881Speter{
7463251881Speter  struct op_delete_baton_t *b = baton;
7464251881Speter  svn_wc__db_status_t status;
7465251881Speter  svn_boolean_t have_row, op_root;
7466251881Speter  svn_boolean_t add_work = FALSE;
7467251881Speter  svn_sqlite__stmt_t *stmt;
7468251881Speter  int select_depth; /* Depth of what is to be deleted */
7469251881Speter  svn_boolean_t refetch_depth = FALSE;
7470251881Speter  svn_node_kind_t kind;
7471251881Speter  apr_array_header_t *moved_nodes = NULL;
7472251881Speter  int delete_depth = relpath_depth(local_relpath);
7473251881Speter
7474251881Speter  SVN_ERR(read_info(&status,
7475251881Speter                    &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7476251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7477251881Speter                    &op_root, NULL, NULL,
7478251881Speter                    NULL, NULL, NULL,
7479251881Speter                    wcroot, local_relpath,
7480251881Speter                    scratch_pool, scratch_pool));
7481251881Speter
7482251881Speter  if (status == svn_wc__db_status_deleted
7483251881Speter      || status == svn_wc__db_status_not_present)
7484251881Speter    return SVN_NO_ERROR;
7485251881Speter
7486251881Speter  /* Don't copy BASE directories with server excluded nodes */
7487251881Speter  if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7488251881Speter    {
7489251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7490251881Speter                                        STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7491251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is",
7492251881Speter                                wcroot->wc_id, local_relpath));
7493251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7494251881Speter      if (have_row)
7495251881Speter        {
7496251881Speter          const char *absent_path = svn_sqlite__column_text(stmt, 0,
7497251881Speter                                                            scratch_pool);
7498251881Speter
7499251881Speter          return svn_error_createf(
7500251881Speter                               SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7501251881Speter                               svn_sqlite__reset(stmt),
7502251881Speter                          _("Cannot delete '%s' as '%s' is excluded by server"),
7503251881Speter                               path_for_error_message(wcroot, local_relpath,
7504251881Speter                                                      scratch_pool),
7505251881Speter                               path_for_error_message(wcroot, absent_path,
7506251881Speter                                                      scratch_pool));
7507251881Speter        }
7508251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
7509251881Speter    }
7510251881Speter  else if (status == svn_wc__db_status_server_excluded)
7511251881Speter    {
7512251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7513251881Speter                          _("Cannot delete '%s' as it is excluded by server"),
7514251881Speter                               path_for_error_message(wcroot, local_relpath,
7515251881Speter                                                      scratch_pool));
7516251881Speter    }
7517251881Speter  else if (status == svn_wc__db_status_excluded)
7518251881Speter    {
7519251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7520251881Speter                          _("Cannot delete '%s' as it is excluded"),
7521251881Speter                               path_for_error_message(wcroot, local_relpath,
7522251881Speter                                                      scratch_pool));
7523251881Speter    }
7524251881Speter
7525251881Speter  if (b->moved_to_relpath)
7526251881Speter    {
7527251881Speter      const char *moved_from_relpath = NULL;
7528251881Speter      struct moved_node_t *moved_node;
7529251881Speter      int move_op_depth;
7530251881Speter
7531251881Speter      moved_nodes = apr_array_make(scratch_pool, 1,
7532251881Speter                                   sizeof(struct moved_node_t *));
7533251881Speter
7534251881Speter      /* The node is being moved-away.
7535251881Speter       * Figure out if the node was moved-here before, or whether this
7536251881Speter       * is the first time the node is moved. */
7537251881Speter      if (status == svn_wc__db_status_added)
7538251881Speter        SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7539251881Speter                              &moved_from_relpath,
7540251881Speter                              NULL,
7541251881Speter                              &move_op_depth,
7542251881Speter                              wcroot, local_relpath,
7543251881Speter                              scratch_pool, scratch_pool));
7544251881Speter
7545251881Speter      if (op_root && moved_from_relpath)
7546251881Speter        {
7547251881Speter          const char *part = svn_relpath_skip_ancestor(local_relpath,
7548251881Speter                                                       moved_from_relpath);
7549251881Speter
7550251881Speter          /* Existing move-root is moved to another location */
7551251881Speter          moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7552251881Speter          if (!part)
7553251881Speter            moved_node->local_relpath = moved_from_relpath;
7554251881Speter          else
7555251881Speter            moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7556251881Speter                                                         part, scratch_pool);
7557251881Speter          moved_node->op_depth = move_op_depth;
7558251881Speter          moved_node->moved_to_relpath = b->moved_to_relpath;
7559251881Speter
7560251881Speter          APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7561251881Speter        }
7562251881Speter      else if (!op_root && (status == svn_wc__db_status_normal
7563251881Speter                            || status == svn_wc__db_status_copied
7564251881Speter                            || status == svn_wc__db_status_moved_here))
7565251881Speter        {
7566251881Speter          /* The node is becoming a move-root for the first time,
7567251881Speter           * possibly because of a nested move operation. */
7568251881Speter          moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7569251881Speter          moved_node->local_relpath = local_relpath;
7570251881Speter          moved_node->op_depth = delete_depth;
7571251881Speter          moved_node->moved_to_relpath = b->moved_to_relpath;
7572251881Speter
7573251881Speter          APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7574251881Speter        }
7575251881Speter      /* Else: We can't track history of local additions and/or of things we are
7576251881Speter               about to delete. */
7577251881Speter
7578251881Speter      /* And update all moved_to values still pointing to this location */
7579251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7580251881Speter                                        STMT_UPDATE_MOVED_TO_DESCENDANTS));
7581251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7582251881Speter                                             local_relpath,
7583251881Speter                                             b->moved_to_relpath));
7584251881Speter      SVN_ERR(svn_sqlite__update(NULL, stmt));
7585251881Speter    }
7586251881Speter  else
7587251881Speter    {
7588251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7589251881Speter                                        STMT_CLEAR_MOVED_TO_DESCENDANTS));
7590251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7591251881Speter                                            local_relpath));
7592251881Speter      SVN_ERR(svn_sqlite__update(NULL, stmt));
7593251881Speter    }
7594251881Speter
7595251881Speter  /* Find children that were moved out of the subtree rooted at this node.
7596251881Speter   * We'll need to update their op-depth columns because their deletion
7597251881Speter   * is now implied by the deletion of their parent (i.e. this node). */
7598251881Speter    {
7599251881Speter      apr_pool_t *iterpool;
7600251881Speter
7601251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7602251881Speter                                        STMT_SELECT_MOVED_FOR_DELETE));
7603251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7604251881Speter
7605251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7606251881Speter      iterpool = svn_pool_create(scratch_pool);
7607251881Speter      while (have_row)
7608251881Speter        {
7609251881Speter          struct moved_node_t *mn;
7610251881Speter          const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7611251881Speter          const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7612251881Speter          int child_op_depth = svn_sqlite__column_int(stmt, 2);
7613251881Speter          svn_boolean_t fixup = FALSE;
7614251881Speter
7615251881Speter          if (!b->moved_to_relpath
7616251881Speter              && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7617251881Speter            {
7618251881Speter              /* Update the op-depth of an moved node below this tree */
7619251881Speter              fixup = TRUE;
7620251881Speter              child_op_depth = delete_depth;
7621251881Speter            }
7622251881Speter          else if (b->moved_to_relpath
7623251881Speter                   && delete_depth == child_op_depth)
7624251881Speter            {
7625251881Speter              /* Update the op-depth of a tree shadowed by this tree */
7626251881Speter              fixup = TRUE;
7627251881Speter              child_op_depth = delete_depth;
7628251881Speter            }
7629251881Speter          else if (b->moved_to_relpath
7630251881Speter                   && child_op_depth >= delete_depth
7631251881Speter                   && !svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7632251881Speter            {
7633251881Speter              /* Update the move destination of something that is now moved
7634251881Speter                 away further */
7635251881Speter
7636251881Speter              child_relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
7637251881Speter
7638251881Speter              if (child_relpath)
7639251881Speter                {
7640251881Speter                  child_relpath = svn_relpath_join(b->moved_to_relpath, child_relpath, scratch_pool);
7641251881Speter
7642251881Speter                  if (child_op_depth > delete_depth
7643251881Speter                      && svn_relpath_skip_ancestor(local_relpath, child_relpath))
7644251881Speter                    child_op_depth = delete_depth;
7645251881Speter                  else
7646251881Speter                    child_op_depth = relpath_depth(child_relpath);
7647251881Speter
7648251881Speter                  fixup = TRUE;
7649251881Speter                }
7650251881Speter            }
7651251881Speter
7652251881Speter          if (fixup)
7653251881Speter            {
7654251881Speter              mn = apr_pcalloc(scratch_pool, sizeof(struct moved_node_t));
7655251881Speter
7656251881Speter              mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7657251881Speter              mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7658251881Speter              mn->op_depth = child_op_depth;
7659251881Speter
7660251881Speter              if (!moved_nodes)
7661251881Speter                moved_nodes = apr_array_make(scratch_pool, 1,
7662251881Speter                                             sizeof(struct moved_node_t *));
7663251881Speter              APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7664251881Speter            }
7665251881Speter
7666251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
7667251881Speter        }
7668251881Speter      svn_pool_destroy(iterpool);
7669251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
7670251881Speter    }
7671251881Speter
7672251881Speter  if (op_root)
7673251881Speter    {
7674251881Speter      svn_boolean_t below_base;
7675251881Speter      svn_boolean_t below_work;
7676251881Speter      svn_wc__db_status_t below_status;
7677251881Speter
7678251881Speter      /* Use STMT_SELECT_NODE_INFO directly instead of read_info plus
7679251881Speter         info_below_working */
7680251881Speter      SVN_ERR(info_below_working(&below_base, &below_work, &below_status,
7681251881Speter                                 wcroot, local_relpath, -1, scratch_pool));
7682251881Speter      if ((below_base || below_work)
7683251881Speter          && below_status != svn_wc__db_status_not_present
7684251881Speter          && below_status != svn_wc__db_status_deleted)
7685251881Speter        {
7686251881Speter          add_work = TRUE;
7687251881Speter          refetch_depth = TRUE;
7688251881Speter        }
7689251881Speter
7690251881Speter      select_depth = relpath_depth(local_relpath);
7691251881Speter    }
7692251881Speter  else
7693251881Speter    {
7694251881Speter      add_work = TRUE;
7695251881Speter      if (status != svn_wc__db_status_normal)
7696251881Speter        SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
7697251881Speter      else
7698251881Speter        select_depth = 0; /* Deleting BASE node */
7699251881Speter    }
7700251881Speter
7701251881Speter  /* ### Put actual-only nodes into the list? */
7702251881Speter  if (b->notify)
7703251881Speter    {
7704251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7705251881Speter                                        STMT_INSERT_DELETE_LIST));
7706251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7707251881Speter                                wcroot->wc_id, local_relpath, select_depth));
7708251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
7709251881Speter    }
7710251881Speter
7711251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7712251881Speter                                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7713251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7714251881Speter                            wcroot->wc_id, local_relpath, delete_depth));
7715251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7716251881Speter
7717251881Speter  if (refetch_depth)
7718251881Speter    SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
7719251881Speter
7720251881Speter  /* Delete ACTUAL_NODE rows, but leave those that have changelist
7721251881Speter     and a NODES row. */
7722251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7723251881Speter                         STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7724251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
7725251881Speter                            wcroot->wc_id, local_relpath));
7726251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7727251881Speter
7728251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7729251881Speter                         STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7730251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
7731251881Speter                            wcroot->wc_id, local_relpath));
7732251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7733251881Speter
7734251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7735251881Speter                                    STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7736251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7737251881Speter                            local_relpath));
7738251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7739251881Speter
7740251881Speter  if (add_work)
7741251881Speter    {
7742251881Speter      /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
7743251881Speter
7744251881Speter      /* Delete the node and possible descendants. */
7745251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7746251881Speter                                 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
7747251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
7748251881Speter                                wcroot->wc_id, local_relpath,
7749251881Speter                                select_depth, delete_depth));
7750251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
7751251881Speter    }
7752251881Speter
7753251881Speter  if (moved_nodes)
7754251881Speter    {
7755251881Speter      int i;
7756251881Speter
7757251881Speter      for (i = 0; i < moved_nodes->nelts; ++i)
7758251881Speter        {
7759251881Speter          const struct moved_node_t *moved_node
7760251881Speter            = APR_ARRAY_IDX(moved_nodes, i, void *);
7761251881Speter
7762251881Speter          SVN_ERR(delete_update_movedto(wcroot,
7763251881Speter                                        moved_node->local_relpath,
7764251881Speter                                        moved_node->op_depth,
7765251881Speter                                        moved_node->moved_to_relpath,
7766251881Speter                                        scratch_pool));
7767251881Speter        }
7768251881Speter    }
7769251881Speter
7770251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7771251881Speter                                    STMT_DELETE_FILE_EXTERNALS));
7772251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7773251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7774251881Speter
7775251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7776251881Speter                                    b->delete_dir_externals
7777251881Speter                                    ? STMT_DELETE_EXTERNAL_REGISTATIONS
7778251881Speter                                    : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
7779251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7780251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
7781251881Speter
7782251881Speter  SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
7783251881Speter  if (b->conflict)
7784251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7785251881Speter                                              b->conflict, scratch_pool));
7786251881Speter
7787251881Speter  return SVN_NO_ERROR;
7788251881Speter}
7789251881Speter
7790251881Speterstatic svn_error_t *
7791251881Speterop_delete_txn(void *baton,
7792251881Speter              svn_wc__db_wcroot_t *wcroot,
7793251881Speter              const char *local_relpath,
7794251881Speter              apr_pool_t *scratch_pool)
7795251881Speter{
7796251881Speter
7797251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
7798251881Speter  SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
7799251881Speter  return SVN_NO_ERROR;
7800251881Speter}
7801251881Speter
7802251881Speter
7803251881Speterstruct op_delete_many_baton_t {
7804251881Speter  apr_array_header_t *rel_targets;
7805251881Speter  svn_boolean_t delete_dir_externals;
7806251881Speter  const svn_skel_t *work_items;
7807251881Speter} op_delete_many_baton_t;
7808251881Speter
7809251881Speterstatic svn_error_t *
7810251881Speterop_delete_many_txn(void *baton,
7811251881Speter                   svn_wc__db_wcroot_t *wcroot,
7812251881Speter                   const char *local_relpath,
7813251881Speter                   apr_pool_t *scratch_pool)
7814251881Speter{
7815251881Speter  struct op_delete_many_baton_t *odmb = baton;
7816251881Speter  struct op_delete_baton_t odb;
7817251881Speter  int i;
7818251881Speter  apr_pool_t *iterpool;
7819251881Speter
7820251881Speter  odb.moved_to_relpath = NULL;
7821251881Speter  odb.conflict = NULL;
7822251881Speter  odb.work_items = NULL;
7823251881Speter  odb.delete_dir_externals = odmb->delete_dir_externals;
7824251881Speter  odb.notify = TRUE;
7825251881Speter
7826251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
7827251881Speter  iterpool = svn_pool_create(scratch_pool);
7828251881Speter  for (i = 0; i < odmb->rel_targets->nelts; i++)
7829251881Speter    {
7830251881Speter      const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
7831251881Speter                                                 const char *);
7832251881Speter
7833251881Speter
7834251881Speter      svn_pool_clear(iterpool);
7835251881Speter      SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
7836251881Speter    }
7837251881Speter  svn_pool_destroy(iterpool);
7838251881Speter
7839251881Speter  SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
7840251881Speter
7841251881Speter  return SVN_NO_ERROR;
7842251881Speter}
7843251881Speter
7844251881Speter
7845251881Speterstatic svn_error_t *
7846251881Speterdo_delete_notify(void *baton,
7847251881Speter                 svn_wc__db_wcroot_t *wcroot,
7848251881Speter                 svn_cancel_func_t cancel_func,
7849251881Speter                 void *cancel_baton,
7850251881Speter                 svn_wc_notify_func2_t notify_func,
7851251881Speter                 void *notify_baton,
7852251881Speter                 apr_pool_t *scratch_pool)
7853251881Speter{
7854251881Speter  svn_sqlite__stmt_t *stmt;
7855251881Speter  svn_boolean_t have_row;
7856251881Speter  apr_pool_t *iterpool;
7857251881Speter
7858251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7859251881Speter                                    STMT_SELECT_DELETE_LIST));
7860251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
7861251881Speter
7862251881Speter  iterpool = svn_pool_create(scratch_pool);
7863251881Speter  while (have_row)
7864251881Speter    {
7865251881Speter      const char *notify_relpath;
7866251881Speter      const char *notify_abspath;
7867251881Speter
7868251881Speter      svn_pool_clear(iterpool);
7869251881Speter
7870251881Speter      notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7871251881Speter      notify_abspath = svn_dirent_join(wcroot->abspath,
7872251881Speter                                       notify_relpath,
7873251881Speter                                       iterpool);
7874251881Speter
7875251881Speter      notify_func(notify_baton,
7876251881Speter                  svn_wc_create_notify(notify_abspath,
7877251881Speter                                       svn_wc_notify_delete,
7878251881Speter                                       iterpool),
7879251881Speter                  iterpool);
7880251881Speter
7881251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
7882251881Speter    }
7883251881Speter  svn_pool_destroy(iterpool);
7884251881Speter
7885251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
7886251881Speter
7887251881Speter  /* We only allow cancellation after notification for all deleted nodes
7888251881Speter   * has happened. The nodes are already deleted so we should notify for
7889251881Speter   * all of them. */
7890251881Speter  if (cancel_func)
7891251881Speter    SVN_ERR(cancel_func(cancel_baton));
7892251881Speter
7893251881Speter  return SVN_NO_ERROR;
7894251881Speter}
7895251881Speter
7896251881Speter
7897251881Spetersvn_error_t *
7898251881Spetersvn_wc__db_op_delete(svn_wc__db_t *db,
7899251881Speter                     const char *local_abspath,
7900251881Speter                     const char *moved_to_abspath,
7901251881Speter                     svn_boolean_t delete_dir_externals,
7902251881Speter                     svn_skel_t *conflict,
7903251881Speter                     svn_skel_t *work_items,
7904251881Speter                     svn_cancel_func_t cancel_func,
7905251881Speter                     void *cancel_baton,
7906251881Speter                     svn_wc_notify_func2_t notify_func,
7907251881Speter                     void *notify_baton,
7908251881Speter                     apr_pool_t *scratch_pool)
7909251881Speter{
7910251881Speter  svn_wc__db_wcroot_t *wcroot;
7911251881Speter  svn_wc__db_wcroot_t *moved_to_wcroot;
7912251881Speter  const char *local_relpath;
7913251881Speter  const char *moved_to_relpath;
7914251881Speter  struct op_delete_baton_t odb;
7915251881Speter
7916251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7917251881Speter
7918251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7919251881Speter                                                db, local_abspath,
7920251881Speter                                                scratch_pool, scratch_pool));
7921251881Speter  VERIFY_USABLE_WCROOT(wcroot);
7922251881Speter
7923251881Speter  if (moved_to_abspath)
7924251881Speter    {
7925251881Speter      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
7926251881Speter                                                    &moved_to_relpath,
7927251881Speter                                                    db, moved_to_abspath,
7928251881Speter                                                    scratch_pool,
7929251881Speter                                                    scratch_pool));
7930251881Speter      VERIFY_USABLE_WCROOT(moved_to_wcroot);
7931251881Speter
7932251881Speter      if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
7933251881Speter        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7934251881Speter                                 _("Cannot move '%s' to '%s' because they "
7935251881Speter                                   "are not in the same working copy"),
7936251881Speter                                 svn_dirent_local_style(local_abspath,
7937251881Speter                                                        scratch_pool),
7938251881Speter                                 svn_dirent_local_style(moved_to_abspath,
7939251881Speter                                                        scratch_pool));
7940251881Speter    }
7941251881Speter  else
7942251881Speter    moved_to_relpath = NULL;
7943251881Speter
7944251881Speter  odb.moved_to_relpath = moved_to_relpath;
7945251881Speter  odb.conflict = conflict;
7946251881Speter  odb.work_items = work_items;
7947251881Speter  odb.delete_dir_externals = delete_dir_externals;
7948251881Speter
7949251881Speter  if (notify_func)
7950251881Speter    {
7951251881Speter      /* Perform the deletion operation (transactionally), perform any
7952251881Speter         notifications necessary, and then clean out our temporary tables.  */
7953251881Speter      odb.notify = TRUE;
7954251881Speter      SVN_ERR(with_finalization(wcroot, local_relpath,
7955251881Speter                                op_delete_txn, &odb,
7956251881Speter                                do_delete_notify, NULL,
7957251881Speter                                cancel_func, cancel_baton,
7958251881Speter                                notify_func, notify_baton,
7959251881Speter                                STMT_FINALIZE_DELETE,
7960251881Speter                                scratch_pool));
7961251881Speter    }
7962251881Speter  else
7963251881Speter    {
7964251881Speter      /* Avoid the trigger work */
7965251881Speter      odb.notify = FALSE;
7966251881Speter      SVN_WC__DB_WITH_TXN(
7967251881Speter                    delete_node(&odb, wcroot, local_relpath, scratch_pool),
7968251881Speter                    wcroot);
7969251881Speter    }
7970251881Speter
7971251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7972251881Speter                        scratch_pool));
7973251881Speter
7974251881Speter  return SVN_NO_ERROR;
7975251881Speter}
7976251881Speter
7977251881Speter
7978251881Spetersvn_error_t *
7979251881Spetersvn_wc__db_op_delete_many(svn_wc__db_t *db,
7980251881Speter                          apr_array_header_t *targets,
7981251881Speter                          svn_boolean_t delete_dir_externals,
7982251881Speter                          const svn_skel_t *work_items,
7983251881Speter                          svn_cancel_func_t cancel_func,
7984251881Speter                          void *cancel_baton,
7985251881Speter                          svn_wc_notify_func2_t notify_func,
7986251881Speter                          void *notify_baton,
7987251881Speter                          apr_pool_t *scratch_pool)
7988251881Speter{
7989251881Speter  svn_wc__db_wcroot_t *wcroot;
7990251881Speter  const char *local_relpath;
7991251881Speter  struct op_delete_many_baton_t odmb;
7992251881Speter  int i;
7993251881Speter  apr_pool_t *iterpool;
7994251881Speter
7995251881Speter  odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
7996251881Speter                                    sizeof(const char *));
7997251881Speter  odmb.work_items = work_items;
7998251881Speter  odmb.delete_dir_externals = delete_dir_externals;
7999251881Speter  iterpool = svn_pool_create(scratch_pool);
8000251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8001251881Speter                                                db,
8002251881Speter                                                APR_ARRAY_IDX(targets, 0,
8003251881Speter                                                              const char *),
8004251881Speter                                                scratch_pool, iterpool));
8005251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8006251881Speter  for (i = 0; i < targets->nelts; i++)
8007251881Speter    {
8008251881Speter      const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8009251881Speter      svn_wc__db_wcroot_t *target_wcroot;
8010251881Speter
8011251881Speter      svn_pool_clear(iterpool);
8012251881Speter
8013251881Speter      SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8014251881Speter                                                    &local_relpath, db,
8015251881Speter                                                    APR_ARRAY_IDX(targets, i,
8016251881Speter                                                                  const char *),
8017251881Speter                                                    scratch_pool, iterpool));
8018251881Speter      VERIFY_USABLE_WCROOT(target_wcroot);
8019251881Speter      SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8020251881Speter
8021251881Speter      /* Assert that all targets are within the same working copy. */
8022251881Speter      SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8023251881Speter
8024251881Speter      APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8025251881Speter      SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8026251881Speter                            iterpool));
8027251881Speter
8028251881Speter    }
8029251881Speter  svn_pool_destroy(iterpool);
8030251881Speter
8031251881Speter  /* Perform the deletion operation (transactionally), perform any
8032251881Speter     notifications necessary, and then clean out our temporary tables.  */
8033251881Speter  return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8034251881Speter                                           op_delete_many_txn, &odmb,
8035251881Speter                                           do_delete_notify, NULL,
8036251881Speter                                           cancel_func, cancel_baton,
8037251881Speter                                           notify_func, notify_baton,
8038251881Speter                                           STMT_FINALIZE_DELETE,
8039251881Speter                                           scratch_pool));
8040251881Speter}
8041251881Speter
8042251881Speter
8043251881Speter/* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8044251881Speter   DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8045251881Speterstatic svn_error_t *
8046251881Speterread_info(svn_wc__db_status_t *status,
8047251881Speter          svn_node_kind_t *kind,
8048251881Speter          svn_revnum_t *revision,
8049251881Speter          const char **repos_relpath,
8050251881Speter          apr_int64_t *repos_id,
8051251881Speter          svn_revnum_t *changed_rev,
8052251881Speter          apr_time_t *changed_date,
8053251881Speter          const char **changed_author,
8054251881Speter          svn_depth_t *depth,
8055251881Speter          const svn_checksum_t **checksum,
8056251881Speter          const char **target,
8057251881Speter          const char **original_repos_relpath,
8058251881Speter          apr_int64_t *original_repos_id,
8059251881Speter          svn_revnum_t *original_revision,
8060251881Speter          svn_wc__db_lock_t **lock,
8061251881Speter          svn_filesize_t *recorded_size,
8062251881Speter          apr_time_t *recorded_time,
8063251881Speter          const char **changelist,
8064251881Speter          svn_boolean_t *conflicted,
8065251881Speter          svn_boolean_t *op_root,
8066251881Speter          svn_boolean_t *had_props,
8067251881Speter          svn_boolean_t *props_mod,
8068251881Speter          svn_boolean_t *have_base,
8069251881Speter          svn_boolean_t *have_more_work,
8070251881Speter          svn_boolean_t *have_work,
8071251881Speter          svn_wc__db_wcroot_t *wcroot,
8072251881Speter          const char *local_relpath,
8073251881Speter          apr_pool_t *result_pool,
8074251881Speter          apr_pool_t *scratch_pool)
8075251881Speter{
8076251881Speter  svn_sqlite__stmt_t *stmt_info;
8077251881Speter  svn_sqlite__stmt_t *stmt_act;
8078251881Speter  svn_boolean_t have_info;
8079251881Speter  svn_boolean_t have_act;
8080251881Speter  svn_error_t *err = NULL;
8081251881Speter
8082251881Speter  /* Obtain the most likely to exist record first, to make sure we don't
8083251881Speter     have to obtain the SQLite read-lock multiple times */
8084251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8085251881Speter                                    lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8086251881Speter                                         : STMT_SELECT_NODE_INFO));
8087251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8088251881Speter  SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8089251881Speter
8090251881Speter  if (changelist || conflicted || props_mod)
8091251881Speter    {
8092251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8093251881Speter                                        STMT_SELECT_ACTUAL_NODE));
8094251881Speter      SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8095251881Speter      SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8096251881Speter    }
8097251881Speter  else
8098251881Speter    {
8099251881Speter      have_act = FALSE;
8100251881Speter      stmt_act = NULL;
8101251881Speter    }
8102251881Speter
8103251881Speter  if (have_info)
8104251881Speter    {
8105251881Speter      int op_depth;
8106251881Speter      svn_node_kind_t node_kind;
8107251881Speter
8108251881Speter      op_depth = svn_sqlite__column_int(stmt_info, 0);
8109251881Speter      node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8110251881Speter
8111251881Speter      if (status)
8112251881Speter        {
8113251881Speter          *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8114251881Speter
8115251881Speter          if (op_depth != 0) /* WORKING */
8116251881Speter            err = svn_error_compose_create(err,
8117251881Speter                                           convert_to_working_status(status,
8118251881Speter                                                                     *status));
8119251881Speter        }
8120251881Speter      if (kind)
8121251881Speter        {
8122251881Speter          *kind = node_kind;
8123251881Speter        }
8124251881Speter      if (op_depth != 0)
8125251881Speter        {
8126251881Speter          if (repos_id)
8127251881Speter            *repos_id = INVALID_REPOS_ID;
8128251881Speter          if (revision)
8129251881Speter            *revision = SVN_INVALID_REVNUM;
8130251881Speter          if (repos_relpath)
8131251881Speter            /* Our path is implied by our parent somewhere up the tree.
8132251881Speter               With the NULL value and status, the caller will know to
8133251881Speter               search up the tree for the base of our path.  */
8134251881Speter            *repos_relpath = NULL;
8135251881Speter        }
8136251881Speter      else
8137251881Speter        {
8138251881Speter          /* Fetch repository information. If we have a
8139251881Speter             WORKING_NODE (and have been added), then the repository
8140251881Speter             we're being added to will be dependent upon a parent. The
8141251881Speter             caller can scan upwards to locate the repository.  */
8142251881Speter          repos_location_from_columns(repos_id, revision, repos_relpath,
8143251881Speter                                      stmt_info, 1, 5, 2, result_pool);
8144251881Speter        }
8145251881Speter      if (changed_rev)
8146251881Speter        {
8147251881Speter          *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8148251881Speter        }
8149251881Speter      if (changed_date)
8150251881Speter        {
8151251881Speter          *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8152251881Speter        }
8153251881Speter      if (changed_author)
8154251881Speter        {
8155251881Speter          *changed_author = svn_sqlite__column_text(stmt_info, 10,
8156251881Speter                                                    result_pool);
8157251881Speter        }
8158251881Speter      if (recorded_time)
8159251881Speter        {
8160251881Speter          *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8161251881Speter        }
8162251881Speter      if (depth)
8163251881Speter        {
8164251881Speter          if (node_kind != svn_node_dir)
8165251881Speter            {
8166251881Speter              *depth = svn_depth_unknown;
8167251881Speter            }
8168251881Speter          else
8169251881Speter            {
8170251881Speter              *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8171251881Speter                                                     svn_depth_unknown);
8172251881Speter            }
8173251881Speter        }
8174251881Speter      if (checksum)
8175251881Speter        {
8176251881Speter          if (node_kind != svn_node_file)
8177251881Speter            {
8178251881Speter              *checksum = NULL;
8179251881Speter            }
8180251881Speter          else
8181251881Speter            {
8182251881Speter
8183251881Speter              err = svn_error_compose_create(
8184251881Speter                        err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8185251881Speter                                                         result_pool));
8186251881Speter            }
8187251881Speter        }
8188251881Speter      if (recorded_size)
8189251881Speter        {
8190251881Speter          *recorded_size = get_recorded_size(stmt_info, 7);
8191251881Speter        }
8192251881Speter      if (target)
8193251881Speter        {
8194251881Speter          if (node_kind != svn_node_symlink)
8195251881Speter            *target = NULL;
8196251881Speter          else
8197251881Speter            *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8198251881Speter        }
8199251881Speter      if (changelist)
8200251881Speter        {
8201251881Speter          if (have_act)
8202251881Speter            *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8203251881Speter          else
8204251881Speter            *changelist = NULL;
8205251881Speter        }
8206251881Speter      if (op_depth == 0)
8207251881Speter        {
8208251881Speter          if (original_repos_id)
8209251881Speter            *original_repos_id = INVALID_REPOS_ID;
8210251881Speter          if (original_revision)
8211251881Speter            *original_revision = SVN_INVALID_REVNUM;
8212251881Speter          if (original_repos_relpath)
8213251881Speter            *original_repos_relpath = NULL;
8214251881Speter        }
8215251881Speter      else
8216251881Speter        {
8217251881Speter          repos_location_from_columns(original_repos_id,
8218251881Speter                                      original_revision,
8219251881Speter                                      original_repos_relpath,
8220251881Speter                                      stmt_info, 1, 5, 2, result_pool);
8221251881Speter        }
8222251881Speter      if (props_mod)
8223251881Speter        {
8224251881Speter          *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8225251881Speter        }
8226251881Speter      if (had_props)
8227251881Speter        {
8228251881Speter          *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8229251881Speter        }
8230251881Speter      if (conflicted)
8231251881Speter        {
8232251881Speter          if (have_act)
8233251881Speter            {
8234251881Speter              *conflicted =
8235251881Speter                 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8236251881Speter            }
8237251881Speter          else
8238251881Speter            *conflicted = FALSE;
8239251881Speter        }
8240251881Speter
8241251881Speter      if (lock)
8242251881Speter        {
8243251881Speter          if (op_depth != 0)
8244251881Speter            *lock = NULL;
8245251881Speter          else
8246251881Speter            *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8247251881Speter        }
8248251881Speter
8249251881Speter      if (have_work)
8250251881Speter        *have_work = (op_depth != 0);
8251251881Speter
8252251881Speter      if (op_root)
8253251881Speter        {
8254251881Speter          *op_root = ((op_depth > 0)
8255251881Speter                      && (op_depth == relpath_depth(local_relpath)));
8256251881Speter        }
8257251881Speter
8258251881Speter      if (have_base || have_more_work)
8259251881Speter        {
8260251881Speter          if (have_more_work)
8261251881Speter            *have_more_work = FALSE;
8262251881Speter
8263251881Speter          while (!err && op_depth != 0)
8264251881Speter            {
8265251881Speter              err = svn_sqlite__step(&have_info, stmt_info);
8266251881Speter
8267251881Speter              if (err || !have_info)
8268251881Speter                break;
8269251881Speter
8270251881Speter              op_depth = svn_sqlite__column_int(stmt_info, 0);
8271251881Speter
8272251881Speter              if (have_more_work)
8273251881Speter                {
8274251881Speter                  if (op_depth > 0)
8275251881Speter                    *have_more_work = TRUE;
8276251881Speter
8277251881Speter                  if (!have_base)
8278251881Speter                   break;
8279251881Speter                }
8280251881Speter            }
8281251881Speter
8282251881Speter          if (have_base)
8283251881Speter            *have_base = (op_depth == 0);
8284251881Speter        }
8285251881Speter    }
8286251881Speter  else if (have_act)
8287251881Speter    {
8288251881Speter      /* A row in ACTUAL_NODE should never exist without a corresponding
8289251881Speter         node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8290251881Speter      if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8291251881Speter          err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8292251881Speter                                  _("Corrupt data for '%s'"),
8293251881Speter                                  path_for_error_message(wcroot, local_relpath,
8294251881Speter                                                         scratch_pool));
8295251881Speter      /* ### What should we return?  Should we have a separate
8296251881Speter             function for reading actual-only nodes? */
8297251881Speter
8298251881Speter      /* As a safety measure, until we decide if we want to use
8299251881Speter         read_info for actual-only nodes, make sure the caller asked
8300251881Speter         for the conflict status. */
8301251881Speter      SVN_ERR_ASSERT(conflicted);
8302251881Speter
8303251881Speter      if (status)
8304251881Speter        *status = svn_wc__db_status_normal;  /* What! No it's not! */
8305251881Speter      if (kind)
8306251881Speter        *kind = svn_node_unknown;
8307251881Speter      if (revision)
8308251881Speter        *revision = SVN_INVALID_REVNUM;
8309251881Speter      if (repos_relpath)
8310251881Speter        *repos_relpath = NULL;
8311251881Speter      if (repos_id)
8312251881Speter        *repos_id = INVALID_REPOS_ID;
8313251881Speter      if (changed_rev)
8314251881Speter        *changed_rev = SVN_INVALID_REVNUM;
8315251881Speter      if (changed_date)
8316251881Speter        *changed_date = 0;
8317251881Speter      if (depth)
8318251881Speter        *depth = svn_depth_unknown;
8319251881Speter      if (checksum)
8320251881Speter        *checksum = NULL;
8321251881Speter      if (target)
8322251881Speter        *target = NULL;
8323251881Speter      if (original_repos_relpath)
8324251881Speter        *original_repos_relpath = NULL;
8325251881Speter      if (original_repos_id)
8326251881Speter        *original_repos_id = INVALID_REPOS_ID;
8327251881Speter      if (original_revision)
8328251881Speter        *original_revision = SVN_INVALID_REVNUM;
8329251881Speter      if (lock)
8330251881Speter        *lock = NULL;
8331251881Speter      if (recorded_size)
8332251881Speter        *recorded_size = 0;
8333251881Speter      if (recorded_time)
8334251881Speter        *recorded_time = 0;
8335251881Speter      if (changelist)
8336251881Speter        *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8337251881Speter      if (op_root)
8338251881Speter        *op_root = FALSE;
8339251881Speter      if (had_props)
8340251881Speter        *had_props = FALSE;
8341251881Speter      if (props_mod)
8342251881Speter        *props_mod = FALSE;
8343251881Speter      if (conflicted)
8344251881Speter        *conflicted = TRUE;
8345251881Speter      if (have_base)
8346251881Speter        *have_base = FALSE;
8347251881Speter      if (have_more_work)
8348251881Speter        *have_more_work = FALSE;
8349251881Speter      if (have_work)
8350251881Speter        *have_work = FALSE;
8351251881Speter    }
8352251881Speter  else
8353251881Speter    {
8354251881Speter      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8355251881Speter                              _("The node '%s' was not found."),
8356251881Speter                              path_for_error_message(wcroot, local_relpath,
8357251881Speter                                                     scratch_pool));
8358251881Speter    }
8359251881Speter
8360251881Speter  if (stmt_act != NULL)
8361251881Speter    err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8362251881Speter
8363251881Speter  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8364251881Speter    err = svn_error_quick_wrap(err,
8365251881Speter                               apr_psprintf(scratch_pool,
8366251881Speter                                            "Error reading node '%s'",
8367251881Speter                                            local_relpath));
8368251881Speter
8369251881Speter  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8370251881Speter
8371251881Speter  return SVN_NO_ERROR;
8372251881Speter}
8373251881Speter
8374251881Speter
8375251881Spetersvn_error_t *
8376251881Spetersvn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8377251881Speter                              svn_node_kind_t *kind,
8378251881Speter                              svn_revnum_t *revision,
8379251881Speter                              const char **repos_relpath,
8380251881Speter                              apr_int64_t *repos_id,
8381251881Speter                              svn_revnum_t *changed_rev,
8382251881Speter                              apr_time_t *changed_date,
8383251881Speter                              const char **changed_author,
8384251881Speter                              svn_depth_t *depth,
8385251881Speter                              const svn_checksum_t **checksum,
8386251881Speter                              const char **target,
8387251881Speter                              const char **original_repos_relpath,
8388251881Speter                              apr_int64_t *original_repos_id,
8389251881Speter                              svn_revnum_t *original_revision,
8390251881Speter                              svn_wc__db_lock_t **lock,
8391251881Speter                              svn_filesize_t *recorded_size,
8392251881Speter                              apr_time_t *recorded_time,
8393251881Speter                              const char **changelist,
8394251881Speter                              svn_boolean_t *conflicted,
8395251881Speter                              svn_boolean_t *op_root,
8396251881Speter                              svn_boolean_t *had_props,
8397251881Speter                              svn_boolean_t *props_mod,
8398251881Speter                              svn_boolean_t *have_base,
8399251881Speter                              svn_boolean_t *have_more_work,
8400251881Speter                              svn_boolean_t *have_work,
8401251881Speter                              svn_wc__db_wcroot_t *wcroot,
8402251881Speter                              const char *local_relpath,
8403251881Speter                              apr_pool_t *result_pool,
8404251881Speter                              apr_pool_t *scratch_pool)
8405251881Speter{
8406251881Speter  return svn_error_trace(
8407251881Speter           read_info(status, kind, revision, repos_relpath, repos_id,
8408251881Speter                     changed_rev, changed_date, changed_author,
8409251881Speter                     depth, checksum, target, original_repos_relpath,
8410251881Speter                     original_repos_id, original_revision, lock,
8411251881Speter                     recorded_size, recorded_time, changelist, conflicted,
8412251881Speter                     op_root, had_props, props_mod,
8413251881Speter                     have_base, have_more_work, have_work,
8414251881Speter                     wcroot, local_relpath, result_pool, scratch_pool));
8415251881Speter}
8416251881Speter
8417251881Speter
8418251881Spetersvn_error_t *
8419251881Spetersvn_wc__db_read_info(svn_wc__db_status_t *status,
8420251881Speter                     svn_node_kind_t *kind,
8421251881Speter                     svn_revnum_t *revision,
8422251881Speter                     const char **repos_relpath,
8423251881Speter                     const char **repos_root_url,
8424251881Speter                     const char **repos_uuid,
8425251881Speter                     svn_revnum_t *changed_rev,
8426251881Speter                     apr_time_t *changed_date,
8427251881Speter                     const char **changed_author,
8428251881Speter                     svn_depth_t *depth,
8429251881Speter                     const svn_checksum_t **checksum,
8430251881Speter                     const char **target,
8431251881Speter                     const char **original_repos_relpath,
8432251881Speter                     const char **original_root_url,
8433251881Speter                     const char **original_uuid,
8434251881Speter                     svn_revnum_t *original_revision,
8435251881Speter                     svn_wc__db_lock_t **lock,
8436251881Speter                     svn_filesize_t *recorded_size,
8437251881Speter                     apr_time_t *recorded_time,
8438251881Speter                     const char **changelist,
8439251881Speter                     svn_boolean_t *conflicted,
8440251881Speter                     svn_boolean_t *op_root,
8441251881Speter                     svn_boolean_t *have_props,
8442251881Speter                     svn_boolean_t *props_mod,
8443251881Speter                     svn_boolean_t *have_base,
8444251881Speter                     svn_boolean_t *have_more_work,
8445251881Speter                     svn_boolean_t *have_work,
8446251881Speter                     svn_wc__db_t *db,
8447251881Speter                     const char *local_abspath,
8448251881Speter                     apr_pool_t *result_pool,
8449251881Speter                     apr_pool_t *scratch_pool)
8450251881Speter{
8451251881Speter  svn_wc__db_wcroot_t *wcroot;
8452251881Speter  const char *local_relpath;
8453251881Speter  apr_int64_t repos_id, original_repos_id;
8454251881Speter
8455251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8456251881Speter
8457251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8458251881Speter                              local_abspath, scratch_pool, scratch_pool));
8459251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8460251881Speter
8461251881Speter  SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
8462251881Speter                    changed_rev, changed_date, changed_author,
8463251881Speter                    depth, checksum, target, original_repos_relpath,
8464251881Speter                    &original_repos_id, original_revision, lock,
8465251881Speter                    recorded_size, recorded_time, changelist, conflicted,
8466251881Speter                    op_root, have_props, props_mod,
8467251881Speter                    have_base, have_more_work, have_work,
8468251881Speter                    wcroot, local_relpath, result_pool, scratch_pool));
8469251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8470251881Speter                                      wcroot->sdb, repos_id, result_pool));
8471251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8472251881Speter                                      wcroot->sdb, original_repos_id,
8473251881Speter                                      result_pool));
8474251881Speter
8475251881Speter  return SVN_NO_ERROR;
8476251881Speter}
8477251881Speter
8478251881Speterstatic svn_error_t *
8479251881Speteris_wclocked(svn_boolean_t *locked,
8480251881Speter            svn_wc__db_wcroot_t *wcroot,
8481251881Speter            const char *dir_relpath,
8482251881Speter            apr_pool_t *scratch_pool);
8483251881Speter
8484251881Speter/* What we really want to store about a node.  This relies on the
8485251881Speter   offset of svn_wc__db_info_t being zero. */
8486251881Speterstruct read_children_info_item_t
8487251881Speter{
8488251881Speter  struct svn_wc__db_info_t info;
8489251881Speter  int op_depth;
8490251881Speter  int nr_layers;
8491251881Speter};
8492251881Speter
8493251881Speterstatic svn_error_t *
8494251881Speterread_children_info(svn_wc__db_wcroot_t *wcroot,
8495251881Speter                   const char *dir_relpath,
8496251881Speter                   apr_hash_t *conflicts,
8497251881Speter                   apr_hash_t *nodes,
8498251881Speter                   apr_pool_t *result_pool,
8499251881Speter                   apr_pool_t *scratch_pool)
8500251881Speter{
8501251881Speter  svn_sqlite__stmt_t *stmt;
8502251881Speter  svn_boolean_t have_row;
8503251881Speter  const char *repos_root_url = NULL;
8504251881Speter  const char *repos_uuid = NULL;
8505251881Speter  apr_int64_t last_repos_id = INVALID_REPOS_ID;
8506251881Speter
8507251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8508251881Speter                                    STMT_SELECT_NODE_CHILDREN_INFO));
8509251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8510251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8511251881Speter
8512251881Speter  while (have_row)
8513251881Speter    {
8514251881Speter      /* CHILD item points to what we have about the node. We only provide
8515251881Speter         CHILD->item to our caller. */
8516251881Speter      struct read_children_info_item_t *child_item;
8517251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8518251881Speter      const char *name = svn_relpath_basename(child_relpath, NULL);
8519251881Speter      svn_error_t *err;
8520251881Speter      int op_depth;
8521251881Speter      svn_boolean_t new_child;
8522251881Speter
8523251881Speter      child_item = svn_hash_gets(nodes, name);
8524251881Speter      if (child_item)
8525251881Speter        new_child = FALSE;
8526251881Speter      else
8527251881Speter        {
8528251881Speter          child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8529251881Speter          new_child = TRUE;
8530251881Speter        }
8531251881Speter
8532251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
8533251881Speter
8534251881Speter      /* Do we have new or better information? */
8535251881Speter      if (new_child || op_depth > child_item->op_depth)
8536251881Speter        {
8537251881Speter          struct svn_wc__db_info_t *child = &child_item->info;
8538251881Speter          child_item->op_depth = op_depth;
8539251881Speter
8540251881Speter          child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8541251881Speter
8542251881Speter          child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8543251881Speter          if (op_depth != 0)
8544251881Speter            {
8545251881Speter              if (child->status == svn_wc__db_status_incomplete)
8546251881Speter                child->incomplete = TRUE;
8547251881Speter              err = convert_to_working_status(&child->status, child->status);
8548251881Speter              if (err)
8549251881Speter                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8550251881Speter            }
8551251881Speter
8552251881Speter          if (op_depth != 0)
8553251881Speter            child->revnum = SVN_INVALID_REVNUM;
8554251881Speter          else
8555251881Speter            child->revnum = svn_sqlite__column_revnum(stmt, 5);
8556251881Speter
8557251881Speter          if (op_depth != 0)
8558251881Speter            child->repos_relpath = NULL;
8559251881Speter          else
8560251881Speter            child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8561251881Speter                                                           result_pool);
8562251881Speter
8563251881Speter          if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8564251881Speter            {
8565251881Speter              child->repos_root_url = NULL;
8566251881Speter              child->repos_uuid = NULL;
8567251881Speter            }
8568251881Speter          else
8569251881Speter            {
8570251881Speter              const char *last_repos_root_url = NULL;
8571251881Speter
8572251881Speter              apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8573251881Speter              if (!repos_root_url ||
8574251881Speter                  (last_repos_id != INVALID_REPOS_ID &&
8575251881Speter                   repos_id != last_repos_id))
8576251881Speter                {
8577251881Speter                  last_repos_root_url = repos_root_url;
8578251881Speter                  err = svn_wc__db_fetch_repos_info(&repos_root_url,
8579251881Speter                                                    &repos_uuid,
8580251881Speter                                                    wcroot->sdb, repos_id,
8581251881Speter                                                    result_pool);
8582251881Speter                  if (err)
8583251881Speter                    SVN_ERR(svn_error_compose_create(err,
8584251881Speter                                                 svn_sqlite__reset(stmt)));
8585251881Speter                }
8586251881Speter
8587251881Speter              if (last_repos_id == INVALID_REPOS_ID)
8588251881Speter                last_repos_id = repos_id;
8589251881Speter
8590251881Speter              /* Assume working copy is all one repos_id so that a
8591251881Speter                 single cached value is sufficient. */
8592251881Speter              if (repos_id != last_repos_id)
8593251881Speter                {
8594251881Speter                  err= svn_error_createf(
8595251881Speter                         SVN_ERR_WC_DB_ERROR, NULL,
8596251881Speter                         _("The node '%s' comes from unexpected repository "
8597251881Speter                           "'%s', expected '%s'; if this node is a file "
8598251881Speter                           "external using the correct URL in the external "
8599251881Speter                           "definition can fix the problem, see issue #4087"),
8600251881Speter                         child_relpath, repos_root_url, last_repos_root_url);
8601251881Speter                  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8602251881Speter                }
8603251881Speter              child->repos_root_url = repos_root_url;
8604251881Speter              child->repos_uuid = repos_uuid;
8605251881Speter            }
8606251881Speter
8607251881Speter          child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8608251881Speter
8609251881Speter          child->changed_date = svn_sqlite__column_int64(stmt, 9);
8610251881Speter
8611251881Speter          child->changed_author = svn_sqlite__column_text(stmt, 10,
8612251881Speter                                                          result_pool);
8613251881Speter
8614251881Speter          if (child->kind != svn_node_dir)
8615251881Speter            child->depth = svn_depth_unknown;
8616251881Speter          else
8617251881Speter            {
8618251881Speter              child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8619251881Speter                                                           svn_depth_unknown);
8620251881Speter              if (new_child)
8621251881Speter                SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8622251881Speter                                    scratch_pool));
8623251881Speter            }
8624251881Speter
8625251881Speter          child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8626251881Speter          child->recorded_size = get_recorded_size(stmt, 7);
8627251881Speter          child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8628251881Speter          child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8629251881Speter          child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8630251881Speter#ifdef HAVE_SYMLINK
8631251881Speter          if (child->had_props)
8632251881Speter            {
8633251881Speter              apr_hash_t *properties;
8634251881Speter              err = svn_sqlite__column_properties(&properties, stmt, 14,
8635251881Speter                                                  scratch_pool, scratch_pool);
8636251881Speter              if (err)
8637251881Speter                SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8638251881Speter
8639251881Speter              child->special = (child->had_props
8640251881Speter                                && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8641251881Speter            }
8642251881Speter#endif
8643251881Speter          if (op_depth == 0)
8644251881Speter            child->op_root = FALSE;
8645251881Speter          else
8646251881Speter            child->op_root = (op_depth == relpath_depth(child_relpath));
8647251881Speter
8648251881Speter          svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8649251881Speter        }
8650251881Speter
8651251881Speter      if (op_depth == 0)
8652251881Speter        {
8653251881Speter          child_item->info.have_base = TRUE;
8654251881Speter
8655251881Speter          /* Get the lock info, available only at op_depth 0. */
8656251881Speter          child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8657251881Speter                                                    result_pool);
8658251881Speter
8659251881Speter          /* FILE_EXTERNAL flag only on op_depth 0. */
8660251881Speter          child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8661251881Speter                                                                      22);
8662251881Speter        }
8663251881Speter      else
8664251881Speter        {
8665251881Speter          const char *moved_to_relpath;
8666251881Speter
8667251881Speter          child_item->nr_layers++;
8668251881Speter          child_item->info.have_more_work = (child_item->nr_layers > 1);
8669251881Speter
8670251881Speter          /* Moved-to can only exist at op_depth > 0. */
8671251881Speter          /* ### Should we really do this for every layer where op_depth > 0
8672251881Speter                 in undefined order? */
8673251881Speter          moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8674251881Speter          if (moved_to_relpath)
8675251881Speter            child_item->info.moved_to_abspath =
8676251881Speter              svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool);
8677251881Speter
8678251881Speter          /* Moved-here can only exist at op_depth > 0. */
8679251881Speter          /* ### Should we really do this for every layer where op_depth > 0
8680251881Speter                 in undefined order? */
8681251881Speter          child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8682251881Speter        }
8683251881Speter
8684251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8685251881Speter    }
8686251881Speter
8687251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
8688251881Speter
8689251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8690251881Speter                                    STMT_SELECT_ACTUAL_CHILDREN_INFO));
8691251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8692251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8693251881Speter
8694251881Speter  while (have_row)
8695251881Speter    {
8696251881Speter      struct read_children_info_item_t *child_item;
8697251881Speter      struct svn_wc__db_info_t *child;
8698251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8699251881Speter      const char *name = svn_relpath_basename(child_relpath, NULL);
8700251881Speter
8701251881Speter      child_item = svn_hash_gets(nodes, name);
8702251881Speter      if (!child_item)
8703251881Speter        {
8704251881Speter          child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8705251881Speter          child_item->info.status = svn_wc__db_status_not_present;
8706251881Speter        }
8707251881Speter
8708251881Speter      child = &child_item->info;
8709251881Speter
8710251881Speter      child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
8711251881Speter
8712251881Speter      child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
8713251881Speter#ifdef HAVE_SYMLINK
8714251881Speter      if (child->props_mod)
8715251881Speter        {
8716251881Speter          svn_error_t *err;
8717251881Speter          apr_hash_t *properties;
8718251881Speter
8719251881Speter          err = svn_sqlite__column_properties(&properties, stmt, 2,
8720251881Speter                                              scratch_pool, scratch_pool);
8721251881Speter          if (err)
8722251881Speter            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8723251881Speter          child->special = (NULL != svn_hash_gets(properties,
8724251881Speter                                                  SVN_PROP_SPECIAL));
8725251881Speter        }
8726251881Speter#endif
8727251881Speter
8728251881Speter      child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
8729251881Speter
8730251881Speter      if (child->conflicted)
8731251881Speter        svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
8732251881Speter
8733251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8734251881Speter    }
8735251881Speter
8736251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
8737251881Speter
8738251881Speter  return SVN_NO_ERROR;
8739251881Speter}
8740251881Speter
8741251881Spetersvn_error_t *
8742251881Spetersvn_wc__db_read_children_info(apr_hash_t **nodes,
8743251881Speter                              apr_hash_t **conflicts,
8744251881Speter                              svn_wc__db_t *db,
8745251881Speter                              const char *dir_abspath,
8746251881Speter                              apr_pool_t *result_pool,
8747251881Speter                              apr_pool_t *scratch_pool)
8748251881Speter{
8749251881Speter  svn_wc__db_wcroot_t *wcroot;
8750251881Speter  const char *dir_relpath;
8751251881Speter
8752251881Speter  *conflicts = apr_hash_make(result_pool);
8753251881Speter  *nodes = apr_hash_make(result_pool);
8754251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
8755251881Speter
8756251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
8757251881Speter                                                dir_abspath,
8758251881Speter                                                scratch_pool, scratch_pool));
8759251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8760251881Speter
8761251881Speter  SVN_WC__DB_WITH_TXN(
8762251881Speter    read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
8763251881Speter                       result_pool, scratch_pool),
8764251881Speter    wcroot);
8765251881Speter
8766251881Speter  return SVN_NO_ERROR;
8767251881Speter}
8768251881Speter
8769251881Spetersvn_error_t *
8770251881Spetersvn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
8771251881Speter                              svn_node_kind_t *kind,
8772251881Speter                              svn_revnum_t *changed_rev,
8773251881Speter                              apr_time_t *changed_date,
8774251881Speter                              const char **changed_author,
8775251881Speter                              svn_depth_t *depth,  /* dirs only */
8776251881Speter                              const svn_checksum_t **checksum, /* files only */
8777251881Speter                              const char **target, /* symlinks only */
8778251881Speter                              svn_boolean_t *had_props,
8779251881Speter                              apr_hash_t **props,
8780251881Speter                              svn_wc__db_t *db,
8781251881Speter                              const char *local_abspath,
8782251881Speter                              apr_pool_t *result_pool,
8783251881Speter                              apr_pool_t *scratch_pool)
8784251881Speter{
8785251881Speter  svn_wc__db_wcroot_t *wcroot;
8786251881Speter  const char *local_relpath;
8787251881Speter  svn_sqlite__stmt_t *stmt;
8788251881Speter  svn_boolean_t have_row;
8789251881Speter  svn_error_t *err = NULL;
8790251881Speter  int op_depth;
8791251881Speter  svn_wc__db_status_t raw_status;
8792251881Speter  svn_node_kind_t node_kind;
8793251881Speter
8794251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8795251881Speter
8796251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8797251881Speter                                                local_abspath,
8798251881Speter                                                scratch_pool, scratch_pool));
8799251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8800251881Speter
8801251881Speter  /* Obtain the most likely to exist record first, to make sure we don't
8802251881Speter     have to obtain the SQLite read-lock multiple times */
8803251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8804251881Speter                                    STMT_SELECT_NODE_INFO));
8805251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8806251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8807251881Speter
8808251881Speter  if (!have_row)
8809251881Speter    {
8810251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8811251881Speter                               svn_sqlite__reset(stmt),
8812251881Speter                               _("The node '%s' was not found."),
8813251881Speter                               path_for_error_message(wcroot,
8814251881Speter                                                      local_relpath,
8815251881Speter                                                      scratch_pool));
8816251881Speter    }
8817251881Speter
8818251881Speter  op_depth = svn_sqlite__column_int(stmt, 0);
8819251881Speter  raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
8820251881Speter
8821251881Speter  if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
8822251881Speter    {
8823251881Speter      SVN_ERR(svn_sqlite__step_row(stmt));
8824251881Speter
8825251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
8826251881Speter      raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
8827251881Speter    }
8828251881Speter
8829251881Speter  node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
8830251881Speter
8831251881Speter  if (status)
8832251881Speter    {
8833251881Speter      if (op_depth > 0)
8834251881Speter        {
8835251881Speter          err = svn_error_compose_create(err,
8836251881Speter                                         convert_to_working_status(
8837251881Speter                                                    status,
8838251881Speter                                                    raw_status));
8839251881Speter        }
8840251881Speter      else
8841251881Speter        *status = raw_status;
8842251881Speter    }
8843251881Speter  if (kind)
8844251881Speter    {
8845251881Speter      *kind = node_kind;
8846251881Speter    }
8847251881Speter  if (changed_rev)
8848251881Speter    {
8849251881Speter      *changed_rev = svn_sqlite__column_revnum(stmt, 8);
8850251881Speter    }
8851251881Speter  if (changed_date)
8852251881Speter    {
8853251881Speter      *changed_date = svn_sqlite__column_int64(stmt, 9);
8854251881Speter    }
8855251881Speter  if (changed_author)
8856251881Speter    {
8857251881Speter      *changed_author = svn_sqlite__column_text(stmt, 10,
8858251881Speter                                                result_pool);
8859251881Speter    }
8860251881Speter  if (depth)
8861251881Speter    {
8862251881Speter      if (node_kind != svn_node_dir)
8863251881Speter        {
8864251881Speter          *depth = svn_depth_unknown;
8865251881Speter        }
8866251881Speter      else
8867251881Speter        {
8868251881Speter          *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8869251881Speter                                                 svn_depth_unknown);
8870251881Speter        }
8871251881Speter    }
8872251881Speter  if (checksum)
8873251881Speter    {
8874251881Speter      if (node_kind != svn_node_file)
8875251881Speter        {
8876251881Speter          *checksum = NULL;
8877251881Speter        }
8878251881Speter      else
8879251881Speter        {
8880251881Speter          svn_error_t *err2;
8881251881Speter          err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
8882251881Speter
8883251881Speter          if (err2 != NULL)
8884251881Speter            {
8885251881Speter              if (err)
8886251881Speter                err = svn_error_compose_create(
8887251881Speter                         err,
8888251881Speter                         svn_error_createf(
8889251881Speter                               err->apr_err, err2,
8890251881Speter                              _("The node '%s' has a corrupt checksum value."),
8891251881Speter                              path_for_error_message(wcroot, local_relpath,
8892251881Speter                                                     scratch_pool)));
8893251881Speter              else
8894251881Speter                err = err2;
8895251881Speter            }
8896251881Speter        }
8897251881Speter    }
8898251881Speter  if (target)
8899251881Speter    {
8900251881Speter      if (node_kind != svn_node_symlink)
8901251881Speter        *target = NULL;
8902251881Speter      else
8903251881Speter        *target = svn_sqlite__column_text(stmt, 12, result_pool);
8904251881Speter    }
8905251881Speter  if (had_props)
8906251881Speter    {
8907251881Speter      *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8908251881Speter    }
8909251881Speter  if (props)
8910251881Speter    {
8911251881Speter      if (raw_status == svn_wc__db_status_normal
8912251881Speter          || raw_status == svn_wc__db_status_incomplete)
8913251881Speter        {
8914251881Speter          SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
8915251881Speter                                                result_pool, scratch_pool));
8916251881Speter          if (*props == NULL)
8917251881Speter            *props = apr_hash_make(result_pool);
8918251881Speter        }
8919251881Speter      else
8920251881Speter        {
8921251881Speter          assert(svn_sqlite__column_is_null(stmt, 14));
8922251881Speter          *props = NULL;
8923251881Speter        }
8924251881Speter    }
8925251881Speter
8926251881Speter  return svn_error_trace(
8927251881Speter            svn_error_compose_create(err,
8928251881Speter                                     svn_sqlite__reset(stmt)));
8929251881Speter}
8930251881Speter
8931251881Spetersvn_error_t *
8932251881Spetersvn_wc__db_read_children_walker_info(apr_hash_t **nodes,
8933251881Speter                                     svn_wc__db_t *db,
8934251881Speter                                     const char *dir_abspath,
8935251881Speter                                     apr_pool_t *result_pool,
8936251881Speter                                     apr_pool_t *scratch_pool)
8937251881Speter{
8938251881Speter  svn_wc__db_wcroot_t *wcroot;
8939251881Speter  const char *dir_relpath;
8940251881Speter  svn_sqlite__stmt_t *stmt;
8941251881Speter  svn_boolean_t have_row;
8942251881Speter
8943251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
8944251881Speter
8945251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
8946251881Speter                                             dir_abspath,
8947251881Speter                                             scratch_pool, scratch_pool));
8948251881Speter  VERIFY_USABLE_WCROOT(wcroot);
8949251881Speter
8950251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8951251881Speter                                    STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
8952251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8953251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
8954251881Speter
8955251881Speter  *nodes = apr_hash_make(result_pool);
8956251881Speter  while (have_row)
8957251881Speter    {
8958251881Speter      struct svn_wc__db_walker_info_t *child;
8959251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8960251881Speter      const char *name = svn_relpath_basename(child_relpath, NULL);
8961251881Speter      int op_depth = svn_sqlite__column_int(stmt, 1);
8962251881Speter      svn_error_t *err;
8963251881Speter
8964251881Speter      child = apr_palloc(result_pool, sizeof(*child));
8965251881Speter      child->status = svn_sqlite__column_token(stmt, 2, presence_map);
8966251881Speter      if (op_depth > 0)
8967251881Speter        {
8968251881Speter          err = convert_to_working_status(&child->status, child->status);
8969251881Speter          if (err)
8970251881Speter            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8971251881Speter        }
8972251881Speter      child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
8973251881Speter      svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
8974251881Speter
8975251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
8976251881Speter    }
8977251881Speter
8978251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
8979251881Speter
8980251881Speter  return SVN_NO_ERROR;
8981251881Speter}
8982251881Speter
8983251881Spetersvn_error_t *
8984251881Spetersvn_wc__db_read_node_install_info(const char **wcroot_abspath,
8985251881Speter                                  const svn_checksum_t **sha1_checksum,
8986251881Speter                                  apr_hash_t **pristine_props,
8987251881Speter                                  apr_time_t *changed_date,
8988251881Speter                                  svn_wc__db_t *db,
8989251881Speter                                  const char *local_abspath,
8990251881Speter                                  const char *wri_abspath,
8991251881Speter                                  apr_pool_t *result_pool,
8992251881Speter                                  apr_pool_t *scratch_pool)
8993251881Speter{
8994251881Speter  svn_wc__db_wcroot_t *wcroot;
8995251881Speter  const char *local_relpath;
8996251881Speter  svn_sqlite__stmt_t *stmt;
8997251881Speter  svn_error_t *err = NULL;
8998251881Speter  svn_boolean_t have_row;
8999251881Speter
9000251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9001251881Speter
9002251881Speter  if (!wri_abspath)
9003251881Speter    wri_abspath = local_abspath;
9004251881Speter
9005251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9006251881Speter                              wri_abspath, scratch_pool, scratch_pool));
9007251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9008251881Speter
9009251881Speter  if (local_abspath != wri_abspath
9010251881Speter      && strcmp(local_abspath, wri_abspath))
9011251881Speter    {
9012251881Speter      if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9013251881Speter        return svn_error_createf(
9014251881Speter                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9015251881Speter                    _("The node '%s' is not in working copy '%s'"),
9016251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool),
9017251881Speter                    svn_dirent_local_style(wcroot->abspath, scratch_pool));
9018251881Speter
9019251881Speter      local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9020251881Speter    }
9021251881Speter
9022251881Speter  if (wcroot_abspath != NULL)
9023251881Speter    *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9024251881Speter
9025251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9026251881Speter                                    STMT_SELECT_NODE_INFO));
9027251881Speter
9028251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9029251881Speter
9030251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9031251881Speter
9032251881Speter  if (have_row)
9033251881Speter    {
9034251881Speter      if (!err && sha1_checksum)
9035251881Speter        err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9036251881Speter
9037251881Speter      if (!err && pristine_props)
9038251881Speter        {
9039251881Speter          err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9040251881Speter                                              result_pool, scratch_pool);
9041251881Speter          /* Null means no props (assuming presence normal or incomplete). */
9042251881Speter          if (*pristine_props == NULL)
9043251881Speter            *pristine_props = apr_hash_make(result_pool);
9044251881Speter        }
9045251881Speter
9046251881Speter      if (changed_date)
9047251881Speter        *changed_date = svn_sqlite__column_int64(stmt, 9);
9048251881Speter    }
9049251881Speter  else
9050251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9051251881Speter                             svn_sqlite__reset(stmt),
9052251881Speter                             _("The node '%s' is not installable"),
9053251881Speter                             svn_dirent_local_style(local_abspath,
9054251881Speter                                                    scratch_pool));
9055251881Speter
9056251881Speter  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9057251881Speter
9058251881Speter  return SVN_NO_ERROR;
9059251881Speter}
9060251881Speter
9061251881Speter
9062251881Speter
9063251881Speter/* The body of svn_wc__db_read_url().
9064251881Speter */
9065251881Speterstatic svn_error_t *
9066251881Speterread_url_txn(const char **url,
9067251881Speter             svn_wc__db_wcroot_t *wcroot,
9068251881Speter             const char *local_relpath,
9069251881Speter             apr_pool_t *result_pool,
9070251881Speter             apr_pool_t *scratch_pool)
9071251881Speter{
9072251881Speter  svn_wc__db_status_t status;
9073251881Speter  const char *repos_relpath;
9074251881Speter  const char *repos_root_url;
9075251881Speter  apr_int64_t repos_id;
9076251881Speter  svn_boolean_t have_base;
9077251881Speter
9078251881Speter  SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9079251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9080251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9081251881Speter                    &have_base, NULL, NULL,
9082251881Speter                    wcroot, local_relpath, scratch_pool, scratch_pool));
9083251881Speter
9084251881Speter  if (repos_relpath == NULL)
9085251881Speter    {
9086251881Speter      if (status == svn_wc__db_status_added)
9087251881Speter        {
9088251881Speter          SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9089251881Speter                                NULL, NULL, NULL, NULL, NULL,
9090251881Speter                                wcroot, local_relpath,
9091251881Speter                                scratch_pool, scratch_pool));
9092251881Speter        }
9093251881Speter      else if (status == svn_wc__db_status_deleted)
9094251881Speter        {
9095251881Speter          const char *base_del_relpath;
9096251881Speter          const char *work_del_relpath;
9097251881Speter
9098251881Speter          SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9099251881Speter                                    &work_del_relpath,
9100251881Speter                                    NULL, wcroot,
9101251881Speter                                    local_relpath,
9102251881Speter                                    scratch_pool,
9103251881Speter                                    scratch_pool));
9104251881Speter
9105251881Speter          if (base_del_relpath)
9106251881Speter            {
9107251881Speter              SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9108251881Speter                                                        &repos_relpath,
9109251881Speter                                                        &repos_id,
9110251881Speter                                                        NULL, NULL, NULL,
9111251881Speter                                                        NULL, NULL, NULL,
9112251881Speter                                                        NULL, NULL, NULL, NULL,
9113251881Speter                                                        wcroot,
9114251881Speter                                                        base_del_relpath,
9115251881Speter                                                        scratch_pool,
9116251881Speter                                                        scratch_pool));
9117251881Speter
9118251881Speter              repos_relpath = svn_relpath_join(
9119251881Speter                                    repos_relpath,
9120251881Speter                                    svn_dirent_skip_ancestor(base_del_relpath,
9121251881Speter                                                             local_relpath),
9122251881Speter                                    scratch_pool);
9123251881Speter            }
9124251881Speter          else
9125251881Speter            {
9126251881Speter              /* The parent of the WORKING delete, must be an addition */
9127251881Speter              const char *work_relpath = NULL;
9128251881Speter
9129251881Speter              /* work_del_relpath should not be NULL. However, we have
9130251881Speter               * observed instances where that assumption was not met.
9131251881Speter               * Bail out in that case instead of crashing with a segfault.
9132251881Speter               */
9133251881Speter              SVN_ERR_ASSERT(work_del_relpath != NULL);
9134251881Speter              work_relpath = svn_relpath_dirname(work_del_relpath,
9135251881Speter                                                 scratch_pool);
9136251881Speter
9137251881Speter              SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9138251881Speter                                    NULL, NULL, NULL, NULL, NULL, NULL,
9139251881Speter                                    wcroot, work_relpath,
9140251881Speter                                    scratch_pool, scratch_pool));
9141251881Speter
9142251881Speter              repos_relpath = svn_relpath_join(
9143251881Speter                                    repos_relpath,
9144251881Speter                                    svn_dirent_skip_ancestor(work_relpath,
9145251881Speter                                                             local_relpath),
9146251881Speter                                    scratch_pool);
9147251881Speter            }
9148251881Speter        }
9149251881Speter      else if (status == svn_wc__db_status_excluded)
9150251881Speter        {
9151251881Speter          const char *parent_relpath;
9152251881Speter          const char *name;
9153251881Speter          const char *url2;
9154251881Speter
9155251881Speter          /* Set 'url' to the *full URL* of the parent WC dir,
9156251881Speter           * and 'name' to the *single path component* that is the
9157251881Speter           * basename of this WC directory, so that joining them will result
9158251881Speter           * in the correct full URL. */
9159251881Speter          svn_relpath_split(&parent_relpath, &name, local_relpath,
9160251881Speter                            scratch_pool);
9161251881Speter          SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9162251881Speter                               scratch_pool, scratch_pool));
9163251881Speter
9164251881Speter          *url = svn_path_url_add_component2(url2, name, result_pool);
9165251881Speter
9166251881Speter          return SVN_NO_ERROR;
9167251881Speter        }
9168251881Speter      else
9169251881Speter        {
9170251881Speter          /* All working statee are explicitly handled and all base statee
9171251881Speter             have a repos_relpath */
9172251881Speter          SVN_ERR_MALFUNCTION();
9173251881Speter        }
9174251881Speter    }
9175251881Speter
9176251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9177251881Speter                                      repos_id, scratch_pool));
9178251881Speter
9179251881Speter  SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9180251881Speter  *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9181251881Speter                                     result_pool);
9182251881Speter
9183251881Speter  return SVN_NO_ERROR;
9184251881Speter}
9185251881Speter
9186251881Speter
9187251881Spetersvn_error_t *
9188251881Spetersvn_wc__db_read_url(const char **url,
9189251881Speter                    svn_wc__db_t *db,
9190251881Speter                    const char *local_abspath,
9191251881Speter                    apr_pool_t *result_pool,
9192251881Speter                    apr_pool_t *scratch_pool)
9193251881Speter{
9194251881Speter  svn_wc__db_wcroot_t *wcroot;
9195251881Speter  const char *local_relpath;
9196251881Speter
9197251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9198251881Speter
9199251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9200251881Speter                                                local_abspath,
9201251881Speter                                                scratch_pool, scratch_pool));
9202251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9203251881Speter
9204251881Speter  SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9205251881Speter                                   result_pool, scratch_pool),
9206251881Speter                      wcroot);
9207251881Speter
9208251881Speter  return SVN_NO_ERROR;
9209251881Speter}
9210251881Speter
9211251881Speter
9212251881Speter/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9213251881Speter   a hash table mapping <tt>char *</tt> names onto svn_string_t *
9214251881Speter   values for any properties of immediate or recursive child nodes of
9215251881Speter   LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9216251881Speter   If FILES_ONLY is true, only report properties for file child nodes.
9217251881Speter   Check for cancellation between calls of RECEIVER_FUNC.
9218251881Speter*/
9219251881Spetertypedef struct cache_props_baton_t
9220251881Speter{
9221251881Speter  svn_depth_t depth;
9222251881Speter  svn_boolean_t pristine;
9223251881Speter  const apr_array_header_t *changelists;
9224251881Speter  svn_cancel_func_t cancel_func;
9225251881Speter  void *cancel_baton;
9226251881Speter} cache_props_baton_t;
9227251881Speter
9228251881Speter
9229251881Speterstatic svn_error_t *
9230251881Spetercache_props_recursive(void *cb_baton,
9231251881Speter                      svn_wc__db_wcroot_t *wcroot,
9232251881Speter                      const char *local_relpath,
9233251881Speter                      apr_pool_t *scratch_pool)
9234251881Speter{
9235251881Speter  cache_props_baton_t *baton = cb_baton;
9236251881Speter  svn_sqlite__stmt_t *stmt;
9237251881Speter  int stmt_idx;
9238251881Speter
9239251881Speter  SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9240251881Speter                                baton->changelists, scratch_pool));
9241251881Speter
9242251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9243251881Speter                                      STMT_CREATE_TARGET_PROP_CACHE));
9244251881Speter
9245251881Speter  if (baton->pristine)
9246251881Speter    stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9247251881Speter  else
9248251881Speter    stmt_idx = STMT_CACHE_TARGET_PROPS;
9249251881Speter
9250251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9251251881Speter  SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9252251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
9253251881Speter
9254251881Speter  return SVN_NO_ERROR;
9255251881Speter}
9256251881Speter
9257251881Speter
9258251881Spetersvn_error_t *
9259251881Spetersvn_wc__db_read_props_streamily(svn_wc__db_t *db,
9260251881Speter                                const char *local_abspath,
9261251881Speter                                svn_depth_t depth,
9262251881Speter                                svn_boolean_t pristine,
9263251881Speter                                const apr_array_header_t *changelists,
9264251881Speter                                svn_wc__proplist_receiver_t receiver_func,
9265251881Speter                                void *receiver_baton,
9266251881Speter                                svn_cancel_func_t cancel_func,
9267251881Speter                                void *cancel_baton,
9268251881Speter                                apr_pool_t *scratch_pool)
9269251881Speter{
9270251881Speter  svn_wc__db_wcroot_t *wcroot;
9271251881Speter  const char *local_relpath;
9272251881Speter  svn_sqlite__stmt_t *stmt;
9273251881Speter  cache_props_baton_t baton;
9274251881Speter  svn_boolean_t have_row;
9275251881Speter  apr_pool_t *iterpool;
9276251881Speter  svn_error_t *err = NULL;
9277251881Speter
9278251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9279251881Speter  SVN_ERR_ASSERT(receiver_func);
9280251881Speter  SVN_ERR_ASSERT((depth == svn_depth_files) ||
9281251881Speter                 (depth == svn_depth_immediates) ||
9282251881Speter                 (depth == svn_depth_infinity));
9283251881Speter
9284251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9285251881Speter                                                db, local_abspath,
9286251881Speter                                                scratch_pool, scratch_pool));
9287251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9288251881Speter
9289251881Speter  baton.depth = depth;
9290251881Speter  baton.pristine = pristine;
9291251881Speter  baton.changelists = changelists;
9292251881Speter  baton.cancel_func = cancel_func;
9293251881Speter  baton.cancel_baton = cancel_baton;
9294251881Speter
9295251881Speter  SVN_ERR(with_finalization(wcroot, local_relpath,
9296251881Speter                            cache_props_recursive, &baton,
9297251881Speter                            NULL, NULL,
9298251881Speter                            cancel_func, cancel_baton,
9299251881Speter                            NULL, NULL,
9300251881Speter                            STMT_DROP_TARGETS_LIST,
9301251881Speter                            scratch_pool));
9302251881Speter
9303251881Speter  iterpool = svn_pool_create(scratch_pool);
9304251881Speter
9305251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9306251881Speter                                    STMT_SELECT_ALL_TARGET_PROP_CACHE));
9307251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9308251881Speter  while (!err && have_row)
9309251881Speter    {
9310251881Speter      apr_hash_t *props;
9311251881Speter
9312251881Speter      svn_pool_clear(iterpool);
9313251881Speter
9314251881Speter      SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9315251881Speter                                            iterpool));
9316251881Speter
9317251881Speter      /* see if someone wants to cancel this operation. */
9318251881Speter      if (cancel_func)
9319251881Speter        err = cancel_func(cancel_baton);
9320251881Speter
9321251881Speter      if (!err && props && apr_hash_count(props) != 0)
9322251881Speter        {
9323251881Speter          const char *child_relpath;
9324251881Speter          const char *child_abspath;
9325251881Speter
9326251881Speter          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9327251881Speter          child_abspath = svn_dirent_join(wcroot->abspath,
9328251881Speter                                          child_relpath, iterpool);
9329251881Speter
9330251881Speter          err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9331251881Speter        }
9332251881Speter
9333251881Speter      err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9334251881Speter    }
9335251881Speter
9336251881Speter  err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9337251881Speter
9338251881Speter  svn_pool_destroy(iterpool);
9339251881Speter
9340251881Speter  SVN_ERR(svn_error_compose_create(
9341251881Speter                    err,
9342251881Speter                    svn_sqlite__exec_statements(wcroot->sdb,
9343251881Speter                                                STMT_DROP_TARGET_PROP_CACHE)));
9344251881Speter  return SVN_NO_ERROR;
9345251881Speter}
9346251881Speter
9347251881Speter
9348251881Speter/* Helper for svn_wc__db_read_props().
9349251881Speter */
9350251881Speterstatic svn_error_t *
9351251881Speterdb_read_props(apr_hash_t **props,
9352251881Speter              svn_wc__db_wcroot_t *wcroot,
9353251881Speter              const char *local_relpath,
9354251881Speter              apr_pool_t *result_pool,
9355251881Speter              apr_pool_t *scratch_pool)
9356251881Speter{
9357251881Speter  svn_sqlite__stmt_t *stmt;
9358251881Speter  svn_boolean_t have_row;
9359251881Speter  svn_error_t *err = NULL;
9360251881Speter
9361251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9362251881Speter                                    STMT_SELECT_ACTUAL_PROPS));
9363251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9364251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9365251881Speter
9366251881Speter  if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9367251881Speter    {
9368251881Speter      err = svn_sqlite__column_properties(props, stmt, 0,
9369251881Speter                                          result_pool, scratch_pool);
9370251881Speter    }
9371251881Speter  else
9372251881Speter    have_row = FALSE;
9373251881Speter
9374251881Speter  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9375251881Speter
9376251881Speter  if (have_row)
9377251881Speter    return SVN_NO_ERROR;
9378251881Speter
9379251881Speter  /* No local changes. Return the pristine props for this node.  */
9380251881Speter  SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9381251881Speter                                 result_pool, scratch_pool));
9382251881Speter  if (*props == NULL)
9383251881Speter    {
9384251881Speter      /* Pristine properties are not defined for this node.
9385251881Speter         ### we need to determine whether this node is in a state that
9386251881Speter         ### allows for ACTUAL properties (ie. not deleted). for now,
9387251881Speter         ### just say all nodes, no matter the state, have at least an
9388251881Speter         ### empty set of props.  */
9389251881Speter      *props = apr_hash_make(result_pool);
9390251881Speter    }
9391251881Speter
9392251881Speter  return SVN_NO_ERROR;
9393251881Speter}
9394251881Speter
9395251881Speter
9396251881Spetersvn_error_t *
9397251881Spetersvn_wc__db_read_props(apr_hash_t **props,
9398251881Speter                      svn_wc__db_t *db,
9399251881Speter                      const char *local_abspath,
9400251881Speter                      apr_pool_t *result_pool,
9401251881Speter                      apr_pool_t *scratch_pool)
9402251881Speter{
9403251881Speter  svn_wc__db_wcroot_t *wcroot;
9404251881Speter  const char *local_relpath;
9405251881Speter
9406251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9407251881Speter
9408251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9409251881Speter                              local_abspath, scratch_pool, scratch_pool));
9410251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9411251881Speter
9412251881Speter  SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9413251881Speter                                    result_pool, scratch_pool),
9414251881Speter                      wcroot);
9415251881Speter
9416251881Speter  return SVN_NO_ERROR;
9417251881Speter}
9418251881Speter
9419251881Speter
9420251881Speterstatic svn_error_t *
9421251881Speterdb_read_pristine_props(apr_hash_t **props,
9422251881Speter                       svn_wc__db_wcroot_t *wcroot,
9423251881Speter                       const char *local_relpath,
9424251881Speter                       svn_boolean_t deleted_ok,
9425251881Speter                       apr_pool_t *result_pool,
9426251881Speter                       apr_pool_t *scratch_pool)
9427251881Speter{
9428251881Speter  svn_sqlite__stmt_t *stmt;
9429251881Speter  svn_boolean_t have_row;
9430251881Speter  svn_wc__db_status_t presence;
9431251881Speter
9432251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9433251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9434251881Speter
9435251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9436251881Speter
9437251881Speter  if (!have_row)
9438251881Speter    {
9439251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9440251881Speter                               svn_sqlite__reset(stmt),
9441251881Speter                               _("The node '%s' was not found."),
9442251881Speter                               path_for_error_message(wcroot,
9443251881Speter                                                      local_relpath,
9444251881Speter                                                      scratch_pool));
9445251881Speter    }
9446251881Speter
9447251881Speter
9448251881Speter  /* Examine the presence: */
9449251881Speter  presence = svn_sqlite__column_token(stmt, 1, presence_map);
9450251881Speter
9451251881Speter  /* For "base-deleted", it is obvious the pristine props are located
9452251881Speter     below the current node. Fetch the NODE from the next record. */
9453251881Speter  if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9454251881Speter    {
9455251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9456251881Speter
9457251881Speter      SVN_ERR_ASSERT(have_row);
9458251881Speter
9459251881Speter      presence = svn_sqlite__column_token(stmt, 1, presence_map);
9460251881Speter    }
9461251881Speter
9462251881Speter  /* normal or copied: Fetch properties (during update we want
9463251881Speter     properties for incomplete as well) */
9464251881Speter  if (presence == svn_wc__db_status_normal
9465251881Speter      || presence == svn_wc__db_status_incomplete)
9466251881Speter    {
9467251881Speter      svn_error_t *err;
9468251881Speter
9469251881Speter      err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9470251881Speter                                          scratch_pool);
9471251881Speter      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9472251881Speter
9473251881Speter      if (!*props)
9474251881Speter        *props = apr_hash_make(result_pool);
9475251881Speter
9476251881Speter      return SVN_NO_ERROR;
9477251881Speter    }
9478251881Speter
9479251881Speter  return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9480251881Speter                           svn_sqlite__reset(stmt),
9481251881Speter                           _("The node '%s' has a status that"
9482251881Speter                             " has no properties."),
9483251881Speter                           path_for_error_message(wcroot,
9484251881Speter                                                  local_relpath,
9485251881Speter                                                  scratch_pool));
9486251881Speter}
9487251881Speter
9488251881Speter
9489251881Spetersvn_error_t *
9490251881Spetersvn_wc__db_read_pristine_props(apr_hash_t **props,
9491251881Speter                               svn_wc__db_t *db,
9492251881Speter                               const char *local_abspath,
9493251881Speter                               apr_pool_t *result_pool,
9494251881Speter                               apr_pool_t *scratch_pool)
9495251881Speter{
9496251881Speter  svn_wc__db_wcroot_t *wcroot;
9497251881Speter  const char *local_relpath;
9498251881Speter
9499251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9500251881Speter
9501251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9502251881Speter                              local_abspath, scratch_pool, scratch_pool));
9503251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9504251881Speter
9505251881Speter  SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
9506251881Speter                                 result_pool, scratch_pool));
9507251881Speter  return SVN_NO_ERROR;
9508251881Speter}
9509251881Speter
9510251881Spetersvn_error_t *
9511251881Spetersvn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
9512251881Speter                                   svn_wc__db_t *db,
9513251881Speter                                   const char *local_abspath,
9514251881Speter                                   const char *propname,
9515251881Speter                                   apr_pool_t *result_pool,
9516251881Speter                                   apr_pool_t *scratch_pool)
9517251881Speter{
9518251881Speter  svn_wc__db_wcroot_t *wcroot;
9519251881Speter  const char *local_relpath;
9520251881Speter  svn_sqlite__stmt_t *stmt;
9521251881Speter  svn_boolean_t have_row;
9522251881Speter  apr_pool_t *iterpool;
9523251881Speter
9524251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9525251881Speter
9526251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9527251881Speter                              local_abspath, scratch_pool, scratch_pool));
9528251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9529251881Speter
9530251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9531251881Speter                                    STMT_SELECT_CURRENT_PROPS_RECURSIVE));
9532251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9533251881Speter
9534251881Speter  *values = apr_hash_make(result_pool);
9535251881Speter
9536251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9537251881Speter  iterpool = svn_pool_create(scratch_pool);
9538251881Speter  while (have_row)
9539251881Speter  {
9540251881Speter    apr_hash_t *node_props;
9541251881Speter    svn_string_t *value;
9542251881Speter
9543251881Speter    svn_pool_clear(iterpool);
9544251881Speter
9545251881Speter    SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
9546251881Speter                                          iterpool, iterpool));
9547251881Speter
9548251881Speter    value = (node_props
9549251881Speter                ? svn_hash_gets(node_props, propname)
9550251881Speter                : NULL);
9551251881Speter
9552251881Speter    if (value)
9553251881Speter      {
9554251881Speter        svn_hash_sets(*values,
9555251881Speter                      svn_dirent_join(wcroot->abspath,
9556251881Speter                                      svn_sqlite__column_text(stmt, 1, NULL),
9557251881Speter                                      result_pool),
9558251881Speter                      svn_string_dup(value, result_pool));
9559251881Speter      }
9560251881Speter
9561251881Speter    SVN_ERR(svn_sqlite__step(&have_row, stmt));
9562251881Speter  }
9563251881Speter
9564251881Speter  svn_pool_destroy(iterpool);
9565251881Speter
9566251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
9567251881Speter}
9568251881Speter
9569251881Speter/* The body of svn_wc__db_read_cached_iprops(). */
9570251881Speterstatic svn_error_t *
9571251881Speterdb_read_cached_iprops(apr_array_header_t **iprops,
9572251881Speter                      svn_wc__db_wcroot_t *wcroot,
9573251881Speter                      const char *local_relpath,
9574251881Speter                      apr_pool_t *result_pool,
9575251881Speter                      apr_pool_t *scratch_pool)
9576251881Speter{
9577251881Speter  svn_sqlite__stmt_t *stmt;
9578251881Speter  svn_boolean_t have_row;
9579251881Speter
9580251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
9581251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9582251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9583251881Speter
9584251881Speter  if (!have_row)
9585251881Speter    {
9586251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9587251881Speter                               svn_sqlite__reset(stmt),
9588251881Speter                               _("The node '%s' was not found."),
9589251881Speter                               path_for_error_message(wcroot, local_relpath,
9590251881Speter                                                      scratch_pool));
9591251881Speter    }
9592251881Speter
9593251881Speter  SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
9594251881Speter                                    result_pool, scratch_pool));
9595251881Speter
9596251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
9597251881Speter
9598251881Speter  return SVN_NO_ERROR;
9599251881Speter}
9600251881Speter
9601251881Spetersvn_error_t *
9602251881Spetersvn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
9603251881Speter                              svn_wc__db_t *db,
9604251881Speter                              const char *local_abspath,
9605251881Speter                              apr_pool_t *result_pool,
9606251881Speter                              apr_pool_t *scratch_pool)
9607251881Speter{
9608251881Speter  svn_wc__db_wcroot_t *wcroot;
9609251881Speter  const char *local_relpath;
9610251881Speter
9611251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9612251881Speter
9613251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9614251881Speter                                                db, local_abspath,
9615251881Speter                                                scratch_pool, scratch_pool));
9616251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9617251881Speter
9618251881Speter  /* Don't use with_txn yet, as we perform just a single transaction */
9619251881Speter  SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
9620251881Speter                                result_pool, scratch_pool));
9621251881Speter
9622251881Speter  if (!*iprops)
9623251881Speter    {
9624251881Speter      *iprops = apr_array_make(result_pool, 0,
9625251881Speter                               sizeof(svn_prop_inherited_item_t *));
9626251881Speter    }
9627251881Speter
9628251881Speter  return SVN_NO_ERROR;
9629251881Speter}
9630251881Speter
9631251881Speter/* Remove all prop name value pairs from PROP_HASH where the property
9632251881Speter   name is not PROPNAME. */
9633251881Speterstatic void
9634251881Speterfilter_unwanted_props(apr_hash_t *prop_hash,
9635251881Speter                      const char * propname,
9636251881Speter                      apr_pool_t *scratch_pool)
9637251881Speter{
9638251881Speter  apr_hash_index_t *hi;
9639251881Speter
9640251881Speter  for (hi = apr_hash_first(scratch_pool, prop_hash);
9641251881Speter       hi;
9642251881Speter       hi = apr_hash_next(hi))
9643251881Speter    {
9644251881Speter      const char *ipropname = svn__apr_hash_index_key(hi);
9645251881Speter
9646251881Speter      if (strcmp(ipropname, propname) != 0)
9647251881Speter        svn_hash_sets(prop_hash, ipropname, NULL);
9648251881Speter    }
9649251881Speter  return;
9650251881Speter}
9651251881Speter
9652251881Speter/* Get the changed properties as stored in the ACTUAL table */
9653251881Speterstatic svn_error_t *
9654251881Speterdb_get_changed_props(apr_hash_t **actual_props,
9655251881Speter                     svn_wc__db_wcroot_t *wcroot,
9656251881Speter                     const char *local_relpath,
9657251881Speter                     apr_pool_t *result_pool,
9658251881Speter                     apr_pool_t *scratch_pool)
9659251881Speter{
9660251881Speter  svn_sqlite__stmt_t *stmt;
9661251881Speter  svn_boolean_t have_row;
9662251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9663251881Speter                                STMT_SELECT_ACTUAL_PROPS));
9664251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9665251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9666251881Speter
9667251881Speter  if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9668251881Speter    SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
9669251881Speter                                          result_pool, scratch_pool));
9670251881Speter  else
9671251881Speter    *actual_props = NULL; /* Cached when we read that record */
9672251881Speter
9673251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
9674251881Speter}
9675251881Speter
9676251881Speter/* The body of svn_wc__db_read_inherited_props().  */
9677251881Speterstatic svn_error_t *
9678251881Speterdb_read_inherited_props(apr_array_header_t **inherited_props,
9679251881Speter                        apr_hash_t **actual_props,
9680251881Speter                        svn_wc__db_wcroot_t *wcroot,
9681251881Speter                        const char *local_relpath,
9682251881Speter                        const char *propname,
9683251881Speter                        apr_pool_t *result_pool,
9684251881Speter                        apr_pool_t *scratch_pool)
9685251881Speter{
9686251881Speter  int i;
9687251881Speter  apr_array_header_t *cached_iprops = NULL;
9688251881Speter  apr_array_header_t *iprops;
9689251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9690251881Speter  svn_sqlite__stmt_t *stmt;
9691251881Speter  const char *relpath;
9692251881Speter  const char *expected_parent_repos_relpath = NULL;
9693251881Speter  const char *parent_relpath;
9694251881Speter
9695251881Speter  iprops = apr_array_make(result_pool, 1,
9696251881Speter                           sizeof(svn_prop_inherited_item_t *));
9697251881Speter  *inherited_props = iprops;
9698251881Speter
9699251881Speter  if (actual_props)
9700251881Speter    *actual_props = NULL;
9701251881Speter
9702251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9703251881Speter                                    STMT_SELECT_NODE_INFO));
9704251881Speter
9705251881Speter  relpath = local_relpath;
9706251881Speter
9707251881Speter  /* Walk up to the root of the WC looking for inherited properties.  When we
9708251881Speter     reach the WC root also check for cached inherited properties. */
9709251881Speter  for (relpath = local_relpath; relpath; relpath = parent_relpath)
9710251881Speter    {
9711251881Speter      svn_boolean_t have_row;
9712251881Speter      int op_depth;
9713251881Speter      svn_wc__db_status_t status;
9714251881Speter      apr_hash_t *node_props;
9715251881Speter
9716251881Speter      parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
9717251881Speter                                  : NULL;
9718251881Speter
9719251881Speter      svn_pool_clear(iterpool);
9720251881Speter
9721251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
9722251881Speter
9723251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9724251881Speter
9725251881Speter      if (!have_row)
9726251881Speter        return svn_error_createf(
9727251881Speter                    SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
9728251881Speter                    _("The node '%s' was not found."),
9729251881Speter                    path_for_error_message(wcroot, relpath,
9730251881Speter                                           scratch_pool));
9731251881Speter
9732251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
9733251881Speter
9734251881Speter      status = svn_sqlite__column_token(stmt, 3, presence_map);
9735251881Speter
9736251881Speter      if (status != svn_wc__db_status_normal
9737251881Speter          && status != svn_wc__db_status_incomplete)
9738251881Speter        return svn_error_createf(
9739251881Speter                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
9740251881Speter                    _("The node '%s' has a status that has no properties."),
9741251881Speter                    path_for_error_message(wcroot, relpath,
9742251881Speter                                           scratch_pool));
9743251881Speter
9744251881Speter      if (op_depth > 0)
9745251881Speter        {
9746251881Speter          /* WORKING node. Nothing to check */
9747251881Speter        }
9748251881Speter      else if (expected_parent_repos_relpath)
9749251881Speter        {
9750251881Speter          const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
9751251881Speter
9752251881Speter          if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
9753251881Speter            {
9754251881Speter              /* The child of this node has a different parent than this node
9755251881Speter                 (It is "switched"), so we can stop here. Note that switched
9756251881Speter                 with the same parent is not interesting for us here. */
9757251881Speter              SVN_ERR(svn_sqlite__reset(stmt));
9758251881Speter              break;
9759251881Speter            }
9760251881Speter
9761251881Speter          expected_parent_repos_relpath =
9762251881Speter              svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
9763251881Speter        }
9764251881Speter      else
9765251881Speter        {
9766251881Speter          const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
9767251881Speter
9768251881Speter          expected_parent_repos_relpath =
9769251881Speter              svn_relpath_dirname(repos_relpath, scratch_pool);
9770251881Speter        }
9771251881Speter
9772251881Speter      if (op_depth == 0
9773251881Speter          && !svn_sqlite__column_is_null(stmt, 16))
9774251881Speter        {
9775251881Speter          /* The node contains a cache. No reason to look further */
9776251881Speter          SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
9777251881Speter                                            result_pool, iterpool));
9778251881Speter
9779251881Speter          parent_relpath = NULL; /* Stop after this */
9780251881Speter        }
9781251881Speter
9782251881Speter      SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
9783251881Speter                                            iterpool, iterpool));
9784251881Speter
9785251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
9786251881Speter
9787251881Speter      /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
9788251881Speter         can inherit properties from it. */
9789251881Speter      if (relpath != local_relpath)
9790251881Speter        {
9791251881Speter          apr_hash_t *changed_props;
9792251881Speter
9793251881Speter          SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
9794251881Speter                                       result_pool, iterpool));
9795251881Speter
9796251881Speter          if (changed_props)
9797251881Speter            node_props = changed_props;
9798251881Speter          else if (node_props)
9799251881Speter            node_props = svn_prop_hash_dup(node_props, result_pool);
9800251881Speter
9801251881Speter          if (node_props && apr_hash_count(node_props))
9802251881Speter            {
9803251881Speter              /* If we only want PROPNAME filter out any other properties. */
9804251881Speter              if (propname)
9805251881Speter                filter_unwanted_props(node_props, propname, iterpool);
9806251881Speter
9807251881Speter              if (apr_hash_count(node_props))
9808251881Speter                {
9809251881Speter                  svn_prop_inherited_item_t *iprop_elt =
9810251881Speter                    apr_pcalloc(result_pool,
9811251881Speter                                sizeof(svn_prop_inherited_item_t));
9812251881Speter                  iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
9813251881Speter                                                           relpath,
9814251881Speter                                                           result_pool);
9815251881Speter
9816251881Speter                  iprop_elt->prop_hash = node_props;
9817251881Speter                  /* Build the output array in depth-first order. */
9818251881Speter                  svn_sort__array_insert(&iprop_elt, iprops, 0);
9819251881Speter                }
9820251881Speter            }
9821251881Speter        }
9822251881Speter      else if (actual_props)
9823251881Speter        {
9824251881Speter          apr_hash_t *changed_props;
9825251881Speter
9826251881Speter          SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
9827251881Speter                                       result_pool, iterpool));
9828251881Speter
9829251881Speter          if (changed_props)
9830251881Speter            *actual_props = changed_props;
9831251881Speter          else if (node_props)
9832251881Speter            *actual_props = svn_prop_hash_dup(node_props, result_pool);
9833251881Speter        }
9834251881Speter    }
9835251881Speter
9836251881Speter  if (cached_iprops)
9837251881Speter    {
9838251881Speter      for (i = cached_iprops->nelts - 1; i >= 0; i--)
9839251881Speter        {
9840251881Speter          svn_prop_inherited_item_t *cached_iprop =
9841251881Speter            APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
9842251881Speter
9843251881Speter          /* An empty property hash in the iprops cache means there are no
9844251881Speter             inherited properties. */
9845251881Speter          if (apr_hash_count(cached_iprop->prop_hash) == 0)
9846251881Speter            continue;
9847251881Speter
9848251881Speter          if (propname)
9849251881Speter            filter_unwanted_props(cached_iprop->prop_hash, propname,
9850251881Speter                                  scratch_pool);
9851251881Speter
9852251881Speter          /* If we didn't filter everything then keep this iprop. */
9853251881Speter          if (apr_hash_count(cached_iprop->prop_hash))
9854251881Speter            svn_sort__array_insert(&cached_iprop, iprops, 0);
9855251881Speter        }
9856251881Speter    }
9857251881Speter
9858251881Speter  if (actual_props && !*actual_props)
9859251881Speter    *actual_props = apr_hash_make(result_pool);
9860251881Speter
9861251881Speter  svn_pool_destroy(iterpool);
9862251881Speter  return SVN_NO_ERROR;
9863251881Speter}
9864251881Speter
9865251881Spetersvn_error_t *
9866251881Spetersvn_wc__db_read_inherited_props(apr_array_header_t **iprops,
9867251881Speter                                apr_hash_t **actual_props,
9868251881Speter                                svn_wc__db_t *db,
9869251881Speter                                const char *local_abspath,
9870251881Speter                                const char *propname,
9871251881Speter                                apr_pool_t *result_pool,
9872251881Speter                                apr_pool_t *scratch_pool)
9873251881Speter{
9874251881Speter  svn_wc__db_wcroot_t *wcroot;
9875251881Speter  const char *local_relpath;
9876251881Speter
9877251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9878251881Speter
9879251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9880251881Speter                                                db, local_abspath,
9881251881Speter                                                scratch_pool, scratch_pool));
9882251881Speter  VERIFY_USABLE_WCROOT(wcroot);
9883251881Speter
9884251881Speter  SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
9885251881Speter                                              wcroot, local_relpath, propname,
9886251881Speter                                              result_pool, scratch_pool),
9887251881Speter                      wcroot);
9888251881Speter
9889251881Speter  return SVN_NO_ERROR;
9890251881Speter}
9891251881Speter
9892251881Speter/* The body of svn_wc__db_get_children_with_cached_iprops().
9893251881Speter */
9894251881Speterstatic svn_error_t *
9895251881Speterget_children_with_cached_iprops(apr_hash_t **iprop_paths,
9896251881Speter                                svn_wc__db_wcroot_t *wcroot,
9897251881Speter                                const char *local_relpath,
9898251881Speter                                svn_depth_t depth,
9899251881Speter                                apr_pool_t *result_pool,
9900251881Speter                                apr_pool_t *scratch_pool)
9901251881Speter{
9902251881Speter  svn_sqlite__stmt_t *stmt;
9903251881Speter  svn_boolean_t have_row;
9904251881Speter
9905251881Speter  *iprop_paths = apr_hash_make(result_pool);
9906251881Speter
9907251881Speter  /* First check if LOCAL_RELPATH itself has iprops */
9908251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9909251881Speter                                    STMT_SELECT_IPROPS_NODE));
9910251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9911251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9912251881Speter
9913251881Speter  if (have_row)
9914251881Speter   {
9915251881Speter      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
9916251881Speter                                                               NULL);
9917251881Speter      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
9918251881Speter                                                       relpath_with_cache,
9919251881Speter                                                       result_pool);
9920251881Speter      svn_hash_sets(*iprop_paths, abspath_with_cache,
9921251881Speter                    svn_sqlite__column_text(stmt, 1, result_pool));
9922251881Speter    }
9923251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
9924251881Speter
9925251881Speter  if (depth == svn_depth_empty)
9926251881Speter    return SVN_NO_ERROR;
9927251881Speter
9928251881Speter  /* Now fetch information for children or all descendants */
9929251881Speter  if (depth == svn_depth_files
9930251881Speter      || depth == svn_depth_immediates)
9931251881Speter    {
9932251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9933251881Speter                                        STMT_SELECT_IPROPS_CHILDREN));
9934251881Speter    }
9935251881Speter  else /* Default to svn_depth_infinity. */
9936251881Speter    {
9937251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9938251881Speter                                        STMT_SELECT_IPROPS_RECURSIVE));
9939251881Speter    }
9940251881Speter
9941251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9942251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
9943251881Speter
9944251881Speter  while (have_row)
9945251881Speter    {
9946251881Speter      const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
9947251881Speter                                                               NULL);
9948251881Speter      const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
9949251881Speter                                                       relpath_with_cache,
9950251881Speter                                                       result_pool);
9951251881Speter      svn_hash_sets(*iprop_paths, abspath_with_cache,
9952251881Speter                    svn_sqlite__column_text(stmt, 1, result_pool));
9953251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
9954251881Speter    }
9955251881Speter
9956251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
9957251881Speter
9958251881Speter  /* For depth files we should filter non files */
9959251881Speter  if (depth == svn_depth_files)
9960251881Speter    {
9961251881Speter      apr_hash_index_t *hi;
9962251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9963251881Speter
9964251881Speter      for (hi = apr_hash_first(scratch_pool, *iprop_paths);
9965251881Speter           hi;
9966251881Speter           hi = apr_hash_next(hi))
9967251881Speter        {
9968251881Speter          const char *child_abspath = svn__apr_hash_index_key(hi);
9969251881Speter          const char *child_relpath;
9970251881Speter          svn_node_kind_t child_kind;
9971251881Speter
9972251881Speter          svn_pool_clear(iterpool);
9973251881Speter
9974251881Speter          child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
9975251881Speter                                              NULL);
9976251881Speter
9977251881Speter          if (! child_relpath)
9978251881Speter            {
9979251881Speter              continue; /* local_relpath itself */
9980251881Speter            }
9981251881Speter
9982251881Speter          SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
9983251881Speter                                                    NULL, NULL, NULL, NULL,
9984251881Speter                                                    NULL, NULL, NULL, NULL,
9985251881Speter                                                    NULL, NULL, NULL, NULL,
9986251881Speter                                                    wcroot, child_relpath,
9987251881Speter                                                    scratch_pool,
9988251881Speter                                                    scratch_pool));
9989251881Speter
9990251881Speter          /* Filter if not a file */
9991251881Speter          if (child_kind != svn_node_file)
9992251881Speter            {
9993251881Speter              svn_hash_sets(*iprop_paths, child_abspath, NULL);
9994251881Speter            }
9995251881Speter        }
9996251881Speter
9997251881Speter      svn_pool_destroy(iterpool);
9998251881Speter    }
9999251881Speter
10000251881Speter  return SVN_NO_ERROR;
10001251881Speter}
10002251881Speter
10003251881Spetersvn_error_t *
10004251881Spetersvn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10005251881Speter                                           svn_depth_t depth,
10006251881Speter                                           const char *local_abspath,
10007251881Speter                                           svn_wc__db_t *db,
10008251881Speter                                           apr_pool_t *result_pool,
10009251881Speter                                           apr_pool_t *scratch_pool)
10010251881Speter{
10011251881Speter  svn_wc__db_wcroot_t *wcroot;
10012251881Speter  const char *local_relpath;
10013251881Speter
10014251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10015251881Speter
10016251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10017251881Speter                                                local_abspath, scratch_pool,
10018251881Speter                                                scratch_pool));
10019251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10020251881Speter
10021251881Speter  SVN_WC__DB_WITH_TXN(
10022251881Speter    get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10023251881Speter                                    depth, result_pool, scratch_pool),
10024251881Speter    wcroot);
10025251881Speter
10026251881Speter  return SVN_NO_ERROR;
10027251881Speter}
10028251881Speter
10029251881Spetersvn_error_t *
10030251881Spetersvn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10031251881Speter                                         svn_wc__db_t *db,
10032251881Speter                                         const char *local_abspath,
10033251881Speter                                         apr_pool_t *result_pool,
10034251881Speter                                         apr_pool_t *scratch_pool)
10035251881Speter{
10036251881Speter  svn_wc__db_wcroot_t *wcroot;
10037251881Speter  const char *local_relpath;
10038251881Speter
10039251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10040251881Speter
10041251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10042251881Speter                                             local_abspath,
10043251881Speter                                             scratch_pool, scratch_pool));
10044251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10045251881Speter
10046251881Speter  return gather_children2(children, wcroot, local_relpath,
10047251881Speter                          result_pool, scratch_pool);
10048251881Speter}
10049251881Speter
10050251881Speter/* Helper for svn_wc__db_node_check_replace().
10051251881Speter */
10052251881Speterstatic svn_error_t *
10053251881Spetercheck_replace_txn(svn_boolean_t *is_replace_root_p,
10054251881Speter                  svn_boolean_t *base_replace_p,
10055251881Speter                  svn_boolean_t *is_replace_p,
10056251881Speter                  svn_wc__db_wcroot_t *wcroot,
10057251881Speter                  const char *local_relpath,
10058251881Speter                  apr_pool_t *scratch_pool)
10059251881Speter{
10060251881Speter  svn_sqlite__stmt_t *stmt;
10061251881Speter  svn_boolean_t have_row;
10062251881Speter  svn_boolean_t is_replace = FALSE;
10063251881Speter  int replaced_op_depth;
10064251881Speter  svn_wc__db_status_t replaced_status;
10065251881Speter
10066251881Speter  /* Our caller initialized the output values to FALSE */
10067251881Speter
10068251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10069251881Speter                                    STMT_SELECT_NODE_INFO));
10070251881Speter
10071251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10072251881Speter
10073251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10074251881Speter
10075251881Speter  if (!have_row)
10076251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10077251881Speter                             svn_sqlite__reset(stmt),
10078251881Speter                             _("The node '%s' was not found."),
10079251881Speter                             path_for_error_message(wcroot, local_relpath,
10080251881Speter                                                    scratch_pool));
10081251881Speter
10082251881Speter  {
10083251881Speter    svn_wc__db_status_t status;
10084251881Speter
10085251881Speter    status = svn_sqlite__column_token(stmt, 3, presence_map);
10086251881Speter
10087251881Speter    if (status != svn_wc__db_status_normal)
10088251881Speter      return svn_error_trace(svn_sqlite__reset(stmt));
10089251881Speter  }
10090251881Speter
10091251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10092251881Speter
10093251881Speter  if (!have_row)
10094251881Speter    return svn_error_trace(svn_sqlite__reset(stmt));
10095251881Speter
10096251881Speter  replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10097251881Speter
10098251881Speter  /* If the layer below the add describes a not present or a deleted node,
10099251881Speter     this is not a replacement. Deleted can only occur if an ancestor is
10100251881Speter     the delete root. */
10101251881Speter  if (replaced_status != svn_wc__db_status_not_present
10102251881Speter      && replaced_status != svn_wc__db_status_excluded
10103251881Speter      && replaced_status != svn_wc__db_status_server_excluded
10104251881Speter      && replaced_status != svn_wc__db_status_base_deleted)
10105251881Speter    {
10106251881Speter      is_replace = TRUE;
10107251881Speter      if (is_replace_p)
10108251881Speter        *is_replace_p = TRUE;
10109251881Speter    }
10110251881Speter
10111251881Speter  replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10112251881Speter
10113251881Speter  if (base_replace_p)
10114251881Speter    {
10115251881Speter      int op_depth = svn_sqlite__column_int(stmt, 0);
10116251881Speter
10117251881Speter      while (op_depth != 0 && have_row)
10118251881Speter        {
10119251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
10120251881Speter
10121251881Speter          if (have_row)
10122251881Speter            op_depth = svn_sqlite__column_int(stmt, 0);
10123251881Speter        }
10124251881Speter
10125251881Speter      if (have_row && op_depth == 0)
10126251881Speter        {
10127251881Speter          svn_wc__db_status_t base_status;
10128251881Speter
10129251881Speter          base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10130251881Speter
10131251881Speter          *base_replace_p = (base_status != svn_wc__db_status_not_present);
10132251881Speter        }
10133251881Speter    }
10134251881Speter
10135251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10136251881Speter
10137251881Speter  if (!is_replace_root_p || !is_replace)
10138251881Speter    return SVN_NO_ERROR;
10139251881Speter
10140251881Speter  if (replaced_status != svn_wc__db_status_base_deleted)
10141251881Speter    {
10142251881Speter      int parent_op_depth;
10143251881Speter
10144251881Speter      /* Check the current op-depth of the parent to see if we are a replacement
10145251881Speter         root */
10146251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10147251881Speter                                svn_relpath_dirname(local_relpath,
10148251881Speter                                                    scratch_pool)));
10149251881Speter
10150251881Speter      SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10151251881Speter
10152251881Speter      parent_op_depth = svn_sqlite__column_int(stmt, 0);
10153251881Speter
10154251881Speter      if (parent_op_depth >= replaced_op_depth)
10155251881Speter        {
10156251881Speter          /* Did we replace inside our directory? */
10157251881Speter
10158251881Speter          *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10159251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
10160251881Speter          return SVN_NO_ERROR;
10161251881Speter        }
10162251881Speter
10163251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10164251881Speter
10165251881Speter      if (have_row)
10166251881Speter        parent_op_depth = svn_sqlite__column_int(stmt, 0);
10167251881Speter
10168251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
10169251881Speter
10170251881Speter      if (!have_row)
10171251881Speter        *is_replace_root_p = TRUE; /* Parent is no replacement */
10172251881Speter      else if (parent_op_depth < replaced_op_depth)
10173251881Speter        *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10174251881Speter      /*else // No replacement root */
10175251881Speter  }
10176251881Speter
10177251881Speter  return SVN_NO_ERROR;
10178251881Speter}
10179251881Speter
10180251881Spetersvn_error_t *
10181251881Spetersvn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10182251881Speter                              svn_boolean_t *base_replace,
10183251881Speter                              svn_boolean_t *is_replace,
10184251881Speter                              svn_wc__db_t *db,
10185251881Speter                              const char *local_abspath,
10186251881Speter                              apr_pool_t *scratch_pool)
10187251881Speter{
10188251881Speter  svn_wc__db_wcroot_t *wcroot;
10189251881Speter  const char *local_relpath;
10190251881Speter
10191251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10192251881Speter
10193251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10194251881Speter                                             local_abspath,
10195251881Speter                                             scratch_pool, scratch_pool));
10196251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10197251881Speter
10198251881Speter  if (is_replace_root)
10199251881Speter    *is_replace_root = FALSE;
10200251881Speter  if (base_replace)
10201251881Speter    *base_replace = FALSE;
10202251881Speter  if (is_replace)
10203251881Speter    *is_replace = FALSE;
10204251881Speter
10205251881Speter  if (local_relpath[0] == '\0')
10206251881Speter    return SVN_NO_ERROR; /* Working copy root can't be replaced */
10207251881Speter
10208251881Speter  SVN_WC__DB_WITH_TXN(
10209251881Speter    check_replace_txn(is_replace_root, base_replace, is_replace,
10210251881Speter                      wcroot, local_relpath, scratch_pool),
10211251881Speter    wcroot);
10212251881Speter
10213251881Speter  return SVN_NO_ERROR;
10214251881Speter}
10215251881Speter
10216251881Spetersvn_error_t *
10217251881Spetersvn_wc__db_read_children(const apr_array_header_t **children,
10218251881Speter                         svn_wc__db_t *db,
10219251881Speter                         const char *local_abspath,
10220251881Speter                         apr_pool_t *result_pool,
10221251881Speter                         apr_pool_t *scratch_pool)
10222251881Speter{
10223251881Speter  svn_wc__db_wcroot_t *wcroot;
10224251881Speter  const char *local_relpath;
10225251881Speter
10226251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10227251881Speter
10228251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10229251881Speter                                             local_abspath,
10230251881Speter                                             scratch_pool, scratch_pool));
10231251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10232251881Speter
10233251881Speter  return gather_children(children, wcroot, local_relpath,
10234251881Speter                         result_pool, scratch_pool);
10235251881Speter}
10236251881Speter
10237251881Speter
10238251881Speter/* */
10239251881Speterstatic svn_error_t *
10240251881Speterrelocate_txn(svn_wc__db_wcroot_t *wcroot,
10241251881Speter             const char *local_relpath,
10242251881Speter             const char *repos_root_url,
10243251881Speter             const char *repos_uuid,
10244251881Speter             svn_boolean_t have_base_node,
10245251881Speter             apr_int64_t old_repos_id,
10246251881Speter             apr_pool_t *scratch_pool)
10247251881Speter{
10248251881Speter  svn_sqlite__stmt_t *stmt;
10249251881Speter  apr_int64_t new_repos_id;
10250251881Speter
10251251881Speter  /* This function affects all the children of the given local_relpath,
10252251881Speter     but the way that it does this is through the repos inheritance mechanism.
10253251881Speter     So, we only need to rewrite the repos_id of the given local_relpath,
10254251881Speter     as well as any children with a non-null repos_id, as well as various
10255251881Speter     repos_id fields in the locks and working_node tables.
10256251881Speter   */
10257251881Speter
10258251881Speter  /* Get the repos_id for the new repository. */
10259251881Speter  SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10260251881Speter                          wcroot->sdb, scratch_pool));
10261251881Speter
10262251881Speter  /* Set the (base and working) repos_ids and clear the dav_caches */
10263251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10264251881Speter                                    STMT_RECURSIVE_UPDATE_NODE_REPO));
10265251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10266251881Speter                            old_repos_id, new_repos_id));
10267251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
10268251881Speter
10269251881Speter  if (have_base_node)
10270251881Speter    {
10271251881Speter      /* Update any locks for the root or its children. */
10272251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10273251881Speter                                        STMT_UPDATE_LOCK_REPOS_ID));
10274251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10275251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
10276251881Speter    }
10277251881Speter
10278251881Speter  return SVN_NO_ERROR;
10279251881Speter}
10280251881Speter
10281251881Speter
10282251881Spetersvn_error_t *
10283251881Spetersvn_wc__db_global_relocate(svn_wc__db_t *db,
10284251881Speter                           const char *local_dir_abspath,
10285251881Speter                           const char *repos_root_url,
10286251881Speter                           apr_pool_t *scratch_pool)
10287251881Speter{
10288251881Speter  svn_wc__db_wcroot_t *wcroot;
10289251881Speter  const char *local_relpath;
10290251881Speter  const char *local_dir_relpath;
10291251881Speter  svn_wc__db_status_t status;
10292251881Speter  const char *repos_uuid;
10293251881Speter  svn_boolean_t have_base_node;
10294251881Speter  apr_int64_t old_repos_id;
10295251881Speter
10296251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10297251881Speter  /* ### assert that we were passed a directory?  */
10298251881Speter
10299251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10300251881Speter                           db, local_dir_abspath, scratch_pool, scratch_pool));
10301251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10302251881Speter  local_relpath = local_dir_relpath;
10303251881Speter
10304251881Speter  SVN_ERR(read_info(&status,
10305251881Speter                    NULL, NULL, NULL, &old_repos_id,
10306251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10307251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10308251881Speter                    NULL,
10309251881Speter                    &have_base_node, NULL, NULL,
10310251881Speter                    wcroot, local_relpath,
10311251881Speter                    scratch_pool, scratch_pool));
10312251881Speter
10313251881Speter  if (status == svn_wc__db_status_excluded)
10314251881Speter    {
10315251881Speter      /* The parent cannot be excluded, so look at the parent and then
10316251881Speter         adjust the relpath */
10317251881Speter      const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10318251881Speter                                                       scratch_pool);
10319251881Speter      SVN_ERR(read_info(&status,
10320251881Speter                        NULL, NULL, NULL, &old_repos_id,
10321251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10322251881Speter                        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10323251881Speter                        NULL, NULL, NULL,
10324251881Speter                        NULL, NULL, NULL,
10325251881Speter                        wcroot, parent_relpath,
10326251881Speter                        scratch_pool, scratch_pool));
10327251881Speter      local_dir_relpath = parent_relpath;
10328251881Speter    }
10329251881Speter
10330251881Speter  if (old_repos_id == INVALID_REPOS_ID)
10331251881Speter    {
10332251881Speter      /* Do we need to support relocating something that is
10333251881Speter         added/deleted/excluded without relocating the parent?  If not
10334251881Speter         then perhaps relpath, root_url and uuid should be passed down
10335251881Speter         to the children so that they don't have to scan? */
10336251881Speter
10337251881Speter      if (status == svn_wc__db_status_deleted)
10338251881Speter        {
10339251881Speter          const char *work_del_relpath;
10340251881Speter
10341251881Speter          SVN_ERR(scan_deletion_txn(NULL, NULL,
10342251881Speter                                    &work_del_relpath, NULL,
10343251881Speter                                    wcroot, local_dir_relpath,
10344251881Speter                                    scratch_pool,
10345251881Speter                                    scratch_pool));
10346251881Speter          if (work_del_relpath)
10347251881Speter            {
10348251881Speter              /* Deleted within a copy/move */
10349251881Speter
10350251881Speter              /* The parent of the delete is added. */
10351251881Speter              status = svn_wc__db_status_added;
10352251881Speter              local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10353251881Speter                                                      scratch_pool);
10354251881Speter            }
10355251881Speter        }
10356251881Speter
10357251881Speter      if (status == svn_wc__db_status_added)
10358251881Speter        {
10359251881Speter          SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10360251881Speter                                NULL, NULL, NULL, NULL, NULL, NULL,
10361251881Speter                                wcroot, local_dir_relpath,
10362251881Speter                                scratch_pool, scratch_pool));
10363251881Speter        }
10364251881Speter      else
10365251881Speter        SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10366251881Speter                                                  &old_repos_id,
10367251881Speter                                                  NULL, NULL, NULL, NULL, NULL,
10368251881Speter                                                  NULL, NULL, NULL, NULL, NULL,
10369251881Speter                                                  wcroot, local_dir_relpath,
10370251881Speter                                                  scratch_pool, scratch_pool));
10371251881Speter    }
10372251881Speter
10373251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10374251881Speter                                      old_repos_id, scratch_pool));
10375251881Speter  SVN_ERR_ASSERT(repos_uuid);
10376251881Speter
10377251881Speter  SVN_WC__DB_WITH_TXN(
10378251881Speter    relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10379251881Speter                 have_base_node, old_repos_id, scratch_pool),
10380251881Speter    wcroot);
10381251881Speter
10382251881Speter  return SVN_NO_ERROR;
10383251881Speter}
10384251881Speter
10385251881Speter
10386251881Speter/* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10387251881Speter   (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10388251881Speter   its parent's BASE row if not. In the latter case, error if the parent
10389251881Speter   BASE row does not exist.  */
10390251881Speterstatic svn_error_t *
10391251881Speterdetermine_repos_info(apr_int64_t *repos_id,
10392251881Speter                     const char **repos_relpath,
10393251881Speter                     svn_wc__db_wcroot_t *wcroot,
10394251881Speter                     const char *local_relpath,
10395251881Speter                     apr_pool_t *result_pool,
10396251881Speter                     apr_pool_t *scratch_pool)
10397251881Speter{
10398251881Speter  svn_sqlite__stmt_t *stmt;
10399251881Speter  svn_boolean_t have_row;
10400251881Speter  const char *repos_parent_relpath;
10401251881Speter  const char *local_parent_relpath, *name;
10402251881Speter
10403251881Speter  /* ### is it faster to fetch fewer columns? */
10404251881Speter
10405251881Speter  /* Prefer the current node's repository information.  */
10406251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10407251881Speter                                    STMT_SELECT_BASE_NODE));
10408251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10409251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10410251881Speter
10411251881Speter  if (have_row)
10412251881Speter    {
10413251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
10414251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10415251881Speter
10416251881Speter      *repos_id = svn_sqlite__column_int64(stmt, 0);
10417251881Speter      *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
10418251881Speter
10419251881Speter      return svn_error_trace(svn_sqlite__reset(stmt));
10420251881Speter    }
10421251881Speter
10422251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10423251881Speter
10424251881Speter  /* This was a child node within this wcroot. We want to look at the
10425251881Speter     BASE node of the directory.  */
10426251881Speter  svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
10427251881Speter
10428251881Speter  /* The REPOS_ID will be the same (### until we support mixed-repos)  */
10429251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
10430251881Speter                                            &repos_parent_relpath, repos_id,
10431251881Speter                                            NULL, NULL, NULL, NULL, NULL,
10432251881Speter                                            NULL, NULL, NULL, NULL, NULL,
10433251881Speter                                            wcroot, local_parent_relpath,
10434251881Speter                                            scratch_pool, scratch_pool));
10435251881Speter
10436251881Speter  *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
10437251881Speter
10438251881Speter  return SVN_NO_ERROR;
10439251881Speter}
10440251881Speter
10441251881Speter/* Helper for svn_wc__db_global_commit()
10442251881Speter
10443251881Speter   Makes local_relpath and all its descendants at the same op-depth represent
10444251881Speter   the copy origin repos_id:repos_relpath@revision.
10445251881Speter
10446251881Speter   This code is only valid to fix-up a move from an old location, to a new
10447251881Speter   location during a commit.
10448251881Speter
10449251881Speter   Assumptions:
10450251881Speter     * local_relpath is not the working copy root (can't be moved)
10451251881Speter     * repos_relpath is not the repository root (can't be moved)
10452251881Speter   */
10453251881Speterstatic svn_error_t *
10454251881Spetermoved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10455251881Speter                        const char *local_relpath,
10456251881Speter                        int op_depth,
10457251881Speter                        apr_int64_t repos_id,
10458251881Speter                        const char *repos_relpath,
10459251881Speter                        svn_revnum_t revision,
10460251881Speter                        apr_pool_t *scratch_pool)
10461251881Speter{
10462251881Speter  apr_hash_t *children;
10463251881Speter  apr_pool_t *iterpool;
10464251881Speter  svn_sqlite__stmt_t *stmt;
10465251881Speter  svn_boolean_t have_row;
10466251881Speter  apr_hash_index_t *hi;
10467251881Speter
10468251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0'
10469251881Speter                 && *repos_relpath != '\0');
10470251881Speter
10471251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10472251881Speter                                    STMT_SELECT_MOVED_DESCENDANTS));
10473251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
10474251881Speter                                         local_relpath,
10475251881Speter                                         op_depth));
10476251881Speter
10477251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
10478251881Speter  if (! have_row)
10479251881Speter    return svn_error_trace(svn_sqlite__reset(stmt));
10480251881Speter
10481251881Speter  children = apr_hash_make(scratch_pool);
10482251881Speter
10483251881Speter  /* First, obtain all moved children */
10484251881Speter  /* To keep error handling simple, first cache them in a hashtable */
10485251881Speter  while (have_row)
10486251881Speter    {
10487251881Speter      const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
10488251881Speter      const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
10489251881Speter
10490251881Speter      svn_hash_sets(children, src_relpath, to_relpath);
10491251881Speter
10492251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
10493251881Speter    }
10494251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
10495251881Speter
10496251881Speter  /* Then update them */
10497251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10498251881Speter                                    STMT_COMMIT_UPDATE_ORIGIN));
10499251881Speter
10500251881Speter  iterpool = svn_pool_create(scratch_pool);
10501251881Speter  for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
10502251881Speter    {
10503251881Speter      const char *src_relpath = svn__apr_hash_index_key(hi);
10504251881Speter      const char *to_relpath = svn__apr_hash_index_val(hi);
10505251881Speter      const char *new_repos_relpath;
10506251881Speter      int to_op_depth = relpath_depth(to_relpath);
10507251881Speter      int affected;
10508251881Speter
10509251881Speter      svn_pool_clear(iterpool);
10510251881Speter
10511251881Speter      SVN_ERR_ASSERT(to_op_depth > 0);
10512251881Speter
10513251881Speter      new_repos_relpath = svn_relpath_join(
10514251881Speter                            repos_relpath,
10515251881Speter                            svn_relpath_skip_ancestor(local_relpath,
10516251881Speter                                                      src_relpath),
10517251881Speter                            iterpool);
10518251881Speter
10519251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
10520251881Speter                                                to_relpath,
10521251881Speter                                                to_op_depth,
10522251881Speter                                                repos_id,
10523251881Speter                                                new_repos_relpath,
10524251881Speter                                                revision));
10525251881Speter      SVN_ERR(svn_sqlite__update(&affected, stmt));
10526251881Speter
10527251881Speter#ifdef SVN_DEBUG
10528251881Speter      /* Enable in release code?
10529251881Speter         Broken moves are not fatal yet, but this assertion would break
10530251881Speter         committing them */
10531251881Speter      SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
10532251881Speter#endif
10533251881Speter
10534251881Speter      SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
10535251881Speter                                      repos_id, new_repos_relpath, revision,
10536251881Speter                                      iterpool));
10537251881Speter    }
10538251881Speter
10539251881Speter  svn_pool_destroy(iterpool);
10540251881Speter  return SVN_NO_ERROR;
10541251881Speter}
10542251881Speter
10543251881Speter/* Helper for svn_wc__db_global_commit()
10544251881Speter
10545251881Speter   Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
10546251881Speter   (BASE), setting their presence to 'not-present' if their presence wasn't
10547251881Speter   'normal'.
10548251881Speter
10549251881Speter   Makes all nodes below LOCAL_RELPATH represent the descendants of repository
10550251881Speter   location repos_id:repos_relpath@revision.
10551251881Speter
10552251881Speter   Assumptions:
10553251881Speter     * local_relpath is not the working copy root (can't be replaced)
10554251881Speter     * repos_relpath is not the repository root (can't be replaced)
10555251881Speter   */
10556251881Speterstatic svn_error_t *
10557251881Speterdescendant_commit(svn_wc__db_wcroot_t *wcroot,
10558251881Speter                  const char *local_relpath,
10559251881Speter                  int op_depth,
10560251881Speter                  apr_int64_t repos_id,
10561251881Speter                  const char *repos_relpath,
10562251881Speter                  svn_revnum_t revision,
10563251881Speter                  apr_pool_t *scratch_pool)
10564251881Speter{
10565251881Speter  svn_sqlite__stmt_t *stmt;
10566251881Speter
10567251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0'
10568251881Speter                 && *repos_relpath != '\0');
10569251881Speter
10570251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10571251881Speter                                    STMT_COMMIT_DESCENDANTS_TO_BASE));
10572251881Speter
10573251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
10574251881Speter                                            local_relpath,
10575251881Speter                                            op_depth,
10576251881Speter                                            repos_id,
10577251881Speter                                            repos_relpath,
10578251881Speter                                            revision));
10579251881Speter
10580251881Speter  SVN_ERR(svn_sqlite__update(NULL, stmt));
10581251881Speter
10582251881Speter  return SVN_NO_ERROR;
10583251881Speter}
10584251881Speter
10585251881Speter/* The body of svn_wc__db_global_commit().
10586251881Speter */
10587251881Speterstatic svn_error_t *
10588251881Spetercommit_node(svn_wc__db_wcroot_t *wcroot,
10589251881Speter            const char *local_relpath,
10590251881Speter            svn_revnum_t new_revision,
10591251881Speter            svn_revnum_t changed_rev,
10592251881Speter            apr_time_t changed_date,
10593251881Speter            const char *changed_author,
10594251881Speter            const svn_checksum_t *new_checksum,
10595251881Speter            const apr_array_header_t *new_children,
10596251881Speter            apr_hash_t *new_dav_cache,
10597251881Speter            svn_boolean_t keep_changelist,
10598251881Speter            svn_boolean_t no_unlock,
10599251881Speter            const svn_skel_t *work_items,
10600251881Speter            apr_pool_t *scratch_pool)
10601251881Speter{
10602251881Speter  svn_sqlite__stmt_t *stmt_info;
10603251881Speter  svn_sqlite__stmt_t *stmt_act;
10604251881Speter  svn_boolean_t have_act;
10605251881Speter  svn_string_t prop_blob = { 0 };
10606251881Speter  svn_string_t inherited_prop_blob = { 0 };
10607251881Speter  const char *changelist = NULL;
10608251881Speter  const char *parent_relpath;
10609251881Speter  svn_wc__db_status_t new_presence;
10610251881Speter  svn_node_kind_t new_kind;
10611251881Speter  const char *new_depth_str = NULL;
10612251881Speter  svn_sqlite__stmt_t *stmt;
10613251881Speter  apr_int64_t repos_id;
10614251881Speter  const char *repos_relpath;
10615251881Speter  int op_depth;
10616251881Speter  svn_wc__db_status_t old_presence;
10617251881Speter
10618251881Speter    /* If we are adding a file or directory, then we need to get
10619251881Speter     repository information from the parent node since "this node" does
10620251881Speter     not have a BASE).
10621251881Speter
10622251881Speter     For existing nodes, we should retain the (potentially-switched)
10623251881Speter     repository information.  */
10624251881Speter  SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
10625251881Speter                               wcroot, local_relpath,
10626251881Speter                               scratch_pool, scratch_pool));
10627251881Speter
10628251881Speter  /* ### is it better to select only the data needed?  */
10629251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
10630251881Speter                                    STMT_SELECT_NODE_INFO));
10631251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
10632251881Speter  SVN_ERR(svn_sqlite__step_row(stmt_info));
10633251881Speter
10634251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
10635251881Speter                                    STMT_SELECT_ACTUAL_NODE));
10636251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
10637251881Speter                            wcroot->wc_id, local_relpath));
10638251881Speter  SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
10639251881Speter
10640251881Speter  /* There should be something to commit!  */
10641251881Speter
10642251881Speter  op_depth = svn_sqlite__column_int(stmt_info, 0);
10643251881Speter
10644251881Speter  /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
10645251881Speter     or there will be a BASE_NODE that has it.  */
10646251881Speter  new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
10647251881Speter
10648251881Speter  /* What will the new depth be?  */
10649251881Speter  if (new_kind == svn_node_dir)
10650251881Speter    new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
10651251881Speter
10652251881Speter  /* Check that the repository information is not being changed.  */
10653251881Speter  if (op_depth == 0)
10654251881Speter    {
10655251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
10656251881Speter      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
10657251881Speter
10658251881Speter      /* A commit cannot change these values.  */
10659251881Speter      SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
10660251881Speter      SVN_ERR_ASSERT(strcmp(repos_relpath,
10661251881Speter                            svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
10662251881Speter    }
10663251881Speter
10664251881Speter  /* Find the appropriate new properties -- ACTUAL overrides any properties
10665251881Speter     in WORKING that arrived as part of a copy/move.
10666251881Speter
10667251881Speter     Note: we'll keep them as a big blob of data, rather than
10668251881Speter     deserialize/serialize them.  */
10669251881Speter  if (have_act)
10670251881Speter    prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
10671251881Speter                                             scratch_pool);
10672251881Speter  if (prop_blob.data == NULL)
10673251881Speter    prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
10674251881Speter                                             scratch_pool);
10675251881Speter
10676251881Speter  inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
10677251881Speter                                                     &inherited_prop_blob.len,
10678251881Speter                                                     scratch_pool);
10679251881Speter
10680251881Speter  if (keep_changelist && have_act)
10681251881Speter    changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
10682251881Speter
10683251881Speter  old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
10684251881Speter
10685251881Speter  /* ### other stuff?  */
10686251881Speter
10687251881Speter  SVN_ERR(svn_sqlite__reset(stmt_info));
10688251881Speter  SVN_ERR(svn_sqlite__reset(stmt_act));
10689251881Speter
10690251881Speter  if (op_depth > 0)
10691251881Speter    {
10692251881Speter      int affected_rows;
10693251881Speter
10694251881Speter      /* This removes all layers of this node and at the same time determines
10695251881Speter         if we need to remove shadowed layers below our descendants. */
10696251881Speter
10697251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10698251881Speter                                        STMT_DELETE_ALL_LAYERS));
10699251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10700251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
10701251881Speter
10702251881Speter      if (affected_rows > 1)
10703251881Speter        {
10704251881Speter          /* We commit a shadowing operation
10705251881Speter
10706251881Speter           1) Remove all shadowed nodes
10707251881Speter           2) And remove all nodes that have a base-deleted as lowest layer,
10708251881Speter              because 1) removed that layer */
10709251881Speter
10710251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10711251881Speter                                            STMT_DELETE_SHADOWED_RECURSIVE));
10712251881Speter
10713251881Speter          SVN_ERR(svn_sqlite__bindf(stmt,
10714251881Speter                                    "isd",
10715251881Speter                                    wcroot->wc_id,
10716251881Speter                                    local_relpath,
10717251881Speter                                    op_depth));
10718251881Speter
10719251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
10720251881Speter        }
10721251881Speter
10722251881Speter      /* Note that while these two calls look so similar that they might
10723251881Speter         be integrated, they really affect a different op-depth and
10724251881Speter         completely different nodes (via a different recursion pattern). */
10725251881Speter
10726251881Speter      /* Collapse descendants of the current op_depth in layer 0 */
10727251881Speter      SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
10728251881Speter                                repos_id, repos_relpath, new_revision,
10729251881Speter                                scratch_pool));
10730251881Speter
10731251881Speter      /* And make the recorded local moves represent moves of the node we just
10732251881Speter         committed. */
10733251881Speter      SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
10734251881Speter                                      repos_id, repos_relpath, new_revision,
10735251881Speter                                      scratch_pool));
10736251881Speter
10737251881Speter      /* This node is no longer modified, so no node was moved here */
10738251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10739251881Speter                                        STMT_CLEAR_MOVED_TO_FROM_DEST));
10740251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10741251881Speter                                            local_relpath));
10742251881Speter
10743251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
10744251881Speter    }
10745251881Speter
10746251881Speter  /* Update or add the BASE_NODE row with all the new information.  */
10747251881Speter
10748251881Speter  if (*local_relpath == '\0')
10749251881Speter    parent_relpath = NULL;
10750251881Speter  else
10751251881Speter    parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
10752251881Speter
10753251881Speter  /* Preserve any incomplete status */
10754251881Speter  new_presence = (old_presence == svn_wc__db_status_incomplete
10755251881Speter                  ? svn_wc__db_status_incomplete
10756251881Speter                  : svn_wc__db_status_normal);
10757251881Speter
10758251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10759251881Speter                                    STMT_APPLY_CHANGES_TO_BASE_NODE));
10760251881Speter  /* symlink_target not yet used */
10761251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
10762251881Speter                            wcroot->wc_id, local_relpath,
10763251881Speter                            parent_relpath,
10764251881Speter                            repos_id,
10765251881Speter                            repos_relpath,
10766251881Speter                            new_revision,
10767251881Speter                            presence_map, new_presence,
10768251881Speter                            new_depth_str,
10769251881Speter                            kind_map, new_kind,
10770251881Speter                            changed_rev,
10771251881Speter                            changed_date,
10772251881Speter                            changed_author,
10773251881Speter                            prop_blob.data, prop_blob.len));
10774251881Speter
10775251881Speter  SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
10776251881Speter                                    scratch_pool));
10777251881Speter  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
10778251881Speter                                      scratch_pool));
10779251881Speter  if (inherited_prop_blob.data != NULL)
10780251881Speter    {
10781251881Speter      SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
10782251881Speter                                    inherited_prop_blob.len));
10783251881Speter    }
10784251881Speter
10785251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
10786251881Speter
10787251881Speter  if (have_act)
10788251881Speter    {
10789251881Speter      if (keep_changelist && changelist != NULL)
10790251881Speter        {
10791251881Speter          /* The user told us to keep the changelist. Replace the row in
10792251881Speter             ACTUAL_NODE with the basic keys and the changelist.  */
10793251881Speter          SVN_ERR(svn_sqlite__get_statement(
10794251881Speter                    &stmt, wcroot->sdb,
10795251881Speter                    STMT_RESET_ACTUAL_WITH_CHANGELIST));
10796251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isss",
10797251881Speter                                    wcroot->wc_id, local_relpath,
10798251881Speter                                    svn_relpath_dirname(local_relpath,
10799251881Speter                                                        scratch_pool),
10800251881Speter                                    changelist));
10801251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
10802251881Speter        }
10803251881Speter      else
10804251881Speter        {
10805251881Speter          /* Toss the ACTUAL_NODE row.  */
10806251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10807251881Speter                                            STMT_DELETE_ACTUAL_NODE));
10808251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10809251881Speter          SVN_ERR(svn_sqlite__step_done(stmt));
10810251881Speter        }
10811251881Speter    }
10812251881Speter
10813251881Speter  if (new_kind == svn_node_dir)
10814251881Speter    {
10815251881Speter      /* When committing a directory, we should have its new children.  */
10816251881Speter      /* ### one day. just not today.  */
10817251881Speter#if 0
10818251881Speter      SVN_ERR_ASSERT(new_children != NULL);
10819251881Speter#endif
10820251881Speter
10821251881Speter      /* ### process the children  */
10822251881Speter    }
10823251881Speter
10824251881Speter  if (!no_unlock)
10825251881Speter    {
10826251881Speter      svn_sqlite__stmt_t *lock_stmt;
10827251881Speter
10828251881Speter      SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
10829253734Speter                                        STMT_DELETE_LOCK_RECURSIVELY));
10830251881Speter      SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
10831251881Speter      SVN_ERR(svn_sqlite__step_done(lock_stmt));
10832251881Speter    }
10833251881Speter
10834251881Speter  /* Install any work items into the queue, as part of this transaction.  */
10835251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
10836251881Speter
10837251881Speter  return SVN_NO_ERROR;
10838251881Speter}
10839251881Speter
10840251881Speter
10841251881Spetersvn_error_t *
10842251881Spetersvn_wc__db_global_commit(svn_wc__db_t *db,
10843251881Speter                         const char *local_abspath,
10844251881Speter                         svn_revnum_t new_revision,
10845251881Speter                         svn_revnum_t changed_revision,
10846251881Speter                         apr_time_t changed_date,
10847251881Speter                         const char *changed_author,
10848251881Speter                         const svn_checksum_t *new_checksum,
10849251881Speter                         const apr_array_header_t *new_children,
10850251881Speter                         apr_hash_t *new_dav_cache,
10851251881Speter                         svn_boolean_t keep_changelist,
10852251881Speter                         svn_boolean_t no_unlock,
10853251881Speter                         const svn_skel_t *work_items,
10854251881Speter                         apr_pool_t *scratch_pool)
10855251881Speter{
10856251881Speter  const char *local_relpath;
10857251881Speter  svn_wc__db_wcroot_t *wcroot;
10858251881Speter
10859251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10860251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
10861251881Speter  SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
10862251881Speter
10863251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10864251881Speter                              local_abspath, scratch_pool, scratch_pool));
10865251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10866251881Speter
10867251881Speter  SVN_WC__DB_WITH_TXN(
10868251881Speter    commit_node(wcroot, local_relpath,
10869251881Speter                new_revision, changed_revision, changed_date, changed_author,
10870251881Speter                new_checksum, new_children, new_dav_cache, keep_changelist,
10871251881Speter                no_unlock, work_items, scratch_pool),
10872251881Speter    wcroot);
10873251881Speter
10874251881Speter  /* We *totally* monkeyed the entries. Toss 'em.  */
10875251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
10876251881Speter
10877251881Speter  return SVN_NO_ERROR;
10878251881Speter}
10879251881Speter
10880251881Speter
10881251881Spetersvn_error_t *
10882251881Spetersvn_wc__db_global_update(svn_wc__db_t *db,
10883251881Speter                         const char *local_abspath,
10884251881Speter                         svn_node_kind_t new_kind,
10885251881Speter                         const char *new_repos_relpath,
10886251881Speter                         svn_revnum_t new_revision,
10887251881Speter                         const apr_hash_t *new_props,
10888251881Speter                         svn_revnum_t new_changed_rev,
10889251881Speter                         apr_time_t new_changed_date,
10890251881Speter                         const char *new_changed_author,
10891251881Speter                         const apr_array_header_t *new_children,
10892251881Speter                         const svn_checksum_t *new_checksum,
10893251881Speter                         const char *new_target,
10894251881Speter                         const apr_hash_t *new_dav_cache,
10895251881Speter                         const svn_skel_t *conflict,
10896251881Speter                         const svn_skel_t *work_items,
10897251881Speter                         apr_pool_t *scratch_pool)
10898251881Speter{
10899251881Speter  NOT_IMPLEMENTED();
10900251881Speter
10901251881Speter#if 0
10902251881Speter  svn_wc__db_wcroot_t *wcroot;
10903251881Speter  const char *local_relpath;
10904251881Speter
10905251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10906251881Speter  /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"?  */
10907251881Speter  SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
10908251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
10909251881Speter  SVN_ERR_ASSERT(new_props != NULL);
10910251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
10911251881Speter  SVN_ERR_ASSERT((new_children != NULL
10912251881Speter                  && new_checksum == NULL
10913251881Speter                  && new_target == NULL)
10914251881Speter                 || (new_children == NULL
10915251881Speter                     && new_checksum != NULL
10916251881Speter                     && new_target == NULL)
10917251881Speter                 || (new_children == NULL
10918251881Speter                     && new_checksum == NULL
10919251881Speter                     && new_target != NULL));
10920251881Speter
10921251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10922251881Speter                              local_abspath, scratch_pool, scratch_pool));
10923251881Speter  VERIFY_USABLE_WCROOT(wcroot);
10924251881Speter
10925251881Speter  SVN_WC__DB_WITH_TXN(
10926251881Speter    update_node(wcroot, local_relpath,
10927251881Speter                new_repos_relpath, new_revision, new_props,
10928251881Speter                new_changed_rev, new_changed_date, new_changed_author,
10929251881Speter                new_children, new_checksum, new_target,
10930251881Speter                conflict, work_items, scratch_pool),
10931251881Speter    wcroot);
10932251881Speter
10933251881Speter  /* We *totally* monkeyed the entries. Toss 'em.  */
10934251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
10935251881Speter
10936251881Speter  return SVN_NO_ERROR;
10937251881Speter#endif
10938251881Speter}
10939251881Speter
10940251881Speter/* Sets a base nodes revision, repository relative path, and/or inherited
10941251881Speter   propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
10942251881Speter   SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
10943251881Speter   (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
10944251881Speter   inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
10945251881Speter   cache for the base node.
10946251881Speter */
10947251881Speterstatic svn_error_t *
10948251881Speterdb_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
10949251881Speter                                   const char *local_relpath,
10950251881Speter                                   apr_array_header_t *iprops,
10951251881Speter                                   svn_revnum_t rev,
10952251881Speter                                   svn_boolean_t set_repos_relpath,
10953251881Speter                                   const char *repos_relpath,
10954251881Speter                                   apr_int64_t repos_id,
10955251881Speter                                   apr_pool_t *scratch_pool)
10956251881Speter{
10957251881Speter  svn_sqlite__stmt_t *stmt;
10958251881Speter
10959251881Speter  SVN_ERR(flush_entries(wcroot,
10960251881Speter                        svn_dirent_join(wcroot->abspath, local_relpath,
10961251881Speter                                        scratch_pool),
10962251881Speter                        svn_depth_empty, scratch_pool));
10963251881Speter
10964251881Speter
10965251881Speter  if (SVN_IS_VALID_REVNUM(rev))
10966251881Speter    {
10967251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10968251881Speter                                        STMT_UPDATE_BASE_REVISION));
10969251881Speter
10970251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
10971251881Speter                                rev));
10972251881Speter
10973251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
10974251881Speter    }
10975251881Speter
10976251881Speter  if (set_repos_relpath)
10977251881Speter    {
10978251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10979251881Speter                                        STMT_UPDATE_BASE_REPOS));
10980251881Speter
10981251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
10982251881Speter                                repos_id, repos_relpath));
10983251881Speter
10984251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
10985251881Speter    }
10986251881Speter
10987251881Speter  /* Set or clear iprops. */
10988251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10989251881Speter                                    STMT_UPDATE_IPROP));
10990251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
10991251881Speter                            wcroot->wc_id,
10992251881Speter                            local_relpath));
10993251881Speter  SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
10994251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
10995251881Speter
10996251881Speter  return SVN_NO_ERROR;
10997251881Speter}
10998251881Speter
10999251881Speter/* The main body of bump_revisions_post_update().
11000251881Speter *
11001251881Speter * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
11002251881Speter * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11003251881Speter * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
11004251881Speter *
11005251881Speter * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11006251881Speter * working copy paths to depth-first ordered arrays of
11007251881Speter * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
11008251881Speter * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11009251881Speter * node's inherited properties.
11010251881Speter *
11011251881Speter * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11012251881Speter * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11013251881Speter */
11014251881Speterstatic svn_error_t *
11015251881Speterbump_node_revision(svn_wc__db_wcroot_t *wcroot,
11016251881Speter                   const char *local_relpath,
11017251881Speter                   apr_int64_t new_repos_id,
11018251881Speter                   const char *new_repos_relpath,
11019251881Speter                   svn_revnum_t new_rev,
11020251881Speter                   svn_depth_t depth,
11021251881Speter                   apr_hash_t *exclude_relpaths,
11022251881Speter                   apr_hash_t *wcroot_iprops,
11023251881Speter                   svn_boolean_t is_root,
11024251881Speter                   svn_boolean_t skip_when_dir,
11025251881Speter                   svn_wc__db_t *db,
11026251881Speter                   apr_pool_t *scratch_pool)
11027251881Speter{
11028251881Speter  apr_pool_t *iterpool;
11029251881Speter  const apr_array_header_t *children;
11030251881Speter  int i;
11031251881Speter  svn_wc__db_status_t status;
11032251881Speter  svn_node_kind_t db_kind;
11033251881Speter  svn_revnum_t revision;
11034251881Speter  const char *repos_relpath;
11035251881Speter  apr_int64_t repos_id;
11036251881Speter  svn_boolean_t set_repos_relpath = FALSE;
11037251881Speter  svn_boolean_t update_root;
11038251881Speter  svn_depth_t depth_below_here = depth;
11039251881Speter  apr_array_header_t *iprops = NULL;
11040251881Speter
11041251881Speter  /* Skip an excluded path and its descendants. */
11042251881Speter  if (svn_hash_gets(exclude_relpaths, local_relpath))
11043251881Speter    return SVN_NO_ERROR;
11044251881Speter
11045251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11046251881Speter                                            &repos_relpath, &repos_id,
11047251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11048251881Speter                                            NULL, NULL, NULL, NULL, &update_root,
11049251881Speter                                            wcroot, local_relpath,
11050251881Speter                                            scratch_pool, scratch_pool));
11051251881Speter
11052251881Speter  /* Skip file externals */
11053251881Speter  if (update_root
11054251881Speter      && db_kind == svn_node_file
11055251881Speter      && !is_root)
11056251881Speter    return SVN_NO_ERROR;
11057251881Speter
11058251881Speter  if (skip_when_dir && db_kind == svn_node_dir)
11059251881Speter    return SVN_NO_ERROR;
11060251881Speter
11061251881Speter  /* If the node is still marked 'not-present', then the server did not
11062251881Speter     re-add it.  So it's really gone in this revision, thus we remove the node.
11063251881Speter
11064251881Speter     If the node is still marked 'server-excluded' and yet is not the same
11065251881Speter     revision as new_rev, then the server did not re-add it, nor
11066251881Speter     re-server-exclude it, so we can remove the node. */
11067251881Speter  if (!is_root
11068251881Speter      && (status == svn_wc__db_status_not_present
11069251881Speter          || (status == svn_wc__db_status_server_excluded &&
11070251881Speter              revision != new_rev)))
11071251881Speter    {
11072251881Speter      return svn_error_trace(db_base_remove(wcroot, local_relpath,
11073253734Speter                                            db, FALSE, FALSE, FALSE,
11074251881Speter                                            SVN_INVALID_REVNUM,
11075251881Speter                                            NULL, NULL, scratch_pool));
11076251881Speter    }
11077251881Speter
11078251881Speter  if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11079251881Speter    set_repos_relpath = TRUE;
11080251881Speter
11081251881Speter  if (wcroot_iprops)
11082251881Speter    iprops = svn_hash_gets(wcroot_iprops,
11083251881Speter                           svn_dirent_join(wcroot->abspath, local_relpath,
11084251881Speter                                           scratch_pool));
11085251881Speter
11086251881Speter  if (iprops
11087251881Speter      || set_repos_relpath
11088251881Speter      || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11089251881Speter    {
11090251881Speter      SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11091251881Speter                                                 iprops, new_rev,
11092251881Speter                                                 set_repos_relpath,
11093251881Speter                                                 new_repos_relpath,
11094251881Speter                                                 new_repos_id,
11095251881Speter                                                 scratch_pool));
11096251881Speter    }
11097251881Speter
11098251881Speter  /* Early out */
11099251881Speter  if (depth <= svn_depth_empty
11100251881Speter      || db_kind != svn_node_dir
11101251881Speter      || status == svn_wc__db_status_server_excluded
11102251881Speter      || status == svn_wc__db_status_excluded
11103251881Speter      || status == svn_wc__db_status_not_present)
11104251881Speter    return SVN_NO_ERROR;
11105251881Speter
11106251881Speter  /* And now recurse over the children */
11107251881Speter
11108251881Speter  depth_below_here = depth;
11109251881Speter
11110251881Speter  if (depth == svn_depth_immediates || depth == svn_depth_files)
11111251881Speter    depth_below_here = svn_depth_empty;
11112251881Speter
11113251881Speter  iterpool = svn_pool_create(scratch_pool);
11114251881Speter
11115251881Speter  SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11116251881Speter                               scratch_pool, iterpool));
11117251881Speter  for (i = 0; i < children->nelts; i++)
11118251881Speter    {
11119251881Speter      const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11120251881Speter      const char *child_local_relpath;
11121251881Speter      const char *child_repos_relpath = NULL;
11122251881Speter
11123251881Speter      svn_pool_clear(iterpool);
11124251881Speter
11125251881Speter      /* Derive the new URL for the current (child) entry */
11126251881Speter      if (new_repos_relpath)
11127251881Speter        child_repos_relpath = svn_relpath_join(new_repos_relpath,
11128251881Speter                                               child_basename, iterpool);
11129251881Speter
11130251881Speter      child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11131251881Speter                                             iterpool);
11132251881Speter
11133251881Speter      SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11134251881Speter                                 child_repos_relpath, new_rev,
11135251881Speter                                 depth_below_here,
11136251881Speter                                 exclude_relpaths, wcroot_iprops,
11137251881Speter                                 FALSE /* is_root */,
11138251881Speter                                 (depth < svn_depth_immediates), db,
11139251881Speter                                 iterpool));
11140251881Speter    }
11141251881Speter
11142251881Speter  /* Cleanup */
11143251881Speter  svn_pool_destroy(iterpool);
11144251881Speter
11145251881Speter  return SVN_NO_ERROR;
11146251881Speter}
11147251881Speter
11148251881Speter/* Helper for svn_wc__db_op_bump_revisions_post_update().
11149251881Speter */
11150251881Speterstatic svn_error_t *
11151251881Speterbump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11152251881Speter                           const char *local_relpath,
11153251881Speter                           svn_wc__db_t *db,
11154251881Speter                           svn_depth_t depth,
11155251881Speter                           const char *new_repos_relpath,
11156251881Speter                           const char *new_repos_root_url,
11157251881Speter                           const char *new_repos_uuid,
11158251881Speter                           svn_revnum_t new_revision,
11159251881Speter                           apr_hash_t *exclude_relpaths,
11160251881Speter                           apr_hash_t *wcroot_iprops,
11161251881Speter                           svn_wc_notify_func2_t notify_func,
11162251881Speter                           void *notify_baton,
11163251881Speter                           apr_pool_t *scratch_pool)
11164251881Speter{
11165251881Speter  svn_wc__db_status_t status;
11166251881Speter  svn_node_kind_t kind;
11167251881Speter  svn_error_t *err;
11168251881Speter  apr_int64_t new_repos_id = INVALID_REPOS_ID;
11169251881Speter
11170251881Speter  err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11171251881Speter                                          NULL, NULL, NULL, NULL, NULL, NULL,
11172251881Speter                                          NULL, NULL, NULL, NULL,
11173251881Speter                                          wcroot, local_relpath,
11174251881Speter                                          scratch_pool, scratch_pool);
11175251881Speter  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11176251881Speter    {
11177251881Speter      svn_error_clear(err);
11178251881Speter      return SVN_NO_ERROR;
11179251881Speter    }
11180251881Speter  else
11181251881Speter    SVN_ERR(err);
11182251881Speter
11183251881Speter  switch (status)
11184251881Speter    {
11185251881Speter      case svn_wc__db_status_excluded:
11186251881Speter      case svn_wc__db_status_server_excluded:
11187251881Speter      case svn_wc__db_status_not_present:
11188251881Speter        return SVN_NO_ERROR;
11189251881Speter
11190251881Speter      /* Explicitly ignore other statii */
11191251881Speter      default:
11192251881Speter        break;
11193251881Speter    }
11194251881Speter
11195251881Speter  if (new_repos_root_url != NULL)
11196251881Speter    SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11197251881Speter                            new_repos_uuid,
11198251881Speter                            wcroot->sdb, scratch_pool));
11199251881Speter
11200251881Speter  SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11201251881Speter                             new_repos_relpath, new_revision,
11202251881Speter                             depth, exclude_relpaths,
11203251881Speter                             wcroot_iprops,
11204251881Speter                             TRUE /* is_root */, FALSE, db,
11205251881Speter                             scratch_pool));
11206251881Speter
11207251881Speter  SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11208251881Speter                                     scratch_pool));
11209251881Speter
11210251881Speter  SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11211251881Speter                                             SVN_INVALID_REVNUM, notify_func,
11212251881Speter                                             notify_baton, scratch_pool));
11213251881Speter
11214251881Speter  return SVN_NO_ERROR;
11215251881Speter}
11216251881Speter
11217251881Spetersvn_error_t *
11218251881Spetersvn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11219251881Speter                                         const char *local_abspath,
11220251881Speter                                         svn_depth_t depth,
11221251881Speter                                         const char *new_repos_relpath,
11222251881Speter                                         const char *new_repos_root_url,
11223251881Speter                                         const char *new_repos_uuid,
11224251881Speter                                         svn_revnum_t new_revision,
11225251881Speter                                         apr_hash_t *exclude_relpaths,
11226251881Speter                                         apr_hash_t *wcroot_iprops,
11227251881Speter                                         svn_wc_notify_func2_t notify_func,
11228251881Speter                                         void *notify_baton,
11229251881Speter                                         apr_pool_t *scratch_pool)
11230251881Speter{
11231251881Speter  const char *local_relpath;
11232251881Speter  svn_wc__db_wcroot_t *wcroot;
11233251881Speter
11234251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11235251881Speter                              local_abspath, scratch_pool, scratch_pool));
11236251881Speter
11237251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11238251881Speter
11239251881Speter  if (svn_hash_gets(exclude_relpaths, local_relpath))
11240251881Speter    return SVN_NO_ERROR;
11241251881Speter
11242251881Speter  if (depth == svn_depth_unknown)
11243251881Speter    depth = svn_depth_infinity;
11244251881Speter
11245251881Speter  SVN_WC__DB_WITH_TXN(
11246251881Speter    bump_revisions_post_update(wcroot, local_relpath, db,
11247251881Speter                               depth, new_repos_relpath, new_repos_root_url,
11248251881Speter                               new_repos_uuid, new_revision,
11249251881Speter                               exclude_relpaths, wcroot_iprops,
11250251881Speter                               notify_func, notify_baton, scratch_pool),
11251251881Speter    wcroot);
11252251881Speter
11253251881Speter  return SVN_NO_ERROR;
11254251881Speter}
11255251881Speter
11256251881Speter/* The body of svn_wc__db_lock_add().
11257251881Speter */
11258251881Speterstatic svn_error_t *
11259251881Speterlock_add_txn(svn_wc__db_wcroot_t *wcroot,
11260251881Speter             const char *local_relpath,
11261251881Speter             const svn_wc__db_lock_t *lock,
11262251881Speter             apr_pool_t *scratch_pool)
11263251881Speter{
11264251881Speter  svn_sqlite__stmt_t *stmt;
11265251881Speter  const char *repos_relpath;
11266251881Speter  apr_int64_t repos_id;
11267251881Speter
11268251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11269251881Speter                                            &repos_relpath, &repos_id,
11270251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11271251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11272251881Speter                                            wcroot, local_relpath,
11273251881Speter                                            scratch_pool, scratch_pool));
11274251881Speter
11275251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11276251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11277251881Speter                            repos_id, repos_relpath, lock->token));
11278251881Speter
11279251881Speter  if (lock->owner != NULL)
11280251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11281251881Speter
11282251881Speter  if (lock->comment != NULL)
11283251881Speter    SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11284251881Speter
11285251881Speter  if (lock->date != 0)
11286251881Speter    SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11287251881Speter
11288251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
11289251881Speter
11290251881Speter  return SVN_NO_ERROR;
11291251881Speter}
11292251881Speter
11293251881Speter
11294251881Spetersvn_error_t *
11295251881Spetersvn_wc__db_lock_add(svn_wc__db_t *db,
11296251881Speter                    const char *local_abspath,
11297251881Speter                    const svn_wc__db_lock_t *lock,
11298251881Speter                    apr_pool_t *scratch_pool)
11299251881Speter{
11300251881Speter  svn_wc__db_wcroot_t *wcroot;
11301251881Speter  const char *local_relpath;
11302251881Speter
11303251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11304251881Speter  SVN_ERR_ASSERT(lock != NULL);
11305251881Speter
11306251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11307251881Speter                              local_abspath, scratch_pool, scratch_pool));
11308251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11309251881Speter
11310251881Speter  SVN_WC__DB_WITH_TXN(
11311251881Speter    lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11312251881Speter    wcroot);
11313251881Speter
11314251881Speter  /* There may be some entries, and the lock info is now out of date.  */
11315251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11316251881Speter
11317251881Speter  return SVN_NO_ERROR;
11318251881Speter}
11319251881Speter
11320251881Speter
11321251881Speter/* The body of svn_wc__db_lock_remove().
11322251881Speter */
11323251881Speterstatic svn_error_t *
11324251881Speterlock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11325251881Speter                const char *local_relpath,
11326251881Speter                apr_pool_t *scratch_pool)
11327251881Speter{
11328251881Speter  const char *repos_relpath;
11329251881Speter  apr_int64_t repos_id;
11330251881Speter  svn_sqlite__stmt_t *stmt;
11331251881Speter
11332251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11333251881Speter                                            &repos_relpath, &repos_id,
11334251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11335251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11336251881Speter                                            wcroot, local_relpath,
11337251881Speter                                            scratch_pool, scratch_pool));
11338251881Speter
11339251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11340251881Speter                                    STMT_DELETE_LOCK));
11341251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11342251881Speter
11343251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
11344251881Speter
11345251881Speter  return SVN_NO_ERROR;
11346251881Speter}
11347251881Speter
11348251881Speter
11349251881Spetersvn_error_t *
11350251881Spetersvn_wc__db_lock_remove(svn_wc__db_t *db,
11351251881Speter                       const char *local_abspath,
11352251881Speter                       apr_pool_t *scratch_pool)
11353251881Speter{
11354251881Speter  svn_wc__db_wcroot_t *wcroot;
11355251881Speter  const char *local_relpath;
11356251881Speter
11357251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11358251881Speter
11359251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11360251881Speter                              local_abspath, scratch_pool, scratch_pool));
11361251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11362251881Speter
11363251881Speter  SVN_WC__DB_WITH_TXN(
11364251881Speter    lock_remove_txn(wcroot, local_relpath, scratch_pool),
11365251881Speter    wcroot);
11366251881Speter
11367251881Speter  /* There may be some entries, and the lock info is now out of date.  */
11368251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11369251881Speter
11370251881Speter  return SVN_NO_ERROR;
11371251881Speter}
11372251881Speter
11373251881Speter
11374251881Spetersvn_error_t *
11375251881Spetersvn_wc__db_scan_base_repos(const char **repos_relpath,
11376251881Speter                           const char **repos_root_url,
11377251881Speter                           const char **repos_uuid,
11378251881Speter                           svn_wc__db_t *db,
11379251881Speter                           const char *local_abspath,
11380251881Speter                           apr_pool_t *result_pool,
11381251881Speter                           apr_pool_t *scratch_pool)
11382251881Speter{
11383251881Speter  svn_wc__db_wcroot_t *wcroot;
11384251881Speter  const char *local_relpath;
11385251881Speter  apr_int64_t repos_id;
11386251881Speter
11387251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11388251881Speter
11389251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11390251881Speter                              local_abspath, scratch_pool, scratch_pool));
11391251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11392251881Speter
11393251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11394251881Speter                                            repos_relpath, &repos_id,
11395251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11396251881Speter                                            NULL, NULL, NULL, NULL, NULL,
11397251881Speter                                            wcroot, local_relpath,
11398251881Speter                                            result_pool, scratch_pool));
11399251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11400251881Speter                                      repos_id, result_pool));
11401251881Speter
11402251881Speter  return SVN_NO_ERROR;
11403251881Speter}
11404251881Speter
11405251881Speter
11406251881Speter/* A helper for scan_addition().
11407251881Speter * Compute moved-from information for the node at LOCAL_RELPATH which
11408251881Speter * has been determined as having been moved-here.
11409251881Speter * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11410251881Speter * path of the move-source node in *MOVED_FROM_RELPATH.
11411251881Speter * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11412251881Speter * to the path of the op-root of the delete-half of the move.
11413251881Speter * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11414251881Speter * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11415251881Speter * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11416251881Speter * of the move. */
11417251881Speterstatic svn_error_t *
11418251881Speterget_moved_from_info(const char **moved_from_relpath,
11419251881Speter                    const char **moved_from_op_root_relpath,
11420251881Speter                    const char *moved_to_op_root_relpath,
11421251881Speter                    int *op_depth,
11422251881Speter                    svn_wc__db_wcroot_t *wcroot,
11423251881Speter                    const char *local_relpath,
11424251881Speter                    apr_pool_t *result_pool,
11425251881Speter                    apr_pool_t *scratch_pool)
11426251881Speter{
11427251881Speter  svn_sqlite__stmt_t *stmt;
11428251881Speter  svn_boolean_t have_row;
11429251881Speter
11430251881Speter  /* Run a query to get the moved-from path from the DB. */
11431251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11432251881Speter                                    STMT_SELECT_MOVED_FROM_RELPATH));
11433251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
11434251881Speter                            wcroot->wc_id, moved_to_op_root_relpath));
11435251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11436251881Speter
11437251881Speter  if (!have_row)
11438251881Speter    {
11439251881Speter      /* The move was only recorded at the copy-half, possibly because
11440251881Speter       * the move operation was interrupted mid-way between the copy
11441251881Speter       * and the delete. Treat this node as a normal copy. */
11442251881Speter      if (moved_from_relpath)
11443251881Speter        *moved_from_relpath = NULL;
11444251881Speter      if (moved_from_op_root_relpath)
11445251881Speter        *moved_from_op_root_relpath = NULL;
11446251881Speter
11447251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
11448251881Speter      return SVN_NO_ERROR;
11449251881Speter    }
11450251881Speter
11451251881Speter  if (op_depth)
11452251881Speter    *op_depth = svn_sqlite__column_int(stmt, 1);
11453251881Speter
11454251881Speter  if (moved_from_relpath || moved_from_op_root_relpath)
11455251881Speter    {
11456251881Speter      const char *db_delete_op_root_relpath;
11457251881Speter
11458251881Speter      /* The moved-from path from the DB is the relpath of
11459251881Speter       * the op_root of the delete-half of the move. */
11460251881Speter      db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
11461251881Speter                                                          result_pool);
11462251881Speter      if (moved_from_op_root_relpath)
11463251881Speter        *moved_from_op_root_relpath = db_delete_op_root_relpath;
11464251881Speter
11465251881Speter      if (moved_from_relpath)
11466251881Speter        {
11467251881Speter          if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
11468251881Speter            {
11469251881Speter              /* LOCAL_RELPATH is the op_root of the copied-half of the
11470251881Speter               * move, so the correct MOVED_FROM_ABSPATH is the op-root
11471251881Speter               * of the delete-half. */
11472251881Speter              *moved_from_relpath = db_delete_op_root_relpath;
11473251881Speter            }
11474251881Speter          else
11475251881Speter            {
11476251881Speter              const char *child_relpath;
11477251881Speter
11478251881Speter              /* LOCAL_RELPATH is a child that was copied along with the
11479251881Speter               * op_root of the copied-half of the move. Construct the
11480251881Speter               * corresponding path beneath the op_root of the delete-half. */
11481251881Speter
11482251881Speter              /* Grab the child path relative to the op_root of the move
11483251881Speter               * destination. */
11484251881Speter              child_relpath = svn_relpath_skip_ancestor(
11485251881Speter                                moved_to_op_root_relpath, local_relpath);
11486251881Speter
11487251881Speter              SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
11488251881Speter
11489251881Speter              /* This join is valid because LOCAL_RELPATH has not been moved
11490251881Speter               * within the copied-half of the move yet -- else, it would
11491251881Speter               * be its own op_root. */
11492251881Speter              *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
11493251881Speter                                                     child_relpath,
11494251881Speter                                                     result_pool);
11495251881Speter            }
11496251881Speter        }
11497251881Speter    }
11498251881Speter
11499251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
11500251881Speter
11501251881Speter  return SVN_NO_ERROR;
11502251881Speter}
11503251881Speter
11504251881Speter/* The body of scan_addition().
11505251881Speter */
11506251881Speterstatic svn_error_t *
11507251881Speterscan_addition_txn(svn_wc__db_status_t *status,
11508251881Speter                  const char **op_root_relpath_p,
11509251881Speter                  const char **repos_relpath,
11510251881Speter                  apr_int64_t *repos_id,
11511251881Speter                  const char **original_repos_relpath,
11512251881Speter                  apr_int64_t *original_repos_id,
11513251881Speter                  svn_revnum_t *original_revision,
11514251881Speter                  const char **moved_from_relpath,
11515251881Speter                  const char **moved_from_op_root_relpath,
11516251881Speter                  int *moved_from_op_depth,
11517251881Speter                  svn_wc__db_wcroot_t *wcroot,
11518251881Speter                  const char *local_relpath,
11519251881Speter                  apr_pool_t *result_pool,
11520251881Speter                  apr_pool_t *scratch_pool)
11521251881Speter{
11522251881Speter  const char *op_root_relpath;
11523251881Speter  const char *build_relpath = "";
11524251881Speter
11525251881Speter  /* Initialize most of the OUT parameters. Generally, we'll only be filling
11526251881Speter     in a subset of these, so it is easier to init all up front. Note that
11527251881Speter     the STATUS parameter will be initialized once we read the status of
11528251881Speter     the specified node.  */
11529251881Speter  if (op_root_relpath_p)
11530251881Speter    *op_root_relpath_p = NULL;
11531251881Speter  if (original_repos_relpath)
11532251881Speter    *original_repos_relpath = NULL;
11533251881Speter  if (original_repos_id)
11534251881Speter    *original_repos_id = INVALID_REPOS_ID;
11535251881Speter  if (original_revision)
11536251881Speter    *original_revision = SVN_INVALID_REVNUM;
11537251881Speter  if (moved_from_relpath)
11538251881Speter    *moved_from_relpath = NULL;
11539251881Speter  if (moved_from_op_root_relpath)
11540251881Speter    *moved_from_op_root_relpath = NULL;
11541251881Speter  if (moved_from_op_depth)
11542251881Speter    *moved_from_op_depth = 0;
11543251881Speter
11544251881Speter  {
11545251881Speter    svn_sqlite__stmt_t *stmt;
11546251881Speter    svn_boolean_t have_row;
11547251881Speter    svn_wc__db_status_t presence;
11548251881Speter    int op_depth;
11549251881Speter    const char *repos_prefix_path = "";
11550251881Speter    int i;
11551251881Speter
11552251881Speter    /* ### is it faster to fetch fewer columns? */
11553251881Speter    SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11554251881Speter                                      STMT_SELECT_WORKING_NODE));
11555251881Speter    SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11556251881Speter    SVN_ERR(svn_sqlite__step(&have_row, stmt));
11557251881Speter
11558251881Speter    if (!have_row)
11559251881Speter      {
11560251881Speter        /* Reset statement before returning */
11561251881Speter        SVN_ERR(svn_sqlite__reset(stmt));
11562251881Speter
11563251881Speter        /* ### maybe we should return a usage error instead?  */
11564251881Speter        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
11565251881Speter                                 _("The node '%s' was not found."),
11566251881Speter                                 path_for_error_message(wcroot,
11567251881Speter                                                        local_relpath,
11568251881Speter                                                        scratch_pool));
11569251881Speter      }
11570251881Speter
11571251881Speter    presence = svn_sqlite__column_token(stmt, 1, presence_map);
11572251881Speter
11573251881Speter    /* The starting node should exist normally.  */
11574251881Speter    op_depth = svn_sqlite__column_int(stmt, 0);
11575251881Speter    if (op_depth == 0 || (presence != svn_wc__db_status_normal
11576251881Speter                          && presence != svn_wc__db_status_incomplete))
11577251881Speter      /* reset the statement as part of the error generation process */
11578251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
11579251881Speter                               svn_sqlite__reset(stmt),
11580251881Speter                               _("Expected node '%s' to be added."),
11581251881Speter                               path_for_error_message(wcroot,
11582251881Speter                                                      local_relpath,
11583251881Speter                                                      scratch_pool));
11584251881Speter
11585251881Speter    if (original_revision)
11586251881Speter      *original_revision = svn_sqlite__column_revnum(stmt, 12);
11587251881Speter
11588251881Speter    /* Provide the default status; we'll override as appropriate. */
11589251881Speter    if (status)
11590251881Speter      {
11591251881Speter        if (presence == svn_wc__db_status_normal)
11592251881Speter          *status = svn_wc__db_status_added;
11593251881Speter        else
11594251881Speter          *status = svn_wc__db_status_incomplete;
11595251881Speter      }
11596251881Speter
11597251881Speter
11598251881Speter    /* Calculate the op root local path components */
11599251881Speter    op_root_relpath = local_relpath;
11600251881Speter
11601251881Speter    for (i = relpath_depth(local_relpath); i > op_depth; --i)
11602251881Speter      {
11603251881Speter        /* Calculate the path of the operation root */
11604251881Speter        repos_prefix_path =
11605251881Speter          svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11606251881Speter                           repos_prefix_path,
11607251881Speter                           scratch_pool);
11608251881Speter        op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
11609251881Speter      }
11610251881Speter
11611251881Speter    if (op_root_relpath_p)
11612251881Speter      *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
11613251881Speter
11614251881Speter    /* ### This if-statement is quite redundant.
11615251881Speter     * ### We're checking all these values again within the body anyway.
11616251881Speter     * ### The body should be broken up appropriately and move into the
11617251881Speter     * ### outer scope. */
11618251881Speter    if (original_repos_relpath
11619251881Speter        || original_repos_id
11620251881Speter        || (original_revision
11621251881Speter                && *original_revision == SVN_INVALID_REVNUM)
11622251881Speter        || status
11623251881Speter        || moved_from_relpath || moved_from_op_root_relpath)
11624251881Speter      {
11625251881Speter        if (local_relpath != op_root_relpath)
11626251881Speter          /* requery to get the add/copy root */
11627251881Speter          {
11628251881Speter            SVN_ERR(svn_sqlite__reset(stmt));
11629251881Speter
11630251881Speter            SVN_ERR(svn_sqlite__bindf(stmt, "is",
11631251881Speter                                      wcroot->wc_id, op_root_relpath));
11632251881Speter            SVN_ERR(svn_sqlite__step(&have_row, stmt));
11633251881Speter
11634251881Speter            if (!have_row)
11635251881Speter              {
11636251881Speter                /* Reset statement before returning */
11637251881Speter                SVN_ERR(svn_sqlite__reset(stmt));
11638251881Speter
11639251881Speter                /* ### maybe we should return a usage error instead?  */
11640251881Speter                return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
11641251881Speter                                         _("The node '%s' was not found."),
11642251881Speter                                         path_for_error_message(wcroot,
11643251881Speter                                                                op_root_relpath,
11644251881Speter                                                                scratch_pool));
11645251881Speter              }
11646251881Speter
11647251881Speter            if (original_revision
11648251881Speter                    && *original_revision == SVN_INVALID_REVNUM)
11649251881Speter              *original_revision = svn_sqlite__column_revnum(stmt, 12);
11650251881Speter          }
11651251881Speter
11652251881Speter        if (original_repos_relpath)
11653251881Speter          *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
11654251881Speter                                                            result_pool);
11655251881Speter
11656251881Speter        if (!svn_sqlite__column_is_null(stmt, 10)
11657251881Speter            && (status
11658251881Speter                || original_repos_id
11659251881Speter                || moved_from_relpath || moved_from_op_root_relpath))
11660251881Speter          /* If column 10 (original_repos_id) is NULL,
11661251881Speter             this is a plain add, not a copy or a move */
11662251881Speter          {
11663251881Speter            svn_boolean_t moved_here;
11664251881Speter            if (original_repos_id)
11665251881Speter              *original_repos_id = svn_sqlite__column_int64(stmt, 10);
11666251881Speter
11667251881Speter            moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
11668251881Speter            if (status)
11669251881Speter              *status = moved_here ? svn_wc__db_status_moved_here
11670251881Speter                                   : svn_wc__db_status_copied;
11671251881Speter
11672251881Speter            if (moved_here
11673251881Speter                && (moved_from_relpath || moved_from_op_root_relpath))
11674251881Speter              {
11675251881Speter                svn_error_t *err;
11676251881Speter
11677251881Speter                err = get_moved_from_info(moved_from_relpath,
11678251881Speter                                          moved_from_op_root_relpath,
11679251881Speter                                          op_root_relpath,
11680251881Speter                                          moved_from_op_depth,
11681251881Speter                                          wcroot, local_relpath,
11682251881Speter                                          result_pool,
11683251881Speter                                          scratch_pool);
11684251881Speter
11685251881Speter                if (err)
11686251881Speter                  return svn_error_compose_create(
11687251881Speter                                err, svn_sqlite__reset(stmt));
11688251881Speter              }
11689251881Speter          }
11690251881Speter      }
11691251881Speter
11692251881Speter
11693251881Speter    /* ### This loop here is to skip up to the first node which is a BASE node,
11694251881Speter       because base_get_info() doesn't accommodate the scenario that
11695251881Speter       we're looking at here; we found the true op_root, which may be inside
11696251881Speter       further changed trees. */
11697251881Speter    if (repos_relpath || repos_id)
11698251881Speter      {
11699251881Speter        const char *base_relpath;
11700251881Speter
11701251881Speter    while (TRUE)
11702251881Speter      {
11703251881Speter
11704251881Speter        SVN_ERR(svn_sqlite__reset(stmt));
11705251881Speter
11706251881Speter        /* Pointing at op_depth, look at the parent */
11707251881Speter        repos_prefix_path =
11708251881Speter          svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11709251881Speter                           repos_prefix_path,
11710251881Speter                           scratch_pool);
11711251881Speter        op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
11712251881Speter
11713251881Speter
11714251881Speter        SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
11715251881Speter        SVN_ERR(svn_sqlite__step(&have_row, stmt));
11716251881Speter
11717251881Speter        if (! have_row)
11718251881Speter          break;
11719251881Speter
11720251881Speter        op_depth = svn_sqlite__column_int(stmt, 0);
11721251881Speter
11722251881Speter        /* Skip to op_depth */
11723251881Speter        for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
11724251881Speter          {
11725251881Speter            /* Calculate the path of the operation root */
11726251881Speter            repos_prefix_path =
11727251881Speter              svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11728251881Speter                               repos_prefix_path,
11729251881Speter                               scratch_pool);
11730251881Speter            op_root_relpath =
11731251881Speter              svn_relpath_dirname(op_root_relpath, scratch_pool);
11732251881Speter          }
11733251881Speter      }
11734251881Speter
11735251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
11736251881Speter
11737251881Speter      build_relpath = repos_prefix_path;
11738251881Speter
11739251881Speter      /* If we're here, then we have an added/copied/moved (start) node, and
11740251881Speter         CURRENT_ABSPATH now points to a BASE node. Figure out the repository
11741251881Speter         information for the current node, and use that to compute the start
11742251881Speter         node's repository information.  */
11743251881Speter      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11744251881Speter                                                &base_relpath, repos_id,
11745251881Speter                                                NULL, NULL, NULL, NULL, NULL,
11746251881Speter                                                NULL, NULL, NULL, NULL, NULL,
11747251881Speter                                                wcroot, op_root_relpath,
11748251881Speter                                                scratch_pool, scratch_pool));
11749251881Speter
11750251881Speter        if (repos_relpath)
11751251881Speter          *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
11752251881Speter                                            result_pool);
11753251881Speter      }
11754251881Speter    else
11755251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
11756251881Speter  }
11757251881Speter  /* Postconditions */
11758251881Speter#ifdef SVN_DEBUG
11759251881Speter  if (status)
11760251881Speter    {
11761251881Speter      SVN_ERR_ASSERT(*status == svn_wc__db_status_added
11762251881Speter                     || *status == svn_wc__db_status_copied
11763251881Speter                     || *status == svn_wc__db_status_incomplete
11764251881Speter                     || *status == svn_wc__db_status_moved_here);
11765251881Speter      if (*status == svn_wc__db_status_added)
11766251881Speter        {
11767251881Speter          SVN_ERR_ASSERT(!original_repos_relpath
11768251881Speter                         || *original_repos_relpath == NULL);
11769251881Speter          SVN_ERR_ASSERT(!original_revision
11770251881Speter                         || *original_revision == SVN_INVALID_REVNUM);
11771251881Speter          SVN_ERR_ASSERT(!original_repos_id
11772251881Speter                         || *original_repos_id == INVALID_REPOS_ID);
11773251881Speter        }
11774251881Speter      /* An upgrade with a missing directory can leave INCOMPLETE working
11775251881Speter         op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
11776251881Speter       */
11777251881Speter      else if (*status != svn_wc__db_status_incomplete)
11778251881Speter        {
11779251881Speter          SVN_ERR_ASSERT(!original_repos_relpath
11780251881Speter                         || *original_repos_relpath != NULL);
11781251881Speter          SVN_ERR_ASSERT(!original_revision
11782251881Speter                         || *original_revision != SVN_INVALID_REVNUM);
11783251881Speter          SVN_ERR_ASSERT(!original_repos_id
11784251881Speter                         || *original_repos_id != INVALID_REPOS_ID);
11785251881Speter        }
11786251881Speter    }
11787251881Speter  SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
11788251881Speter#endif
11789251881Speter
11790251881Speter  return SVN_NO_ERROR;
11791251881Speter}
11792251881Speter
11793251881Speter
11794251881Speter/* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
11795251881Speter   DB+LOCAL_ABSPATH.
11796251881Speter
11797251881Speter   The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
11798251881Speter   is no 'copy-from' repository.  */
11799251881Speterstatic svn_error_t *
11800251881Speterscan_addition(svn_wc__db_status_t *status,
11801251881Speter              const char **op_root_relpath,
11802251881Speter              const char **repos_relpath,
11803251881Speter              apr_int64_t *repos_id,
11804251881Speter              const char **original_repos_relpath,
11805251881Speter              apr_int64_t *original_repos_id,
11806251881Speter              svn_revnum_t *original_revision,
11807251881Speter              const char **moved_from_relpath,
11808251881Speter              const char **moved_from_op_root_relpath,
11809251881Speter              int *moved_from_op_depth,
11810251881Speter              svn_wc__db_wcroot_t *wcroot,
11811251881Speter              const char *local_relpath,
11812251881Speter              apr_pool_t *result_pool,
11813251881Speter              apr_pool_t *scratch_pool)
11814251881Speter{
11815251881Speter  SVN_WC__DB_WITH_TXN(
11816251881Speter    scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
11817251881Speter                      original_repos_relpath, original_repos_id,
11818251881Speter                      original_revision, moved_from_relpath,
11819251881Speter                      moved_from_op_root_relpath, moved_from_op_depth,
11820251881Speter                      wcroot, local_relpath, result_pool, scratch_pool),
11821251881Speter    wcroot);
11822251881Speter  return SVN_NO_ERROR;
11823251881Speter}
11824251881Speter
11825251881Speter
11826251881Spetersvn_error_t *
11827251881Spetersvn_wc__db_scan_addition(svn_wc__db_status_t *status,
11828251881Speter                         const char **op_root_abspath,
11829251881Speter                         const char **repos_relpath,
11830251881Speter                         const char **repos_root_url,
11831251881Speter                         const char **repos_uuid,
11832251881Speter                         const char **original_repos_relpath,
11833251881Speter                         const char **original_root_url,
11834251881Speter                         const char **original_uuid,
11835251881Speter                         svn_revnum_t *original_revision,
11836251881Speter                         svn_wc__db_t *db,
11837251881Speter                         const char *local_abspath,
11838251881Speter                         apr_pool_t *result_pool,
11839251881Speter                         apr_pool_t *scratch_pool)
11840251881Speter{
11841251881Speter  svn_wc__db_wcroot_t *wcroot;
11842251881Speter  const char *local_relpath;
11843251881Speter  const char *op_root_relpath = NULL;
11844251881Speter  apr_int64_t repos_id = INVALID_REPOS_ID;
11845251881Speter  apr_int64_t original_repos_id = INVALID_REPOS_ID;
11846251881Speter  apr_int64_t *repos_id_p
11847251881Speter    = (repos_root_url || repos_uuid) ? &repos_id : NULL;
11848251881Speter  apr_int64_t *original_repos_id_p
11849251881Speter    = (original_root_url || original_uuid) ? &original_repos_id : NULL;
11850251881Speter
11851251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11852251881Speter
11853251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11854251881Speter                              local_abspath, scratch_pool, scratch_pool));
11855251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11856251881Speter
11857251881Speter  SVN_ERR(scan_addition(status,
11858251881Speter                        op_root_abspath
11859251881Speter                                ? &op_root_relpath
11860251881Speter                                : NULL,
11861251881Speter                        repos_relpath, repos_id_p,
11862251881Speter                        original_repos_relpath, original_repos_id_p,
11863251881Speter                        original_revision,
11864251881Speter                        NULL, NULL, NULL,
11865251881Speter                        wcroot, local_relpath, result_pool, scratch_pool));
11866251881Speter
11867251881Speter  if (op_root_abspath)
11868251881Speter    *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
11869251881Speter                                       result_pool);
11870251881Speter  /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
11871251881Speter  SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
11872251881Speter
11873251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11874251881Speter                                      repos_id, result_pool));
11875251881Speter  SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
11876251881Speter                                      wcroot->sdb, original_repos_id,
11877251881Speter                                      result_pool));
11878251881Speter
11879251881Speter  return SVN_NO_ERROR;
11880251881Speter}
11881251881Speter
11882251881Spetersvn_error_t *
11883251881Spetersvn_wc__db_scan_moved(const char **moved_from_abspath,
11884251881Speter                      const char **op_root_abspath,
11885251881Speter                      const char **op_root_moved_from_abspath,
11886251881Speter                      const char **moved_from_delete_abspath,
11887251881Speter                      svn_wc__db_t *db,
11888251881Speter                      const char *local_abspath,
11889251881Speter                      apr_pool_t *result_pool,
11890251881Speter                      apr_pool_t *scratch_pool)
11891251881Speter{
11892251881Speter  svn_wc__db_wcroot_t *wcroot;
11893251881Speter  const char *local_relpath;
11894251881Speter  svn_wc__db_status_t status;
11895251881Speter  const char *op_root_relpath = NULL;
11896251881Speter  const char *moved_from_relpath = NULL;
11897251881Speter  const char *moved_from_op_root_relpath = NULL;
11898251881Speter  int moved_from_op_depth = -1;
11899251881Speter
11900251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11901251881Speter
11902251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11903251881Speter                              local_abspath, scratch_pool, scratch_pool));
11904251881Speter  VERIFY_USABLE_WCROOT(wcroot);
11905251881Speter
11906251881Speter  SVN_ERR(scan_addition(&status,
11907251881Speter                        op_root_abspath
11908251881Speter                                ? &op_root_relpath
11909251881Speter                                : NULL,
11910251881Speter                        NULL, NULL,
11911251881Speter                        NULL, NULL, NULL,
11912251881Speter                        moved_from_abspath
11913251881Speter                            ? &moved_from_relpath
11914251881Speter                            : NULL,
11915251881Speter                        (op_root_moved_from_abspath
11916251881Speter                         || moved_from_delete_abspath)
11917251881Speter                            ? &moved_from_op_root_relpath
11918251881Speter                            : NULL,
11919251881Speter                        moved_from_delete_abspath
11920251881Speter                            ? &moved_from_op_depth
11921251881Speter                            : NULL,
11922251881Speter                        wcroot, local_relpath, scratch_pool, scratch_pool));
11923251881Speter
11924251881Speter  if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
11925251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
11926251881Speter                             _("Path '%s' was not moved here"),
11927251881Speter                             path_for_error_message(wcroot, local_relpath,
11928251881Speter                                                    scratch_pool));
11929251881Speter
11930251881Speter  if (op_root_abspath)
11931251881Speter    *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
11932251881Speter                                       result_pool);
11933251881Speter
11934251881Speter  if (moved_from_abspath)
11935251881Speter    *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
11936251881Speter                                          result_pool);
11937251881Speter
11938251881Speter  if (op_root_moved_from_abspath)
11939251881Speter    *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
11940251881Speter                                                  moved_from_op_root_relpath,
11941251881Speter                                                  result_pool);
11942251881Speter
11943251881Speter  /* The deleted node is either where we moved from, or one of its ancestors */
11944251881Speter  if (moved_from_delete_abspath)
11945251881Speter    {
11946251881Speter      const char *tmp = moved_from_op_root_relpath;
11947251881Speter
11948251881Speter      SVN_ERR_ASSERT(moved_from_op_depth >= 0);
11949251881Speter
11950251881Speter      while (relpath_depth(tmp) > moved_from_op_depth)
11951251881Speter        tmp = svn_relpath_dirname(tmp, scratch_pool);
11952251881Speter
11953251881Speter      *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
11954251881Speter                                                   scratch_pool);
11955251881Speter    }
11956251881Speter
11957251881Speter  return SVN_NO_ERROR;
11958251881Speter}
11959251881Speter
11960251881Speter/* ###
11961251881Speter */
11962251881Speterstatic svn_error_t *
11963251881Speterfollow_moved_to(apr_array_header_t **moved_tos,
11964251881Speter                int op_depth,
11965251881Speter                const char *repos_path,
11966251881Speter                svn_revnum_t revision,
11967251881Speter                svn_wc__db_wcroot_t *wcroot,
11968251881Speter                const char *local_relpath,
11969251881Speter                apr_pool_t *result_pool,
11970251881Speter                apr_pool_t *scratch_pool)
11971251881Speter{
11972251881Speter  svn_sqlite__stmt_t *stmt;
11973251881Speter  svn_boolean_t have_row;
11974251881Speter  int working_op_depth;
11975251881Speter  const char *ancestor_relpath, *node_moved_to = NULL;
11976251881Speter  int i;
11977251881Speter
11978251881Speter  SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
11979251881Speter
11980251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11981251881Speter                                    STMT_SELECT_OP_DEPTH_MOVED_TO));
11982251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
11983251881Speter                            op_depth));
11984251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
11985251881Speter  if (have_row)
11986251881Speter    {
11987251881Speter      working_op_depth = svn_sqlite__column_int(stmt, 0);
11988251881Speter      node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
11989251881Speter      if (!repos_path)
11990251881Speter        {
11991251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
11992251881Speter          if (!have_row || svn_sqlite__column_revnum(stmt, 0))
11993251881Speter            return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11994251881Speter                                     svn_sqlite__reset(stmt),
11995251881Speter                                     _("The base node '%s' was not found."),
11996251881Speter                                     path_for_error_message(wcroot,
11997251881Speter                                                            local_relpath,
11998251881Speter                                                            scratch_pool));
11999251881Speter          repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12000251881Speter          revision = svn_sqlite__column_revnum(stmt, 3);
12001251881Speter        }
12002251881Speter    }
12003251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
12004251881Speter
12005251881Speter  if (node_moved_to)
12006251881Speter    {
12007251881Speter      svn_boolean_t have_row2;
12008251881Speter
12009251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12010251881Speter                                        STMT_SELECT_MOVED_HERE));
12011251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12012251881Speter                                relpath_depth(node_moved_to)));
12013251881Speter      SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12014251881Speter      if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12015251881Speter          || revision != svn_sqlite__column_revnum(stmt, 3)
12016251881Speter          || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12017251881Speter        node_moved_to = NULL;
12018251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12019251881Speter    }
12020251881Speter
12021251881Speter  if (node_moved_to)
12022251881Speter    {
12023251881Speter      struct svn_wc__db_moved_to_t *moved_to;
12024251881Speter
12025251881Speter      moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12026251881Speter      moved_to->op_depth = working_op_depth;
12027251881Speter      moved_to->local_relpath = node_moved_to;
12028251881Speter      APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12029251881Speter    }
12030251881Speter
12031251881Speter  /* A working row with moved_to, or no working row, and we are done. */
12032251881Speter  if (node_moved_to || !have_row)
12033251881Speter    return SVN_NO_ERROR;
12034251881Speter
12035251881Speter  /* Need to handle being moved via an ancestor. */
12036251881Speter  ancestor_relpath = local_relpath;
12037251881Speter  for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12038251881Speter    {
12039251881Speter      const char *ancestor_moved_to;
12040251881Speter
12041251881Speter      ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12042251881Speter
12043251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12044251881Speter                                        STMT_SELECT_MOVED_TO));
12045251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12046251881Speter                                working_op_depth));
12047251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12048251881Speter      SVN_ERR_ASSERT(have_row);
12049251881Speter      ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12050251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12051251881Speter      if (ancestor_moved_to)
12052251881Speter        {
12053251881Speter          node_moved_to
12054251881Speter            = svn_relpath_join(ancestor_moved_to,
12055251881Speter                               svn_relpath_skip_ancestor(ancestor_relpath,
12056251881Speter                                                         local_relpath),
12057251881Speter                               result_pool);
12058251881Speter
12059251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12060251881Speter                                            STMT_SELECT_MOVED_HERE));
12061251881Speter          SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12062251881Speter                                    relpath_depth(ancestor_moved_to)));
12063251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
12064251881Speter          if (!have_row)
12065251881Speter            ancestor_moved_to = NULL;
12066251881Speter          else if (!svn_sqlite__column_int(stmt, 0))
12067251881Speter            {
12068251881Speter              svn_wc__db_status_t presence
12069251881Speter                = svn_sqlite__column_token(stmt, 1, presence_map);
12070251881Speter              if (presence != svn_wc__db_status_not_present)
12071251881Speter                ancestor_moved_to = NULL;
12072251881Speter              else
12073251881Speter                {
12074251881Speter                  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12075251881Speter                  if (!have_row && !svn_sqlite__column_int(stmt, 0))
12076251881Speter                    ancestor_moved_to = NULL;
12077251881Speter                }
12078251881Speter            }
12079251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
12080251881Speter          if (!ancestor_moved_to)
12081251881Speter            break;
12082251881Speter          /* verify repos_path points back? */
12083251881Speter        }
12084251881Speter      if (ancestor_moved_to)
12085251881Speter        {
12086251881Speter          struct svn_wc__db_moved_to_t *moved_to;
12087251881Speter
12088251881Speter          moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12089251881Speter          moved_to->op_depth = working_op_depth;
12090251881Speter          moved_to->local_relpath = node_moved_to;
12091251881Speter          APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12092251881Speter
12093251881Speter          SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12094251881Speter                                  repos_path, revision, wcroot, node_moved_to,
12095251881Speter                                  result_pool, scratch_pool));
12096251881Speter          break;
12097251881Speter        }
12098251881Speter    }
12099251881Speter
12100251881Speter  return SVN_NO_ERROR;
12101251881Speter}
12102251881Speter
12103251881Spetersvn_error_t *
12104251881Spetersvn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12105251881Speter                           svn_wc__db_t *db,
12106251881Speter                           const char *local_abspath,
12107251881Speter                           apr_pool_t *result_pool,
12108251881Speter                           apr_pool_t *scratch_pool)
12109251881Speter{
12110251881Speter  svn_wc__db_wcroot_t *wcroot;
12111251881Speter  const char *local_relpath;
12112251881Speter
12113251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12114251881Speter
12115251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12116251881Speter                              local_abspath, scratch_pool, scratch_pool));
12117251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12118251881Speter
12119251881Speter  *moved_tos = apr_array_make(result_pool, 0,
12120251881Speter                              sizeof(struct svn_wc__db_moved_to_t *));
12121251881Speter
12122251881Speter  /* ### Wrap in a transaction */
12123251881Speter  SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12124251881Speter                          wcroot, local_relpath,
12125251881Speter                          result_pool, scratch_pool));
12126251881Speter
12127251881Speter  /* ### Convert moved_to to abspath */
12128251881Speter
12129251881Speter  return SVN_NO_ERROR;
12130251881Speter}
12131251881Speter
12132251881Speter/* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12133251881Speter   examining the lowest working node above OP_DEPTH.  The output paths
12134251881Speter   are NULL if there is no move, otherwise:
12135251881Speter
12136251881Speter   *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12137251881Speter
12138251881Speter   *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12139251881Speter   the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12140251881Speter   if LOCAL_RELPATH is the root of the move.
12141251881Speter
12142251881Speter   *MOVE_SRC_ROOT_RELPATH: the root of the move source.  For moves
12143251881Speter   inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12144251881Speter
12145251881Speter   *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12146251881Speter   contains the move.  For moves inside deletes this is the root of
12147251881Speter   the delete, for other moves this is the root of the move.
12148251881Speter
12149251881Speter   Given a path A/B/C with A/B moved to X then for A/B/C
12150251881Speter
12151251881Speter     MOVE_DST_RELPATH is X/C
12152251881Speter     MOVE_DST_OP_ROOT_RELPATH is X
12153251881Speter     MOVE_SRC_ROOT_RELPATH is A/B
12154251881Speter     MOVE_SRC_OP_ROOT_RELPATH is A/B
12155251881Speter
12156251881Speter   If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12157251881Speter   and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12158251881Speter   changes to A.
12159251881Speter
12160251881Speter   ### Think about combining with scan_deletion?  Also with
12161251881Speter   ### scan_addition to get moved-to for replaces?  Do we need to
12162251881Speter   ### return the op-root of the move source, i.e. A/B in the example
12163251881Speter   ### above?  */
12164251881Spetersvn_error_t *
12165251881Spetersvn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12166251881Speter                             const char **move_dst_op_root_relpath,
12167251881Speter                             const char **move_src_root_relpath,
12168251881Speter                             const char **move_src_op_root_relpath,
12169251881Speter                             int op_depth,
12170251881Speter                             svn_wc__db_wcroot_t *wcroot,
12171251881Speter                             const char *local_relpath,
12172251881Speter                             apr_pool_t *result_pool,
12173251881Speter                             apr_pool_t *scratch_pool)
12174251881Speter{
12175251881Speter  svn_sqlite__stmt_t *stmt;
12176251881Speter  svn_boolean_t have_row;
12177251881Speter  int delete_op_depth;
12178251881Speter  const char *relpath = local_relpath;
12179251881Speter
12180251881Speter  *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12181251881Speter  *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12182251881Speter
12183251881Speter  do
12184251881Speter    {
12185251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12186251881Speter                                        STMT_SELECT_LOWEST_WORKING_NODE));
12187251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12188251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12189251881Speter      if (have_row)
12190251881Speter        {
12191251881Speter          delete_op_depth = svn_sqlite__column_int(stmt, 0);
12192251881Speter          *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12193251881Speter                                                              result_pool);
12194251881Speter          if (*move_dst_op_root_relpath)
12195251881Speter            *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12196251881Speter        }
12197251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
12198251881Speter      if (!*move_dst_op_root_relpath)
12199251881Speter        relpath = svn_relpath_dirname(relpath, scratch_pool);
12200251881Speter    }
12201251881Speter  while (!*move_dst_op_root_relpath
12202251881Speter        && have_row && delete_op_depth <= relpath_depth(relpath));
12203251881Speter
12204251881Speter  if (*move_dst_op_root_relpath)
12205251881Speter    {
12206251881Speter      *move_dst_relpath
12207251881Speter        = svn_relpath_join(*move_dst_op_root_relpath,
12208251881Speter                           svn_relpath_skip_ancestor(relpath, local_relpath),
12209251881Speter                           result_pool);
12210251881Speter      while (delete_op_depth < relpath_depth(relpath))
12211251881Speter        relpath = svn_relpath_dirname(relpath, scratch_pool);
12212251881Speter      *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12213251881Speter    }
12214251881Speter
12215251881Speter  return SVN_NO_ERROR;
12216251881Speter}
12217251881Speter
12218251881Speter/* Public (within libsvn_wc) absolute path version of
12219251881Speter   svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12220251881Speter   BASE. */
12221251881Spetersvn_error_t *
12222251881Spetersvn_wc__db_base_moved_to(const char **move_dst_abspath,
12223251881Speter                         const char **move_dst_op_root_abspath,
12224251881Speter                         const char **move_src_root_abspath,
12225251881Speter                         const char **move_src_op_root_abspath,
12226251881Speter                         svn_wc__db_t *db,
12227251881Speter                         const char *local_abspath,
12228251881Speter                         apr_pool_t *result_pool,
12229251881Speter                         apr_pool_t *scratch_pool)
12230251881Speter{
12231251881Speter  svn_wc__db_wcroot_t *wcroot;
12232251881Speter  const char *local_relpath;
12233251881Speter  const char *move_dst_relpath, *move_dst_op_root_relpath;
12234251881Speter  const char *move_src_root_relpath, *move_src_op_root_relpath;
12235251881Speter
12236251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12237251881Speter
12238251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12239251881Speter                              local_abspath, scratch_pool, scratch_pool));
12240251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12241251881Speter
12242251881Speter  SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12243251881Speter                                                   &move_dst_op_root_relpath,
12244251881Speter                                                   &move_src_root_relpath,
12245251881Speter                                                   &move_src_op_root_relpath,
12246251881Speter                                                   0 /* BASE op-depth */,
12247251881Speter                                                   wcroot, local_relpath,
12248251881Speter                                                   scratch_pool, scratch_pool),
12249251881Speter                      wcroot);
12250251881Speter
12251251881Speter  if (move_dst_abspath)
12252251881Speter    *move_dst_abspath
12253251881Speter      = move_dst_relpath
12254251881Speter      ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12255251881Speter      : NULL;
12256251881Speter
12257251881Speter  if (move_dst_op_root_abspath)
12258251881Speter    *move_dst_op_root_abspath
12259251881Speter      = move_dst_op_root_relpath
12260251881Speter      ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12261251881Speter      : NULL;
12262251881Speter
12263251881Speter  if (move_src_root_abspath)
12264251881Speter    *move_src_root_abspath
12265251881Speter      = move_src_root_relpath
12266251881Speter      ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12267251881Speter      : NULL;
12268251881Speter
12269251881Speter  if (move_src_op_root_abspath)
12270251881Speter    *move_src_op_root_abspath
12271251881Speter      = move_src_op_root_relpath
12272251881Speter      ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12273251881Speter      : NULL;
12274251881Speter
12275251881Speter  return SVN_NO_ERROR;
12276251881Speter}
12277251881Speter
12278251881Spetersvn_error_t *
12279251881Spetersvn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12280251881Speter                         apr_int64_t *repos_id,
12281251881Speter                         apr_int64_t *wc_id,
12282251881Speter                         svn_wc__db_t *wc_db,
12283251881Speter                         const char *dir_abspath,
12284251881Speter                         const char *repos_root_url,
12285251881Speter                         const char *repos_uuid,
12286251881Speter                         apr_pool_t *scratch_pool)
12287251881Speter{
12288251881Speter  svn_wc__db_wcroot_t *wcroot;
12289251881Speter
12290251881Speter  /* Upgrade is inherently exclusive so specify exclusive locking. */
12291251881Speter  SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12292251881Speter                    repos_root_url, repos_uuid,
12293251881Speter                    SDB_FILE,
12294251881Speter                    NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12295251881Speter                    TRUE /* exclusive */,
12296251881Speter                    wc_db->state_pool, scratch_pool));
12297251881Speter
12298251881Speter  SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12299251881Speter                                       apr_pstrdup(wc_db->state_pool,
12300251881Speter                                                   dir_abspath),
12301251881Speter                                       *sdb, *wc_id, FORMAT_FROM_SDB,
12302251881Speter                                       FALSE /* auto-upgrade */,
12303251881Speter                                       FALSE /* enforce_empty_wq */,
12304251881Speter                                       wc_db->state_pool, scratch_pool));
12305251881Speter
12306251881Speter  /* The WCROOT is complete. Stash it into DB.  */
12307251881Speter  svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12308251881Speter
12309251881Speter  return SVN_NO_ERROR;
12310251881Speter}
12311251881Speter
12312251881Speter
12313251881Spetersvn_error_t *
12314251881Spetersvn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12315251881Speter                                   const char *dir_relpath,
12316251881Speter                                   apr_hash_t *cache_values,
12317251881Speter                                   apr_pool_t *scratch_pool)
12318251881Speter{
12319251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12320251881Speter  apr_int64_t wc_id;
12321251881Speter  apr_hash_index_t *hi;
12322251881Speter  svn_sqlite__stmt_t *stmt;
12323251881Speter
12324251881Speter  SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12325251881Speter
12326251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12327251881Speter                                    STMT_UPDATE_BASE_NODE_DAV_CACHE));
12328251881Speter
12329251881Speter  /* Iterate over all the wcprops, writing each one to the wc_db. */
12330251881Speter  for (hi = apr_hash_first(scratch_pool, cache_values);
12331251881Speter       hi;
12332251881Speter       hi = apr_hash_next(hi))
12333251881Speter    {
12334251881Speter      const char *name = svn__apr_hash_index_key(hi);
12335251881Speter      apr_hash_t *props = svn__apr_hash_index_val(hi);
12336251881Speter      const char *local_relpath;
12337251881Speter
12338251881Speter      svn_pool_clear(iterpool);
12339251881Speter
12340251881Speter      local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12341251881Speter
12342251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12343251881Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12344251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
12345251881Speter    }
12346251881Speter
12347251881Speter  svn_pool_destroy(iterpool);
12348251881Speter
12349251881Speter  return SVN_NO_ERROR;
12350251881Speter}
12351251881Speter
12352251881Speter
12353251881Spetersvn_error_t *
12354251881Spetersvn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12355251881Speter                               const char *dir_abspath,
12356251881Speter                               const char *local_relpath,
12357251881Speter                               apr_hash_t *base_props,
12358251881Speter                               apr_hash_t *revert_props,
12359251881Speter                               apr_hash_t *working_props,
12360251881Speter                               int original_format,
12361251881Speter                               apr_int64_t wc_id,
12362251881Speter                               apr_pool_t *scratch_pool)
12363251881Speter{
12364251881Speter  svn_sqlite__stmt_t *stmt;
12365251881Speter  svn_boolean_t have_row;
12366251881Speter  int top_op_depth = -1;
12367251881Speter  int below_op_depth = -1;
12368251881Speter  svn_wc__db_status_t top_presence;
12369251881Speter  svn_wc__db_status_t below_presence;
12370251881Speter  int affected_rows;
12371251881Speter
12372251881Speter  /* ### working_props: use set_props_txn.
12373251881Speter     ### if working_props == NULL, then skip. what if they equal the
12374251881Speter     ### pristine props? we should probably do the compare here.
12375251881Speter     ###
12376251881Speter     ### base props go into WORKING_NODE if avail, otherwise BASE.
12377251881Speter     ###
12378251881Speter     ### revert only goes into BASE. (and WORKING better be there!)
12379251881Speter
12380251881Speter     Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12381251881Speter     file was deleted, then a copy (potentially with props) was disallowed
12382251881Speter     and could not replace the deletion. An addition *could* be performed,
12383251881Speter     but that would never bring its own props.
12384251881Speter
12385251881Speter     1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12386251881Speter     bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12387251881Speter     construct a REVERT_PROPS if the target had no props. Thus, reverting
12388251881Speter     the delete/copy would see no REVERT_PROPS to restore, leaving the
12389251881Speter     props from the copy source intact, and appearing as if they are (now)
12390251881Speter     the base props for the previously-deleted file. (wc corruption)
12391251881Speter
12392251881Speter     1.4.6 ensured that an empty REVERT_PROPS would be established at all
12393251881Speter     times. See issue 2530, and r861670 as starting points.
12394251881Speter
12395251881Speter     We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12396251881Speter     the handling of our inputs, relative to the state of this node.
12397251881Speter  */
12398251881Speter
12399251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12400251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12401251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12402251881Speter  if (have_row)
12403251881Speter    {
12404251881Speter      top_op_depth = svn_sqlite__column_int(stmt, 0);
12405251881Speter      top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12406251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12407251881Speter      if (have_row)
12408251881Speter        {
12409251881Speter          below_op_depth = svn_sqlite__column_int(stmt, 0);
12410251881Speter          below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12411251881Speter        }
12412251881Speter    }
12413251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
12414251881Speter
12415251881Speter  /* Detect the buggy scenario described above. We cannot upgrade this
12416251881Speter     working copy if we have no idea where BASE_PROPS should go.  */
12417251881Speter  if (original_format > SVN_WC__NO_REVERT_FILES
12418251881Speter      && revert_props == NULL
12419251881Speter      && top_op_depth != -1
12420251881Speter      && top_presence == svn_wc__db_status_normal
12421251881Speter      && below_op_depth != -1
12422251881Speter      && below_presence != svn_wc__db_status_not_present)
12423251881Speter    {
12424251881Speter      /* There should be REVERT_PROPS, so it appears that we just ran into
12425251881Speter         the described bug. Sigh.  */
12426251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12427251881Speter                               _("The properties of '%s' are in an "
12428251881Speter                                 "indeterminate state and cannot be "
12429251881Speter                                 "upgraded. See issue #2530."),
12430251881Speter                               svn_dirent_local_style(
12431251881Speter                                 svn_dirent_join(dir_abspath, local_relpath,
12432251881Speter                                                 scratch_pool), scratch_pool));
12433251881Speter    }
12434251881Speter
12435251881Speter  /* Need at least one row, or two rows if there are revert props */
12436251881Speter  if (top_op_depth == -1
12437251881Speter      || (below_op_depth == -1 && revert_props))
12438251881Speter    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12439251881Speter                             _("Insufficient NODES rows for '%s'"),
12440251881Speter                             svn_dirent_local_style(
12441251881Speter                               svn_dirent_join(dir_abspath, local_relpath,
12442251881Speter                                               scratch_pool), scratch_pool));
12443251881Speter
12444251881Speter  /* one row, base props only: upper row gets base props
12445251881Speter     two rows, base props only: lower row gets base props
12446251881Speter     two rows, revert props only: lower row gets revert props
12447251881Speter     two rows, base and revert props: upper row gets base, lower gets revert */
12448251881Speter
12449251881Speter
12450251881Speter  if (revert_props || below_op_depth == -1)
12451251881Speter    {
12452251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12453251881Speter                                        STMT_UPDATE_NODE_PROPS));
12454251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12455251881Speter                                wc_id, local_relpath, top_op_depth));
12456251881Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12457251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12458251881Speter
12459251881Speter      SVN_ERR_ASSERT(affected_rows == 1);
12460251881Speter    }
12461251881Speter
12462251881Speter  if (below_op_depth != -1)
12463251881Speter    {
12464251881Speter      apr_hash_t *props = revert_props ? revert_props : base_props;
12465251881Speter
12466251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12467251881Speter                                        STMT_UPDATE_NODE_PROPS));
12468251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12469251881Speter                                wc_id, local_relpath, below_op_depth));
12470251881Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
12471251881Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12472251881Speter
12473251881Speter      SVN_ERR_ASSERT(affected_rows == 1);
12474251881Speter    }
12475251881Speter
12476251881Speter  /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE.  */
12477251881Speter  if (working_props != NULL
12478251881Speter      && base_props != NULL)
12479251881Speter    {
12480251881Speter      apr_array_header_t *diffs;
12481251881Speter
12482251881Speter      SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
12483251881Speter
12484251881Speter      if (diffs->nelts == 0)
12485251881Speter        working_props = NULL; /* No differences */
12486251881Speter    }
12487251881Speter
12488251881Speter  if (working_props != NULL)
12489251881Speter    {
12490251881Speter      SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
12491251881Speter                               sdb, scratch_pool));
12492251881Speter    }
12493251881Speter
12494251881Speter  return SVN_NO_ERROR;
12495251881Speter}
12496251881Speter
12497251881Spetersvn_error_t *
12498251881Spetersvn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
12499251881Speter                                   const char *local_abspath,
12500251881Speter                                   svn_node_kind_t kind,
12501251881Speter                                   const char *parent_abspath,
12502251881Speter                                   const char *def_local_abspath,
12503251881Speter                                   const char *repos_relpath,
12504251881Speter                                   const char *repos_root_url,
12505251881Speter                                   const char *repos_uuid,
12506251881Speter                                   svn_revnum_t def_peg_revision,
12507251881Speter                                   svn_revnum_t def_revision,
12508251881Speter                                   apr_pool_t *scratch_pool)
12509251881Speter{
12510251881Speter  svn_wc__db_wcroot_t *wcroot;
12511251881Speter  const char *def_local_relpath;
12512251881Speter  svn_sqlite__stmt_t *stmt;
12513251881Speter  svn_boolean_t have_row;
12514251881Speter  apr_int64_t repos_id;
12515251881Speter
12516251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12517251881Speter
12518251881Speter  /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
12519251881Speter   * WC, i.e. where the svn:externals prop is set. The external target path
12520251881Speter   * itself may be "hidden behind" other working copies. */
12521251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
12522251881Speter                                                db, def_local_abspath,
12523251881Speter                                                scratch_pool, scratch_pool));
12524251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12525251881Speter
12526251881Speter
12527251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12528251881Speter                                    STMT_SELECT_REPOSITORY));
12529251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
12530251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12531251881Speter
12532251881Speter  if (have_row)
12533251881Speter    repos_id = svn_sqlite__column_int64(stmt, 0);
12534251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
12535251881Speter
12536251881Speter  if (!have_row)
12537251881Speter    {
12538251881Speter      /* Need to set up a new repository row. */
12539251881Speter      SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
12540251881Speter                              wcroot->sdb, scratch_pool));
12541251881Speter    }
12542251881Speter
12543251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12544251881Speter                                    STMT_INSERT_EXTERNAL));
12545251881Speter
12546251881Speter  /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
12547251881Speter   * repos_id, def_repos_relpath, def_operational_revision, def_revision */
12548251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
12549251881Speter                            wcroot->wc_id,
12550251881Speter                            svn_dirent_skip_ancestor(wcroot->abspath,
12551251881Speter                                                     local_abspath),
12552251881Speter                            svn_dirent_skip_ancestor(wcroot->abspath,
12553251881Speter                                                     parent_abspath),
12554251881Speter                            "normal",
12555251881Speter                            kind_map, kind,
12556251881Speter                            def_local_relpath,
12557251881Speter                            repos_id,
12558251881Speter                            repos_relpath));
12559251881Speter
12560251881Speter  if (SVN_IS_VALID_REVNUM(def_peg_revision))
12561251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
12562251881Speter
12563251881Speter  if (SVN_IS_VALID_REVNUM(def_revision))
12564251881Speter    SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
12565251881Speter
12566251881Speter  SVN_ERR(svn_sqlite__insert(NULL, stmt));
12567251881Speter
12568251881Speter  return SVN_NO_ERROR;
12569251881Speter}
12570251881Speter
12571251881Spetersvn_error_t *
12572251881Spetersvn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
12573251881Speter                                svn_sqlite__db_t *sdb,
12574251881Speter                                const char *repos_root_url,
12575251881Speter                                apr_pool_t *scratch_pool)
12576251881Speter{
12577251881Speter  svn_sqlite__stmt_t *stmt;
12578251881Speter  svn_boolean_t have_row;
12579251881Speter
12580251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
12581251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
12582251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12583251881Speter
12584251881Speter  if (!have_row)
12585251881Speter    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
12586251881Speter                             _("Repository '%s' not found in the database"),
12587251881Speter                             repos_root_url);
12588251881Speter
12589251881Speter  *repos_id = svn_sqlite__column_int64(stmt, 0);
12590251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
12591251881Speter}
12592251881Speter
12593251881Speter
12594251881Spetersvn_error_t *
12595251881Spetersvn_wc__db_wq_add(svn_wc__db_t *db,
12596251881Speter                  const char *wri_abspath,
12597251881Speter                  const svn_skel_t *work_item,
12598251881Speter                  apr_pool_t *scratch_pool)
12599251881Speter{
12600251881Speter  svn_wc__db_wcroot_t *wcroot;
12601251881Speter  const char *local_relpath;
12602251881Speter
12603251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12604251881Speter
12605251881Speter  /* Quick exit, if there are no work items to queue up.  */
12606251881Speter  if (work_item == NULL)
12607251881Speter    return SVN_NO_ERROR;
12608251881Speter
12609251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12610251881Speter                              wri_abspath, scratch_pool, scratch_pool));
12611251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12612251881Speter
12613251881Speter  /* Add the work item(s) to the WORK_QUEUE.  */
12614251881Speter  return svn_error_trace(add_work_items(wcroot->sdb, work_item,
12615251881Speter                                        scratch_pool));
12616251881Speter}
12617251881Speter
12618251881Speter/* The body of svn_wc__db_wq_fetch_next().
12619251881Speter */
12620251881Speterstatic svn_error_t *
12621251881Speterwq_fetch_next(apr_uint64_t *id,
12622251881Speter              svn_skel_t **work_item,
12623251881Speter              svn_wc__db_wcroot_t *wcroot,
12624251881Speter              const char *local_relpath,
12625251881Speter              apr_uint64_t completed_id,
12626251881Speter              apr_pool_t *result_pool,
12627251881Speter              apr_pool_t *scratch_pool)
12628251881Speter{
12629251881Speter  svn_sqlite__stmt_t *stmt;
12630251881Speter  svn_boolean_t have_row;
12631251881Speter
12632251881Speter  if (completed_id != 0)
12633251881Speter    {
12634251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12635251881Speter                                        STMT_DELETE_WORK_ITEM));
12636251881Speter      SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
12637251881Speter
12638251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
12639251881Speter    }
12640251881Speter
12641251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12642251881Speter                                    STMT_SELECT_WORK_ITEM));
12643251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12644251881Speter
12645251881Speter  if (!have_row)
12646251881Speter    {
12647251881Speter      *id = 0;
12648251881Speter      *work_item = NULL;
12649251881Speter    }
12650251881Speter  else
12651251881Speter    {
12652251881Speter      apr_size_t len;
12653251881Speter      const void *val;
12654251881Speter
12655251881Speter      *id = svn_sqlite__column_int64(stmt, 0);
12656251881Speter
12657251881Speter      val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
12658251881Speter
12659251881Speter      *work_item = svn_skel__parse(val, len, result_pool);
12660251881Speter    }
12661251881Speter
12662251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
12663251881Speter}
12664251881Speter
12665251881Spetersvn_error_t *
12666251881Spetersvn_wc__db_wq_fetch_next(apr_uint64_t *id,
12667251881Speter                         svn_skel_t **work_item,
12668251881Speter                         svn_wc__db_t *db,
12669251881Speter                         const char *wri_abspath,
12670251881Speter                         apr_uint64_t completed_id,
12671251881Speter                         apr_pool_t *result_pool,
12672251881Speter                         apr_pool_t *scratch_pool)
12673251881Speter{
12674251881Speter  svn_wc__db_wcroot_t *wcroot;
12675251881Speter  const char *local_relpath;
12676251881Speter
12677251881Speter  SVN_ERR_ASSERT(id != NULL);
12678251881Speter  SVN_ERR_ASSERT(work_item != NULL);
12679251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12680251881Speter
12681251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12682251881Speter                              wri_abspath, scratch_pool, scratch_pool));
12683251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12684251881Speter
12685251881Speter  SVN_WC__DB_WITH_TXN(
12686251881Speter    wq_fetch_next(id, work_item,
12687251881Speter                  wcroot, local_relpath, completed_id,
12688251881Speter                  result_pool, scratch_pool),
12689251881Speter    wcroot);
12690251881Speter
12691251881Speter  return SVN_NO_ERROR;
12692251881Speter}
12693251881Speter
12694251881Speter/* Records timestamp and date for one or more files in wcroot */
12695251881Speterstatic svn_error_t *
12696251881Speterwq_record(svn_wc__db_wcroot_t *wcroot,
12697251881Speter          apr_hash_t *record_map,
12698251881Speter          apr_pool_t *scratch_pool)
12699251881Speter{
12700251881Speter  apr_hash_index_t *hi;
12701251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12702251881Speter
12703251881Speter  for (hi = apr_hash_first(scratch_pool, record_map); hi;
12704251881Speter       hi = apr_hash_next(hi))
12705251881Speter    {
12706251881Speter      const char *local_abspath = svn__apr_hash_index_key(hi);
12707251881Speter      const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
12708251881Speter      const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
12709251881Speter                                                           local_abspath);
12710251881Speter
12711251881Speter      svn_pool_clear(iterpool);
12712251881Speter
12713251881Speter      if (! local_relpath)
12714251881Speter        continue;
12715251881Speter
12716251881Speter      SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
12717251881Speter                                 dirent->filesize, dirent->mtime,
12718251881Speter                                 iterpool));
12719251881Speter    }
12720251881Speter
12721251881Speter  svn_pool_destroy(iterpool);
12722251881Speter  return SVN_NO_ERROR;
12723251881Speter}
12724251881Speter
12725251881Spetersvn_error_t *
12726251881Spetersvn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
12727251881Speter                                    svn_skel_t **work_item,
12728251881Speter                                    svn_wc__db_t *db,
12729251881Speter                                    const char *wri_abspath,
12730251881Speter                                    apr_uint64_t completed_id,
12731251881Speter                                    apr_hash_t *record_map,
12732251881Speter                                    apr_pool_t *result_pool,
12733251881Speter                                    apr_pool_t *scratch_pool)
12734251881Speter{
12735251881Speter  svn_wc__db_wcroot_t *wcroot;
12736251881Speter  const char *local_relpath;
12737251881Speter
12738251881Speter  SVN_ERR_ASSERT(id != NULL);
12739251881Speter  SVN_ERR_ASSERT(work_item != NULL);
12740251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12741251881Speter
12742251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12743251881Speter                              wri_abspath, scratch_pool, scratch_pool));
12744251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12745251881Speter
12746251881Speter  SVN_WC__DB_WITH_TXN(
12747251881Speter    svn_error_compose_create(
12748251881Speter            wq_fetch_next(id, work_item,
12749251881Speter                          wcroot, local_relpath, completed_id,
12750251881Speter                          result_pool, scratch_pool),
12751251881Speter            wq_record(wcroot, record_map, scratch_pool)),
12752251881Speter    wcroot);
12753251881Speter
12754251881Speter  return SVN_NO_ERROR;
12755251881Speter}
12756251881Speter
12757251881Speter
12758251881Speter
12759251881Speter/* ### temporary API. remove before release.  */
12760251881Spetersvn_error_t *
12761251881Spetersvn_wc__db_temp_get_format(int *format,
12762251881Speter                           svn_wc__db_t *db,
12763251881Speter                           const char *local_dir_abspath,
12764251881Speter                           apr_pool_t *scratch_pool)
12765251881Speter{
12766251881Speter  svn_wc__db_wcroot_t *wcroot;
12767251881Speter  const char *local_relpath;
12768251881Speter  svn_error_t *err;
12769251881Speter
12770251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12771251881Speter  /* ### assert that we were passed a directory?  */
12772251881Speter
12773251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12774251881Speter                                local_dir_abspath, scratch_pool, scratch_pool);
12775251881Speter
12776251881Speter  /* If we hit an error examining this directory, then declare this
12777251881Speter     directory to not be a working copy.  */
12778251881Speter  if (err)
12779251881Speter    {
12780251881Speter      if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
12781251881Speter        return svn_error_trace(err);
12782251881Speter      svn_error_clear(err);
12783251881Speter
12784251881Speter      /* Remap the returned error.  */
12785251881Speter      *format = 0;
12786251881Speter      return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
12787251881Speter                               _("'%s' is not a working copy"),
12788251881Speter                               svn_dirent_local_style(local_dir_abspath,
12789251881Speter                                                      scratch_pool));
12790251881Speter    }
12791251881Speter
12792251881Speter  SVN_ERR_ASSERT(wcroot != NULL);
12793251881Speter  SVN_ERR_ASSERT(wcroot->format >= 1);
12794251881Speter
12795251881Speter  *format = wcroot->format;
12796251881Speter
12797251881Speter  return SVN_NO_ERROR;
12798251881Speter}
12799251881Speter
12800251881Speter/* ### temporary API. remove before release.  */
12801251881Spetersvn_wc_adm_access_t *
12802251881Spetersvn_wc__db_temp_get_access(svn_wc__db_t *db,
12803251881Speter                           const char *local_dir_abspath,
12804251881Speter                           apr_pool_t *scratch_pool)
12805251881Speter{
12806251881Speter  const char *local_relpath;
12807251881Speter  svn_wc__db_wcroot_t *wcroot;
12808251881Speter  svn_error_t *err;
12809251881Speter
12810251881Speter  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12811251881Speter
12812251881Speter  /* ### we really need to assert that we were passed a directory. sometimes
12813251881Speter     ### adm_retrieve_internal is asked about a file, and then it asks us
12814251881Speter     ### for an access baton for it. we should definitely return NULL, but
12815251881Speter     ### ideally: the caller would never ask us about a non-directory.  */
12816251881Speter
12817251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12818251881Speter                            db, local_dir_abspath, scratch_pool, scratch_pool);
12819251881Speter  if (err)
12820251881Speter    {
12821251881Speter      svn_error_clear(err);
12822251881Speter      return NULL;
12823251881Speter    }
12824251881Speter
12825251881Speter  if (!wcroot)
12826251881Speter    return NULL;
12827251881Speter
12828251881Speter  return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
12829251881Speter}
12830251881Speter
12831251881Speter
12832251881Speter/* ### temporary API. remove before release.  */
12833251881Spetervoid
12834251881Spetersvn_wc__db_temp_set_access(svn_wc__db_t *db,
12835251881Speter                           const char *local_dir_abspath,
12836251881Speter                           svn_wc_adm_access_t *adm_access,
12837251881Speter                           apr_pool_t *scratch_pool)
12838251881Speter{
12839251881Speter  const char *local_relpath;
12840251881Speter  svn_wc__db_wcroot_t *wcroot;
12841251881Speter  svn_error_t *err;
12842251881Speter
12843251881Speter  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12844251881Speter  /* ### assert that we were passed a directory?  */
12845251881Speter
12846251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12847251881Speter                            db, local_dir_abspath, scratch_pool, scratch_pool);
12848251881Speter  if (err)
12849251881Speter    {
12850251881Speter      /* We don't even have a wcroot, so just bail. */
12851251881Speter      svn_error_clear(err);
12852251881Speter      return;
12853251881Speter    }
12854251881Speter
12855251881Speter  /* Better not override something already there.  */
12856251881Speter  SVN_ERR_ASSERT_NO_RETURN(
12857251881Speter    svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
12858251881Speter  );
12859251881Speter  svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
12860251881Speter}
12861251881Speter
12862251881Speter
12863251881Speter/* ### temporary API. remove before release.  */
12864251881Spetersvn_error_t *
12865251881Spetersvn_wc__db_temp_close_access(svn_wc__db_t *db,
12866251881Speter                             const char *local_dir_abspath,
12867251881Speter                             svn_wc_adm_access_t *adm_access,
12868251881Speter                             apr_pool_t *scratch_pool)
12869251881Speter{
12870251881Speter  const char *local_relpath;
12871251881Speter  svn_wc__db_wcroot_t *wcroot;
12872251881Speter
12873251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12874251881Speter  /* ### assert that we were passed a directory?  */
12875251881Speter
12876251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12877251881Speter                              local_dir_abspath, scratch_pool, scratch_pool));
12878251881Speter  svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
12879251881Speter
12880251881Speter  return SVN_NO_ERROR;
12881251881Speter}
12882251881Speter
12883251881Speter
12884251881Speter/* ### temporary API. remove before release.  */
12885251881Spetervoid
12886251881Spetersvn_wc__db_temp_clear_access(svn_wc__db_t *db,
12887251881Speter                             const char *local_dir_abspath,
12888251881Speter                             apr_pool_t *scratch_pool)
12889251881Speter{
12890251881Speter  const char *local_relpath;
12891251881Speter  svn_wc__db_wcroot_t *wcroot;
12892251881Speter  svn_error_t *err;
12893251881Speter
12894251881Speter  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12895251881Speter  /* ### assert that we were passed a directory?  */
12896251881Speter
12897251881Speter  err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12898251881Speter                            db, local_dir_abspath, scratch_pool, scratch_pool);
12899251881Speter  if (err)
12900251881Speter    {
12901251881Speter      svn_error_clear(err);
12902251881Speter      return;
12903251881Speter    }
12904251881Speter
12905251881Speter  svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
12906251881Speter}
12907251881Speter
12908251881Speter
12909251881Speterapr_hash_t *
12910251881Spetersvn_wc__db_temp_get_all_access(svn_wc__db_t *db,
12911251881Speter                               apr_pool_t *result_pool)
12912251881Speter{
12913251881Speter  apr_hash_t *result = apr_hash_make(result_pool);
12914251881Speter  apr_hash_index_t *hi;
12915251881Speter
12916251881Speter  for (hi = apr_hash_first(result_pool, db->dir_data);
12917251881Speter       hi;
12918251881Speter       hi = apr_hash_next(hi))
12919251881Speter    {
12920251881Speter      const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
12921251881Speter
12922251881Speter      /* This is highly redundant, 'cause the same WCROOT will appear many
12923251881Speter         times in dir_data. */
12924251881Speter      result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
12925251881Speter    }
12926251881Speter
12927251881Speter  return result;
12928251881Speter}
12929251881Speter
12930251881Speter
12931251881Spetersvn_error_t *
12932251881Spetersvn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
12933251881Speter                           svn_wc__db_t *db,
12934251881Speter                           const char *local_dir_abspath,
12935251881Speter                           apr_pool_t *scratch_pool)
12936251881Speter{
12937251881Speter  svn_wc__db_wcroot_t *wcroot;
12938251881Speter  const char *local_relpath;
12939251881Speter
12940251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12941251881Speter
12942251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12943251881Speter                            local_dir_abspath, scratch_pool, scratch_pool));
12944251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12945251881Speter
12946251881Speter  *sdb = wcroot->sdb;
12947251881Speter
12948251881Speter  return SVN_NO_ERROR;
12949251881Speter}
12950251881Speter
12951251881Speter
12952251881Spetersvn_error_t *
12953251881Spetersvn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
12954251881Speter                                 svn_wc__db_t *db,
12955251881Speter                                 const char *local_abspath,
12956251881Speter                                 apr_pool_t *result_pool,
12957251881Speter                                 apr_pool_t *scratch_pool)
12958251881Speter{
12959251881Speter  svn_wc__db_wcroot_t *wcroot;
12960251881Speter  const char *local_relpath;
12961251881Speter  svn_sqlite__stmt_t *stmt;
12962251881Speter  svn_boolean_t have_row;
12963251881Speter  apr_array_header_t *new_victims;
12964251881Speter
12965251881Speter  /* The parent should be a working copy directory. */
12966251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12967251881Speter                              local_abspath, scratch_pool, scratch_pool));
12968251881Speter  VERIFY_USABLE_WCROOT(wcroot);
12969251881Speter
12970251881Speter  /* ### This will be much easier once we have all conflicts in one
12971251881Speter         field of actual*/
12972251881Speter
12973251881Speter  /* Look for text, tree and property conflicts in ACTUAL */
12974251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12975251881Speter                                    STMT_SELECT_CONFLICT_VICTIMS));
12976251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12977251881Speter
12978251881Speter  new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
12979251881Speter
12980251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
12981251881Speter  while (have_row)
12982251881Speter    {
12983251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
12984251881Speter
12985251881Speter      APR_ARRAY_PUSH(new_victims, const char *) =
12986251881Speter                            svn_relpath_basename(child_relpath, result_pool);
12987251881Speter
12988251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
12989251881Speter    }
12990251881Speter
12991251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
12992251881Speter
12993251881Speter  *victims = new_victims;
12994251881Speter  return SVN_NO_ERROR;
12995251881Speter}
12996251881Speter
12997251881Speter/* The body of svn_wc__db_get_conflict_marker_files().
12998251881Speter */
12999251881Speterstatic svn_error_t *
13000251881Speterget_conflict_marker_files(apr_hash_t **marker_files_p,
13001251881Speter                          svn_wc__db_wcroot_t *wcroot,
13002251881Speter                          const char *local_relpath,
13003251881Speter                          svn_wc__db_t *db,
13004251881Speter                          apr_pool_t *result_pool,
13005251881Speter                          apr_pool_t *scratch_pool)
13006251881Speter{
13007251881Speter  svn_sqlite__stmt_t *stmt;
13008251881Speter  svn_boolean_t have_row;
13009251881Speter  apr_hash_t *marker_files = apr_hash_make(result_pool);
13010251881Speter
13011251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13012251881Speter                                    STMT_SELECT_ACTUAL_NODE));
13013251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13014251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13015251881Speter
13016251881Speter  if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13017251881Speter    {
13018251881Speter      apr_size_t len;
13019251881Speter      const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13020251881Speter      svn_skel_t *conflicts;
13021251881Speter      const apr_array_header_t *markers;
13022251881Speter      int i;
13023251881Speter
13024251881Speter      conflicts = svn_skel__parse(data, len, scratch_pool);
13025251881Speter
13026251881Speter      /* ### ADD markers to *marker_files */
13027251881Speter      SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13028251881Speter                                            conflicts,
13029251881Speter                                            result_pool, scratch_pool));
13030251881Speter
13031251881Speter      for (i = 0; markers && (i < markers->nelts); i++)
13032251881Speter        {
13033251881Speter          const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13034251881Speter
13035251881Speter          svn_hash_sets(marker_files, marker_abspath, "");
13036251881Speter        }
13037251881Speter    }
13038251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
13039251881Speter
13040251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13041251881Speter                                    STMT_SELECT_CONFLICT_VICTIMS));
13042251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13043251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13044251881Speter
13045251881Speter  while (have_row)
13046251881Speter    {
13047251881Speter      apr_size_t len;
13048251881Speter      const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13049251881Speter
13050251881Speter      const apr_array_header_t *markers;
13051251881Speter      int i;
13052251881Speter
13053251881Speter      if (data)
13054251881Speter        {
13055251881Speter          svn_skel_t *conflicts;
13056251881Speter          conflicts = svn_skel__parse(data, len, scratch_pool);
13057251881Speter
13058251881Speter          SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13059251881Speter                                                conflicts,
13060251881Speter                                                result_pool, scratch_pool));
13061251881Speter
13062251881Speter          for (i = 0; markers && (i < markers->nelts); i++)
13063251881Speter            {
13064251881Speter              const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13065251881Speter
13066251881Speter              svn_hash_sets(marker_files, marker_abspath, "");
13067251881Speter            }
13068251881Speter        }
13069251881Speter
13070251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13071251881Speter    }
13072251881Speter
13073251881Speter  if (apr_hash_count(marker_files))
13074251881Speter    *marker_files_p = marker_files;
13075251881Speter  else
13076251881Speter    *marker_files_p = NULL;
13077251881Speter
13078251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
13079251881Speter}
13080251881Speter
13081251881Spetersvn_error_t *
13082251881Spetersvn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13083251881Speter                                     svn_wc__db_t *db,
13084251881Speter                                     const char *local_abspath,
13085251881Speter                                     apr_pool_t *result_pool,
13086251881Speter                                     apr_pool_t *scratch_pool)
13087251881Speter{
13088251881Speter  svn_wc__db_wcroot_t *wcroot;
13089251881Speter  const char *local_relpath;
13090251881Speter
13091251881Speter  /* The parent should be a working copy directory. */
13092251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13093251881Speter                              local_abspath, scratch_pool, scratch_pool));
13094251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13095251881Speter
13096251881Speter  SVN_WC__DB_WITH_TXN(
13097251881Speter    get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13098251881Speter                              result_pool, scratch_pool),
13099251881Speter    wcroot);
13100251881Speter
13101251881Speter  return SVN_NO_ERROR;
13102251881Speter}
13103251881Speter
13104251881Speter
13105251881Spetersvn_error_t *
13106251881Spetersvn_wc__db_read_conflict(svn_skel_t **conflict,
13107251881Speter                         svn_wc__db_t *db,
13108251881Speter                         const char *local_abspath,
13109251881Speter                         apr_pool_t *result_pool,
13110251881Speter                         apr_pool_t *scratch_pool)
13111251881Speter{
13112251881Speter  svn_wc__db_wcroot_t *wcroot;
13113251881Speter  const char *local_relpath;
13114251881Speter
13115251881Speter  /* The parent should be a working copy directory. */
13116251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13117251881Speter                              local_abspath, scratch_pool, scratch_pool));
13118251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13119251881Speter
13120251881Speter  return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13121251881Speter                                                           local_relpath,
13122251881Speter                                                           result_pool,
13123251881Speter                                                           scratch_pool));
13124251881Speter}
13125251881Speter
13126251881Spetersvn_error_t *
13127251881Spetersvn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13128251881Speter                                  svn_wc__db_wcroot_t *wcroot,
13129251881Speter                                  const char *local_relpath,
13130251881Speter                                  apr_pool_t *result_pool,
13131251881Speter                                  apr_pool_t *scratch_pool)
13132251881Speter{
13133251881Speter  svn_sqlite__stmt_t *stmt;
13134251881Speter  svn_boolean_t have_row;
13135251881Speter
13136251881Speter  /* Check if we have a conflict in ACTUAL */
13137251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13138251881Speter                                    STMT_SELECT_ACTUAL_NODE));
13139251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13140251881Speter
13141251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13142251881Speter
13143251881Speter  if (! have_row)
13144251881Speter    {
13145251881Speter      /* Do this while stmt is still open to avoid closing the sqlite
13146251881Speter         transaction and then reopening. */
13147251881Speter      svn_sqlite__stmt_t *stmt_node;
13148251881Speter      svn_error_t *err;
13149251881Speter
13150251881Speter      err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13151251881Speter                                      STMT_SELECT_NODE_INFO);
13152251881Speter
13153251881Speter      if (err)
13154251881Speter        stmt_node = NULL;
13155251881Speter      else
13156251881Speter        err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13157251881Speter                                local_relpath);
13158251881Speter
13159251881Speter      if (!err)
13160251881Speter        err = svn_sqlite__step(&have_row, stmt_node);
13161251881Speter
13162251881Speter      if (stmt_node)
13163251881Speter        err = svn_error_compose_create(err,
13164251881Speter                                       svn_sqlite__reset(stmt_node));
13165251881Speter
13166251881Speter      SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13167251881Speter
13168251881Speter      if (have_row)
13169251881Speter        {
13170251881Speter          *conflict = NULL;
13171251881Speter          return SVN_NO_ERROR;
13172251881Speter        }
13173251881Speter
13174251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13175251881Speter                               _("The node '%s' was not found."),
13176251881Speter                                   path_for_error_message(wcroot,
13177251881Speter                                                          local_relpath,
13178251881Speter                                                          scratch_pool));
13179251881Speter    }
13180251881Speter
13181251881Speter  {
13182251881Speter    apr_size_t cfl_len;
13183251881Speter    const void *cfl_data;
13184251881Speter
13185251881Speter    /* svn_skel__parse doesn't copy data, so store in result_pool */
13186251881Speter    cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13187251881Speter
13188251881Speter    if (cfl_data)
13189251881Speter      *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13190251881Speter    else
13191251881Speter      *conflict = NULL;
13192251881Speter
13193251881Speter    return svn_error_trace(svn_sqlite__reset(stmt));
13194251881Speter  }
13195251881Speter}
13196251881Speter
13197251881Speter
13198251881Spetersvn_error_t *
13199251881Spetersvn_wc__db_read_kind(svn_node_kind_t *kind,
13200251881Speter                     svn_wc__db_t *db,
13201251881Speter                     const char *local_abspath,
13202251881Speter                     svn_boolean_t allow_missing,
13203251881Speter                     svn_boolean_t show_deleted,
13204251881Speter                     svn_boolean_t show_hidden,
13205251881Speter                     apr_pool_t *scratch_pool)
13206251881Speter{
13207251881Speter  svn_wc__db_wcroot_t *wcroot;
13208251881Speter  const char *local_relpath;
13209251881Speter  svn_sqlite__stmt_t *stmt_info;
13210251881Speter  svn_boolean_t have_info;
13211251881Speter
13212251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13213251881Speter
13214251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13215251881Speter                              local_abspath, scratch_pool, scratch_pool));
13216251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13217251881Speter
13218251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13219251881Speter                                    STMT_SELECT_NODE_INFO));
13220251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13221251881Speter  SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13222251881Speter
13223251881Speter  if (!have_info)
13224251881Speter    {
13225251881Speter      if (allow_missing)
13226251881Speter        {
13227251881Speter          *kind = svn_node_unknown;
13228251881Speter          SVN_ERR(svn_sqlite__reset(stmt_info));
13229251881Speter          return SVN_NO_ERROR;
13230251881Speter        }
13231251881Speter      else
13232251881Speter        {
13233251881Speter          SVN_ERR(svn_sqlite__reset(stmt_info));
13234251881Speter          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13235251881Speter                                   _("The node '%s' was not found."),
13236251881Speter                                   path_for_error_message(wcroot,
13237251881Speter                                                          local_relpath,
13238251881Speter                                                          scratch_pool));
13239251881Speter        }
13240251881Speter    }
13241251881Speter
13242251881Speter  if (!(show_deleted && show_hidden))
13243251881Speter    {
13244251881Speter      int op_depth = svn_sqlite__column_int(stmt_info, 0);
13245251881Speter      svn_boolean_t report_none = FALSE;
13246251881Speter      svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13247251881Speter                                                            presence_map);
13248251881Speter
13249251881Speter      if (op_depth > 0)
13250251881Speter        SVN_ERR(convert_to_working_status(&status, status));
13251251881Speter
13252251881Speter      switch (status)
13253251881Speter        {
13254251881Speter          case svn_wc__db_status_not_present:
13255251881Speter            if (! (show_hidden && show_deleted))
13256251881Speter              report_none = TRUE;
13257251881Speter            break;
13258251881Speter          case svn_wc__db_status_excluded:
13259251881Speter          case svn_wc__db_status_server_excluded:
13260251881Speter            if (! show_hidden)
13261251881Speter              report_none = TRUE;
13262251881Speter            break;
13263251881Speter          case svn_wc__db_status_deleted:
13264251881Speter            if (! show_deleted)
13265251881Speter              report_none = TRUE;
13266251881Speter            break;
13267251881Speter          default:
13268251881Speter            break;
13269251881Speter        }
13270251881Speter
13271251881Speter      if (report_none)
13272251881Speter        {
13273251881Speter          *kind = svn_node_none;
13274251881Speter          return svn_error_trace(svn_sqlite__reset(stmt_info));
13275251881Speter        }
13276251881Speter    }
13277251881Speter
13278251881Speter  *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13279251881Speter
13280251881Speter  return svn_error_trace(svn_sqlite__reset(stmt_info));
13281251881Speter}
13282251881Speter
13283251881Speter
13284251881Spetersvn_error_t *
13285251881Spetersvn_wc__db_node_hidden(svn_boolean_t *hidden,
13286251881Speter                       svn_wc__db_t *db,
13287251881Speter                       const char *local_abspath,
13288251881Speter                       apr_pool_t *scratch_pool)
13289251881Speter{
13290251881Speter  svn_wc__db_wcroot_t *wcroot;
13291251881Speter  const char *local_relpath;
13292251881Speter  svn_wc__db_status_t status;
13293251881Speter
13294251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13295251881Speter
13296251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13297251881Speter                              local_abspath, scratch_pool, scratch_pool));
13298251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13299251881Speter
13300251881Speter  SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13301251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13302251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13303251881Speter                    NULL, NULL, NULL,
13304251881Speter                    wcroot, local_relpath,
13305251881Speter                    scratch_pool, scratch_pool));
13306251881Speter
13307251881Speter  *hidden = (status == svn_wc__db_status_server_excluded
13308251881Speter             || status == svn_wc__db_status_not_present
13309251881Speter             || status == svn_wc__db_status_excluded);
13310251881Speter
13311251881Speter  return SVN_NO_ERROR;
13312251881Speter}
13313251881Speter
13314251881Speter
13315251881Spetersvn_error_t *
13316251881Spetersvn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13317251881Speter                     svn_wc__db_t *db,
13318251881Speter                     const char *local_abspath,
13319251881Speter                     apr_pool_t *scratch_pool)
13320251881Speter{
13321251881Speter  svn_wc__db_wcroot_t *wcroot;
13322251881Speter  const char *local_relpath;
13323251881Speter
13324251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13325251881Speter
13326251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13327251881Speter                              local_abspath, scratch_pool, scratch_pool));
13328251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13329251881Speter
13330251881Speter  if (*local_relpath != '\0')
13331251881Speter    {
13332251881Speter      *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13333251881Speter                           the same wcroot */
13334251881Speter      return SVN_NO_ERROR;
13335251881Speter    }
13336251881Speter
13337251881Speter   *is_wcroot = TRUE;
13338251881Speter
13339251881Speter   return SVN_NO_ERROR;
13340251881Speter}
13341251881Speter
13342251881Speter/* Find a node's kind and whether it is switched, putting the outputs in
13343251881Speter * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13344251881Speter */
13345251881Speterstatic svn_error_t *
13346251881Speterdb_is_switched(svn_boolean_t *is_switched,
13347251881Speter               svn_node_kind_t *kind,
13348251881Speter               svn_wc__db_wcroot_t *wcroot,
13349251881Speter               const char *local_relpath,
13350251881Speter               apr_pool_t *scratch_pool)
13351251881Speter{
13352251881Speter  svn_wc__db_status_t status;
13353251881Speter  apr_int64_t repos_id;
13354251881Speter  const char *repos_relpath;
13355251881Speter  const char *name;
13356251881Speter  const char *parent_local_relpath;
13357251881Speter  apr_int64_t parent_repos_id;
13358251881Speter  const char *parent_repos_relpath;
13359251881Speter
13360251881Speter  SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13361251881Speter
13362251881Speter  SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13363251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13364251881Speter                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13365251881Speter                    wcroot, local_relpath, scratch_pool, scratch_pool));
13366251881Speter
13367251881Speter  if (status == svn_wc__db_status_server_excluded
13368251881Speter      || status == svn_wc__db_status_excluded
13369251881Speter      || status == svn_wc__db_status_not_present)
13370251881Speter    {
13371251881Speter      return svn_error_createf(
13372251881Speter                    SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13373251881Speter                    _("The node '%s' was not found."),
13374251881Speter                    path_for_error_message(wcroot, local_relpath,
13375251881Speter                                           scratch_pool));
13376251881Speter    }
13377251881Speter  else if (! repos_relpath)
13378251881Speter    {
13379251881Speter      /* Node is shadowed; easy out */
13380251881Speter      if (is_switched)
13381251881Speter        *is_switched = FALSE;
13382251881Speter
13383251881Speter      return SVN_NO_ERROR;
13384251881Speter    }
13385251881Speter
13386251881Speter  if (! is_switched)
13387251881Speter    return SVN_NO_ERROR;
13388251881Speter
13389251881Speter  svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13390251881Speter
13391251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13392251881Speter                                            &parent_repos_relpath,
13393251881Speter                                            &parent_repos_id, NULL, NULL, NULL,
13394251881Speter                                            NULL, NULL, NULL, NULL, NULL,
13395251881Speter                                            NULL, NULL,
13396251881Speter                                            wcroot, parent_local_relpath,
13397251881Speter                                            scratch_pool, scratch_pool));
13398251881Speter
13399251881Speter  if (repos_id != parent_repos_id)
13400251881Speter    *is_switched = TRUE;
13401251881Speter  else
13402251881Speter    {
13403251881Speter      const char *expected_relpath;
13404251881Speter
13405251881Speter      expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13406251881Speter                                          scratch_pool);
13407251881Speter
13408251881Speter      *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13409251881Speter    }
13410251881Speter
13411251881Speter  return SVN_NO_ERROR;
13412251881Speter}
13413251881Speter
13414251881Spetersvn_error_t *
13415251881Spetersvn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13416251881Speter                       svn_boolean_t *is_switched,
13417251881Speter                       svn_node_kind_t *kind,
13418251881Speter                       svn_wc__db_t *db,
13419251881Speter                       const char *local_abspath,
13420251881Speter                       apr_pool_t *scratch_pool)
13421251881Speter{
13422251881Speter  svn_wc__db_wcroot_t *wcroot;
13423251881Speter  const char *local_relpath;
13424251881Speter
13425251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13426251881Speter
13427251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13428251881Speter                              local_abspath, scratch_pool, scratch_pool));
13429251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13430251881Speter
13431251881Speter  if (is_switched)
13432251881Speter    *is_switched = FALSE;
13433251881Speter
13434251881Speter  if (*local_relpath == '\0')
13435251881Speter    {
13436251881Speter      /* Easy out */
13437251881Speter      if (is_wcroot)
13438251881Speter        *is_wcroot = TRUE;
13439251881Speter
13440251881Speter      if (kind)
13441251881Speter        *kind = svn_node_dir;
13442251881Speter      return SVN_NO_ERROR;
13443251881Speter    }
13444251881Speter
13445251881Speter  if (is_wcroot)
13446251881Speter    *is_wcroot = FALSE;
13447251881Speter
13448251881Speter  if (! is_switched && ! kind)
13449251881Speter    return SVN_NO_ERROR;
13450251881Speter
13451251881Speter  SVN_WC__DB_WITH_TXN(
13452251881Speter    db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13453251881Speter    wcroot);
13454251881Speter  return SVN_NO_ERROR;
13455251881Speter}
13456251881Speter
13457251881Speter
13458251881Spetersvn_error_t *
13459251881Spetersvn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
13460251881Speter                               svn_wc__db_t *db,
13461251881Speter                               const char *wri_abspath,
13462251881Speter                               apr_pool_t *result_pool,
13463251881Speter                               apr_pool_t *scratch_pool)
13464251881Speter{
13465251881Speter  svn_wc__db_wcroot_t *wcroot;
13466251881Speter  const char *local_relpath;
13467251881Speter
13468251881Speter  SVN_ERR_ASSERT(temp_dir_abspath != NULL);
13469251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13470251881Speter
13471251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13472251881Speter                              wri_abspath, scratch_pool, scratch_pool));
13473251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13474251881Speter
13475251881Speter  *temp_dir_abspath = svn_dirent_join_many(result_pool,
13476251881Speter                                           wcroot->abspath,
13477251881Speter                                           svn_wc_get_adm_dir(scratch_pool),
13478251881Speter                                           WCROOT_TEMPDIR_RELPATH,
13479251881Speter                                           NULL);
13480251881Speter  return SVN_NO_ERROR;
13481251881Speter}
13482251881Speter
13483251881Speter
13484251881Speter/* Helper for wclock_obtain_cb() to steal an existing lock */
13485251881Speterstatic svn_error_t *
13486251881Speterwclock_steal(svn_wc__db_wcroot_t *wcroot,
13487251881Speter             const char *local_relpath,
13488251881Speter             apr_pool_t *scratch_pool)
13489251881Speter{
13490251881Speter  svn_sqlite__stmt_t *stmt;
13491251881Speter
13492251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
13493251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13494251881Speter
13495251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
13496251881Speter
13497251881Speter  return SVN_NO_ERROR;
13498251881Speter}
13499251881Speter
13500251881Speter
13501251881Speter/* The body of svn_wc__db_wclock_obtain().
13502251881Speter */
13503251881Speterstatic svn_error_t *
13504251881Speterwclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
13505251881Speter                 const char *local_relpath,
13506251881Speter                 int levels_to_lock,
13507251881Speter                 svn_boolean_t steal_lock,
13508251881Speter                 apr_pool_t *scratch_pool)
13509251881Speter{
13510251881Speter  svn_sqlite__stmt_t *stmt;
13511251881Speter  svn_error_t *err;
13512251881Speter  const char *lock_relpath;
13513251881Speter  int max_depth;
13514251881Speter  int lock_depth;
13515251881Speter  svn_boolean_t got_row;
13516251881Speter
13517251881Speter  svn_wc__db_wclock_t lock;
13518251881Speter
13519251881Speter  /* Upgrade locks the root before the node exists.  Apart from that
13520251881Speter     the root node always exists so we will just skip the check.
13521251881Speter
13522251881Speter     ### Perhaps the lock for upgrade should be created when the db is
13523251881Speter         created?  1.6 used to lock .svn on creation. */
13524251881Speter  if (local_relpath[0])
13525251881Speter    {
13526251881Speter      svn_boolean_t exists;
13527251881Speter
13528251881Speter      SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
13529251881Speter      if (!exists)
13530251881Speter        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13531251881Speter                                 _("The node '%s' was not found."),
13532251881Speter                                 path_for_error_message(wcroot,
13533251881Speter                                                        local_relpath,
13534251881Speter                                                        scratch_pool));
13535251881Speter    }
13536251881Speter
13537251881Speter  /* Check if there are nodes locked below the new lock root */
13538251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
13539251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13540251881Speter
13541251881Speter  lock_depth = relpath_depth(local_relpath);
13542251881Speter  max_depth = lock_depth + levels_to_lock;
13543251881Speter
13544251881Speter  SVN_ERR(svn_sqlite__step(&got_row, stmt));
13545251881Speter
13546251881Speter  while (got_row)
13547251881Speter    {
13548251881Speter      svn_boolean_t own_lock;
13549251881Speter
13550251881Speter      lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
13551251881Speter
13552251881Speter      /* If we are not locking with depth infinity, check if this lock
13553251881Speter         voids our lock request */
13554251881Speter      if (levels_to_lock >= 0
13555251881Speter          && relpath_depth(lock_relpath) > max_depth)
13556251881Speter        {
13557251881Speter          SVN_ERR(svn_sqlite__step(&got_row, stmt));
13558251881Speter          continue;
13559251881Speter        }
13560251881Speter
13561251881Speter      /* Check if we are the lock owner, because we should be able to
13562251881Speter         extend our lock. */
13563251881Speter      err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
13564251881Speter                             TRUE, scratch_pool);
13565251881Speter
13566251881Speter      if (err)
13567251881Speter        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13568251881Speter
13569251881Speter      if (!own_lock && !steal_lock)
13570251881Speter        {
13571251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
13572251881Speter          err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
13573251881Speter                                   _("'%s' is already locked."),
13574251881Speter                                   path_for_error_message(wcroot,
13575251881Speter                                                          lock_relpath,
13576251881Speter                                                          scratch_pool));
13577251881Speter          return svn_error_createf(SVN_ERR_WC_LOCKED, err,
13578251881Speter                                   _("Working copy '%s' locked."),
13579251881Speter                                   path_for_error_message(wcroot,
13580251881Speter                                                          local_relpath,
13581251881Speter                                                          scratch_pool));
13582251881Speter        }
13583251881Speter      else if (!own_lock)
13584251881Speter        {
13585251881Speter          err = wclock_steal(wcroot, lock_relpath, scratch_pool);
13586251881Speter
13587251881Speter          if (err)
13588251881Speter            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13589251881Speter        }
13590251881Speter
13591251881Speter      SVN_ERR(svn_sqlite__step(&got_row, stmt));
13592251881Speter    }
13593251881Speter
13594251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
13595251881Speter
13596251881Speter  if (steal_lock)
13597251881Speter    SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
13598251881Speter
13599251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
13600251881Speter  lock_relpath = local_relpath;
13601251881Speter
13602251881Speter  while (TRUE)
13603251881Speter    {
13604251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
13605251881Speter
13606251881Speter      SVN_ERR(svn_sqlite__step(&got_row, stmt));
13607251881Speter
13608251881Speter      if (got_row)
13609251881Speter        {
13610251881Speter          int levels = svn_sqlite__column_int(stmt, 0);
13611251881Speter          if (levels >= 0)
13612251881Speter            levels += relpath_depth(lock_relpath);
13613251881Speter
13614251881Speter          SVN_ERR(svn_sqlite__reset(stmt));
13615251881Speter
13616251881Speter          if (levels == -1 || levels >= lock_depth)
13617251881Speter            {
13618251881Speter
13619251881Speter              err = svn_error_createf(
13620251881Speter                              SVN_ERR_WC_LOCKED, NULL,
13621251881Speter                              _("'%s' is already locked."),
13622251881Speter                              svn_dirent_local_style(
13623251881Speter                                       svn_dirent_join(wcroot->abspath,
13624251881Speter                                                       lock_relpath,
13625251881Speter                                                       scratch_pool),
13626251881Speter                              scratch_pool));
13627251881Speter              return svn_error_createf(
13628251881Speter                              SVN_ERR_WC_LOCKED, err,
13629251881Speter                              _("Working copy '%s' locked."),
13630251881Speter                              path_for_error_message(wcroot,
13631251881Speter                                                     local_relpath,
13632251881Speter                                                     scratch_pool));
13633251881Speter            }
13634251881Speter
13635251881Speter          break; /* There can't be interesting locks on higher nodes */
13636251881Speter        }
13637251881Speter      else
13638251881Speter        SVN_ERR(svn_sqlite__reset(stmt));
13639251881Speter
13640251881Speter      if (!*lock_relpath)
13641251881Speter        break;
13642251881Speter
13643251881Speter      lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
13644251881Speter    }
13645251881Speter
13646251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
13647251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13648251881Speter                            levels_to_lock));
13649251881Speter  err = svn_sqlite__insert(NULL, stmt);
13650251881Speter  if (err)
13651251881Speter    return svn_error_createf(SVN_ERR_WC_LOCKED, err,
13652251881Speter                             _("Working copy '%s' locked"),
13653251881Speter                             path_for_error_message(wcroot,
13654251881Speter                                                    local_relpath,
13655251881Speter                                                    scratch_pool));
13656251881Speter
13657251881Speter  /* And finally store that we obtained the lock */
13658251881Speter  lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
13659251881Speter  lock.levels = levels_to_lock;
13660251881Speter  APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
13661251881Speter
13662251881Speter  return SVN_NO_ERROR;
13663251881Speter}
13664251881Speter
13665251881Speter
13666251881Spetersvn_error_t *
13667251881Spetersvn_wc__db_wclock_obtain(svn_wc__db_t *db,
13668251881Speter                         const char *local_abspath,
13669251881Speter                         int levels_to_lock,
13670251881Speter                         svn_boolean_t steal_lock,
13671251881Speter                         apr_pool_t *scratch_pool)
13672251881Speter{
13673251881Speter  svn_wc__db_wcroot_t *wcroot;
13674251881Speter  const char *local_relpath;
13675251881Speter
13676251881Speter  SVN_ERR_ASSERT(levels_to_lock >= -1);
13677251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13678251881Speter
13679251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13680251881Speter                                             db, local_abspath,
13681251881Speter                                             scratch_pool, scratch_pool));
13682251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13683251881Speter
13684251881Speter  if (!steal_lock)
13685251881Speter    {
13686251881Speter      int i;
13687251881Speter      int depth = relpath_depth(local_relpath);
13688251881Speter
13689251881Speter      for (i = 0; i < wcroot->owned_locks->nelts; i++)
13690251881Speter        {
13691251881Speter          svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
13692251881Speter                                                     i, svn_wc__db_wclock_t);
13693251881Speter
13694251881Speter          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
13695251881Speter              && (lock->levels == -1
13696251881Speter                  || (lock->levels + relpath_depth(lock->local_relpath))
13697251881Speter                            >= depth))
13698251881Speter            {
13699251881Speter              return svn_error_createf(
13700251881Speter                SVN_ERR_WC_LOCKED, NULL,
13701251881Speter                _("'%s' is already locked via '%s'."),
13702251881Speter                svn_dirent_local_style(local_abspath, scratch_pool),
13703251881Speter                path_for_error_message(wcroot, lock->local_relpath,
13704251881Speter                                       scratch_pool));
13705251881Speter            }
13706251881Speter        }
13707251881Speter    }
13708251881Speter
13709251881Speter  SVN_WC__DB_WITH_TXN(
13710251881Speter    wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
13711251881Speter                     scratch_pool),
13712251881Speter    wcroot);
13713251881Speter  return SVN_NO_ERROR;
13714251881Speter}
13715251881Speter
13716251881Speter
13717251881Speter/* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
13718251881Speterstatic svn_error_t *
13719251881Speterfind_wclock(const char **lock_relpath,
13720251881Speter            svn_wc__db_wcroot_t *wcroot,
13721251881Speter            const char *dir_relpath,
13722251881Speter            apr_pool_t *result_pool,
13723251881Speter            apr_pool_t *scratch_pool)
13724251881Speter{
13725251881Speter  svn_sqlite__stmt_t *stmt;
13726251881Speter  svn_boolean_t have_row;
13727251881Speter  int dir_depth = relpath_depth(dir_relpath);
13728251881Speter  const char *first_relpath;
13729251881Speter
13730251881Speter  /* Check for locks on all directories that might be ancestors.
13731251881Speter     As our new apis only use recursive locks the number of locks stored
13732251881Speter     in the DB will be very low */
13733251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13734251881Speter                                    STMT_SELECT_ANCESTOR_WCLOCKS));
13735251881Speter
13736251881Speter  /* Get the top level relpath to reduce the worst case number of results
13737251881Speter     to the number of directories below this node plus two.
13738251881Speter     (1: the node itself and 2: the wcroot). */
13739251881Speter  first_relpath = strchr(dir_relpath, '/');
13740251881Speter
13741251881Speter  if (first_relpath != NULL)
13742251881Speter    first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
13743251881Speter                                 first_relpath - dir_relpath);
13744251881Speter  else
13745251881Speter    first_relpath = dir_relpath;
13746251881Speter
13747251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss",
13748251881Speter                            wcroot->wc_id,
13749251881Speter                            dir_relpath,
13750251881Speter                            first_relpath));
13751251881Speter
13752251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
13753251881Speter
13754251881Speter  while (have_row)
13755251881Speter    {
13756251881Speter      const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
13757251881Speter
13758251881Speter      if (svn_relpath_skip_ancestor(relpath, dir_relpath))
13759251881Speter        {
13760251881Speter          int locked_levels = svn_sqlite__column_int(stmt, 1);
13761251881Speter          int row_depth = relpath_depth(relpath);
13762251881Speter
13763251881Speter          if (locked_levels == -1
13764251881Speter              || locked_levels + row_depth >= dir_depth)
13765251881Speter            {
13766251881Speter              *lock_relpath = apr_pstrdup(result_pool, relpath);
13767251881Speter              SVN_ERR(svn_sqlite__reset(stmt));
13768251881Speter              return SVN_NO_ERROR;
13769251881Speter            }
13770251881Speter        }
13771251881Speter
13772251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
13773251881Speter    }
13774251881Speter
13775251881Speter  *lock_relpath = NULL;
13776251881Speter
13777251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
13778251881Speter}
13779251881Speter
13780251881Speterstatic svn_error_t *
13781251881Speteris_wclocked(svn_boolean_t *locked,
13782251881Speter            svn_wc__db_wcroot_t *wcroot,
13783251881Speter            const char *dir_relpath,
13784251881Speter            apr_pool_t *scratch_pool)
13785251881Speter{
13786251881Speter  const char *lock_relpath;
13787251881Speter
13788251881Speter  SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
13789251881Speter                      scratch_pool, scratch_pool));
13790251881Speter  *locked = (lock_relpath != NULL);
13791251881Speter  return SVN_NO_ERROR;
13792251881Speter}
13793251881Speter
13794251881Speter
13795251881Spetersvn_error_t*
13796251881Spetersvn_wc__db_wclock_find_root(const char **lock_abspath,
13797251881Speter                            svn_wc__db_t *db,
13798251881Speter                            const char *local_abspath,
13799251881Speter                            apr_pool_t *result_pool,
13800251881Speter                            apr_pool_t *scratch_pool)
13801251881Speter{
13802251881Speter  svn_wc__db_wcroot_t *wcroot;
13803251881Speter  const char *local_relpath;
13804251881Speter  const char *lock_relpath;
13805251881Speter
13806251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13807251881Speter                              local_abspath, scratch_pool, scratch_pool));
13808251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13809251881Speter
13810251881Speter  SVN_WC__DB_WITH_TXN(
13811251881Speter    find_wclock(&lock_relpath, wcroot, local_relpath,
13812251881Speter                scratch_pool, scratch_pool),
13813251881Speter    wcroot);
13814251881Speter
13815251881Speter  if (!lock_relpath)
13816251881Speter    *lock_abspath = NULL;
13817251881Speter  else
13818251881Speter    SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
13819251881Speter                                    lock_relpath, result_pool, scratch_pool));
13820251881Speter  return SVN_NO_ERROR;
13821251881Speter}
13822251881Speter
13823251881Speter
13824251881Spetersvn_error_t *
13825251881Spetersvn_wc__db_wclocked(svn_boolean_t *locked,
13826251881Speter                    svn_wc__db_t *db,
13827251881Speter                    const char *local_abspath,
13828251881Speter                    apr_pool_t *scratch_pool)
13829251881Speter{
13830251881Speter  svn_wc__db_wcroot_t *wcroot;
13831251881Speter  const char *local_relpath;
13832251881Speter
13833251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13834251881Speter                              local_abspath, scratch_pool, scratch_pool));
13835251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13836251881Speter
13837251881Speter  SVN_WC__DB_WITH_TXN(
13838251881Speter    is_wclocked(locked, wcroot, local_relpath, scratch_pool),
13839251881Speter    wcroot);
13840251881Speter
13841251881Speter  return SVN_NO_ERROR;
13842251881Speter}
13843251881Speter
13844251881Speter
13845251881Spetersvn_error_t *
13846251881Spetersvn_wc__db_wclock_release(svn_wc__db_t *db,
13847251881Speter                          const char *local_abspath,
13848251881Speter                          apr_pool_t *scratch_pool)
13849251881Speter{
13850251881Speter  svn_sqlite__stmt_t *stmt;
13851251881Speter  svn_wc__db_wcroot_t *wcroot;
13852251881Speter  const char *local_relpath;
13853251881Speter  int i;
13854251881Speter  apr_array_header_t *owned_locks;
13855251881Speter
13856251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13857251881Speter                              local_abspath, scratch_pool, scratch_pool));
13858251881Speter
13859251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13860251881Speter
13861251881Speter  /* First check and remove the owns-lock information as failure in
13862251881Speter     removing the db record implies that we have to steal the lock later. */
13863251881Speter  owned_locks = wcroot->owned_locks;
13864251881Speter  for (i = 0; i < owned_locks->nelts; i++)
13865251881Speter    {
13866251881Speter      svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13867251881Speter                                                 svn_wc__db_wclock_t);
13868251881Speter
13869251881Speter      if (strcmp(lock->local_relpath, local_relpath) == 0)
13870251881Speter        break;
13871251881Speter    }
13872251881Speter
13873251881Speter  if (i >= owned_locks->nelts)
13874251881Speter    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
13875251881Speter                             _("Working copy not locked at '%s'."),
13876251881Speter                             svn_dirent_local_style(local_abspath,
13877251881Speter                                                    scratch_pool));
13878251881Speter
13879251881Speter  if (i < owned_locks->nelts)
13880251881Speter    {
13881251881Speter      owned_locks->nelts--;
13882251881Speter
13883251881Speter      /* Move the last item in the array to the deleted place */
13884251881Speter      if (owned_locks->nelts > 0)
13885251881Speter        APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
13886251881Speter           APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
13887251881Speter    }
13888251881Speter
13889251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13890251881Speter                                    STMT_DELETE_WC_LOCK));
13891251881Speter
13892251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13893251881Speter
13894251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
13895251881Speter
13896251881Speter  return SVN_NO_ERROR;
13897251881Speter}
13898251881Speter
13899251881Speter
13900251881Speter/* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
13901251881Speter   of DB+LOCAL_ABSPATH.  */
13902251881Speterstatic svn_error_t *
13903251881Speterwclock_owns_lock(svn_boolean_t *own_lock,
13904251881Speter                 svn_wc__db_wcroot_t *wcroot,
13905251881Speter                 const char *local_relpath,
13906251881Speter                 svn_boolean_t exact,
13907251881Speter                 apr_pool_t *scratch_pool)
13908251881Speter{
13909251881Speter  apr_array_header_t *owned_locks;
13910251881Speter  int lock_level;
13911251881Speter  int i;
13912251881Speter
13913251881Speter  *own_lock = FALSE;
13914251881Speter  owned_locks = wcroot->owned_locks;
13915251881Speter  lock_level = relpath_depth(local_relpath);
13916251881Speter
13917251881Speter  if (exact)
13918251881Speter    {
13919251881Speter      for (i = 0; i < owned_locks->nelts; i++)
13920251881Speter        {
13921251881Speter          svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13922251881Speter                                                     svn_wc__db_wclock_t);
13923251881Speter
13924251881Speter          if (strcmp(lock->local_relpath, local_relpath) == 0)
13925251881Speter            {
13926251881Speter              *own_lock = TRUE;
13927251881Speter              return SVN_NO_ERROR;
13928251881Speter            }
13929251881Speter        }
13930251881Speter    }
13931251881Speter  else
13932251881Speter    {
13933251881Speter      for (i = 0; i < owned_locks->nelts; i++)
13934251881Speter        {
13935251881Speter          svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13936251881Speter                                                     svn_wc__db_wclock_t);
13937251881Speter
13938251881Speter          if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
13939251881Speter              && (lock->levels == -1
13940251881Speter                  || ((relpath_depth(lock->local_relpath) + lock->levels)
13941251881Speter                      >= lock_level)))
13942251881Speter            {
13943251881Speter              *own_lock = TRUE;
13944251881Speter              return SVN_NO_ERROR;
13945251881Speter            }
13946251881Speter        }
13947251881Speter    }
13948251881Speter
13949251881Speter  return SVN_NO_ERROR;
13950251881Speter}
13951251881Speter
13952251881Speter
13953251881Spetersvn_error_t *
13954251881Spetersvn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
13955251881Speter                            svn_wc__db_t *db,
13956251881Speter                            const char *local_abspath,
13957251881Speter                            svn_boolean_t exact,
13958251881Speter                            apr_pool_t *scratch_pool)
13959251881Speter{
13960251881Speter  svn_wc__db_wcroot_t *wcroot;
13961251881Speter  const char *local_relpath;
13962251881Speter
13963251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13964251881Speter                              local_abspath, scratch_pool, scratch_pool));
13965251881Speter
13966251881Speter  if (!wcroot)
13967251881Speter    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
13968251881Speter                             _("The node '%s' was not found."),
13969251881Speter                             svn_dirent_local_style(local_abspath,
13970251881Speter                                                    scratch_pool));
13971251881Speter
13972251881Speter  VERIFY_USABLE_WCROOT(wcroot);
13973251881Speter
13974251881Speter  SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
13975251881Speter                           scratch_pool));
13976251881Speter
13977251881Speter  return SVN_NO_ERROR;
13978251881Speter}
13979251881Speter
13980251881Speter/* The body of svn_wc__db_temp_op_end_directory_update().
13981251881Speter */
13982251881Speterstatic svn_error_t *
13983251881Speterend_directory_update(svn_wc__db_wcroot_t *wcroot,
13984251881Speter                     const char *local_relpath,
13985251881Speter                     apr_pool_t *scratch_pool)
13986251881Speter{
13987251881Speter  svn_sqlite__stmt_t *stmt;
13988251881Speter  svn_wc__db_status_t base_status;
13989251881Speter
13990251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
13991251881Speter                                            NULL, NULL, NULL, NULL, NULL,
13992251881Speter                                            NULL, NULL, NULL, NULL, NULL, NULL,
13993251881Speter                                            wcroot, local_relpath,
13994251881Speter                                            scratch_pool, scratch_pool));
13995251881Speter
13996251881Speter  if (base_status == svn_wc__db_status_normal)
13997251881Speter    return SVN_NO_ERROR;
13998251881Speter
13999251881Speter  SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14000251881Speter
14001251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14002251881Speter                                    STMT_UPDATE_NODE_BASE_PRESENCE));
14003251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14004251881Speter                            presence_map, svn_wc__db_status_normal));
14005251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
14006251881Speter
14007251881Speter  return SVN_NO_ERROR;
14008251881Speter}
14009251881Speter
14010251881Spetersvn_error_t *
14011251881Spetersvn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14012251881Speter                                        const char *local_dir_abspath,
14013251881Speter                                        apr_pool_t *scratch_pool)
14014251881Speter{
14015251881Speter  svn_wc__db_wcroot_t *wcroot;
14016251881Speter  const char *local_relpath;
14017251881Speter
14018251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14019251881Speter
14020251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14021251881Speter                              local_dir_abspath, scratch_pool, scratch_pool));
14022251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14023251881Speter
14024251881Speter  SVN_WC__DB_WITH_TXN(
14025251881Speter    end_directory_update(wcroot, local_relpath, scratch_pool),
14026251881Speter    wcroot);
14027251881Speter
14028251881Speter  SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14029251881Speter                        scratch_pool));
14030251881Speter
14031251881Speter  return SVN_NO_ERROR;
14032251881Speter}
14033251881Speter
14034251881Speter
14035251881Speter/* The body of svn_wc__db_temp_op_start_directory_update().
14036251881Speter */
14037251881Speterstatic svn_error_t *
14038251881Speterstart_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14039251881Speter                           const char *local_relpath,
14040251881Speter                           const char *new_repos_relpath,
14041251881Speter                           svn_revnum_t new_rev,
14042251881Speter                           apr_pool_t *scratch_pool)
14043251881Speter{
14044251881Speter  svn_sqlite__stmt_t *stmt;
14045251881Speter
14046251881Speter  /* Note: In the majority of calls, the repos_relpath is unchanged. */
14047251881Speter  /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14048251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14049251881Speter                    STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14050251881Speter
14051251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14052251881Speter                            wcroot->wc_id,
14053251881Speter                            local_relpath,
14054251881Speter                            presence_map, svn_wc__db_status_incomplete,
14055251881Speter                            new_rev,
14056251881Speter                            new_repos_relpath));
14057251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
14058251881Speter
14059251881Speter  return SVN_NO_ERROR;
14060251881Speter
14061251881Speter}
14062251881Speter
14063251881Spetersvn_error_t *
14064251881Spetersvn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14065251881Speter                                          const char *local_abspath,
14066251881Speter                                          const char *new_repos_relpath,
14067251881Speter                                          svn_revnum_t new_rev,
14068251881Speter                                          apr_pool_t *scratch_pool)
14069251881Speter{
14070251881Speter  svn_wc__db_wcroot_t *wcroot;
14071251881Speter  const char *local_relpath;
14072251881Speter
14073251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14074251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14075251881Speter  SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14076251881Speter
14077251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14078251881Speter                              local_abspath, scratch_pool, scratch_pool));
14079251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14080251881Speter
14081251881Speter  SVN_WC__DB_WITH_TXN(
14082251881Speter    start_directory_update_txn(wcroot, local_relpath,
14083251881Speter                               new_repos_relpath, new_rev, scratch_pool),
14084251881Speter    wcroot);
14085251881Speter
14086251881Speter  SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14087251881Speter
14088251881Speter  return SVN_NO_ERROR;
14089251881Speter}
14090251881Speter
14091251881Speter
14092251881Speter/* The body of svn_wc__db_temp_op_make_copy().  This is
14093251881Speter   used by the update editor when deleting a base node tree would be a
14094251881Speter   tree-conflict because there are changes to subtrees.  This function
14095251881Speter   inserts a copy of the base node tree below any existing working
14096251881Speter   subtrees.  Given a tree:
14097251881Speter
14098251881Speter             0            1           2            3
14099251881Speter    /     normal          -
14100251881Speter    A     normal          -
14101251881Speter    A/B   normal          -         normal
14102251881Speter    A/B/C normal          -         base-del       normal
14103251881Speter    A/F   normal          -         normal
14104251881Speter    A/F/G normal          -         normal
14105251881Speter    A/F/H normal          -         base-deleted   normal
14106251881Speter    A/F/E normal          -         not-present
14107251881Speter    A/X   normal          -
14108251881Speter    A/X/Y incomplete      -
14109251881Speter
14110251881Speter    This function adds layers to A and some of its descendants in an attempt
14111251881Speter    to make the working copy look like as if it were a copy of the BASE nodes.
14112251881Speter
14113251881Speter             0            1              2            3
14114251881Speter    /     normal        -
14115251881Speter    A     normal        norm
14116251881Speter    A/B   normal        norm        norm
14117251881Speter    A/B/C normal        norm        base-del       normal
14118251881Speter    A/F   normal        norm        norm
14119251881Speter    A/F/G normal        norm        norm
14120251881Speter    A/F/H normal        norm        not-pres
14121251881Speter    A/F/E normal        norm        base-del
14122251881Speter    A/X   normal        norm
14123251881Speter    A/X/Y incomplete  incomplete
14124251881Speter */
14125251881Speterstatic svn_error_t *
14126251881Spetermake_copy_txn(svn_wc__db_wcroot_t *wcroot,
14127251881Speter              const char *local_relpath,
14128251881Speter              int op_depth,
14129251881Speter              const svn_skel_t *conflicts,
14130251881Speter              const svn_skel_t *work_items,
14131251881Speter              apr_pool_t *scratch_pool)
14132251881Speter{
14133251881Speter  svn_sqlite__stmt_t *stmt;
14134251881Speter  svn_boolean_t have_row;
14135251881Speter  svn_boolean_t add_working_base_deleted = FALSE;
14136251881Speter  svn_boolean_t remove_working = FALSE;
14137251881Speter  const apr_array_header_t *children;
14138251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14139251881Speter  int i;
14140251881Speter
14141251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14142251881Speter                                    STMT_SELECT_LOWEST_WORKING_NODE));
14143251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14144251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14145251881Speter
14146251881Speter  if (have_row)
14147251881Speter    {
14148251881Speter      svn_wc__db_status_t working_status;
14149251881Speter      int working_op_depth;
14150251881Speter
14151251881Speter      working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14152251881Speter      working_op_depth = svn_sqlite__column_int(stmt, 0);
14153251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
14154251881Speter
14155251881Speter      SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14156251881Speter                     || working_status == svn_wc__db_status_base_deleted
14157251881Speter                     || working_status == svn_wc__db_status_not_present
14158251881Speter                     || working_status == svn_wc__db_status_incomplete);
14159251881Speter
14160251881Speter      /* Only change nodes in the layers where we are creating the copy.
14161251881Speter         Deletes in higher layers will just apply to the copy */
14162251881Speter      if (working_op_depth <= op_depth)
14163251881Speter        {
14164251881Speter          add_working_base_deleted = TRUE;
14165251881Speter
14166251881Speter          if (working_status == svn_wc__db_status_base_deleted)
14167251881Speter            remove_working = TRUE;
14168251881Speter        }
14169251881Speter    }
14170251881Speter  else
14171251881Speter    SVN_ERR(svn_sqlite__reset(stmt));
14172251881Speter
14173251881Speter  if (remove_working)
14174251881Speter    {
14175251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14176251881Speter                                        STMT_DELETE_LOWEST_WORKING_NODE));
14177251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14178251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
14179251881Speter    }
14180251881Speter
14181251881Speter  if (add_working_base_deleted)
14182251881Speter    {
14183251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14184251881Speter                                        STMT_INSERT_DELETE_FROM_BASE));
14185251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14186251881Speter                                op_depth));
14187251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
14188251881Speter    }
14189251881Speter  else
14190251881Speter    {
14191251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14192251881Speter                                      STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14193251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14194251881Speter                                op_depth));
14195251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
14196251881Speter    }
14197251881Speter
14198251881Speter  /* Get the BASE children, as WORKING children don't need modifications */
14199251881Speter  SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14200251881Speter                               0, scratch_pool, iterpool));
14201251881Speter
14202251881Speter  for (i = 0; i < children->nelts; i++)
14203251881Speter    {
14204251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
14205251881Speter      const char *copy_relpath;
14206251881Speter
14207251881Speter      svn_pool_clear(iterpool);
14208251881Speter
14209251881Speter      copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14210251881Speter
14211251881Speter      SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14212251881Speter                            iterpool));
14213251881Speter    }
14214251881Speter
14215251881Speter  SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14216251881Speter                                                iterpool),
14217251881Speter                                                svn_depth_empty, iterpool));
14218251881Speter
14219251881Speter  if (conflicts)
14220251881Speter    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14221251881Speter                                              conflicts, iterpool));
14222251881Speter
14223251881Speter  SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14224251881Speter
14225251881Speter  svn_pool_destroy(iterpool);
14226251881Speter
14227251881Speter  return SVN_NO_ERROR;
14228251881Speter}
14229251881Speter
14230251881Speter
14231251881Spetersvn_error_t *
14232251881Spetersvn_wc__db_op_make_copy(svn_wc__db_t *db,
14233251881Speter                        const char *local_abspath,
14234251881Speter                        const svn_skel_t *conflicts,
14235251881Speter                        const svn_skel_t *work_items,
14236251881Speter                        apr_pool_t *scratch_pool)
14237251881Speter{
14238251881Speter  svn_wc__db_wcroot_t *wcroot;
14239251881Speter  const char *local_relpath;
14240251881Speter  svn_sqlite__stmt_t *stmt;
14241251881Speter  svn_boolean_t have_row;
14242251881Speter
14243251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14244251881Speter
14245251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14246251881Speter                              local_abspath, scratch_pool, scratch_pool));
14247251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14248251881Speter
14249251881Speter  /* The update editor is supposed to call this function when there is
14250251881Speter     no working node for LOCAL_ABSPATH. */
14251251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14252251881Speter                                    STMT_SELECT_WORKING_NODE));
14253251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14254251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14255251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14256251881Speter  if (have_row)
14257251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14258251881Speter                             _("Modification of '%s' already exists"),
14259251881Speter                             path_for_error_message(wcroot,
14260251881Speter                                                    local_relpath,
14261251881Speter                                                    scratch_pool));
14262251881Speter
14263251881Speter  /* We don't allow copies to contain server-excluded nodes;
14264251881Speter     the update editor is going to have to bail out. */
14265251881Speter  SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14266251881Speter
14267251881Speter  SVN_WC__DB_WITH_TXN(
14268251881Speter    make_copy_txn(wcroot, local_relpath,
14269251881Speter                  relpath_depth(local_relpath), conflicts, work_items,
14270251881Speter                  scratch_pool),
14271251881Speter    wcroot);
14272251881Speter
14273251881Speter  return SVN_NO_ERROR;
14274251881Speter}
14275251881Speter
14276251881Spetersvn_error_t *
14277251881Spetersvn_wc__db_info_below_working(svn_boolean_t *have_base,
14278251881Speter                              svn_boolean_t *have_work,
14279251881Speter                              svn_wc__db_status_t *status,
14280251881Speter                              svn_wc__db_t *db,
14281251881Speter                              const char *local_abspath,
14282251881Speter                              apr_pool_t *scratch_pool)
14283251881Speter{
14284251881Speter  svn_wc__db_wcroot_t *wcroot;
14285251881Speter  const char *local_relpath;
14286251881Speter
14287251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14288251881Speter
14289251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14290251881Speter                              local_abspath, scratch_pool, scratch_pool));
14291251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14292251881Speter  SVN_ERR(info_below_working(have_base, have_work, status,
14293251881Speter                             wcroot, local_relpath, -1, scratch_pool));
14294251881Speter
14295251881Speter  return SVN_NO_ERROR;
14296251881Speter}
14297251881Speter
14298251881Spetersvn_error_t *
14299251881Spetersvn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14300251881Speter                                       svn_wc__db_t *db,
14301251881Speter                                       const char *local_abspath,
14302251881Speter                                       apr_pool_t *result_pool,
14303251881Speter                                       apr_pool_t *scratch_pool)
14304251881Speter{
14305251881Speter  svn_wc__db_wcroot_t *wcroot;
14306251881Speter  const char *local_relpath;
14307251881Speter  svn_sqlite__stmt_t *stmt;
14308251881Speter  svn_boolean_t have_row;
14309251881Speter
14310251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14311251881Speter
14312251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14313251881Speter                              local_abspath, scratch_pool, scratch_pool));
14314251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14315251881Speter
14316251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14317251881Speter                                    STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14318251881Speter
14319251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14320251881Speter                            wcroot->wc_id,
14321251881Speter                            local_relpath,
14322251881Speter                            relpath_depth(local_relpath)));
14323251881Speter
14324251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14325251881Speter
14326251881Speter  if (have_row)
14327251881Speter    {
14328251881Speter      apr_array_header_t *paths;
14329251881Speter
14330251881Speter      paths = apr_array_make(result_pool, 4, sizeof(const char*));
14331251881Speter      while (have_row)
14332251881Speter        {
14333251881Speter          const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14334251881Speter
14335251881Speter          APR_ARRAY_PUSH(paths, const char *)
14336251881Speter              = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14337251881Speter                                           local_relpath, found_relpath));
14338251881Speter
14339251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
14340251881Speter        }
14341251881Speter
14342251881Speter      *descendants = paths;
14343251881Speter    }
14344251881Speter  else
14345251881Speter    *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14346251881Speter
14347251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
14348251881Speter}
14349251881Speter
14350251881Speter
14351251881Speter/* Like svn_wc__db_min_max_revisions(),
14352251881Speter * but accepts a WCROOT/LOCAL_RELPATH pair. */
14353251881Speterstatic svn_error_t *
14354251881Speterget_min_max_revisions(svn_revnum_t *min_revision,
14355251881Speter                      svn_revnum_t *max_revision,
14356251881Speter                      svn_wc__db_wcroot_t *wcroot,
14357251881Speter                      const char *local_relpath,
14358251881Speter                      svn_boolean_t committed,
14359251881Speter                      apr_pool_t *scratch_pool)
14360251881Speter{
14361251881Speter  svn_sqlite__stmt_t *stmt;
14362251881Speter  svn_revnum_t min_rev, max_rev;
14363251881Speter
14364251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14365251881Speter                                    STMT_SELECT_MIN_MAX_REVISIONS));
14366251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14367251881Speter  SVN_ERR(svn_sqlite__step_row(stmt));
14368251881Speter
14369251881Speter  if (committed)
14370251881Speter    {
14371251881Speter      min_rev = svn_sqlite__column_revnum(stmt, 2);
14372251881Speter      max_rev = svn_sqlite__column_revnum(stmt, 3);
14373251881Speter    }
14374251881Speter  else
14375251881Speter    {
14376251881Speter      min_rev = svn_sqlite__column_revnum(stmt, 0);
14377251881Speter      max_rev = svn_sqlite__column_revnum(stmt, 1);
14378251881Speter    }
14379251881Speter
14380251881Speter  /* The statement returns exactly one row. */
14381251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14382251881Speter
14383251881Speter  if (min_revision)
14384251881Speter    *min_revision = min_rev;
14385251881Speter  if (max_revision)
14386251881Speter    *max_revision = max_rev;
14387251881Speter
14388251881Speter  return SVN_NO_ERROR;
14389251881Speter}
14390251881Speter
14391251881Speter
14392251881Spetersvn_error_t *
14393251881Spetersvn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14394251881Speter                             svn_revnum_t *max_revision,
14395251881Speter                             svn_wc__db_t *db,
14396251881Speter                             const char *local_abspath,
14397251881Speter                             svn_boolean_t committed,
14398251881Speter                             apr_pool_t *scratch_pool)
14399251881Speter{
14400251881Speter  svn_wc__db_wcroot_t *wcroot;
14401251881Speter  const char *local_relpath;
14402251881Speter
14403251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14404251881Speter
14405251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14406251881Speter                                                db, local_abspath,
14407251881Speter                                                scratch_pool, scratch_pool));
14408251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14409251881Speter
14410251881Speter  return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14411251881Speter                                               wcroot, local_relpath,
14412251881Speter                                               committed, scratch_pool));
14413251881Speter}
14414251881Speter
14415251881Speter
14416251881Speter/* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14417251881Speter * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14418251881Speterstatic svn_error_t *
14419251881Speteris_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14420251881Speter                            svn_wc__db_wcroot_t *wcroot,
14421251881Speter                            const char *local_relpath,
14422251881Speter                            apr_pool_t *scratch_pool)
14423251881Speter{
14424251881Speter  svn_sqlite__stmt_t *stmt;
14425251881Speter  svn_boolean_t have_row;
14426251881Speter
14427251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14428251881Speter                                    STMT_HAS_SPARSE_NODES));
14429251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
14430251881Speter                            wcroot->wc_id,
14431251881Speter                            local_relpath));
14432251881Speter  /* If this query returns a row, the working copy is sparse. */
14433251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14434251881Speter  *is_sparse_checkout = have_row;
14435251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14436251881Speter
14437251881Speter  return SVN_NO_ERROR;
14438251881Speter}
14439251881Speter
14440251881Speter
14441251881Speter/* Like svn_wc__db_has_switched_subtrees(),
14442251881Speter * but accepts a WCROOT/LOCAL_RELPATH pair. */
14443251881Speterstatic svn_error_t *
14444251881Speterhas_switched_subtrees(svn_boolean_t *is_switched,
14445251881Speter                      svn_wc__db_wcroot_t *wcroot,
14446251881Speter                      const char *local_relpath,
14447251881Speter                      const char *trail_url,
14448251881Speter                      apr_pool_t *scratch_pool)
14449251881Speter{
14450251881Speter  svn_sqlite__stmt_t *stmt;
14451251881Speter  svn_boolean_t have_row;
14452251881Speter  apr_int64_t repos_id;
14453251881Speter  const char *repos_relpath;
14454251881Speter
14455251881Speter  /* Optional argument handling for caller */
14456251881Speter  if (!is_switched)
14457251881Speter    return SVN_NO_ERROR;
14458251881Speter
14459251881Speter  *is_switched = FALSE;
14460251881Speter
14461251881Speter  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14462251881Speter                                            &repos_relpath, &repos_id,
14463251881Speter                                            NULL, NULL, NULL, NULL, NULL,
14464251881Speter                                            NULL, NULL, NULL, NULL, NULL,
14465251881Speter                                            wcroot, local_relpath,
14466251881Speter                                            scratch_pool, scratch_pool));
14467251881Speter
14468251881Speter  /* First do the cheap check where we only need info on the origin itself */
14469251881Speter  if (trail_url != NULL)
14470251881Speter    {
14471251881Speter      const char *repos_root_url;
14472251881Speter      const char *url;
14473251881Speter      apr_size_t len1, len2;
14474251881Speter
14475251881Speter      /* If the trailing part of the URL of the working copy directory
14476251881Speter         does not match the given trailing URL then the whole working
14477251881Speter         copy is switched. */
14478251881Speter
14479251881Speter      SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
14480251881Speter                                          repos_id, scratch_pool));
14481251881Speter      url = svn_path_url_add_component2(repos_root_url, repos_relpath,
14482251881Speter                                        scratch_pool);
14483251881Speter
14484251881Speter      len1 = strlen(trail_url);
14485251881Speter      len2 = strlen(url);
14486251881Speter      if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
14487251881Speter        {
14488251881Speter          *is_switched = TRUE;
14489251881Speter          return SVN_NO_ERROR;
14490251881Speter        }
14491251881Speter    }
14492251881Speter
14493251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
14494251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
14495251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14496251881Speter  if (have_row)
14497251881Speter    *is_switched = TRUE;
14498251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14499251881Speter
14500251881Speter  return SVN_NO_ERROR;
14501251881Speter}
14502251881Speter
14503251881Speter
14504251881Spetersvn_error_t *
14505251881Spetersvn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
14506251881Speter                                 svn_wc__db_t *db,
14507251881Speter                                 const char *local_abspath,
14508251881Speter                                 const char *trail_url,
14509251881Speter                                 apr_pool_t *scratch_pool)
14510251881Speter{
14511251881Speter  svn_wc__db_wcroot_t *wcroot;
14512251881Speter  const char *local_relpath;
14513251881Speter
14514251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14515251881Speter
14516251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14517251881Speter                                                db, local_abspath,
14518251881Speter                                                scratch_pool, scratch_pool));
14519251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14520251881Speter
14521251881Speter  return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
14522251881Speter                                               local_relpath, trail_url,
14523251881Speter                                               scratch_pool));
14524251881Speter}
14525251881Speter
14526251881Spetersvn_error_t *
14527251881Spetersvn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
14528251881Speter                                 svn_wc__db_t *db,
14529251881Speter                                 const char *local_abspath,
14530251881Speter                                 apr_pool_t *result_pool,
14531251881Speter                                 apr_pool_t *scratch_pool)
14532251881Speter{
14533251881Speter  svn_wc__db_wcroot_t *wcroot;
14534251881Speter  const char *local_relpath;
14535251881Speter  svn_sqlite__stmt_t *stmt;
14536251881Speter  svn_boolean_t have_row;
14537251881Speter
14538251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14539251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14540251881Speter                                                db, local_abspath,
14541251881Speter                                                scratch_pool, scratch_pool));
14542251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14543251881Speter
14544251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14545251881Speter                                    STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
14546251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is",
14547251881Speter                            wcroot->wc_id,
14548251881Speter                            local_relpath));
14549251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14550251881Speter
14551251881Speter  if (have_row)
14552251881Speter    *excluded_subtrees = apr_hash_make(result_pool);
14553251881Speter  else
14554251881Speter    *excluded_subtrees = NULL;
14555251881Speter
14556251881Speter  while (have_row)
14557251881Speter    {
14558251881Speter      const char *abs_path =
14559251881Speter        svn_dirent_join(wcroot->abspath,
14560251881Speter                        svn_sqlite__column_text(stmt, 0, NULL),
14561251881Speter                        result_pool);
14562251881Speter      svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
14563251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
14564251881Speter    }
14565251881Speter
14566251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14567251881Speter  return SVN_NO_ERROR;
14568251881Speter}
14569251881Speter
14570251881Speter/* Like svn_wc__db_has_local_mods(),
14571251881Speter * but accepts a WCROOT/LOCAL_RELPATH pair.
14572251881Speter * ### This needs a DB as well as a WCROOT/RELPATH pair... */
14573251881Speterstatic svn_error_t *
14574251881Speterhas_local_mods(svn_boolean_t *is_modified,
14575251881Speter               svn_wc__db_wcroot_t *wcroot,
14576251881Speter               const char *local_relpath,
14577251881Speter               svn_wc__db_t *db,
14578251881Speter               svn_cancel_func_t cancel_func,
14579251881Speter               void *cancel_baton,
14580251881Speter               apr_pool_t *scratch_pool)
14581251881Speter{
14582251881Speter  svn_sqlite__stmt_t *stmt;
14583251881Speter
14584251881Speter  /* Check for additions or deletions. */
14585251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14586251881Speter                                    STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
14587251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14588251881Speter  /* If this query returns a row, the working copy is modified. */
14589251881Speter  SVN_ERR(svn_sqlite__step(is_modified, stmt));
14590251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
14591251881Speter
14592251881Speter  if (cancel_func)
14593251881Speter    SVN_ERR(cancel_func(cancel_baton));
14594251881Speter
14595251881Speter  if (! *is_modified)
14596251881Speter    {
14597251881Speter      /* Check for property modifications. */
14598251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14599251881Speter                                        STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
14600251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14601251881Speter      /* If this query returns a row, the working copy is modified. */
14602251881Speter      SVN_ERR(svn_sqlite__step(is_modified, stmt));
14603251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
14604251881Speter
14605251881Speter      if (cancel_func)
14606251881Speter        SVN_ERR(cancel_func(cancel_baton));
14607251881Speter    }
14608251881Speter
14609251881Speter  if (! *is_modified)
14610251881Speter    {
14611251881Speter      apr_pool_t *iterpool = NULL;
14612251881Speter      svn_boolean_t have_row;
14613251881Speter
14614251881Speter      /* Check for text modifications. */
14615251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14616251881Speter                                        STMT_SELECT_BASE_FILES_RECURSIVE));
14617251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14618251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
14619251881Speter      if (have_row)
14620251881Speter        iterpool = svn_pool_create(scratch_pool);
14621251881Speter      while (have_row)
14622251881Speter        {
14623251881Speter          const char *node_abspath;
14624251881Speter          svn_filesize_t recorded_size;
14625251881Speter          apr_time_t recorded_time;
14626251881Speter          svn_boolean_t skip_check = FALSE;
14627251881Speter          svn_error_t *err;
14628251881Speter
14629251881Speter          if (cancel_func)
14630251881Speter            {
14631251881Speter              err = cancel_func(cancel_baton);
14632251881Speter              if (err)
14633251881Speter                return svn_error_trace(svn_error_compose_create(
14634251881Speter                                                    err,
14635251881Speter                                                    svn_sqlite__reset(stmt)));
14636251881Speter            }
14637251881Speter
14638251881Speter          svn_pool_clear(iterpool);
14639251881Speter
14640251881Speter          node_abspath = svn_dirent_join(wcroot->abspath,
14641251881Speter                                         svn_sqlite__column_text(stmt, 0,
14642251881Speter                                                                 iterpool),
14643251881Speter                                         iterpool);
14644251881Speter
14645251881Speter          recorded_size = get_recorded_size(stmt, 1);
14646251881Speter          recorded_time = svn_sqlite__column_int64(stmt, 2);
14647251881Speter
14648251881Speter          if (recorded_size != SVN_INVALID_FILESIZE
14649251881Speter              && recorded_time != 0)
14650251881Speter            {
14651251881Speter              const svn_io_dirent2_t *dirent;
14652251881Speter
14653251881Speter              err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
14654251881Speter                                        iterpool, iterpool);
14655251881Speter              if (err)
14656251881Speter                return svn_error_trace(svn_error_compose_create(
14657251881Speter                                                    err,
14658251881Speter                                                    svn_sqlite__reset(stmt)));
14659251881Speter
14660251881Speter              if (dirent->kind != svn_node_file)
14661251881Speter                {
14662251881Speter                  *is_modified = TRUE; /* Missing or obstruction */
14663251881Speter                  break;
14664251881Speter                }
14665251881Speter              else if (dirent->filesize == recorded_size
14666251881Speter                       && dirent->mtime == recorded_time)
14667251881Speter                {
14668251881Speter                  /* The file is not modified */
14669251881Speter                  skip_check = TRUE;
14670251881Speter                }
14671251881Speter            }
14672251881Speter
14673251881Speter          if (! skip_check)
14674251881Speter            {
14675251881Speter              err = svn_wc__internal_file_modified_p(is_modified,
14676251881Speter                                                     db, node_abspath,
14677251881Speter                                                     FALSE, iterpool);
14678251881Speter
14679251881Speter              if (err)
14680251881Speter                return svn_error_trace(svn_error_compose_create(
14681251881Speter                                                    err,
14682251881Speter                                                    svn_sqlite__reset(stmt)));
14683251881Speter
14684251881Speter              if (*is_modified)
14685251881Speter                break;
14686251881Speter            }
14687251881Speter
14688251881Speter          SVN_ERR(svn_sqlite__step(&have_row, stmt));
14689251881Speter        }
14690251881Speter      if (iterpool)
14691251881Speter        svn_pool_destroy(iterpool);
14692251881Speter
14693251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
14694251881Speter    }
14695251881Speter
14696251881Speter  return SVN_NO_ERROR;
14697251881Speter}
14698251881Speter
14699251881Speter
14700251881Spetersvn_error_t *
14701251881Spetersvn_wc__db_has_local_mods(svn_boolean_t *is_modified,
14702251881Speter                          svn_wc__db_t *db,
14703251881Speter                          const char *local_abspath,
14704251881Speter                          svn_cancel_func_t cancel_func,
14705251881Speter                          void *cancel_baton,
14706251881Speter                          apr_pool_t *scratch_pool)
14707251881Speter{
14708251881Speter  svn_wc__db_wcroot_t *wcroot;
14709251881Speter  const char *local_relpath;
14710251881Speter
14711251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14712251881Speter
14713251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14714251881Speter                                                db, local_abspath,
14715251881Speter                                                scratch_pool, scratch_pool));
14716251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14717251881Speter
14718251881Speter  return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
14719251881Speter                                        db, cancel_func, cancel_baton,
14720251881Speter                                        scratch_pool));
14721251881Speter}
14722251881Speter
14723251881Speter
14724251881Speter/* The body of svn_wc__db_revision_status().
14725251881Speter */
14726251881Speterstatic svn_error_t *
14727251881Speterrevision_status_txn(svn_revnum_t *min_revision,
14728251881Speter                    svn_revnum_t *max_revision,
14729251881Speter                    svn_boolean_t *is_sparse_checkout,
14730251881Speter                    svn_boolean_t *is_modified,
14731251881Speter                    svn_boolean_t *is_switched,
14732251881Speter                    svn_wc__db_wcroot_t *wcroot,
14733251881Speter                    const char *local_relpath,
14734251881Speter                    svn_wc__db_t *db,
14735251881Speter                    const char *trail_url,
14736251881Speter                    svn_boolean_t committed,
14737251881Speter                    svn_cancel_func_t cancel_func,
14738251881Speter                    void *cancel_baton,
14739251881Speter                    apr_pool_t *scratch_pool)
14740251881Speter{
14741251881Speter  svn_error_t *err;
14742251881Speter  svn_boolean_t exists;
14743251881Speter
14744251881Speter  SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14745251881Speter
14746251881Speter  if (!exists)
14747251881Speter    {
14748251881Speter      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14749251881Speter                               _("The node '%s' was not found."),
14750251881Speter                               path_for_error_message(wcroot, local_relpath,
14751251881Speter                                                      scratch_pool));
14752251881Speter    }
14753251881Speter
14754251881Speter  /* Determine mixed-revisionness. */
14755251881Speter  SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
14756251881Speter                                local_relpath, committed, scratch_pool));
14757251881Speter
14758251881Speter  if (cancel_func)
14759251881Speter    SVN_ERR(cancel_func(cancel_baton));
14760251881Speter
14761251881Speter  /* Determine sparseness. */
14762251881Speter  SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
14763251881Speter                                      local_relpath, scratch_pool));
14764251881Speter
14765251881Speter  if (cancel_func)
14766251881Speter    SVN_ERR(cancel_func(cancel_baton));
14767251881Speter
14768251881Speter  /* Check for switched nodes. */
14769251881Speter  {
14770251881Speter    err = has_switched_subtrees(is_switched, wcroot, local_relpath,
14771251881Speter                                trail_url, scratch_pool);
14772251881Speter
14773251881Speter    if (err)
14774251881Speter      {
14775251881Speter        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
14776251881Speter          return svn_error_trace(err);
14777251881Speter
14778251881Speter        svn_error_clear(err); /* No Base node, but no fatal error */
14779251881Speter        *is_switched = FALSE;
14780251881Speter      }
14781251881Speter  }
14782251881Speter
14783251881Speter  if (cancel_func)
14784251881Speter    SVN_ERR(cancel_func(cancel_baton));
14785251881Speter
14786251881Speter  /* Check for local mods. */
14787251881Speter  SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
14788251881Speter                         cancel_func, cancel_baton, scratch_pool));
14789251881Speter
14790251881Speter  return SVN_NO_ERROR;
14791251881Speter}
14792251881Speter
14793251881Speter
14794251881Spetersvn_error_t *
14795251881Spetersvn_wc__db_revision_status(svn_revnum_t *min_revision,
14796251881Speter                           svn_revnum_t *max_revision,
14797251881Speter                           svn_boolean_t *is_sparse_checkout,
14798251881Speter                           svn_boolean_t *is_modified,
14799251881Speter                           svn_boolean_t *is_switched,
14800251881Speter                           svn_wc__db_t *db,
14801251881Speter                           const char *local_abspath,
14802251881Speter                           const char *trail_url,
14803251881Speter                           svn_boolean_t committed,
14804251881Speter                           svn_cancel_func_t cancel_func,
14805251881Speter                           void *cancel_baton,
14806251881Speter                           apr_pool_t *scratch_pool)
14807251881Speter{
14808251881Speter  svn_wc__db_wcroot_t *wcroot;
14809251881Speter  const char *local_relpath;
14810251881Speter
14811251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14812251881Speter
14813251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14814251881Speter                                                db, local_abspath,
14815251881Speter                                                scratch_pool, scratch_pool));
14816251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14817251881Speter
14818251881Speter  SVN_WC__DB_WITH_TXN(
14819251881Speter    revision_status_txn(min_revision, max_revision,
14820251881Speter                        is_sparse_checkout, is_modified, is_switched,
14821251881Speter                        wcroot, local_relpath, db,
14822251881Speter                        trail_url, committed, cancel_func, cancel_baton,
14823251881Speter                        scratch_pool),
14824251881Speter    wcroot);
14825251881Speter  return SVN_NO_ERROR;
14826251881Speter}
14827251881Speter
14828251881Speter
14829251881Spetersvn_error_t *
14830251881Spetersvn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
14831251881Speter                                          svn_wc__db_t *db,
14832251881Speter                                          const char *local_abspath,
14833251881Speter                                          apr_pool_t *result_pool,
14834251881Speter                                          apr_pool_t *scratch_pool)
14835251881Speter{
14836251881Speter  svn_wc__db_wcroot_t *wcroot;
14837251881Speter  const char *local_relpath;
14838251881Speter  svn_sqlite__stmt_t *stmt;
14839251881Speter  svn_boolean_t have_row;
14840251881Speter  apr_int64_t last_repos_id = INVALID_REPOS_ID;
14841251881Speter  const char *last_repos_root_url = NULL;
14842251881Speter
14843251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14844251881Speter
14845251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14846251881Speter                                                db, local_abspath,
14847251881Speter                                                scratch_pool, scratch_pool));
14848251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14849251881Speter
14850251881Speter  *lock_tokens = apr_hash_make(result_pool);
14851251881Speter
14852251881Speter  /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
14853251881Speter  SVN_ERR(svn_sqlite__get_statement(
14854251881Speter              &stmt, wcroot->sdb,
14855251881Speter              STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
14856251881Speter
14857251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14858251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
14859251881Speter  while (have_row)
14860251881Speter    {
14861251881Speter      apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
14862251881Speter      const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
14863251881Speter      const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
14864251881Speter
14865251881Speter      if (child_repos_id != last_repos_id)
14866251881Speter        {
14867251881Speter          svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
14868251881Speter                                                         NULL, wcroot->sdb,
14869251881Speter                                                         child_repos_id,
14870251881Speter                                                         scratch_pool);
14871251881Speter
14872251881Speter          if (err)
14873251881Speter            {
14874251881Speter              return svn_error_trace(
14875251881Speter                            svn_error_compose_create(err,
14876251881Speter                                                     svn_sqlite__reset(stmt)));
14877251881Speter            }
14878251881Speter
14879251881Speter          last_repos_id = child_repos_id;
14880251881Speter        }
14881251881Speter
14882251881Speter      SVN_ERR_ASSERT(last_repos_root_url != NULL);
14883251881Speter      svn_hash_sets(*lock_tokens,
14884251881Speter                    svn_path_url_add_component2(last_repos_root_url,
14885251881Speter                                                child_relpath, result_pool),
14886251881Speter                    lock_token);
14887251881Speter
14888251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
14889251881Speter    }
14890251881Speter  return svn_sqlite__reset(stmt);
14891251881Speter}
14892251881Speter
14893251881Speter
14894251881Speter/* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
14895251881Speter * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
14896251881Speter#define VERIFY(expression) \
14897251881Speter  do { \
14898251881Speter    if (! (expression)) \
14899251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
14900251881Speter        _("database inconsistency at local_relpath='%s' verifying " \
14901251881Speter          "expression '%s'"), local_relpath, #expression); \
14902251881Speter  } while (0)
14903251881Speter
14904251881Speter
14905251881Speter/* Verify consistency of the metadata concerning WCROOT.  This is intended
14906251881Speter * for use only during testing and debugging, so is not intended to be
14907251881Speter * blazingly fast.
14908251881Speter *
14909251881Speter * This code is a complement to any verification that we can do in SQLite
14910251881Speter * triggers.  See, for example, 'wc-checks.sql'.
14911251881Speter *
14912251881Speter * Some more verification steps we might want to add are:
14913251881Speter *
14914251881Speter *   * on every ACTUAL row (except root): a NODES row exists at its parent path
14915251881Speter *   * the op-depth root must always exist and every intermediate too
14916251881Speter */
14917251881Speterstatic svn_error_t *
14918251881Speterverify_wcroot(svn_wc__db_wcroot_t *wcroot,
14919251881Speter              apr_pool_t *scratch_pool)
14920251881Speter{
14921251881Speter  svn_sqlite__stmt_t *stmt;
14922251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14923251881Speter
14924251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14925251881Speter                                    STMT_SELECT_ALL_NODES));
14926251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
14927251881Speter  while (TRUE)
14928251881Speter    {
14929251881Speter      svn_boolean_t have_row;
14930251881Speter      const char *local_relpath, *parent_relpath;
14931251881Speter      int op_depth;
14932251881Speter
14933251881Speter      svn_pool_clear(iterpool);
14934251881Speter
14935251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
14936251881Speter      if (!have_row)
14937251881Speter        break;
14938251881Speter
14939251881Speter      op_depth = svn_sqlite__column_int(stmt, 0);
14940251881Speter      local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
14941251881Speter      parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
14942251881Speter
14943251881Speter      /* Verify parent_relpath is the parent path of local_relpath */
14944251881Speter      VERIFY((parent_relpath == NULL)
14945251881Speter             ? (local_relpath[0] == '\0')
14946251881Speter             : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
14947251881Speter                       parent_relpath) == 0));
14948251881Speter
14949251881Speter      /* Verify op_depth <= the tree depth of local_relpath */
14950251881Speter      VERIFY(op_depth <= relpath_depth(local_relpath));
14951251881Speter
14952251881Speter      /* Verify parent_relpath refers to a row that exists */
14953251881Speter      /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
14954251881Speter       * the child's and a suitable presence */
14955251881Speter      if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
14956251881Speter        {
14957251881Speter          svn_sqlite__stmt_t *stmt2;
14958251881Speter          svn_boolean_t have_a_parent_row;
14959251881Speter
14960251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
14961251881Speter                                            STMT_SELECT_NODE_INFO));
14962251881Speter          SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
14963251881Speter                                    parent_relpath));
14964251881Speter          SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
14965251881Speter          VERIFY(have_a_parent_row);
14966251881Speter          SVN_ERR(svn_sqlite__reset(stmt2));
14967251881Speter        }
14968251881Speter    }
14969251881Speter  svn_pool_destroy(iterpool);
14970251881Speter
14971251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
14972251881Speter}
14973251881Speter
14974251881Spetersvn_error_t *
14975251881Spetersvn_wc__db_verify(svn_wc__db_t *db,
14976251881Speter                  const char *wri_abspath,
14977251881Speter                  apr_pool_t *scratch_pool)
14978251881Speter{
14979251881Speter  svn_wc__db_wcroot_t *wcroot;
14980251881Speter  const char *local_relpath;
14981251881Speter
14982251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14983251881Speter                                                db, wri_abspath,
14984251881Speter                                                scratch_pool, scratch_pool));
14985251881Speter  VERIFY_USABLE_WCROOT(wcroot);
14986251881Speter
14987251881Speter  SVN_ERR(verify_wcroot(wcroot, scratch_pool));
14988251881Speter  return SVN_NO_ERROR;
14989251881Speter}
14990251881Speter
14991251881Spetersvn_error_t *
14992251881Spetersvn_wc__db_bump_format(int *result_format,
14993253734Speter                       svn_boolean_t *bumped_format,
14994253734Speter                       svn_wc__db_t *db,
14995251881Speter                       const char *wcroot_abspath,
14996251881Speter                       apr_pool_t *scratch_pool)
14997251881Speter{
14998251881Speter  svn_sqlite__db_t *sdb;
14999251881Speter  svn_error_t *err;
15000251881Speter  int format;
15001251881Speter
15002253734Speter  if (bumped_format)
15003253734Speter    *bumped_format = FALSE;
15004253734Speter
15005251881Speter  /* Do not scan upwards for a working copy root here to prevent accidental
15006251881Speter   * upgrades of any working copies the WCROOT might be nested in.
15007251881Speter   * Just try to open a DB at the specified path instead. */
15008251881Speter  err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15009251881Speter                                svn_sqlite__mode_readwrite,
15010251881Speter                                TRUE, /* exclusive */
15011251881Speter                                NULL, /* my statements */
15012251881Speter                                scratch_pool, scratch_pool);
15013251881Speter  if (err)
15014251881Speter    {
15015251881Speter      svn_error_t *err2;
15016251881Speter      apr_hash_t *entries;
15017251881Speter
15018251881Speter      /* Could not open an sdb. Check for an entries file instead. */
15019251881Speter      err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15020251881Speter                                      scratch_pool, scratch_pool);
15021251881Speter      if (err2 || apr_hash_count(entries) == 0)
15022251881Speter        return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15023251881Speter                  svn_error_compose_create(err, err2),
15024251881Speter                  _("Can't upgrade '%s' as it is not a working copy root"),
15025251881Speter                  svn_dirent_local_style(wcroot_abspath, scratch_pool));
15026251881Speter
15027251881Speter      /* An entries file was found. This is a pre-wc-ng working copy
15028251881Speter       * so suggest an upgrade. */
15029251881Speter      return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15030251881Speter                _("Working copy '%s' is too old and must be upgraded to "
15031251881Speter                  "at least format %d, as created by Subversion %s"),
15032251881Speter                svn_dirent_local_style(wcroot_abspath, scratch_pool),
15033251881Speter                SVN_WC__WC_NG_VERSION,
15034251881Speter                svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15035251881Speter    }
15036251881Speter
15037251881Speter  SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15038251881Speter  err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15039253734Speter                            sdb, format, scratch_pool);
15040251881Speter
15041253734Speter  if (err == SVN_NO_ERROR && bumped_format)
15042253734Speter    *bumped_format = (*result_format > format);
15043253734Speter
15044251881Speter  /* Make sure we return a different error than expected for upgrades from
15045251881Speter     entries */
15046251881Speter  if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15047251881Speter    err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15048251881Speter                           _("Working copy upgrade failed"));
15049251881Speter
15050251881Speter  err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15051251881Speter
15052251881Speter  return svn_error_trace(err);
15053251881Speter}
15054251881Speter
15055251881Spetersvn_error_t *
15056251881Spetersvn_wc__db_vacuum(svn_wc__db_t *db,
15057251881Speter                  const char *local_abspath,
15058251881Speter                  apr_pool_t *scratch_pool)
15059251881Speter{
15060251881Speter  svn_wc__db_wcroot_t *wcroot;
15061251881Speter  const char *local_relpath;
15062251881Speter
15063251881Speter  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15064251881Speter                                                db, local_abspath,
15065251881Speter                                                scratch_pool, scratch_pool));
15066251881Speter  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15067251881Speter
15068251881Speter  return SVN_NO_ERROR;
15069251881Speter}
15070