1 /*
2  * wc_db.c :  manipulating the administrative database
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 #define SVN_WC__I_AM_WC_DB
25 
26 #include <assert.h>
27 #include <apr_pools.h>
28 #include <apr_hash.h>
29 
30 #include "svn_private_config.h"
31 #include "svn_types.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_path.h"
35 #include "svn_hash.h"
36 #include "svn_sorts.h"
37 #include "svn_wc.h"
38 #include "svn_checksum.h"
39 #include "svn_pools.h"
40 
41 #include "wc.h"
42 #include "wc_db.h"
43 #include "adm_files.h"
44 #include "wc-queries.h"
45 #include "entries.h"
46 #include "lock.h"
47 #include "conflicts.h"
48 #include "wc_db_private.h"
49 #include "workqueue.h"
50 #include "token-map.h"
51 
52 #include "private/svn_sorts_private.h"
53 #include "private/svn_sqlite.h"
54 #include "private/svn_skel.h"
55 #include "private/svn_wc_private.h"
56 #include "private/svn_token.h"
57 
58 
59 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
60 
61 
62 /*
63  * Some filename constants.
64  */
65 #define SDB_FILE  "wc.db"
66 
67 #define WCROOT_TEMPDIR_RELPATH   "tmp"
68 
69 
70 /*
71  * PARAMETER ASSERTIONS
72  *
73  * Every (semi-)public entrypoint in this file has a set of assertions on
74  * the parameters passed into the function. Since this is a brand new API,
75  * we want to make sure that everybody calls it properly. The original WC
76  * code had years to catch stray bugs, but we do not have that luxury in
77  * the wc-nb rewrite. Any extra assurances that we can find will be
78  * welcome. The asserts will ensure we have no doubt about the values
79  * passed into the function.
80  *
81  * Some parameters are *not* specifically asserted. Typically, these are
82  * params that will be used immediately, so something like a NULL value
83  * will be obvious.
84  *
85  * ### near 1.7 release, it would be a Good Thing to review the assertions
86  * ### and decide if any can be removed or switched to assert() in order
87  * ### to remove their runtime cost in the production release.
88  *
89  *
90  * DATABASE OPERATIONS
91  *
92  * Each function should leave the database in a consistent state. If it
93  * does *not*, then the implication is some other function needs to be
94  * called to restore consistency. Subtle requirements like that are hard
95  * to maintain over a long period of time, so this API will not allow it.
96  *
97  *
98  * STANDARD VARIABLE NAMES
99  *
100  * db     working copy database (this module)
101  * sdb    SQLite database (not to be confused with 'db')
102  * wc_id  a WCROOT id associated with a node
103  */
104 
105 #define INVALID_REPOS_ID ((apr_int64_t) -1)
106 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
107 #define FORMAT_FROM_SDB (-1)
108 
109 /* Check if column number I, a property-skel column, contains a non-empty
110    set of properties. The empty set of properties is stored as "()", so we
111    have properties if the size of the column is larger than 2. */
112 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
113                  (svn_sqlite__column_bytes(stmt, i) > 2)
114 
115 int
svn_wc__db_op_depth_for_upgrade(const char * local_relpath)116 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
117 {
118   return relpath_depth(local_relpath);
119 }
120 
121 
122 /* Representation of a new base row for the NODES table */
123 typedef struct insert_base_baton_t {
124   /* common to all insertions into BASE */
125   svn_wc__db_status_t status;
126   svn_node_kind_t kind;
127   apr_int64_t repos_id;
128   const char *repos_relpath;
129   svn_revnum_t revision;
130 
131   /* Only used when repos_id == INVALID_REPOS_ID */
132   const char *repos_root_url;
133   const char *repos_uuid;
134 
135   /* common to all "normal" presence insertions */
136   const apr_hash_t *props;
137   svn_revnum_t changed_rev;
138   apr_time_t changed_date;
139   const char *changed_author;
140   const apr_hash_t *dav_cache;
141 
142   /* for inserting directories */
143   const apr_array_header_t *children;
144   svn_depth_t depth;
145 
146   /* for inserting files */
147   const svn_checksum_t *checksum;
148 
149   /* for inserting symlinks */
150   const char *target;
151 
152   svn_boolean_t file_external;
153 
154   /* may need to insert/update ACTUAL to record a conflict  */
155   const svn_skel_t *conflict;
156 
157   /* may need to insert/update ACTUAL to record new properties */
158   svn_boolean_t update_actual_props;
159   const apr_hash_t *new_actual_props;
160 
161   /* A depth-first ordered array of svn_prop_inherited_item_t *
162      structures representing the properties inherited by the base
163      node. */
164   apr_array_header_t *iprops;
165 
166   /* maybe we should copy information from a previous record? */
167   svn_boolean_t keep_recorded_info;
168 
169   /* insert a base-deleted working node as well as a base node */
170   svn_boolean_t insert_base_deleted;
171 
172   /* delete the current working nodes above BASE */
173   svn_boolean_t delete_working;
174 
175   /* may have work items to queue in this transaction  */
176   const svn_skel_t *work_items;
177 
178 } insert_base_baton_t;
179 
180 
181 /* Representation of a new working row for the NODES table */
182 typedef struct insert_working_baton_t {
183   /* common to all insertions into WORKING (including NODE_DATA) */
184   svn_wc__db_status_t presence;
185   svn_node_kind_t kind;
186   int op_depth;
187 
188   /* common to all "normal" presence insertions */
189   const apr_hash_t *props;
190   svn_revnum_t changed_rev;
191   apr_time_t changed_date;
192   const char *changed_author;
193   apr_int64_t original_repos_id;
194   const char *original_repos_relpath;
195   svn_revnum_t original_revnum;
196   svn_boolean_t moved_here;
197 
198   /* for inserting directories */
199   const apr_array_header_t *children;
200   svn_depth_t depth;
201 
202   /* for inserting (copied/moved-here) files */
203   const svn_checksum_t *checksum;
204 
205   /* for inserting symlinks */
206   const char *target;
207 
208   svn_boolean_t update_actual_props;
209   const apr_hash_t *new_actual_props;
210 
211   /* may have work items to queue in this transaction  */
212   const svn_skel_t *work_items;
213 
214   /* may have conflict to install in this transaction */
215   const svn_skel_t *conflict;
216 
217   /* If the value is > 0 and < op_depth, also insert a not-present
218      at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
219   int not_present_op_depth;
220 
221 } insert_working_baton_t;
222 
223 /* Representation of a new row for the EXTERNALS table */
224 typedef struct insert_external_baton_t {
225   /* common to all insertions into EXTERNALS */
226   svn_node_kind_t kind;
227   svn_wc__db_status_t presence;
228 
229   /* The repository of the external */
230   apr_int64_t repos_id;
231   /* for file and symlink externals */
232   const char *repos_relpath;
233   svn_revnum_t revision;
234 
235   /* Only used when repos_id == INVALID_REPOS_ID */
236   const char *repos_root_url;
237   const char *repos_uuid;
238 
239   /* for file and symlink externals */
240   const apr_hash_t *props;
241   apr_array_header_t *iprops;
242   svn_revnum_t changed_rev;
243   apr_time_t changed_date;
244   const char *changed_author;
245   const apr_hash_t *dav_cache;
246 
247   /* for inserting files */
248   const svn_checksum_t *checksum;
249 
250   /* for inserting symlinks */
251   const char *target;
252 
253   const char *record_ancestor_relpath;
254   const char *recorded_repos_relpath;
255   svn_revnum_t recorded_peg_revision;
256   svn_revnum_t recorded_revision;
257 
258   /* may need to insert/update ACTUAL to record a conflict  */
259   const svn_skel_t *conflict;
260 
261   /* may need to insert/update ACTUAL to record new properties */
262   svn_boolean_t update_actual_props;
263   const apr_hash_t *new_actual_props;
264 
265   /* maybe we should copy information from a previous record? */
266   svn_boolean_t keep_recorded_info;
267 
268   /* may have work items to queue in this transaction  */
269   const svn_skel_t *work_items;
270 
271 } insert_external_baton_t;
272 
273 
274 /* Forward declarations  */
275 static svn_error_t *
276 add_work_items(svn_sqlite__db_t *sdb,
277                const svn_skel_t *skel,
278                apr_pool_t *scratch_pool);
279 
280 static svn_error_t *
281 set_actual_props(svn_wc__db_wcroot_t *wcroot,
282                  const char *local_relpath,
283                  apr_hash_t *props,
284                  apr_pool_t *scratch_pool);
285 
286 static svn_error_t *
287 insert_incomplete_children(svn_sqlite__db_t *sdb,
288                            apr_int64_t wc_id,
289                            const char *local_relpath,
290                            apr_int64_t repos_id,
291                            const char *repos_relpath,
292                            svn_revnum_t revision,
293                            const apr_array_header_t *children,
294                            int op_depth,
295                            apr_pool_t *scratch_pool);
296 
297 static svn_error_t *
298 db_read_pristine_props(apr_hash_t **props,
299                        svn_wc__db_wcroot_t *wcroot,
300                        const char *local_relpath,
301                        svn_boolean_t deleted_ok,
302                        apr_pool_t *result_pool,
303                        apr_pool_t *scratch_pool);
304 
305 static svn_error_t *
306 read_info(svn_wc__db_status_t *status,
307           svn_node_kind_t *kind,
308           svn_revnum_t *revision,
309           const char **repos_relpath,
310           apr_int64_t *repos_id,
311           svn_revnum_t *changed_rev,
312           apr_time_t *changed_date,
313           const char **changed_author,
314           svn_depth_t *depth,
315           const svn_checksum_t **checksum,
316           const char **target,
317           const char **original_repos_relpath,
318           apr_int64_t *original_repos_id,
319           svn_revnum_t *original_revision,
320           svn_wc__db_lock_t **lock,
321           svn_filesize_t *recorded_size,
322           apr_time_t *recorded_time,
323           const char **changelist,
324           svn_boolean_t *conflicted,
325           svn_boolean_t *op_root,
326           svn_boolean_t *had_props,
327           svn_boolean_t *props_mod,
328           svn_boolean_t *have_base,
329           svn_boolean_t *have_more_work,
330           svn_boolean_t *have_work,
331           svn_wc__db_wcroot_t *wcroot,
332           const char *local_relpath,
333           apr_pool_t *result_pool,
334           apr_pool_t *scratch_pool);
335 
336 static svn_error_t *
337 scan_addition(svn_wc__db_status_t *status,
338               const char **op_root_relpath,
339               const char **repos_relpath,
340               apr_int64_t *repos_id,
341               const char **original_repos_relpath,
342               apr_int64_t *original_repos_id,
343               svn_revnum_t *original_revision,
344               const char **moved_from_relpath,
345               const char **moved_from_op_root_relpath,
346               int *moved_from_op_depth,
347               svn_wc__db_wcroot_t *wcroot,
348               const char *local_relpath,
349               apr_pool_t *result_pool,
350               apr_pool_t *scratch_pool);
351 
352 static svn_error_t *
353 convert_to_working_status(svn_wc__db_status_t *working_status,
354                           svn_wc__db_status_t status);
355 
356 static svn_error_t *
357 db_is_switched(svn_boolean_t *is_switched,
358                svn_node_kind_t *kind,
359                svn_wc__db_wcroot_t *wcroot,
360                const char *local_relpath,
361                apr_pool_t *scratch_pool);
362 
363 
364 /* Return the absolute path, in local path style, of LOCAL_RELPATH
365    in WCROOT.  */
366 static const char *
path_for_error_message(const svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool)367 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
368                        const char *local_relpath,
369                        apr_pool_t *result_pool)
370 {
371   const char *local_abspath
372     = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
373 
374   return svn_dirent_local_style(local_abspath, result_pool);
375 }
376 
377 
378 /* Return a file size from column SLOT of the SQLITE statement STMT, or
379    SVN_INVALID_FILESIZE if the column value is NULL.  */
380 static svn_filesize_t
get_recorded_size(svn_sqlite__stmt_t * stmt,int slot)381 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
382 {
383   if (svn_sqlite__column_is_null(stmt, slot))
384     return SVN_INVALID_FILESIZE;
385   return svn_sqlite__column_int64(stmt, slot);
386 }
387 
388 
389 /* Return a lock info structure constructed from the given columns of the
390    SQLITE statement STMT, or return NULL if the token column value is null.  */
391 static svn_wc__db_lock_t *
lock_from_columns(svn_sqlite__stmt_t * stmt,int col_token,int col_owner,int col_comment,int col_date,apr_pool_t * result_pool)392 lock_from_columns(svn_sqlite__stmt_t *stmt,
393                   int col_token,
394                   int col_owner,
395                   int col_comment,
396                   int col_date,
397                   apr_pool_t *result_pool)
398 {
399   svn_wc__db_lock_t *lock;
400 
401   if (svn_sqlite__column_is_null(stmt, col_token))
402     {
403       lock = NULL;
404     }
405   else
406     {
407       lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
408       lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
409       lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
410       lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
411       lock->date = svn_sqlite__column_int64(stmt, col_date);
412     }
413   return lock;
414 }
415 
416 
417 svn_error_t *
svn_wc__db_fetch_repos_info(const char ** repos_root_url,const char ** repos_uuid,svn_wc__db_wcroot_t * wcroot,apr_int64_t repos_id,apr_pool_t * result_pool)418 svn_wc__db_fetch_repos_info(const char **repos_root_url,
419                             const char **repos_uuid,
420                             svn_wc__db_wcroot_t *wcroot,
421                             apr_int64_t repos_id,
422                             apr_pool_t *result_pool)
423 {
424   svn_sqlite__stmt_t *stmt;
425   svn_boolean_t have_row;
426 
427   if (!repos_root_url && !repos_uuid)
428     return SVN_NO_ERROR;
429 
430   if (repos_id == INVALID_REPOS_ID)
431     {
432       if (repos_root_url)
433         *repos_root_url = NULL;
434       if (repos_uuid)
435         *repos_uuid = NULL;
436       return SVN_NO_ERROR;
437     }
438 
439   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
440                                     STMT_SELECT_REPOSITORY_BY_ID));
441   SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
442   SVN_ERR(svn_sqlite__step(&have_row, stmt));
443   if (!have_row)
444     return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
445                              _("No REPOSITORY table entry for id '%ld'"),
446                              (long int)repos_id);
447 
448   if (repos_root_url)
449     *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
450   if (repos_uuid)
451     *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
452 
453   return svn_error_trace(svn_sqlite__reset(stmt));
454 }
455 
456 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
457    SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
458    column value is null.  Any of the output parameters may be NULL if not
459    required.  */
460 static void
repos_location_from_columns(apr_int64_t * repos_id,svn_revnum_t * revision,const char ** repos_relpath,svn_sqlite__stmt_t * stmt,int col_repos_id,int col_revision,int col_repos_relpath,apr_pool_t * result_pool)461 repos_location_from_columns(apr_int64_t *repos_id,
462                             svn_revnum_t *revision,
463                             const char **repos_relpath,
464                             svn_sqlite__stmt_t *stmt,
465                             int col_repos_id,
466                             int col_revision,
467                             int col_repos_relpath,
468                             apr_pool_t *result_pool)
469 {
470   if (repos_id)
471     {
472       /* Fetch repository information via REPOS_ID. */
473       if (svn_sqlite__column_is_null(stmt, col_repos_id))
474         *repos_id = INVALID_REPOS_ID;
475       else
476         *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
477     }
478   if (revision)
479     {
480       *revision = svn_sqlite__column_revnum(stmt, col_revision);
481     }
482   if (repos_relpath)
483     {
484       *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
485                                                result_pool);
486     }
487 }
488 
489 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
490    value. If one does not exist, then create a new one. */
491 static svn_error_t *
create_repos_id(apr_int64_t * repos_id,const char * repos_root_url,const char * repos_uuid,svn_sqlite__db_t * sdb,apr_pool_t * scratch_pool)492 create_repos_id(apr_int64_t *repos_id,
493                 const char *repos_root_url,
494                 const char *repos_uuid,
495                 svn_sqlite__db_t *sdb,
496                 apr_pool_t *scratch_pool)
497 {
498   svn_sqlite__stmt_t *get_stmt;
499   svn_sqlite__stmt_t *insert_stmt;
500   svn_boolean_t have_row;
501 
502   SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
503   SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
504   SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
505 
506   if (have_row)
507     {
508       *repos_id = svn_sqlite__column_int64(get_stmt, 0);
509       return svn_error_trace(svn_sqlite__reset(get_stmt));
510     }
511   SVN_ERR(svn_sqlite__reset(get_stmt));
512 
513   /* NOTE: strictly speaking, there is a race condition between the
514      above query and the insertion below. We're simply going to ignore
515      that, as it means two processes are *modifying* the working copy
516      at the same time, *and* new repositores are becoming visible.
517      This is rare enough, let alone the miniscule chance of hitting
518      this race condition. Further, simply failing out will leave the
519      database in a consistent state, and the user can just re-run the
520      failed operation. */
521 
522   SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
523                                     STMT_INSERT_REPOSITORY));
524   SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
525   return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
526 }
527 
528 
529 /* Initialize the baton with appropriate "blank" values. This allows the
530    insertion function to leave certain columns null.  */
531 static void
blank_ibb(insert_base_baton_t * pibb)532 blank_ibb(insert_base_baton_t *pibb)
533 {
534   memset(pibb, 0, sizeof(*pibb));
535   pibb->revision = SVN_INVALID_REVNUM;
536   pibb->changed_rev = SVN_INVALID_REVNUM;
537   pibb->depth = svn_depth_infinity;
538   pibb->repos_id = INVALID_REPOS_ID;
539 }
540 
541 
542 /* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
543 
544    ### What about KIND and OP_DEPTH?  KIND ought to be redundant; I'm
545        discussing on dev@ whether we can let that be null for presence
546        == base-deleted.  OP_DEPTH is the op-depth of what, and why?
547        It is used to select the lowest working node higher than OP_DEPTH,
548        so, in terms of the API, OP_DEPTH means ...?
549 
550    Given a wc:
551 
552               0         1         2         3         4
553               normal
554    A          normal
555    A/B        normal              normal
556    A/B/C                          not-pres  normal
557    A/B/C/D                                            normal
558 
559    That is checkout, delete A/B, copy a replacement A/B, delete copied
560    child A/B/C, add replacement A/B/C, add A/B/C/D.
561 
562    Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
563    must extend the A/B deletion:
564 
565               0         1         2         3         4
566               normal
567    A          normal
568    A/B        normal              normal
569    A/B/C      normal              not-pres  normal
570    A/B/C/D    normal              base-del            normal
571    A/B/C/D/E  normal              base-del
572 
573    When adding a node if the parent has a higher working node then the
574    parent node is deleted (or replaced) and the delete must be extended
575    to cover new node.
576 
577    In the example above A/B/C/D and A/B/C/D/E are the nodes that get
578    the extended delete, A/B/C is already deleted.
579 
580    If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete
581    was recorded, otherwise to FALSE.
582  */
583 static svn_error_t *
db_extend_parent_delete(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_node_kind_t kind,int op_depth,apr_pool_t * scratch_pool)584 db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
585                         const char *local_relpath,
586                         svn_node_kind_t kind,
587                         int op_depth,
588                         apr_pool_t *scratch_pool)
589 {
590   svn_boolean_t have_row;
591   svn_sqlite__stmt_t *stmt;
592   int parent_op_depth;
593   const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
594 
595   SVN_ERR_ASSERT(local_relpath[0]);
596 
597   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
598                                     STMT_SELECT_LOWEST_WORKING_NODE));
599   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
600                             op_depth));
601   SVN_ERR(svn_sqlite__step(&have_row, stmt));
602   if (have_row)
603     parent_op_depth = svn_sqlite__column_int(stmt, 0);
604   SVN_ERR(svn_sqlite__reset(stmt));
605   if (have_row)
606     {
607       int existing_op_depth;
608 
609       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
610                                 op_depth));
611       SVN_ERR(svn_sqlite__step(&have_row, stmt));
612       if (have_row)
613         existing_op_depth = svn_sqlite__column_int(stmt, 0);
614       SVN_ERR(svn_sqlite__reset(stmt));
615       if (!have_row || parent_op_depth < existing_op_depth)
616         {
617           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
618                               STMT_INSTALL_WORKING_NODE_FOR_DELETE));
619           SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
620                                     local_relpath, parent_op_depth,
621                                     parent_relpath, kind_map, kind));
622           SVN_ERR(svn_sqlite__update(NULL, stmt));
623         }
624     }
625 
626   return SVN_NO_ERROR;
627 }
628 
629 
630 /* This is the reverse of db_extend_parent_delete.
631 
632    When removing a node if the parent has a higher working node then
633    the parent node and this node are both deleted or replaced and any
634    delete over this node must be removed.
635 
636    This function (like most wcroot functions) assumes that its caller
637    only uses this function within an sqlite transaction if atomic
638    behavior is needed.
639  */
640 static svn_error_t *
db_retract_parent_delete(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * scratch_pool)641 db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
642                          const char *local_relpath,
643                          int op_depth,
644                          apr_pool_t *scratch_pool)
645 {
646   svn_sqlite__stmt_t *stmt;
647   svn_boolean_t have_row;
648   int working_depth;
649   svn_wc__db_status_t presence;
650   const char *moved_to;
651 
652   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
653                                     STMT_SELECT_LOWEST_WORKING_NODE));
654   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
655                             op_depth));
656   SVN_ERR(svn_sqlite__step(&have_row, stmt));
657 
658   if (!have_row)
659     return svn_error_trace(svn_sqlite__reset(stmt));
660 
661   working_depth = svn_sqlite__column_int(stmt, 0);
662   presence = svn_sqlite__column_token(stmt, 1, presence_map);
663   moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
664 
665   SVN_ERR(svn_sqlite__reset(stmt));
666 
667   if (moved_to)
668     {
669       /* Turn the move into a copy to keep the NODES table valid */
670       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
671                                         STMT_CLEAR_MOVED_HERE_RECURSIVE));
672       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
673                                 moved_to, relpath_depth(moved_to)));
674       SVN_ERR(svn_sqlite__step_done(stmt));
675 
676       /* This leaves just the moved_to information on the origin,
677          which we will remove in the next step */
678     }
679 
680   if (presence == svn_wc__db_status_base_deleted)
681     {
682       /* Nothing left to shadow; remove the base-deleted node */
683       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
684     }
685   else if (moved_to)
686     {
687       /* Clear moved to information, as this node is no longer base-deleted */
688       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
689                                         STMT_CLEAR_MOVED_TO_RELPATH));
690       }
691   else
692     {
693       /* Nothing to update */
694       return SVN_NO_ERROR;
695     }
696 
697   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
698                             working_depth));
699 
700   return svn_error_trace(svn_sqlite__update(NULL, stmt));
701 }
702 
703 
704 
705 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
706 static svn_error_t *
insert_base_node(const insert_base_baton_t * pibb,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)707 insert_base_node(const insert_base_baton_t *pibb,
708                  svn_wc__db_wcroot_t *wcroot,
709                  const char *local_relpath,
710                  apr_pool_t *scratch_pool)
711 {
712   apr_int64_t repos_id = pibb->repos_id;
713   svn_sqlite__stmt_t *stmt;
714   svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
715   apr_int64_t recorded_time;
716   svn_boolean_t present;
717 
718   /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
719      bind the appropriate parent_relpath. */
720   const char *parent_relpath =
721     (*local_relpath == '\0') ? NULL
722     : svn_relpath_dirname(local_relpath, scratch_pool);
723 
724   if (pibb->repos_id == INVALID_REPOS_ID)
725     SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
726                             wcroot->sdb, scratch_pool));
727 
728   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
729   SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
730 
731   if (pibb->keep_recorded_info)
732     {
733       svn_boolean_t have_row;
734       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
735                                         STMT_SELECT_BASE_NODE));
736       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
737       SVN_ERR(svn_sqlite__step(&have_row, stmt));
738       if (have_row)
739         {
740           /* Preserve size and modification time if caller asked us to. */
741           recorded_size = get_recorded_size(stmt, 6);
742           recorded_time = svn_sqlite__column_int64(stmt, 12);
743         }
744       SVN_ERR(svn_sqlite__reset(stmt));
745     }
746 
747   present = (pibb->status == svn_wc__db_status_normal
748              || pibb->status == svn_wc__db_status_incomplete);
749 
750   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
751   SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
752                             "tstr"               /* 8 - 11 */
753                             "isnnnnns",          /* 12 - 19 */
754                             wcroot->wc_id,       /* 1 */
755                             local_relpath,       /* 2 */
756                             0,              /* op_depth is 0 for base */
757                             parent_relpath,      /* 4 */
758                             repos_id,
759                             pibb->repos_relpath,
760                             pibb->revision,
761                             presence_map, pibb->status, /* 8 */
762                             (pibb->kind == svn_node_dir && present) /* 9 */
763                               ? svn_token__to_word(depth_map, pibb->depth)
764                               : NULL,
765                             kind_map, pibb->kind, /* 10 */
766                             pibb->changed_rev,    /* 11 */
767                             pibb->changed_date,   /* 12 */
768                             pibb->changed_author, /* 13 */
769                             (pibb->kind == svn_node_symlink && present) ?
770                                 pibb->target : NULL)); /* 19 */
771   if (pibb->kind == svn_node_file && present)
772     {
773       if (!pibb->checksum
774           && pibb->status != svn_wc__db_status_not_present
775           && pibb->status != svn_wc__db_status_excluded
776           && pibb->status != svn_wc__db_status_server_excluded)
777         return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
778                                  _("The file '%s' has no checksum."),
779                                  path_for_error_message(wcroot, local_relpath,
780                                                         scratch_pool));
781 
782       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
783                                         scratch_pool));
784 
785       if (recorded_size != SVN_INVALID_FILESIZE)
786         {
787           SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
788           SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
789         }
790     }
791 
792   /* Set properties.  Must be null if presence not normal or incomplete. */
793   assert(pibb->status == svn_wc__db_status_normal
794          || pibb->status == svn_wc__db_status_incomplete
795          || pibb->props == NULL);
796   if (present)
797     {
798       SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
799                                           scratch_pool));
800 
801       SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
802                                       scratch_pool));
803     }
804 
805   if (pibb->dav_cache)
806     SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
807                                         scratch_pool));
808 
809   if (pibb->file_external)
810     SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
811 
812   SVN_ERR(svn_sqlite__insert(NULL, stmt));
813 
814   if (pibb->update_actual_props)
815     {
816       /* Cast away const, to allow calling property helpers */
817       apr_hash_t *base_props = (apr_hash_t *)pibb->props;
818       apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
819 
820       if (base_props != NULL
821           && new_actual_props != NULL
822           && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
823         {
824           apr_array_header_t *diffs;
825 
826           SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
827                                  scratch_pool));
828 
829           if (diffs->nelts == 0)
830             new_actual_props = NULL;
831         }
832 
833       SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
834                                scratch_pool));
835     }
836 
837   if (pibb->kind == svn_node_dir && pibb->children)
838     SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
839                                        local_relpath,
840                                        repos_id,
841                                        pibb->repos_relpath,
842                                        pibb->revision,
843                                        pibb->children,
844                                        0 /* BASE */,
845                                        scratch_pool));
846 
847   /* When this is not the root node, check shadowing behavior */
848   if (*local_relpath)
849     {
850       if (parent_relpath
851           && ((pibb->status == svn_wc__db_status_normal)
852               || (pibb->status == svn_wc__db_status_incomplete))
853           && ! pibb->file_external)
854         {
855           SVN_ERR(db_extend_parent_delete(wcroot, local_relpath,
856                                           pibb->kind, 0,
857                                           scratch_pool));
858         }
859       else if (pibb->status == svn_wc__db_status_not_present
860                || pibb->status == svn_wc__db_status_server_excluded
861                || pibb->status == svn_wc__db_status_excluded)
862         {
863           SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0,
864                                            scratch_pool));
865         }
866     }
867 
868   if (pibb->delete_working)
869     {
870       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
871                                     STMT_DELETE_WORKING_NODE));
872       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
873       SVN_ERR(svn_sqlite__step_done(stmt));
874     }
875   if (pibb->insert_base_deleted)
876     {
877       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
878                                         STMT_INSERT_DELETE_FROM_BASE));
879       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
880                                 wcroot->wc_id, local_relpath,
881                                 relpath_depth(local_relpath)));
882       SVN_ERR(svn_sqlite__step_done(stmt));
883     }
884 
885   SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
886   if (pibb->conflict)
887     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
888                                               pibb->conflict, scratch_pool));
889 
890   return SVN_NO_ERROR;
891 }
892 
893 
894 /* Initialize the baton with appropriate "blank" values. This allows the
895    insertion function to leave certain columns null.  */
896 static void
blank_iwb(insert_working_baton_t * piwb)897 blank_iwb(insert_working_baton_t *piwb)
898 {
899   memset(piwb, 0, sizeof(*piwb));
900   piwb->changed_rev = SVN_INVALID_REVNUM;
901   piwb->depth = svn_depth_infinity;
902 
903   /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
904      value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
905 }
906 
907 
908 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
909    whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH.  Set each
910    child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
911    repos_path by appending the child name to REPOS_PATH, and revision to
912    REVISION (which should match the parent's revision).
913 
914    If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
915 static svn_error_t *
insert_incomplete_children(svn_sqlite__db_t * sdb,apr_int64_t wc_id,const char * local_relpath,apr_int64_t repos_id,const char * repos_path,svn_revnum_t revision,const apr_array_header_t * children,int op_depth,apr_pool_t * scratch_pool)916 insert_incomplete_children(svn_sqlite__db_t *sdb,
917                            apr_int64_t wc_id,
918                            const char *local_relpath,
919                            apr_int64_t repos_id,
920                            const char *repos_path,
921                            svn_revnum_t revision,
922                            const apr_array_header_t *children,
923                            int op_depth,
924                            apr_pool_t *scratch_pool)
925 {
926   svn_sqlite__stmt_t *stmt;
927   int i;
928   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
929   apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
930 
931   SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
932   SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
933                  == (repos_path != NULL));
934 
935   /* If we're inserting WORKING nodes, we might be replacing existing
936    * nodes which were moved-away. We need to retain the moved-to relpath of
937    * such nodes in order not to lose move information during replace. */
938   if (op_depth > 0)
939     {
940       for (i = children->nelts; i--; )
941         {
942           const char *name = APR_ARRAY_IDX(children, i, const char *);
943           svn_boolean_t have_row;
944 
945           svn_pool_clear(iterpool);
946 
947           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
948                                             STMT_SELECT_WORKING_NODE));
949           SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
950                                     svn_relpath_join(local_relpath, name,
951                                                      iterpool)));
952           SVN_ERR(svn_sqlite__step(&have_row, stmt));
953           if (have_row && !svn_sqlite__column_is_null(stmt, 14))
954             svn_hash_sets(moved_to_relpaths, name,
955                           svn_sqlite__column_text(stmt, 14, scratch_pool));
956 
957           SVN_ERR(svn_sqlite__reset(stmt));
958         }
959     }
960 
961   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
962 
963   for (i = children->nelts; i--; )
964     {
965       const char *name = APR_ARRAY_IDX(children, i, const char *);
966 
967       svn_pool_clear(iterpool);
968 
969       SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
970                                 wc_id,
971                                 svn_relpath_join(local_relpath, name,
972                                                  iterpool),
973                                 op_depth,
974                                 local_relpath,
975                                 revision,
976                                 "incomplete", /* 8, presence */
977                                 "unknown",    /* 10, kind */
978                                 /* 21, moved_to */
979                                 svn_hash_gets(moved_to_relpaths, name)));
980       if (repos_id != INVALID_REPOS_ID)
981         {
982           SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
983           SVN_ERR(svn_sqlite__bind_text(stmt, 6,
984                                         svn_relpath_join(repos_path, name,
985                                                          iterpool)));
986         }
987 
988       SVN_ERR(svn_sqlite__insert(NULL, stmt));
989     }
990 
991   svn_pool_destroy(iterpool);
992 
993   return SVN_NO_ERROR;
994 }
995 
996 
997 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
998 static svn_error_t *
insert_working_node(const insert_working_baton_t * piwb,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)999 insert_working_node(const insert_working_baton_t *piwb,
1000                     svn_wc__db_wcroot_t *wcroot,
1001                     const char *local_relpath,
1002                     apr_pool_t *scratch_pool)
1003 {
1004   const char *parent_relpath;
1005   const char *moved_to_relpath = NULL;
1006   svn_sqlite__stmt_t *stmt;
1007   svn_boolean_t have_row;
1008   svn_boolean_t present;
1009 
1010   SVN_ERR_ASSERT(piwb->op_depth > 0);
1011 
1012   /* We cannot insert a WORKING_NODE row at the wcroot.  */
1013   SVN_ERR_ASSERT(*local_relpath != '\0');
1014   parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1015 
1016   /* Preserve existing moved-to information for this relpath,
1017    * which might exist in case we're replacing an existing base-deleted
1018    * node. */
1019   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1020   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1021                             piwb->op_depth));
1022   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1023   if (have_row)
1024     moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1025   SVN_ERR(svn_sqlite__reset(stmt));
1026 
1027   present = (piwb->presence == svn_wc__db_status_normal
1028              || piwb->presence == svn_wc__db_status_incomplete);
1029 
1030   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1031   SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1032                 "nnnn" /* properties translated_size last_mod_time dav_cache */
1033                 "sns", /* symlink_target, file_external, moved_to */
1034                 wcroot->wc_id, local_relpath,
1035                 piwb->op_depth,
1036                 parent_relpath,
1037                 presence_map, piwb->presence,
1038                 (piwb->kind == svn_node_dir && present)
1039                             ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1040                 kind_map, piwb->kind,
1041                 piwb->changed_rev,
1042                 piwb->changed_date,
1043                 piwb->changed_author,
1044                 /* Note: incomplete nodes may have a NULL target.  */
1045                 (piwb->kind == svn_node_symlink && present)
1046                             ? piwb->target : NULL,
1047                 moved_to_relpath));
1048 
1049   if (piwb->moved_here)
1050     {
1051       SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1052     }
1053 
1054   if (piwb->kind == svn_node_file && present)
1055     {
1056       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1057                                         scratch_pool));
1058     }
1059 
1060   if (piwb->original_repos_relpath != NULL)
1061     {
1062       SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1063       SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1064       SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1065     }
1066 
1067   /* Set properties.  Must be null if presence not normal or incomplete. */
1068   assert(piwb->presence == svn_wc__db_status_normal
1069          || piwb->presence == svn_wc__db_status_incomplete
1070          || piwb->props == NULL);
1071   if (present && piwb->original_repos_relpath)
1072     SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1073 
1074   SVN_ERR(svn_sqlite__insert(NULL, stmt));
1075 
1076   /* Insert incomplete children, if specified.
1077      The children are part of the same op and so have the same op_depth.
1078      (The only time we'd want a different depth is during a recursive
1079      simple add, but we never insert children here during a simple add.) */
1080   if (piwb->kind == svn_node_dir && piwb->children)
1081     SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1082                                        local_relpath,
1083                                        INVALID_REPOS_ID /* inherit repos_id */,
1084                                        NULL /* inherit repos_path */,
1085                                        piwb->original_revnum,
1086                                        piwb->children,
1087                                        piwb->op_depth,
1088                                        scratch_pool));
1089 
1090   if (piwb->update_actual_props)
1091     {
1092       /* Cast away const, to allow calling property helpers */
1093       apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1094       apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1095 
1096       if (base_props != NULL
1097           && new_actual_props != NULL
1098           && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1099         {
1100           apr_array_header_t *diffs;
1101 
1102           SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1103                                  scratch_pool));
1104 
1105           if (diffs->nelts == 0)
1106             new_actual_props = NULL;
1107         }
1108 
1109       SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
1110                                scratch_pool));
1111     }
1112 
1113   if (piwb->kind == svn_node_dir)
1114     {
1115       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116                                         STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1117       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1118       SVN_ERR(svn_sqlite__step_done(stmt));
1119 
1120       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1121                                         STMT_DELETE_ACTUAL_EMPTY));
1122       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1123       SVN_ERR(svn_sqlite__step_done(stmt));
1124     }
1125 
1126   if (piwb->not_present_op_depth > 0
1127       && piwb->not_present_op_depth < piwb->op_depth)
1128     {
1129       /* And also insert a not-present node to tell the commit processing that
1130          a child of the parent node was not copied. */
1131       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1132                                         STMT_INSERT_NODE));
1133 
1134       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1135                                 wcroot->wc_id, local_relpath,
1136                                 piwb->not_present_op_depth, parent_relpath,
1137                                 piwb->original_repos_id,
1138                                 piwb->original_repos_relpath,
1139                                 piwb->original_revnum,
1140                                 presence_map, svn_wc__db_status_not_present,
1141                                 /* NULL */
1142                                 kind_map, piwb->kind));
1143 
1144       SVN_ERR(svn_sqlite__step_done(stmt));
1145     }
1146 
1147   SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1148   if (piwb->conflict)
1149     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1150                                               piwb->conflict, scratch_pool));
1151 
1152   return SVN_NO_ERROR;
1153 }
1154 
1155 
1156 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1157    of any status, in all op-depths in the NODES table. */
1158 static svn_error_t *
gather_children(const apr_array_header_t ** children,svn_wc__db_wcroot_t * wcroot,const char * parent_relpath,int stmt_idx,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1159 gather_children(const apr_array_header_t **children,
1160                 svn_wc__db_wcroot_t *wcroot,
1161                 const char *parent_relpath,
1162                 int stmt_idx,
1163                 int op_depth,
1164                 apr_pool_t *result_pool,
1165                 apr_pool_t *scratch_pool)
1166 {
1167   apr_array_header_t *result;
1168   svn_sqlite__stmt_t *stmt;
1169   svn_boolean_t have_row;
1170 
1171   result = apr_array_make(result_pool, 16, sizeof(const char*));
1172 
1173   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
1174   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
1175   if (op_depth >= 0)
1176     SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth));
1177 
1178   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1179   while (have_row)
1180     {
1181       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1182       const char *name = svn_relpath_basename(child_relpath, result_pool);
1183 
1184       APR_ARRAY_PUSH(result, const char *) = name;
1185 
1186       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1187     }
1188 
1189   SVN_ERR(svn_sqlite__reset(stmt));
1190   *children = result;
1191   return SVN_NO_ERROR;
1192 }
1193 
1194 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1195  * Else, return FALSE. */
1196 static svn_boolean_t
is_immediate_child_path(const char * parent_abspath,const char * child_abspath)1197 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1198 {
1199   const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1200                                                        child_abspath);
1201 
1202   /* To be an immediate child local_relpath should have one (not empty)
1203      component */
1204   return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1205 }
1206 
1207 
1208 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1209 static void
remove_from_access_cache(apr_hash_t * access_cache,const char * local_abspath)1210 remove_from_access_cache(apr_hash_t *access_cache,
1211                          const char *local_abspath)
1212 {
1213   svn_wc_adm_access_t *adm_access;
1214 
1215   adm_access = svn_hash_gets(access_cache, local_abspath);
1216   if (adm_access)
1217     svn_wc__adm_access_set_entries(adm_access, NULL);
1218 }
1219 
1220 
1221 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1222  * the specified DEPTH, from the access baton cache in WCROOT.
1223  * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1224  *
1225  * This function must be called when the access baton cache goes stale,
1226  * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1227  *
1228  * Use SCRATCH_POOL for temporary allocations. */
1229 static svn_error_t *
flush_entries(svn_wc__db_wcroot_t * wcroot,const char * local_abspath,svn_depth_t depth,apr_pool_t * scratch_pool)1230 flush_entries(svn_wc__db_wcroot_t *wcroot,
1231               const char *local_abspath,
1232               svn_depth_t depth,
1233               apr_pool_t *scratch_pool)
1234 {
1235   const char *parent_abspath;
1236 
1237   if (apr_hash_count(wcroot->access_cache) == 0)
1238     return SVN_NO_ERROR;
1239 
1240   remove_from_access_cache(wcroot->access_cache, local_abspath);
1241 
1242   if (depth > svn_depth_empty)
1243     {
1244       apr_hash_index_t *hi;
1245 
1246       /* Flush access batons of children within the specified depth. */
1247       for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1248            hi;
1249            hi = apr_hash_next(hi))
1250         {
1251           const char *item_abspath = apr_hash_this_key(hi);
1252 
1253           if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1254               is_immediate_child_path(local_abspath, item_abspath))
1255             {
1256               remove_from_access_cache(wcroot->access_cache, item_abspath);
1257             }
1258           else if (depth == svn_depth_infinity &&
1259                    svn_dirent_is_ancestor(local_abspath, item_abspath))
1260             {
1261               remove_from_access_cache(wcroot->access_cache, item_abspath);
1262             }
1263         }
1264     }
1265 
1266   /* We're going to be overly aggressive here and just flush the parent
1267      without doing much checking.  This may hurt performance for
1268      legacy API consumers, but that's not our problem. :) */
1269   parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1270   remove_from_access_cache(wcroot->access_cache, parent_abspath);
1271 
1272   return SVN_NO_ERROR;
1273 }
1274 
1275 
1276 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1277    not perform its work within a transaction, assuming the caller will
1278    manage that.  */
1279 static svn_error_t *
add_single_work_item(svn_sqlite__db_t * sdb,const svn_skel_t * work_item,apr_pool_t * scratch_pool)1280 add_single_work_item(svn_sqlite__db_t *sdb,
1281                      const svn_skel_t *work_item,
1282                      apr_pool_t *scratch_pool)
1283 {
1284   svn_stringbuf_t *serialized;
1285   svn_sqlite__stmt_t *stmt;
1286 
1287   serialized = svn_skel__unparse(work_item, scratch_pool);
1288   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1289   SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1290   return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1291 }
1292 
1293 
1294 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1295    SKEL is usually passed to the various wc_db operation functions. It may
1296    be NULL, indicating no additional work items are needed, it may be a
1297    single work item, or it may be a list of work items.  */
1298 static svn_error_t *
add_work_items(svn_sqlite__db_t * sdb,const svn_skel_t * skel,apr_pool_t * scratch_pool)1299 add_work_items(svn_sqlite__db_t *sdb,
1300                const svn_skel_t *skel,
1301                apr_pool_t *scratch_pool)
1302 {
1303   apr_pool_t *iterpool;
1304 
1305   /* Maybe there are no work items to insert.  */
1306   if (skel == NULL)
1307     return SVN_NO_ERROR;
1308 
1309   /* Should have a list.  */
1310   SVN_ERR_ASSERT(!skel->is_atom);
1311 
1312   /* Is the list a single work item? Or a list of work items?  */
1313   if (SVN_WC__SINGLE_WORK_ITEM(skel))
1314     return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1315 
1316   /* SKEL is a list-of-lists, aka list of work items.  */
1317 
1318   iterpool = svn_pool_create(scratch_pool);
1319   for (skel = skel->children; skel; skel = skel->next)
1320     {
1321       svn_pool_clear(iterpool);
1322 
1323       SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1324     }
1325   svn_pool_destroy(iterpool);
1326 
1327   return SVN_NO_ERROR;
1328 }
1329 
1330 
1331 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH.  */
1332 static svn_error_t *
does_node_exist(svn_boolean_t * exists,const svn_wc__db_wcroot_t * wcroot,const char * local_relpath)1333 does_node_exist(svn_boolean_t *exists,
1334                 const svn_wc__db_wcroot_t *wcroot,
1335                 const char *local_relpath)
1336 {
1337   svn_sqlite__stmt_t *stmt;
1338 
1339   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1340   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1341   SVN_ERR(svn_sqlite__step(exists, stmt));
1342 
1343   return svn_error_trace(svn_sqlite__reset(stmt));
1344 }
1345 
1346 svn_error_t *
svn_wc__db_install_schema_statistics(svn_sqlite__db_t * sdb,apr_pool_t * scratch_pool)1347 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1348                                      apr_pool_t *scratch_pool)
1349 {
1350   SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1351 
1352   return SVN_NO_ERROR;
1353 }
1354 
1355 /* Helper for create_db(). Initializes our wc.db schema.
1356  */
1357 static svn_error_t *
init_db(apr_int64_t * repos_id,apr_int64_t * wc_id,svn_sqlite__db_t * db,const char * repos_root_url,const char * repos_uuid,const char * root_node_repos_relpath,svn_revnum_t root_node_revision,svn_depth_t root_node_depth,apr_pool_t * scratch_pool)1358 init_db(/* output values */
1359         apr_int64_t *repos_id,
1360         apr_int64_t *wc_id,
1361         /* input values */
1362         svn_sqlite__db_t *db,
1363         const char *repos_root_url,
1364         const char *repos_uuid,
1365         const char *root_node_repos_relpath,
1366         svn_revnum_t root_node_revision,
1367         svn_depth_t root_node_depth,
1368         apr_pool_t *scratch_pool)
1369 {
1370   svn_sqlite__stmt_t *stmt;
1371 
1372   /* Create the database's schema.  */
1373   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1374   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1375   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1376   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1377 
1378   SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1379 
1380   /* Insert the repository. */
1381   SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1382                           db, scratch_pool));
1383 
1384   /* Insert the wcroot. */
1385   /* ### Right now, this just assumes wc metadata is being stored locally. */
1386   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1387   SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1388 
1389   if (root_node_repos_relpath)
1390     {
1391       svn_wc__db_status_t status = svn_wc__db_status_normal;
1392 
1393       if (root_node_revision > 0)
1394         status = svn_wc__db_status_incomplete; /* Will be filled by update */
1395 
1396       SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1397       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1398                                 *wc_id,              /* 1 */
1399                                 "",                  /* 2 */
1400                                 0,                   /* op_depth is 0 for base */
1401                                 SVN_VA_NULL,         /* 4 */
1402                                 *repos_id,
1403                                 root_node_repos_relpath,
1404                                 root_node_revision,
1405                                 presence_map, status, /* 8 */
1406                                 svn_token__to_word(depth_map,
1407                                                    root_node_depth),
1408                                 kind_map, svn_node_dir /* 10 */));
1409 
1410       SVN_ERR(svn_sqlite__insert(NULL, stmt));
1411     }
1412 
1413   return SVN_NO_ERROR;
1414 }
1415 
1416 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1417    records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1418    REPOSITORY and for WC_ID into WCROOT.  Return the DB connection
1419    in *SDB.
1420 
1421    If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1422    the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1423    revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1424    */
1425 static svn_error_t *
create_db(svn_sqlite__db_t ** sdb,apr_int64_t * repos_id,apr_int64_t * wc_id,const char * dir_abspath,const char * repos_root_url,const char * repos_uuid,const char * sdb_fname,const char * root_node_repos_relpath,svn_revnum_t root_node_revision,svn_depth_t root_node_depth,svn_boolean_t exclusive,apr_int32_t timeout,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1426 create_db(svn_sqlite__db_t **sdb,
1427           apr_int64_t *repos_id,
1428           apr_int64_t *wc_id,
1429           const char *dir_abspath,
1430           const char *repos_root_url,
1431           const char *repos_uuid,
1432           const char *sdb_fname,
1433           const char *root_node_repos_relpath,
1434           svn_revnum_t root_node_revision,
1435           svn_depth_t root_node_depth,
1436           svn_boolean_t exclusive,
1437           apr_int32_t timeout,
1438           apr_pool_t *result_pool,
1439           apr_pool_t *scratch_pool)
1440 {
1441   SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1442                                   svn_sqlite__mode_rwcreate, exclusive,
1443                                   timeout,
1444                                   NULL /* my_statements */,
1445                                   result_pool, scratch_pool));
1446 
1447   SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1448                                 *sdb, repos_root_url, repos_uuid,
1449                                 root_node_repos_relpath, root_node_revision,
1450                                 root_node_depth, scratch_pool),
1451                         *sdb);
1452 
1453   return SVN_NO_ERROR;
1454 }
1455 
1456 
1457 svn_error_t *
svn_wc__db_init(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t initial_rev,svn_depth_t depth,apr_pool_t * scratch_pool)1458 svn_wc__db_init(svn_wc__db_t *db,
1459                 const char *local_abspath,
1460                 const char *repos_relpath,
1461                 const char *repos_root_url,
1462                 const char *repos_uuid,
1463                 svn_revnum_t initial_rev,
1464                 svn_depth_t depth,
1465                 apr_pool_t *scratch_pool)
1466 {
1467   svn_sqlite__db_t *sdb;
1468   apr_int64_t repos_id;
1469   apr_int64_t wc_id;
1470   svn_wc__db_wcroot_t *wcroot;
1471   svn_boolean_t sqlite_exclusive = FALSE;
1472   apr_int32_t sqlite_timeout = 0; /* default timeout */
1473   apr_hash_index_t *hi;
1474 
1475   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1476   SVN_ERR_ASSERT(repos_relpath != NULL);
1477   SVN_ERR_ASSERT(depth == svn_depth_empty
1478                  || depth == svn_depth_files
1479                  || depth == svn_depth_immediates
1480                  || depth == svn_depth_infinity);
1481 
1482   /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
1483 
1484   SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
1485                               SVN_CONFIG_SECTION_WORKING_COPY,
1486                               SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1487                               FALSE));
1488 
1489   /* Create the SDB and insert the basic rows.  */
1490   SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1491                     repos_uuid, SDB_FILE,
1492                     repos_relpath, initial_rev, depth, sqlite_exclusive,
1493                     sqlite_timeout,
1494                     db->state_pool, scratch_pool));
1495 
1496   /* Create the WCROOT for this directory.  */
1497   SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1498                         apr_pstrdup(db->state_pool, local_abspath),
1499                         sdb, wc_id, FORMAT_FROM_SDB,
1500                         FALSE /* auto-upgrade */,
1501                         db->state_pool, scratch_pool));
1502 
1503   /* Any previously cached children may now have a new WCROOT, most likely that
1504      of the new WCROOT, but there might be descendant directories that are their
1505      own working copy, in which case setting WCROOT to our new WCROOT might
1506      actually break things for those.
1507 
1508      Clearing is the safest thing we can do in this case, as a test would lead
1509      to unnecessary probing, while the standard code probes later anyway. So we
1510      only lose a bit of memory
1511 
1512      ### Perhaps we could check wcroot->abspath to detect which case we have
1513          where, but currently it is already very hard to trigger this from
1514          the short living 'svn' client. (GUI clients like TortoiseSVN are far
1515          more likely to get in these cases)
1516      */
1517   for (hi = apr_hash_first(scratch_pool, db->dir_data);
1518        hi;
1519        hi = apr_hash_next(hi))
1520     {
1521       const char *abspath = apr_hash_this_key(hi);
1522       if (svn_dirent_is_ancestor(wcroot->abspath, abspath))
1523         svn_hash_sets(db->dir_data, abspath, NULL);
1524     }
1525 
1526   /* The WCROOT is complete. Stash it into DB.  */
1527   svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1528 
1529   return SVN_NO_ERROR;
1530 }
1531 
1532 
1533 svn_error_t *
svn_wc__db_to_relpath(const char ** local_relpath,svn_wc__db_t * db,const char * wri_abspath,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1534 svn_wc__db_to_relpath(const char **local_relpath,
1535                       svn_wc__db_t *db,
1536                       const char *wri_abspath,
1537                       const char *local_abspath,
1538                       apr_pool_t *result_pool,
1539                       apr_pool_t *scratch_pool)
1540 {
1541   svn_wc__db_wcroot_t *wcroot;
1542   const char *relpath;
1543 
1544   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1545 
1546   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1547                               wri_abspath, result_pool, scratch_pool));
1548 
1549   /* This function is indirectly called from the upgrade code, so we
1550      can't verify the wcroot here. Just check that it is not NULL */
1551   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1552 
1553   if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1554     {
1555       *local_relpath = apr_pstrdup(result_pool,
1556                                    svn_dirent_skip_ancestor(wcroot->abspath,
1557                                                             local_abspath));
1558     }
1559   else
1560     /* Probably moving from $TMP. Should we allow this? */
1561     *local_relpath = apr_pstrdup(result_pool, local_abspath);
1562 
1563   return SVN_NO_ERROR;
1564 }
1565 
1566 
1567 svn_error_t *
svn_wc__db_from_relpath(const char ** local_abspath,svn_wc__db_t * db,const char * wri_abspath,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1568 svn_wc__db_from_relpath(const char **local_abspath,
1569                         svn_wc__db_t *db,
1570                         const char *wri_abspath,
1571                         const char *local_relpath,
1572                         apr_pool_t *result_pool,
1573                         apr_pool_t *scratch_pool)
1574 {
1575   svn_wc__db_wcroot_t *wcroot;
1576   const char *unused_relpath;
1577 #if 0
1578   SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1579 #endif
1580 
1581   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1582                               wri_abspath, scratch_pool, scratch_pool));
1583 
1584   /* This function is indirectly called from the upgrade code, so we
1585      can't verify the wcroot here. Just check that it is not NULL */
1586   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1587 
1588 
1589   *local_abspath = svn_dirent_join(wcroot->abspath,
1590                                    local_relpath,
1591                                    result_pool);
1592   return SVN_NO_ERROR;
1593 }
1594 
1595 
1596 svn_error_t *
svn_wc__db_get_wcroot(const char ** wcroot_abspath,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1597 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1598                       svn_wc__db_t *db,
1599                       const char *wri_abspath,
1600                       apr_pool_t *result_pool,
1601                       apr_pool_t *scratch_pool)
1602 {
1603   svn_wc__db_wcroot_t *wcroot;
1604   const char *unused_relpath;
1605 
1606   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1607                               wri_abspath, scratch_pool, scratch_pool));
1608 
1609   /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1610      where call upgrade */
1611   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1612 
1613   *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1614 
1615   return SVN_NO_ERROR;
1616 }
1617 
1618 
1619 svn_error_t *
svn_wc__db_base_add_directory(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const apr_array_header_t * children,svn_depth_t depth,apr_hash_t * dav_cache,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,apr_array_header_t * new_iprops,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1620 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1621                               const char *local_abspath,
1622                               const char *wri_abspath,
1623                               const char *repos_relpath,
1624                               const char *repos_root_url,
1625                               const char *repos_uuid,
1626                               svn_revnum_t revision,
1627                               const apr_hash_t *props,
1628                               svn_revnum_t changed_rev,
1629                               apr_time_t changed_date,
1630                               const char *changed_author,
1631                               const apr_array_header_t *children,
1632                               svn_depth_t depth,
1633                               apr_hash_t *dav_cache,
1634                               svn_boolean_t update_actual_props,
1635                               apr_hash_t *new_actual_props,
1636                               apr_array_header_t *new_iprops,
1637                               const svn_skel_t *conflict,
1638                               const svn_skel_t *work_items,
1639                               apr_pool_t *scratch_pool)
1640 {
1641   svn_wc__db_wcroot_t *wcroot;
1642   const char *local_relpath;
1643   insert_base_baton_t ibb;
1644 
1645   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1646   SVN_ERR_ASSERT(repos_relpath != NULL);
1647   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1648   SVN_ERR_ASSERT(repos_uuid != NULL);
1649   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1650   SVN_ERR_ASSERT(props != NULL);
1651   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1652 #if 0
1653   SVN_ERR_ASSERT(children != NULL);
1654 #endif
1655 
1656   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1657                               wri_abspath, scratch_pool, scratch_pool));
1658   VERIFY_USABLE_WCROOT(wcroot);
1659   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1660 
1661   blank_ibb(&ibb);
1662 
1663   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1664   ibb.repos_root_url = repos_root_url;
1665   ibb.repos_uuid = repos_uuid;
1666 
1667   ibb.status = svn_wc__db_status_normal;
1668   ibb.kind = svn_node_dir;
1669   ibb.repos_relpath = repos_relpath;
1670   ibb.revision = revision;
1671 
1672   ibb.iprops = new_iprops;
1673   ibb.props = props;
1674   ibb.changed_rev = changed_rev;
1675   ibb.changed_date = changed_date;
1676   ibb.changed_author = changed_author;
1677 
1678   ibb.children = children;
1679   ibb.depth = depth;
1680 
1681   ibb.dav_cache = dav_cache;
1682   ibb.conflict = conflict;
1683   ibb.work_items = work_items;
1684 
1685   if (update_actual_props)
1686     {
1687       ibb.update_actual_props = TRUE;
1688       ibb.new_actual_props = new_actual_props;
1689     }
1690 
1691   /* Insert the directory and all its children transactionally.
1692 
1693      Note: old children can stick around, even if they are no longer present
1694      in this directory's revision.  */
1695   SVN_WC__DB_WITH_TXN(
1696             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1697             wcroot);
1698 
1699   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1700   return SVN_NO_ERROR;
1701 }
1702 
1703 svn_error_t *
svn_wc__db_base_add_incomplete_directory(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_depth_t depth,svn_boolean_t insert_base_deleted,svn_boolean_t delete_working,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)1704 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1705                                          const char *local_abspath,
1706                                          const char *repos_relpath,
1707                                          const char *repos_root_url,
1708                                          const char *repos_uuid,
1709                                          svn_revnum_t revision,
1710                                          svn_depth_t depth,
1711                                          svn_boolean_t insert_base_deleted,
1712                                          svn_boolean_t delete_working,
1713                                          svn_skel_t *conflict,
1714                                          svn_skel_t *work_items,
1715                                          apr_pool_t *scratch_pool)
1716 {
1717   svn_wc__db_wcroot_t *wcroot;
1718   const char *local_relpath;
1719   struct insert_base_baton_t ibb;
1720 
1721   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1722   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1723   SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1724 
1725   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1726                                                 db, local_abspath,
1727                                                 scratch_pool, scratch_pool));
1728 
1729   VERIFY_USABLE_WCROOT(wcroot);
1730 
1731   blank_ibb(&ibb);
1732 
1733   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1734   ibb.repos_root_url = repos_root_url;
1735   ibb.repos_uuid = repos_uuid;
1736 
1737   ibb.status = svn_wc__db_status_incomplete;
1738   ibb.kind = svn_node_dir;
1739   ibb.repos_relpath = repos_relpath;
1740   ibb.revision = revision;
1741   ibb.depth = depth;
1742   ibb.insert_base_deleted = insert_base_deleted;
1743   ibb.delete_working = delete_working;
1744 
1745   ibb.conflict = conflict;
1746   ibb.work_items = work_items;
1747 
1748   SVN_WC__DB_WITH_TXN(
1749             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1750             wcroot);
1751 
1752   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1753 
1754   return SVN_NO_ERROR;
1755 }
1756 
1757 
1758 svn_error_t *
svn_wc__db_base_add_file(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * checksum,apr_hash_t * dav_cache,svn_boolean_t delete_working,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,apr_array_header_t * new_iprops,svn_boolean_t keep_recorded_info,svn_boolean_t insert_base_deleted,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1759 svn_wc__db_base_add_file(svn_wc__db_t *db,
1760                          const char *local_abspath,
1761                          const char *wri_abspath,
1762                          const char *repos_relpath,
1763                          const char *repos_root_url,
1764                          const char *repos_uuid,
1765                          svn_revnum_t revision,
1766                          const apr_hash_t *props,
1767                          svn_revnum_t changed_rev,
1768                          apr_time_t changed_date,
1769                          const char *changed_author,
1770                          const svn_checksum_t *checksum,
1771                          apr_hash_t *dav_cache,
1772                          svn_boolean_t delete_working,
1773                          svn_boolean_t update_actual_props,
1774                          apr_hash_t *new_actual_props,
1775                          apr_array_header_t *new_iprops,
1776                          svn_boolean_t keep_recorded_info,
1777                          svn_boolean_t insert_base_deleted,
1778                          const svn_skel_t *conflict,
1779                          const svn_skel_t *work_items,
1780                          apr_pool_t *scratch_pool)
1781 {
1782   svn_wc__db_wcroot_t *wcroot;
1783   const char *local_relpath;
1784   insert_base_baton_t ibb;
1785 
1786   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1787   SVN_ERR_ASSERT(repos_relpath != NULL);
1788   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1789   SVN_ERR_ASSERT(repos_uuid != NULL);
1790   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1791   SVN_ERR_ASSERT(props != NULL);
1792   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1793   SVN_ERR_ASSERT(checksum != NULL);
1794 
1795   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1796                               wri_abspath, scratch_pool, scratch_pool));
1797   VERIFY_USABLE_WCROOT(wcroot);
1798   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1799 
1800   blank_ibb(&ibb);
1801 
1802   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1803   ibb.repos_root_url = repos_root_url;
1804   ibb.repos_uuid = repos_uuid;
1805 
1806   ibb.status = svn_wc__db_status_normal;
1807   ibb.kind = svn_node_file;
1808   ibb.repos_relpath = repos_relpath;
1809   ibb.revision = revision;
1810 
1811   ibb.props = props;
1812   ibb.changed_rev = changed_rev;
1813   ibb.changed_date = changed_date;
1814   ibb.changed_author = changed_author;
1815 
1816   ibb.checksum = checksum;
1817 
1818   ibb.dav_cache = dav_cache;
1819   ibb.iprops = new_iprops;
1820 
1821   if (update_actual_props)
1822     {
1823       ibb.update_actual_props = TRUE;
1824       ibb.new_actual_props = new_actual_props;
1825     }
1826 
1827   ibb.keep_recorded_info = keep_recorded_info;
1828   ibb.insert_base_deleted = insert_base_deleted;
1829   ibb.delete_working = delete_working;
1830 
1831   ibb.conflict = conflict;
1832   ibb.work_items = work_items;
1833 
1834   SVN_WC__DB_WITH_TXN(
1835             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1836             wcroot);
1837 
1838   /* If this used to be a directory we should remove children so pass
1839    * depth infinity. */
1840   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1841                         scratch_pool));
1842   return SVN_NO_ERROR;
1843 }
1844 
1845 
1846 svn_error_t *
svn_wc__db_base_add_symlink(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * target,apr_hash_t * dav_cache,svn_boolean_t delete_working,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,apr_array_header_t * new_iprops,svn_boolean_t keep_recorded_info,svn_boolean_t insert_base_deleted,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1847 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1848                             const char *local_abspath,
1849                             const char *wri_abspath,
1850                             const char *repos_relpath,
1851                             const char *repos_root_url,
1852                             const char *repos_uuid,
1853                             svn_revnum_t revision,
1854                             const apr_hash_t *props,
1855                             svn_revnum_t changed_rev,
1856                             apr_time_t changed_date,
1857                             const char *changed_author,
1858                             const char *target,
1859                             apr_hash_t *dav_cache,
1860                             svn_boolean_t delete_working,
1861                             svn_boolean_t update_actual_props,
1862                             apr_hash_t *new_actual_props,
1863                             apr_array_header_t *new_iprops,
1864                             svn_boolean_t keep_recorded_info,
1865                             svn_boolean_t insert_base_deleted,
1866                             const svn_skel_t *conflict,
1867                             const svn_skel_t *work_items,
1868                             apr_pool_t *scratch_pool)
1869 {
1870   svn_wc__db_wcroot_t *wcroot;
1871   const char *local_relpath;
1872   insert_base_baton_t ibb;
1873 
1874   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1875   SVN_ERR_ASSERT(repos_relpath != NULL);
1876   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1877   SVN_ERR_ASSERT(repos_uuid != NULL);
1878   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1879   SVN_ERR_ASSERT(props != NULL);
1880   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1881   SVN_ERR_ASSERT(target != NULL);
1882 
1883   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1884                               wri_abspath, scratch_pool, scratch_pool));
1885   VERIFY_USABLE_WCROOT(wcroot);
1886   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1887   blank_ibb(&ibb);
1888 
1889   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1890   ibb.repos_root_url = repos_root_url;
1891   ibb.repos_uuid = repos_uuid;
1892 
1893   ibb.status = svn_wc__db_status_normal;
1894   ibb.kind = svn_node_symlink;
1895   ibb.repos_relpath = repos_relpath;
1896   ibb.revision = revision;
1897 
1898   ibb.props = props;
1899   ibb.changed_rev = changed_rev;
1900   ibb.changed_date = changed_date;
1901   ibb.changed_author = changed_author;
1902 
1903   ibb.target = target;
1904 
1905   ibb.dav_cache = dav_cache;
1906   ibb.iprops = new_iprops;
1907 
1908   if (update_actual_props)
1909     {
1910       ibb.update_actual_props = TRUE;
1911       ibb.new_actual_props = new_actual_props;
1912     }
1913 
1914   ibb.keep_recorded_info = keep_recorded_info;
1915   ibb.insert_base_deleted = insert_base_deleted;
1916   ibb.delete_working = delete_working;
1917 
1918   ibb.conflict = conflict;
1919   ibb.work_items = work_items;
1920 
1921   SVN_WC__DB_WITH_TXN(
1922             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1923             wcroot);
1924 
1925   /* If this used to be a directory we should remove children so pass
1926    * depth infinity. */
1927   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1928                         scratch_pool));
1929   return SVN_NO_ERROR;
1930 }
1931 
1932 
1933 static svn_error_t *
add_excluded_or_not_present_node(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_node_kind_t kind,svn_wc__db_status_t status,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1934 add_excluded_or_not_present_node(svn_wc__db_t *db,
1935                                  const char *local_abspath,
1936                                  const char *repos_relpath,
1937                                  const char *repos_root_url,
1938                                  const char *repos_uuid,
1939                                  svn_revnum_t revision,
1940                                  svn_node_kind_t kind,
1941                                  svn_wc__db_status_t status,
1942                                  const svn_skel_t *conflict,
1943                                  const svn_skel_t *work_items,
1944                                  apr_pool_t *scratch_pool)
1945 {
1946   svn_wc__db_wcroot_t *wcroot;
1947   const char *local_relpath;
1948   insert_base_baton_t ibb;
1949   const char *dir_abspath, *name;
1950 
1951   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1952   SVN_ERR_ASSERT(repos_relpath != NULL);
1953   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1954   SVN_ERR_ASSERT(repos_uuid != NULL);
1955   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1956   SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1957                  || status == svn_wc__db_status_excluded
1958                  || status == svn_wc__db_status_not_present);
1959 
1960   /* These absent presence nodes are only useful below a parent node that is
1961      present. To avoid problems with working copies obstructing the child
1962      we calculate the wcroot and local_relpath of the parent and then add
1963      our own relpath. */
1964 
1965   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1966 
1967   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1968                               dir_abspath, scratch_pool, scratch_pool));
1969   VERIFY_USABLE_WCROOT(wcroot);
1970 
1971   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1972 
1973   blank_ibb(&ibb);
1974 
1975   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1976   ibb.repos_root_url = repos_root_url;
1977   ibb.repos_uuid = repos_uuid;
1978 
1979   ibb.status = status;
1980   ibb.kind = kind;
1981   ibb.repos_relpath = repos_relpath;
1982   ibb.revision = revision;
1983 
1984   /* Depending upon KIND, any of these might get used. */
1985   ibb.children = NULL;
1986   ibb.depth = svn_depth_unknown;
1987   ibb.checksum = NULL;
1988   ibb.target = NULL;
1989 
1990   ibb.conflict = conflict;
1991   ibb.work_items = work_items;
1992 
1993   SVN_WC__DB_WITH_TXN(
1994             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1995             wcroot);
1996 
1997   /* If this used to be a directory we should remove children so pass
1998    * depth infinity. */
1999   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2000                         scratch_pool));
2001 
2002   return SVN_NO_ERROR;
2003 }
2004 
2005 
2006 svn_error_t *
svn_wc__db_base_add_excluded_node(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_node_kind_t kind,svn_wc__db_status_t status,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)2007 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2008                                   const char *local_abspath,
2009                                   const char *repos_relpath,
2010                                   const char *repos_root_url,
2011                                   const char *repos_uuid,
2012                                   svn_revnum_t revision,
2013                                   svn_node_kind_t kind,
2014                                   svn_wc__db_status_t status,
2015                                   const svn_skel_t *conflict,
2016                                   const svn_skel_t *work_items,
2017                                   apr_pool_t *scratch_pool)
2018 {
2019   SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2020                  || status == svn_wc__db_status_excluded);
2021 
2022   return add_excluded_or_not_present_node(
2023     db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2024     kind, status, conflict, work_items, scratch_pool);
2025 }
2026 
2027 
2028 svn_error_t *
svn_wc__db_base_add_not_present_node(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_node_kind_t kind,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)2029 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2030                                      const char *local_abspath,
2031                                      const char *repos_relpath,
2032                                      const char *repos_root_url,
2033                                      const char *repos_uuid,
2034                                      svn_revnum_t revision,
2035                                      svn_node_kind_t kind,
2036                                      const svn_skel_t *conflict,
2037                                      const svn_skel_t *work_items,
2038                                      apr_pool_t *scratch_pool)
2039 {
2040   return add_excluded_or_not_present_node(
2041     db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2042     kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2043 }
2044 
2045 /* Recursively clear moved-here information at the copy-half of the move
2046  * which moved a node to MOVED_TO_RELPATH. This transforms this side of the
2047  * move into a simple copy.
2048  */
2049 static svn_error_t *
clear_moved_here(svn_wc__db_wcroot_t * wcroot,const char * moved_to_relpath,apr_pool_t * scratch_pool)2050 clear_moved_here(svn_wc__db_wcroot_t *wcroot,
2051                  const char *moved_to_relpath,
2052                  apr_pool_t *scratch_pool)
2053 {
2054   svn_sqlite__stmt_t *stmt;
2055   int affected_rows;
2056 
2057   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2058                                     STMT_CLEAR_MOVED_HERE_RECURSIVE));
2059   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath,
2060                             relpath_depth(moved_to_relpath)));
2061 
2062   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2063 
2064   if (affected_rows == 0)
2065      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2066                               _("The node '%s' was not found."),
2067                               path_for_error_message(wcroot, moved_to_relpath,
2068                                                      scratch_pool));
2069 
2070   return SVN_NO_ERROR;
2071 }
2072 
2073 svn_error_t *
svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t * wcroot,const char * src_relpath,int delete_op_depth,const char * dst_relpath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)2074 svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
2075                                   const char *src_relpath,
2076                                   int delete_op_depth,
2077                                   const char *dst_relpath,
2078                                   const svn_skel_t *work_items,
2079                                   apr_pool_t *scratch_pool)
2080 {
2081   svn_sqlite__stmt_t *stmt;
2082   int affected;
2083 
2084   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2085                                     STMT_CLEAR_MOVED_TO_RELPATH));
2086   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2087                             delete_op_depth));
2088   SVN_ERR(svn_sqlite__update(&affected, stmt));
2089 
2090   if (affected != 1)
2091     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2092                              _("Path '%s' is not moved"),
2093                              path_for_error_message(wcroot, src_relpath,
2094                                                     scratch_pool));
2095 
2096   SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool));
2097 
2098   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2099   return SVN_NO_ERROR;
2100 }
2101 
2102 
2103 /* The body of svn_wc__db_base_remove().
2104  */
2105 static svn_error_t *
db_base_remove(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_boolean_t keep_as_working,svn_boolean_t mark_not_present,svn_boolean_t mark_excluded,svn_revnum_t marker_revision,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)2106 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2107                const char *local_relpath,
2108                svn_wc__db_t *db, /* For checking conflicts */
2109                svn_boolean_t keep_as_working,
2110                svn_boolean_t mark_not_present,
2111                svn_boolean_t mark_excluded,
2112                svn_revnum_t marker_revision,
2113                svn_skel_t *conflict,
2114                svn_skel_t *work_items,
2115                apr_pool_t *scratch_pool)
2116 {
2117   svn_sqlite__stmt_t *stmt;
2118   svn_boolean_t have_row;
2119   svn_wc__db_status_t status;
2120   svn_revnum_t revision;
2121   apr_int64_t repos_id;
2122   const char *repos_relpath;
2123   svn_node_kind_t kind;
2124   svn_boolean_t keep_working;
2125   int op_depth;
2126   svn_node_kind_t wrk_kind;
2127   svn_boolean_t no_delete_wc = FALSE;
2128   svn_boolean_t file_external;
2129 
2130   SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
2131                                             &repos_relpath, &repos_id,
2132                                             NULL, NULL, NULL, NULL, NULL,
2133                                             NULL, NULL, NULL, NULL,
2134                                             &file_external,
2135                                             wcroot, local_relpath,
2136                                             scratch_pool, scratch_pool));
2137 
2138   /* Check if there is already a working node */
2139   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2140                                     STMT_SELECT_NODE_INFO));
2141   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2142   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2143 
2144   if (!have_row)
2145     return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
2146 
2147   op_depth = svn_sqlite__column_int(stmt, 0);
2148   wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
2149 
2150   if (op_depth > 0
2151       && op_depth == relpath_depth(local_relpath))
2152     {
2153       svn_wc__db_status_t presence;
2154       presence = svn_sqlite__column_token(stmt, 3, presence_map);
2155 
2156       if (presence == svn_wc__db_status_base_deleted)
2157         {
2158           keep_working = FALSE;
2159           no_delete_wc = TRUE;
2160         }
2161       else
2162         {
2163           keep_working = TRUE;
2164         }
2165     }
2166   else
2167     keep_working = FALSE;
2168   SVN_ERR(svn_sqlite__reset(stmt));
2169 
2170   if (keep_as_working && op_depth == 0)
2171     {
2172       if (status == svn_wc__db_status_normal
2173           || status == svn_wc__db_status_incomplete)
2174         {
2175           SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
2176                                                    NULL, NULL,
2177                                                    scratch_pool));
2178         }
2179       keep_working = TRUE;
2180     }
2181 
2182   /* Step 1: Create workqueue operations to remove files and dirs in the
2183      local-wc */
2184   if (!keep_working && !no_delete_wc)
2185     {
2186       svn_skel_t *work_item;
2187       const char *local_abspath;
2188 
2189       local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2190                                       scratch_pool);
2191       if (wrk_kind == svn_node_dir)
2192         {
2193           apr_pool_t *iterpool;
2194           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2195                                             STMT_SELECT_WORKING_PRESENT));
2196           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2197 
2198           iterpool = svn_pool_create(scratch_pool);
2199 
2200           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2201 
2202           while (have_row)
2203             {
2204               const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2205               svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2206                                                               kind_map);
2207               const char *node_abspath;
2208               svn_error_t *err;
2209 
2210               svn_pool_clear(iterpool);
2211 
2212               node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2213                                              iterpool);
2214 
2215               if (node_kind == svn_node_dir)
2216                 err = svn_wc__wq_build_dir_remove(&work_item,
2217                                                   db, wcroot->abspath,
2218                                                   node_abspath, FALSE,
2219                                                   iterpool, iterpool);
2220               else
2221                 err = svn_wc__wq_build_file_remove(&work_item,
2222                                                    db,
2223                                                    wcroot->abspath,
2224                                                    node_abspath,
2225                                                    iterpool, iterpool);
2226 
2227               if (!err)
2228                 err = add_work_items(wcroot->sdb, work_item, iterpool);
2229               if (err)
2230                 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2231 
2232               SVN_ERR(svn_sqlite__step(&have_row, stmt));
2233            }
2234 
2235           SVN_ERR(svn_sqlite__reset(stmt));
2236 
2237           SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2238                                               db, wcroot->abspath,
2239                                               local_abspath, FALSE,
2240                                               scratch_pool, iterpool));
2241           svn_pool_destroy(iterpool);
2242         }
2243       else
2244         SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2245                                              db, wcroot->abspath,
2246                                              local_abspath,
2247                                              scratch_pool, scratch_pool));
2248 
2249       SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2250     }
2251 
2252   /* Step 2: Delete ACTUAL nodes */
2253   if (! keep_working)
2254     {
2255       /* There won't be a record in NODE left for this node, so we want
2256          to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2257       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2258                                         STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2259       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2260       SVN_ERR(svn_sqlite__step_done(stmt));
2261     }
2262   else if (! keep_as_working)
2263     {
2264       /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2265       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2266                                        STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2267       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2268       SVN_ERR(svn_sqlite__step_done(stmt));
2269     }
2270   /* Else: Everything has been turned into a copy, so we want to keep all
2271            ACTUAL_NODE records */
2272 
2273   /* Step 3: Delete WORKING nodes */
2274   if (!keep_working)
2275     {
2276       apr_pool_t *iterpool;
2277 
2278       /* When deleting everything in working we should break moves from
2279          here and to here.
2280        */
2281       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2282                                         STMT_SELECT_MOVED_OUTSIDE));
2283       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2284                                              local_relpath,
2285                                              relpath_depth(local_relpath)));
2286       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2287       iterpool = svn_pool_create(scratch_pool);
2288       while (have_row)
2289         {
2290           const char *moved_to_relpath;
2291           svn_error_t *err;
2292 
2293           svn_pool_clear(iterpool);
2294           moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2295           err = clear_moved_here(wcroot, moved_to_relpath, iterpool);
2296           if (err)
2297             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2298           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2299         }
2300       svn_pool_destroy(iterpool);
2301       SVN_ERR(svn_sqlite__reset(stmt));
2302     }
2303   else
2304     {
2305       /* We are keeping things that are in WORKING, but we should still
2306          break moves of things in BASE. (Mixed revisions make it
2307          impossible to guarantee that we can keep everything moved) */
2308 
2309       apr_pool_t *iterpool;
2310 
2311       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2312                                         STMT_SELECT_MOVED_DESCENDANTS_SRC));
2313       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2314                                 local_relpath, 0));
2315       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2316       iterpool = svn_pool_create(scratch_pool);
2317       while (have_row)
2318         {
2319           int delete_op_depth = svn_sqlite__column_int(stmt, 0);
2320           const char *src_relpath;
2321           const char *dst_relpath;
2322           svn_error_t *err;
2323 
2324           svn_pool_clear(iterpool);
2325 
2326           src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2327           dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
2328 
2329           err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
2330                                                   delete_op_depth,
2331                                                   dst_relpath,
2332                                                   NULL,
2333                                                   iterpool);
2334 
2335           if (err)
2336             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2337 
2338           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2339         }
2340       svn_pool_destroy(iterpool);
2341       SVN_ERR(svn_sqlite__reset(stmt));
2342     }
2343   if (keep_working)
2344     {
2345       SVN_ERR(svn_sqlite__get_statement(
2346                     &stmt, wcroot->sdb,
2347                     STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
2348       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2349       SVN_ERR(svn_sqlite__step_done(stmt));
2350     }
2351   else
2352     {
2353       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2354                                         STMT_DELETE_WORKING_RECURSIVE));
2355       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2356       SVN_ERR(svn_sqlite__step_done(stmt));
2357     }
2358 
2359   /* Step 4: Delete the BASE node descendants */
2360   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2361                                     STMT_DELETE_BASE_RECURSIVE));
2362   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2363   SVN_ERR(svn_sqlite__step_done(stmt));
2364 
2365   SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
2366 
2367   if (mark_not_present || mark_excluded)
2368     {
2369       struct insert_base_baton_t ibb;
2370       svn_boolean_t no_marker = FALSE;
2371 
2372       if (file_external)
2373         {
2374           const char *parent_local_relpath;
2375           const char *name;
2376           svn_error_t *err;
2377 
2378           /* For file externals we only want to place a not present marker
2379              if there is a BASE parent */
2380 
2381           svn_relpath_split(&parent_local_relpath, &name, local_relpath,
2382                             scratch_pool);
2383 
2384           err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
2385                                                   &repos_relpath, &repos_id,
2386                                                   NULL, NULL, NULL, NULL, NULL,
2387                                                   NULL, NULL, NULL, NULL, NULL,
2388                                                   wcroot, parent_local_relpath,
2389                                                   scratch_pool, scratch_pool);
2390 
2391           if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2392             return svn_error_trace(err);
2393           else if (err)
2394             {
2395               svn_error_clear(err);
2396               no_marker = TRUE;
2397             }
2398           else
2399             {
2400               /* Replace the repos_relpath with something more expected than
2401                  the unrelated old file external repository relpath, which
2402                  one day may come from a different repository */
2403               repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool);
2404             }
2405         }
2406 
2407       if (!no_marker)
2408         {
2409           blank_ibb(&ibb);
2410 
2411           ibb.repos_id = repos_id;
2412           ibb.status = mark_excluded ? svn_wc__db_status_excluded
2413                                      : svn_wc__db_status_not_present;
2414           ibb.kind = kind;
2415           ibb.repos_relpath = repos_relpath;
2416           ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
2417                             ? marker_revision
2418                             : revision;
2419 
2420           /* Depending upon KIND, any of these might get used. */
2421           ibb.children = NULL;
2422           ibb.depth = svn_depth_unknown;
2423           ibb.checksum = NULL;
2424           ibb.target = NULL;
2425 
2426           SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2427         }
2428     }
2429 
2430   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2431   if (conflict)
2432     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2433                                               conflict, scratch_pool));
2434 
2435   return SVN_NO_ERROR;
2436 }
2437 
2438 
2439 svn_error_t *
svn_wc__db_base_remove(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t keep_as_working,svn_boolean_t mark_not_present,svn_boolean_t mark_excluded,svn_revnum_t marker_revision,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)2440 svn_wc__db_base_remove(svn_wc__db_t *db,
2441                        const char *local_abspath,
2442                        svn_boolean_t keep_as_working,
2443                        svn_boolean_t mark_not_present,
2444                        svn_boolean_t mark_excluded,
2445                        svn_revnum_t marker_revision,
2446                        svn_skel_t *conflict,
2447                        svn_skel_t *work_items,
2448                        apr_pool_t *scratch_pool)
2449 {
2450   svn_wc__db_wcroot_t *wcroot;
2451   const char *local_relpath;
2452 
2453   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2454 
2455   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2456                               local_abspath, scratch_pool, scratch_pool));
2457   VERIFY_USABLE_WCROOT(wcroot);
2458 
2459   SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2460                                      db, keep_as_working,
2461                                      mark_not_present, mark_excluded,
2462                                      marker_revision,
2463                                      conflict, work_items, scratch_pool),
2464                       wcroot);
2465 
2466   /* If this used to be a directory we should remove children so pass
2467    * depth infinity. */
2468   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2469                         scratch_pool));
2470 
2471   return SVN_NO_ERROR;
2472 }
2473 
2474 
2475 svn_error_t *
svn_wc__db_base_get_info_internal(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_wc__db_lock_t ** lock,svn_boolean_t * had_props,apr_hash_t ** props,svn_boolean_t * update_root,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2476 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2477                                   svn_node_kind_t *kind,
2478                                   svn_revnum_t *revision,
2479                                   const char **repos_relpath,
2480                                   apr_int64_t *repos_id,
2481                                   svn_revnum_t *changed_rev,
2482                                   apr_time_t *changed_date,
2483                                   const char **changed_author,
2484                                   svn_depth_t *depth,
2485                                   const svn_checksum_t **checksum,
2486                                   const char **target,
2487                                   svn_wc__db_lock_t **lock,
2488                                   svn_boolean_t *had_props,
2489                                   apr_hash_t **props,
2490                                   svn_boolean_t *update_root,
2491                                   svn_wc__db_wcroot_t *wcroot,
2492                                   const char *local_relpath,
2493                                   apr_pool_t *result_pool,
2494                                   apr_pool_t *scratch_pool)
2495 {
2496   svn_sqlite__stmt_t *stmt;
2497   svn_boolean_t have_row;
2498   svn_error_t *err = SVN_NO_ERROR;
2499 
2500   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2501                                     lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2502                                          : STMT_SELECT_BASE_NODE));
2503   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2504   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2505 
2506   if (have_row)
2507     {
2508       svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2509                                                                  presence_map);
2510       svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2511 
2512       if (kind)
2513         {
2514           *kind = node_kind;
2515         }
2516       if (status)
2517         {
2518           *status = node_status;
2519         }
2520       repos_location_from_columns(repos_id, revision, repos_relpath,
2521                                   stmt, 0, 4, 1, result_pool);
2522       SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2523       SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2524       if (lock)
2525         {
2526           *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2527         }
2528       if (changed_rev)
2529         {
2530           *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2531         }
2532       if (changed_date)
2533         {
2534           *changed_date = svn_sqlite__column_int64(stmt, 8);
2535         }
2536       if (changed_author)
2537         {
2538           /* Result may be NULL. */
2539           *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2540         }
2541       if (depth)
2542         {
2543           if (node_kind != svn_node_dir)
2544             {
2545               *depth = svn_depth_unknown;
2546             }
2547           else
2548             {
2549               *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2550                                                      svn_depth_unknown);
2551             }
2552         }
2553       if (checksum)
2554         {
2555           if (node_kind != svn_node_file)
2556             {
2557               *checksum = NULL;
2558             }
2559           else
2560             {
2561               err = svn_sqlite__column_checksum(checksum, stmt, 5,
2562                                                 result_pool);
2563               if (err != NULL)
2564                 err = svn_error_createf(
2565                         err->apr_err, err,
2566                         _("The node '%s' has a corrupt checksum value."),
2567                         path_for_error_message(wcroot, local_relpath,
2568                                                scratch_pool));
2569             }
2570         }
2571       if (target)
2572         {
2573           if (node_kind != svn_node_symlink)
2574             *target = NULL;
2575           else
2576             *target = svn_sqlite__column_text(stmt, 11, result_pool);
2577         }
2578       if (had_props)
2579         {
2580           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2581         }
2582       if (props)
2583         {
2584           if (node_status == svn_wc__db_status_normal
2585               || node_status == svn_wc__db_status_incomplete)
2586             {
2587               SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2588                                                     result_pool, scratch_pool));
2589               if (*props == NULL)
2590                 *props = apr_hash_make(result_pool);
2591             }
2592           else
2593             {
2594               assert(svn_sqlite__column_is_null(stmt, 13));
2595               *props = NULL;
2596             }
2597         }
2598       if (update_root)
2599         {
2600           /* It's an update root iff it's a file external. */
2601           *update_root = svn_sqlite__column_boolean(stmt, 14);
2602         }
2603     }
2604   else
2605     {
2606       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2607                               _("The node '%s' was not found."),
2608                               path_for_error_message(wcroot, local_relpath,
2609                                                      scratch_pool));
2610     }
2611 
2612   /* Note: given the composition, no need to wrap for tracing.  */
2613   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2614 }
2615 
2616 
2617 svn_error_t *
svn_wc__db_base_get_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_wc__db_lock_t ** lock,svn_boolean_t * had_props,apr_hash_t ** props,svn_boolean_t * update_root,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2618 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2619                          svn_node_kind_t *kind,
2620                          svn_revnum_t *revision,
2621                          const char **repos_relpath,
2622                          const char **repos_root_url,
2623                          const char **repos_uuid,
2624                          svn_revnum_t *changed_rev,
2625                          apr_time_t *changed_date,
2626                          const char **changed_author,
2627                          svn_depth_t *depth,
2628                          const svn_checksum_t **checksum,
2629                          const char **target,
2630                          svn_wc__db_lock_t **lock,
2631                          svn_boolean_t *had_props,
2632                          apr_hash_t **props,
2633                          svn_boolean_t *update_root,
2634                          svn_wc__db_t *db,
2635                          const char *local_abspath,
2636                          apr_pool_t *result_pool,
2637                          apr_pool_t *scratch_pool)
2638 {
2639   svn_wc__db_wcroot_t *wcroot;
2640   const char *local_relpath;
2641   apr_int64_t repos_id;
2642 
2643   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2644 
2645   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2646                               local_abspath, scratch_pool, scratch_pool));
2647   VERIFY_USABLE_WCROOT(wcroot);
2648 
2649   SVN_WC__DB_WITH_TXN4(
2650           svn_wc__db_base_get_info_internal(status, kind, revision,
2651                                             repos_relpath, &repos_id,
2652                                             changed_rev, changed_date,
2653                                             changed_author, depth,
2654                                             checksum, target, lock,
2655                                             had_props, props, update_root,
2656                                             wcroot, local_relpath,
2657                                             result_pool, scratch_pool),
2658           svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2659                                       wcroot, repos_id, result_pool),
2660           SVN_NO_ERROR,
2661           SVN_NO_ERROR,
2662           wcroot);
2663   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2664 
2665   return SVN_NO_ERROR;
2666 }
2667 
2668 /* The implementation of svn_wc__db_base_get_children_info */
2669 static svn_error_t *
base_get_children_info(apr_hash_t ** nodes,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t obtain_locks,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2670 base_get_children_info(apr_hash_t **nodes,
2671                        svn_wc__db_wcroot_t *wcroot,
2672                        const char *local_relpath,
2673                        svn_boolean_t obtain_locks,
2674                        apr_pool_t *result_pool,
2675                        apr_pool_t *scratch_pool)
2676 {
2677   svn_sqlite__stmt_t *stmt;
2678   svn_boolean_t have_row;
2679   apr_int64_t last_repos_id = INVALID_REPOS_ID;
2680   const char *last_repos_root_url = NULL;
2681 
2682   *nodes = apr_hash_make(result_pool);
2683 
2684   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2685                                     obtain_locks
2686                                       ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK
2687                                       : STMT_SELECT_BASE_CHILDREN_INFO));
2688   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2689 
2690   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2691 
2692   while (have_row)
2693     {
2694       struct svn_wc__db_base_info_t *info;
2695       apr_int64_t repos_id;
2696       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2697       const char *name = svn_relpath_basename(child_relpath, result_pool);
2698 
2699       info = apr_pcalloc(result_pool, sizeof(*info));
2700 
2701       repos_id = svn_sqlite__column_int64(stmt, 1);
2702       info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2703       info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2704       info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2705       info->revnum = svn_sqlite__column_revnum(stmt, 5);
2706 
2707       info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2708                                                   svn_depth_unknown);
2709 
2710       info->update_root = svn_sqlite__column_boolean(stmt, 7);
2711 
2712       if (obtain_locks)
2713         info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2714 
2715       if (repos_id != last_repos_id)
2716         {
2717           svn_error_t *err;
2718 
2719           err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
2720                                             wcroot, repos_id,
2721                                             result_pool);
2722 
2723           if (err)
2724             return svn_error_trace(
2725                      svn_error_compose_create(err,
2726                                               svn_sqlite__reset(stmt)));
2727 
2728           last_repos_id = repos_id;
2729         }
2730 
2731       info->repos_root_url = last_repos_root_url;
2732 
2733       svn_hash_sets(*nodes, name, info);
2734 
2735       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2736     }
2737 
2738   SVN_ERR(svn_sqlite__reset(stmt));
2739 
2740   return SVN_NO_ERROR;
2741 }
2742 
2743 svn_error_t *
svn_wc__db_base_get_children_info(apr_hash_t ** nodes,svn_wc__db_t * db,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2744 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2745                                   svn_wc__db_t *db,
2746                                   const char *dir_abspath,
2747                                   apr_pool_t *result_pool,
2748                                   apr_pool_t *scratch_pool)
2749 {
2750   svn_wc__db_wcroot_t *wcroot;
2751   const char *local_relpath;
2752 
2753   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2754 
2755   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2756                               dir_abspath, scratch_pool, scratch_pool));
2757   VERIFY_USABLE_WCROOT(wcroot);
2758 
2759   return svn_error_trace(base_get_children_info(nodes,
2760                                                 wcroot,
2761                                                 local_relpath,
2762                                                 TRUE /* obtain_locks */,
2763                                                 result_pool,
2764                                                 scratch_pool));
2765 }
2766 
2767 
2768 svn_error_t *
svn_wc__db_base_get_props(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2769 svn_wc__db_base_get_props(apr_hash_t **props,
2770                           svn_wc__db_t *db,
2771                           const char *local_abspath,
2772                           apr_pool_t *result_pool,
2773                           apr_pool_t *scratch_pool)
2774 {
2775   svn_wc__db_status_t presence;
2776 
2777   SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2778                                    NULL, NULL, NULL, NULL, NULL,
2779                                    NULL, NULL, NULL, NULL, props, NULL,
2780                                    db, local_abspath,
2781                                    result_pool, scratch_pool));
2782   if (presence != svn_wc__db_status_normal
2783       && presence != svn_wc__db_status_incomplete)
2784     {
2785       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2786                                _("The node '%s' has a BASE status that"
2787                                   " has no properties."),
2788                                svn_dirent_local_style(local_abspath,
2789                                                       scratch_pool));
2790     }
2791 
2792   return SVN_NO_ERROR;
2793 }
2794 
2795 
2796 svn_error_t *
svn_wc__db_base_get_children(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2797 svn_wc__db_base_get_children(const apr_array_header_t **children,
2798                              svn_wc__db_t *db,
2799                              const char *local_abspath,
2800                              apr_pool_t *result_pool,
2801                              apr_pool_t *scratch_pool)
2802 {
2803   svn_wc__db_wcroot_t *wcroot;
2804   const char *local_relpath;
2805 
2806   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2807 
2808   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2809                                              local_abspath,
2810                                              scratch_pool, scratch_pool));
2811   VERIFY_USABLE_WCROOT(wcroot);
2812 
2813   return svn_error_trace(
2814               gather_children(children, wcroot, local_relpath,
2815                               STMT_SELECT_OP_DEPTH_CHILDREN, 0,
2816                               result_pool, scratch_pool));
2817 }
2818 
2819 
2820 svn_error_t *
svn_wc__db_base_set_dav_cache(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,apr_pool_t * scratch_pool)2821 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2822                               const char *local_abspath,
2823                               const apr_hash_t *props,
2824                               apr_pool_t *scratch_pool)
2825 {
2826   svn_wc__db_wcroot_t *wcroot;
2827   const char *local_relpath;
2828   svn_sqlite__stmt_t *stmt;
2829   int affected_rows;
2830 
2831   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2832 
2833   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2834                               local_abspath, scratch_pool, scratch_pool));
2835   VERIFY_USABLE_WCROOT(wcroot);
2836 
2837   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2838                                     STMT_UPDATE_BASE_NODE_DAV_CACHE));
2839   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2840   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2841 
2842   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2843 
2844   if (affected_rows != 1)
2845     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2846                              _("The node '%s' was not found."),
2847                              svn_dirent_local_style(local_abspath,
2848                                                     scratch_pool));
2849 
2850   return SVN_NO_ERROR;
2851 }
2852 
2853 
2854 svn_error_t *
svn_wc__db_base_get_dav_cache(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2855 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2856                               svn_wc__db_t *db,
2857                               const char *local_abspath,
2858                               apr_pool_t *result_pool,
2859                               apr_pool_t *scratch_pool)
2860 {
2861   svn_wc__db_wcroot_t *wcroot;
2862   const char *local_relpath;
2863   svn_sqlite__stmt_t *stmt;
2864   svn_boolean_t have_row;
2865 
2866   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2867 
2868   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2869                               local_abspath, scratch_pool, scratch_pool));
2870   VERIFY_USABLE_WCROOT(wcroot);
2871 
2872   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2873                                     STMT_SELECT_BASE_DAV_CACHE));
2874 
2875   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2876   if (!have_row)
2877     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2878                              svn_sqlite__reset(stmt),
2879                              _("The node '%s' was not found."),
2880                              svn_dirent_local_style(local_abspath,
2881                                                     scratch_pool));
2882 
2883   SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2884                                         scratch_pool));
2885   return svn_error_trace(svn_sqlite__reset(stmt));
2886 }
2887 
2888 
2889 svn_error_t *
svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)2890 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2891                                           const char *local_abspath,
2892                                           apr_pool_t *scratch_pool)
2893 {
2894   svn_wc__db_wcroot_t *wcroot;
2895   const char *local_relpath;
2896   svn_sqlite__stmt_t *stmt;
2897 
2898   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2899                                              db, local_abspath,
2900                                              scratch_pool, scratch_pool));
2901   VERIFY_USABLE_WCROOT(wcroot);
2902 
2903   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2904                                     STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2905   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2906 
2907   SVN_ERR(svn_sqlite__step_done(stmt));
2908 
2909   return SVN_NO_ERROR;
2910 }
2911 
2912 
2913 svn_error_t *
svn_wc__db_depth_get_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_boolean_t * had_props,apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2914 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2915                           svn_node_kind_t *kind,
2916                           svn_revnum_t *revision,
2917                           const char **repos_relpath,
2918                           apr_int64_t *repos_id,
2919                           svn_revnum_t *changed_rev,
2920                           apr_time_t *changed_date,
2921                           const char **changed_author,
2922                           svn_depth_t *depth,
2923                           const svn_checksum_t **checksum,
2924                           const char **target,
2925                           svn_boolean_t *had_props,
2926                           apr_hash_t **props,
2927                           svn_wc__db_wcroot_t *wcroot,
2928                           const char *local_relpath,
2929                           int op_depth,
2930                           apr_pool_t *result_pool,
2931                           apr_pool_t *scratch_pool)
2932 {
2933   svn_sqlite__stmt_t *stmt;
2934   svn_boolean_t have_row;
2935   svn_error_t *err = SVN_NO_ERROR;
2936 
2937   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2938                                     STMT_SELECT_DEPTH_NODE));
2939   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2940                             wcroot->wc_id, local_relpath, op_depth));
2941   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2942 
2943   if (have_row)
2944     {
2945       svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2946                                                                  presence_map);
2947       svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2948 
2949       if (kind)
2950         {
2951           *kind = node_kind;
2952         }
2953       if (status)
2954         {
2955           *status = node_status;
2956 
2957           if (op_depth > 0)
2958             SVN_ERR(convert_to_working_status(status, *status));
2959         }
2960       repos_location_from_columns(repos_id, revision, repos_relpath,
2961                                   stmt, 0, 4, 1, result_pool);
2962 
2963       if (changed_rev)
2964         {
2965           *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2966         }
2967       if (changed_date)
2968         {
2969           *changed_date = svn_sqlite__column_int64(stmt, 8);
2970         }
2971       if (changed_author)
2972         {
2973           /* Result may be NULL. */
2974           *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2975         }
2976       if (depth)
2977         {
2978           if (node_kind != svn_node_dir)
2979             {
2980               *depth = svn_depth_unknown;
2981             }
2982           else
2983             {
2984               *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2985                                                      svn_depth_unknown);
2986             }
2987         }
2988       if (checksum)
2989         {
2990           if (node_kind != svn_node_file)
2991             {
2992               *checksum = NULL;
2993             }
2994           else
2995             {
2996               err = svn_sqlite__column_checksum(checksum, stmt, 5,
2997                                                 result_pool);
2998               if (err != NULL)
2999                 err = svn_error_createf(
3000                         err->apr_err, err,
3001                         _("The node '%s' has a corrupt checksum value."),
3002                         path_for_error_message(wcroot, local_relpath,
3003                                                scratch_pool));
3004             }
3005         }
3006       if (target)
3007         {
3008           if (node_kind != svn_node_symlink)
3009             *target = NULL;
3010           else
3011             *target = svn_sqlite__column_text(stmt, 11, result_pool);
3012         }
3013       if (had_props)
3014         {
3015           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12);
3016         }
3017       if (props)
3018         {
3019           if (node_status == svn_wc__db_status_normal
3020               || node_status == svn_wc__db_status_incomplete)
3021             {
3022               SVN_ERR(svn_sqlite__column_properties(props, stmt, 12,
3023                                                     result_pool, scratch_pool));
3024               if (*props == NULL)
3025                 *props = apr_hash_make(result_pool);
3026             }
3027           else
3028             {
3029               assert(svn_sqlite__column_is_null(stmt, 12));
3030               *props = NULL;
3031             }
3032         }
3033     }
3034   else
3035     {
3036       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3037                               _("The node '%s' was not found."),
3038                               path_for_error_message(wcroot, local_relpath,
3039                                                      scratch_pool));
3040     }
3041 
3042   /* Note: given the composition, no need to wrap for tracing.  */
3043   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
3044 }
3045 
3046 /* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
3047 typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
3048                                           svn_wc__db_wcroot_t *wcroot,
3049                                           const char *local_relpath,
3050                                           apr_pool_t *scratch_pool);
3051 
3052 /* Baton for passing args to with_triggers(). */
3053 struct with_triggers_baton_t {
3054   int create_trigger;
3055   int drop_trigger;
3056   svn_wc__db_txn_callback_t cb_func;
3057   void *cb_baton;
3058 };
3059 
3060 /* Helper for creating SQLite triggers, running the main transaction
3061    callback, and then dropping the triggers.  It guarantees that the
3062    triggers will not survive the transaction.  This could be used for
3063    any general prefix/postscript statements where the postscript
3064    *must* be executed if the transaction completes.
3065 
3066    Implements svn_wc__db_txn_callback_t. */
3067 static svn_error_t *
with_triggers(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)3068 with_triggers(void *baton,
3069               svn_wc__db_wcroot_t *wcroot,
3070               const char *local_relpath,
3071               apr_pool_t *scratch_pool)
3072 {
3073   struct with_triggers_baton_t *b = baton;
3074   svn_error_t *err1;
3075   svn_error_t *err2;
3076 
3077   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3078 
3079   err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3080 
3081   err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3082 
3083   return svn_error_trace(svn_error_compose_create(err1, err2));
3084 }
3085 
3086 
3087 /* Prototype for the "work callback" used by with_finalization().  */
3088 typedef svn_error_t * (*work_callback_t)(
3089                           void *baton,
3090                           svn_wc__db_wcroot_t *wcroot,
3091                           svn_cancel_func_t cancel_func,
3092                           void *cancel_baton,
3093                           svn_wc_notify_func2_t notify_func,
3094                           void *notify_baton,
3095                           apr_pool_t *scratch_pool);
3096 
3097 /* Utility function to provide several features, with a guaranteed
3098    finalization (ie. to drop temporary tables).
3099 
3100    1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3101       sqlite transaction
3102    2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3103       the "work" step: WORK_CB(WORK_BATON).
3104    3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3105       from the above two steps.
3106 
3107    CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3108    typical values. These are passed to the work callback, which typically
3109    provides notification about the work done by TXN_CB.  */
3110 static svn_error_t *
with_finalization(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_txn_callback_t txn_cb,void * txn_baton,work_callback_t work_cb,void * work_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,int finalize_stmt_idx,apr_pool_t * scratch_pool)3111 with_finalization(svn_wc__db_wcroot_t *wcroot,
3112                   const char *local_relpath,
3113                   svn_wc__db_txn_callback_t txn_cb,
3114                   void *txn_baton,
3115                   work_callback_t work_cb,
3116                   void *work_baton,
3117                   svn_cancel_func_t cancel_func,
3118                   void *cancel_baton,
3119                   svn_wc_notify_func2_t notify_func,
3120                   void *notify_baton,
3121                   int finalize_stmt_idx,
3122                   apr_pool_t *scratch_pool)
3123 {
3124   svn_error_t *err1;
3125   svn_error_t *err2;
3126 
3127   err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
3128   if (!err1)
3129     {
3130       err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
3131 
3132       err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
3133     }
3134 
3135   if (err1 == NULL && notify_func != NULL)
3136     {
3137       err2 = work_cb(work_baton, wcroot,
3138                      cancel_func, cancel_baton,
3139                      notify_func, notify_baton,
3140                      scratch_pool);
3141       err1 = svn_error_compose_create(err1, err2);
3142     }
3143 
3144   err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3145 
3146   return svn_error_trace(svn_error_compose_create(err1, err2));
3147 }
3148 
3149 
3150 /* Initialize the baton with appropriate "blank" values. This allows the
3151    insertion function to leave certain columns null.  */
3152 static void
blank_ieb(insert_external_baton_t * ieb)3153 blank_ieb(insert_external_baton_t *ieb)
3154 {
3155   memset(ieb, 0, sizeof(*ieb));
3156   ieb->revision = SVN_INVALID_REVNUM;
3157   ieb->changed_rev = SVN_INVALID_REVNUM;
3158   ieb->repos_id = INVALID_REPOS_ID;
3159 
3160   ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3161   ieb->recorded_revision = SVN_INVALID_REVNUM;
3162 }
3163 
3164 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3165  *
3166  * Implements svn_wc__db_txn_callback_t. */
3167 static svn_error_t *
insert_external_node(const insert_external_baton_t * ieb,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)3168 insert_external_node(const insert_external_baton_t *ieb,
3169                      svn_wc__db_wcroot_t *wcroot,
3170                      const char *local_relpath,
3171                      apr_pool_t *scratch_pool)
3172 {
3173   svn_wc__db_status_t status;
3174   svn_error_t *err;
3175   svn_boolean_t update_root;
3176   apr_int64_t repos_id;
3177   svn_sqlite__stmt_t *stmt;
3178 
3179   if (ieb->repos_id != INVALID_REPOS_ID)
3180     repos_id = ieb->repos_id;
3181   else
3182     SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3183                             wcroot->sdb, scratch_pool));
3184 
3185   /* And there must be no existing BASE node or it must be a file external */
3186   err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3187                                           NULL, NULL, NULL, NULL, NULL,
3188                                           NULL, NULL, NULL, NULL, &update_root,
3189                                           wcroot, local_relpath,
3190                                           scratch_pool, scratch_pool);
3191   if (err)
3192     {
3193       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3194         return svn_error_trace(err);
3195 
3196       svn_error_clear(err);
3197     }
3198   else if (status == svn_wc__db_status_normal && !update_root)
3199     return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3200 
3201   if (ieb->kind == svn_node_file
3202       || ieb->kind == svn_node_symlink)
3203     {
3204       struct insert_base_baton_t ibb;
3205 
3206       blank_ibb(&ibb);
3207 
3208       ibb.status          = svn_wc__db_status_normal;
3209       ibb.kind            = ieb->kind;
3210 
3211       ibb.repos_id        = repos_id;
3212       ibb.repos_relpath   = ieb->repos_relpath;
3213       ibb.revision        = ieb->revision;
3214 
3215       ibb.props           = ieb->props;
3216       ibb.iprops          = ieb->iprops;
3217       ibb.changed_rev     = ieb->changed_rev;
3218       ibb.changed_date    = ieb->changed_date;
3219       ibb.changed_author  = ieb->changed_author;
3220 
3221       ibb.dav_cache       = ieb->dav_cache;
3222 
3223       ibb.checksum        = ieb->checksum;
3224       ibb.target          = ieb->target;
3225 
3226       ibb.conflict        = ieb->conflict;
3227 
3228       ibb.update_actual_props = ieb->update_actual_props;
3229       ibb.new_actual_props    = ieb->new_actual_props;
3230 
3231       ibb.keep_recorded_info  = ieb->keep_recorded_info;
3232 
3233       ibb.work_items      = ieb->work_items;
3234 
3235       ibb.file_external = TRUE;
3236 
3237       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3238     }
3239   else
3240     SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3241 
3242   /* The externals table only support presence normal and excluded */
3243   SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3244                  || ieb->presence == svn_wc__db_status_excluded);
3245 
3246   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3247 
3248   SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3249                             wcroot->wc_id,
3250                             local_relpath,
3251                             svn_relpath_dirname(local_relpath,
3252                                                 scratch_pool),
3253                             presence_map, ieb->presence,
3254                             kind_map, ieb->kind,
3255                             ieb->record_ancestor_relpath,
3256                             repos_id,
3257                             ieb->recorded_repos_relpath));
3258 
3259   if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3260     SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3261 
3262   if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3263     SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3264 
3265   SVN_ERR(svn_sqlite__insert(NULL, stmt));
3266 
3267   return SVN_NO_ERROR;
3268 }
3269 
3270 svn_error_t *
svn_wc__db_external_add_file(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,apr_array_header_t * iprops,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * checksum,const apr_hash_t * dav_cache,const char * record_ancestor_abspath,const char * recorded_repos_relpath,svn_revnum_t recorded_peg_revision,svn_revnum_t recorded_revision,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,svn_boolean_t keep_recorded_info,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3271 svn_wc__db_external_add_file(svn_wc__db_t *db,
3272                              const char *local_abspath,
3273                              const char *wri_abspath,
3274 
3275                              const char *repos_relpath,
3276                              const char *repos_root_url,
3277                              const char *repos_uuid,
3278                              svn_revnum_t revision,
3279 
3280                              const apr_hash_t *props,
3281                              apr_array_header_t *iprops,
3282 
3283                              svn_revnum_t changed_rev,
3284                              apr_time_t changed_date,
3285                              const char *changed_author,
3286 
3287                              const svn_checksum_t *checksum,
3288 
3289                              const apr_hash_t *dav_cache,
3290 
3291                              const char *record_ancestor_abspath,
3292                              const char *recorded_repos_relpath,
3293                              svn_revnum_t recorded_peg_revision,
3294                              svn_revnum_t recorded_revision,
3295 
3296                              svn_boolean_t update_actual_props,
3297                              apr_hash_t *new_actual_props,
3298 
3299                              svn_boolean_t keep_recorded_info,
3300                              const svn_skel_t *conflict,
3301                              const svn_skel_t *work_items,
3302                              apr_pool_t *scratch_pool)
3303 {
3304   svn_wc__db_wcroot_t *wcroot;
3305   const char *local_relpath;
3306   insert_external_baton_t ieb;
3307 
3308   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3309 
3310   if (! wri_abspath)
3311     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3312 
3313   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3314                               wri_abspath, scratch_pool, scratch_pool));
3315   VERIFY_USABLE_WCROOT(wcroot);
3316 
3317   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3318                                         record_ancestor_abspath));
3319 
3320   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3321 
3322   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3323 
3324   blank_ieb(&ieb);
3325 
3326   ieb.kind = svn_node_file;
3327   ieb.presence = svn_wc__db_status_normal;
3328 
3329   ieb.repos_root_url = repos_root_url;
3330   ieb.repos_uuid = repos_uuid;
3331 
3332   ieb.repos_relpath = repos_relpath;
3333   ieb.revision = revision;
3334 
3335   ieb.props = props;
3336   ieb.iprops = iprops;
3337 
3338   ieb.changed_rev = changed_rev;
3339   ieb.changed_date = changed_date;
3340   ieb.changed_author = changed_author;
3341 
3342   ieb.checksum = checksum;
3343 
3344   ieb.dav_cache = dav_cache;
3345 
3346   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3347                                                 wcroot->abspath,
3348                                                 record_ancestor_abspath);
3349   ieb.recorded_repos_relpath = recorded_repos_relpath;
3350   ieb.recorded_peg_revision = recorded_peg_revision;
3351   ieb.recorded_revision = recorded_revision;
3352 
3353   ieb.update_actual_props = update_actual_props;
3354   ieb.new_actual_props = new_actual_props;
3355 
3356   ieb.keep_recorded_info = keep_recorded_info;
3357 
3358   ieb.conflict = conflict;
3359   ieb.work_items = work_items;
3360 
3361   SVN_WC__DB_WITH_TXN(
3362             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3363             wcroot);
3364 
3365   return SVN_NO_ERROR;
3366 }
3367 
3368 svn_error_t *
svn_wc__db_external_add_symlink(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * target,const apr_hash_t * dav_cache,const char * record_ancestor_abspath,const char * recorded_repos_relpath,svn_revnum_t recorded_peg_revision,svn_revnum_t recorded_revision,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,svn_boolean_t keep_recorded_info,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3369 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3370                                 const char *local_abspath,
3371                                 const char *wri_abspath,
3372                                 const char *repos_relpath,
3373                                 const char *repos_root_url,
3374                                 const char *repos_uuid,
3375                                 svn_revnum_t revision,
3376                                 const apr_hash_t *props,
3377                                 svn_revnum_t changed_rev,
3378                                 apr_time_t changed_date,
3379                                 const char *changed_author,
3380                                 const char *target,
3381                                 const apr_hash_t *dav_cache,
3382                                 const char *record_ancestor_abspath,
3383                                 const char *recorded_repos_relpath,
3384                                 svn_revnum_t recorded_peg_revision,
3385                                 svn_revnum_t recorded_revision,
3386                                 svn_boolean_t update_actual_props,
3387                                 apr_hash_t *new_actual_props,
3388                                 svn_boolean_t keep_recorded_info,
3389                                 const svn_skel_t *work_items,
3390                                 apr_pool_t *scratch_pool)
3391 {
3392   svn_wc__db_wcroot_t *wcroot;
3393   const char *local_relpath;
3394   insert_external_baton_t ieb;
3395 
3396   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3397 
3398   if (! wri_abspath)
3399     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3400 
3401   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3402                               wri_abspath, scratch_pool, scratch_pool));
3403   VERIFY_USABLE_WCROOT(wcroot);
3404 
3405   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3406                                         record_ancestor_abspath));
3407 
3408   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3409 
3410   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3411 
3412   blank_ieb(&ieb);
3413 
3414   ieb.kind = svn_node_symlink;
3415   ieb.presence = svn_wc__db_status_normal;
3416 
3417   ieb.repos_root_url = repos_root_url;
3418   ieb.repos_uuid = repos_uuid;
3419 
3420   ieb.repos_relpath = repos_relpath;
3421   ieb.revision = revision;
3422 
3423   ieb.props = props;
3424 
3425   ieb.changed_rev = changed_rev;
3426   ieb.changed_date = changed_date;
3427   ieb.changed_author = changed_author;
3428 
3429   ieb.target = target;
3430 
3431   ieb.dav_cache = dav_cache;
3432 
3433   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3434                                                 wcroot->abspath,
3435                                                 record_ancestor_abspath);
3436   ieb.recorded_repos_relpath = recorded_repos_relpath;
3437   ieb.recorded_peg_revision = recorded_peg_revision;
3438   ieb.recorded_revision = recorded_revision;
3439 
3440   ieb.update_actual_props = update_actual_props;
3441   ieb.new_actual_props = new_actual_props;
3442 
3443   ieb.keep_recorded_info = keep_recorded_info;
3444 
3445   ieb.work_items = work_items;
3446 
3447   SVN_WC__DB_WITH_TXN(
3448             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3449             wcroot);
3450 
3451   return SVN_NO_ERROR;
3452 }
3453 
3454 svn_error_t *
svn_wc__db_external_add_dir(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_root_url,const char * repos_uuid,const char * record_ancestor_abspath,const char * recorded_repos_relpath,svn_revnum_t recorded_peg_revision,svn_revnum_t recorded_revision,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3455 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3456                             const char *local_abspath,
3457                             const char *wri_abspath,
3458                             const char *repos_root_url,
3459                             const char *repos_uuid,
3460                             const char *record_ancestor_abspath,
3461                             const char *recorded_repos_relpath,
3462                             svn_revnum_t recorded_peg_revision,
3463                             svn_revnum_t recorded_revision,
3464                             const svn_skel_t *work_items,
3465                             apr_pool_t *scratch_pool)
3466 {
3467   svn_wc__db_wcroot_t *wcroot;
3468   const char *local_relpath;
3469   insert_external_baton_t ieb;
3470 
3471   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3472 
3473   if (! wri_abspath)
3474     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3475 
3476   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3477                               wri_abspath, scratch_pool, scratch_pool));
3478   VERIFY_USABLE_WCROOT(wcroot);
3479 
3480   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3481                                         record_ancestor_abspath));
3482 
3483   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3484 
3485   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3486 
3487   blank_ieb(&ieb);
3488 
3489   ieb.kind = svn_node_dir;
3490   ieb.presence = svn_wc__db_status_normal;
3491 
3492   ieb.repos_root_url = repos_root_url;
3493   ieb.repos_uuid = repos_uuid;
3494 
3495   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3496                                                 wcroot->abspath,
3497                                                 record_ancestor_abspath);
3498   ieb.recorded_repos_relpath = recorded_repos_relpath;
3499   ieb.recorded_peg_revision = recorded_peg_revision;
3500   ieb.recorded_revision = recorded_revision;
3501 
3502   ieb.work_items = work_items;
3503 
3504   SVN_WC__DB_WITH_TXN(
3505             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3506             wcroot);
3507 
3508   return SVN_NO_ERROR;
3509 }
3510 
3511 /* The body of svn_wc__db_external_remove(). */
3512 static svn_error_t *
db_external_remove(const svn_skel_t * work_items,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)3513 db_external_remove(const svn_skel_t *work_items,
3514                    svn_wc__db_wcroot_t *wcroot,
3515                    const char *local_relpath,
3516                    apr_pool_t *scratch_pool)
3517 {
3518   svn_sqlite__stmt_t *stmt;
3519   int affected_rows;
3520 
3521   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3522                                     STMT_DELETE_EXTERNAL));
3523   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3524   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
3525 
3526   if (!affected_rows)
3527     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3528                              _("The node '%s' is not an external."),
3529                              path_for_error_message(wcroot, local_relpath,
3530                                                     scratch_pool));
3531 
3532   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3533 
3534   /* ### What about actual? */
3535   return SVN_NO_ERROR;
3536 }
3537 
3538 svn_error_t *
svn_wc__db_external_remove(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3539 svn_wc__db_external_remove(svn_wc__db_t *db,
3540                            const char *local_abspath,
3541                            const char *wri_abspath,
3542                            const svn_skel_t *work_items,
3543                            apr_pool_t *scratch_pool)
3544 {
3545   svn_wc__db_wcroot_t *wcroot;
3546   const char *local_relpath;
3547 
3548   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3549 
3550   if (! wri_abspath)
3551     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3552 
3553   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3554                               wri_abspath, scratch_pool, scratch_pool));
3555   VERIFY_USABLE_WCROOT(wcroot);
3556 
3557   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3558 
3559   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3560 
3561   SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3562                                          scratch_pool),
3563                       wcroot);
3564 
3565   return SVN_NO_ERROR;
3566 }
3567 
3568 svn_error_t *
svn_wc__db_external_read(svn_wc__db_status_t * status,svn_node_kind_t * kind,const char ** definining_abspath,const char ** repos_root_url,const char ** repos_uuid,const char ** recorded_repos_relpath,svn_revnum_t * recorded_peg_revision,svn_revnum_t * recorded_revision,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3569 svn_wc__db_external_read(svn_wc__db_status_t *status,
3570                          svn_node_kind_t *kind,
3571                          const char **definining_abspath,
3572                          const char **repos_root_url,
3573                          const char **repos_uuid,
3574                          const char **recorded_repos_relpath,
3575                          svn_revnum_t *recorded_peg_revision,
3576                          svn_revnum_t *recorded_revision,
3577                          svn_wc__db_t *db,
3578                          const char *local_abspath,
3579                          const char *wri_abspath,
3580                          apr_pool_t *result_pool,
3581                          apr_pool_t *scratch_pool)
3582 {
3583   svn_wc__db_wcroot_t *wcroot;
3584   const char *local_relpath;
3585   svn_sqlite__stmt_t *stmt;
3586   svn_boolean_t have_info;
3587   svn_error_t *err = NULL;
3588   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3589 
3590   if (! wri_abspath)
3591     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3592 
3593   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3594                               wri_abspath, scratch_pool, scratch_pool));
3595   VERIFY_USABLE_WCROOT(wcroot);
3596 
3597   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3598 
3599   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3600 
3601   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3602                                     STMT_SELECT_EXTERNAL_INFO));
3603   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3604   SVN_ERR(svn_sqlite__step(&have_info, stmt));
3605 
3606   if (have_info)
3607     {
3608       if (status)
3609         *status = svn_sqlite__column_token(stmt, 0, presence_map);
3610 
3611       if (kind)
3612         *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3613 
3614       if (definining_abspath)
3615         {
3616           const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3617 
3618           *definining_abspath = svn_dirent_join(wcroot->abspath,
3619                                                 record_relpath, result_pool);
3620         }
3621 
3622       if (repos_root_url || repos_uuid)
3623         {
3624           apr_int64_t repos_id;
3625 
3626           repos_id = svn_sqlite__column_int64(stmt, 3);
3627 
3628           err = svn_error_compose_create(
3629                         err,
3630                         svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3631                                                     wcroot, repos_id,
3632                                                     result_pool));
3633         }
3634 
3635       if (recorded_repos_relpath)
3636         *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3637                                                           result_pool);
3638 
3639       if (recorded_peg_revision)
3640         *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3641 
3642       if (recorded_revision)
3643         *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3644     }
3645   else
3646     {
3647       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3648                               _("The node '%s' is not an external."),
3649                               svn_dirent_local_style(local_abspath,
3650                                                      scratch_pool));
3651     }
3652 
3653   return svn_error_trace(
3654                 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3655 }
3656 
3657 svn_error_t *
svn_wc__db_committable_externals_below(apr_array_header_t ** externals,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t immediates_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3658 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3659                                        svn_wc__db_t *db,
3660                                        const char *local_abspath,
3661                                        svn_boolean_t immediates_only,
3662                                        apr_pool_t *result_pool,
3663                                        apr_pool_t *scratch_pool)
3664 {
3665   svn_wc__db_wcroot_t *wcroot;
3666   svn_sqlite__stmt_t *stmt;
3667   const char *local_relpath;
3668   svn_boolean_t have_row;
3669   svn_wc__committable_external_info_t *info;
3670   svn_node_kind_t db_kind;
3671   apr_array_header_t *result = NULL;
3672 
3673   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3674 
3675   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3676                               local_abspath, scratch_pool, scratch_pool));
3677   VERIFY_USABLE_WCROOT(wcroot);
3678 
3679   SVN_ERR(svn_sqlite__get_statement(
3680                 &stmt, wcroot->sdb,
3681                 immediates_only
3682                     ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3683                     : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3684 
3685   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3686 
3687   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3688 
3689   if (have_row)
3690     result = apr_array_make(result_pool, 0,
3691                             sizeof(svn_wc__committable_external_info_t *));
3692 
3693   while (have_row)
3694     {
3695       info = apr_palloc(result_pool, sizeof(*info));
3696 
3697       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3698       info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3699                                             result_pool);
3700 
3701       db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3702       SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3703       info->kind = db_kind;
3704 
3705       info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3706       info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3707 
3708       APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3709 
3710       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3711     }
3712 
3713   *externals = result;
3714   return svn_error_trace(svn_sqlite__reset(stmt));
3715 }
3716 
3717 svn_error_t *
svn_wc__db_externals_defined_below(apr_hash_t ** externals,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3718 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3719                                    svn_wc__db_t *db,
3720                                    const char *local_abspath,
3721                                    apr_pool_t *result_pool,
3722                                    apr_pool_t *scratch_pool)
3723 {
3724   svn_wc__db_wcroot_t *wcroot;
3725   svn_sqlite__stmt_t *stmt;
3726   const char *local_relpath;
3727   svn_boolean_t have_row;
3728 
3729   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3730 
3731   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3732                               local_abspath, scratch_pool, scratch_pool));
3733   VERIFY_USABLE_WCROOT(wcroot);
3734 
3735   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3736                                     STMT_SELECT_EXTERNALS_DEFINED));
3737 
3738   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3739 
3740   *externals = apr_hash_make(result_pool);
3741   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3742 
3743   while (have_row)
3744     {
3745       const char *def_local_relpath;
3746 
3747       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3748       def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3749 
3750       svn_hash_sets(*externals,
3751                     svn_dirent_join(wcroot->abspath, local_relpath,
3752                                     result_pool),
3753                     svn_dirent_join(wcroot->abspath, def_local_relpath,
3754                                     result_pool));
3755 
3756       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3757     }
3758 
3759   return svn_error_trace(svn_sqlite__reset(stmt));
3760 }
3761 
3762 svn_error_t *
svn_wc__db_externals_gather_definitions(apr_hash_t ** externals,apr_hash_t ** depths,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3763 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3764                                         apr_hash_t **depths,
3765                                         svn_wc__db_t *db,
3766                                         const char *local_abspath,
3767                                         apr_pool_t *result_pool,
3768                                         apr_pool_t *scratch_pool)
3769 {
3770   svn_wc__db_wcroot_t *wcroot;
3771   svn_sqlite__stmt_t *stmt;
3772   const char *local_relpath;
3773   svn_boolean_t have_row;
3774   svn_error_t *err = NULL;
3775   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3776 
3777   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3778 
3779   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3780                               local_abspath, scratch_pool, iterpool));
3781   VERIFY_USABLE_WCROOT(wcroot);
3782 
3783   *externals = apr_hash_make(result_pool);
3784   if (depths != NULL)
3785     *depths = apr_hash_make(result_pool);
3786 
3787   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3788                                     STMT_SELECT_EXTERNAL_PROPERTIES));
3789 
3790   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3791 
3792   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3793 
3794   while (have_row)
3795     {
3796       apr_hash_t *node_props;
3797       const char *external_value;
3798 
3799       svn_pool_clear(iterpool);
3800       err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3801                                           iterpool);
3802 
3803       if (err)
3804         break;
3805 
3806       external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3807 
3808       if (external_value)
3809         {
3810           const char *node_abspath;
3811           const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3812 
3813           node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3814                                          result_pool);
3815 
3816           svn_hash_sets(*externals, node_abspath,
3817                         apr_pstrdup(result_pool, external_value));
3818 
3819           if (depths)
3820             {
3821               svn_depth_t depth
3822                 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3823                                                 svn_depth_unknown);
3824 
3825               svn_hash_sets(*depths, node_abspath,
3826                             /* Use static string */
3827                             svn_token__to_word(depth_map, depth));
3828             }
3829         }
3830 
3831       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3832     }
3833 
3834   svn_pool_destroy(iterpool);
3835 
3836   return svn_error_trace(svn_error_compose_create(err,
3837                                                   svn_sqlite__reset(stmt)));
3838 }
3839 
3840 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3841    The new ACTUAL data won't have any conflicts. */
3842 static svn_error_t *
copy_actual(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,apr_pool_t * scratch_pool)3843 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3844             const char *src_relpath,
3845             svn_wc__db_wcroot_t *dst_wcroot,
3846             const char *dst_relpath,
3847             apr_pool_t *scratch_pool)
3848 {
3849   svn_sqlite__stmt_t *stmt;
3850   svn_boolean_t have_row;
3851 
3852   SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3853                                     STMT_SELECT_ACTUAL_NODE));
3854   SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3855   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3856   if (have_row)
3857     {
3858       apr_size_t props_size;
3859       const char *changelist;
3860       const char *properties;
3861 
3862       /* Skipping conflict data... */
3863       changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3864       /* No need to parse the properties when simply copying. */
3865       properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3866 
3867       if (changelist || properties)
3868         {
3869           SVN_ERR(svn_sqlite__reset(stmt));
3870 
3871           SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3872                                             STMT_INSERT_ACTUAL_NODE));
3873           SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3874                                     dst_wcroot->wc_id, dst_relpath,
3875                                 svn_relpath_dirname(dst_relpath, scratch_pool),
3876                                     properties, props_size, changelist));
3877           SVN_ERR(svn_sqlite__step(&have_row, stmt));
3878         }
3879     }
3880   SVN_ERR(svn_sqlite__reset(stmt));
3881 
3882   return SVN_NO_ERROR;
3883 }
3884 
3885 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3886    another */
3887 static svn_error_t *
cross_db_copy(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,svn_wc__db_status_t dst_status,int dst_op_depth,int dst_np_op_depth,svn_node_kind_t kind,const apr_array_header_t * children,apr_int64_t copyfrom_id,const char * copyfrom_relpath,svn_revnum_t copyfrom_rev,apr_pool_t * scratch_pool)3888 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3889               const char *src_relpath,
3890               svn_wc__db_wcroot_t *dst_wcroot,
3891               const char *dst_relpath,
3892               svn_wc__db_status_t dst_status,
3893               int dst_op_depth,
3894               int dst_np_op_depth,
3895               svn_node_kind_t kind,
3896               const apr_array_header_t *children,
3897               apr_int64_t copyfrom_id,
3898               const char *copyfrom_relpath,
3899               svn_revnum_t copyfrom_rev,
3900               apr_pool_t *scratch_pool)
3901 {
3902   insert_working_baton_t iwb;
3903   svn_revnum_t changed_rev;
3904   apr_time_t changed_date;
3905   const char *changed_author;
3906   const svn_checksum_t *checksum;
3907   apr_hash_t *props;
3908   svn_depth_t depth;
3909 
3910   SVN_ERR_ASSERT(kind == svn_node_file
3911                  || kind == svn_node_dir
3912                  );
3913 
3914   SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3915                     &changed_rev, &changed_date, &changed_author, &depth,
3916                     &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3917                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3918                     src_wcroot, src_relpath, scratch_pool, scratch_pool));
3919 
3920   if (dst_status != svn_wc__db_status_not_present
3921       && dst_status != svn_wc__db_status_excluded
3922       && dst_status != svn_wc__db_status_server_excluded)
3923     {
3924       SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3925                                      scratch_pool, scratch_pool));
3926     }
3927   else
3928     props = NULL;
3929 
3930   blank_iwb(&iwb);
3931   iwb.presence = dst_status;
3932   iwb.kind = kind;
3933 
3934   iwb.props = props;
3935   iwb.changed_rev = changed_rev;
3936   iwb.changed_date = changed_date;
3937   iwb.changed_author = changed_author;
3938   iwb.original_repos_id = copyfrom_id;
3939   iwb.original_repos_relpath = copyfrom_relpath;
3940   iwb.original_revnum = copyfrom_rev;
3941   iwb.moved_here = FALSE;
3942 
3943   iwb.op_depth = dst_op_depth;
3944 
3945   iwb.checksum = checksum;
3946   iwb.children = children;
3947   iwb.depth = depth;
3948 
3949   iwb.not_present_op_depth = dst_np_op_depth;
3950 
3951   SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3952 
3953   SVN_ERR(copy_actual(src_wcroot, src_relpath,
3954                       dst_wcroot, dst_relpath, scratch_pool));
3955 
3956   return SVN_NO_ERROR;
3957 }
3958 
3959 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3960    any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
3961 static svn_error_t *
get_moved_to(const char ** moved_to_relpath_p,const char ** moved_to_op_root_relpath_p,svn_boolean_t * scan,svn_sqlite__stmt_t * stmt,const char * current_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3962 get_moved_to(const char **moved_to_relpath_p,
3963              const char **moved_to_op_root_relpath_p,
3964              svn_boolean_t *scan,
3965              svn_sqlite__stmt_t *stmt,
3966              const char *current_relpath,
3967              svn_wc__db_wcroot_t *wcroot,
3968              const char *local_relpath,
3969              apr_pool_t *result_pool,
3970              apr_pool_t *scratch_pool)
3971 {
3972   const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3973 
3974   if (moved_to_relpath)
3975     {
3976       const char *moved_to_op_root_relpath = moved_to_relpath;
3977 
3978       if (strcmp(current_relpath, local_relpath))
3979         {
3980           /* LOCAL_RELPATH is a child inside the move op-root. */
3981           const char *moved_child_relpath;
3982 
3983           /* The CURRENT_RELPATH is the op_root of the delete-half of
3984            * the move. LOCAL_RELPATH is a child that was moved along.
3985            * Compute the child's new location within the move target. */
3986           moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3987                                                           local_relpath);
3988           SVN_ERR_ASSERT(moved_child_relpath &&
3989                          strlen(moved_child_relpath) > 0);
3990           moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3991                                               moved_child_relpath,
3992                                               result_pool);
3993         }
3994 
3995       if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3996         *moved_to_op_root_relpath_p
3997           = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3998 
3999       if (moved_to_relpath && moved_to_relpath_p)
4000         *moved_to_relpath_p
4001           = apr_pstrdup(result_pool, moved_to_relpath);
4002 
4003       *scan = FALSE;
4004     }
4005 
4006   return SVN_NO_ERROR;
4007 }
4008 
4009 
4010 /* The body of svn_wc__db_scan_deletion().
4011  */
4012 static svn_error_t *
scan_deletion(const char ** base_del_relpath,const char ** moved_to_relpath,const char ** work_del_relpath,const char ** moved_to_op_root_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4013 scan_deletion(const char **base_del_relpath,
4014               const char **moved_to_relpath,
4015               const char **work_del_relpath,
4016               const char **moved_to_op_root_relpath,
4017               svn_wc__db_wcroot_t *wcroot,
4018               const char *local_relpath,
4019               apr_pool_t *result_pool,
4020               apr_pool_t *scratch_pool)
4021 {
4022   const char *current_relpath = local_relpath;
4023   svn_sqlite__stmt_t *stmt;
4024   svn_wc__db_status_t work_presence;
4025   svn_boolean_t have_row, scan, have_base;
4026   int op_depth;
4027 
4028   /* Initialize all the OUT parameters.  */
4029   if (base_del_relpath != NULL)
4030     *base_del_relpath = NULL;
4031   if (moved_to_relpath != NULL)
4032     *moved_to_relpath = NULL;
4033   if (work_del_relpath != NULL)
4034     *work_del_relpath = NULL;
4035   if (moved_to_op_root_relpath != NULL)
4036     *moved_to_op_root_relpath = NULL;
4037 
4038   /* If looking for moved-to info then we need to scan every path
4039      until we find it.  If not looking for moved-to we only need to
4040      check op-roots and parents of op-roots. */
4041   scan = (moved_to_op_root_relpath || moved_to_relpath);
4042 
4043   SVN_ERR(svn_sqlite__get_statement(
4044                     &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
4045 
4046   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
4047   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4048   if (!have_row)
4049     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
4050                              _("The node '%s' was not found."),
4051                              path_for_error_message(wcroot, local_relpath,
4052                                                     scratch_pool));
4053 
4054   work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
4055   have_base = !svn_sqlite__column_is_null(stmt, 0);
4056   if (work_presence != svn_wc__db_status_not_present
4057       && work_presence != svn_wc__db_status_base_deleted)
4058     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
4059                              svn_sqlite__reset(stmt),
4060                              _("Expected node '%s' to be deleted."),
4061                              path_for_error_message(wcroot, local_relpath,
4062                                                     scratch_pool));
4063 
4064   op_depth = svn_sqlite__column_int(stmt, 2);
4065 
4066   /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
4067      treat this as an op-root.  At commit time we need to explicitly
4068      delete such nodes otherwise they will be present in the
4069      repository copy. */
4070   if (work_presence == svn_wc__db_status_not_present
4071       && work_del_relpath && !*work_del_relpath)
4072     {
4073       *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4074 
4075       if (!scan && !base_del_relpath)
4076         {
4077           /* We have all we need, exit early */
4078           SVN_ERR(svn_sqlite__reset(stmt));
4079           return SVN_NO_ERROR;
4080         }
4081     }
4082 
4083 
4084   while (TRUE)
4085     {
4086       svn_error_t *err;
4087       const char *parent_relpath;
4088       int current_depth = relpath_depth(current_relpath);
4089 
4090       /* Step CURRENT_RELPATH to op-root */
4091 
4092       while (TRUE)
4093         {
4094           if (scan)
4095             {
4096               err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4097                                  &scan, stmt, current_relpath,
4098                                  wcroot, local_relpath,
4099                                  result_pool, scratch_pool);
4100               if (err || (!scan
4101                           && !base_del_relpath
4102                           && !work_del_relpath))
4103                 {
4104                   /* We have all we need (or an error occurred) */
4105                   SVN_ERR(svn_sqlite__reset(stmt));
4106                   return svn_error_trace(err);
4107                 }
4108             }
4109 
4110           if (current_depth <= op_depth)
4111             break;
4112 
4113           current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4114           --current_depth;
4115 
4116           if (scan || current_depth == op_depth)
4117             {
4118               SVN_ERR(svn_sqlite__reset(stmt));
4119               SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4120                                         current_relpath));
4121               SVN_ERR(svn_sqlite__step(&have_row, stmt));
4122               SVN_ERR_ASSERT(have_row);
4123               have_base = !svn_sqlite__column_is_null(stmt, 0);
4124             }
4125         }
4126       SVN_ERR(svn_sqlite__reset(stmt));
4127 
4128       /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4129 
4130       SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4131       parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4132       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4133       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4134       if (!have_row)
4135         {
4136           /* No row means no WORKING node which mean we just fell off
4137              the WORKING tree, so CURRENT_RELPATH is the op-root
4138              closest to the wc root. */
4139           if (have_base && base_del_relpath)
4140             *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4141           break;
4142         }
4143 
4144       /* Still in the WORKING tree so the first time we get here
4145          CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4146       if (work_del_relpath && !*work_del_relpath)
4147         {
4148           *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4149 
4150           if (!scan && !base_del_relpath)
4151             break; /* We have all we need */
4152         }
4153 
4154       current_relpath = parent_relpath;
4155       op_depth = svn_sqlite__column_int(stmt, 2);
4156       have_base = !svn_sqlite__column_is_null(stmt, 0);
4157     }
4158 
4159   SVN_ERR(svn_sqlite__reset(stmt));
4160 
4161   return SVN_NO_ERROR;
4162 }
4163 
4164 svn_error_t *
svn_wc__db_scan_deletion_internal(const char ** base_del_relpath,const char ** moved_to_relpath,const char ** work_del_relpath,const char ** moved_to_op_root_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4165 svn_wc__db_scan_deletion_internal(
4166               const char **base_del_relpath,
4167               const char **moved_to_relpath,
4168               const char **work_del_relpath,
4169               const char **moved_to_op_root_relpath,
4170               svn_wc__db_wcroot_t *wcroot,
4171               const char *local_relpath,
4172               apr_pool_t *result_pool,
4173               apr_pool_t *scratch_pool)
4174 {
4175   return svn_error_trace(
4176             scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
4177                           moved_to_op_root_relpath,
4178                           wcroot, local_relpath,
4179                           result_pool, scratch_pool));
4180 }
4181 
4182 
4183 svn_error_t *
svn_wc__db_scan_deletion(const char ** base_del_abspath,const char ** moved_to_abspath,const char ** work_del_abspath,const char ** moved_to_op_root_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4184 svn_wc__db_scan_deletion(const char **base_del_abspath,
4185                          const char **moved_to_abspath,
4186                          const char **work_del_abspath,
4187                          const char **moved_to_op_root_abspath,
4188                          svn_wc__db_t *db,
4189                          const char *local_abspath,
4190                          apr_pool_t *result_pool,
4191                          apr_pool_t *scratch_pool)
4192 {
4193   svn_wc__db_wcroot_t *wcroot;
4194   const char *local_relpath;
4195   const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4196   const char *moved_to_op_root_relpath;
4197 
4198   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4199 
4200   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4201                               local_abspath, scratch_pool, scratch_pool));
4202   VERIFY_USABLE_WCROOT(wcroot);
4203 
4204   SVN_WC__DB_WITH_TXN(
4205     scan_deletion(&base_del_relpath, &moved_to_relpath,
4206                   &work_del_relpath, &moved_to_op_root_relpath,
4207                   wcroot, local_relpath, result_pool, scratch_pool),
4208     wcroot);
4209 
4210   if (base_del_abspath)
4211     {
4212       *base_del_abspath = (base_del_relpath
4213                            ? svn_dirent_join(wcroot->abspath,
4214                                              base_del_relpath, result_pool)
4215                            : NULL);
4216     }
4217   if (moved_to_abspath)
4218     {
4219       *moved_to_abspath = (moved_to_relpath
4220                            ? svn_dirent_join(wcroot->abspath,
4221                                              moved_to_relpath, result_pool)
4222                            : NULL);
4223     }
4224   if (work_del_abspath)
4225     {
4226       *work_del_abspath = (work_del_relpath
4227                            ? svn_dirent_join(wcroot->abspath,
4228                                              work_del_relpath, result_pool)
4229                            : NULL);
4230     }
4231   if (moved_to_op_root_abspath)
4232     {
4233       *moved_to_op_root_abspath = (moved_to_op_root_relpath
4234                            ? svn_dirent_join(wcroot->abspath,
4235                                              moved_to_op_root_relpath,
4236                                              result_pool)
4237                            : NULL);
4238     }
4239 
4240   return SVN_NO_ERROR;
4241 }
4242 
4243 
4244 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4245    appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4246    since they are available.  This is a helper for
4247    svn_wc__db_op_copy. */
4248 static svn_error_t *
get_info_for_copy(apr_int64_t * copyfrom_id,const char ** copyfrom_relpath,svn_revnum_t * copyfrom_rev,svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_boolean_t * op_root,svn_wc__db_wcroot_t * src_wcroot,const char * local_relpath,svn_wc__db_wcroot_t * dst_wcroot,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4249 get_info_for_copy(apr_int64_t *copyfrom_id,
4250                   const char **copyfrom_relpath,
4251                   svn_revnum_t *copyfrom_rev,
4252                   svn_wc__db_status_t *status,
4253                   svn_node_kind_t *kind,
4254                   svn_boolean_t *op_root,
4255                   svn_wc__db_wcroot_t *src_wcroot,
4256                   const char *local_relpath,
4257                   svn_wc__db_wcroot_t *dst_wcroot,
4258                   apr_pool_t *result_pool,
4259                   apr_pool_t *scratch_pool)
4260 {
4261   const char *repos_relpath;
4262   svn_revnum_t revision;
4263   svn_wc__db_status_t node_status;
4264   apr_int64_t repos_id;
4265   svn_boolean_t is_op_root;
4266 
4267   SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4268                     NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4269                     copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4270                     NULL, &is_op_root, NULL, NULL,
4271                     NULL /* have_base */,
4272                     NULL /* have_more_work */,
4273                     NULL /* have_work */,
4274                     src_wcroot, local_relpath, result_pool, scratch_pool));
4275 
4276   if (op_root)
4277     *op_root = is_op_root;
4278 
4279   if (node_status == svn_wc__db_status_excluded)
4280     {
4281       /* The parent cannot be excluded, so look at the parent and then
4282          adjust the relpath */
4283       const char *parent_relpath, *base_name;
4284 
4285       svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4286                        scratch_pool);
4287       SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4288                                 NULL, NULL, NULL,
4289                                 src_wcroot, parent_relpath, dst_wcroot,
4290                                 scratch_pool, scratch_pool));
4291       if (*copyfrom_relpath)
4292         *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4293                                              result_pool);
4294     }
4295   else if (node_status == svn_wc__db_status_added)
4296     {
4297       SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4298                             NULL, NULL, NULL, src_wcroot, local_relpath,
4299                             scratch_pool, scratch_pool));
4300     }
4301   else if (node_status == svn_wc__db_status_deleted && is_op_root)
4302     {
4303       const char *base_del_relpath, *work_del_relpath;
4304 
4305       SVN_ERR(scan_deletion(&base_del_relpath, NULL,
4306                             &work_del_relpath,
4307                             NULL, src_wcroot, local_relpath,
4308                             scratch_pool, scratch_pool));
4309       if (work_del_relpath)
4310         {
4311           const char *op_root_relpath;
4312           const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4313                                                                scratch_pool);
4314 
4315           /* Similar to, but not the same as, the _scan_addition and
4316              _join above.  Can we use get_copyfrom here? */
4317           SVN_ERR(scan_addition(NULL, &op_root_relpath,
4318                                 NULL, NULL, /* repos_* */
4319                                 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4320                                 NULL, NULL, NULL,
4321                                 src_wcroot, parent_del_relpath,
4322                                 scratch_pool, scratch_pool));
4323           *copyfrom_relpath
4324             = svn_relpath_join(*copyfrom_relpath,
4325                                svn_relpath_skip_ancestor(op_root_relpath,
4326                                                          local_relpath),
4327                                result_pool);
4328         }
4329       else if (base_del_relpath)
4330         {
4331           SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4332                                                     copyfrom_relpath,
4333                                                     copyfrom_id, NULL, NULL,
4334                                                     NULL, NULL, NULL, NULL,
4335                                                     NULL, NULL, NULL, NULL,
4336                                                     src_wcroot, local_relpath,
4337                                                     result_pool,
4338                                                     scratch_pool));
4339         }
4340       else
4341         SVN_ERR_MALFUNCTION();
4342     }
4343   else if (node_status == svn_wc__db_status_deleted)
4344     {
4345       /* Keep original_* from read_info() to allow seeing the difference
4346          between base-deleted and not present */
4347     }
4348   else
4349     {
4350       *copyfrom_relpath = repos_relpath;
4351       *copyfrom_rev = revision;
4352       *copyfrom_id = repos_id;
4353     }
4354 
4355   if (status)
4356     *status = node_status;
4357 
4358   if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4359     {
4360       const char *repos_root_url;
4361       const char *repos_uuid;
4362 
4363       /* Pass the right repos-id for the destination db. We can't just use
4364          the id of the source database, as this value can change after
4365          relocation (and perhaps also when we start storing multiple
4366          working copies in a single db)! */
4367 
4368       SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4369                                           src_wcroot, *copyfrom_id,
4370                                           scratch_pool));
4371 
4372       SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4373                               dst_wcroot->sdb, scratch_pool));
4374     }
4375 
4376   return SVN_NO_ERROR;
4377 }
4378 
4379 
4380 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4381 static svn_error_t *
op_depth_of(int * op_depth,svn_wc__db_wcroot_t * wcroot,const char * local_relpath)4382 op_depth_of(int *op_depth,
4383             svn_wc__db_wcroot_t *wcroot,
4384             const char *local_relpath)
4385 {
4386   svn_sqlite__stmt_t *stmt;
4387   svn_boolean_t have_row;
4388 
4389   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4390                                     STMT_SELECT_NODE_INFO));
4391   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4392   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4393   SVN_ERR_ASSERT(have_row);
4394   *op_depth = svn_sqlite__column_int(stmt, 0);
4395   SVN_ERR(svn_sqlite__reset(stmt));
4396 
4397   return SVN_NO_ERROR;
4398 }
4399 
4400 
4401 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4402    revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4403    by checking if this would be a direct child of a copy of its parent
4404    directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4405 
4406    If the node is not a direct copy at the same revision of the parent
4407    *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4408    node should be inserted at this op_depth. This will be the case when the
4409    parent already defined an incomplete child with the same name. Otherwise
4410    *NP_OP_DEPTH will be set to -1.
4411 
4412    If the parent node is not the parent of the to be copied node, then
4413    *OP_DEPTH will be set to the proper op_depth for a new operation root.
4414 
4415    Set *PARENT_OP_DEPTH to the op_depth of the parent.
4416 
4417  */
4418 static svn_error_t *
op_depth_for_copy(int * op_depth,int * np_op_depth,int * parent_op_depth,apr_int64_t copyfrom_repos_id,const char * copyfrom_relpath,svn_revnum_t copyfrom_revision,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)4419 op_depth_for_copy(int *op_depth,
4420                   int *np_op_depth,
4421                   int *parent_op_depth,
4422                   apr_int64_t copyfrom_repos_id,
4423                   const char *copyfrom_relpath,
4424                   svn_revnum_t copyfrom_revision,
4425                   svn_wc__db_wcroot_t *wcroot,
4426                   const char *local_relpath,
4427                   apr_pool_t *scratch_pool)
4428 {
4429   const char *parent_relpath, *name;
4430   svn_sqlite__stmt_t *stmt;
4431   svn_boolean_t have_row;
4432   int incomplete_op_depth = -1;
4433   int min_op_depth = 1; /* Never touch BASE */
4434 
4435   *op_depth = relpath_depth(local_relpath);
4436   *np_op_depth = -1;
4437 
4438   svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4439   *parent_op_depth = relpath_depth(parent_relpath);
4440 
4441   if (!copyfrom_relpath)
4442     return SVN_NO_ERROR;
4443 
4444   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4445                                     STMT_SELECT_WORKING_NODE));
4446   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4447   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4448   if (have_row)
4449     {
4450       svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4451                                                             presence_map);
4452 
4453       min_op_depth = svn_sqlite__column_int(stmt, 0);
4454       if (status == svn_wc__db_status_incomplete)
4455         incomplete_op_depth = min_op_depth;
4456     }
4457   SVN_ERR(svn_sqlite__reset(stmt));
4458 
4459   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4460                                     STMT_SELECT_WORKING_NODE));
4461   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4462   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4463   if (have_row)
4464     {
4465       svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4466                                                               presence_map);
4467 
4468       *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4469       if (*parent_op_depth < min_op_depth)
4470         {
4471           /* We want to create a copy; not overwrite the lower layers */
4472           SVN_ERR(svn_sqlite__reset(stmt));
4473           return SVN_NO_ERROR;
4474         }
4475 
4476       /* You can only add children below a node that exists.
4477          In WORKING that must be status added, which is represented
4478          as presence normal */
4479       SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4480 
4481       if ((incomplete_op_depth < 0)
4482           || (incomplete_op_depth == *parent_op_depth))
4483         {
4484           apr_int64_t parent_copyfrom_repos_id
4485             = svn_sqlite__column_int64(stmt, 10);
4486           const char *parent_copyfrom_relpath
4487             = svn_sqlite__column_text(stmt, 11, NULL);
4488           svn_revnum_t parent_copyfrom_revision
4489             = svn_sqlite__column_revnum(stmt, 12);
4490 
4491           if (parent_copyfrom_repos_id == copyfrom_repos_id)
4492             {
4493               if (copyfrom_revision == parent_copyfrom_revision
4494                   && !strcmp(copyfrom_relpath,
4495                              svn_relpath_join(parent_copyfrom_relpath, name,
4496                                               scratch_pool)))
4497                 *op_depth = *parent_op_depth;
4498               else if (incomplete_op_depth > 0)
4499                 *np_op_depth = incomplete_op_depth;
4500             }
4501         }
4502     }
4503   SVN_ERR(svn_sqlite__reset(stmt));
4504 
4505   return SVN_NO_ERROR;
4506 }
4507 
4508 
4509 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4510  * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4511  * copy operation is part of a move, and indicates the op-depth of the
4512  * move destination op-root. */
4513 static svn_error_t *
db_op_copy(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,const svn_skel_t * work_items,int move_op_depth,apr_pool_t * scratch_pool)4514 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4515            const char *src_relpath,
4516            svn_wc__db_wcroot_t *dst_wcroot,
4517            const char *dst_relpath,
4518            const svn_skel_t *work_items,
4519            int move_op_depth,
4520            apr_pool_t *scratch_pool)
4521 {
4522   const char *copyfrom_relpath;
4523   svn_revnum_t copyfrom_rev;
4524   svn_wc__db_status_t status;
4525   svn_wc__db_status_t dst_presence;
4526   svn_boolean_t op_root;
4527   apr_int64_t copyfrom_id;
4528   int dst_op_depth;
4529   int dst_np_op_depth;
4530   int dst_parent_op_depth;
4531   svn_node_kind_t kind;
4532   const apr_array_header_t *children;
4533 
4534   SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
4535                             &status, &kind, &op_root,
4536                             src_wcroot, src_relpath, dst_wcroot,
4537                             scratch_pool, scratch_pool));
4538 
4539   SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4540                             &dst_parent_op_depth,
4541                             copyfrom_id, copyfrom_relpath, copyfrom_rev,
4542                             dst_wcroot, dst_relpath, scratch_pool));
4543 
4544   SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4545 
4546   /* ### New status, not finished, see notes/wc-ng/copying */
4547   switch (status)
4548     {
4549     case svn_wc__db_status_normal:
4550     case svn_wc__db_status_added:
4551     case svn_wc__db_status_moved_here:
4552     case svn_wc__db_status_copied:
4553       dst_presence = svn_wc__db_status_normal;
4554       break;
4555     case svn_wc__db_status_deleted:
4556       if (op_root)
4557         {
4558           /* If the lower layer is already shadowcopied we can skip adding
4559              a not present node. */
4560           svn_error_t *err;
4561           svn_wc__db_status_t dst_status;
4562 
4563           err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4564                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4565                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4566                           dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4567 
4568           if (err)
4569             {
4570               if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4571                 svn_error_clear(err);
4572               else
4573                 return svn_error_trace(err);
4574             }
4575           else if (dst_status == svn_wc__db_status_deleted)
4576             {
4577               /* Node is already deleted; skip the NODES work, but do
4578                  install wq items if requested */
4579               SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4580                                      scratch_pool));
4581               return SVN_NO_ERROR;
4582             }
4583         }
4584       else
4585         {
4586           /* This node is either a not-present node (which should be copied), or
4587              a base-delete of some lower layer (which shouldn't).
4588              Subversion <= 1.7 always added a not-present node here, which is
4589              safe (as it postpones the hard work until commit time and then we
4590              ask the repository), but it breaks some move scenarios.
4591              */
4592 
4593            if (! copyfrom_relpath)
4594              {
4595                SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4596                                      scratch_pool));
4597                return SVN_NO_ERROR;
4598              }
4599 
4600            /* Fall through. Install not present node */
4601         }
4602     case svn_wc__db_status_not_present:
4603     case svn_wc__db_status_excluded:
4604       /* These presence values should not create a new op depth */
4605       if (dst_np_op_depth > 0)
4606         {
4607           dst_op_depth = dst_np_op_depth;
4608           dst_np_op_depth = -1;
4609         }
4610       if (status == svn_wc__db_status_excluded)
4611         dst_presence = svn_wc__db_status_excluded;
4612       else
4613         dst_presence = svn_wc__db_status_not_present;
4614       break;
4615     case svn_wc__db_status_server_excluded:
4616       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4617                                _("Cannot copy '%s' excluded by server"),
4618                                path_for_error_message(src_wcroot,
4619                                                       src_relpath,
4620                                                       scratch_pool));
4621     default:
4622       /* Perhaps we should allow incomplete to incomplete? We can't
4623          avoid incomplete working nodes as one step in copying a
4624          directory is to add incomplete children. */
4625       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4626                                _("Cannot handle status of '%s'"),
4627                                path_for_error_message(src_wcroot,
4628                                                       src_relpath,
4629                                                       scratch_pool));
4630     }
4631 
4632   if (kind == svn_node_dir)
4633     {
4634       int src_op_depth;
4635 
4636       SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4637       SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
4638                               STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
4639                               scratch_pool, scratch_pool));
4640     }
4641   else
4642     children = NULL;
4643 
4644   if (src_wcroot == dst_wcroot)
4645     {
4646       svn_sqlite__stmt_t *stmt;
4647       const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4648                                                            scratch_pool);
4649 
4650       SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4651                                         STMT_INSERT_WORKING_NODE_COPY_FROM));
4652 
4653       SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4654                     src_wcroot->wc_id, src_relpath,
4655                     dst_relpath,
4656                     dst_op_depth,
4657                     dst_parent_relpath,
4658                     presence_map, dst_presence));
4659 
4660       if (move_op_depth > 0)
4661         {
4662           if (relpath_depth(dst_relpath) == move_op_depth)
4663             {
4664               /* We're moving the root of the move operation.
4665                *
4666                * When an added node or the op-root of a copy is moved,
4667                * there is no 'moved-from' corresponding to the moved-here
4668                * node. So the net effect is the same as copy+delete.
4669                * Perform a normal copy operation in these cases. */
4670               if (!(status == svn_wc__db_status_added ||
4671                     (status == svn_wc__db_status_copied && op_root)))
4672                 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4673             }
4674           else
4675             {
4676               svn_sqlite__stmt_t *info_stmt;
4677               svn_boolean_t have_row;
4678 
4679               /* We're moving a child along with the root of the move.
4680                *
4681                * Set moved-here depending on dst_parent, propagating the
4682                * above decision to moved-along children at the same op_depth.
4683                * We can't use scan_addition() to detect moved-here because
4684                * the delete-half of the move might not yet exist. */
4685               SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4686                                                 STMT_SELECT_NODE_INFO));
4687               SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4688                                         dst_parent_relpath));
4689               SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4690               SVN_ERR_ASSERT(have_row);
4691               if (svn_sqlite__column_boolean(info_stmt, 15) &&
4692                   dst_op_depth == dst_parent_op_depth)
4693                 {
4694                   SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4695                   SVN_ERR(svn_sqlite__reset(info_stmt));
4696                 }
4697               else
4698                 {
4699                   SVN_ERR(svn_sqlite__reset(info_stmt));
4700 
4701                   /* If the child has been moved into the tree we're moving,
4702                    * keep its moved-here bit set. */
4703                   SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4704                                                     dst_wcroot->sdb,
4705                                                     STMT_SELECT_NODE_INFO));
4706                   SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4707                                             dst_wcroot->wc_id, src_relpath));
4708                   SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4709                   SVN_ERR_ASSERT(have_row);
4710                   if (svn_sqlite__column_boolean(info_stmt, 15))
4711                     SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4712                   SVN_ERR(svn_sqlite__reset(info_stmt));
4713                 }
4714             }
4715         }
4716 
4717       SVN_ERR(svn_sqlite__step_done(stmt));
4718 
4719       /* ### Copying changelist is OK for a move but what about a copy? */
4720       SVN_ERR(copy_actual(src_wcroot, src_relpath,
4721                           dst_wcroot, dst_relpath, scratch_pool));
4722 
4723       if (dst_np_op_depth > 0)
4724         {
4725           /* We introduce a not-present node at the parent's op_depth to
4726              properly start a new op-depth at our own op_depth. This marks
4727              us as an op_root for commit and allows reverting just this
4728              operation */
4729 
4730           SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4731                                             STMT_INSERT_NODE));
4732           SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4733                                     src_wcroot->wc_id, dst_relpath,
4734                                     dst_np_op_depth, dst_parent_relpath,
4735                                     copyfrom_id, copyfrom_relpath,
4736                                     copyfrom_rev,
4737                                     presence_map,
4738                                        svn_wc__db_status_not_present,
4739                                     /* NULL */
4740                                     kind_map, kind));
4741 
4742           SVN_ERR(svn_sqlite__step_done(stmt));
4743         }
4744       /* Insert incomplete children, if relevant.
4745          The children are part of the same op and so have the same op_depth.
4746          (The only time we'd want a different depth is during a recursive
4747          simple add, but we never insert children here during a simple add.) */
4748       if (kind == svn_node_dir
4749           && dst_presence == svn_wc__db_status_normal)
4750         SVN_ERR(insert_incomplete_children(
4751                   dst_wcroot->sdb,
4752                   dst_wcroot->wc_id,
4753                   dst_relpath,
4754                   copyfrom_id,
4755                   copyfrom_relpath,
4756                   copyfrom_rev,
4757                   children,
4758                   dst_op_depth,
4759                   scratch_pool));
4760     }
4761   else
4762     {
4763       SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4764                             dst_relpath, dst_presence, dst_op_depth,
4765                             dst_np_op_depth, kind,
4766                             children, copyfrom_id, copyfrom_relpath,
4767                             copyfrom_rev, scratch_pool));
4768     }
4769 
4770   SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4771 
4772   return SVN_NO_ERROR;
4773 }
4774 
4775 /* Baton for passing args to op_copy_txn(). */
4776 struct op_copy_baton
4777 {
4778   svn_wc__db_wcroot_t *src_wcroot;
4779   const char *src_relpath;
4780 
4781   svn_wc__db_wcroot_t *dst_wcroot;
4782   const char *dst_relpath;
4783 
4784   const svn_skel_t *work_items;
4785 
4786   svn_boolean_t is_move;
4787   const char *dst_op_root_relpath;
4788 };
4789 
4790 /* Helper for svn_wc__db_op_copy(). */
4791 static svn_error_t *
op_copy_txn(svn_wc__db_wcroot_t * wcroot,struct op_copy_baton * ocb,apr_pool_t * scratch_pool)4792 op_copy_txn(svn_wc__db_wcroot_t *wcroot,
4793             struct op_copy_baton *ocb,
4794             apr_pool_t *scratch_pool)
4795 {
4796   int move_op_depth;
4797 
4798   if (wcroot != ocb->dst_wcroot)
4799     {
4800        /* Source and destination databases differ; so also start a lock
4801           in the destination database, by calling ourself in an extra lock. */
4802 
4803       SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool),
4804                           ocb->dst_wcroot);
4805 
4806       return SVN_NO_ERROR;
4807     }
4808 
4809   /* From this point we can assume a lock in the src and dst databases */
4810 
4811   if (ocb->is_move)
4812     move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4813   else
4814     move_op_depth = 0;
4815 
4816   SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4817                      ocb->dst_wcroot, ocb->dst_relpath,
4818                      ocb->work_items, move_op_depth, scratch_pool));
4819 
4820   return SVN_NO_ERROR;
4821 }
4822 
4823 svn_error_t *
svn_wc__db_op_copy(svn_wc__db_t * db,const char * src_abspath,const char * dst_abspath,const char * dst_op_root_abspath,svn_boolean_t is_move,const svn_skel_t * work_items,apr_pool_t * scratch_pool)4824 svn_wc__db_op_copy(svn_wc__db_t *db,
4825                    const char *src_abspath,
4826                    const char *dst_abspath,
4827                    const char *dst_op_root_abspath,
4828                    svn_boolean_t is_move,
4829                    const svn_skel_t *work_items,
4830                    apr_pool_t *scratch_pool)
4831 {
4832   struct op_copy_baton ocb = {0};
4833 
4834   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4835   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4836   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4837 
4838   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4839                                                 &ocb.src_relpath, db,
4840                                                 src_abspath,
4841                                                 scratch_pool, scratch_pool));
4842   VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4843 
4844   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4845                                                 &ocb.dst_relpath,
4846                                                 db, dst_abspath,
4847                                                 scratch_pool, scratch_pool));
4848   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4849 
4850   ocb.work_items = work_items;
4851   ocb.is_move = is_move;
4852   ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4853                                                      dst_op_root_abspath);
4854 
4855   /* Call with the sdb in src_wcroot. It might call itself again to
4856      also obtain a lock in dst_wcroot */
4857   SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool),
4858                       ocb.src_wcroot);
4859 
4860   return SVN_NO_ERROR;
4861 }
4862 
4863 /* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
4864 static svn_error_t *
clear_or_remove_actual(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * scratch_pool)4865 clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
4866                        const char *local_relpath,
4867                        int op_depth,
4868                        apr_pool_t *scratch_pool)
4869 {
4870   svn_sqlite__stmt_t *stmt;
4871   svn_boolean_t have_row, shadowed;
4872   svn_boolean_t keep_conflict = FALSE;
4873 
4874   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4875                                     STMT_SELECT_NODE_INFO));
4876 
4877   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4878   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4879 
4880   if (have_row)
4881     {
4882       svn_wc__db_status_t presence;
4883 
4884       shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4885       presence = svn_sqlite__column_token(stmt, 3, presence_map);
4886 
4887       if (shadowed && presence == svn_wc__db_status_base_deleted)
4888         {
4889           keep_conflict = TRUE;
4890           SVN_ERR(svn_sqlite__step(&have_row, stmt));
4891 
4892           if (have_row)
4893             shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4894           else
4895             shadowed = FALSE;
4896         }
4897     }
4898   else
4899     shadowed = FALSE;
4900 
4901   SVN_ERR(svn_sqlite__reset(stmt));
4902   if (shadowed)
4903     return SVN_NO_ERROR;
4904 
4905   if (keep_conflict)
4906     {
4907       /* We don't want to accidentally remove delete-delete conflicts */
4908       SVN_ERR(svn_sqlite__get_statement(
4909                           &stmt, wcroot->sdb,
4910                           STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
4911       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4912       SVN_ERR(svn_sqlite__step_done(stmt));
4913       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4914                                         STMT_DELETE_ACTUAL_EMPTY));
4915       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4916       SVN_ERR(svn_sqlite__step_done(stmt));
4917     }
4918   else
4919     {
4920       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4921                                         STMT_DELETE_ACTUAL_NODE));
4922       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4923       SVN_ERR(svn_sqlite__step_done(stmt));
4924     }
4925 
4926   return SVN_NO_ERROR;
4927 }
4928 
4929 svn_error_t *
svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t * wcroot,const char * src_op_relpath,int src_op_depth,const char * dst_op_relpath,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)4930 svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
4931                                   const char *src_op_relpath,
4932                                   int src_op_depth,
4933                                   const char *dst_op_relpath,
4934                                   svn_skel_t *conflict,
4935                                   svn_skel_t *work_items,
4936                                   apr_pool_t *scratch_pool)
4937 {
4938   svn_sqlite__stmt_t *stmt, *stmt2;
4939   svn_boolean_t have_row;
4940   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4941   int dst_op_depth = relpath_depth(dst_op_relpath);
4942   svn_boolean_t locked;
4943   svn_error_t *err = NULL;
4944 
4945   SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
4946                                                FALSE, scratch_pool));
4947 
4948   if (!locked)
4949     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
4950                              _("No write-lock in '%s'"),
4951                              path_for_error_message(wcroot, dst_op_relpath,
4952                                                     scratch_pool));
4953 
4954   SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
4955                                     STMT_COPY_NODE_MOVE));
4956 
4957   /* Replace entire subtree at one op-depth. */
4958   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4959                                     STMT_SELECT_LAYER_FOR_REPLACE));
4960   SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
4961                             src_op_relpath, src_op_depth,
4962                             dst_op_relpath, dst_op_depth));
4963   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4964   while (have_row)
4965     {
4966       const char *src_relpath;
4967       const char *dst_relpath;
4968 
4969       svn_pool_clear(iterpool);
4970 
4971       src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
4972       dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
4973 
4974       err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
4975                               src_relpath, src_op_depth,
4976                               dst_relpath, dst_op_depth,
4977                               svn_relpath_dirname(dst_relpath, iterpool));
4978       if (!err)
4979         err = svn_sqlite__step_done(stmt2);
4980 
4981       /* stmt2 is reset (never modified or by step_done) */
4982 
4983       if (err)
4984         break;
4985 
4986       /* The node can't be deleted where it is added, so extension of
4987          an existing shadowing is only interesting 2 levels deep. */
4988       if (relpath_depth(dst_relpath) > (dst_op_depth+1))
4989         {
4990           svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
4991 
4992           if (exists)
4993             {
4994               svn_wc__db_status_t presence;
4995 
4996               presence = svn_sqlite__column_token(stmt, 3, presence_map);
4997 
4998               if (presence != svn_wc__db_status_normal)
4999                 exists = FALSE;
5000             }
5001 
5002           if (!exists)
5003             {
5004               svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
5005 
5006               err = db_extend_parent_delete(wcroot, dst_relpath,
5007                                             kind, dst_op_depth, iterpool);
5008 
5009               if (err)
5010                 break;
5011             }
5012         }
5013 
5014       SVN_ERR(svn_sqlite__step(&have_row, stmt));
5015     }
5016 
5017   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5018 
5019   /* And now remove the records that are no longer needed */
5020   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5021                                     STMT_SELECT_NO_LONGER_MOVED_RV));
5022   SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
5023                             dst_op_relpath, dst_op_depth,
5024                             src_op_relpath, src_op_depth));
5025   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5026   while (have_row)
5027     {
5028       const char *dst_relpath;
5029       svn_wc__db_status_t shadowed_presence;
5030 
5031       svn_pool_clear(iterpool);
5032 
5033       dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
5034 
5035       if (!svn_sqlite__column_is_null(stmt, 2))
5036         shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
5037       else
5038         shadowed_presence = svn_wc__db_status_not_present;
5039 
5040       if (shadowed_presence != svn_wc__db_status_normal
5041           && shadowed_presence != svn_wc__db_status_incomplete)
5042         {
5043           err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5044                                             STMT_DELETE_NODE);
5045         }
5046       else
5047         {
5048           err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5049                                          STMT_REPLACE_WITH_BASE_DELETED);
5050         }
5051 
5052       if (!err)
5053         err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
5054                                              dst_op_depth);
5055 
5056       if (!err)
5057         err = svn_sqlite__step_done(stmt2);
5058 
5059       /* stmt2 is reset (never modified or by step_done) */
5060       if (err)
5061         break;
5062 
5063       /* Delete ACTUAL information about this node that we just deleted */
5064       err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
5065                                    scratch_pool);
5066 
5067       if (err)
5068         break;
5069 
5070       /* Retract base-delete for the node itself */
5071       err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
5072                                      scratch_pool);
5073 
5074       if (err)
5075         break;
5076 
5077       SVN_ERR(svn_sqlite__step(&have_row, stmt));
5078     }
5079   svn_pool_destroy(iterpool);
5080 
5081   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5082 
5083   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5084 
5085   if (conflict)
5086     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
5087                                               conflict, scratch_pool));
5088 
5089   return SVN_NO_ERROR;
5090 }
5091 
5092 /* The txn body of svn_wc__db_op_handle_move_back */
5093 static svn_error_t *
handle_move_back(svn_boolean_t * moved_back,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * moved_from_relpath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5094 handle_move_back(svn_boolean_t *moved_back,
5095                  svn_wc__db_wcroot_t *wcroot,
5096                  const char *local_relpath,
5097                  const char *moved_from_relpath,
5098                  const svn_skel_t *work_items,
5099                  apr_pool_t *scratch_pool)
5100 {
5101   svn_sqlite__stmt_t *stmt;
5102   svn_wc__db_status_t status;
5103   svn_boolean_t op_root;
5104   svn_boolean_t have_more_work;
5105   int from_op_depth = 0;
5106   svn_boolean_t have_row;
5107   svn_boolean_t different = FALSE;
5108 
5109   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5110 
5111   SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
5112                                         NULL, NULL, NULL, NULL, NULL, NULL,
5113                                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5114                                         &op_root, NULL, NULL, NULL,
5115                                         &have_more_work, NULL,
5116                                         wcroot, local_relpath,
5117                                         scratch_pool, scratch_pool));
5118 
5119   if (status != svn_wc__db_status_added || !op_root)
5120     return SVN_NO_ERROR;
5121 
5122   /* We have two cases here: BASE-move-back and WORKING-move-back */
5123   if (have_more_work)
5124     SVN_ERR(op_depth_of(&from_op_depth, wcroot,
5125                         svn_relpath_dirname(local_relpath, scratch_pool)));
5126   else
5127     from_op_depth = 0;
5128 
5129   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5130                                     STMT_SELECT_MOVED_BACK));
5131 
5132   SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
5133                                           local_relpath,
5134                                           from_op_depth,
5135                                           relpath_depth(local_relpath)));
5136 
5137   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5138 
5139   SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
5140 
5141   {
5142     svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
5143     const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
5144 
5145     if (!moved_here
5146         || !moved_to
5147         || strcmp(moved_to, moved_from_relpath))
5148       {
5149         different = TRUE;
5150         have_row = FALSE;
5151       }
5152   }
5153 
5154   while (have_row)
5155     {
5156       svn_wc__db_status_t upper_status;
5157       svn_wc__db_status_t lower_status;
5158 
5159       upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
5160 
5161       if (svn_sqlite__column_is_null(stmt, 5))
5162         {
5163           /* No lower layer replaced. */
5164           if (upper_status != svn_wc__db_status_not_present)
5165             {
5166               different = TRUE;
5167               break;
5168             }
5169           continue;
5170         }
5171 
5172       lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
5173 
5174       if (upper_status != lower_status)
5175         {
5176           different = TRUE;
5177           break;
5178         }
5179 
5180       if (upper_status == svn_wc__db_status_not_present
5181           || upper_status == svn_wc__db_status_excluded)
5182         {
5183           SVN_ERR(svn_sqlite__step(&have_row, stmt));
5184           continue; /* Nothing to check */
5185         }
5186       else if (upper_status != svn_wc__db_status_normal)
5187         {
5188           /* Not a normal move. Mixed revision move? */
5189           different = TRUE;
5190           break;
5191         }
5192 
5193       {
5194         const char *upper_repos_relpath;
5195         const char *lower_repos_relpath;
5196 
5197         upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
5198         lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
5199 
5200         if (! upper_repos_relpath
5201             || strcmp(upper_repos_relpath, lower_repos_relpath))
5202           {
5203             different = TRUE;
5204             break;
5205           }
5206       }
5207 
5208       {
5209         svn_revnum_t upper_rev;
5210         svn_revnum_t lower_rev;
5211 
5212         upper_rev = svn_sqlite__column_revnum(stmt, 4);
5213         lower_rev = svn_sqlite__column_revnum(stmt, 8);
5214 
5215         if (upper_rev != lower_rev)
5216           {
5217             different = TRUE;
5218             break;
5219           }
5220       }
5221 
5222       {
5223         apr_int64_t upper_repos_id;
5224         apr_int64_t lower_repos_id;
5225 
5226         upper_repos_id = svn_sqlite__column_int64(stmt, 2);
5227         lower_repos_id = svn_sqlite__column_int64(stmt, 6);
5228 
5229         if (upper_repos_id != lower_repos_id)
5230           {
5231             different = TRUE;
5232             break;
5233           }
5234       }
5235 
5236       /* Check moved_here? */
5237 
5238       SVN_ERR(svn_sqlite__step(&have_row, stmt));
5239     }
5240   SVN_ERR(svn_sqlite__reset(stmt));
5241 
5242   if (! different)
5243     {
5244       /* Ok, we can now safely remove this complete move, because we
5245          determined that it 100% matches the layer below it. */
5246 
5247       /* ### We could copy the recorded timestamps from the higher to the
5248              lower layer in an attempt to improve status performance, but
5249              generally these values should be the same anyway as it was
5250              a no-op move. */
5251       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5252                                         STMT_DELETE_WORKING_OP_DEPTH));
5253 
5254       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
5255                                              local_relpath,
5256                                              relpath_depth(local_relpath)));
5257 
5258       SVN_ERR(svn_sqlite__step_done(stmt));
5259 
5260       if (moved_back)
5261         *moved_back = TRUE;
5262     }
5263 
5264   return SVN_NO_ERROR;
5265 }
5266 
5267 svn_error_t *
svn_wc__db_op_handle_move_back(svn_boolean_t * moved_back,svn_wc__db_t * db,const char * local_abspath,const char * moved_from_abspath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5268 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
5269                                svn_wc__db_t *db,
5270                                const char *local_abspath,
5271                                const char *moved_from_abspath,
5272                                const svn_skel_t *work_items,
5273                                apr_pool_t *scratch_pool)
5274 {
5275   svn_wc__db_wcroot_t *wcroot;
5276   const char *local_relpath;
5277   const char *moved_from_relpath;
5278   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5279 
5280   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5281                                                 local_abspath,
5282                                                 scratch_pool, scratch_pool));
5283   VERIFY_USABLE_WCROOT(wcroot);
5284 
5285   if (moved_back)
5286     *moved_back = FALSE;
5287 
5288   moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
5289                                                 moved_from_abspath);
5290 
5291   if (! local_relpath[0]
5292       || !moved_from_relpath)
5293     {
5294        /* WC-Roots can't be moved */
5295       SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5296       return SVN_NO_ERROR;
5297     }
5298 
5299   SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
5300                                        moved_from_relpath, work_items,
5301                                        scratch_pool),
5302                       wcroot);
5303 
5304   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5305                         scratch_pool));
5306 
5307   return SVN_NO_ERROR;
5308 }
5309 
5310 
5311 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
5312  *
5313  * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
5314  * a move, and indicates the op-depth of the move destination op-root. */
5315 static svn_error_t *
db_op_copy_shadowed_layer(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,int src_op_depth,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,int dst_op_depth,int del_op_depth,apr_int64_t repos_id,const char * repos_relpath,svn_revnum_t revision,int move_op_depth,apr_pool_t * scratch_pool)5316 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
5317                           const char *src_relpath,
5318                           int src_op_depth,
5319                           svn_wc__db_wcroot_t *dst_wcroot,
5320                           const char *dst_relpath,
5321                           int dst_op_depth,
5322                           int del_op_depth,
5323                           apr_int64_t repos_id,
5324                           const char *repos_relpath,
5325                           svn_revnum_t revision,
5326                           int move_op_depth,
5327                           apr_pool_t *scratch_pool)
5328 {
5329   const apr_array_header_t *children;
5330   apr_pool_t *iterpool;
5331   svn_wc__db_status_t status;
5332   svn_node_kind_t kind;
5333   svn_revnum_t node_revision;
5334   const char *node_repos_relpath;
5335   apr_int64_t node_repos_id;
5336   svn_sqlite__stmt_t *stmt;
5337   svn_wc__db_status_t dst_presence;
5338   int i;
5339 
5340   {
5341     svn_error_t *err;
5342     err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5343                                     &node_repos_relpath, &node_repos_id,
5344                                     NULL, NULL, NULL, NULL, NULL, NULL,
5345                                     NULL, NULL,
5346                                     src_wcroot, src_relpath, src_op_depth,
5347                                     scratch_pool, scratch_pool);
5348 
5349     if (err)
5350       {
5351         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5352           return svn_error_trace(err);
5353 
5354         svn_error_clear(err);
5355         return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5356       }
5357   }
5358 
5359   if (src_op_depth == 0)
5360     {
5361       /* If the node is switched or has a different revision then its parent
5362          we shouldn't copy it. (We can't as we would have to insert it at
5363          an unshadowed depth) */
5364       if (status == svn_wc__db_status_not_present
5365           || status == svn_wc__db_status_excluded
5366           || status == svn_wc__db_status_server_excluded
5367           || node_revision != revision
5368           || node_repos_id != repos_id
5369           || strcmp(node_repos_relpath, repos_relpath))
5370         {
5371           /* Add a not-present node in the destination wcroot */
5372           struct insert_working_baton_t iwb;
5373           const char *repos_root_url;
5374           const char *repos_uuid;
5375 
5376           SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5377                                               src_wcroot, node_repos_id,
5378                                               scratch_pool));
5379 
5380           SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5381                                   dst_wcroot->sdb, scratch_pool));
5382 
5383           blank_iwb(&iwb);
5384 
5385           iwb.op_depth = dst_op_depth;
5386           if (status != svn_wc__db_status_excluded)
5387             iwb.presence = svn_wc__db_status_not_present;
5388           else
5389             iwb.presence = svn_wc__db_status_excluded;
5390 
5391           iwb.kind = kind;
5392 
5393           iwb.original_repos_id = node_repos_id;
5394           iwb.original_revnum = node_revision;
5395           iwb.original_repos_relpath = node_repos_relpath;
5396 
5397           SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5398                                       scratch_pool));
5399 
5400           return SVN_NO_ERROR;
5401         }
5402     }
5403 
5404   iterpool = svn_pool_create(scratch_pool);
5405 
5406   switch (status)
5407     {
5408     case svn_wc__db_status_normal:
5409     case svn_wc__db_status_added:
5410     case svn_wc__db_status_moved_here:
5411     case svn_wc__db_status_copied:
5412       dst_presence = svn_wc__db_status_normal;
5413       break;
5414     case svn_wc__db_status_deleted:
5415     case svn_wc__db_status_not_present:
5416       dst_presence = svn_wc__db_status_not_present;
5417       break;
5418     case svn_wc__db_status_excluded:
5419       dst_presence = svn_wc__db_status_excluded;
5420       break;
5421     case svn_wc__db_status_server_excluded:
5422       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5423                                _("Cannot copy '%s' excluded by server"),
5424                                path_for_error_message(src_wcroot,
5425                                                       src_relpath,
5426                                                       scratch_pool));
5427     default:
5428       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5429                                _("Cannot handle status of '%s'"),
5430                                path_for_error_message(src_wcroot,
5431                                                       src_relpath,
5432                                                       scratch_pool));
5433     }
5434 
5435   if (dst_presence == svn_wc__db_status_normal
5436       && src_wcroot == dst_wcroot) /* ### Remove limitation */
5437     {
5438       SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5439                              STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5440 
5441       SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5442                         src_wcroot->wc_id, src_relpath,
5443                         dst_relpath,
5444                         dst_op_depth,
5445                         svn_relpath_dirname(dst_relpath, iterpool),
5446                         presence_map, dst_presence,
5447                         src_op_depth));
5448 
5449       /* moved_here */
5450       if (dst_op_depth == move_op_depth)
5451         SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5452 
5453       SVN_ERR(svn_sqlite__step_done(stmt));
5454 
5455       {
5456         /* And mark it deleted to allow proper shadowing */
5457         struct insert_working_baton_t iwb;
5458 
5459         blank_iwb(&iwb);
5460 
5461         iwb.op_depth = del_op_depth;
5462         iwb.presence = svn_wc__db_status_base_deleted;
5463 
5464         iwb.kind = kind;
5465 
5466         SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5467                                     scratch_pool));
5468       }
5469     }
5470   else
5471     {
5472       struct insert_working_baton_t iwb;
5473       if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5474         dst_presence = svn_wc__db_status_not_present;
5475 
5476       /* And mark it deleted to allow proper shadowing */
5477 
5478       blank_iwb(&iwb);
5479 
5480       iwb.op_depth = dst_op_depth;
5481       iwb.presence = dst_presence;
5482       iwb.kind = kind;
5483 
5484       SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5485                                     scratch_pool));
5486     }
5487 
5488   if (dst_presence == svn_wc__db_status_not_present)
5489     {
5490       /* Don't create descendants of a not present node! */
5491 
5492       /* This code is currently still triggered by copying deleted nodes
5493          between separate working copies. See ### comment above. */
5494 
5495       svn_pool_destroy(iterpool);
5496       return SVN_NO_ERROR;
5497     }
5498 
5499   SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
5500                           STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
5501                           scratch_pool, iterpool));
5502 
5503   for (i = 0; i < children->nelts; i++)
5504     {
5505       const char *name = APR_ARRAY_IDX(children, i, const char *);
5506       const char *child_src_relpath;
5507       const char *child_dst_relpath;
5508       const char *child_repos_relpath = NULL;
5509 
5510       svn_pool_clear(iterpool);
5511       child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5512       child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5513 
5514       if (repos_relpath)
5515         child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5516 
5517       SVN_ERR(db_op_copy_shadowed_layer(
5518                          src_wcroot, child_src_relpath, src_op_depth,
5519                          dst_wcroot, child_dst_relpath, dst_op_depth,
5520                          del_op_depth,
5521                          repos_id, child_repos_relpath, revision,
5522                          move_op_depth, scratch_pool));
5523     }
5524 
5525   svn_pool_destroy(iterpool);
5526 
5527   return SVN_NO_ERROR;
5528 }
5529 
5530 /* Helper for svn_wc__db_op_copy_shadowed_layer(). */
5531 static svn_error_t *
op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t * wcroot,struct op_copy_baton * ocb,apr_pool_t * scratch_pool)5532 op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot,
5533                            struct op_copy_baton *ocb,
5534                            apr_pool_t *scratch_pool)
5535 {
5536   const char *src_parent_relpath;
5537   const char *dst_parent_relpath;
5538   int src_op_depth;
5539   int dst_op_depth;
5540   int del_op_depth;
5541   const char *repos_relpath = NULL;
5542   apr_int64_t repos_id = INVALID_REPOS_ID;
5543   svn_revnum_t revision = SVN_INVALID_REVNUM;
5544 
5545   if (wcroot != ocb->dst_wcroot)
5546     {
5547       /* Source and destination databases differ; so also start a lock
5548          in the destination database, by calling ourself in an extra lock. */
5549 
5550       SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb,
5551                                                      scratch_pool),
5552                           ocb->dst_wcroot);
5553 
5554       return SVN_NO_ERROR;
5555     }
5556 
5557   /* From this point we can assume a lock in the src and dst databases */
5558 
5559 
5560   /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5561   SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5562 
5563   src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5564   dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5565 
5566   /* src_parent must be status normal or added; get its op-depth */
5567   SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5568 
5569   /* dst_parent must be status added; get its op-depth */
5570   SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5571 
5572   del_op_depth = relpath_depth(ocb->dst_relpath);
5573 
5574   /* Get some information from the parent */
5575   SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5576                                     &repos_id, NULL, NULL, NULL, NULL, NULL,
5577                                     NULL, NULL, NULL,
5578                                     ocb->src_wcroot,
5579                                     src_parent_relpath, src_op_depth,
5580                                     scratch_pool, scratch_pool));
5581 
5582   if (repos_relpath == NULL)
5583     {
5584       /* The node is a local addition and has no shadowed information */
5585       return SVN_NO_ERROR;
5586     }
5587 
5588   /* And calculate the child repos relpath */
5589   repos_relpath = svn_relpath_join(repos_relpath,
5590                                    svn_relpath_basename(ocb->src_relpath,
5591                                                         NULL),
5592                                    scratch_pool);
5593 
5594   SVN_ERR(db_op_copy_shadowed_layer(
5595                         ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5596                         ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5597                         del_op_depth,
5598                         repos_id, repos_relpath, revision,
5599                         (ocb->is_move ? dst_op_depth : 0),
5600                         scratch_pool));
5601 
5602   return SVN_NO_ERROR;
5603 }
5604 
5605 svn_error_t *
svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t * db,const char * src_abspath,const char * dst_abspath,svn_boolean_t is_move,apr_pool_t * scratch_pool)5606 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5607                                   const char *src_abspath,
5608                                   const char *dst_abspath,
5609                                   svn_boolean_t is_move,
5610                                   apr_pool_t *scratch_pool)
5611 {
5612   struct op_copy_baton ocb = {0};
5613 
5614   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5615   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5616 
5617   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5618                                                 &ocb.src_relpath, db,
5619                                                 src_abspath,
5620                                                 scratch_pool, scratch_pool));
5621   VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5622 
5623   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5624                                                 &ocb.dst_relpath,
5625                                                 db, dst_abspath,
5626                                                 scratch_pool, scratch_pool));
5627   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5628 
5629   ocb.is_move = is_move;
5630   ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5631 
5632   ocb.work_items = NULL;
5633 
5634   /* Call with the sdb in src_wcroot. It might call itself again to
5635      also obtain a lock in dst_wcroot */
5636   SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb,
5637                                                  scratch_pool),
5638                       ocb.src_wcroot);
5639 
5640   return SVN_NO_ERROR;
5641 }
5642 
5643 
5644 /* If there are any server-excluded base nodes then the copy must fail
5645    as it's not possible to commit such a copy.
5646    Return an error if there are any server-excluded nodes. */
5647 static svn_error_t *
catch_copy_of_server_excluded(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)5648 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5649                               const char *local_relpath,
5650                               apr_pool_t *scratch_pool)
5651 {
5652   svn_sqlite__stmt_t *stmt;
5653   svn_boolean_t have_row;
5654   const char *server_excluded_relpath;
5655 
5656   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5657                                     STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5658   SVN_ERR(svn_sqlite__bindf(stmt, "is",
5659                             wcroot->wc_id,
5660                             local_relpath));
5661   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5662   if (have_row)
5663     server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5664   SVN_ERR(svn_sqlite__reset(stmt));
5665   if (have_row)
5666     return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5667                              _("Cannot copy '%s' excluded by server"),
5668                              path_for_error_message(wcroot,
5669                                                     server_excluded_relpath,
5670                                                     scratch_pool));
5671 
5672   return SVN_NO_ERROR;
5673 }
5674 
5675 
5676 svn_error_t *
svn_wc__db_op_copy_dir(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * original_repos_relpath,const char * original_root_url,const char * original_uuid,svn_revnum_t original_revision,const apr_array_header_t * children,svn_depth_t depth,svn_boolean_t is_move,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5677 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5678                        const char *local_abspath,
5679                        const apr_hash_t *props,
5680                        svn_revnum_t changed_rev,
5681                        apr_time_t changed_date,
5682                        const char *changed_author,
5683                        const char *original_repos_relpath,
5684                        const char *original_root_url,
5685                        const char *original_uuid,
5686                        svn_revnum_t original_revision,
5687                        const apr_array_header_t *children,
5688                        svn_depth_t depth,
5689                        svn_boolean_t is_move,
5690                        const svn_skel_t *conflict,
5691                        const svn_skel_t *work_items,
5692                        apr_pool_t *scratch_pool)
5693 {
5694   svn_wc__db_wcroot_t *wcroot;
5695   const char *local_relpath;
5696   insert_working_baton_t iwb;
5697   int parent_op_depth;
5698 
5699   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5700   SVN_ERR_ASSERT(props != NULL);
5701   /* ### any assertions for CHANGED_* ?  */
5702   /* ### any assertions for ORIGINAL_* ?  */
5703 #if 0
5704   SVN_ERR_ASSERT(children != NULL);
5705 #endif
5706 
5707   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5708                               local_abspath, scratch_pool, scratch_pool));
5709   VERIFY_USABLE_WCROOT(wcroot);
5710 
5711   blank_iwb(&iwb);
5712 
5713   iwb.presence = svn_wc__db_status_normal;
5714   iwb.kind = svn_node_dir;
5715 
5716   if (original_root_url != NULL)
5717     {
5718       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5719                               original_root_url, original_uuid,
5720                               wcroot->sdb, scratch_pool));
5721       iwb.original_repos_relpath = original_repos_relpath;
5722       iwb.original_revnum = original_revision;
5723 
5724       iwb.props = props;
5725       iwb.changed_rev = changed_rev;
5726       iwb.changed_date = changed_date;
5727       iwb.changed_author = changed_author;
5728     }
5729 
5730   /* ### Should we do this inside the transaction? */
5731   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5732                             &parent_op_depth, iwb.original_repos_id,
5733                             original_repos_relpath, original_revision,
5734                             wcroot, local_relpath, scratch_pool));
5735 
5736   iwb.children = children;
5737   iwb.depth = depth;
5738   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5739                                iwb.op_depth == parent_op_depth);
5740 
5741   iwb.work_items = work_items;
5742   iwb.conflict = conflict;
5743 
5744   SVN_WC__DB_WITH_TXN(
5745                 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5746                 wcroot);
5747   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5748 
5749   return SVN_NO_ERROR;
5750 }
5751 
5752 
5753 svn_error_t *
svn_wc__db_op_copy_file(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * original_repos_relpath,const char * original_root_url,const char * original_uuid,svn_revnum_t original_revision,const svn_checksum_t * checksum,svn_boolean_t update_actual_props,const apr_hash_t * new_actual_props,svn_boolean_t is_move,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5754 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5755                         const char *local_abspath,
5756                         const apr_hash_t *props,
5757                         svn_revnum_t changed_rev,
5758                         apr_time_t changed_date,
5759                         const char *changed_author,
5760                         const char *original_repos_relpath,
5761                         const char *original_root_url,
5762                         const char *original_uuid,
5763                         svn_revnum_t original_revision,
5764                         const svn_checksum_t *checksum,
5765                         svn_boolean_t update_actual_props,
5766                         const apr_hash_t *new_actual_props,
5767                         svn_boolean_t is_move,
5768                         const svn_skel_t *conflict,
5769                         const svn_skel_t *work_items,
5770                         apr_pool_t *scratch_pool)
5771 {
5772   svn_wc__db_wcroot_t *wcroot;
5773   const char *local_relpath;
5774   insert_working_baton_t iwb;
5775   int parent_op_depth;
5776 
5777   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5778   SVN_ERR_ASSERT(props != NULL);
5779   /* ### any assertions for CHANGED_* ?  */
5780   SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5781                   && ! original_uuid && ! checksum
5782                   && original_revision == SVN_INVALID_REVNUM)
5783                  || (original_repos_relpath && original_root_url
5784                      && original_uuid && checksum
5785                      && original_revision != SVN_INVALID_REVNUM));
5786 
5787   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5788                               local_abspath, scratch_pool, scratch_pool));
5789   VERIFY_USABLE_WCROOT(wcroot);
5790 
5791   blank_iwb(&iwb);
5792 
5793   iwb.presence = svn_wc__db_status_normal;
5794   iwb.kind = svn_node_file;
5795 
5796   if (original_root_url != NULL)
5797     {
5798       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5799                               original_root_url, original_uuid,
5800                               wcroot->sdb, scratch_pool));
5801       iwb.original_repos_relpath = original_repos_relpath;
5802       iwb.original_revnum = original_revision;
5803 
5804       iwb.props = props;
5805       iwb.changed_rev = changed_rev;
5806       iwb.changed_date = changed_date;
5807       iwb.changed_author = changed_author;
5808     }
5809 
5810   /* ### Should we do this inside the transaction? */
5811   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5812                             &parent_op_depth, iwb.original_repos_id,
5813                             original_repos_relpath, original_revision,
5814                             wcroot, local_relpath, scratch_pool));
5815 
5816   iwb.checksum = checksum;
5817   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5818                                iwb.op_depth == parent_op_depth);
5819 
5820   if (update_actual_props)
5821     {
5822       iwb.update_actual_props = update_actual_props;
5823       iwb.new_actual_props = new_actual_props;
5824     }
5825 
5826   iwb.work_items = work_items;
5827   iwb.conflict = conflict;
5828 
5829   SVN_WC__DB_WITH_TXN(
5830           insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5831           wcroot);
5832   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5833 
5834   return SVN_NO_ERROR;
5835 }
5836 
5837 
5838 svn_error_t *
svn_wc__db_op_copy_symlink(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * original_repos_relpath,const char * original_root_url,const char * original_uuid,svn_revnum_t original_revision,const char * target,svn_boolean_t is_move,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5839 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5840                            const char *local_abspath,
5841                            const apr_hash_t *props,
5842                            svn_revnum_t changed_rev,
5843                            apr_time_t changed_date,
5844                            const char *changed_author,
5845                            const char *original_repos_relpath,
5846                            const char *original_root_url,
5847                            const char *original_uuid,
5848                            svn_revnum_t original_revision,
5849                            const char *target,
5850                            svn_boolean_t is_move,
5851                            const svn_skel_t *conflict,
5852                            const svn_skel_t *work_items,
5853                            apr_pool_t *scratch_pool)
5854 {
5855   svn_wc__db_wcroot_t *wcroot;
5856   const char *local_relpath;
5857   insert_working_baton_t iwb;
5858   int parent_op_depth;
5859 
5860   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5861   SVN_ERR_ASSERT(props != NULL);
5862   /* ### any assertions for CHANGED_* ?  */
5863   /* ### any assertions for ORIGINAL_* ?  */
5864   SVN_ERR_ASSERT(target != NULL);
5865 
5866   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5867                               local_abspath, scratch_pool, scratch_pool));
5868   VERIFY_USABLE_WCROOT(wcroot);
5869 
5870   blank_iwb(&iwb);
5871 
5872   iwb.presence = svn_wc__db_status_normal;
5873   iwb.kind = svn_node_symlink;
5874 
5875 
5876   if (original_root_url != NULL)
5877     {
5878       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5879                               original_root_url, original_uuid,
5880                               wcroot->sdb, scratch_pool));
5881       iwb.original_repos_relpath = original_repos_relpath;
5882       iwb.original_revnum = original_revision;
5883 
5884       iwb.props = props;
5885       iwb.changed_rev = changed_rev;
5886       iwb.changed_date = changed_date;
5887       iwb.changed_author = changed_author;
5888     }
5889 
5890   /* ### Should we do this inside the transaction? */
5891   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5892                             &parent_op_depth, iwb.original_repos_id,
5893                             original_repos_relpath, original_revision,
5894                             wcroot, local_relpath, scratch_pool));
5895 
5896   iwb.target = target;
5897   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5898                                iwb.op_depth == parent_op_depth);
5899 
5900   iwb.work_items = work_items;
5901   iwb.conflict = conflict;
5902 
5903   SVN_WC__DB_WITH_TXN(
5904             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5905             wcroot);
5906   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5907 
5908   return SVN_NO_ERROR;
5909 }
5910 
5911 
5912 svn_error_t *
svn_wc__db_op_add_directory(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5913 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5914                             const char *local_abspath,
5915                             const apr_hash_t *props,
5916                             const svn_skel_t *work_items,
5917                             apr_pool_t *scratch_pool)
5918 {
5919   svn_wc__db_wcroot_t *wcroot;
5920   const char *local_relpath;
5921   const char *dir_abspath;
5922   const char *name;
5923   insert_working_baton_t iwb;
5924 
5925   /* Resolve wcroot via parent directory to avoid obstruction handling */
5926   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5927   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5928 
5929   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5930                               dir_abspath, scratch_pool, scratch_pool));
5931   VERIFY_USABLE_WCROOT(wcroot);
5932 
5933   blank_iwb(&iwb);
5934 
5935   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5936   iwb.presence = svn_wc__db_status_normal;
5937   iwb.kind = svn_node_dir;
5938   iwb.op_depth = relpath_depth(local_relpath);
5939   if (props && apr_hash_count((apr_hash_t *)props))
5940     {
5941       iwb.update_actual_props = TRUE;
5942       iwb.new_actual_props = props;
5943     }
5944 
5945   iwb.work_items = work_items;
5946 
5947   SVN_WC__DB_WITH_TXN(
5948             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5949             wcroot);
5950   /* Use depth infinity to make sure we have no invalid cached information
5951    * about children of this dir. */
5952   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5953                         scratch_pool));
5954 
5955   return SVN_NO_ERROR;
5956 }
5957 
5958 
5959 svn_error_t *
svn_wc__db_op_add_file(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5960 svn_wc__db_op_add_file(svn_wc__db_t *db,
5961                        const char *local_abspath,
5962                        const apr_hash_t *props,
5963                        const svn_skel_t *work_items,
5964                        apr_pool_t *scratch_pool)
5965 {
5966   svn_wc__db_wcroot_t *wcroot;
5967   const char *local_relpath;
5968   insert_working_baton_t iwb;
5969   const char *dir_abspath;
5970   const char *name;
5971 
5972   /* Resolve wcroot via parent directory to avoid obstruction handling */
5973   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5974   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5975 
5976   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5977                               dir_abspath, scratch_pool, scratch_pool));
5978   VERIFY_USABLE_WCROOT(wcroot);
5979 
5980   blank_iwb(&iwb);
5981 
5982   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5983   iwb.presence = svn_wc__db_status_normal;
5984   iwb.kind = svn_node_file;
5985   iwb.op_depth = relpath_depth(local_relpath);
5986   if (props && apr_hash_count((apr_hash_t *)props))
5987     {
5988       iwb.update_actual_props = TRUE;
5989       iwb.new_actual_props = props;
5990     }
5991 
5992   iwb.work_items = work_items;
5993 
5994   SVN_WC__DB_WITH_TXN(
5995             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5996             wcroot);
5997   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5998 
5999   return SVN_NO_ERROR;
6000 }
6001 
6002 
6003 svn_error_t *
svn_wc__db_op_add_symlink(svn_wc__db_t * db,const char * local_abspath,const char * target,const apr_hash_t * props,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6004 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
6005                           const char *local_abspath,
6006                           const char *target,
6007                           const apr_hash_t *props,
6008                           const svn_skel_t *work_items,
6009                           apr_pool_t *scratch_pool)
6010 {
6011   svn_wc__db_wcroot_t *wcroot;
6012   const char *local_relpath;
6013   insert_working_baton_t iwb;
6014   const char *dir_abspath;
6015   const char *name;
6016 
6017   /* Resolve wcroot via parent directory to avoid obstruction handling */
6018   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6019   SVN_ERR_ASSERT(target != NULL);
6020 
6021   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
6022 
6023   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6024                               dir_abspath, scratch_pool, scratch_pool));
6025 
6026   VERIFY_USABLE_WCROOT(wcroot);
6027 
6028   blank_iwb(&iwb);
6029 
6030   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
6031   iwb.presence = svn_wc__db_status_normal;
6032   iwb.kind = svn_node_symlink;
6033   iwb.op_depth = relpath_depth(local_relpath);
6034   if (props && apr_hash_count((apr_hash_t *)props))
6035     {
6036       iwb.update_actual_props = TRUE;
6037       iwb.new_actual_props = props;
6038     }
6039 
6040   iwb.target = target;
6041 
6042   iwb.work_items = work_items;
6043 
6044   SVN_WC__DB_WITH_TXN(
6045             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
6046             wcroot);
6047   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6048 
6049   return SVN_NO_ERROR;
6050 }
6051 
6052 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
6053 static svn_error_t *
db_record_fileinfo(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_int64_t recorded_size,apr_int64_t recorded_time,apr_pool_t * scratch_pool)6054 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
6055                    const char *local_relpath,
6056                    apr_int64_t recorded_size,
6057                    apr_int64_t recorded_time,
6058                    apr_pool_t *scratch_pool)
6059 {
6060   svn_sqlite__stmt_t *stmt;
6061   int affected_rows;
6062 
6063   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6064                                     STMT_UPDATE_NODE_FILEINFO));
6065   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
6066                             recorded_size, recorded_time));
6067   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6068 
6069   SVN_ERR_ASSERT(affected_rows == 1);
6070 
6071   return SVN_NO_ERROR;
6072 }
6073 
6074 
6075 svn_error_t *
svn_wc__db_global_record_fileinfo(svn_wc__db_t * db,const char * local_abspath,svn_filesize_t recorded_size,apr_time_t recorded_time,apr_pool_t * scratch_pool)6076 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
6077                                   const char *local_abspath,
6078                                   svn_filesize_t recorded_size,
6079                                   apr_time_t recorded_time,
6080                                   apr_pool_t *scratch_pool)
6081 {
6082   svn_wc__db_wcroot_t *wcroot;
6083   const char *local_relpath;
6084 
6085   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6086 
6087   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6088                               local_abspath, scratch_pool, scratch_pool));
6089   VERIFY_USABLE_WCROOT(wcroot);
6090 
6091   SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6092                              recorded_size, recorded_time, scratch_pool));
6093 
6094   /* We *totally* monkeyed the entries. Toss 'em.  */
6095   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6096 
6097   return SVN_NO_ERROR;
6098 }
6099 
6100 
6101 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
6102  * PROPS.
6103  *
6104  * Note: PROPS=NULL means the actual props are the same as the pristine
6105  * props; to indicate no properties when the pristine has some props,
6106  * PROPS must be an empty hash. */
6107 static svn_error_t *
set_actual_props(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_hash_t * props,apr_pool_t * scratch_pool)6108 set_actual_props(svn_wc__db_wcroot_t *wcroot,
6109                  const char *local_relpath,
6110                  apr_hash_t *props,
6111                  apr_pool_t *scratch_pool)
6112 {
6113   svn_sqlite__stmt_t *stmt;
6114   int affected_rows;
6115 
6116   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6117                                     STMT_UPDATE_ACTUAL_PROPS));
6118   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6119   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
6120   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6121 
6122   if (affected_rows == 1 || !props)
6123     {
6124       /* Perhaps the entire ACTUAL record is unneeded now? */
6125       if (!props && affected_rows)
6126         {
6127           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6128                                             STMT_DELETE_ACTUAL_EMPTY));
6129           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6130           SVN_ERR(svn_sqlite__step_done(stmt));
6131         }
6132 
6133       return SVN_NO_ERROR; /* We are done */
6134     }
6135 
6136   /* We have to insert a row in ACTUAL */
6137 
6138   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6139                                     STMT_INSERT_ACTUAL_PROPS));
6140   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6141   if (*local_relpath != '\0')
6142     SVN_ERR(svn_sqlite__bind_text(stmt, 3,
6143                                   svn_relpath_dirname(local_relpath,
6144                                                       scratch_pool)));
6145   SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
6146   return svn_error_trace(svn_sqlite__step_done(stmt));
6147 }
6148 
6149 svn_error_t *
svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_hash_t * props,svn_boolean_t clear_recorded_info,apr_pool_t * scratch_pool)6150 svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot,
6151                                  const char *local_relpath,
6152                                  apr_hash_t *props,
6153                                  svn_boolean_t clear_recorded_info,
6154                                  apr_pool_t *scratch_pool)
6155 {
6156   SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
6157 
6158   if (clear_recorded_info)
6159     {
6160       SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6161                                  SVN_INVALID_FILESIZE, 0,
6162                                  scratch_pool));
6163     }
6164 
6165   return SVN_NO_ERROR;
6166 }
6167 
6168 /* The body of svn_wc__db_op_set_props().
6169 
6170    Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
6171    Create an entry in the ACTUAL table for the node if it does not yet
6172    have one.
6173    To specify no properties, BATON->props must be an empty hash, not NULL.
6174    BATON is of type 'struct set_props_baton_t'.
6175 */
6176 static svn_error_t *
set_props_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_hash_t * props,svn_boolean_t clear_recorded_info,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6177 set_props_txn(svn_wc__db_wcroot_t *wcroot,
6178               const char *local_relpath,
6179               apr_hash_t *props,
6180               svn_boolean_t clear_recorded_info,
6181               const svn_skel_t *conflict,
6182               const svn_skel_t *work_items,
6183               apr_pool_t *scratch_pool)
6184 {
6185   apr_hash_t *pristine_props;
6186 
6187   /* Check if the props are modified. If no changes, then wipe out the
6188      ACTUAL props.  PRISTINE_PROPS==NULL means that any
6189      ACTUAL props are okay as provided, so go ahead and set them.  */
6190   SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
6191                                  scratch_pool, scratch_pool));
6192   if (props && pristine_props)
6193     {
6194       apr_array_header_t *prop_diffs;
6195 
6196       SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
6197                              scratch_pool));
6198       if (prop_diffs->nelts == 0)
6199         props = NULL;
6200     }
6201 
6202   SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props,
6203                                            clear_recorded_info, scratch_pool));
6204 
6205   /* And finally.  */
6206   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6207   if (conflict)
6208     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6209                                               conflict, scratch_pool));
6210 
6211   return SVN_NO_ERROR;
6212 }
6213 
6214 
6215 svn_error_t *
svn_wc__db_op_set_props(svn_wc__db_t * db,const char * local_abspath,apr_hash_t * props,svn_boolean_t clear_recorded_info,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6216 svn_wc__db_op_set_props(svn_wc__db_t *db,
6217                         const char *local_abspath,
6218                         apr_hash_t *props,
6219                         svn_boolean_t clear_recorded_info,
6220                         const svn_skel_t *conflict,
6221                         const svn_skel_t *work_items,
6222                         apr_pool_t *scratch_pool)
6223 {
6224   svn_wc__db_wcroot_t *wcroot;
6225   const char *local_relpath;
6226 
6227   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6228 
6229   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6230                               db, local_abspath, scratch_pool, scratch_pool));
6231   VERIFY_USABLE_WCROOT(wcroot);
6232 
6233   SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
6234                                     clear_recorded_info, conflict, work_items,
6235                                     scratch_pool),
6236                       wcroot);
6237   return SVN_NO_ERROR;
6238 }
6239 
6240 
6241 svn_error_t *
svn_wc__db_op_modified(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)6242 svn_wc__db_op_modified(svn_wc__db_t *db,
6243                        const char *local_abspath,
6244                        apr_pool_t *scratch_pool)
6245 {
6246   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6247 
6248   NOT_IMPLEMENTED();
6249 }
6250 
6251 /* */
6252 static svn_error_t *
populate_targets_tree(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_depth_t depth,const apr_array_header_t * changelist_filter,apr_pool_t * scratch_pool)6253 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
6254                       const char *local_relpath,
6255                       svn_depth_t depth,
6256                       const apr_array_header_t *changelist_filter,
6257                       apr_pool_t *scratch_pool)
6258 {
6259   svn_sqlite__stmt_t *stmt;
6260   int affected_rows = 0;
6261   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6262                                       STMT_CREATE_TARGETS_LIST));
6263 
6264   if (changelist_filter && changelist_filter->nelts > 0)
6265     {
6266       /* Iterate over the changelists, adding the nodes which match.
6267          Common case: we only have one changelist, so this only
6268          happens once. */
6269       int i;
6270       int stmt_idx;
6271 
6272       switch (depth)
6273         {
6274           case svn_depth_empty:
6275             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
6276             break;
6277 
6278           case svn_depth_files:
6279             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
6280             break;
6281 
6282           case svn_depth_immediates:
6283             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
6284             break;
6285 
6286           case svn_depth_infinity:
6287             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
6288             break;
6289 
6290           default:
6291             /* We don't know how to handle unknown or exclude. */
6292             SVN_ERR_MALFUNCTION();
6293             break;
6294         }
6295 
6296       for (i = 0; i < changelist_filter->nelts; i++)
6297         {
6298           int sub_affected;
6299           const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
6300                                                  const char *);
6301 
6302           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6303                                         STMT_INSERT_TARGET_WITH_CHANGELIST));
6304           SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6305                                     local_relpath, changelist));
6306           SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6307 
6308           /* If the root is matched by the changelist, we don't have to match
6309              the children. As that tells us the root is a file */
6310           if (!sub_affected && depth > svn_depth_empty)
6311             {
6312               SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6313               SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6314                                         local_relpath, changelist));
6315               SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6316             }
6317 
6318           affected_rows += sub_affected;
6319         }
6320     }
6321   else /* No changelist filtering */
6322     {
6323       int stmt_idx;
6324       int sub_affected;
6325 
6326       switch (depth)
6327         {
6328           case svn_depth_empty:
6329             stmt_idx = STMT_INSERT_TARGET;
6330             break;
6331 
6332           case svn_depth_files:
6333             stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
6334             break;
6335 
6336           case svn_depth_immediates:
6337             stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
6338             break;
6339 
6340           case svn_depth_infinity:
6341             stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
6342             break;
6343 
6344           default:
6345             /* We don't know how to handle unknown or exclude. */
6346             SVN_ERR_MALFUNCTION();
6347             break;
6348         }
6349 
6350       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6351                                         STMT_INSERT_TARGET));
6352       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6353       SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6354       affected_rows += sub_affected;
6355 
6356       if (depth > svn_depth_empty)
6357         {
6358           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6359           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6360           SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6361           affected_rows += sub_affected;
6362         }
6363     }
6364 
6365   /* Does the target exist? */
6366   if (affected_rows == 0)
6367     {
6368       svn_boolean_t exists;
6369       SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6370 
6371       if (!exists)
6372         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6373                                  _("The node '%s' was not found."),
6374                                  path_for_error_message(wcroot,
6375                                                         local_relpath,
6376                                                         scratch_pool));
6377     }
6378 
6379   return SVN_NO_ERROR;
6380 }
6381 
6382 
6383 #if 0
6384 static svn_error_t *
6385 dump_targets(svn_wc__db_wcroot_t *wcroot,
6386              apr_pool_t *scratch_pool)
6387 {
6388   svn_sqlite__stmt_t *stmt;
6389   svn_boolean_t have_row;
6390 
6391   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6392                                     STMT_SELECT_TARGETS));
6393   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6394   while (have_row)
6395     {
6396       const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6397       SVN_DBG(("Target: '%s'\n", target));
6398       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6399     }
6400 
6401   SVN_ERR(svn_sqlite__reset(stmt));
6402 
6403   return SVN_NO_ERROR;
6404 }
6405 #endif
6406 
6407 
6408 struct set_changelist_baton_t
6409 {
6410   const char *new_changelist;
6411   const apr_array_header_t *changelist_filter;
6412   svn_depth_t depth;
6413 };
6414 
6415 
6416 /* The main part of svn_wc__db_op_set_changelist().
6417  *
6418  * Implements svn_wc__db_txn_callback_t. */
6419 static svn_error_t *
set_changelist_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)6420 set_changelist_txn(void *baton,
6421                    svn_wc__db_wcroot_t *wcroot,
6422                    const char *local_relpath,
6423                    apr_pool_t *scratch_pool)
6424 {
6425   struct set_changelist_baton_t *scb = baton;
6426   svn_sqlite__stmt_t *stmt;
6427 
6428   SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6429                                 scb->changelist_filter, scratch_pool));
6430 
6431   /* Ensure we have actual nodes for our targets. */
6432   if (scb->new_changelist)
6433     {
6434       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6435                                         STMT_INSERT_ACTUAL_EMPTIES_FILES));
6436       SVN_ERR(svn_sqlite__step_done(stmt));
6437     }
6438 
6439   /* Now create our notification table. */
6440   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6441                                       STMT_CREATE_CHANGELIST_LIST));
6442   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6443                                       STMT_CREATE_CHANGELIST_TRIGGER));
6444 
6445   /* Update our changelists. */
6446   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6447                                     STMT_UPDATE_ACTUAL_CHANGELISTS));
6448   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6449                             scb->new_changelist));
6450   SVN_ERR(svn_sqlite__step_done(stmt));
6451 
6452   if (scb->new_changelist)
6453     {
6454       /* We have to notify that we skipped directories, so do that now. */
6455       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6456                                         STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6457       SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6458                                 scb->new_changelist));
6459       SVN_ERR(svn_sqlite__step_done(stmt));
6460     }
6461 
6462   /* We may have left empty ACTUAL nodes, so remove them.  This is only a
6463      potential problem if we removed changelists. */
6464   if (!scb->new_changelist)
6465     {
6466       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467                                         STMT_DELETE_ACTUAL_EMPTIES));
6468       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469       SVN_ERR(svn_sqlite__step_done(stmt));
6470     }
6471 
6472   return SVN_NO_ERROR;
6473 }
6474 
6475 
6476 /* Send notifications for svn_wc__db_op_set_changelist().
6477  *
6478  * Implements work_callback_t. */
6479 static svn_error_t *
do_changelist_notify(void * baton,svn_wc__db_wcroot_t * wcroot,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)6480 do_changelist_notify(void *baton,
6481                      svn_wc__db_wcroot_t *wcroot,
6482                      svn_cancel_func_t cancel_func,
6483                      void *cancel_baton,
6484                      svn_wc_notify_func2_t notify_func,
6485                      void *notify_baton,
6486                      apr_pool_t *scratch_pool)
6487 {
6488   svn_sqlite__stmt_t *stmt;
6489   svn_boolean_t have_row;
6490   apr_pool_t *iterpool;
6491 
6492   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6493                                     STMT_SELECT_CHANGELIST_LIST));
6494   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6495 
6496   iterpool = svn_pool_create(scratch_pool);
6497   while (have_row)
6498     {
6499       /* ### wc_id is column 0. use it one day...  */
6500       const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6501       svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6502       svn_wc_notify_t *notify;
6503       const char *notify_abspath;
6504 
6505       svn_pool_clear(iterpool);
6506 
6507       if (cancel_func)
6508         {
6509           svn_error_t *err = cancel_func(cancel_baton);
6510 
6511           if (err)
6512             return svn_error_trace(svn_error_compose_create(
6513                                                     err,
6514                                                     svn_sqlite__reset(stmt)));
6515         }
6516 
6517       notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6518                                        iterpool);
6519       notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6520       notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6521       notify_func(notify_baton, notify, iterpool);
6522 
6523       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6524     }
6525   svn_pool_destroy(iterpool);
6526 
6527   return svn_error_trace(svn_sqlite__reset(stmt));
6528 }
6529 
6530 
6531 svn_error_t *
svn_wc__db_op_set_changelist(svn_wc__db_t * db,const char * local_abspath,const char * new_changelist,const apr_array_header_t * changelist_filter,svn_depth_t depth,svn_wc_notify_func2_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)6532 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6533                              const char *local_abspath,
6534                              const char *new_changelist,
6535                              const apr_array_header_t *changelist_filter,
6536                              svn_depth_t depth,
6537                              svn_wc_notify_func2_t notify_func,
6538                              void *notify_baton,
6539                              svn_cancel_func_t cancel_func,
6540                              void *cancel_baton,
6541                              apr_pool_t *scratch_pool)
6542 {
6543   svn_wc__db_wcroot_t *wcroot;
6544   const char *local_relpath;
6545   struct set_changelist_baton_t scb;
6546 
6547   scb.new_changelist = new_changelist;
6548   scb.changelist_filter = changelist_filter;
6549   scb.depth = depth;
6550 
6551   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6552 
6553   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6554                                                 db, local_abspath,
6555                                                 scratch_pool, scratch_pool));
6556   VERIFY_USABLE_WCROOT(wcroot);
6557 
6558   /* Flush the entries before we do the work. Even if no work is performed,
6559      the flush isn't a problem. */
6560   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6561 
6562   /* Perform the set-changelist operation (transactionally), perform any
6563      notifications necessary, and then clean out our temporary tables.  */
6564   return svn_error_trace(with_finalization(wcroot, local_relpath,
6565                                            set_changelist_txn, &scb,
6566                                            do_changelist_notify, NULL,
6567                                            cancel_func, cancel_baton,
6568                                            notify_func, notify_baton,
6569                                            STMT_FINALIZE_CHANGELIST,
6570                                            scratch_pool));
6571 }
6572 
6573 /* Implementation of svn_wc__db_op_mark_conflict() */
6574 svn_error_t *
svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const svn_skel_t * conflict_skel,apr_pool_t * scratch_pool)6575 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6576                                   const char *local_relpath,
6577                                   const svn_skel_t *conflict_skel,
6578                                   apr_pool_t *scratch_pool)
6579 {
6580   svn_sqlite__stmt_t *stmt;
6581   svn_boolean_t got_row;
6582   svn_boolean_t is_complete;
6583 
6584   SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6585   SVN_ERR_ASSERT(is_complete);
6586 
6587   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6588                                     STMT_SELECT_ACTUAL_NODE));
6589   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6590   SVN_ERR(svn_sqlite__step(&got_row, stmt));
6591   SVN_ERR(svn_sqlite__reset(stmt));
6592 
6593   if (got_row)
6594     {
6595       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6596                                         STMT_UPDATE_ACTUAL_CONFLICT));
6597       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6598     }
6599   else
6600     {
6601       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6602                                         STMT_INSERT_ACTUAL_CONFLICT));
6603       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6604       if (*local_relpath != '\0')
6605         SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6606                                       svn_relpath_dirname(local_relpath,
6607                                                           scratch_pool)));
6608     }
6609 
6610   {
6611     svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6612 
6613     SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6614   }
6615 
6616   SVN_ERR(svn_sqlite__update(NULL, stmt));
6617 
6618   return SVN_NO_ERROR;
6619 }
6620 
6621 svn_error_t *
svn_wc__db_op_mark_conflict(svn_wc__db_t * db,const char * local_abspath,const svn_skel_t * conflict_skel,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6622 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6623                             const char *local_abspath,
6624                             const svn_skel_t *conflict_skel,
6625                             const svn_skel_t *work_items,
6626                             apr_pool_t *scratch_pool)
6627 {
6628   svn_wc__db_wcroot_t *wcroot;
6629   const char *local_relpath;
6630 
6631   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6632 
6633   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6634                               local_abspath, scratch_pool, scratch_pool));
6635   VERIFY_USABLE_WCROOT(wcroot);
6636 
6637   SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6638                                             conflict_skel, scratch_pool));
6639 
6640   /* ### Should be handled in the same transaction as setting the conflict */
6641   if (work_items)
6642     SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6643 
6644   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6645 
6646   return SVN_NO_ERROR;
6647 
6648 }
6649 
6650 /* The body of svn_wc__db_op_mark_resolved().
6651  */
6652 svn_error_t *
svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_boolean_t resolved_text,svn_boolean_t resolved_props,svn_boolean_t resolved_tree,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6653 svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
6654                                      const char *local_relpath,
6655                                      svn_wc__db_t *db,
6656                                      svn_boolean_t resolved_text,
6657                                      svn_boolean_t resolved_props,
6658                                      svn_boolean_t resolved_tree,
6659                                      const svn_skel_t *work_items,
6660                                      apr_pool_t *scratch_pool)
6661 {
6662   svn_sqlite__stmt_t *stmt;
6663   svn_boolean_t have_row;
6664   int total_affected_rows = 0;
6665   svn_boolean_t resolved_all;
6666   apr_size_t conflict_len;
6667   const void *conflict_data;
6668   svn_skel_t *conflicts;
6669 
6670   /* Check if we have a conflict in ACTUAL */
6671   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6672                                     STMT_SELECT_ACTUAL_NODE));
6673   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6674 
6675   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6676 
6677   if (! have_row)
6678     {
6679       SVN_ERR(svn_sqlite__reset(stmt));
6680 
6681       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6682                                         STMT_SELECT_NODE_INFO));
6683 
6684       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6685 
6686       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6687       SVN_ERR(svn_sqlite__reset(stmt));
6688 
6689       if (have_row)
6690         return SVN_NO_ERROR;
6691 
6692       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6693                                _("The node '%s' was not found."),
6694                                    path_for_error_message(wcroot,
6695                                                           local_relpath,
6696                                                           scratch_pool));
6697     }
6698 
6699   conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6700                                           scratch_pool);
6701   conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6702   SVN_ERR(svn_sqlite__reset(stmt));
6703 
6704   SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6705                                         db, wcroot->abspath,
6706                                         resolved_text,
6707                                         resolved_props ? "" : NULL,
6708                                         resolved_tree,
6709                                         scratch_pool, scratch_pool));
6710 
6711   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6712                                     STMT_UPDATE_ACTUAL_CONFLICT));
6713   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6714 
6715   if (! resolved_all)
6716     {
6717       svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6718 
6719       SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6720     }
6721 
6722   SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6723 
6724   /* Now, remove the actual node if it doesn't have any more useful
6725      information.  We only need to do this if we've remove data ourselves. */
6726   if (total_affected_rows > 0)
6727     {
6728       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6729                                         STMT_DELETE_ACTUAL_EMPTY));
6730       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6731       SVN_ERR(svn_sqlite__step_done(stmt));
6732     }
6733 
6734   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6735 
6736   return SVN_NO_ERROR;
6737 }
6738 
6739 svn_error_t *
svn_wc__db_op_mark_resolved(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t resolved_text,svn_boolean_t resolved_props,svn_boolean_t resolved_tree,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6740 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6741                             const char *local_abspath,
6742                             svn_boolean_t resolved_text,
6743                             svn_boolean_t resolved_props,
6744                             svn_boolean_t resolved_tree,
6745                             const svn_skel_t *work_items,
6746                             apr_pool_t *scratch_pool)
6747 {
6748   svn_wc__db_wcroot_t *wcroot;
6749   const char *local_relpath;
6750 
6751   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6752 
6753   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6754                               local_abspath, scratch_pool, scratch_pool));
6755   VERIFY_USABLE_WCROOT(wcroot);
6756 
6757   SVN_WC__DB_WITH_TXN(
6758     svn_wc__db_op_mark_resolved_internal(
6759                         wcroot, local_relpath, db,
6760                         resolved_text, resolved_props, resolved_tree,
6761                         work_items, scratch_pool),
6762     wcroot);
6763 
6764   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6765   return SVN_NO_ERROR;
6766 }
6767 
6768 /* Clear moved-to information at the delete-half of the move which moved
6769  * MOVED_TO_RELPATH here. This transforms the delete part of the move into a
6770  * normal delete.
6771  *
6772  * Note that the moved-to location is always an op-root, while this is not the
6773  * case for a moved-from location.
6774  */
6775 static svn_error_t *
clear_moved_to(svn_wc__db_wcroot_t * wcroot,const char * moved_to_relpath,apr_pool_t * scratch_pool)6776 clear_moved_to(svn_wc__db_wcroot_t *wcroot,
6777                const char *moved_to_relpath,
6778                apr_pool_t *scratch_pool)
6779 {
6780   svn_sqlite__stmt_t *stmt;
6781   const char *moved_from_relpath;
6782   int moved_from_op_depth;
6783 
6784   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6785                                     STMT_SELECT_MOVED_FROM_RELPATH));
6786   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath));
6787   SVN_ERR(svn_sqlite__step_row(stmt));
6788 
6789   moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6790   moved_from_op_depth = svn_sqlite__column_int(stmt, 1);
6791   SVN_ERR(svn_sqlite__reset(stmt));
6792 
6793   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6794                                     STMT_CLEAR_MOVED_TO_RELPATH));
6795   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6796                             moved_from_relpath, moved_from_op_depth));
6797   SVN_ERR(svn_sqlite__update(NULL, stmt));
6798 
6799   return SVN_NO_ERROR;
6800 }
6801 
6802 /* Helper function for op_revert_txn. Raises move tree conflicts on
6803    descendants to ensure database stability on a non recursive revert
6804    of an ancestor that contains a possible move related tree conflict.
6805  */
6806 static svn_error_t *
revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,svn_wc__db_t * db,const char * local_relpath,int op_depth_below,apr_pool_t * scratch_pool)6807 revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,
6808                               svn_wc__db_t *db,
6809                               const char *local_relpath,
6810                               int op_depth_below,
6811                               apr_pool_t *scratch_pool)
6812 {
6813   svn_skel_t *conflict;
6814   svn_wc_operation_t operation;
6815   svn_boolean_t tree_conflicted;
6816   const apr_array_header_t *locations;
6817   svn_wc_conflict_reason_t reason;
6818   svn_wc_conflict_action_t action;
6819 
6820   SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
6821                                             local_relpath,
6822                                             scratch_pool, scratch_pool));
6823 
6824   if (!conflict)
6825     return SVN_NO_ERROR;
6826 
6827   SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL,
6828                                      &tree_conflicted,
6829                                      db, wcroot->abspath,
6830                                      conflict,
6831                                      scratch_pool, scratch_pool));
6832 
6833   if (!tree_conflicted
6834       || (operation != svn_wc_operation_update
6835           && operation != svn_wc_operation_switch))
6836     {
6837       return SVN_NO_ERROR;
6838     }
6839 
6840   SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6841                                               NULL,
6842                                               db, wcroot->abspath,
6843                                               conflict,
6844                                               scratch_pool,
6845                                               scratch_pool));
6846 
6847   if (reason == svn_wc_conflict_reason_deleted
6848       || reason == svn_wc_conflict_reason_replaced)
6849     {
6850       SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
6851         wcroot, local_relpath, op_depth_below, db,
6852         operation, action,
6853         (locations && locations->nelts > 0)
6854         ? APR_ARRAY_IDX(locations, 0,
6855                         const svn_wc_conflict_version_t *)
6856         : NULL,
6857         (locations && locations->nelts > 1)
6858         ? APR_ARRAY_IDX(locations, 1,
6859                         const svn_wc_conflict_version_t *)
6860         : NULL,
6861         scratch_pool));
6862 
6863       /* Transform the move information into revert information */
6864       SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6865                                           STMT_MOVE_NOTIFY_TO_REVERT));
6866     }
6867 
6868   return SVN_NO_ERROR;
6869 }
6870 
6871 /* Baton for op_revert_txn and op_revert_recursive_txn */
6872 struct revert_baton_t
6873 {
6874   svn_wc__db_t *db;
6875   svn_boolean_t clear_changelists;
6876 };
6877 
6878 /* One of the two alternative bodies of svn_wc__db_op_revert().
6879  *
6880  * Implements svn_wc__db_txn_callback_t. */
6881 static svn_error_t *
op_revert_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)6882 op_revert_txn(void *baton,
6883               svn_wc__db_wcroot_t *wcroot,
6884               const char *local_relpath,
6885               apr_pool_t *scratch_pool)
6886 {
6887   struct revert_baton_t *rvb = baton;
6888   svn_wc__db_t *db = rvb->db;
6889   svn_sqlite__stmt_t *stmt;
6890   svn_boolean_t have_row;
6891   int op_depth;
6892   svn_boolean_t moved_here;
6893   int affected_rows;
6894   const char *moved_to;
6895   int op_depth_below;
6896 
6897   /* ### Similar structure to op_revert_recursive_txn, should they be
6898          combined? */
6899 
6900   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6901                                     STMT_SELECT_NODE_INFO));
6902   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6903   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6904   if (!have_row)
6905     {
6906       SVN_ERR(svn_sqlite__reset(stmt));
6907 
6908       /* There was no NODE row, so attempt to delete an ACTUAL_NODE row.  */
6909       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6910                                         STMT_DELETE_ACTUAL_NODE));
6911       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6912       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6913       if (affected_rows)
6914         {
6915           /* Can't do non-recursive actual-only revert if actual-only
6916              children exist. Raise an error to cancel the transaction.  */
6917           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6918                                             STMT_ACTUAL_HAS_CHILDREN));
6919           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6920           SVN_ERR(svn_sqlite__step(&have_row, stmt));
6921           SVN_ERR(svn_sqlite__reset(stmt));
6922           if (have_row)
6923             return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6924                                      _("Can't revert '%s' without"
6925                                        " reverting children"),
6926                                      path_for_error_message(wcroot,
6927                                                             local_relpath,
6928                                                             scratch_pool));
6929           return SVN_NO_ERROR;
6930         }
6931 
6932       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6933                                _("The node '%s' was not found."),
6934                                path_for_error_message(wcroot,
6935                                                       local_relpath,
6936                                                       scratch_pool));
6937     }
6938 
6939   op_depth = svn_sqlite__column_int(stmt, 0);
6940   moved_here = svn_sqlite__column_boolean(stmt, 15);
6941   moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6942 
6943   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6944   if (have_row)
6945     op_depth_below = svn_sqlite__column_int(stmt, 0);
6946   else
6947     op_depth_below = -1;
6948 
6949   SVN_ERR(svn_sqlite__reset(stmt));
6950 
6951   if (moved_to)
6952     {
6953       SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
6954                                                 local_relpath, op_depth,
6955                                                 moved_to, NULL, scratch_pool));
6956     }
6957 
6958   if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6959     {
6960       int op_depth_increased;
6961 
6962       /* Can't do non-recursive revert if children exist */
6963       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6964                                         STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6965       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6966                                 local_relpath, op_depth));
6967       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6968       SVN_ERR(svn_sqlite__reset(stmt));
6969       if (have_row)
6970         return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6971                                  _("Can't revert '%s' without"
6972                                    " reverting children"),
6973                                  path_for_error_message(wcroot,
6974                                                         local_relpath,
6975                                                         scratch_pool));
6976 
6977       /* Rewrite the op-depth of all deleted children making the
6978          direct children into roots of deletes. */
6979       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6980                                      STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6981       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6982                                 local_relpath,
6983                                 op_depth));
6984       SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt));
6985 
6986       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6987                                         STMT_DELETE_WORKING_NODE));
6988       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6989       SVN_ERR(svn_sqlite__step_done(stmt));
6990 
6991       /* ### This removes the lock, but what about the access baton? */
6992       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6993                                         STMT_DELETE_WC_LOCK_ORPHAN));
6994       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6995       SVN_ERR(svn_sqlite__step_done(stmt));
6996 
6997       /* If this node was moved-here, clear moved-to at the move source. */
6998       if (moved_here)
6999         SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7000 
7001       /* If the node was moved itself, we don't have interesting moved
7002          children (and the move itself was already broken) */
7003       if (op_depth_increased && !moved_to)
7004         SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath,
7005                                               op_depth_below, scratch_pool));
7006     }
7007 
7008   if (rvb->clear_changelists)
7009     {
7010       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7011                                         STMT_DELETE_ACTUAL_NODE));
7012       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7013       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7014     }
7015   else
7016     {
7017       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7018                                   STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
7019       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7020       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7021       if (!affected_rows)
7022         {
7023           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7024                                   STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
7025           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7026           SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7027         }
7028     }
7029 
7030   return SVN_NO_ERROR;
7031 }
7032 
7033 
7034 /* One of the two alternative bodies of svn_wc__db_op_revert().
7035  *
7036  * Implements svn_wc__db_txn_callback_t. */
7037 static svn_error_t *
op_revert_recursive_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)7038 op_revert_recursive_txn(void *baton,
7039                         svn_wc__db_wcroot_t *wcroot,
7040                         const char *local_relpath,
7041                         apr_pool_t *scratch_pool)
7042 {
7043   struct revert_baton_t *rvb = baton;
7044   svn_sqlite__stmt_t *stmt;
7045   svn_boolean_t have_row;
7046   int op_depth;
7047   int select_op_depth;
7048   svn_boolean_t moved_here;
7049   int affected_rows;
7050   apr_pool_t *iterpool;
7051 
7052   /* ### Similar structure to op_revert_txn, should they be
7053          combined? */
7054 
7055   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7056                                     STMT_SELECT_NODE_INFO));
7057   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7058   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7059   if (!have_row)
7060     {
7061       SVN_ERR(svn_sqlite__reset(stmt));
7062 
7063       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7064                                         STMT_DELETE_ACTUAL_NODE));
7065       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7066                                 local_relpath));
7067       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7068 
7069       if (affected_rows)
7070         return SVN_NO_ERROR;  /* actual-only revert */
7071 
7072       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7073                                _("The node '%s' was not found."),
7074                                path_for_error_message(wcroot,
7075                                                       local_relpath,
7076                                                       scratch_pool));
7077     }
7078 
7079   op_depth = svn_sqlite__column_int(stmt, 0);
7080   moved_here = svn_sqlite__column_boolean(stmt, 15);
7081   SVN_ERR(svn_sqlite__reset(stmt));
7082 
7083   if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
7084     return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
7085                              _("Can't revert '%s' without"
7086                                " reverting parent"),
7087                              path_for_error_message(wcroot,
7088                                                     local_relpath,
7089                                                     scratch_pool));
7090 
7091   /* Remove moved-here from move destinations outside the tree. */
7092   SVN_ERR(svn_sqlite__get_statement(
7093                     &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
7094   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7095                             op_depth));
7096   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7097   while (have_row)
7098     {
7099       const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7100       const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7101       int move_op_depth = svn_sqlite__column_int(stmt, 2);
7102       svn_error_t *err;
7103 
7104       err = svn_wc__db_op_break_move_internal(wcroot,
7105                                               src_relpath, move_op_depth,
7106                                               dst_relpath, NULL, scratch_pool);
7107       if (err)
7108         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
7109 
7110       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7111     }
7112   SVN_ERR(svn_sqlite__reset(stmt));
7113 
7114   /* Don't delete BASE nodes */
7115   select_op_depth = op_depth ? op_depth : 1;
7116 
7117   /* Reverting any non wc-root node */
7118   SVN_ERR(svn_sqlite__get_statement(
7119                     &stmt, wcroot->sdb,
7120                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7121   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
7122                             local_relpath, select_op_depth));
7123   SVN_ERR(svn_sqlite__step_done(stmt));
7124 
7125   if (rvb->clear_changelists)
7126     {
7127       SVN_ERR(svn_sqlite__get_statement(
7128                         &stmt, wcroot->sdb,
7129                         STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7130       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7131       SVN_ERR(svn_sqlite__step_done(stmt));
7132     }
7133   else
7134     {
7135       SVN_ERR(svn_sqlite__get_statement(
7136                         &stmt, wcroot->sdb,
7137                         STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7138       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7139       SVN_ERR(svn_sqlite__step_done(stmt));
7140 
7141       SVN_ERR(svn_sqlite__get_statement(
7142                         &stmt, wcroot->sdb,
7143                         STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7144       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7145       SVN_ERR(svn_sqlite__step_done(stmt));
7146     }
7147 
7148   /* ### This removes the locks, but what about the access batons? */
7149   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7150                                     STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7151   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7152                             local_relpath));
7153   SVN_ERR(svn_sqlite__step_done(stmt));
7154 
7155   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7156                                     STMT_SELECT_MOVED_HERE_CHILDREN));
7157   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7158 
7159   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7160 
7161   iterpool = svn_pool_create(scratch_pool);
7162   while (have_row)
7163     {
7164       const char *moved_here_child_relpath;
7165       svn_error_t *err;
7166 
7167       svn_pool_clear(iterpool);
7168 
7169       moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
7170       err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool);
7171       if (err)
7172         return svn_error_trace(svn_error_compose_create(
7173                                         err,
7174                                         svn_sqlite__reset(stmt)));
7175 
7176       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7177     }
7178   SVN_ERR(svn_sqlite__reset(stmt));
7179   svn_pool_destroy(iterpool);
7180 
7181   /* Clear potential moved-to pointing at the target node itself. */
7182   if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
7183       && moved_here)
7184     SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7185 
7186   return SVN_NO_ERROR;
7187 }
7188 
7189 svn_error_t *
svn_wc__db_op_revert(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,svn_boolean_t clear_changelists,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7190 svn_wc__db_op_revert(svn_wc__db_t *db,
7191                      const char *local_abspath,
7192                      svn_depth_t depth,
7193                      svn_boolean_t clear_changelists,
7194                      apr_pool_t *result_pool,
7195                      apr_pool_t *scratch_pool)
7196 {
7197   svn_wc__db_wcroot_t *wcroot;
7198   const char *local_relpath;
7199   struct revert_baton_t rvb;
7200   struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
7201                                        STMT_DROP_REVERT_LIST_TRIGGERS,
7202                                        NULL, NULL};
7203 
7204   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7205 
7206   rvb.db = db;
7207   rvb.clear_changelists = clear_changelists;
7208   wtb.cb_baton = &rvb;
7209 
7210   switch (depth)
7211     {
7212     case svn_depth_empty:
7213       wtb.cb_func = op_revert_txn;
7214       break;
7215     case svn_depth_infinity:
7216       wtb.cb_func = op_revert_recursive_txn;
7217       break;
7218     default:
7219       return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7220                                _("Unsupported depth for revert of '%s'"),
7221                                svn_dirent_local_style(local_abspath,
7222                                                       scratch_pool));
7223     }
7224 
7225   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7226                               db, local_abspath, scratch_pool, scratch_pool));
7227   VERIFY_USABLE_WCROOT(wcroot);
7228 
7229   SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
7230                       wcroot);
7231 
7232   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
7233 
7234   return SVN_NO_ERROR;
7235 }
7236 
7237 /* The body of svn_wc__db_revert_list_read().
7238  */
7239 static svn_error_t *
revert_list_read(svn_boolean_t * reverted,const apr_array_header_t ** marker_paths,svn_boolean_t * copied_here,svn_node_kind_t * kind,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7240 revert_list_read(svn_boolean_t *reverted,
7241                  const apr_array_header_t **marker_paths,
7242                  svn_boolean_t *copied_here,
7243                  svn_node_kind_t *kind,
7244                  svn_wc__db_wcroot_t *wcroot,
7245                  const char *local_relpath,
7246                  svn_wc__db_t *db,
7247                  apr_pool_t *result_pool,
7248                  apr_pool_t *scratch_pool)
7249 {
7250   svn_sqlite__stmt_t *stmt;
7251   svn_boolean_t have_row;
7252 
7253   *reverted = FALSE;
7254   *marker_paths = NULL;
7255   *copied_here = FALSE;
7256   *kind = svn_node_unknown;
7257 
7258   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7259                                     STMT_SELECT_REVERT_LIST));
7260   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7261   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7262   if (have_row)
7263     {
7264       svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
7265       svn_boolean_t another_row = FALSE;
7266 
7267       if (is_actual)
7268         {
7269           apr_size_t conflict_len;
7270           const void *conflict_data;
7271 
7272           conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
7273                                                   scratch_pool);
7274           if (conflict_data)
7275             {
7276               svn_skel_t *conflicts = svn_skel__parse(conflict_data,
7277                                                       conflict_len,
7278                                                       scratch_pool);
7279 
7280               SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
7281                                                     db, wcroot->abspath,
7282                                                     conflicts,
7283                                                     result_pool,
7284                                                     scratch_pool));
7285             }
7286 
7287           if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
7288             *reverted = TRUE;
7289 
7290           SVN_ERR(svn_sqlite__step(&another_row, stmt));
7291         }
7292 
7293       if (!is_actual || another_row)
7294         {
7295           *reverted = TRUE;
7296           if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
7297             {
7298               int op_depth = svn_sqlite__column_int(stmt, 3);
7299               *copied_here = (op_depth == relpath_depth(local_relpath));
7300             }
7301           *kind = svn_sqlite__column_token(stmt, 2, kind_map);
7302         }
7303 
7304     }
7305   SVN_ERR(svn_sqlite__reset(stmt));
7306 
7307   if (have_row)
7308     {
7309       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7310                                         STMT_DELETE_REVERT_LIST));
7311       SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7312       SVN_ERR(svn_sqlite__step_done(stmt));
7313     }
7314 
7315   return SVN_NO_ERROR;
7316 }
7317 
7318 svn_error_t *
svn_wc__db_revert_list_read(svn_boolean_t * reverted,const apr_array_header_t ** marker_files,svn_boolean_t * copied_here,svn_node_kind_t * kind,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7319 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
7320                             const apr_array_header_t **marker_files,
7321                             svn_boolean_t *copied_here,
7322                             svn_node_kind_t *kind,
7323                             svn_wc__db_t *db,
7324                             const char *local_abspath,
7325                             apr_pool_t *result_pool,
7326                             apr_pool_t *scratch_pool)
7327 {
7328   svn_wc__db_wcroot_t *wcroot;
7329   const char *local_relpath;
7330 
7331   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7332                               db, local_abspath, scratch_pool, scratch_pool));
7333   VERIFY_USABLE_WCROOT(wcroot);
7334 
7335   SVN_WC__DB_WITH_TXN(
7336     revert_list_read(reverted, marker_files, copied_here, kind,
7337                      wcroot, local_relpath, db,
7338                      result_pool, scratch_pool),
7339     wcroot);
7340   return SVN_NO_ERROR;
7341 }
7342 
7343 
7344 /* The body of svn_wc__db_revert_list_read_copied_children().
7345  */
7346 static svn_error_t *
revert_list_read_copied_children(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_array_header_t ** children_p,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7347 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
7348                                  const char *local_relpath,
7349                                  apr_array_header_t **children_p,
7350                                  apr_pool_t *result_pool,
7351                                  apr_pool_t *scratch_pool)
7352 {
7353   svn_sqlite__stmt_t *stmt;
7354   svn_boolean_t have_row;
7355   apr_array_header_t *children;
7356 
7357   children =
7358     apr_array_make(result_pool, 0,
7359                   sizeof(svn_wc__db_revert_list_copied_child_info_t *));
7360 
7361   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7362                                     STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
7363   SVN_ERR(svn_sqlite__bindf(stmt, "sd",
7364                             local_relpath, relpath_depth(local_relpath)));
7365   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7366   while (have_row)
7367     {
7368       svn_wc__db_revert_list_copied_child_info_t *child_info;
7369       const char *child_relpath;
7370 
7371       child_info = apr_palloc(result_pool, sizeof(*child_info));
7372 
7373       child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7374       child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7375                                             result_pool);
7376       child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7377       APR_ARRAY_PUSH(
7378         children,
7379         svn_wc__db_revert_list_copied_child_info_t *) = child_info;
7380 
7381       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7382     }
7383    SVN_ERR(svn_sqlite__reset(stmt));
7384 
7385   *children_p = children;
7386 
7387   return SVN_NO_ERROR;
7388 }
7389 
7390 
7391 svn_error_t *
svn_wc__db_revert_list_read_copied_children(apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7392 svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children,
7393                                             svn_wc__db_t *db,
7394                                             const char *local_abspath,
7395                                             apr_pool_t *result_pool,
7396                                             apr_pool_t *scratch_pool)
7397 {
7398   svn_wc__db_wcroot_t *wcroot;
7399   const char *local_relpath;
7400 
7401   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7402                               db, local_abspath, scratch_pool, scratch_pool));
7403   VERIFY_USABLE_WCROOT(wcroot);
7404 
7405   SVN_WC__DB_WITH_TXN(
7406     revert_list_read_copied_children(wcroot, local_relpath, children,
7407                                      result_pool, scratch_pool),
7408     wcroot);
7409   return SVN_NO_ERROR;
7410 }
7411 
7412 
7413 svn_error_t *
svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,void * notify_baton,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)7414 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
7415                               void *notify_baton,
7416                               svn_wc__db_t *db,
7417                               const char *local_abspath,
7418                               apr_pool_t *scratch_pool)
7419 {
7420   svn_wc__db_wcroot_t *wcroot;
7421   const char *local_relpath;
7422   svn_sqlite__stmt_t *stmt;
7423   svn_boolean_t have_row;
7424   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7425 
7426   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7427                               db, local_abspath, scratch_pool, iterpool));
7428   VERIFY_USABLE_WCROOT(wcroot);
7429 
7430   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7431                                     STMT_SELECT_REVERT_LIST_RECURSIVE));
7432   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7433   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7434   if (!have_row)
7435     return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7436   while (have_row)
7437     {
7438       const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7439       svn_wc_notify_t *notify;
7440 
7441       svn_pool_clear(iterpool);
7442 
7443       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7444                                                     notify_relpath,
7445                                                     iterpool),
7446                                     svn_wc_notify_revert,
7447                                     iterpool);
7448 
7449       if (!svn_sqlite__column_is_null(stmt, 1))
7450         notify->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7451       else
7452         {
7453           if (!svn_sqlite__column_is_null(stmt, 3))
7454             notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none);
7455 
7456           switch (svn_sqlite__column_int(stmt, 2))
7457             {
7458               case 0:
7459                 continue;
7460               case 1:
7461                 /* standard revert */
7462                 break;
7463               case 2:
7464                 notify->action = svn_wc_notify_tree_conflict;
7465                 break;
7466               default:
7467                 SVN_ERR_MALFUNCTION();
7468             }
7469         }
7470 
7471       notify_func(notify_baton, notify, iterpool);
7472 
7473       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7474     }
7475   SVN_ERR(svn_sqlite__reset(stmt));
7476 
7477   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7478                                     STMT_DELETE_REVERT_LIST_RECURSIVE));
7479   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7480   SVN_ERR(svn_sqlite__step_done(stmt));
7481 
7482   svn_pool_destroy(iterpool);
7483 
7484   return SVN_NO_ERROR;
7485 }
7486 
7487 svn_error_t *
svn_wc__db_revert_list_done(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)7488 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7489                             const char *local_abspath,
7490                             apr_pool_t *scratch_pool)
7491 {
7492   svn_wc__db_wcroot_t *wcroot;
7493   const char *local_relpath;
7494 
7495   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7496                               db, local_abspath, scratch_pool, scratch_pool));
7497   VERIFY_USABLE_WCROOT(wcroot);
7498 
7499   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7500 
7501   return SVN_NO_ERROR;
7502 }
7503 
7504 /* The body of svn_wc__db_op_remove_node().
7505  */
7506 static svn_error_t *
remove_node_txn(svn_boolean_t * left_changes,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_boolean_t destroy_wc,svn_boolean_t destroy_changes,const svn_skel_t * conflict,const svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)7507 remove_node_txn(svn_boolean_t *left_changes,
7508                 svn_wc__db_wcroot_t *wcroot,
7509                 const char *local_relpath,
7510                 svn_wc__db_t *db,
7511                 svn_boolean_t destroy_wc,
7512                 svn_boolean_t destroy_changes,
7513                 const svn_skel_t *conflict,
7514                 const svn_skel_t *work_items,
7515                 svn_cancel_func_t cancel_func,
7516                 void *cancel_baton,
7517                 apr_pool_t *scratch_pool)
7518 {
7519   svn_sqlite__stmt_t *stmt;
7520 
7521   /* Note that unlike many similar functions it is a valid scenario for this
7522      function to be called on a wcroot! */
7523 
7524    /* db set when destroying wc */
7525   SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7526 
7527   if (left_changes)
7528     *left_changes = FALSE;
7529 
7530   if (destroy_wc
7531       && (!destroy_changes || *local_relpath == '\0'))
7532     {
7533       svn_boolean_t have_row;
7534       apr_pool_t *iterpool;
7535       svn_error_t *err = NULL;
7536 
7537       /* Install WQ items for deleting the unmodified files and all dirs */
7538       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7539                                         STMT_SELECT_WORKING_PRESENT));
7540       SVN_ERR(svn_sqlite__bindf(stmt, "is",
7541                                 wcroot->wc_id, local_relpath));
7542 
7543       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7544 
7545       iterpool = svn_pool_create(scratch_pool);
7546 
7547       while (have_row)
7548         {
7549           const char *child_relpath;
7550           const char *child_abspath;
7551           svn_node_kind_t child_kind;
7552           svn_boolean_t have_checksum;
7553           svn_filesize_t recorded_size;
7554           apr_int64_t recorded_time;
7555           const svn_io_dirent2_t *dirent;
7556           svn_boolean_t modified_p = TRUE;
7557           svn_skel_t *work_item = NULL;
7558 
7559           svn_pool_clear(iterpool);
7560 
7561           child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7562           child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7563 
7564           child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7565                                           iterpool);
7566 
7567           if (child_kind == svn_node_file)
7568             {
7569               have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7570               recorded_size = get_recorded_size(stmt, 3);
7571               recorded_time = svn_sqlite__column_int64(stmt, 4);
7572             }
7573 
7574           if (cancel_func)
7575             err = cancel_func(cancel_baton);
7576 
7577           if (err)
7578             break;
7579 
7580           err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7581                                     iterpool, iterpool);
7582 
7583           if (err)
7584             break;
7585 
7586           if (destroy_changes
7587               || dirent->kind != svn_node_file
7588               || child_kind != svn_node_file)
7589             {
7590               /* Not interested in keeping changes */
7591               modified_p = FALSE;
7592             }
7593           else if (child_kind == svn_node_file
7594                    && dirent->kind == svn_node_file
7595                    && dirent->filesize == recorded_size
7596                    && dirent->mtime == recorded_time)
7597             {
7598               modified_p = FALSE; /* File matches recorded state */
7599             }
7600           else if (have_checksum)
7601             err = svn_wc__internal_file_modified_p(&modified_p,
7602                                                    db, child_abspath,
7603                                                    FALSE, iterpool);
7604 
7605           if (err)
7606             break;
7607 
7608           if (modified_p)
7609             {
7610               if (left_changes)
7611                 *left_changes = TRUE;
7612             }
7613           else if (child_kind == svn_node_dir)
7614             {
7615               err = svn_wc__wq_build_dir_remove(&work_item,
7616                                                 db, wcroot->abspath,
7617                                                 child_abspath, FALSE,
7618                                                 iterpool, iterpool);
7619             }
7620           else /* svn_node_file || svn_node_symlink */
7621             {
7622               err = svn_wc__wq_build_file_remove(&work_item,
7623                                                  db, wcroot->abspath,
7624                                                  child_abspath,
7625                                                  iterpool, iterpool);
7626             }
7627 
7628           if (err)
7629             break;
7630 
7631           if (work_item)
7632             {
7633               err = add_work_items(wcroot->sdb, work_item, iterpool);
7634               if (err)
7635                 break;
7636             }
7637 
7638           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7639         }
7640       svn_pool_destroy(iterpool);
7641 
7642       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7643     }
7644 
7645   if (destroy_wc && *local_relpath != '\0')
7646     {
7647       /* Create work item for destroying the root */
7648       svn_wc__db_status_t status;
7649       svn_node_kind_t kind;
7650       SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7651                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7652                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7653                         wcroot, local_relpath,
7654                         scratch_pool, scratch_pool));
7655 
7656       if (status == svn_wc__db_status_normal
7657           || status == svn_wc__db_status_added
7658           || status == svn_wc__db_status_incomplete)
7659         {
7660           svn_skel_t *work_item = NULL;
7661           const char *local_abspath = svn_dirent_join(wcroot->abspath,
7662                                                           local_relpath,
7663                                                           scratch_pool);
7664 
7665           if (kind == svn_node_dir)
7666             {
7667               SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7668                                                   db, wcroot->abspath,
7669                                                   local_abspath,
7670                                                   destroy_changes
7671                                                       /* recursive */,
7672                                                   scratch_pool, scratch_pool));
7673             }
7674           else
7675             {
7676               svn_boolean_t modified_p = FALSE;
7677 
7678               if (!destroy_changes)
7679                 {
7680                   SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7681                                                            db, local_abspath,
7682                                                            FALSE,
7683                                                            scratch_pool));
7684                 }
7685 
7686               if (!modified_p)
7687                 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7688                                                      db, wcroot->abspath,
7689                                                      local_abspath,
7690                                                      scratch_pool,
7691                                                      scratch_pool));
7692               else
7693                 {
7694                   if (left_changes)
7695                     *left_changes = TRUE;
7696                 }
7697             }
7698 
7699           SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7700         }
7701     }
7702 
7703   /* Remove all nodes below local_relpath */
7704   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7705                                     STMT_DELETE_NODE_RECURSIVE));
7706   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7707   SVN_ERR(svn_sqlite__step_done(stmt));
7708 
7709   /* Delete the root NODE when this is not the working copy root */
7710   if (local_relpath[0] != '\0')
7711     {
7712       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7713                                         STMT_DELETE_NODE_ALL_LAYERS));
7714       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7715       SVN_ERR(svn_sqlite__step_done(stmt));
7716     }
7717 
7718   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7719                                     STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7720 
7721   /* Delete all actual nodes at or below local_relpath */
7722   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7723                                          local_relpath));
7724   SVN_ERR(svn_sqlite__step_done(stmt));
7725 
7726   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7727   if (conflict)
7728     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7729                                               conflict, scratch_pool));
7730 
7731   return SVN_NO_ERROR;
7732 }
7733 
7734 svn_error_t *
svn_wc__db_op_remove_node(svn_boolean_t * left_changes,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t destroy_wc,svn_boolean_t destroy_changes,const svn_skel_t * conflict,const svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)7735 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7736                           svn_wc__db_t *db,
7737                           const char *local_abspath,
7738                           svn_boolean_t destroy_wc,
7739                           svn_boolean_t destroy_changes,
7740                           const svn_skel_t *conflict,
7741                           const svn_skel_t *work_items,
7742                           svn_cancel_func_t cancel_func,
7743                           void *cancel_baton,
7744                           apr_pool_t *scratch_pool)
7745 {
7746   svn_wc__db_wcroot_t *wcroot;
7747   const char *local_relpath;
7748 
7749   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7750 
7751   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7752                               local_abspath, scratch_pool, scratch_pool));
7753   VERIFY_USABLE_WCROOT(wcroot);
7754 
7755   SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7756                                       wcroot, local_relpath, db,
7757                                       destroy_wc, destroy_changes,
7758                                       conflict, work_items,
7759                                       cancel_func, cancel_baton, scratch_pool),
7760                       wcroot);
7761 
7762   /* Flush everything below this node in all ways */
7763   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7764                         scratch_pool));
7765 
7766   return SVN_NO_ERROR;
7767 }
7768 
7769 
7770 /* The body of svn_wc__db_op_set_base_depth().
7771  */
7772 static svn_error_t *
db_op_set_base_depth(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_depth_t depth,apr_pool_t * scratch_pool)7773 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7774                      const char *local_relpath,
7775                      svn_depth_t depth,
7776                      apr_pool_t *scratch_pool)
7777 {
7778   svn_sqlite__stmt_t *stmt;
7779   int affected_rows;
7780 
7781   /* Flush any entries before we start monkeying the database.  */
7782   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7783                                     STMT_UPDATE_NODE_BASE_DEPTH));
7784   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7785                             svn_token__to_word(depth_map, depth)));
7786   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7787 
7788   if (affected_rows == 0)
7789     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7790                              _("The node '%s' is not a committed directory"),
7791                              path_for_error_message(wcroot, local_relpath,
7792                                                     scratch_pool));
7793 
7794   return SVN_NO_ERROR;
7795 }
7796 
7797 
7798 svn_error_t *
svn_wc__db_op_set_base_depth(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,apr_pool_t * scratch_pool)7799 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7800                              const char *local_abspath,
7801                              svn_depth_t depth,
7802                              apr_pool_t *scratch_pool)
7803 {
7804   svn_wc__db_wcroot_t *wcroot;
7805   const char *local_relpath;
7806 
7807   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7808   SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7809 
7810   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7811                               local_abspath, scratch_pool, scratch_pool));
7812   VERIFY_USABLE_WCROOT(wcroot);
7813 
7814   /* ### We set depth on working and base to match entry behavior.
7815          Maybe these should be separated later? */
7816   SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7817                                            scratch_pool),
7818                       wcroot);
7819 
7820   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7821 
7822   return SVN_NO_ERROR;
7823 }
7824 
7825 
7826 static svn_error_t *
7827 info_below_working(svn_boolean_t *have_base,
7828                    svn_boolean_t *have_work,
7829                    svn_wc__db_status_t *status,
7830                    svn_wc__db_wcroot_t *wcroot,
7831                    const char *local_relpath,
7832                    int below_op_depth, /* < 0 is ignored */
7833                    apr_pool_t *scratch_pool);
7834 
7835 
7836 /* Convert STATUS, the raw status obtained from the presence map, to
7837    the status appropriate for a working (op_depth > 0) node and return
7838    it in *WORKING_STATUS. */
7839 static svn_error_t *
convert_to_working_status(svn_wc__db_status_t * working_status,svn_wc__db_status_t status)7840 convert_to_working_status(svn_wc__db_status_t *working_status,
7841                           svn_wc__db_status_t status)
7842 {
7843   svn_wc__db_status_t work_status = status;
7844 
7845   SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7846                  || work_status == svn_wc__db_status_not_present
7847                  || work_status == svn_wc__db_status_base_deleted
7848                  || work_status == svn_wc__db_status_incomplete
7849                  || work_status == svn_wc__db_status_excluded);
7850 
7851   if (work_status == svn_wc__db_status_excluded)
7852     {
7853       *working_status = svn_wc__db_status_excluded;
7854     }
7855   else if (work_status == svn_wc__db_status_not_present
7856            || work_status == svn_wc__db_status_base_deleted)
7857     {
7858       /* The caller should scan upwards to detect whether this
7859          deletion has occurred because this node has been moved
7860          away, or it is a regular deletion. Also note that the
7861          deletion could be of the BASE tree, or a child of
7862          something that has been copied/moved here. */
7863 
7864       *working_status = svn_wc__db_status_deleted;
7865     }
7866   else /* normal or incomplete */
7867     {
7868       /* The caller should scan upwards to detect whether this
7869          addition has occurred because of a simple addition,
7870          a copy, or is the destination of a move. */
7871       *working_status = svn_wc__db_status_added;
7872     }
7873 
7874   return SVN_NO_ERROR;
7875 }
7876 
7877 
7878 /* Return the status of the node, if any, below the "working" node (or
7879    below BELOW_OP_DEPTH if >= 0).
7880    Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7881    working node is present, and *STATUS to the status of the first
7882    layer below the selected node. */
7883 static svn_error_t *
info_below_working(svn_boolean_t * have_base,svn_boolean_t * have_work,svn_wc__db_status_t * status,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int below_op_depth,apr_pool_t * scratch_pool)7884 info_below_working(svn_boolean_t *have_base,
7885                    svn_boolean_t *have_work,
7886                    svn_wc__db_status_t *status,
7887                    svn_wc__db_wcroot_t *wcroot,
7888                    const char *local_relpath,
7889                    int below_op_depth,
7890                    apr_pool_t *scratch_pool)
7891 {
7892   svn_sqlite__stmt_t *stmt;
7893   svn_boolean_t have_row;
7894 
7895   *have_base = *have_work =  FALSE;
7896   *status = svn_wc__db_status_normal;
7897 
7898   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7899                                     STMT_SELECT_NODE_INFO));
7900   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7901   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7902 
7903   if (below_op_depth >= 0)
7904     {
7905       while (have_row &&
7906              (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7907         {
7908           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7909         }
7910     }
7911   if (have_row)
7912     {
7913       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7914       if (have_row)
7915         *status = svn_sqlite__column_token(stmt, 3, presence_map);
7916 
7917       while (have_row)
7918         {
7919           int op_depth = svn_sqlite__column_int(stmt, 0);
7920 
7921           if (op_depth > 0)
7922             *have_work = TRUE;
7923           else
7924             *have_base = TRUE;
7925 
7926           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7927         }
7928     }
7929   SVN_ERR(svn_sqlite__reset(stmt));
7930 
7931   if (*have_work)
7932     SVN_ERR(convert_to_working_status(status, *status));
7933 
7934   return SVN_NO_ERROR;
7935 }
7936 
7937 /* Helper function for op_delete_txn */
7938 static svn_error_t *
delete_update_movedto(svn_wc__db_wcroot_t * wcroot,const char * child_moved_from_relpath,int op_depth,const char * new_moved_to_relpath,apr_pool_t * scratch_pool)7939 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7940                       const char *child_moved_from_relpath,
7941                       int op_depth,
7942                       const char *new_moved_to_relpath,
7943                       apr_pool_t *scratch_pool)
7944 {
7945   svn_sqlite__stmt_t *stmt;
7946   int affected;
7947 
7948   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7949                                     STMT_UPDATE_MOVED_TO_RELPATH));
7950 
7951   SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7952                             wcroot->wc_id,
7953                             child_moved_from_relpath,
7954                             op_depth,
7955                             new_moved_to_relpath));
7956   SVN_ERR(svn_sqlite__update(&affected, stmt));
7957 #ifdef SVN_DEBUG
7958   /* Not fatal in release mode. The move recording is broken,
7959      but the rest of the working copy can handle this. */
7960   SVN_ERR_ASSERT(affected == 1);
7961 #endif
7962 
7963   return SVN_NO_ERROR;
7964 }
7965 
7966 
7967 struct op_delete_baton_t {
7968   const char *moved_to_relpath; /* NULL if delete is not part of a move */
7969   svn_skel_t *conflict;
7970   svn_skel_t *work_items;
7971   svn_boolean_t delete_dir_externals;
7972   svn_boolean_t notify;
7973 };
7974 
7975 /* This structure is used while rewriting move information for nodes.
7976  *
7977  * The most simple case of rewriting move information happens when
7978  * a moved-away subtree is moved again:  mv A B; mv B C
7979  * The second move requires rewriting moved-to info at or within A.
7980  *
7981  * Another example is a move of a subtree which had nodes moved into it:
7982  *   mv A B/F; mv B G
7983  * This requires rewriting such that A/F is marked has having moved to G/F.
7984  *
7985  * Another case is where a node becomes a nested moved node.
7986  * A nested move happens when a subtree child is moved before or after
7987  * the subtree itself is moved. For example:
7988  *   mv A/F A/G; mv A B
7989  * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7990  * Note that the following sequence results in the same DB state:
7991  *   mv A B; mv B/F B/G
7992  * We do not care about the order the moves were performed in.
7993  * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7994  */
7995 struct moved_node_t {
7996   /* The source of the move. */
7997   const char *local_relpath;
7998 
7999   /* The move destination. */
8000   const char *moved_to_relpath;
8001 
8002   /* The op-depth of the deleted node at the source of the move. */
8003   int op_depth;
8004 
8005   /* When >= 1 the op_depth at which local_relpath was moved to its
8006      location. Used to find its original location outside the delete */
8007   int moved_from_depth;
8008 };
8009 
8010 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
8011    before it was moved into the tree rooted at ROOT_RELPATH. */
8012 static svn_error_t *
resolve_moved_from(const char ** moved_from_relpath,int * moved_from_op_depth,svn_wc__db_wcroot_t * wcroot,const char * root_relpath,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8013 resolve_moved_from(const char **moved_from_relpath,
8014                    int *moved_from_op_depth,
8015                    svn_wc__db_wcroot_t *wcroot,
8016                    const char *root_relpath,
8017                    const char *local_relpath,
8018                    int op_depth,
8019                    apr_pool_t *result_pool,
8020                    apr_pool_t *scratch_pool)
8021 {
8022   const char *suffix = "";
8023   svn_sqlite__stmt_t *stmt;
8024   const char *m_from_relpath;
8025   int m_from_op_depth;
8026   int m_move_from_depth;
8027   svn_boolean_t have_row;
8028 
8029   while (relpath_depth(local_relpath) > op_depth)
8030     {
8031       const char *name;
8032       svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
8033       suffix = svn_relpath_join(suffix, name, scratch_pool);
8034     }
8035 
8036   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8037                                     STMT_SELECT_MOVED_FROM_FOR_DELETE));
8038   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8039                             wcroot->wc_id, local_relpath));
8040   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8041 
8042   if (!have_row)
8043     {
8044       /* assert(have_row); */
8045       *moved_from_relpath = NULL;
8046       *moved_from_op_depth = -1;
8047 
8048       SVN_ERR(svn_sqlite__reset(stmt));
8049 
8050       return SVN_NO_ERROR;
8051     }
8052 
8053   m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
8054   m_from_op_depth = svn_sqlite__column_int(stmt, 1);
8055   m_move_from_depth = svn_sqlite__column_int(stmt, 2);
8056 
8057   SVN_ERR(svn_sqlite__reset(stmt));
8058 
8059   if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
8060     {
8061       *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
8062                                              result_pool);
8063       *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
8064       return SVN_NO_ERROR;
8065     }
8066   else if (!m_move_from_depth)
8067     {
8068       *moved_from_relpath = NULL;
8069       *moved_from_op_depth = -1;
8070       return SVN_NO_ERROR;
8071     }
8072 
8073   return svn_error_trace(
8074         resolve_moved_from(moved_from_relpath,
8075                            moved_from_op_depth,
8076                            wcroot,
8077                            root_relpath,
8078                            svn_relpath_join(m_from_relpath, suffix,
8079                                             scratch_pool),
8080                            m_move_from_depth,
8081                            result_pool, scratch_pool));
8082 }
8083 
8084 static svn_error_t *
delete_node(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)8085 delete_node(void *baton,
8086             svn_wc__db_wcroot_t *wcroot,
8087             const char *local_relpath,
8088             apr_pool_t *scratch_pool)
8089 {
8090   struct op_delete_baton_t *b = baton;
8091   svn_wc__db_status_t status;
8092   svn_boolean_t have_row, op_root;
8093   svn_boolean_t add_work = FALSE;
8094   svn_sqlite__stmt_t *stmt;
8095   int working_op_depth; /* Depth of what is to be deleted */
8096   int keep_op_depth = 0; /* Depth of what is below what is deleted */
8097   svn_node_kind_t kind;
8098   apr_array_header_t *moved_nodes = NULL;
8099   int delete_op_depth = relpath_depth(local_relpath);
8100 
8101   assert(*local_relpath); /* Can't delete wcroot */
8102 
8103   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8104                                     STMT_SELECT_NODE_INFO));
8105   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8106   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8107 
8108   if (!have_row)
8109     {
8110       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8111                                svn_sqlite__reset(stmt),
8112                                _("The node '%s' was not found."),
8113                                path_for_error_message(wcroot,
8114                                                       local_relpath,
8115                                                       scratch_pool));
8116     }
8117 
8118   working_op_depth = svn_sqlite__column_int(stmt, 0);
8119   status = svn_sqlite__column_token(stmt, 3, presence_map);
8120   kind = svn_sqlite__column_token(stmt, 4, kind_map);
8121 
8122   if (working_op_depth < delete_op_depth)
8123     {
8124       op_root = FALSE;
8125       add_work = TRUE;
8126       keep_op_depth = working_op_depth;
8127     }
8128   else
8129     {
8130       op_root = TRUE;
8131 
8132       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8133 
8134       if (have_row)
8135         {
8136           svn_wc__db_status_t below_status;
8137           int below_op_depth;
8138 
8139           below_op_depth = svn_sqlite__column_int(stmt, 0);
8140           below_status = svn_sqlite__column_token(stmt, 3, presence_map);
8141 
8142           if (below_status != svn_wc__db_status_not_present
8143               && below_status != svn_wc__db_status_base_deleted)
8144             {
8145               add_work = TRUE;
8146               keep_op_depth = below_op_depth;
8147             }
8148           else
8149             keep_op_depth = 0;
8150         }
8151       else
8152         keep_op_depth = -1;
8153     }
8154 
8155   SVN_ERR(svn_sqlite__reset(stmt));
8156 
8157   if (working_op_depth != 0) /* WORKING */
8158     SVN_ERR(convert_to_working_status(&status, status));
8159 
8160   if (status == svn_wc__db_status_deleted
8161       || status == svn_wc__db_status_not_present)
8162     return SVN_NO_ERROR;
8163 
8164   /* Don't copy BASE directories with server excluded nodes */
8165   if (status == svn_wc__db_status_normal && kind == svn_node_dir)
8166     {
8167       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8168                                         STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
8169       SVN_ERR(svn_sqlite__bindf(stmt, "is",
8170                                 wcroot->wc_id, local_relpath));
8171       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8172       if (have_row)
8173         {
8174           const char *absent_path = svn_sqlite__column_text(stmt, 0,
8175                                                             scratch_pool);
8176 
8177           return svn_error_createf(
8178                                SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
8179                                svn_sqlite__reset(stmt),
8180                           _("Cannot delete '%s' as '%s' is excluded by server"),
8181                                path_for_error_message(wcroot, local_relpath,
8182                                                       scratch_pool),
8183                                path_for_error_message(wcroot, absent_path,
8184                                                       scratch_pool));
8185         }
8186       SVN_ERR(svn_sqlite__reset(stmt));
8187     }
8188   else if (status == svn_wc__db_status_server_excluded)
8189     {
8190       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8191                           _("Cannot delete '%s' as it is excluded by server"),
8192                                path_for_error_message(wcroot, local_relpath,
8193                                                       scratch_pool));
8194     }
8195   else if (status == svn_wc__db_status_excluded)
8196     {
8197       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8198                           _("Cannot delete '%s' as it is excluded"),
8199                                path_for_error_message(wcroot, local_relpath,
8200                                                       scratch_pool));
8201     }
8202 
8203   if (b->moved_to_relpath)
8204     {
8205       const char *moved_from_relpath = NULL;
8206       struct moved_node_t *moved_node;
8207       int move_op_depth;
8208 
8209       moved_nodes = apr_array_make(scratch_pool, 1,
8210                                    sizeof(struct moved_node_t *));
8211 
8212       /* The node is being moved-away.
8213        * Figure out if the node was moved-here before, or whether this
8214        * is the first time the node is moved. */
8215       if (status == svn_wc__db_status_added)
8216         SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
8217                               &moved_from_relpath,
8218                               NULL,
8219                               &move_op_depth,
8220                               wcroot, local_relpath,
8221                               scratch_pool, scratch_pool));
8222 
8223       if (op_root && moved_from_relpath)
8224         {
8225           const char *part = svn_relpath_skip_ancestor(local_relpath,
8226                                                        moved_from_relpath);
8227 
8228           /* Existing move-root is moved to another location */
8229           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8230           if (!part)
8231             moved_node->local_relpath = moved_from_relpath;
8232           else
8233             moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
8234                                                          part, scratch_pool);
8235           moved_node->op_depth = move_op_depth;
8236           moved_node->moved_to_relpath = b->moved_to_relpath;
8237           moved_node->moved_from_depth = -1;
8238 
8239           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8240         }
8241       else if (!op_root && (status == svn_wc__db_status_normal
8242                             || status == svn_wc__db_status_copied
8243                             || status == svn_wc__db_status_moved_here))
8244         {
8245           /* The node is becoming a move-root for the first time,
8246            * possibly because of a nested move operation. */
8247           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8248           moved_node->local_relpath = local_relpath;
8249           moved_node->op_depth = delete_op_depth;
8250           moved_node->moved_to_relpath = b->moved_to_relpath;
8251           moved_node->moved_from_depth = -1;
8252 
8253           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8254         }
8255       /* Else: We can't track history of local additions and/or of things we are
8256                about to delete. */
8257 
8258       /* And update all moved_to values still pointing to this location */
8259       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8260                                         STMT_UPDATE_MOVED_TO_DESCENDANTS));
8261       SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
8262                                              local_relpath,
8263                                              b->moved_to_relpath));
8264       SVN_ERR(svn_sqlite__update(NULL, stmt));
8265     }
8266 
8267   /* Find children that were moved out of the subtree rooted at this node.
8268    * We'll need to update their op-depth columns because their deletion
8269    * is now implied by the deletion of their parent (i.e. this node). */
8270     {
8271       apr_pool_t *iterpool;
8272       int i;
8273 
8274       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8275                                         STMT_SELECT_MOVED_FOR_DELETE));
8276       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
8277                                 delete_op_depth));
8278 
8279       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8280       iterpool = svn_pool_create(scratch_pool);
8281       while (have_row)
8282         {
8283           struct moved_node_t *mn;
8284           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8285           const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
8286           int child_op_depth = svn_sqlite__column_int(stmt, 2);
8287           int moved_from_depth = -1;
8288           svn_boolean_t fixup = FALSE;
8289 
8290           if (! b->moved_to_relpath
8291               && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
8292             {
8293               /* a NULL moved_here_depth will be reported as 0 */
8294               int moved_here_depth = svn_sqlite__column_int(stmt, 3);
8295 
8296               /* Plain delete. Fixup move information of descendants that were
8297                  moved here, or that were moved out */
8298 
8299               if (moved_here_depth >= delete_op_depth)
8300                 {
8301                   /* The move we recorded here must be moved to the location
8302                      this node had before it was moved here.
8303 
8304                      This might contain multiple steps when the node was moved
8305                      in several places within the to be deleted tree */
8306 
8307                   /* ### TODO: Add logic */
8308                   fixup = TRUE;
8309                   moved_from_depth = moved_here_depth;
8310                 }
8311               else
8312                 {
8313                   /* Update the op-depth of an moved away node that was
8314                      registered as moved by the records that we are about
8315                      to delete */
8316                   fixup = TRUE;
8317                   child_op_depth = delete_op_depth;
8318                 }
8319             }
8320           else if (b->moved_to_relpath)
8321             {
8322               /* The node is moved to a new location */
8323 
8324               if (delete_op_depth == child_op_depth)
8325                 {
8326                   /* Update the op-depth of a tree shadowed by this tree */
8327                   fixup = TRUE;
8328                   /*child_op_depth = delete_depth;*/
8329                 }
8330               else if (child_op_depth >= delete_op_depth
8331                        && !svn_relpath_skip_ancestor(local_relpath,
8332                                                      mv_to_relpath))
8333                 {
8334                   /* Update the move destination of something that is now moved
8335                      away further */
8336 
8337                   child_relpath = svn_relpath_skip_ancestor(local_relpath,
8338                                                             child_relpath);
8339 
8340                   if (child_relpath)
8341                     {
8342                       child_relpath = svn_relpath_join(b->moved_to_relpath,
8343                                                        child_relpath,
8344                                                        scratch_pool);
8345 
8346                       if (child_op_depth > delete_op_depth
8347                            && svn_relpath_skip_ancestor(local_relpath,
8348                                                         child_relpath))
8349                         child_op_depth = delete_op_depth;
8350                       else
8351                         {
8352                           /* Calculate depth of the shadowing at the new location */
8353                           child_op_depth = child_op_depth
8354                                                 - relpath_depth(local_relpath)
8355                                                 + relpath_depth(b->moved_to_relpath);
8356                         }
8357 
8358                       fixup = TRUE;
8359                     }
8360                 }
8361             }
8362 
8363           if (fixup)
8364             {
8365               mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8366 
8367               mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
8368               mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
8369               mn->op_depth = child_op_depth;
8370               mn->moved_from_depth = moved_from_depth;
8371 
8372               if (!moved_nodes)
8373                 moved_nodes = apr_array_make(scratch_pool, 1,
8374                                              sizeof(struct moved_node_t *));
8375               APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
8376             }
8377 
8378           SVN_ERR(svn_sqlite__step(&have_row, stmt));
8379         }
8380       SVN_ERR(svn_sqlite__reset(stmt));
8381 
8382       for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
8383         {
8384           struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
8385                                                   struct moved_node_t *);
8386 
8387           if (mn->moved_from_depth > 0)
8388             {
8389               svn_pool_clear(iterpool);
8390 
8391               SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
8392                                          wcroot, local_relpath,
8393                                          mn->local_relpath,
8394                                          mn->moved_from_depth,
8395                                          scratch_pool, iterpool));
8396 
8397               if (!mn->local_relpath)
8398                 svn_sort__array_delete(moved_nodes, i--, 1);
8399             }
8400         }
8401 
8402       svn_pool_destroy(iterpool);
8403     }
8404 
8405   if (!b->moved_to_relpath)
8406     {
8407       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8408                                         STMT_CLEAR_MOVED_TO_DESCENDANTS));
8409       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8410                                 local_relpath));
8411       SVN_ERR(svn_sqlite__update(NULL, stmt));
8412 
8413       if (op_root)
8414         {
8415           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8416                                             STMT_CLEAR_MOVED_TO_FROM_DEST));
8417           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8418                                     local_relpath));
8419 
8420           SVN_ERR(svn_sqlite__update(NULL, stmt));
8421         }
8422     }
8423 
8424 
8425   /* ### Put actual-only nodes into the list? */
8426   if (b->notify)
8427     {
8428       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8429                                         STMT_INSERT_DELETE_LIST));
8430       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8431                                 wcroot->wc_id, local_relpath, working_op_depth));
8432       SVN_ERR(svn_sqlite__step_done(stmt));
8433     }
8434 
8435   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8436                                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8437   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8438                             wcroot->wc_id, local_relpath, delete_op_depth));
8439   SVN_ERR(svn_sqlite__step_done(stmt));
8440 
8441   /* Delete ACTUAL_NODE rows, but leave those that have changelist
8442      and a NODES row. */
8443   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8444                          STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8445   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8446                             wcroot->wc_id, local_relpath));
8447   SVN_ERR(svn_sqlite__step_done(stmt));
8448 
8449   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8450                          STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8451   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8452                             wcroot->wc_id, local_relpath));
8453   SVN_ERR(svn_sqlite__step_done(stmt));
8454 
8455   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8456                                     STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8457   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8458                             local_relpath));
8459   SVN_ERR(svn_sqlite__step_done(stmt));
8460 
8461   if (add_work)
8462     {
8463       /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8464 
8465       /* Delete the node and possible descendants. */
8466       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8467                                  STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8468       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8469                                 wcroot->wc_id, local_relpath,
8470                                 keep_op_depth, delete_op_depth));
8471       SVN_ERR(svn_sqlite__step_done(stmt));
8472     }
8473 
8474   if (moved_nodes)
8475     {
8476       int i;
8477 
8478       for (i = 0; i < moved_nodes->nelts; ++i)
8479         {
8480           const struct moved_node_t *moved_node
8481             = APR_ARRAY_IDX(moved_nodes, i, void *);
8482 
8483           SVN_ERR(delete_update_movedto(wcroot,
8484                                         moved_node->local_relpath,
8485                                         moved_node->op_depth,
8486                                         moved_node->moved_to_relpath,
8487                                         scratch_pool));
8488         }
8489     }
8490 
8491   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8492                                     STMT_DELETE_FILE_EXTERNALS));
8493   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8494   SVN_ERR(svn_sqlite__step_done(stmt));
8495 
8496   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8497                                     b->delete_dir_externals
8498                                     ? STMT_DELETE_EXTERNAL_REGISTATIONS
8499                                     : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8500   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8501   SVN_ERR(svn_sqlite__step_done(stmt));
8502 
8503   SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8504   if (b->conflict)
8505     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8506                                               b->conflict, scratch_pool));
8507 
8508   return SVN_NO_ERROR;
8509 }
8510 
8511 static svn_error_t *
op_delete_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)8512 op_delete_txn(void *baton,
8513               svn_wc__db_wcroot_t *wcroot,
8514               const char *local_relpath,
8515               apr_pool_t *scratch_pool)
8516 {
8517 
8518   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8519   SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8520   return SVN_NO_ERROR;
8521 }
8522 
8523 
8524 struct op_delete_many_baton_t {
8525   apr_array_header_t *rel_targets;
8526   svn_boolean_t delete_dir_externals;
8527   const svn_skel_t *work_items;
8528 };
8529 
8530 static svn_error_t *
op_delete_many_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)8531 op_delete_many_txn(void *baton,
8532                    svn_wc__db_wcroot_t *wcroot,
8533                    const char *local_relpath,
8534                    apr_pool_t *scratch_pool)
8535 {
8536   struct op_delete_many_baton_t *odmb = baton;
8537   struct op_delete_baton_t odb;
8538   int i;
8539   apr_pool_t *iterpool;
8540 
8541   odb.moved_to_relpath = NULL;
8542   odb.conflict = NULL;
8543   odb.work_items = NULL;
8544   odb.delete_dir_externals = odmb->delete_dir_externals;
8545   odb.notify = TRUE;
8546 
8547   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8548   iterpool = svn_pool_create(scratch_pool);
8549   for (i = 0; i < odmb->rel_targets->nelts; i++)
8550     {
8551       const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8552                                                  const char *);
8553 
8554 
8555       svn_pool_clear(iterpool);
8556       SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8557     }
8558   svn_pool_destroy(iterpool);
8559 
8560   SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8561 
8562   return SVN_NO_ERROR;
8563 }
8564 
8565 
8566 static svn_error_t *
do_delete_notify(void * baton,svn_wc__db_wcroot_t * wcroot,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)8567 do_delete_notify(void *baton,
8568                  svn_wc__db_wcroot_t *wcroot,
8569                  svn_cancel_func_t cancel_func,
8570                  void *cancel_baton,
8571                  svn_wc_notify_func2_t notify_func,
8572                  void *notify_baton,
8573                  apr_pool_t *scratch_pool)
8574 {
8575   svn_sqlite__stmt_t *stmt;
8576   svn_boolean_t have_row;
8577   apr_pool_t *iterpool;
8578 
8579   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8580                                     STMT_SELECT_DELETE_LIST));
8581   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8582 
8583   iterpool = svn_pool_create(scratch_pool);
8584   while (have_row)
8585     {
8586       const char *notify_relpath;
8587       const char *notify_abspath;
8588 
8589       svn_pool_clear(iterpool);
8590 
8591       notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8592       notify_abspath = svn_dirent_join(wcroot->abspath,
8593                                        notify_relpath,
8594                                        iterpool);
8595 
8596       notify_func(notify_baton,
8597                   svn_wc_create_notify(notify_abspath,
8598                                        svn_wc_notify_delete,
8599                                        iterpool),
8600                   iterpool);
8601 
8602       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8603     }
8604   svn_pool_destroy(iterpool);
8605 
8606   SVN_ERR(svn_sqlite__reset(stmt));
8607 
8608   /* We only allow cancellation after notification for all deleted nodes
8609    * has happened. The nodes are already deleted so we should notify for
8610    * all of them. */
8611   if (cancel_func)
8612     SVN_ERR(cancel_func(cancel_baton));
8613 
8614   return SVN_NO_ERROR;
8615 }
8616 
8617 
8618 svn_error_t *
svn_wc__db_op_delete(svn_wc__db_t * db,const char * local_abspath,const char * moved_to_abspath,svn_boolean_t delete_dir_externals,svn_skel_t * conflict,svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)8619 svn_wc__db_op_delete(svn_wc__db_t *db,
8620                      const char *local_abspath,
8621                      const char *moved_to_abspath,
8622                      svn_boolean_t delete_dir_externals,
8623                      svn_skel_t *conflict,
8624                      svn_skel_t *work_items,
8625                      svn_cancel_func_t cancel_func,
8626                      void *cancel_baton,
8627                      svn_wc_notify_func2_t notify_func,
8628                      void *notify_baton,
8629                      apr_pool_t *scratch_pool)
8630 {
8631   svn_wc__db_wcroot_t *wcroot;
8632   svn_wc__db_wcroot_t *moved_to_wcroot;
8633   const char *local_relpath;
8634   const char *moved_to_relpath;
8635   struct op_delete_baton_t odb;
8636 
8637   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8638 
8639   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8640                                                 db, local_abspath,
8641                                                 scratch_pool, scratch_pool));
8642   VERIFY_USABLE_WCROOT(wcroot);
8643 
8644   if (moved_to_abspath)
8645     {
8646       SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8647                                                     &moved_to_relpath,
8648                                                     db, moved_to_abspath,
8649                                                     scratch_pool,
8650                                                     scratch_pool));
8651       VERIFY_USABLE_WCROOT(moved_to_wcroot);
8652 
8653       if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8654         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8655                                  _("Cannot move '%s' to '%s' because they "
8656                                    "are not in the same working copy"),
8657                                  svn_dirent_local_style(local_abspath,
8658                                                         scratch_pool),
8659                                  svn_dirent_local_style(moved_to_abspath,
8660                                                         scratch_pool));
8661     }
8662   else
8663     moved_to_relpath = NULL;
8664 
8665   odb.moved_to_relpath = moved_to_relpath;
8666   odb.conflict = conflict;
8667   odb.work_items = work_items;
8668   odb.delete_dir_externals = delete_dir_externals;
8669 
8670   if (notify_func)
8671     {
8672       /* Perform the deletion operation (transactionally), perform any
8673          notifications necessary, and then clean out our temporary tables.  */
8674       odb.notify = TRUE;
8675       SVN_ERR(with_finalization(wcroot, local_relpath,
8676                                 op_delete_txn, &odb,
8677                                 do_delete_notify, NULL,
8678                                 cancel_func, cancel_baton,
8679                                 notify_func, notify_baton,
8680                                 STMT_FINALIZE_DELETE,
8681                                 scratch_pool));
8682     }
8683   else
8684     {
8685       /* Avoid the trigger work */
8686       odb.notify = FALSE;
8687       SVN_WC__DB_WITH_TXN(
8688                     delete_node(&odb, wcroot, local_relpath, scratch_pool),
8689                     wcroot);
8690     }
8691 
8692   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8693                         scratch_pool));
8694 
8695   return SVN_NO_ERROR;
8696 }
8697 
8698 
8699 svn_error_t *
svn_wc__db_op_delete_many(svn_wc__db_t * db,apr_array_header_t * targets,svn_boolean_t delete_dir_externals,const svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)8700 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8701                           apr_array_header_t *targets,
8702                           svn_boolean_t delete_dir_externals,
8703                           const svn_skel_t *work_items,
8704                           svn_cancel_func_t cancel_func,
8705                           void *cancel_baton,
8706                           svn_wc_notify_func2_t notify_func,
8707                           void *notify_baton,
8708                           apr_pool_t *scratch_pool)
8709 {
8710   svn_wc__db_wcroot_t *wcroot;
8711   const char *local_relpath;
8712   struct op_delete_many_baton_t odmb;
8713   int i;
8714   apr_pool_t *iterpool;
8715 
8716   odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8717                                     sizeof(const char *));
8718   odmb.work_items = work_items;
8719   odmb.delete_dir_externals = delete_dir_externals;
8720   iterpool = svn_pool_create(scratch_pool);
8721   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8722                                                 db,
8723                                                 APR_ARRAY_IDX(targets, 0,
8724                                                               const char *),
8725                                                 scratch_pool, iterpool));
8726   VERIFY_USABLE_WCROOT(wcroot);
8727   for (i = 0; i < targets->nelts; i++)
8728     {
8729       const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8730       svn_wc__db_wcroot_t *target_wcroot;
8731 
8732       svn_pool_clear(iterpool);
8733 
8734       SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8735                                                     &local_relpath, db,
8736                                                     APR_ARRAY_IDX(targets, i,
8737                                                                   const char *),
8738                                                     scratch_pool, iterpool));
8739       VERIFY_USABLE_WCROOT(target_wcroot);
8740       SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8741 
8742       /* Assert that all targets are within the same working copy. */
8743       SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8744 
8745       APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8746       SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8747                             iterpool));
8748 
8749     }
8750   svn_pool_destroy(iterpool);
8751 
8752   /* Perform the deletion operation (transactionally), perform any
8753      notifications necessary, and then clean out our temporary tables.  */
8754   return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8755                                            op_delete_many_txn, &odmb,
8756                                            do_delete_notify, NULL,
8757                                            cancel_func, cancel_baton,
8758                                            notify_func, notify_baton,
8759                                            STMT_FINALIZE_DELETE,
8760                                            scratch_pool));
8761 }
8762 
8763 /* Helper function for read_info() to provide better diagnostics than just
8764    asserting.
8765 
8766    ### BH: Yes this code is ugly, and that is why I only introduce it in
8767    ### read_info(). But we really need something to determine the root cause
8768    ### of this problem to diagnose why TortoiseSVN users were seeing all those
8769    ### assertions.
8770 
8771    Adds an error to the *err chain if invalid values are encountered. In that
8772    case the value is set to the first value in the map, assuming that caller
8773    will just return the combined error.
8774  */
8775 static int
column_token_err(svn_error_t ** err,svn_sqlite__stmt_t * stmt,int column,const svn_token_map_t * map)8776 column_token_err(svn_error_t **err,
8777                  svn_sqlite__stmt_t *stmt,
8778                  int column,
8779                  const svn_token_map_t *map)
8780 {
8781   svn_error_t *err2;
8782   const char *word = svn_sqlite__column_text(stmt, column, NULL);
8783   int value;
8784 
8785   /* svn_token__from_word_err() handles NULL for us */
8786   err2 = svn_token__from_word_err(&value, map, word);
8787 
8788   if (err2)
8789     {
8790       *err = svn_error_compose_create(
8791                 *err,
8792                 svn_error_createf(
8793                     SVN_ERR_WC_CORRUPT, err2,
8794                     _("Encountered invalid node state in column %d of "
8795                       "info query to working copy database"),
8796                     column));
8797       value = map[0].val;
8798     }
8799 
8800   return value;
8801 }
8802 
8803 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8804    DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8805 static svn_error_t *
read_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,svn_wc__db_lock_t ** lock,svn_filesize_t * recorded_size,apr_time_t * recorded_time,const char ** changelist,svn_boolean_t * conflicted,svn_boolean_t * op_root,svn_boolean_t * had_props,svn_boolean_t * props_mod,svn_boolean_t * have_base,svn_boolean_t * have_more_work,svn_boolean_t * have_work,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8806 read_info(svn_wc__db_status_t *status,
8807           svn_node_kind_t *kind,
8808           svn_revnum_t *revision,
8809           const char **repos_relpath,
8810           apr_int64_t *repos_id,
8811           svn_revnum_t *changed_rev,
8812           apr_time_t *changed_date,
8813           const char **changed_author,
8814           svn_depth_t *depth,
8815           const svn_checksum_t **checksum,
8816           const char **target,
8817           const char **original_repos_relpath,
8818           apr_int64_t *original_repos_id,
8819           svn_revnum_t *original_revision,
8820           svn_wc__db_lock_t **lock,
8821           svn_filesize_t *recorded_size,
8822           apr_time_t *recorded_time,
8823           const char **changelist,
8824           svn_boolean_t *conflicted,
8825           svn_boolean_t *op_root,
8826           svn_boolean_t *had_props,
8827           svn_boolean_t *props_mod,
8828           svn_boolean_t *have_base,
8829           svn_boolean_t *have_more_work,
8830           svn_boolean_t *have_work,
8831           svn_wc__db_wcroot_t *wcroot,
8832           const char *local_relpath,
8833           apr_pool_t *result_pool,
8834           apr_pool_t *scratch_pool)
8835 {
8836   svn_sqlite__stmt_t *stmt_info;
8837   svn_sqlite__stmt_t *stmt_act;
8838   svn_boolean_t have_info;
8839   svn_boolean_t have_act;
8840   svn_error_t *err = NULL;
8841 
8842   /* Obtain the most likely to exist record first, to make sure we don't
8843      have to obtain the SQLite read-lock multiple times */
8844   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8845                                     lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8846                                          : STMT_SELECT_NODE_INFO));
8847   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8848   SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8849 
8850   if (changelist || conflicted || props_mod)
8851     {
8852       SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8853                                         STMT_SELECT_ACTUAL_NODE));
8854       SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8855       SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8856     }
8857   else
8858     {
8859       have_act = FALSE;
8860       stmt_act = NULL;
8861     }
8862 
8863   if (have_info)
8864     {
8865       int op_depth;
8866       svn_node_kind_t node_kind;
8867 
8868       op_depth = svn_sqlite__column_int(stmt_info, 0);
8869       node_kind = column_token_err(&err, stmt_info, 4, kind_map);
8870 
8871       if (status)
8872         {
8873           *status = column_token_err(&err, stmt_info, 3, presence_map);
8874 
8875           if (op_depth != 0) /* WORKING */
8876             err = svn_error_compose_create(err,
8877                                            convert_to_working_status(status,
8878                                                                      *status));
8879         }
8880       if (kind)
8881         {
8882           *kind = node_kind;
8883         }
8884       if (op_depth != 0)
8885         {
8886           if (repos_id)
8887             *repos_id = INVALID_REPOS_ID;
8888           if (revision)
8889             *revision = SVN_INVALID_REVNUM;
8890           if (repos_relpath)
8891             /* Our path is implied by our parent somewhere up the tree.
8892                With the NULL value and status, the caller will know to
8893                search up the tree for the base of our path.  */
8894             *repos_relpath = NULL;
8895         }
8896       else
8897         {
8898           /* Fetch repository information. If we have a
8899              WORKING_NODE (and have been added), then the repository
8900              we're being added to will be dependent upon a parent. The
8901              caller can scan upwards to locate the repository.  */
8902           repos_location_from_columns(repos_id, revision, repos_relpath,
8903                                       stmt_info, 1, 5, 2, result_pool);
8904         }
8905       if (changed_rev)
8906         {
8907           *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8908         }
8909       if (changed_date)
8910         {
8911           *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8912         }
8913       if (changed_author)
8914         {
8915           *changed_author = svn_sqlite__column_text(stmt_info, 10,
8916                                                     result_pool);
8917         }
8918       if (recorded_time)
8919         {
8920           *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8921         }
8922       if (depth)
8923         {
8924           if (node_kind != svn_node_dir)
8925             *depth = svn_depth_unknown;
8926           else if (svn_sqlite__column_is_null(stmt_info, 11))
8927             *depth = svn_depth_unknown;
8928           else
8929             *depth = column_token_err(&err, stmt_info, 11, depth_map);
8930         }
8931       if (checksum)
8932         {
8933           if (node_kind != svn_node_file)
8934             {
8935               *checksum = NULL;
8936             }
8937           else
8938             {
8939 
8940               err = svn_error_compose_create(
8941                         err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8942                                                          result_pool));
8943             }
8944         }
8945       if (recorded_size)
8946         {
8947           *recorded_size = get_recorded_size(stmt_info, 7);
8948         }
8949       if (target)
8950         {
8951           if (node_kind != svn_node_symlink)
8952             *target = NULL;
8953           else
8954             *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8955         }
8956       if (changelist)
8957         {
8958           if (have_act)
8959             *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8960           else
8961             *changelist = NULL;
8962         }
8963       if (op_depth == 0)
8964         {
8965           if (original_repos_id)
8966             *original_repos_id = INVALID_REPOS_ID;
8967           if (original_revision)
8968             *original_revision = SVN_INVALID_REVNUM;
8969           if (original_repos_relpath)
8970             *original_repos_relpath = NULL;
8971         }
8972       else
8973         {
8974           repos_location_from_columns(original_repos_id,
8975                                       original_revision,
8976                                       original_repos_relpath,
8977                                       stmt_info, 1, 5, 2, result_pool);
8978         }
8979       if (props_mod)
8980         {
8981           *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8982         }
8983       if (had_props)
8984         {
8985           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8986         }
8987       if (conflicted)
8988         {
8989           if (have_act)
8990             {
8991               *conflicted =
8992                  !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8993             }
8994           else
8995             *conflicted = FALSE;
8996         }
8997 
8998       if (lock)
8999         {
9000           if (op_depth != 0)
9001             *lock = NULL;
9002           else
9003             *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
9004         }
9005 
9006       if (have_work)
9007         *have_work = (op_depth != 0);
9008 
9009       if (op_root)
9010         {
9011           *op_root = ((op_depth > 0)
9012                       && (op_depth == relpath_depth(local_relpath)));
9013         }
9014 
9015       if (have_base || have_more_work)
9016         {
9017           if (have_more_work)
9018             *have_more_work = FALSE;
9019 
9020           while (!err && op_depth != 0)
9021             {
9022               err = svn_sqlite__step(&have_info, stmt_info);
9023 
9024               if (err || !have_info)
9025                 break;
9026 
9027               op_depth = svn_sqlite__column_int(stmt_info, 0);
9028 
9029               if (have_more_work)
9030                 {
9031                   if (op_depth > 0)
9032                     *have_more_work = TRUE;
9033 
9034                   if (!have_base)
9035                    break;
9036                 }
9037             }
9038 
9039           if (have_base)
9040             *have_base = (op_depth == 0);
9041         }
9042     }
9043   else if (have_act)
9044     {
9045       /* A row in ACTUAL_NODE should never exist without a corresponding
9046          node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
9047       if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
9048           err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
9049                                   _("Corrupt data for '%s'"),
9050                                   path_for_error_message(wcroot, local_relpath,
9051                                                          scratch_pool));
9052       /* ### What should we return?  Should we have a separate
9053              function for reading actual-only nodes? */
9054 
9055       /* As a safety measure, until we decide if we want to use
9056          read_info for actual-only nodes, make sure the caller asked
9057          for the conflict status. */
9058       SVN_ERR_ASSERT(conflicted);
9059 
9060       if (status)
9061         *status = svn_wc__db_status_normal;  /* What! No it's not! */
9062       if (kind)
9063         *kind = svn_node_unknown;
9064       if (revision)
9065         *revision = SVN_INVALID_REVNUM;
9066       if (repos_relpath)
9067         *repos_relpath = NULL;
9068       if (repos_id)
9069         *repos_id = INVALID_REPOS_ID;
9070       if (changed_rev)
9071         *changed_rev = SVN_INVALID_REVNUM;
9072       if (changed_date)
9073         *changed_date = 0;
9074       if (depth)
9075         *depth = svn_depth_unknown;
9076       if (checksum)
9077         *checksum = NULL;
9078       if (target)
9079         *target = NULL;
9080       if (original_repos_relpath)
9081         *original_repos_relpath = NULL;
9082       if (original_repos_id)
9083         *original_repos_id = INVALID_REPOS_ID;
9084       if (original_revision)
9085         *original_revision = SVN_INVALID_REVNUM;
9086       if (lock)
9087         *lock = NULL;
9088       if (recorded_size)
9089         *recorded_size = 0;
9090       if (recorded_time)
9091         *recorded_time = 0;
9092       if (changelist)
9093         *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
9094       if (op_root)
9095         *op_root = FALSE;
9096       if (had_props)
9097         *had_props = FALSE;
9098       if (props_mod)
9099         *props_mod = FALSE;
9100       if (conflicted)
9101         *conflicted = TRUE;
9102       if (have_base)
9103         *have_base = FALSE;
9104       if (have_more_work)
9105         *have_more_work = FALSE;
9106       if (have_work)
9107         *have_work = FALSE;
9108     }
9109   else
9110     {
9111       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9112                               _("The node '%s' was not found."),
9113                               path_for_error_message(wcroot, local_relpath,
9114                                                      scratch_pool));
9115     }
9116 
9117   if (stmt_act != NULL)
9118     err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
9119 
9120   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
9121     err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
9122                                 local_relpath);
9123 
9124   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
9125 
9126   return SVN_NO_ERROR;
9127 }
9128 
9129 
9130 svn_error_t *
svn_wc__db_read_info_internal(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,svn_wc__db_lock_t ** lock,svn_filesize_t * recorded_size,apr_time_t * recorded_time,const char ** changelist,svn_boolean_t * conflicted,svn_boolean_t * op_root,svn_boolean_t * had_props,svn_boolean_t * props_mod,svn_boolean_t * have_base,svn_boolean_t * have_more_work,svn_boolean_t * have_work,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9131 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
9132                               svn_node_kind_t *kind,
9133                               svn_revnum_t *revision,
9134                               const char **repos_relpath,
9135                               apr_int64_t *repos_id,
9136                               svn_revnum_t *changed_rev,
9137                               apr_time_t *changed_date,
9138                               const char **changed_author,
9139                               svn_depth_t *depth,
9140                               const svn_checksum_t **checksum,
9141                               const char **target,
9142                               const char **original_repos_relpath,
9143                               apr_int64_t *original_repos_id,
9144                               svn_revnum_t *original_revision,
9145                               svn_wc__db_lock_t **lock,
9146                               svn_filesize_t *recorded_size,
9147                               apr_time_t *recorded_time,
9148                               const char **changelist,
9149                               svn_boolean_t *conflicted,
9150                               svn_boolean_t *op_root,
9151                               svn_boolean_t *had_props,
9152                               svn_boolean_t *props_mod,
9153                               svn_boolean_t *have_base,
9154                               svn_boolean_t *have_more_work,
9155                               svn_boolean_t *have_work,
9156                               svn_wc__db_wcroot_t *wcroot,
9157                               const char *local_relpath,
9158                               apr_pool_t *result_pool,
9159                               apr_pool_t *scratch_pool)
9160 {
9161   return svn_error_trace(
9162            read_info(status, kind, revision, repos_relpath, repos_id,
9163                      changed_rev, changed_date, changed_author,
9164                      depth, checksum, target, original_repos_relpath,
9165                      original_repos_id, original_revision, lock,
9166                      recorded_size, recorded_time, changelist, conflicted,
9167                      op_root, had_props, props_mod,
9168                      have_base, have_more_work, have_work,
9169                      wcroot, local_relpath, result_pool, scratch_pool));
9170 }
9171 
9172 
9173 svn_error_t *
svn_wc__db_read_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,const char ** original_repos_relpath,const char ** original_root_url,const char ** original_uuid,svn_revnum_t * original_revision,svn_wc__db_lock_t ** lock,svn_filesize_t * recorded_size,apr_time_t * recorded_time,const char ** changelist,svn_boolean_t * conflicted,svn_boolean_t * op_root,svn_boolean_t * have_props,svn_boolean_t * props_mod,svn_boolean_t * have_base,svn_boolean_t * have_more_work,svn_boolean_t * have_work,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9174 svn_wc__db_read_info(svn_wc__db_status_t *status,
9175                      svn_node_kind_t *kind,
9176                      svn_revnum_t *revision,
9177                      const char **repos_relpath,
9178                      const char **repos_root_url,
9179                      const char **repos_uuid,
9180                      svn_revnum_t *changed_rev,
9181                      apr_time_t *changed_date,
9182                      const char **changed_author,
9183                      svn_depth_t *depth,
9184                      const svn_checksum_t **checksum,
9185                      const char **target,
9186                      const char **original_repos_relpath,
9187                      const char **original_root_url,
9188                      const char **original_uuid,
9189                      svn_revnum_t *original_revision,
9190                      svn_wc__db_lock_t **lock,
9191                      svn_filesize_t *recorded_size,
9192                      apr_time_t *recorded_time,
9193                      const char **changelist,
9194                      svn_boolean_t *conflicted,
9195                      svn_boolean_t *op_root,
9196                      svn_boolean_t *have_props,
9197                      svn_boolean_t *props_mod,
9198                      svn_boolean_t *have_base,
9199                      svn_boolean_t *have_more_work,
9200                      svn_boolean_t *have_work,
9201                      svn_wc__db_t *db,
9202                      const char *local_abspath,
9203                      apr_pool_t *result_pool,
9204                      apr_pool_t *scratch_pool)
9205 {
9206   svn_wc__db_wcroot_t *wcroot;
9207   const char *local_relpath;
9208   apr_int64_t repos_id, original_repos_id;
9209 
9210   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9211 
9212   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9213                               local_abspath, scratch_pool, scratch_pool));
9214   VERIFY_USABLE_WCROOT(wcroot);
9215 
9216   SVN_WC__DB_WITH_TXN4(
9217           read_info(status, kind, revision, repos_relpath, &repos_id,
9218                     changed_rev, changed_date, changed_author,
9219                     depth, checksum, target, original_repos_relpath,
9220                     &original_repos_id, original_revision, lock,
9221                     recorded_size, recorded_time, changelist, conflicted,
9222                     op_root, have_props, props_mod,
9223                     have_base, have_more_work, have_work,
9224                     wcroot, local_relpath, result_pool, scratch_pool),
9225           svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
9226                                       wcroot, repos_id, result_pool),
9227           svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
9228                                       wcroot, original_repos_id,
9229                                       result_pool),
9230         SVN_NO_ERROR,
9231         wcroot);
9232 
9233   return SVN_NO_ERROR;
9234 }
9235 
9236 static svn_error_t *
9237 is_wclocked(svn_boolean_t *locked,
9238             svn_wc__db_wcroot_t *wcroot,
9239             const char *dir_relpath,
9240             apr_pool_t *scratch_pool);
9241 
9242 /* Helper for read_children_info and single variant */
9243 static svn_error_t *
find_conflict_descendants(svn_boolean_t * conflict_exists,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)9244 find_conflict_descendants(svn_boolean_t *conflict_exists,
9245                           svn_wc__db_wcroot_t *wcroot,
9246                           const char *local_relpath,
9247                           apr_pool_t *scratch_pool)
9248 {
9249   svn_sqlite__stmt_t *stmt;
9250 
9251   /* Only used on files, so certainly not wcroot*/
9252   assert(local_relpath[0] != '\0');
9253 
9254   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9255                                     STMT_FIND_CONFLICT_DESCENDANT));
9256 
9257   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9258   SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
9259 
9260   return svn_error_trace(svn_sqlite__reset(stmt));
9261 }
9262 
9263 /* What we really want to store about a node.  This relies on the
9264    offset of svn_wc__db_info_t being zero. */
9265 struct read_children_info_item_t
9266 {
9267   struct svn_wc__db_info_t info;
9268   int op_depth;
9269   int nr_layers;
9270   svn_boolean_t was_dir;
9271 };
9272 
9273 /* Implementation of svn_wc__db_read_children_info */
9274 static svn_error_t *
read_children_info(svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_hash_t * conflicts,apr_hash_t * nodes,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9275 read_children_info(svn_wc__db_wcroot_t *wcroot,
9276                    const char *dir_relpath,
9277                    apr_hash_t *conflicts,
9278                    apr_hash_t *nodes,
9279                    svn_boolean_t base_tree_only,
9280                    apr_pool_t *result_pool,
9281                    apr_pool_t *scratch_pool)
9282 {
9283   svn_sqlite__stmt_t *stmt;
9284   svn_boolean_t have_row;
9285   const char *repos_root_url = NULL;
9286   const char *repos_uuid = NULL;
9287   apr_int64_t last_repos_id = INVALID_REPOS_ID;
9288   const char *last_repos_root_url = NULL;
9289 
9290   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9291                                     (base_tree_only
9292                                      ? STMT_SELECT_BASE_NODE_CHILDREN_INFO
9293                                      : STMT_SELECT_NODE_CHILDREN_INFO)));
9294   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9295   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9296 
9297   while (have_row)
9298     {
9299       /* CHILD item points to what we have about the node. We only provide
9300          CHILD->item to our caller. */
9301       struct read_children_info_item_t *child_item;
9302       const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
9303       const char *name = svn_relpath_basename(child_relpath, NULL);
9304       svn_error_t *err;
9305       int op_depth;
9306       svn_boolean_t new_child;
9307 
9308       child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name));
9309       if (child_item)
9310         new_child = FALSE;
9311       else
9312         {
9313           child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9314           new_child = TRUE;
9315         }
9316 
9317       op_depth = svn_sqlite__column_int(stmt, 0);
9318 
9319       /* Do we have new or better information? */
9320       if (new_child)
9321         {
9322           struct svn_wc__db_info_t *child = &child_item->info;
9323           child_item->op_depth = op_depth;
9324 
9325           child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
9326 
9327           child->status = svn_sqlite__column_token(stmt, 3, presence_map);
9328           if (op_depth != 0)
9329             {
9330               if (child->status == svn_wc__db_status_incomplete)
9331                 child->incomplete = TRUE;
9332               err = convert_to_working_status(&child->status, child->status);
9333               if (err)
9334                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9335             }
9336 
9337           if (op_depth != 0)
9338             child->revnum = SVN_INVALID_REVNUM;
9339           else
9340             child->revnum = svn_sqlite__column_revnum(stmt, 5);
9341 
9342           if (op_depth != 0)
9343             child->repos_relpath = NULL;
9344           else
9345             child->repos_relpath = svn_sqlite__column_text(stmt, 2,
9346                                                            result_pool);
9347 
9348           if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
9349             {
9350               child->repos_root_url = NULL;
9351               child->repos_uuid = NULL;
9352             }
9353           else
9354             {
9355               apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
9356               if (!repos_root_url ||
9357                   (last_repos_id != INVALID_REPOS_ID &&
9358                    repos_id != last_repos_id))
9359                 {
9360                   last_repos_root_url = repos_root_url;
9361                   err = svn_wc__db_fetch_repos_info(&repos_root_url,
9362                                                     &repos_uuid,
9363                                                     wcroot, repos_id,
9364                                                     result_pool);
9365                   if (err)
9366                     SVN_ERR(svn_error_compose_create(err,
9367                                                  svn_sqlite__reset(stmt)));
9368                 }
9369 
9370               if (last_repos_id == INVALID_REPOS_ID)
9371                 last_repos_id = repos_id;
9372 
9373               /* Assume working copy is all one repos_id so that a
9374                  single cached value is sufficient. */
9375               if (repos_id != last_repos_id)
9376                 {
9377                   err= svn_error_createf(
9378                          SVN_ERR_WC_DB_ERROR, NULL,
9379                          _("The node '%s' comes from unexpected repository "
9380                            "'%s', expected '%s'; if this node is a file "
9381                            "external using the correct URL in the external "
9382                            "definition can fix the problem, see issue #4087"),
9383                          child_relpath, repos_root_url, last_repos_root_url);
9384                   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
9385                 }
9386               child->repos_root_url = repos_root_url;
9387               child->repos_uuid = repos_uuid;
9388             }
9389 
9390           child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
9391 
9392           child->changed_date = svn_sqlite__column_int64(stmt, 9);
9393 
9394           child->changed_author = svn_sqlite__column_text(stmt, 10,
9395                                                           result_pool);
9396 
9397           if (child->kind != svn_node_dir)
9398             child->depth = svn_depth_unknown;
9399           else
9400             {
9401               child->has_descendants = TRUE;
9402               child_item->was_dir = TRUE;
9403               child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9404                                                            svn_depth_unknown);
9405               if (new_child)
9406                 {
9407                   err = is_wclocked(&child->locked, wcroot, child_relpath,
9408                                     scratch_pool);
9409 
9410                   if (err)
9411                     SVN_ERR(svn_error_compose_create(err,
9412                                                      svn_sqlite__reset(stmt)));
9413                 }
9414             }
9415 
9416           child->recorded_time = svn_sqlite__column_int64(stmt, 13);
9417           child->recorded_size = get_recorded_size(stmt, 7);
9418           child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
9419           child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
9420           child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9421 #ifdef HAVE_SYMLINK
9422           if (child->had_props)
9423             {
9424               apr_hash_t *properties;
9425               err = svn_sqlite__column_properties(&properties, stmt, 14,
9426                                                   scratch_pool, scratch_pool);
9427               if (err)
9428                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9429 
9430               child->special = (child->had_props
9431                                 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
9432             }
9433 #endif
9434           if (op_depth == 0)
9435             child->op_root = FALSE;
9436           else
9437             child->op_root = (op_depth == relpath_depth(child_relpath));
9438 
9439           if (op_depth && child->op_root)
9440             child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
9441 
9442           if (new_child)
9443             svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
9444         }
9445       else if (!child_item->was_dir
9446                && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
9447         {
9448           child_item->was_dir = TRUE;
9449 
9450           err = find_conflict_descendants(&child_item->info.has_descendants,
9451                                           wcroot, child_relpath,
9452                                           scratch_pool);
9453           if (err)
9454             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9455         }
9456 
9457       if (op_depth == 0)
9458         {
9459           child_item->info.have_base = TRUE;
9460 
9461           /* Get the lock info, available only at op_depth 0. */
9462           child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
9463                                                     result_pool);
9464 
9465           /* FILE_EXTERNAL flag only on op_depth 0. */
9466           child_item->info.file_external = svn_sqlite__column_boolean(stmt,
9467                                                                       22);
9468         }
9469       else
9470         {
9471           const char *moved_to_relpath;
9472 
9473           child_item->nr_layers++;
9474           child_item->info.have_more_work = (child_item->nr_layers > 1);
9475 
9476 
9477           /* A local_relpath can be moved multiple times at different op
9478              depths and it really depends on the caller what is interesting.
9479              We provide a simple linked list with the moved_from information */
9480 
9481           moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
9482           if (moved_to_relpath)
9483             {
9484               struct svn_wc__db_moved_to_info_t *moved_to;
9485               struct svn_wc__db_moved_to_info_t **next;
9486               const char *shadow_op_relpath;
9487 
9488               moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
9489               moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9490                                                            moved_to_relpath,
9491                                                            result_pool);
9492 
9493               shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
9494                                                      scratch_pool);
9495 
9496               moved_to->shadow_op_root_abspath =
9497                         svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9498                                         result_pool);
9499 
9500               next = &child_item->info.moved_to;
9501 
9502               while (*next &&
9503                      0 < strcmp((*next)->shadow_op_root_abspath,
9504                                 moved_to->shadow_op_root_abspath))
9505                 next = &((*next)->next);
9506 
9507               moved_to->next = *next;
9508               *next = moved_to;
9509             }
9510         }
9511 
9512       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9513     }
9514 
9515   SVN_ERR(svn_sqlite__reset(stmt));
9516 
9517   if (!base_tree_only)
9518     {
9519       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9520                                         STMT_SELECT_ACTUAL_CHILDREN_INFO));
9521       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9522       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9523 
9524       while (have_row)
9525         {
9526           struct read_children_info_item_t *child_item;
9527           struct svn_wc__db_info_t *child;
9528           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9529           const char *name = svn_relpath_basename(child_relpath, NULL);
9530 
9531           child_item = svn_hash_gets(nodes, name);
9532           if (!child_item)
9533             {
9534               child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9535               child_item->info.status = svn_wc__db_status_not_present;
9536             }
9537 
9538           child = &child_item->info;
9539 
9540           child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9541 
9542           child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9543 #ifdef HAVE_SYMLINK
9544           if (child->props_mod)
9545             {
9546               svn_error_t *err;
9547               apr_hash_t *properties;
9548 
9549               err = svn_sqlite__column_properties(&properties, stmt, 2,
9550                                                   scratch_pool, scratch_pool);
9551               if (err)
9552                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9553               child->special = (NULL != svn_hash_gets(properties,
9554                                                       SVN_PROP_SPECIAL));
9555             }
9556 #endif
9557 
9558           /* conflict */
9559           child->conflicted = !svn_sqlite__column_is_null(stmt, 3);
9560 
9561           if (child->conflicted)
9562             svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9563 
9564           SVN_ERR(svn_sqlite__step(&have_row, stmt));
9565         }
9566 
9567       SVN_ERR(svn_sqlite__reset(stmt));
9568     }
9569 
9570   return SVN_NO_ERROR;
9571 }
9572 
9573 svn_error_t *
svn_wc__db_read_children_info(apr_hash_t ** nodes,apr_hash_t ** conflicts,svn_wc__db_t * db,const char * dir_abspath,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9574 svn_wc__db_read_children_info(apr_hash_t **nodes,
9575                               apr_hash_t **conflicts,
9576                               svn_wc__db_t *db,
9577                               const char *dir_abspath,
9578                               svn_boolean_t base_tree_only,
9579                               apr_pool_t *result_pool,
9580                               apr_pool_t *scratch_pool)
9581 {
9582   svn_wc__db_wcroot_t *wcroot;
9583   const char *dir_relpath;
9584 
9585   *conflicts = apr_hash_make(result_pool);
9586   *nodes = apr_hash_make(result_pool);
9587   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9588 
9589   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9590                                                 dir_abspath,
9591                                                 scratch_pool, scratch_pool));
9592   VERIFY_USABLE_WCROOT(wcroot);
9593 
9594   SVN_WC__DB_WITH_TXN(
9595     read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9596                        base_tree_only, result_pool, scratch_pool),
9597     wcroot);
9598 
9599   return SVN_NO_ERROR;
9600 }
9601 
9602 /* Implementation of svn_wc__db_read_single_info.
9603 
9604    ### This function is very similar to a lot of code inside
9605    read_children_info, but that performs some tricks to re-use data between
9606    different siblings.
9607 
9608    ### We read the same few NODES records a few times via different helper
9609    functions, so this could be optimized bit, but everything is within
9610    a sqlite transaction and all queries are backed by an index, so generally
9611    everything (including the used indexes) should be in the sqlite page cache
9612    after the first query.
9613 */
9614 static svn_error_t *
read_single_info(const struct svn_wc__db_info_t ** info,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9615 read_single_info(const struct svn_wc__db_info_t **info,
9616                  svn_wc__db_wcroot_t *wcroot,
9617                  const char *local_relpath,
9618                  svn_boolean_t base_tree_only,
9619                  apr_pool_t *result_pool,
9620                  apr_pool_t *scratch_pool)
9621 {
9622   struct svn_wc__db_info_t *mtb;
9623   apr_int64_t repos_id;
9624   const svn_checksum_t *checksum;
9625   const char *original_repos_relpath;
9626   svn_boolean_t have_work;
9627   apr_hash_t *properties;
9628 
9629   mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9630 
9631   if (!base_tree_only)
9632     SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9633                       &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9634                       &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9635                       &checksum, NULL, &original_repos_relpath, NULL, NULL,
9636                       &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9637                       &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9638                       &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9639                       &mtb->have_more_work, &have_work,
9640                       wcroot, local_relpath, result_pool, scratch_pool));
9641   else
9642     {
9643       svn_boolean_t update_root;
9644 
9645       have_work = FALSE;
9646       original_repos_relpath = NULL;
9647 
9648       SVN_ERR(svn_wc__db_base_get_info_internal(
9649                   &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath,
9650                   &repos_id, &mtb->changed_rev, &mtb->changed_date,
9651                   &mtb->changed_author, &mtb->depth, &checksum, NULL,
9652                   &mtb->lock, &mtb->had_props, &properties, &update_root,
9653                   wcroot, local_relpath, scratch_pool, scratch_pool));
9654 
9655       mtb->have_base = TRUE;
9656       mtb->file_external = (update_root && mtb->kind == svn_node_file);
9657     }
9658 
9659   /* Query the same rows in the database again for move information */
9660   if (have_work && (mtb->have_base || mtb->have_more_work))
9661     {
9662       svn_sqlite__stmt_t *stmt;
9663       svn_boolean_t have_row;
9664 
9665       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9666                                         STMT_SELECT_MOVED_TO_NODE));
9667       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9668 
9669       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9670 
9671       while (have_row)
9672         {
9673           struct svn_wc__db_moved_to_info_t *move;
9674           int op_depth = svn_sqlite__column_int(stmt, 0);
9675           const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9676           const char *cur_relpath;
9677 
9678           move = apr_pcalloc(result_pool, sizeof(*move));
9679           move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9680                                                    moved_to_relpath,
9681                                                    result_pool);
9682 
9683           cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
9684                                            scratch_pool);
9685 
9686           move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9687                                                          cur_relpath,
9688                                                          result_pool);
9689 
9690           move->next = mtb->moved_to;
9691           mtb->moved_to = move;
9692 
9693           SVN_ERR(svn_sqlite__step(&have_row, stmt));
9694         }
9695 
9696       SVN_ERR(svn_sqlite__reset(stmt));
9697     }
9698 
9699   /* Maybe we have to get some shadowed lock from BASE to make our test suite
9700      happy... (It might be completely unrelated, but...)
9701      This queries the same BASE row again, joined to the lock table */
9702   if (!base_tree_only && mtb->have_base
9703       && (have_work || mtb->kind == svn_node_file))
9704     {
9705       svn_boolean_t update_root;
9706       svn_wc__db_lock_t **lock_arg = NULL;
9707 
9708       if (have_work)
9709         lock_arg = &mtb->lock;
9710 
9711       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9712                                                 NULL, NULL, NULL, NULL, NULL,
9713                                                 NULL, lock_arg, NULL, NULL,
9714                                                 &update_root,
9715                                                 wcroot, local_relpath,
9716                                                 result_pool, scratch_pool));
9717 
9718       mtb->file_external = (update_root && mtb->kind == svn_node_file);
9719     }
9720 
9721   if (mtb->status == svn_wc__db_status_added)
9722     {
9723       svn_wc__db_status_t status;
9724 
9725       SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9726                             NULL, NULL,
9727                             wcroot, local_relpath,
9728                             result_pool, scratch_pool));
9729 
9730       mtb->moved_here = (status == svn_wc__db_status_moved_here);
9731       mtb->incomplete = (status == svn_wc__db_status_incomplete);
9732     }
9733 
9734 #ifdef HAVE_SYMLINK
9735   if (mtb->kind == svn_node_file
9736       && (mtb->had_props || mtb->props_mod
9737           || (base_tree_only && properties)))
9738     {
9739       if (!base_tree_only)
9740         {
9741           if (mtb->props_mod)
9742             SVN_ERR(svn_wc__db_read_props_internal(&properties,
9743                                                    wcroot, local_relpath,
9744                                                    scratch_pool, scratch_pool));
9745           else
9746             SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9747                                            TRUE /* deleted_ok */,
9748                                            scratch_pool, scratch_pool));
9749         }
9750 
9751       mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9752     }
9753 #endif
9754 
9755   mtb->has_checksum = (checksum != NULL);
9756   mtb->copied = (original_repos_relpath != NULL);
9757 
9758   SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9759                                       wcroot, repos_id, result_pool));
9760 
9761   if (!base_tree_only && mtb->kind == svn_node_dir)
9762     SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9763 
9764   if (mtb->kind == svn_node_dir)
9765     mtb->has_descendants = TRUE;
9766   else
9767     SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
9768                                       wcroot, local_relpath, scratch_pool));
9769 
9770   *info = mtb;
9771 
9772   return SVN_NO_ERROR;
9773 }
9774 
9775 svn_error_t *
svn_wc__db_read_single_info(const struct svn_wc__db_info_t ** info,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9776 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9777                             svn_wc__db_t *db,
9778                             const char *local_abspath,
9779                             svn_boolean_t base_tree_only,
9780                             apr_pool_t *result_pool,
9781                             apr_pool_t *scratch_pool)
9782 {
9783   svn_wc__db_wcroot_t *wcroot;
9784   const char *local_relpath;
9785 
9786   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9787 
9788   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9789                                                 local_abspath,
9790                                                 scratch_pool, scratch_pool));
9791   VERIFY_USABLE_WCROOT(wcroot);
9792 
9793   SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9794                                        base_tree_only,
9795                                        result_pool, scratch_pool),
9796                       wcroot);
9797 
9798   return SVN_NO_ERROR;
9799 }
9800 
9801 svn_error_t *
svn_wc__db_read_pristine_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_boolean_t * had_props,apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9802 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9803                               svn_node_kind_t *kind,
9804                               svn_revnum_t *changed_rev,
9805                               apr_time_t *changed_date,
9806                               const char **changed_author,
9807                               svn_depth_t *depth,  /* dirs only */
9808                               const svn_checksum_t **checksum, /* files only */
9809                               const char **target, /* symlinks only */
9810                               svn_boolean_t *had_props,
9811                               apr_hash_t **props,
9812                               svn_wc__db_t *db,
9813                               const char *local_abspath,
9814                               apr_pool_t *result_pool,
9815                               apr_pool_t *scratch_pool)
9816 {
9817   svn_wc__db_wcroot_t *wcroot;
9818   const char *local_relpath;
9819   svn_sqlite__stmt_t *stmt;
9820   svn_boolean_t have_row;
9821   svn_error_t *err = NULL;
9822   int op_depth;
9823   svn_wc__db_status_t raw_status;
9824   svn_node_kind_t node_kind;
9825 
9826   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9827 
9828   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9829                                                 local_abspath,
9830                                                 scratch_pool, scratch_pool));
9831   VERIFY_USABLE_WCROOT(wcroot);
9832 
9833   /* Obtain the most likely to exist record first, to make sure we don't
9834      have to obtain the SQLite read-lock multiple times */
9835   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9836                                     STMT_SELECT_NODE_INFO));
9837   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9838   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9839 
9840   if (!have_row)
9841     {
9842       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9843                                svn_sqlite__reset(stmt),
9844                                _("The node '%s' was not found."),
9845                                path_for_error_message(wcroot,
9846                                                       local_relpath,
9847                                                       scratch_pool));
9848     }
9849 
9850   op_depth = svn_sqlite__column_int(stmt, 0);
9851   raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9852 
9853   if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9854     {
9855       SVN_ERR(svn_sqlite__step_row(stmt));
9856 
9857       op_depth = svn_sqlite__column_int(stmt, 0);
9858       raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9859     }
9860 
9861   node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9862 
9863   if (status)
9864     {
9865       if (op_depth > 0)
9866         {
9867           err = svn_error_compose_create(err,
9868                                          convert_to_working_status(
9869                                                     status,
9870                                                     raw_status));
9871         }
9872       else
9873         *status = raw_status;
9874     }
9875   if (kind)
9876     {
9877       *kind = node_kind;
9878     }
9879   if (changed_rev)
9880     {
9881       *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9882     }
9883   if (changed_date)
9884     {
9885       *changed_date = svn_sqlite__column_int64(stmt, 9);
9886     }
9887   if (changed_author)
9888     {
9889       *changed_author = svn_sqlite__column_text(stmt, 10,
9890                                                 result_pool);
9891     }
9892   if (depth)
9893     {
9894       if (node_kind != svn_node_dir)
9895         {
9896           *depth = svn_depth_unknown;
9897         }
9898       else
9899         {
9900           *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9901                                                  svn_depth_unknown);
9902         }
9903     }
9904   if (checksum)
9905     {
9906       if (node_kind != svn_node_file)
9907         {
9908           *checksum = NULL;
9909         }
9910       else
9911         {
9912           svn_error_t *err2;
9913           err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9914 
9915           if (err2 != NULL)
9916             {
9917               if (err)
9918                 err = svn_error_compose_create(
9919                          err,
9920                          svn_error_createf(
9921                                err->apr_err, err2,
9922                               _("The node '%s' has a corrupt checksum value."),
9923                               path_for_error_message(wcroot, local_relpath,
9924                                                      scratch_pool)));
9925               else
9926                 err = err2;
9927             }
9928         }
9929     }
9930   if (target)
9931     {
9932       if (node_kind != svn_node_symlink)
9933         *target = NULL;
9934       else
9935         *target = svn_sqlite__column_text(stmt, 12, result_pool);
9936     }
9937   if (had_props)
9938     {
9939       *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9940     }
9941   if (props)
9942     {
9943       if (raw_status == svn_wc__db_status_normal
9944           || raw_status == svn_wc__db_status_incomplete)
9945         {
9946           SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9947                                                 result_pool, scratch_pool));
9948           if (*props == NULL)
9949             *props = apr_hash_make(result_pool);
9950         }
9951       else
9952         {
9953           assert(svn_sqlite__column_is_null(stmt, 14));
9954           *props = NULL;
9955         }
9956     }
9957 
9958   return svn_error_trace(
9959             svn_error_compose_create(err,
9960                                      svn_sqlite__reset(stmt)));
9961 }
9962 
9963 svn_error_t *
svn_wc__db_read_children_walker_info(const apr_array_header_t ** items,svn_wc__db_t * db,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9964 svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
9965                                      svn_wc__db_t *db,
9966                                      const char *dir_abspath,
9967                                      apr_pool_t *result_pool,
9968                                      apr_pool_t *scratch_pool)
9969 {
9970   svn_wc__db_wcroot_t *wcroot;
9971   const char *dir_relpath;
9972   svn_sqlite__stmt_t *stmt;
9973   svn_boolean_t have_row;
9974   apr_array_header_t *nodes;
9975 
9976   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9977 
9978   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9979                                              dir_abspath,
9980                                              scratch_pool, scratch_pool));
9981   VERIFY_USABLE_WCROOT(wcroot);
9982 
9983   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9984                                     STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9985   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9986   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9987 
9988   nodes = apr_array_make(result_pool, 16,
9989                           sizeof(struct svn_wc__db_walker_info_t *));
9990   while (have_row)
9991     {
9992       struct svn_wc__db_walker_info_t *child;
9993       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9994       const char *name = svn_relpath_basename(child_relpath, result_pool);
9995       int op_depth = svn_sqlite__column_int(stmt, 1);
9996       svn_error_t *err;
9997 
9998       child = apr_palloc(result_pool, sizeof(*child));
9999       child->name = name;
10000       child->status = svn_sqlite__column_token(stmt, 2, presence_map);
10001       if (op_depth > 0)
10002         {
10003           err = convert_to_working_status(&child->status, child->status);
10004           if (err)
10005             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10006         }
10007       child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
10008 
10009       APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
10010 
10011       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10012     }
10013 
10014   SVN_ERR(svn_sqlite__reset(stmt));
10015 
10016   *items = nodes;
10017 
10018   return SVN_NO_ERROR;
10019 }
10020 
10021 svn_error_t *
svn_wc__db_read_node_install_info(const char ** wcroot_abspath,const svn_checksum_t ** sha1_checksum,apr_hash_t ** pristine_props,apr_time_t * changed_date,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10022 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
10023                                   const svn_checksum_t **sha1_checksum,
10024                                   apr_hash_t **pristine_props,
10025                                   apr_time_t *changed_date,
10026                                   svn_wc__db_t *db,
10027                                   const char *local_abspath,
10028                                   const char *wri_abspath,
10029                                   apr_pool_t *result_pool,
10030                                   apr_pool_t *scratch_pool)
10031 {
10032   svn_wc__db_wcroot_t *wcroot;
10033   const char *local_relpath;
10034   svn_sqlite__stmt_t *stmt;
10035   svn_error_t *err = NULL;
10036   svn_boolean_t have_row;
10037 
10038   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10039 
10040   if (!wri_abspath)
10041     wri_abspath = local_abspath;
10042 
10043   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10044                               wri_abspath, scratch_pool, scratch_pool));
10045   VERIFY_USABLE_WCROOT(wcroot);
10046 
10047   if (local_abspath != wri_abspath
10048       && strcmp(local_abspath, wri_abspath))
10049     {
10050       if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
10051         return svn_error_createf(
10052                     SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10053                     _("The node '%s' is not in working copy '%s'"),
10054                     svn_dirent_local_style(local_abspath, scratch_pool),
10055                     svn_dirent_local_style(wcroot->abspath, scratch_pool));
10056 
10057       local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
10058     }
10059 
10060   if (wcroot_abspath != NULL)
10061     *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
10062 
10063   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10064                                     STMT_SELECT_NODE_INFO));
10065 
10066   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10067 
10068   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10069 
10070   if (have_row)
10071     {
10072       if (sha1_checksum)
10073         err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
10074 
10075       if (!err && pristine_props)
10076         {
10077           err = svn_sqlite__column_properties(pristine_props, stmt, 14,
10078                                               result_pool, scratch_pool);
10079           /* Null means no props (assuming presence normal or incomplete). */
10080           if (*pristine_props == NULL)
10081             *pristine_props = apr_hash_make(result_pool);
10082         }
10083 
10084       if (changed_date)
10085         *changed_date = svn_sqlite__column_int64(stmt, 9);
10086     }
10087   else
10088     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10089                              svn_sqlite__reset(stmt),
10090                              _("The node '%s' is not installable"),
10091                              svn_dirent_local_style(local_abspath,
10092                                                     scratch_pool));
10093 
10094   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10095 
10096   return SVN_NO_ERROR;
10097 }
10098 
10099 
10100 
10101 /* The body of svn_wc__db_read_repos_info().
10102  */
10103 static svn_error_t *
db_read_repos_info(svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10104 db_read_repos_info(svn_revnum_t *revision,
10105                    const char **repos_relpath,
10106                    apr_int64_t *repos_id,
10107                    svn_wc__db_wcroot_t *wcroot,
10108                    const char *local_relpath,
10109                    apr_pool_t *result_pool,
10110                    apr_pool_t *scratch_pool)
10111 {
10112   svn_wc__db_status_t status;
10113 
10114   SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
10115                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10116                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10117                     NULL, NULL, NULL,
10118                     wcroot, local_relpath, result_pool, scratch_pool));
10119 
10120   if ((repos_relpath && !*repos_relpath)
10121       || (repos_id && *repos_id == INVALID_REPOS_ID))
10122     {
10123       if (status == svn_wc__db_status_added)
10124         {
10125           SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10126                                 NULL, NULL, NULL, NULL, NULL,
10127                                 wcroot, local_relpath,
10128                                 result_pool, scratch_pool));
10129         }
10130       else if (status == svn_wc__db_status_deleted)
10131         {
10132           const char *base_del_relpath;
10133           const char *work_del_relpath;
10134 
10135           SVN_ERR(scan_deletion(&base_del_relpath, NULL,
10136                                 &work_del_relpath,
10137                                 NULL, wcroot,
10138                                 local_relpath,
10139                                 scratch_pool,
10140                                 scratch_pool));
10141 
10142           if (work_del_relpath)
10143             {
10144               /* The parent of the WORKING delete, must be an addition */
10145               const char *work_relpath = NULL;
10146 
10147               /* work_del_relpath should not be NULL. However, we have
10148                * observed instances where that assumption was not met.
10149                * Bail out in that case instead of crashing with a segfault.
10150                */
10151               SVN_ERR_ASSERT(work_del_relpath != NULL);
10152               work_relpath = svn_relpath_dirname(work_del_relpath,
10153                                                  scratch_pool);
10154 
10155               SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
10156                                     NULL, NULL, NULL, NULL, NULL, NULL,
10157                                     wcroot, work_relpath,
10158                                     scratch_pool, scratch_pool));
10159 
10160               if (repos_relpath)
10161                 *repos_relpath = svn_relpath_join(
10162                                     *repos_relpath,
10163                                     svn_dirent_skip_ancestor(work_relpath,
10164                                                              local_relpath),
10165                                     result_pool);
10166             }
10167           else
10168             {
10169               SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
10170                                                         repos_relpath,
10171                                                         repos_id,
10172                                                         NULL, NULL, NULL,
10173                                                         NULL, NULL, NULL,
10174                                                         NULL, NULL, NULL, NULL,
10175                                                         wcroot,
10176                                                         base_del_relpath,
10177                                                         scratch_pool,
10178                                                         scratch_pool));
10179 
10180               if (repos_relpath)
10181                 *repos_relpath = svn_relpath_join(
10182                                     *repos_relpath,
10183                                     svn_dirent_skip_ancestor(base_del_relpath,
10184                                                              local_relpath),
10185                                     result_pool);
10186             }
10187         }
10188       else if (status == svn_wc__db_status_excluded)
10189         {
10190           const char *parent_relpath;
10191           const char *name;
10192 
10193           /* A BASE excluded would have had repository information, so
10194              we have a working exclude, which must be below an addition */
10195 
10196           svn_relpath_split(&parent_relpath, &name, local_relpath,
10197                             scratch_pool);
10198           SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10199                                 NULL, NULL, NULL, NULL, NULL,
10200                                 wcroot, parent_relpath,
10201                                 scratch_pool, scratch_pool));
10202 
10203           if (repos_relpath)
10204             *repos_relpath = svn_relpath_join(*repos_relpath, name,
10205                                               result_pool);
10206 
10207           return SVN_NO_ERROR;
10208         }
10209       else
10210         {
10211           /* All working statee are explicitly handled and all base statee
10212              have a repos_relpath */
10213           SVN_ERR_MALFUNCTION();
10214         }
10215     }
10216 
10217   return SVN_NO_ERROR;
10218 }
10219 
10220 
10221 svn_error_t *
svn_wc__db_read_repos_info(svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10222 svn_wc__db_read_repos_info(svn_revnum_t *revision,
10223                            const char **repos_relpath,
10224                            const char **repos_root_url,
10225                            const char **repos_uuid,
10226                            svn_wc__db_t *db,
10227                            const char *local_abspath,
10228                            apr_pool_t *result_pool,
10229                            apr_pool_t *scratch_pool)
10230 {
10231   svn_wc__db_wcroot_t *wcroot;
10232   const char *local_relpath;
10233   apr_int64_t repos_id = INVALID_REPOS_ID;
10234 
10235   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10236 
10237   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10238                                                 local_abspath,
10239                                                 scratch_pool, scratch_pool));
10240   VERIFY_USABLE_WCROOT(wcroot);
10241 
10242   SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
10243                                           (repos_root_url || repos_uuid)
10244                                             ? &repos_id : NULL,
10245                                           wcroot, local_relpath,
10246                                           result_pool, scratch_pool),
10247                        svn_wc__db_fetch_repos_info(repos_root_url,
10248                                                    repos_uuid,
10249                                                    wcroot, repos_id,
10250                                                    result_pool),
10251                        SVN_NO_ERROR, SVN_NO_ERROR,
10252                        wcroot);
10253 
10254   return SVN_NO_ERROR;
10255 }
10256 
10257 
10258 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
10259    a hash table mapping <tt>char *</tt> names onto svn_string_t *
10260    values for any properties of immediate or recursive child nodes of
10261    LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
10262    If FILES_ONLY is true, only report properties for file child nodes.
10263    Check for cancellation between calls of RECEIVER_FUNC.
10264 */
10265 typedef struct cache_props_baton_t
10266 {
10267   svn_depth_t depth;
10268   svn_boolean_t pristine;
10269   const apr_array_header_t *changelists;
10270   svn_cancel_func_t cancel_func;
10271   void *cancel_baton;
10272 } cache_props_baton_t;
10273 
10274 
10275 static svn_error_t *
cache_props_recursive(void * cb_baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)10276 cache_props_recursive(void *cb_baton,
10277                       svn_wc__db_wcroot_t *wcroot,
10278                       const char *local_relpath,
10279                       apr_pool_t *scratch_pool)
10280 {
10281   cache_props_baton_t *baton = cb_baton;
10282   svn_sqlite__stmt_t *stmt;
10283   int stmt_idx;
10284 
10285   SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
10286                                 baton->changelists, scratch_pool));
10287 
10288   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
10289                                       STMT_CREATE_TARGET_PROP_CACHE));
10290 
10291   if (baton->pristine)
10292     stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
10293   else
10294     stmt_idx = STMT_CACHE_TARGET_PROPS;
10295 
10296   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
10297   SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
10298   SVN_ERR(svn_sqlite__step_done(stmt));
10299 
10300   return SVN_NO_ERROR;
10301 }
10302 
10303 
10304 svn_error_t *
svn_wc__db_read_props_streamily(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,svn_boolean_t pristine,const apr_array_header_t * changelists,svn_wc__proplist_receiver_t receiver_func,void * receiver_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)10305 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
10306                                 const char *local_abspath,
10307                                 svn_depth_t depth,
10308                                 svn_boolean_t pristine,
10309                                 const apr_array_header_t *changelists,
10310                                 svn_wc__proplist_receiver_t receiver_func,
10311                                 void *receiver_baton,
10312                                 svn_cancel_func_t cancel_func,
10313                                 void *cancel_baton,
10314                                 apr_pool_t *scratch_pool)
10315 {
10316   svn_wc__db_wcroot_t *wcroot;
10317   const char *local_relpath;
10318   svn_sqlite__stmt_t *stmt;
10319   cache_props_baton_t baton;
10320   svn_boolean_t have_row;
10321   apr_pool_t *iterpool;
10322   svn_error_t *err = NULL;
10323 
10324   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10325   SVN_ERR_ASSERT(receiver_func);
10326   SVN_ERR_ASSERT((depth == svn_depth_files) ||
10327                  (depth == svn_depth_immediates) ||
10328                  (depth == svn_depth_infinity));
10329 
10330   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10331                                                 db, local_abspath,
10332                                                 scratch_pool, scratch_pool));
10333   VERIFY_USABLE_WCROOT(wcroot);
10334 
10335   baton.depth = depth;
10336   baton.pristine = pristine;
10337   baton.changelists = changelists;
10338   baton.cancel_func = cancel_func;
10339   baton.cancel_baton = cancel_baton;
10340 
10341   SVN_ERR(with_finalization(wcroot, local_relpath,
10342                             cache_props_recursive, &baton,
10343                             NULL, NULL,
10344                             cancel_func, cancel_baton,
10345                             NULL, NULL,
10346                             STMT_DROP_TARGETS_LIST,
10347                             scratch_pool));
10348 
10349   iterpool = svn_pool_create(scratch_pool);
10350 
10351   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10352                                     STMT_SELECT_ALL_TARGET_PROP_CACHE));
10353   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10354   while (!err && have_row)
10355     {
10356       apr_hash_t *props;
10357 
10358       svn_pool_clear(iterpool);
10359 
10360       SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
10361                                             iterpool));
10362 
10363       /* see if someone wants to cancel this operation. */
10364       if (cancel_func)
10365         err = cancel_func(cancel_baton);
10366 
10367       if (!err && props && apr_hash_count(props) != 0)
10368         {
10369           const char *child_relpath;
10370           const char *child_abspath;
10371 
10372           child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
10373           child_abspath = svn_dirent_join(wcroot->abspath,
10374                                           child_relpath, iterpool);
10375 
10376           err = receiver_func(receiver_baton, child_abspath, props, iterpool);
10377         }
10378 
10379       err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
10380     }
10381 
10382   err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
10383 
10384   svn_pool_destroy(iterpool);
10385 
10386   SVN_ERR(svn_error_compose_create(
10387                     err,
10388                     svn_sqlite__exec_statements(wcroot->sdb,
10389                                                 STMT_DROP_TARGET_PROP_CACHE)));
10390   return SVN_NO_ERROR;
10391 }
10392 
10393 
10394 /* Helper for svn_wc__db_read_props().
10395  */
10396 svn_error_t *
svn_wc__db_read_props_internal(apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10397 svn_wc__db_read_props_internal(apr_hash_t **props,
10398                                svn_wc__db_wcroot_t *wcroot,
10399                                const char *local_relpath,
10400                                apr_pool_t *result_pool,
10401                                apr_pool_t *scratch_pool)
10402 {
10403   svn_sqlite__stmt_t *stmt;
10404   svn_boolean_t have_row;
10405   svn_error_t *err = NULL;
10406 
10407   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10408                                     STMT_SELECT_ACTUAL_PROPS));
10409   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10410   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10411 
10412   if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10413     {
10414       err = svn_sqlite__column_properties(props, stmt, 0,
10415                                           result_pool, scratch_pool);
10416     }
10417   else
10418     have_row = FALSE;
10419 
10420   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10421 
10422   if (have_row)
10423     return SVN_NO_ERROR;
10424 
10425   /* No local changes. Return the pristine props for this node.  */
10426   SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
10427                                  result_pool, scratch_pool));
10428   if (*props == NULL)
10429     {
10430       /* Pristine properties are not defined for this node.
10431          ### we need to determine whether this node is in a state that
10432          ### allows for ACTUAL properties (ie. not deleted). for now,
10433          ### just say all nodes, no matter the state, have at least an
10434          ### empty set of props.  */
10435       *props = apr_hash_make(result_pool);
10436     }
10437 
10438   return SVN_NO_ERROR;
10439 }
10440 
10441 
10442 svn_error_t *
svn_wc__db_read_props(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10443 svn_wc__db_read_props(apr_hash_t **props,
10444                       svn_wc__db_t *db,
10445                       const char *local_abspath,
10446                       apr_pool_t *result_pool,
10447                       apr_pool_t *scratch_pool)
10448 {
10449   svn_wc__db_wcroot_t *wcroot;
10450   const char *local_relpath;
10451 
10452   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10453 
10454   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10455                               local_abspath, scratch_pool, scratch_pool));
10456   VERIFY_USABLE_WCROOT(wcroot);
10457 
10458   SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot,
10459                                                      local_relpath,
10460                                                      result_pool,
10461                                                      scratch_pool),
10462                       wcroot);
10463 
10464   return SVN_NO_ERROR;
10465 }
10466 
10467 
10468 static svn_error_t *
db_read_pristine_props(apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t deleted_ok,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10469 db_read_pristine_props(apr_hash_t **props,
10470                        svn_wc__db_wcroot_t *wcroot,
10471                        const char *local_relpath,
10472                        svn_boolean_t deleted_ok,
10473                        apr_pool_t *result_pool,
10474                        apr_pool_t *scratch_pool)
10475 {
10476   svn_sqlite__stmt_t *stmt;
10477   svn_boolean_t have_row;
10478   svn_wc__db_status_t presence;
10479 
10480   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
10481   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10482 
10483   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10484 
10485   if (!have_row)
10486     {
10487       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10488                                svn_sqlite__reset(stmt),
10489                                _("The node '%s' was not found."),
10490                                path_for_error_message(wcroot,
10491                                                       local_relpath,
10492                                                       scratch_pool));
10493     }
10494 
10495 
10496   /* Examine the presence: */
10497   presence = svn_sqlite__column_token(stmt, 1, presence_map);
10498 
10499   /* For "base-deleted", it is obvious the pristine props are located
10500      below the current node. Fetch the NODE from the next record. */
10501   if (presence == svn_wc__db_status_base_deleted && deleted_ok)
10502     {
10503       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10504 
10505       SVN_ERR_ASSERT(have_row);
10506 
10507       presence = svn_sqlite__column_token(stmt, 1, presence_map);
10508     }
10509 
10510   /* normal or copied: Fetch properties (during update we want
10511      properties for incomplete as well) */
10512   if (presence == svn_wc__db_status_normal
10513       || presence == svn_wc__db_status_incomplete)
10514     {
10515       svn_error_t *err;
10516 
10517       err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
10518                                           scratch_pool);
10519       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10520 
10521       if (!*props)
10522         *props = apr_hash_make(result_pool);
10523 
10524       return SVN_NO_ERROR;
10525     }
10526 
10527   return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
10528                            svn_sqlite__reset(stmt),
10529                            _("The node '%s' has a status that"
10530                              " has no properties."),
10531                            path_for_error_message(wcroot,
10532                                                   local_relpath,
10533                                                   scratch_pool));
10534 }
10535 
10536 
10537 svn_error_t *
svn_wc__db_read_pristine_props(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10538 svn_wc__db_read_pristine_props(apr_hash_t **props,
10539                                svn_wc__db_t *db,
10540                                const char *local_abspath,
10541                                apr_pool_t *result_pool,
10542                                apr_pool_t *scratch_pool)
10543 {
10544   svn_wc__db_wcroot_t *wcroot;
10545   const char *local_relpath;
10546 
10547   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10548 
10549   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10550                               local_abspath, scratch_pool, scratch_pool));
10551   VERIFY_USABLE_WCROOT(wcroot);
10552 
10553   SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10554                                  result_pool, scratch_pool));
10555   return SVN_NO_ERROR;
10556 }
10557 
10558 svn_error_t *
svn_wc__db_prop_retrieve_recursive(apr_hash_t ** values,svn_wc__db_t * db,const char * local_abspath,const char * propname,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10559 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10560                                    svn_wc__db_t *db,
10561                                    const char *local_abspath,
10562                                    const char *propname,
10563                                    apr_pool_t *result_pool,
10564                                    apr_pool_t *scratch_pool)
10565 {
10566   svn_wc__db_wcroot_t *wcroot;
10567   const char *local_relpath;
10568   svn_sqlite__stmt_t *stmt;
10569   svn_boolean_t have_row;
10570   apr_pool_t *iterpool;
10571 
10572   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10573 
10574   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10575                               local_abspath, scratch_pool, scratch_pool));
10576   VERIFY_USABLE_WCROOT(wcroot);
10577 
10578   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10579                                     STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10580   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10581 
10582   *values = apr_hash_make(result_pool);
10583 
10584   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10585   iterpool = svn_pool_create(scratch_pool);
10586   while (have_row)
10587   {
10588     apr_hash_t *node_props;
10589     svn_string_t *value;
10590 
10591     svn_pool_clear(iterpool);
10592 
10593     SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10594                                           iterpool, iterpool));
10595 
10596     value = (node_props
10597                 ? svn_hash_gets(node_props, propname)
10598                 : NULL);
10599 
10600     if (value)
10601       {
10602         svn_hash_sets(*values,
10603                       svn_dirent_join(wcroot->abspath,
10604                                       svn_sqlite__column_text(stmt, 1, NULL),
10605                                       result_pool),
10606                       svn_string_dup(value, result_pool));
10607       }
10608 
10609     SVN_ERR(svn_sqlite__step(&have_row, stmt));
10610   }
10611 
10612   svn_pool_destroy(iterpool);
10613 
10614   return svn_error_trace(svn_sqlite__reset(stmt));
10615 }
10616 
10617 /* The body of svn_wc__db_read_cached_iprops(). */
10618 static svn_error_t *
db_read_cached_iprops(apr_array_header_t ** iprops,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10619 db_read_cached_iprops(apr_array_header_t **iprops,
10620                       svn_wc__db_wcroot_t *wcroot,
10621                       const char *local_relpath,
10622                       apr_pool_t *result_pool,
10623                       apr_pool_t *scratch_pool)
10624 {
10625   svn_sqlite__stmt_t *stmt;
10626   svn_boolean_t have_row;
10627 
10628   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10629   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10630   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10631 
10632   if (!have_row)
10633     {
10634       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10635                                svn_sqlite__reset(stmt),
10636                                _("The node '%s' was not found."),
10637                                path_for_error_message(wcroot, local_relpath,
10638                                                       scratch_pool));
10639     }
10640 
10641   SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10642                                     result_pool, scratch_pool));
10643 
10644   SVN_ERR(svn_sqlite__reset(stmt));
10645 
10646   return SVN_NO_ERROR;
10647 }
10648 
10649 svn_error_t *
svn_wc__db_read_cached_iprops(apr_array_header_t ** iprops,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10650 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10651                               svn_wc__db_t *db,
10652                               const char *local_abspath,
10653                               apr_pool_t *result_pool,
10654                               apr_pool_t *scratch_pool)
10655 {
10656   svn_wc__db_wcroot_t *wcroot;
10657   const char *local_relpath;
10658 
10659   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10660 
10661   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10662                                                 db, local_abspath,
10663                                                 scratch_pool, scratch_pool));
10664   VERIFY_USABLE_WCROOT(wcroot);
10665 
10666   /* Don't use with_txn yet, as we perform just a single transaction */
10667   SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10668                                 result_pool, scratch_pool));
10669 
10670   if (!*iprops)
10671     {
10672       *iprops = apr_array_make(result_pool, 0,
10673                                sizeof(svn_prop_inherited_item_t *));
10674     }
10675 
10676   return SVN_NO_ERROR;
10677 }
10678 
10679 /* Remove all prop name value pairs from PROP_HASH where the property
10680    name is not PROPNAME. */
10681 static void
filter_unwanted_props(apr_hash_t * prop_hash,const char * propname,apr_pool_t * scratch_pool)10682 filter_unwanted_props(apr_hash_t *prop_hash,
10683                       const char * propname,
10684                       apr_pool_t *scratch_pool)
10685 {
10686   apr_hash_index_t *hi;
10687 
10688   for (hi = apr_hash_first(scratch_pool, prop_hash);
10689        hi;
10690        hi = apr_hash_next(hi))
10691     {
10692       const char *ipropname = apr_hash_this_key(hi);
10693 
10694       if (strcmp(ipropname, propname) != 0)
10695         svn_hash_sets(prop_hash, ipropname, NULL);
10696     }
10697   return;
10698 }
10699 
10700 /* Get the changed properties as stored in the ACTUAL table */
10701 static svn_error_t *
db_get_changed_props(apr_hash_t ** actual_props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10702 db_get_changed_props(apr_hash_t **actual_props,
10703                      svn_wc__db_wcroot_t *wcroot,
10704                      const char *local_relpath,
10705                      apr_pool_t *result_pool,
10706                      apr_pool_t *scratch_pool)
10707 {
10708   svn_sqlite__stmt_t *stmt;
10709   svn_boolean_t have_row;
10710   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10711                                 STMT_SELECT_ACTUAL_PROPS));
10712   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10713   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10714 
10715   if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10716     SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10717                                           result_pool, scratch_pool));
10718   else
10719     *actual_props = NULL; /* Cached when we read that record */
10720 
10721   return svn_error_trace(svn_sqlite__reset(stmt));
10722 }
10723 
10724 /* The body of svn_wc__db_read_inherited_props().  */
10725 static svn_error_t *
db_read_inherited_props(apr_array_header_t ** inherited_props,apr_hash_t ** actual_props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * propname,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10726 db_read_inherited_props(apr_array_header_t **inherited_props,
10727                         apr_hash_t **actual_props,
10728                         svn_wc__db_wcroot_t *wcroot,
10729                         const char *local_relpath,
10730                         const char *propname,
10731                         apr_pool_t *result_pool,
10732                         apr_pool_t *scratch_pool)
10733 {
10734   int i;
10735   apr_array_header_t *cached_iprops = NULL;
10736   apr_array_header_t *iprops;
10737   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10738   svn_sqlite__stmt_t *stmt;
10739   const char *relpath;
10740   const char *expected_parent_repos_relpath = NULL;
10741   const char *parent_relpath;
10742 
10743   iprops = apr_array_make(result_pool, 1,
10744                            sizeof(svn_prop_inherited_item_t *));
10745   *inherited_props = iprops;
10746 
10747   if (actual_props)
10748     *actual_props = NULL;
10749 
10750   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10751                                     STMT_SELECT_NODE_INFO));
10752 
10753   relpath = local_relpath;
10754 
10755   /* Walk up to the root of the WC looking for inherited properties.  When we
10756      reach the WC root also check for cached inherited properties. */
10757   for (relpath = local_relpath; relpath; relpath = parent_relpath)
10758     {
10759       svn_boolean_t have_row;
10760       int op_depth;
10761       svn_wc__db_status_t status;
10762       apr_hash_t *node_props;
10763 
10764       parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10765                                   : NULL;
10766 
10767       svn_pool_clear(iterpool);
10768 
10769       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10770 
10771       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10772 
10773       if (!have_row)
10774         return svn_error_createf(
10775                     SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10776                     _("The node '%s' was not found."),
10777                     path_for_error_message(wcroot, relpath,
10778                                            scratch_pool));
10779 
10780       op_depth = svn_sqlite__column_int(stmt, 0);
10781 
10782       status = svn_sqlite__column_token(stmt, 3, presence_map);
10783 
10784       if (status != svn_wc__db_status_normal
10785           && status != svn_wc__db_status_incomplete)
10786         return svn_error_createf(
10787                     SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10788                     _("The node '%s' has a status that has no properties."),
10789                     path_for_error_message(wcroot, relpath,
10790                                            scratch_pool));
10791 
10792       if (op_depth > 0)
10793         {
10794           /* WORKING node. Nothing to check */
10795         }
10796       else if (expected_parent_repos_relpath)
10797         {
10798           const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10799 
10800           if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10801             {
10802               /* The child of this node has a different parent than this node
10803                  (It is "switched"), so we can stop here. Note that switched
10804                  with the same parent is not interesting for us here. */
10805               SVN_ERR(svn_sqlite__reset(stmt));
10806               break;
10807             }
10808 
10809           expected_parent_repos_relpath =
10810               svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10811         }
10812       else
10813         {
10814           const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10815 
10816           expected_parent_repos_relpath =
10817               svn_relpath_dirname(repos_relpath, scratch_pool);
10818         }
10819 
10820       if (op_depth == 0
10821           && !svn_sqlite__column_is_null(stmt, 16))
10822         {
10823           /* The node contains a cache. No reason to look further */
10824           SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10825                                             result_pool, iterpool));
10826 
10827           parent_relpath = NULL; /* Stop after this */
10828         }
10829 
10830       SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10831                                             iterpool, iterpool));
10832 
10833       SVN_ERR(svn_sqlite__reset(stmt));
10834 
10835       /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10836          can inherit properties from it. */
10837       if (relpath != local_relpath)
10838         {
10839           apr_hash_t *changed_props;
10840 
10841           SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10842                                        result_pool, iterpool));
10843 
10844           if (changed_props)
10845             node_props = changed_props;
10846           else if (node_props)
10847             node_props = svn_prop_hash_dup(node_props, result_pool);
10848 
10849           if (node_props && apr_hash_count(node_props))
10850             {
10851               /* If we only want PROPNAME filter out any other properties. */
10852               if (propname)
10853                 filter_unwanted_props(node_props, propname, iterpool);
10854 
10855               if (apr_hash_count(node_props))
10856                 {
10857                   svn_prop_inherited_item_t *iprop_elt =
10858                     apr_pcalloc(result_pool,
10859                                 sizeof(svn_prop_inherited_item_t));
10860                   iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10861                                                            relpath,
10862                                                            result_pool);
10863 
10864                   iprop_elt->prop_hash = node_props;
10865                   /* Build the output array in depth-first order. */
10866                   svn_sort__array_insert(iprops, &iprop_elt, 0);
10867                 }
10868             }
10869         }
10870       else if (actual_props)
10871         {
10872           apr_hash_t *changed_props;
10873 
10874           SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10875                                        result_pool, iterpool));
10876 
10877           if (changed_props)
10878             *actual_props = changed_props;
10879           else if (node_props)
10880             *actual_props = svn_prop_hash_dup(node_props, result_pool);
10881         }
10882     }
10883 
10884   if (cached_iprops)
10885     {
10886       for (i = cached_iprops->nelts - 1; i >= 0; i--)
10887         {
10888           svn_prop_inherited_item_t *cached_iprop =
10889             APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10890 
10891           /* An empty property hash in the iprops cache means there are no
10892              inherited properties. */
10893           if (apr_hash_count(cached_iprop->prop_hash) == 0)
10894             continue;
10895 
10896           if (propname)
10897             filter_unwanted_props(cached_iprop->prop_hash, propname,
10898                                   scratch_pool);
10899 
10900           /* If we didn't filter everything then keep this iprop. */
10901           if (apr_hash_count(cached_iprop->prop_hash))
10902             svn_sort__array_insert(iprops, &cached_iprop, 0);
10903         }
10904     }
10905 
10906   if (actual_props && !*actual_props)
10907     *actual_props = apr_hash_make(result_pool);
10908 
10909   svn_pool_destroy(iterpool);
10910   return SVN_NO_ERROR;
10911 }
10912 
10913 svn_error_t *
svn_wc__db_read_inherited_props(apr_array_header_t ** iprops,apr_hash_t ** actual_props,svn_wc__db_t * db,const char * local_abspath,const char * propname,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10914 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10915                                 apr_hash_t **actual_props,
10916                                 svn_wc__db_t *db,
10917                                 const char *local_abspath,
10918                                 const char *propname,
10919                                 apr_pool_t *result_pool,
10920                                 apr_pool_t *scratch_pool)
10921 {
10922   svn_wc__db_wcroot_t *wcroot;
10923   const char *local_relpath;
10924 
10925   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10926 
10927   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10928                                                 db, local_abspath,
10929                                                 scratch_pool, scratch_pool));
10930   VERIFY_USABLE_WCROOT(wcroot);
10931 
10932   SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10933                                               wcroot, local_relpath, propname,
10934                                               result_pool, scratch_pool),
10935                       wcroot);
10936 
10937   return SVN_NO_ERROR;
10938 }
10939 
10940 /* The body of svn_wc__db_get_children_with_cached_iprops().
10941  */
10942 static svn_error_t *
get_children_with_cached_iprops(apr_hash_t ** iprop_paths,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_depth_t depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10943 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10944                                 svn_wc__db_wcroot_t *wcroot,
10945                                 const char *local_relpath,
10946                                 svn_depth_t depth,
10947                                 apr_pool_t *result_pool,
10948                                 apr_pool_t *scratch_pool)
10949 {
10950   svn_sqlite__stmt_t *stmt;
10951   svn_boolean_t have_row;
10952 
10953   *iprop_paths = apr_hash_make(result_pool);
10954 
10955   /* First check if LOCAL_RELPATH itself has iprops */
10956   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10957                                     STMT_SELECT_IPROPS_NODE));
10958   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10959   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10960 
10961   if (have_row)
10962    {
10963       const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10964                                                                NULL);
10965       const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10966                                                        relpath_with_cache,
10967                                                        result_pool);
10968       svn_hash_sets(*iprop_paths, abspath_with_cache,
10969                     svn_sqlite__column_text(stmt, 1, result_pool));
10970     }
10971   SVN_ERR(svn_sqlite__reset(stmt));
10972 
10973   if (depth == svn_depth_empty)
10974     return SVN_NO_ERROR;
10975 
10976   /* Now fetch information for children or all descendants */
10977   if (depth == svn_depth_files
10978       || depth == svn_depth_immediates)
10979     {
10980       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10981                                         STMT_SELECT_IPROPS_CHILDREN));
10982     }
10983   else /* Default to svn_depth_infinity. */
10984     {
10985       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10986                                         STMT_SELECT_IPROPS_RECURSIVE));
10987     }
10988 
10989   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10990   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10991 
10992   while (have_row)
10993     {
10994       const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10995                                                                NULL);
10996       const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10997                                                        relpath_with_cache,
10998                                                        result_pool);
10999       svn_hash_sets(*iprop_paths, abspath_with_cache,
11000                     svn_sqlite__column_text(stmt, 1, result_pool));
11001       SVN_ERR(svn_sqlite__step(&have_row, stmt));
11002     }
11003 
11004   SVN_ERR(svn_sqlite__reset(stmt));
11005 
11006   /* For depth files we should filter non files */
11007   if (depth == svn_depth_files)
11008     {
11009       apr_hash_index_t *hi;
11010       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11011 
11012       for (hi = apr_hash_first(scratch_pool, *iprop_paths);
11013            hi;
11014            hi = apr_hash_next(hi))
11015         {
11016           const char *child_abspath = apr_hash_this_key(hi);
11017           const char *child_relpath;
11018           svn_node_kind_t child_kind;
11019 
11020           svn_pool_clear(iterpool);
11021 
11022           child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
11023                                               NULL);
11024 
11025           if (! child_relpath)
11026             {
11027               continue; /* local_relpath itself */
11028             }
11029 
11030           SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
11031                                                     NULL, NULL, NULL, NULL,
11032                                                     NULL, NULL, NULL, NULL,
11033                                                     NULL, NULL, NULL, NULL,
11034                                                     wcroot, child_relpath,
11035                                                     scratch_pool,
11036                                                     scratch_pool));
11037 
11038           /* Filter if not a file */
11039           if (child_kind != svn_node_file)
11040             {
11041               svn_hash_sets(*iprop_paths, child_abspath, NULL);
11042             }
11043         }
11044 
11045       svn_pool_destroy(iterpool);
11046     }
11047 
11048   return SVN_NO_ERROR;
11049 }
11050 
11051 svn_error_t *
svn_wc__db_get_children_with_cached_iprops(apr_hash_t ** iprop_paths,svn_depth_t depth,const char * local_abspath,svn_wc__db_t * db,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11052 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
11053                                            svn_depth_t depth,
11054                                            const char *local_abspath,
11055                                            svn_wc__db_t *db,
11056                                            apr_pool_t *result_pool,
11057                                            apr_pool_t *scratch_pool)
11058 {
11059   svn_wc__db_wcroot_t *wcroot;
11060   const char *local_relpath;
11061 
11062   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11063 
11064   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11065                                                 local_abspath, scratch_pool,
11066                                                 scratch_pool));
11067   VERIFY_USABLE_WCROOT(wcroot);
11068 
11069   SVN_WC__DB_WITH_TXN(
11070     get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
11071                                     depth, result_pool, scratch_pool),
11072     wcroot);
11073 
11074   return SVN_NO_ERROR;
11075 }
11076 
11077 svn_error_t *
svn_wc__db_read_children_of_working_node(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11078 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
11079                                          svn_wc__db_t *db,
11080                                          const char *local_abspath,
11081                                          apr_pool_t *result_pool,
11082                                          apr_pool_t *scratch_pool)
11083 {
11084   svn_wc__db_wcroot_t *wcroot;
11085   const char *local_relpath;
11086 
11087   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11088 
11089   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11090                                              local_abspath,
11091                                              scratch_pool, scratch_pool));
11092   VERIFY_USABLE_WCROOT(wcroot);
11093 
11094   return svn_error_trace(
11095           gather_children(children, wcroot, local_relpath,
11096                           STMT_SELECT_WORKING_CHILDREN, -1,
11097                           result_pool, scratch_pool));
11098 }
11099 
11100 svn_error_t *
svn_wc__db_base_read_not_present_children(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11101 svn_wc__db_base_read_not_present_children(
11102                                 const apr_array_header_t **children,
11103                                 svn_wc__db_t *db,
11104                                 const char *local_abspath,
11105                                 apr_pool_t *result_pool,
11106                                 apr_pool_t *scratch_pool)
11107 {
11108   svn_wc__db_wcroot_t *wcroot;
11109   const char *local_relpath;
11110 
11111   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11112 
11113   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11114                                              local_abspath,
11115                                              scratch_pool, scratch_pool));
11116   VERIFY_USABLE_WCROOT(wcroot);
11117 
11118   return svn_error_trace(
11119           gather_children(children, wcroot, local_relpath,
11120                           STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
11121                           result_pool, scratch_pool));
11122 }
11123 
11124 /* Helper for svn_wc__db_node_check_replace().
11125  */
11126 static svn_error_t *
check_replace_txn(svn_boolean_t * is_replace_root_p,svn_boolean_t * base_replace_p,svn_boolean_t * is_replace_p,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)11127 check_replace_txn(svn_boolean_t *is_replace_root_p,
11128                   svn_boolean_t *base_replace_p,
11129                   svn_boolean_t *is_replace_p,
11130                   svn_wc__db_wcroot_t *wcroot,
11131                   const char *local_relpath,
11132                   apr_pool_t *scratch_pool)
11133 {
11134   svn_sqlite__stmt_t *stmt;
11135   svn_boolean_t have_row;
11136   svn_boolean_t is_replace = FALSE;
11137   int replaced_op_depth;
11138   svn_wc__db_status_t replaced_status;
11139 
11140   /* Our caller initialized the output values to FALSE */
11141 
11142   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11143                                     STMT_SELECT_NODE_INFO));
11144 
11145   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11146 
11147   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11148 
11149   if (!have_row)
11150     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11151                              svn_sqlite__reset(stmt),
11152                              _("The node '%s' was not found."),
11153                              path_for_error_message(wcroot, local_relpath,
11154                                                     scratch_pool));
11155 
11156   {
11157     svn_wc__db_status_t status;
11158 
11159     status = svn_sqlite__column_token(stmt, 3, presence_map);
11160 
11161     if (status != svn_wc__db_status_normal)
11162       return svn_error_trace(svn_sqlite__reset(stmt));
11163   }
11164 
11165   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11166 
11167   if (!have_row)
11168     return svn_error_trace(svn_sqlite__reset(stmt));
11169 
11170   replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
11171 
11172   /* If the layer below the add describes a not present or a deleted node,
11173      this is not a replacement. Deleted can only occur if an ancestor is
11174      the delete root. */
11175   if (replaced_status != svn_wc__db_status_not_present
11176       && replaced_status != svn_wc__db_status_excluded
11177       && replaced_status != svn_wc__db_status_server_excluded
11178       && replaced_status != svn_wc__db_status_base_deleted)
11179     {
11180       is_replace = TRUE;
11181       if (is_replace_p)
11182         *is_replace_p = TRUE;
11183     }
11184 
11185   replaced_op_depth = svn_sqlite__column_int(stmt, 0);
11186 
11187   if (base_replace_p)
11188     {
11189       int op_depth = svn_sqlite__column_int(stmt, 0);
11190 
11191       while (op_depth != 0 && have_row)
11192         {
11193           SVN_ERR(svn_sqlite__step(&have_row, stmt));
11194 
11195           if (have_row)
11196             op_depth = svn_sqlite__column_int(stmt, 0);
11197         }
11198 
11199       if (have_row && op_depth == 0)
11200         {
11201           svn_wc__db_status_t base_status;
11202 
11203           base_status = svn_sqlite__column_token(stmt, 3, presence_map);
11204 
11205           *base_replace_p = (base_status != svn_wc__db_status_not_present);
11206         }
11207     }
11208 
11209   SVN_ERR(svn_sqlite__reset(stmt));
11210 
11211   if (!is_replace_root_p || !is_replace)
11212     return SVN_NO_ERROR;
11213 
11214   if (replaced_status != svn_wc__db_status_base_deleted)
11215     {
11216       int parent_op_depth;
11217 
11218       /* Check the current op-depth of the parent to see if we are a replacement
11219          root */
11220       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11221                                 svn_relpath_dirname(local_relpath,
11222                                                     scratch_pool)));
11223 
11224       SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
11225 
11226       parent_op_depth = svn_sqlite__column_int(stmt, 0);
11227 
11228       if (parent_op_depth >= replaced_op_depth)
11229         {
11230           /* Did we replace inside our directory? */
11231 
11232           *is_replace_root_p = (parent_op_depth == replaced_op_depth);
11233           SVN_ERR(svn_sqlite__reset(stmt));
11234           return SVN_NO_ERROR;
11235         }
11236 
11237       SVN_ERR(svn_sqlite__step(&have_row, stmt));
11238 
11239       if (have_row)
11240         parent_op_depth = svn_sqlite__column_int(stmt, 0);
11241 
11242       SVN_ERR(svn_sqlite__reset(stmt));
11243 
11244       if (!have_row)
11245         *is_replace_root_p = TRUE; /* Parent is no replacement */
11246       else if (parent_op_depth < replaced_op_depth)
11247         *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
11248       /*else // No replacement root */
11249   }
11250 
11251   return SVN_NO_ERROR;
11252 }
11253 
11254 svn_error_t *
svn_wc__db_node_check_replace(svn_boolean_t * is_replace_root,svn_boolean_t * base_replace,svn_boolean_t * is_replace,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)11255 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
11256                               svn_boolean_t *base_replace,
11257                               svn_boolean_t *is_replace,
11258                               svn_wc__db_t *db,
11259                               const char *local_abspath,
11260                               apr_pool_t *scratch_pool)
11261 {
11262   svn_wc__db_wcroot_t *wcroot;
11263   const char *local_relpath;
11264 
11265   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11266 
11267   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11268                                              local_abspath,
11269                                              scratch_pool, scratch_pool));
11270   VERIFY_USABLE_WCROOT(wcroot);
11271 
11272   if (is_replace_root)
11273     *is_replace_root = FALSE;
11274   if (base_replace)
11275     *base_replace = FALSE;
11276   if (is_replace)
11277     *is_replace = FALSE;
11278 
11279   if (local_relpath[0] == '\0')
11280     return SVN_NO_ERROR; /* Working copy root can't be replaced */
11281 
11282   SVN_WC__DB_WITH_TXN(
11283     check_replace_txn(is_replace_root, base_replace, is_replace,
11284                       wcroot, local_relpath, scratch_pool),
11285     wcroot);
11286 
11287   return SVN_NO_ERROR;
11288 }
11289 
11290 svn_error_t *
svn_wc__db_read_children(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11291 svn_wc__db_read_children(const apr_array_header_t **children,
11292                          svn_wc__db_t *db,
11293                          const char *local_abspath,
11294                          apr_pool_t *result_pool,
11295                          apr_pool_t *scratch_pool)
11296 {
11297   svn_wc__db_wcroot_t *wcroot;
11298   const char *local_relpath;
11299 
11300   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11301 
11302   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11303                                              local_abspath,
11304                                              scratch_pool, scratch_pool));
11305   VERIFY_USABLE_WCROOT(wcroot);
11306 
11307   return gather_children(children, wcroot, local_relpath,
11308                          STMT_SELECT_NODE_CHILDREN, -1,
11309                          result_pool, scratch_pool);
11310 }
11311 
11312 
11313 /* Implementation of svn_wc__db_global_relocate */
11314 static svn_error_t *
relocate_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * repos_root_url,apr_pool_t * scratch_pool)11315 relocate_txn(svn_wc__db_wcroot_t *wcroot,
11316              const char *local_relpath,
11317              const char *repos_root_url,
11318              apr_pool_t *scratch_pool)
11319 {
11320   svn_sqlite__stmt_t *stmt;
11321   apr_int64_t new_repos_id;
11322   const char *local_dir_relpath;
11323   svn_wc__db_status_t status;
11324   const char *repos_uuid;
11325   svn_boolean_t have_base_node;
11326   apr_int64_t old_repos_id;
11327 
11328   local_dir_relpath = local_relpath;
11329 
11330   SVN_ERR(read_info(&status,
11331                     NULL, NULL, NULL, &old_repos_id,
11332                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11333                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11334                     NULL,
11335                     &have_base_node, NULL, NULL,
11336                     wcroot, local_relpath,
11337                     scratch_pool, scratch_pool));
11338 
11339   if (status == svn_wc__db_status_excluded)
11340     {
11341       /* The parent cannot be excluded, so look at the parent and then
11342          adjust the relpath */
11343       const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
11344                                                        scratch_pool);
11345       SVN_ERR(read_info(&status,
11346                         NULL, NULL, NULL, &old_repos_id,
11347                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11348                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11349                         NULL, NULL, NULL,
11350                         NULL, NULL, NULL,
11351                         wcroot, parent_relpath,
11352                         scratch_pool, scratch_pool));
11353       local_dir_relpath = parent_relpath;
11354     }
11355 
11356   if (old_repos_id == INVALID_REPOS_ID)
11357     {
11358       /* Do we need to support relocating something that is
11359          added/deleted/excluded without relocating the parent?  If not
11360          then perhaps relpath, root_url and uuid should be passed down
11361          to the children so that they don't have to scan? */
11362 
11363       if (status == svn_wc__db_status_deleted)
11364         {
11365           const char *work_del_relpath;
11366 
11367           SVN_ERR(scan_deletion(NULL, NULL,
11368                                 &work_del_relpath, NULL,
11369                                 wcroot, local_dir_relpath,
11370                                 scratch_pool,
11371                                 scratch_pool));
11372           if (work_del_relpath)
11373             {
11374               /* Deleted within a copy/move */
11375 
11376               /* The parent of the delete is added. */
11377               status = svn_wc__db_status_added;
11378               local_dir_relpath = svn_relpath_dirname(work_del_relpath,
11379                                                       scratch_pool);
11380             }
11381         }
11382 
11383       if (status == svn_wc__db_status_added)
11384         {
11385           SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
11386                                 NULL, NULL, NULL, NULL, NULL, NULL,
11387                                 wcroot, local_dir_relpath,
11388                                 scratch_pool, scratch_pool));
11389         }
11390       else
11391         SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
11392                                                   &old_repos_id,
11393                                                   NULL, NULL, NULL, NULL, NULL,
11394                                                   NULL, NULL, NULL, NULL, NULL,
11395                                                   wcroot, local_dir_relpath,
11396                                                   scratch_pool, scratch_pool));
11397     }
11398 
11399   SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot,
11400                                       old_repos_id, scratch_pool));
11401   SVN_ERR_ASSERT(repos_uuid);  /* This function affects all the children of the given local_relpath,
11402      but the way that it does this is through the repos inheritance mechanism.
11403      So, we only need to rewrite the repos_id of the given local_relpath,
11404      as well as any children with a non-null repos_id, as well as various
11405      repos_id fields in the locks and working_node tables.
11406    */
11407 
11408   /* Get the repos_id for the new repository. */
11409   SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
11410                           wcroot->sdb, scratch_pool));
11411 
11412   /* Set the (base and working) repos_ids and clear the dav_caches */
11413   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11414                                     STMT_RECURSIVE_UPDATE_NODE_REPO));
11415   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
11416                             old_repos_id, new_repos_id));
11417   SVN_ERR(svn_sqlite__step_done(stmt));
11418 
11419   if (have_base_node)
11420     {
11421       /* Update any locks for the root or its children. */
11422       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11423                                         STMT_UPDATE_LOCK_REPOS_ID));
11424       SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
11425       SVN_ERR(svn_sqlite__step_done(stmt));
11426     }
11427 
11428   return SVN_NO_ERROR;
11429 }
11430 
11431 
11432 svn_error_t *
svn_wc__db_global_relocate(svn_wc__db_t * db,const char * local_dir_abspath,const char * repos_root_url,apr_pool_t * scratch_pool)11433 svn_wc__db_global_relocate(svn_wc__db_t *db,
11434                            const char *local_dir_abspath,
11435                            const char *repos_root_url,
11436                            apr_pool_t *scratch_pool)
11437 {
11438   svn_wc__db_wcroot_t *wcroot;
11439   const char *local_relpath;
11440 
11441   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
11442   /* ### assert that we were passed a directory?  */
11443 
11444   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
11445                            db, local_dir_abspath, scratch_pool, scratch_pool));
11446   VERIFY_USABLE_WCROOT(wcroot);
11447 
11448   SVN_WC__DB_WITH_TXN(
11449     relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool),
11450     wcroot);
11451 
11452   SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity,
11453                         scratch_pool));
11454 
11455   return SVN_NO_ERROR;
11456 }
11457 
11458 
11459 /* Helper for commit_node()
11460    Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
11461    (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
11462    its parent's BASE row if not. In the latter case, error if the parent
11463    BASE row does not exist.  */
11464 static svn_error_t *
determine_commit_repos_info(apr_int64_t * repos_id,const char ** repos_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11465 determine_commit_repos_info(apr_int64_t *repos_id,
11466                             const char **repos_relpath,
11467                             svn_wc__db_wcroot_t *wcroot,
11468                             const char *local_relpath,
11469                             apr_pool_t *result_pool,
11470                             apr_pool_t *scratch_pool)
11471 {
11472   svn_sqlite__stmt_t *stmt;
11473   svn_boolean_t have_row;
11474   int op_depth;
11475 
11476   /* Prefer the current node's repository information.  */
11477   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11478                                     STMT_SELECT_NODE_INFO));
11479   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11480   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11481 
11482   if (!have_row)
11483     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11484                              svn_sqlite__reset(stmt),
11485                              _("The node '%s' was not found."),
11486                              path_for_error_message(wcroot, local_relpath,
11487                                                     scratch_pool));
11488 
11489   op_depth = svn_sqlite__column_int(stmt, 0);
11490 
11491   if (op_depth > 0)
11492     {
11493       svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
11494                                                               presence_map);
11495 
11496       if (presence == svn_wc__db_status_base_deleted)
11497         {
11498           SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
11499           op_depth = svn_sqlite__column_int(stmt, 0);
11500         }
11501       else
11502         {
11503           const char *parent_repos_relpath;
11504           const char *parent_relpath;
11505           const char *name;
11506 
11507           SVN_ERR(svn_sqlite__reset(stmt));
11508 
11509           /* The repository relative path of an add/copy is based on its
11510              ancestor, not on the shadowed base layer.
11511 
11512              As this function is only used from the commit processing we know
11513              the parent directory has only a BASE row, so we can just obtain
11514              the information directly by recursing (once!)  */
11515 
11516           svn_relpath_split(&parent_relpath, &name, local_relpath,
11517                             scratch_pool);
11518 
11519           SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
11520                                               wcroot, parent_relpath,
11521                                               scratch_pool, scratch_pool));
11522 
11523           *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
11524                                             result_pool);
11525           return SVN_NO_ERROR;
11526         }
11527     }
11528 
11529 
11530   SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
11531 
11532   SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
11533   SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
11534 
11535   *repos_id = svn_sqlite__column_int64(stmt, 1);
11536   *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
11537 
11538   return svn_error_trace(svn_sqlite__reset(stmt));
11539 }
11540 
11541 static svn_error_t *
moved_descendant_collect(apr_hash_t ** map,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11542 moved_descendant_collect(apr_hash_t **map,
11543                         svn_wc__db_wcroot_t *wcroot,
11544                         const char *local_relpath,
11545                         int op_depth,
11546                         apr_pool_t *result_pool,
11547                         apr_pool_t *scratch_pool)
11548 {
11549   svn_sqlite__stmt_t *stmt;
11550   svn_boolean_t have_row;
11551 
11552   *map = NULL;
11553 
11554   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11555                                     STMT_SELECT_MOVED_DESCENDANTS_SRC));
11556   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11557                                          local_relpath,
11558                                          op_depth));
11559 
11560   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11561   if (! have_row)
11562     return svn_error_trace(svn_sqlite__reset(stmt));
11563 
11564   /* Find all moved descendants. Key them on target, because that is
11565      always unique */
11566   while (have_row)
11567     {
11568       const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
11569       const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
11570 
11571       if (!*map)
11572         *map = apr_hash_make(result_pool);
11573 
11574       svn_hash_sets(*map, to_relpath, src_relpath);
11575 
11576       SVN_ERR(svn_sqlite__step(&have_row, stmt));
11577     }
11578   SVN_ERR(svn_sqlite__reset(stmt));
11579 
11580   return SVN_NO_ERROR;
11581 }
11582 
11583 /* Helper for svn_wc__db_global_commit()
11584 
11585    Makes local_relpath and all its descendants at the same op-depth represent
11586    the copy origin repos_id:repos_relpath@revision.
11587 
11588    This code is only valid to fix-up a move from an old location, to a new
11589    location during a commit.
11590 
11591    Assumptions:
11592      * local_relpath is not the working copy root (can't be moved)
11593      * repos_relpath is not the repository root (can't be moved)
11594  */
11595 static svn_error_t *
moved_descendant_commit(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_int64_t repos_id,const char * repos_relpath,svn_revnum_t revision,apr_hash_t * children,apr_pool_t * scratch_pool)11596 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
11597                         const char *local_relpath,
11598                         apr_int64_t repos_id,
11599                         const char *repos_relpath,
11600                         svn_revnum_t revision,
11601                         apr_hash_t *children,
11602                         apr_pool_t *scratch_pool)
11603 {
11604   apr_pool_t *iterpool;
11605   svn_sqlite__stmt_t *stmt;
11606   apr_hash_index_t *hi;
11607 
11608   SVN_ERR_ASSERT(*local_relpath != '\0'
11609                  && *repos_relpath != '\0');
11610 
11611   if (!children)
11612     return SVN_NO_ERROR;
11613 
11614   /* Then update them */
11615   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11616                                     STMT_COMMIT_UPDATE_ORIGIN));
11617 
11618   iterpool = svn_pool_create(scratch_pool);
11619   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11620     {
11621       const char *src_relpath = apr_hash_this_val(hi);
11622       const char *to_relpath = apr_hash_this_key(hi);
11623       const char *new_repos_relpath;
11624       int to_op_depth = relpath_depth(to_relpath);
11625       int affected;
11626       apr_hash_t *map;
11627 
11628       svn_pool_clear(iterpool);
11629 
11630       SVN_ERR_ASSERT(to_op_depth > 0);
11631 
11632       new_repos_relpath = svn_relpath_join(
11633                             repos_relpath,
11634                             svn_relpath_skip_ancestor(local_relpath,
11635                                                       src_relpath),
11636                             iterpool);
11637 
11638       SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11639                                                 to_relpath,
11640                                                 to_op_depth,
11641                                                 repos_id,
11642                                                 new_repos_relpath,
11643                                                 revision));
11644       SVN_ERR(svn_sqlite__update(&affected, stmt));
11645 
11646 #ifdef SVN_DEBUG
11647       /* Enable in release code?
11648          Broken moves are not fatal yet, but this assertion would break
11649          committing them */
11650       SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11651 #endif
11652 
11653       SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
11654                                        iterpool, iterpool));
11655       SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
11656                                       repos_id, new_repos_relpath, revision,
11657                                       map, iterpool));
11658     }
11659 
11660   svn_pool_destroy(iterpool);
11661   return SVN_NO_ERROR;
11662 }
11663 
11664 /* Helper for svn_wc__db_global_commit()
11665 
11666    Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11667    (BASE), setting their presence to 'not-present' if their presence wasn't
11668    'normal'.
11669 
11670    Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11671    location repos_id:repos_relpath@revision.
11672 
11673    Assumptions:
11674      * local_relpath is not the working copy root (can't be replaced)
11675      * repos_relpath is not the repository root (can't be replaced)
11676    */
11677 static svn_error_t *
descendant_commit(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_int64_t repos_id,const char * repos_relpath,svn_revnum_t revision,apr_pool_t * scratch_pool)11678 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11679                   const char *local_relpath,
11680                   int op_depth,
11681                   apr_int64_t repos_id,
11682                   const char *repos_relpath,
11683                   svn_revnum_t revision,
11684                   apr_pool_t *scratch_pool)
11685 {
11686   svn_sqlite__stmt_t *stmt;
11687 
11688   SVN_ERR_ASSERT(*local_relpath != '\0'
11689                  && *repos_relpath != '\0');
11690 
11691   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11692                                     STMT_COMMIT_DESCENDANTS_TO_BASE));
11693 
11694   SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11695                                             local_relpath,
11696                                             op_depth,
11697                                             repos_id,
11698                                             repos_relpath,
11699                                             revision));
11700 
11701   SVN_ERR(svn_sqlite__update(NULL, stmt));
11702 
11703   return SVN_NO_ERROR;
11704 }
11705 
11706 /* The body of svn_wc__db_global_commit().
11707  */
11708 static svn_error_t *
commit_node(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_revnum_t new_revision,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * new_checksum,apr_hash_t * new_dav_cache,svn_boolean_t keep_changelist,svn_boolean_t no_unlock,const svn_skel_t * work_items,apr_pool_t * scratch_pool)11709 commit_node(svn_wc__db_wcroot_t *wcroot,
11710             const char *local_relpath,
11711             svn_revnum_t new_revision,
11712             svn_revnum_t changed_rev,
11713             apr_time_t changed_date,
11714             const char *changed_author,
11715             const svn_checksum_t *new_checksum,
11716             apr_hash_t *new_dav_cache,
11717             svn_boolean_t keep_changelist,
11718             svn_boolean_t no_unlock,
11719             const svn_skel_t *work_items,
11720             apr_pool_t *scratch_pool)
11721 {
11722   svn_sqlite__stmt_t *stmt_info;
11723   svn_sqlite__stmt_t *stmt_act;
11724   svn_boolean_t have_act;
11725   svn_string_t prop_blob = { 0 };
11726   svn_string_t inherited_prop_blob = { 0 };
11727   const char *changelist = NULL;
11728   const char *parent_relpath;
11729   svn_wc__db_status_t new_presence;
11730   svn_node_kind_t new_kind;
11731   const char *new_depth_str = NULL;
11732   svn_sqlite__stmt_t *stmt;
11733   apr_int64_t repos_id;
11734   const char *repos_relpath;
11735   int op_depth;
11736   svn_wc__db_status_t old_presence;
11737   svn_boolean_t moved_here;
11738 
11739     /* If we are adding a file or directory, then we need to get
11740      repository information from the parent node since "this node" does
11741      not have a BASE).
11742 
11743      For existing nodes, we should retain the (potentially-switched)
11744      repository information.  */
11745   SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11746                                       wcroot, local_relpath,
11747                                       scratch_pool, scratch_pool));
11748 
11749   /* ### is it better to select only the data needed?  */
11750   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11751                                     STMT_SELECT_NODE_INFO));
11752   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11753   SVN_ERR(svn_sqlite__step_row(stmt_info));
11754 
11755   SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11756                                     STMT_SELECT_ACTUAL_NODE));
11757   SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11758                             wcroot->wc_id, local_relpath));
11759   SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11760 
11761   /* There should be something to commit!  */
11762 
11763   op_depth = svn_sqlite__column_int(stmt_info, 0);
11764 
11765   /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11766      or there will be a BASE_NODE that has it.  */
11767   old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11768   new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11769 
11770   /* What will the new depth be?  */
11771   if (new_kind == svn_node_dir)
11772     new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11773 
11774   /* Check that the repository information is not being changed.  */
11775   if (op_depth == 0)
11776     {
11777       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11778       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11779 
11780       /* A commit cannot change these values.  */
11781       SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11782       SVN_ERR_ASSERT(strcmp(repos_relpath,
11783                             svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11784     }
11785 
11786   if (old_presence != svn_wc__db_status_base_deleted)
11787     {
11788       /* Find the appropriate new properties -- ACTUAL overrides any properties
11789          in WORKING that arrived as part of a copy/move.
11790 
11791          Note: we'll keep them as a big blob of data, rather than
11792          deserialize/serialize them.  */
11793       if (have_act)
11794         prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11795                                                  scratch_pool);
11796       if (prop_blob.data == NULL)
11797         prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11798                                                  scratch_pool);
11799 
11800       inherited_prop_blob.data = svn_sqlite__column_blob(
11801                                             stmt_info, 16,
11802                                             &inherited_prop_blob.len,
11803                                             scratch_pool);
11804 
11805       if (keep_changelist && have_act)
11806         changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11807 
11808       moved_here = svn_sqlite__column_int(stmt_info, 15);
11809     }
11810   else
11811     {
11812       moved_here = FALSE;
11813       changelist = NULL;
11814     }
11815 
11816   /* ### other stuff?  */
11817 
11818   SVN_ERR(svn_sqlite__reset(stmt_info));
11819   SVN_ERR(svn_sqlite__reset(stmt_act));
11820 
11821   if (op_depth > 0)
11822     {
11823       int affected_rows;
11824 
11825       SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
11826 
11827       /* First clear the moves that we are going to delete in a bit */
11828       {
11829         apr_hash_t *old_moves;
11830         apr_hash_index_t *hi;
11831         SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
11832                                          scratch_pool, scratch_pool));
11833 
11834         if (old_moves)
11835           for (hi = apr_hash_first(scratch_pool, old_moves);
11836                 hi; hi = apr_hash_next(hi))
11837             {
11838               SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
11839                                         scratch_pool));
11840             }
11841       }
11842 
11843       /* This removes all layers of this node and at the same time determines
11844          if we need to remove shadowed layers below our descendants. */
11845 
11846       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11847                                         STMT_DELETE_NODE_ALL_LAYERS));
11848       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11849       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11850 
11851       if (affected_rows > 1)
11852         {
11853           /* We commit a shadowing operation
11854 
11855            1) Remove all shadowed nodes
11856            2) And remove all nodes that have a base-deleted as lowest layer,
11857               because 1) removed that layer */
11858 
11859           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11860                                             STMT_DELETE_SHADOWED_RECURSIVE));
11861 
11862           SVN_ERR(svn_sqlite__bindf(stmt,
11863                                     "isd",
11864                                     wcroot->wc_id,
11865                                     local_relpath,
11866                                     op_depth));
11867 
11868           SVN_ERR(svn_sqlite__step_done(stmt));
11869         }
11870 
11871       /* Note that while these two calls look so similar that they might
11872          be integrated, they really affect a different op-depth and
11873          completely different nodes (via a different recursion pattern). */
11874 
11875       if (old_presence != svn_wc__db_status_base_deleted)
11876         {
11877           /* Collapse descendants of the current op_depth to layer 0,
11878              this includes moved-from/to clearing */
11879           SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11880                                     repos_id, repos_relpath, new_revision,
11881                                     scratch_pool));
11882         }
11883 
11884       if (old_presence != svn_wc__db_status_base_deleted)
11885         {
11886           apr_hash_t *moves = NULL;
11887 
11888           SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
11889                                            scratch_pool, scratch_pool));
11890 
11891           /* And make the recorded local moves represent moves of the node we
11892              just committed. */
11893           SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
11894                                       repos_id, repos_relpath, new_revision,
11895                                       moves, scratch_pool));
11896         }
11897 
11898       if (moved_here)
11899         {
11900           /* This node is no longer modified, so no node was moved here */
11901           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11902                                             STMT_CLEAR_MOVED_TO_FROM_DEST));
11903           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11904                                                 local_relpath));
11905 
11906           SVN_ERR(svn_sqlite__step_done(stmt));
11907         }
11908     }
11909   /* Update or add the BASE_NODE row with all the new information.  */
11910 
11911   if (*local_relpath == '\0')
11912     parent_relpath = NULL;
11913   else
11914     parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11915 
11916   /* Preserve any incomplete status */
11917   if (old_presence != svn_wc__db_status_base_deleted)
11918     {
11919       new_presence = (old_presence == svn_wc__db_status_incomplete
11920                       ? svn_wc__db_status_incomplete
11921                       : svn_wc__db_status_normal);
11922 
11923       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11924                                         STMT_APPLY_CHANGES_TO_BASE_NODE));
11925       /* symlink_target not yet used */
11926       SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11927                                 wcroot->wc_id, local_relpath,
11928                                 parent_relpath,
11929                                 repos_id,
11930                                 repos_relpath,
11931                                 new_revision,
11932                                 presence_map, new_presence,
11933                                 new_depth_str,
11934                                 kind_map, new_kind,
11935                                 changed_rev,
11936                                 changed_date,
11937                                 changed_author,
11938                                 prop_blob.data, prop_blob.len));
11939 
11940       SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11941                                         scratch_pool));
11942       SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11943                                           scratch_pool));
11944       if (inherited_prop_blob.data != NULL)
11945         {
11946           SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11947                                         inherited_prop_blob.len));
11948         }
11949 
11950       SVN_ERR(svn_sqlite__step_done(stmt));
11951     }
11952   else
11953     {
11954       struct insert_base_baton_t ibb;
11955       blank_ibb(&ibb);
11956 
11957       ibb.repos_id = repos_id;
11958       ibb.status = svn_wc__db_status_not_present;
11959       ibb.kind = new_kind;
11960       ibb.repos_relpath = repos_relpath;
11961       ibb.revision = new_revision;
11962 
11963       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
11964 
11965       keep_changelist = FALSE; /* Nothing there */
11966     }
11967 
11968   if (have_act)
11969     {
11970       if (keep_changelist && changelist != NULL)
11971         {
11972           /* The user told us to keep the changelist. Replace the row in
11973              ACTUAL_NODE with the basic keys and the changelist.  */
11974           SVN_ERR(svn_sqlite__get_statement(
11975                     &stmt, wcroot->sdb,
11976                     STMT_RESET_ACTUAL_WITH_CHANGELIST));
11977           SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11978                                     wcroot->wc_id, local_relpath,
11979                                     svn_relpath_dirname(local_relpath,
11980                                                         scratch_pool),
11981                                     changelist));
11982           SVN_ERR(svn_sqlite__step_done(stmt));
11983         }
11984       else
11985         {
11986           /* Toss the ACTUAL_NODE row.  */
11987           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11988                                             STMT_DELETE_ACTUAL_NODE));
11989           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11990           SVN_ERR(svn_sqlite__step_done(stmt));
11991         }
11992     }
11993 
11994   if (!no_unlock)
11995     {
11996       svn_sqlite__stmt_t *lock_stmt;
11997       svn_boolean_t op_root = (op_depth > 0
11998                                && (relpath_depth(local_relpath) == op_depth));
11999 
12000       /* If we are committing an add of a delete, we can assume we own
12001          all locks at or below REPOS_RELPATH (or the server would have
12002          denied the commit). As we must have passed these to the server
12003          we can now safely remove them.
12004        */
12005       SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
12006                                         op_root
12007                                           ? STMT_DELETE_LOCK_RECURSIVELY
12008                                           : STMT_DELETE_LOCK));
12009       SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
12010       SVN_ERR(svn_sqlite__step_done(lock_stmt));
12011     }
12012 
12013   /* Install any work items into the queue, as part of this transaction.  */
12014   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12015 
12016   return SVN_NO_ERROR;
12017 }
12018 
12019 
12020 svn_error_t *
svn_wc__db_global_commit(svn_wc__db_t * db,const char * local_abspath,svn_revnum_t new_revision,svn_revnum_t changed_revision,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * new_checksum,apr_hash_t * new_dav_cache,svn_boolean_t keep_changelist,svn_boolean_t no_unlock,const svn_skel_t * work_items,apr_pool_t * scratch_pool)12021 svn_wc__db_global_commit(svn_wc__db_t *db,
12022                          const char *local_abspath,
12023                          svn_revnum_t new_revision,
12024                          svn_revnum_t changed_revision,
12025                          apr_time_t changed_date,
12026                          const char *changed_author,
12027                          const svn_checksum_t *new_checksum,
12028                          apr_hash_t *new_dav_cache,
12029                          svn_boolean_t keep_changelist,
12030                          svn_boolean_t no_unlock,
12031                          const svn_skel_t *work_items,
12032                          apr_pool_t *scratch_pool)
12033 {
12034   const char *local_relpath;
12035   svn_wc__db_wcroot_t *wcroot;
12036 
12037   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12038   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12039 
12040   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12041                               local_abspath, scratch_pool, scratch_pool));
12042   VERIFY_USABLE_WCROOT(wcroot);
12043 
12044   SVN_WC__DB_WITH_TXN(
12045     commit_node(wcroot, local_relpath,
12046                 new_revision, changed_revision, changed_date, changed_author,
12047                 new_checksum, new_dav_cache, keep_changelist,
12048                 no_unlock, work_items, scratch_pool),
12049     wcroot);
12050 
12051   /* We *totally* monkeyed the entries. Toss 'em.  */
12052   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12053 
12054   return SVN_NO_ERROR;
12055 }
12056 
12057 
12058 svn_error_t *
svn_wc__db_global_update(svn_wc__db_t * db,const char * local_abspath,svn_node_kind_t new_kind,const char * new_repos_relpath,svn_revnum_t new_revision,const apr_hash_t * new_props,svn_revnum_t new_changed_rev,apr_time_t new_changed_date,const char * new_changed_author,const apr_array_header_t * new_children,const svn_checksum_t * new_checksum,const char * new_target,const apr_hash_t * new_dav_cache,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)12059 svn_wc__db_global_update(svn_wc__db_t *db,
12060                          const char *local_abspath,
12061                          svn_node_kind_t new_kind,
12062                          const char *new_repos_relpath,
12063                          svn_revnum_t new_revision,
12064                          const apr_hash_t *new_props,
12065                          svn_revnum_t new_changed_rev,
12066                          apr_time_t new_changed_date,
12067                          const char *new_changed_author,
12068                          const apr_array_header_t *new_children,
12069                          const svn_checksum_t *new_checksum,
12070                          const char *new_target,
12071                          const apr_hash_t *new_dav_cache,
12072                          const svn_skel_t *conflict,
12073                          const svn_skel_t *work_items,
12074                          apr_pool_t *scratch_pool)
12075 {
12076   NOT_IMPLEMENTED();
12077 
12078 #if 0
12079   svn_wc__db_wcroot_t *wcroot;
12080   const char *local_relpath;
12081 
12082   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12083   /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"?  */
12084   SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
12085   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12086   SVN_ERR_ASSERT(new_props != NULL);
12087   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
12088   SVN_ERR_ASSERT((new_children != NULL
12089                   && new_checksum == NULL
12090                   && new_target == NULL)
12091                  || (new_children == NULL
12092                      && new_checksum != NULL
12093                      && new_target == NULL)
12094                  || (new_children == NULL
12095                      && new_checksum == NULL
12096                      && new_target != NULL));
12097 
12098   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12099                               local_abspath, scratch_pool, scratch_pool));
12100   VERIFY_USABLE_WCROOT(wcroot);
12101 
12102   SVN_WC__DB_WITH_TXN(
12103     update_node(wcroot, local_relpath,
12104                 new_repos_relpath, new_revision, new_props,
12105                 new_changed_rev, new_changed_date, new_changed_author,
12106                 new_children, new_checksum, new_target,
12107                 conflict, work_items, scratch_pool),
12108     wcroot);
12109 
12110   /* We *totally* monkeyed the entries. Toss 'em.  */
12111   SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
12112 
12113   return SVN_NO_ERROR;
12114 #endif
12115 }
12116 
12117 /* Sets a base nodes revision, repository relative path, and/or inherited
12118    propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
12119    SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
12120    (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
12121    inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
12122    cache for the base node.
12123  */
12124 static svn_error_t *
db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_array_header_t * iprops,svn_revnum_t rev,svn_boolean_t set_repos_relpath,const char * repos_relpath,apr_int64_t repos_id,apr_pool_t * scratch_pool)12125 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
12126                                    const char *local_relpath,
12127                                    apr_array_header_t *iprops,
12128                                    svn_revnum_t rev,
12129                                    svn_boolean_t set_repos_relpath,
12130                                    const char *repos_relpath,
12131                                    apr_int64_t repos_id,
12132                                    apr_pool_t *scratch_pool)
12133 {
12134   svn_sqlite__stmt_t *stmt;
12135 
12136   SVN_ERR(flush_entries(wcroot,
12137                         svn_dirent_join(wcroot->abspath, local_relpath,
12138                                         scratch_pool),
12139                         svn_depth_empty, scratch_pool));
12140 
12141 
12142   if (SVN_IS_VALID_REVNUM(rev))
12143     {
12144       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12145                                         STMT_UPDATE_BASE_REVISION));
12146 
12147       SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
12148                                 rev));
12149 
12150       SVN_ERR(svn_sqlite__step_done(stmt));
12151     }
12152 
12153   if (set_repos_relpath)
12154     {
12155       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12156                                         STMT_UPDATE_BASE_REPOS));
12157 
12158       SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
12159                                 repos_id, repos_relpath));
12160 
12161       SVN_ERR(svn_sqlite__step_done(stmt));
12162     }
12163 
12164   /* Set or clear iprops. */
12165   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12166                                     STMT_UPDATE_IPROP));
12167   SVN_ERR(svn_sqlite__bindf(stmt, "is",
12168                             wcroot->wc_id,
12169                             local_relpath));
12170   SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
12171   SVN_ERR(svn_sqlite__step_done(stmt));
12172 
12173   return SVN_NO_ERROR;
12174 }
12175 
12176 /* The main body of bump_revisions_post_update().
12177  *
12178  * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
12179  * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
12180  * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
12181  *
12182  * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the
12183  * values as stored currently in WCROOT for LOCAL_RELPATH.
12184  *
12185  * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
12186  * working copy paths to depth-first ordered arrays of
12187  * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
12188  * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
12189  * node's inherited properties.
12190  *
12191  * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
12192  * be removed from the WC; if IS_ROOT is TRUE this will not happen.
12193  */
12194 static svn_error_t *
bump_node_revision(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_status_t node_status,svn_node_kind_t node_kind,svn_revnum_t node_revision,const char * node_repos_relpath,apr_int64_t new_repos_id,const char * new_repos_relpath,svn_revnum_t new_rev,svn_depth_t depth,apr_hash_t * exclude_relpaths,apr_hash_t * wcroot_iprops,svn_boolean_t is_root,svn_boolean_t skip_when_dir,svn_wc__db_t * db,apr_pool_t * scratch_pool)12195 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
12196                    const char *local_relpath,
12197                    svn_wc__db_status_t node_status,
12198                    svn_node_kind_t node_kind,
12199                    svn_revnum_t node_revision,
12200                    const char *node_repos_relpath,
12201                    apr_int64_t new_repos_id,
12202                    const char *new_repos_relpath,
12203                    svn_revnum_t new_rev,
12204                    svn_depth_t depth,
12205                    apr_hash_t *exclude_relpaths,
12206                    apr_hash_t *wcroot_iprops,
12207                    svn_boolean_t is_root,
12208                    svn_boolean_t skip_when_dir,
12209                    svn_wc__db_t *db,
12210                    apr_pool_t *scratch_pool)
12211 {
12212   apr_pool_t *iterpool;
12213   apr_hash_t *children;
12214   apr_hash_index_t *hi;
12215   svn_boolean_t set_repos_relpath = FALSE;
12216   svn_depth_t depth_below_here = depth;
12217   apr_array_header_t *iprops = NULL;
12218 
12219   if (new_repos_relpath != NULL
12220       && strcmp(node_repos_relpath, new_repos_relpath))
12221     set_repos_relpath = TRUE;
12222 
12223   if (wcroot_iprops)
12224     iprops = svn_hash_gets(wcroot_iprops,
12225                            svn_dirent_join(wcroot->abspath, local_relpath,
12226                                            scratch_pool));
12227 
12228   if (iprops
12229       || set_repos_relpath
12230       || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision))
12231     {
12232       SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
12233                                                  iprops, new_rev,
12234                                                  set_repos_relpath,
12235                                                  new_repos_relpath,
12236                                                  new_repos_id,
12237                                                  scratch_pool));
12238     }
12239 
12240   /* Early out */
12241   if (depth <= svn_depth_empty
12242       || node_kind != svn_node_dir
12243       || node_status == svn_wc__db_status_server_excluded
12244       || node_status == svn_wc__db_status_excluded
12245       || node_status == svn_wc__db_status_not_present)
12246     return SVN_NO_ERROR;
12247 
12248   /* And now recurse over the children */
12249 
12250   depth_below_here = depth;
12251 
12252   if (depth == svn_depth_immediates || depth == svn_depth_files)
12253     depth_below_here = svn_depth_empty;
12254 
12255   iterpool = svn_pool_create(scratch_pool);
12256 
12257   SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0,
12258                                  scratch_pool, iterpool));
12259   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
12260     {
12261       const char *child_basename = apr_hash_this_key(hi);
12262       const struct svn_wc__db_base_info_t *child_info;
12263       const char *child_local_relpath;
12264       const char *child_repos_relpath = NULL;
12265 
12266       svn_pool_clear(iterpool);
12267 
12268       child_info = apr_hash_this_val(hi);
12269 
12270       if (child_info->update_root && child_info->kind == svn_node_file)
12271         continue; /* Skip file externals */
12272 
12273       if (depth < svn_depth_immediates && child_info->kind == svn_node_dir)
12274           continue; /* Skip directories */
12275 
12276       child_local_relpath = svn_relpath_join(local_relpath, child_basename,
12277                                              iterpool);
12278 
12279       /* Don't touch nodes that can't be touched via the exclude list */
12280       if (svn_hash_gets(exclude_relpaths, child_local_relpath))
12281           continue;
12282 
12283       /* If the node is still marked 'not-present', then the server did not
12284           re-add it.  So it's really gone in this revision, thus we remove the
12285           node.
12286 
12287           If the node is still marked 'server-excluded' and yet is not the same
12288           revision as new_rev, then the server did not re-add it, nor
12289           re-server-exclude it, so we can remove the node. */
12290       if (child_info->status == svn_wc__db_status_not_present
12291           || (child_info->status == svn_wc__db_status_server_excluded &&
12292               child_info->revnum != new_rev))
12293         {
12294           svn_sqlite__stmt_t *stmt;
12295           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12296                                     STMT_DELETE_BASE_NODE));
12297           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath));
12298           SVN_ERR(svn_sqlite__step_done(stmt));
12299           continue;
12300         }
12301 
12302       /* Derive the new URL for the current (child) entry */
12303       if (new_repos_relpath)
12304         child_repos_relpath = svn_relpath_join(new_repos_relpath,
12305                                                child_basename, iterpool);
12306 
12307       SVN_ERR(bump_node_revision(wcroot, child_local_relpath,
12308                                  child_info->status,
12309                                  child_info->kind,
12310                                  child_info->revnum,
12311                                  child_info->repos_relpath,
12312                                  new_repos_id,
12313                                  child_repos_relpath, new_rev,
12314                                  depth_below_here,
12315                                  exclude_relpaths, wcroot_iprops,
12316                                  FALSE /* is_root */,
12317                                  (depth < svn_depth_immediates), db,
12318                                  iterpool));
12319     }
12320 
12321   /* Cleanup */
12322   svn_pool_destroy(iterpool);
12323 
12324   return SVN_NO_ERROR;
12325 }
12326 
12327 /* Helper for svn_wc__db_op_bump_revisions_post_update().
12328  */
12329 static svn_error_t *
bump_revisions_post_update(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_depth_t depth,const char * new_repos_relpath,const char * new_repos_root_url,const char * new_repos_uuid,svn_revnum_t new_revision,apr_hash_t * exclude_relpaths,apr_hash_t * wcroot_iprops,svn_boolean_t empty_update,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)12330 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
12331                            const char *local_relpath,
12332                            svn_wc__db_t *db,
12333                            svn_depth_t depth,
12334                            const char *new_repos_relpath,
12335                            const char *new_repos_root_url,
12336                            const char *new_repos_uuid,
12337                            svn_revnum_t new_revision,
12338                            apr_hash_t *exclude_relpaths,
12339                            apr_hash_t *wcroot_iprops,
12340                            svn_boolean_t empty_update,
12341                            svn_wc_notify_func2_t notify_func,
12342                            void *notify_baton,
12343                            apr_pool_t *scratch_pool)
12344 {
12345   svn_wc__db_status_t status;
12346   svn_node_kind_t kind;
12347   svn_error_t *err;
12348   apr_int64_t new_repos_id = INVALID_REPOS_ID;
12349   svn_revnum_t revision;
12350   const char *repos_relpath;
12351 
12352   err = svn_wc__db_base_get_info_internal(&status, &kind, &revision,
12353                                           &repos_relpath, NULL,
12354                                           NULL, NULL, NULL, NULL, NULL, NULL,
12355                                           NULL, NULL, NULL, NULL,
12356                                           wcroot, local_relpath,
12357                                           scratch_pool, scratch_pool);
12358   if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
12359     {
12360       svn_error_clear(err);
12361       return SVN_NO_ERROR;
12362     }
12363   else
12364     SVN_ERR(err);
12365 
12366   switch (status)
12367     {
12368       case svn_wc__db_status_excluded:
12369       case svn_wc__db_status_server_excluded:
12370       case svn_wc__db_status_not_present:
12371         return SVN_NO_ERROR;
12372 
12373       /* Explicitly ignore other statii */
12374       default:
12375         break;
12376     }
12377 
12378   if (new_repos_root_url != NULL)
12379     SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
12380                             new_repos_uuid,
12381                             wcroot->sdb, scratch_pool));
12382 
12383   SVN_ERR(bump_node_revision(wcroot, local_relpath,
12384                              status, kind,  revision, repos_relpath,
12385                              new_repos_id,
12386                              new_repos_relpath, new_revision,
12387                              depth, exclude_relpaths,
12388                              wcroot_iprops,
12389                              TRUE /* is_root */, FALSE, db,
12390                              scratch_pool));
12391 
12392   /* ### TODO: Use empty_update flag for change knowledge */
12393   SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
12394                                      scratch_pool));
12395 
12396   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
12397                                              SVN_INVALID_REVNUM, notify_func,
12398                                              notify_baton, scratch_pool));
12399 
12400   return SVN_NO_ERROR;
12401 }
12402 
12403 svn_error_t *
svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,const char * new_repos_relpath,const char * new_repos_root_url,const char * new_repos_uuid,svn_revnum_t new_revision,apr_hash_t * exclude_relpaths,apr_hash_t * wcroot_iprops,svn_boolean_t empty_update,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)12404 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
12405                                          const char *local_abspath,
12406                                          svn_depth_t depth,
12407                                          const char *new_repos_relpath,
12408                                          const char *new_repos_root_url,
12409                                          const char *new_repos_uuid,
12410                                          svn_revnum_t new_revision,
12411                                          apr_hash_t *exclude_relpaths,
12412                                          apr_hash_t *wcroot_iprops,
12413                                          svn_boolean_t empty_update,
12414                                          svn_wc_notify_func2_t notify_func,
12415                                          void *notify_baton,
12416                                          apr_pool_t *scratch_pool)
12417 {
12418   const char *local_relpath;
12419   svn_wc__db_wcroot_t *wcroot;
12420 
12421   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12422                               local_abspath, scratch_pool, scratch_pool));
12423 
12424   VERIFY_USABLE_WCROOT(wcroot);
12425 
12426   if (svn_hash_gets(exclude_relpaths, local_relpath))
12427     return SVN_NO_ERROR;
12428 
12429   if (depth == svn_depth_unknown)
12430     depth = svn_depth_infinity;
12431 
12432   SVN_WC__DB_WITH_TXN(
12433     bump_revisions_post_update(wcroot, local_relpath, db,
12434                                depth, new_repos_relpath, new_repos_root_url,
12435                                new_repos_uuid, new_revision,
12436                                exclude_relpaths, wcroot_iprops, empty_update,
12437                                notify_func, notify_baton, scratch_pool),
12438     wcroot);
12439 
12440   return SVN_NO_ERROR;
12441 }
12442 
12443 /* The body of svn_wc__db_lock_add().
12444  */
12445 static svn_error_t *
lock_add_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const svn_wc__db_lock_t * lock,apr_pool_t * scratch_pool)12446 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
12447              const char *local_relpath,
12448              const svn_wc__db_lock_t *lock,
12449              apr_pool_t *scratch_pool)
12450 {
12451   svn_sqlite__stmt_t *stmt;
12452   const char *repos_relpath;
12453   apr_int64_t repos_id;
12454 
12455   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12456                                             &repos_relpath, &repos_id,
12457                                             NULL, NULL, NULL, NULL, NULL,
12458                                             NULL, NULL, NULL, NULL, NULL,
12459                                             wcroot, local_relpath,
12460                                             scratch_pool, scratch_pool));
12461 
12462   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
12463   SVN_ERR(svn_sqlite__bindf(stmt, "iss",
12464                             repos_id, repos_relpath, lock->token));
12465 
12466   if (lock->owner != NULL)
12467     SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
12468 
12469   if (lock->comment != NULL)
12470     SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
12471 
12472   if (lock->date != 0)
12473     SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
12474 
12475   SVN_ERR(svn_sqlite__insert(NULL, stmt));
12476 
12477   return SVN_NO_ERROR;
12478 }
12479 
12480 
12481 svn_error_t *
svn_wc__db_lock_add(svn_wc__db_t * db,const char * local_abspath,const svn_wc__db_lock_t * lock,apr_pool_t * scratch_pool)12482 svn_wc__db_lock_add(svn_wc__db_t *db,
12483                     const char *local_abspath,
12484                     const svn_wc__db_lock_t *lock,
12485                     apr_pool_t *scratch_pool)
12486 {
12487   svn_wc__db_wcroot_t *wcroot;
12488   const char *local_relpath;
12489 
12490   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12491   SVN_ERR_ASSERT(lock != NULL);
12492 
12493   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12494                               local_abspath, scratch_pool, scratch_pool));
12495   VERIFY_USABLE_WCROOT(wcroot);
12496 
12497   SVN_WC__DB_WITH_TXN(
12498     lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
12499     wcroot);
12500 
12501   /* There may be some entries, and the lock info is now out of date.  */
12502   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12503 
12504   return SVN_NO_ERROR;
12505 }
12506 
12507 
12508 /* The body of svn_wc__db_lock_remove().
12509  */
12510 static svn_error_t *
lock_remove_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_skel_t * work_items,apr_pool_t * scratch_pool)12511 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
12512                 const char *local_relpath,
12513                 svn_skel_t *work_items,
12514                 apr_pool_t *scratch_pool)
12515 {
12516   const char *repos_relpath;
12517   apr_int64_t repos_id;
12518   svn_sqlite__stmt_t *stmt;
12519 
12520   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12521                                             &repos_relpath, &repos_id,
12522                                             NULL, NULL, NULL, NULL, NULL,
12523                                             NULL, NULL, NULL, NULL, NULL,
12524                                             wcroot, local_relpath,
12525                                             scratch_pool, scratch_pool));
12526 
12527   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12528                                     STMT_DELETE_LOCK));
12529   SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
12530 
12531   SVN_ERR(svn_sqlite__step_done(stmt));
12532 
12533   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12534 
12535   return SVN_NO_ERROR;
12536 }
12537 
12538 
12539 svn_error_t *
svn_wc__db_lock_remove(svn_wc__db_t * db,const char * local_abspath,svn_skel_t * work_items,apr_pool_t * scratch_pool)12540 svn_wc__db_lock_remove(svn_wc__db_t *db,
12541                        const char *local_abspath,
12542                        svn_skel_t *work_items,
12543                        apr_pool_t *scratch_pool)
12544 {
12545   svn_wc__db_wcroot_t *wcroot;
12546   const char *local_relpath;
12547 
12548   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12549 
12550   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12551                               local_abspath, scratch_pool, scratch_pool));
12552   VERIFY_USABLE_WCROOT(wcroot);
12553 
12554   SVN_WC__DB_WITH_TXN(
12555     lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool),
12556     wcroot);
12557 
12558   /* There may be some entries, and the lock info is now out of date.  */
12559   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12560 
12561   return SVN_NO_ERROR;
12562 }
12563 
12564 /* A helper for scan_addition().
12565  * Compute moved-from information for the node at LOCAL_RELPATH which
12566  * has been determined as having been moved-here.
12567  * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
12568  * path of the move-source node in *MOVED_FROM_RELPATH.
12569  * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
12570  * to the path of the op-root of the delete-half of the move.
12571  * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
12572  * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
12573  * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
12574  * of the move. */
12575 static svn_error_t *
get_moved_from_info(const char ** moved_from_relpath,const char ** moved_from_op_root_relpath,const char * moved_to_op_root_relpath,int * op_depth,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12576 get_moved_from_info(const char **moved_from_relpath,
12577                     const char **moved_from_op_root_relpath,
12578                     const char *moved_to_op_root_relpath,
12579                     int *op_depth,
12580                     svn_wc__db_wcroot_t *wcroot,
12581                     const char *local_relpath,
12582                     apr_pool_t *result_pool,
12583                     apr_pool_t *scratch_pool)
12584 {
12585   svn_sqlite__stmt_t *stmt;
12586   svn_boolean_t have_row;
12587 
12588   /* Run a query to get the moved-from path from the DB. */
12589   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12590                                     STMT_SELECT_MOVED_FROM_RELPATH));
12591   SVN_ERR(svn_sqlite__bindf(stmt, "is",
12592                             wcroot->wc_id, moved_to_op_root_relpath));
12593   SVN_ERR(svn_sqlite__step(&have_row, stmt));
12594 
12595   if (!have_row)
12596     {
12597       /* The move was only recorded at the copy-half, possibly because
12598        * the move operation was interrupted mid-way between the copy
12599        * and the delete. Treat this node as a normal copy. */
12600       if (moved_from_relpath)
12601         *moved_from_relpath = NULL;
12602       if (moved_from_op_root_relpath)
12603         *moved_from_op_root_relpath = NULL;
12604 
12605       SVN_ERR(svn_sqlite__reset(stmt));
12606       return SVN_NO_ERROR;
12607     }
12608 
12609   if (op_depth)
12610     *op_depth = svn_sqlite__column_int(stmt, 1);
12611 
12612   if (moved_from_relpath || moved_from_op_root_relpath)
12613     {
12614       const char *db_delete_op_root_relpath;
12615 
12616       /* The moved-from path from the DB is the relpath of
12617        * the op_root of the delete-half of the move. */
12618       db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12619                                                           result_pool);
12620       if (moved_from_op_root_relpath)
12621         *moved_from_op_root_relpath = db_delete_op_root_relpath;
12622 
12623       if (moved_from_relpath)
12624         {
12625           if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12626             {
12627               /* LOCAL_RELPATH is the op_root of the copied-half of the
12628                * move, so the correct MOVED_FROM_ABSPATH is the op-root
12629                * of the delete-half. */
12630               *moved_from_relpath = db_delete_op_root_relpath;
12631             }
12632           else
12633             {
12634               const char *child_relpath;
12635 
12636               /* LOCAL_RELPATH is a child that was copied along with the
12637                * op_root of the copied-half of the move. Construct the
12638                * corresponding path beneath the op_root of the delete-half. */
12639 
12640               /* Grab the child path relative to the op_root of the move
12641                * destination. */
12642               child_relpath = svn_relpath_skip_ancestor(
12643                                 moved_to_op_root_relpath, local_relpath);
12644 
12645               SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12646 
12647               /* This join is valid because LOCAL_RELPATH has not been moved
12648                * within the copied-half of the move yet -- else, it would
12649                * be its own op_root. */
12650               *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12651                                                      child_relpath,
12652                                                      result_pool);
12653             }
12654         }
12655     }
12656 
12657   SVN_ERR(svn_sqlite__reset(stmt));
12658 
12659   return SVN_NO_ERROR;
12660 }
12661 
12662 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12663    DB+LOCAL_ABSPATH.
12664 
12665    The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12666    is no 'copy-from' repository.  */
12667 static svn_error_t *
scan_addition(svn_wc__db_status_t * status,const char ** op_root_relpath_p,const char ** repos_relpath,apr_int64_t * repos_id,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,const char ** moved_from_relpath,const char ** moved_from_op_root_relpath,int * moved_from_op_depth,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12668 scan_addition(svn_wc__db_status_t *status,
12669               const char **op_root_relpath_p,
12670               const char **repos_relpath,
12671               apr_int64_t *repos_id,
12672               const char **original_repos_relpath,
12673               apr_int64_t *original_repos_id,
12674               svn_revnum_t *original_revision,
12675               const char **moved_from_relpath,
12676               const char **moved_from_op_root_relpath,
12677               int *moved_from_op_depth,
12678               svn_wc__db_wcroot_t *wcroot,
12679               const char *local_relpath,
12680               apr_pool_t *result_pool,
12681               apr_pool_t *scratch_pool)
12682 {
12683   const char *op_root_relpath;
12684   const char *build_relpath = "";
12685 
12686   /* Initialize most of the OUT parameters. Generally, we'll only be filling
12687      in a subset of these, so it is easier to init all up front. Note that
12688      the STATUS parameter will be initialized once we read the status of
12689      the specified node.  */
12690   if (op_root_relpath_p)
12691     *op_root_relpath_p = NULL;
12692   if (original_repos_relpath)
12693     *original_repos_relpath = NULL;
12694   if (original_repos_id)
12695     *original_repos_id = INVALID_REPOS_ID;
12696   if (original_revision)
12697     *original_revision = SVN_INVALID_REVNUM;
12698   if (moved_from_relpath)
12699     *moved_from_relpath = NULL;
12700   if (moved_from_op_root_relpath)
12701     *moved_from_op_root_relpath = NULL;
12702   if (moved_from_op_depth)
12703     *moved_from_op_depth = 0;
12704 
12705   {
12706     svn_sqlite__stmt_t *stmt;
12707     svn_boolean_t have_row;
12708     svn_wc__db_status_t presence;
12709     int op_depth;
12710     const char *repos_prefix_path;
12711 
12712     /* ### is it faster to fetch fewer columns? */
12713     SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12714                                       STMT_SELECT_WORKING_NODE));
12715     SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12716     SVN_ERR(svn_sqlite__step(&have_row, stmt));
12717 
12718     if (!have_row)
12719       {
12720         /* Reset statement before returning */
12721         SVN_ERR(svn_sqlite__reset(stmt));
12722 
12723         /* ### maybe we should return a usage error instead?  */
12724         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12725                                  _("The node '%s' was not found."),
12726                                  path_for_error_message(wcroot,
12727                                                         local_relpath,
12728                                                         scratch_pool));
12729       }
12730 
12731     presence = svn_sqlite__column_token(stmt, 1, presence_map);
12732 
12733     /* The starting node should exist normally.  */
12734     op_depth = svn_sqlite__column_int(stmt, 0);
12735     if (op_depth == 0 || (presence != svn_wc__db_status_normal
12736                           && presence != svn_wc__db_status_incomplete))
12737       /* reset the statement as part of the error generation process */
12738       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12739                                svn_sqlite__reset(stmt),
12740                                _("Expected node '%s' to be added."),
12741                                path_for_error_message(wcroot,
12742                                                       local_relpath,
12743                                                       scratch_pool));
12744 
12745     if (original_revision)
12746       *original_revision = svn_sqlite__column_revnum(stmt, 12);
12747 
12748     /* Provide the default status; we'll override as appropriate. */
12749     if (status)
12750       {
12751         if (presence == svn_wc__db_status_normal)
12752           *status = svn_wc__db_status_added;
12753         else
12754           *status = svn_wc__db_status_incomplete;
12755       }
12756 
12757 
12758     /* Calculate the op root local path components */
12759     op_root_relpath = svn_relpath_prefix(local_relpath, op_depth,
12760                                          scratch_pool);
12761     repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath,
12762                                                   local_relpath);
12763 
12764     if (op_root_relpath_p)
12765       *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12766 
12767     /* ### This if-statement is quite redundant.
12768      * ### We're checking all these values again within the body anyway.
12769      * ### The body should be broken up appropriately and move into the
12770      * ### outer scope. */
12771     if (original_repos_relpath
12772         || original_repos_id
12773         || (original_revision
12774                 && *original_revision == SVN_INVALID_REVNUM)
12775         || status
12776         || moved_from_relpath || moved_from_op_root_relpath)
12777       {
12778         if (local_relpath != op_root_relpath)
12779           /* requery to get the add/copy root */
12780           {
12781             SVN_ERR(svn_sqlite__reset(stmt));
12782 
12783             SVN_ERR(svn_sqlite__bindf(stmt, "is",
12784                                       wcroot->wc_id, op_root_relpath));
12785             SVN_ERR(svn_sqlite__step(&have_row, stmt));
12786 
12787             if (!have_row)
12788               {
12789                 /* Reset statement before returning */
12790                 SVN_ERR(svn_sqlite__reset(stmt));
12791 
12792                 /* ### maybe we should return a usage error instead?  */
12793                 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12794                                          _("The node '%s' was not found."),
12795                                          path_for_error_message(wcroot,
12796                                                                 op_root_relpath,
12797                                                                 scratch_pool));
12798               }
12799 
12800             if (original_revision
12801                     && *original_revision == SVN_INVALID_REVNUM)
12802               *original_revision = svn_sqlite__column_revnum(stmt, 12);
12803           }
12804 
12805         if (original_repos_relpath)
12806           *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12807                                                             result_pool);
12808 
12809         if (!svn_sqlite__column_is_null(stmt, 10)
12810             && (status
12811                 || original_repos_id
12812                 || moved_from_relpath || moved_from_op_root_relpath))
12813           /* If column 10 (original_repos_id) is NULL,
12814              this is a plain add, not a copy or a move */
12815           {
12816             svn_boolean_t moved_here;
12817             if (original_repos_id)
12818               *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12819 
12820             moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12821             if (status)
12822               *status = moved_here ? svn_wc__db_status_moved_here
12823                                    : svn_wc__db_status_copied;
12824 
12825             if (moved_here
12826                 && (moved_from_relpath || moved_from_op_root_relpath))
12827               {
12828                 svn_error_t *err;
12829 
12830                 err = get_moved_from_info(moved_from_relpath,
12831                                           moved_from_op_root_relpath,
12832                                           op_root_relpath,
12833                                           moved_from_op_depth,
12834                                           wcroot, local_relpath,
12835                                           result_pool,
12836                                           scratch_pool);
12837 
12838                 if (err)
12839                   return svn_error_compose_create(
12840                                 err, svn_sqlite__reset(stmt));
12841               }
12842           }
12843       }
12844 
12845 
12846     /* ### This loop here is to skip up to the first node which is a BASE node,
12847        because base_get_info() doesn't accommodate the scenario that
12848        we're looking at here; we found the true op_root, which may be inside
12849        further changed trees. */
12850     if (repos_relpath || repos_id)
12851       {
12852         const char *base_relpath;
12853 
12854         while (TRUE)
12855           {
12856             const char *tmp;
12857 
12858             SVN_ERR(svn_sqlite__reset(stmt));
12859 
12860             /* Pointing at op_depth, look at the parent */
12861             repos_prefix_path =
12862                 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12863                                  repos_prefix_path,
12864                                  scratch_pool);
12865             op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12866 
12867 
12868             SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12869             SVN_ERR(svn_sqlite__step(&have_row, stmt));
12870 
12871             if (! have_row)
12872               break;
12873 
12874             op_depth = svn_sqlite__column_int(stmt, 0);
12875 
12876             /* Skip to op_depth */
12877             tmp = op_root_relpath;
12878 
12879             op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth,
12880                                                  scratch_pool);
12881             repos_prefix_path = svn_relpath_join(
12882                                                  svn_relpath_skip_ancestor(op_root_relpath, tmp),
12883                                                  repos_prefix_path, scratch_pool);
12884           }
12885 
12886       SVN_ERR(svn_sqlite__reset(stmt));
12887 
12888       build_relpath = repos_prefix_path;
12889 
12890       /* If we're here, then we have an added/copied/moved (start) node, and
12891          CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12892          information for the current node, and use that to compute the start
12893          node's repository information.  */
12894       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12895                                                 &base_relpath, repos_id,
12896                                                 NULL, NULL, NULL, NULL, NULL,
12897                                                 NULL, NULL, NULL, NULL, NULL,
12898                                                 wcroot, op_root_relpath,
12899                                                 scratch_pool, scratch_pool));
12900 
12901         if (repos_relpath)
12902           *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12903                                             result_pool);
12904       }
12905     else
12906       SVN_ERR(svn_sqlite__reset(stmt));
12907   }
12908   /* Postconditions */
12909 #ifdef SVN_DEBUG
12910   if (status)
12911     {
12912       SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12913                      || *status == svn_wc__db_status_copied
12914                      || *status == svn_wc__db_status_incomplete
12915                      || *status == svn_wc__db_status_moved_here);
12916       if (*status == svn_wc__db_status_added)
12917         {
12918           SVN_ERR_ASSERT(!original_repos_relpath
12919                          || *original_repos_relpath == NULL);
12920           SVN_ERR_ASSERT(!original_revision
12921                          || *original_revision == SVN_INVALID_REVNUM);
12922           SVN_ERR_ASSERT(!original_repos_id
12923                          || *original_repos_id == INVALID_REPOS_ID);
12924         }
12925       /* An upgrade with a missing directory can leave INCOMPLETE working
12926          op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12927        */
12928       else if (*status != svn_wc__db_status_incomplete)
12929         {
12930           SVN_ERR_ASSERT(!original_repos_relpath
12931                          || *original_repos_relpath != NULL);
12932           SVN_ERR_ASSERT(!original_revision
12933                          || *original_revision != SVN_INVALID_REVNUM);
12934           SVN_ERR_ASSERT(!original_repos_id
12935                          || *original_repos_id != INVALID_REPOS_ID);
12936         }
12937     }
12938   SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12939 #endif
12940 
12941   return SVN_NO_ERROR;
12942 }
12943 
12944 svn_error_t *
svn_wc__db_scan_addition_internal(svn_wc__db_status_t * status,const char ** op_root_relpath_p,const char ** repos_relpath,apr_int64_t * repos_id,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12945 svn_wc__db_scan_addition_internal(
12946               svn_wc__db_status_t *status,
12947               const char **op_root_relpath_p,
12948               const char **repos_relpath,
12949               apr_int64_t *repos_id,
12950               const char **original_repos_relpath,
12951               apr_int64_t *original_repos_id,
12952               svn_revnum_t *original_revision,
12953               svn_wc__db_wcroot_t *wcroot,
12954               const char *local_relpath,
12955               apr_pool_t *result_pool,
12956               apr_pool_t *scratch_pool)
12957 {
12958   return svn_error_trace(
12959       scan_addition(status, op_root_relpath_p, repos_relpath, repos_id,
12960                     original_repos_relpath, original_repos_id,
12961                     original_revision, NULL, NULL, NULL,
12962                     wcroot, local_relpath, result_pool, scratch_pool));
12963 }
12964 
12965 svn_error_t *
svn_wc__db_scan_addition(svn_wc__db_status_t * status,const char ** op_root_abspath,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,const char ** original_repos_relpath,const char ** original_root_url,const char ** original_uuid,svn_revnum_t * original_revision,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12966 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12967                          const char **op_root_abspath,
12968                          const char **repos_relpath,
12969                          const char **repos_root_url,
12970                          const char **repos_uuid,
12971                          const char **original_repos_relpath,
12972                          const char **original_root_url,
12973                          const char **original_uuid,
12974                          svn_revnum_t *original_revision,
12975                          svn_wc__db_t *db,
12976                          const char *local_abspath,
12977                          apr_pool_t *result_pool,
12978                          apr_pool_t *scratch_pool)
12979 {
12980   svn_wc__db_wcroot_t *wcroot;
12981   const char *local_relpath;
12982   const char *op_root_relpath = NULL;
12983   apr_int64_t repos_id = INVALID_REPOS_ID;
12984   apr_int64_t original_repos_id = INVALID_REPOS_ID;
12985   apr_int64_t *repos_id_p
12986     = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12987   apr_int64_t *original_repos_id_p
12988     = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12989 
12990   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12991 
12992   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12993                               local_abspath, scratch_pool, scratch_pool));
12994   VERIFY_USABLE_WCROOT(wcroot);
12995 
12996   SVN_WC__DB_WITH_TXN4(
12997           scan_addition(status,
12998                         op_root_abspath
12999                                 ? &op_root_relpath
13000                                 : NULL,
13001                         repos_relpath, repos_id_p,
13002                         original_repos_relpath, original_repos_id_p,
13003                         original_revision,
13004                         NULL, NULL, NULL,
13005                         wcroot, local_relpath, result_pool, scratch_pool),
13006           svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot,
13007                                       repos_id, result_pool),
13008           svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
13009                                       wcroot, original_repos_id,
13010                                       result_pool),
13011           SVN_NO_ERROR,
13012           wcroot);
13013 
13014   if (op_root_abspath)
13015     *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13016                                        result_pool);
13017   /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
13018   SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
13019 
13020   return SVN_NO_ERROR;
13021 }
13022 
13023 svn_error_t *
svn_wc__db_scan_moved(const char ** moved_from_abspath,const char ** op_root_abspath,const char ** op_root_moved_from_abspath,const char ** moved_from_delete_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13024 svn_wc__db_scan_moved(const char **moved_from_abspath,
13025                       const char **op_root_abspath,
13026                       const char **op_root_moved_from_abspath,
13027                       const char **moved_from_delete_abspath,
13028                       svn_wc__db_t *db,
13029                       const char *local_abspath,
13030                       apr_pool_t *result_pool,
13031                       apr_pool_t *scratch_pool)
13032 {
13033   svn_wc__db_wcroot_t *wcroot;
13034   const char *local_relpath;
13035   svn_wc__db_status_t status;
13036   const char *op_root_relpath = NULL;
13037   const char *moved_from_relpath = NULL;
13038   const char *moved_from_op_root_relpath = NULL;
13039   int moved_from_op_depth = -1;
13040 
13041   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13042 
13043   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13044                               local_abspath, scratch_pool, scratch_pool));
13045   VERIFY_USABLE_WCROOT(wcroot);
13046 
13047   SVN_WC__DB_WITH_TXN(
13048           scan_addition(&status,
13049                         op_root_abspath
13050                                 ? &op_root_relpath
13051                                 : NULL,
13052                         NULL, NULL,
13053                         NULL, NULL, NULL,
13054                         moved_from_abspath
13055                             ? &moved_from_relpath
13056                             : NULL,
13057                         (op_root_moved_from_abspath
13058                          || moved_from_delete_abspath)
13059                             ? &moved_from_op_root_relpath
13060                             : NULL,
13061                         moved_from_delete_abspath
13062                             ? &moved_from_op_depth
13063                             : NULL,
13064                         wcroot, local_relpath, scratch_pool, scratch_pool),
13065           wcroot);
13066 
13067   if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
13068     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
13069                              _("Path '%s' was not moved here"),
13070                              path_for_error_message(wcroot, local_relpath,
13071                                                     scratch_pool));
13072 
13073   if (op_root_abspath)
13074     *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13075                                        result_pool);
13076 
13077   if (moved_from_abspath)
13078     *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
13079                                           result_pool);
13080 
13081   if (op_root_moved_from_abspath)
13082     *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
13083                                                   moved_from_op_root_relpath,
13084                                                   result_pool);
13085 
13086   /* The deleted node is either where we moved from, or one of its ancestors */
13087   if (moved_from_delete_abspath)
13088     {
13089       const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath,
13090                                            moved_from_op_depth, scratch_pool);
13091 
13092       *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
13093                                                    scratch_pool);
13094     }
13095 
13096   return SVN_NO_ERROR;
13097 }
13098 
13099 /* ### Recursive helper for svn_wc__db_follow_moved_to()
13100  */
13101 static svn_error_t *
follow_moved_to(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_array_header_t ** moved_tos,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13102 follow_moved_to(svn_wc__db_wcroot_t *wcroot,
13103                 const char *local_relpath,
13104                 int op_depth,
13105                 apr_array_header_t **moved_tos,
13106                 apr_pool_t *result_pool,
13107                 apr_pool_t *scratch_pool)
13108 {
13109   svn_sqlite__stmt_t *stmt;
13110   svn_boolean_t have_row;
13111   int shadowing_op_depth;
13112   const char *ancestor_relpath;
13113   const char *node_moved_to = NULL;
13114   int i;
13115 
13116   /* Obtain the depth of the node directly shadowing local_relpath
13117      as it exists at OP_DEPTH, and perhaps moved to info */
13118   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13119                                     STMT_SELECT_OP_DEPTH_MOVED_TO));
13120   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13121                             op_depth));
13122   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13123   if (have_row)
13124     {
13125       shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
13126       node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
13127 
13128       if (node_moved_to)
13129         {
13130           struct svn_wc__db_moved_to_t *moved_to;
13131 
13132           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13133           moved_to->op_depth = shadowing_op_depth;
13134           moved_to->local_relpath = node_moved_to;
13135           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13136         }
13137     }
13138 
13139   SVN_ERR(svn_sqlite__reset(stmt));
13140 
13141   if (!have_row)
13142     {
13143       /* Node is not shadowed, so not moved */
13144       return SVN_NO_ERROR;
13145     }
13146   else if (node_moved_to)
13147     {
13148       /* Moved directly, so we have the final location */
13149       return SVN_NO_ERROR;
13150     }
13151   /* Need to handle being moved via an ancestor. */
13152   ancestor_relpath = local_relpath;
13153   for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
13154     {
13155       const char *ancestor_moved_to;
13156 
13157       ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
13158 
13159       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13160                                         STMT_SELECT_MOVED_TO));
13161       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
13162                                 shadowing_op_depth));
13163       SVN_ERR(svn_sqlite__step_row(stmt));
13164 
13165       ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
13166       SVN_ERR(svn_sqlite__reset(stmt));
13167       if (ancestor_moved_to)
13168         {
13169           struct svn_wc__db_moved_to_t *moved_to;
13170 
13171           node_moved_to
13172               = svn_relpath_join(ancestor_moved_to,
13173                                  svn_relpath_skip_ancestor(ancestor_relpath,
13174                                                            local_relpath),
13175                                  result_pool);
13176 
13177           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13178           moved_to->op_depth = shadowing_op_depth;
13179           moved_to->local_relpath = node_moved_to;
13180           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13181 
13182           SVN_ERR(follow_moved_to(wcroot, node_moved_to,
13183                                   relpath_depth(ancestor_moved_to),
13184                                   moved_tos, result_pool, scratch_pool));
13185 
13186           break;
13187         }
13188     }
13189 
13190   return SVN_NO_ERROR;
13191 }
13192 
13193 svn_error_t *
svn_wc__db_follow_moved_to(apr_array_header_t ** moved_tos,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13194 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
13195                            svn_wc__db_t *db,
13196                            const char *local_abspath,
13197                            apr_pool_t *result_pool,
13198                            apr_pool_t *scratch_pool)
13199 {
13200   svn_wc__db_wcroot_t *wcroot;
13201   const char *local_relpath;
13202 
13203   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13204 
13205   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13206                               local_abspath, scratch_pool, scratch_pool));
13207   VERIFY_USABLE_WCROOT(wcroot);
13208 
13209   *moved_tos = apr_array_make(result_pool, 0,
13210                               sizeof(struct svn_wc__db_moved_to_t *));
13211 
13212   /* ### Wrap in a transaction */
13213   SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos,
13214                                       result_pool, scratch_pool),
13215                       wcroot);
13216 
13217   /* ### Convert moved_to to abspath */
13218 
13219   return SVN_NO_ERROR;
13220 }
13221 
13222 svn_error_t *
svn_wc__db_scan_moved_to_internal(const char ** move_src_relpath,const char ** move_dst_relpath,const char ** delete_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13223 svn_wc__db_scan_moved_to_internal(const char **move_src_relpath,
13224                                   const char **move_dst_relpath,
13225                                   const char **delete_relpath,
13226                                   svn_wc__db_wcroot_t *wcroot,
13227                                   const char *local_relpath,
13228                                   int op_depth,
13229                                   apr_pool_t *result_pool,
13230                                   apr_pool_t *scratch_pool)
13231 {
13232   svn_sqlite__stmt_t *stmt;
13233   svn_boolean_t have_row;
13234   int delete_op_depth;
13235   const char *relpath = local_relpath;
13236   const char *dst_relpath;
13237 
13238   SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */
13239 
13240   if (move_src_relpath)
13241     *move_src_relpath = NULL;
13242   if (move_dst_relpath)
13243     *move_dst_relpath = NULL;
13244   if (delete_relpath)
13245     *delete_relpath = NULL;
13246 
13247   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13248                                     STMT_SELECT_OP_DEPTH_MOVED_TO));
13249   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
13250 
13251   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13252 
13253   if (!have_row)
13254     {
13255       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
13256                                svn_sqlite__reset(stmt),
13257                                _("Node '%s' is not shadowed"),
13258                                path_for_error_message(wcroot, local_relpath,
13259                                                       scratch_pool));
13260     }
13261 
13262   delete_op_depth = svn_sqlite__column_int(stmt, 0);
13263   dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
13264 
13265   SVN_ERR(svn_sqlite__reset(stmt));
13266 
13267   while (!dst_relpath && have_row)
13268     {
13269       relpath = svn_relpath_dirname(relpath, scratch_pool);
13270 
13271       if (relpath_depth(relpath) < delete_op_depth)
13272         break;
13273 
13274       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13275                                         STMT_SELECT_DEPTH_NODE));
13276       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath,
13277                                 delete_op_depth));
13278 
13279       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13280 
13281       if (have_row)
13282         dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool);
13283 
13284       SVN_ERR(svn_sqlite__reset(stmt));
13285     }
13286 
13287   if (dst_relpath)
13288     {
13289       if (move_src_relpath)
13290         *move_src_relpath = apr_pstrdup(result_pool, relpath);
13291 
13292       if (move_dst_relpath)
13293         *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath);
13294 
13295       if (delete_relpath)
13296         *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth,
13297                                              result_pool);
13298     }
13299 
13300   return SVN_NO_ERROR;
13301 }
13302 
13303 /* Public (within libsvn_wc) absolute path version of
13304    svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
13305    BASE. */
13306 svn_error_t *
svn_wc__db_base_moved_to(const char ** move_dst_abspath,const char ** move_dst_op_root_abspath,const char ** move_src_root_abspath,const char ** delete_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13307 svn_wc__db_base_moved_to(const char **move_dst_abspath,
13308                          const char **move_dst_op_root_abspath,
13309                          const char **move_src_root_abspath,
13310                          const char **delete_abspath,
13311                          svn_wc__db_t *db,
13312                          const char *local_abspath,
13313                          apr_pool_t *result_pool,
13314                          apr_pool_t *scratch_pool)
13315 {
13316   svn_wc__db_wcroot_t *wcroot;
13317   const char *local_relpath;
13318   const char *dst_root_relpath;
13319   const char *src_root_relpath, *delete_relpath;
13320 
13321   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13322 
13323 
13324   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13325                               local_abspath, scratch_pool, scratch_pool));
13326   VERIFY_USABLE_WCROOT(wcroot);
13327 
13328   SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath,
13329                                                         &dst_root_relpath,
13330                                                         &delete_relpath,
13331                                                         wcroot, local_relpath,
13332                                                         0 /* BASE */,
13333                                                         scratch_pool,
13334                                                         scratch_pool),
13335                       wcroot);
13336 
13337   if (move_dst_abspath)
13338     *move_dst_abspath =
13339         dst_root_relpath
13340           ? svn_dirent_join(wcroot->abspath,
13341                             svn_dirent_join(
13342                                     dst_root_relpath,
13343                                     svn_relpath_skip_ancestor(src_root_relpath,
13344                                                               local_relpath),
13345                                     scratch_pool),
13346                             result_pool)
13347           : NULL;
13348 
13349   if (move_dst_op_root_abspath)
13350     *move_dst_op_root_abspath =
13351           dst_root_relpath
13352               ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool)
13353               : NULL;
13354 
13355   if (move_src_root_abspath)
13356     *move_src_root_abspath =
13357           src_root_relpath
13358               ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool)
13359               : NULL;
13360 
13361   if (delete_abspath)
13362     *delete_abspath =
13363           delete_relpath
13364               ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool)
13365               : NULL;
13366 
13367   return SVN_NO_ERROR;
13368 }
13369 
13370 svn_error_t *
svn_wc__db_upgrade_begin(svn_sqlite__db_t ** sdb,apr_int64_t * repos_id,apr_int64_t * wc_id,svn_wc__db_t * wc_db,const char * dir_abspath,const char * repos_root_url,const char * repos_uuid,apr_pool_t * scratch_pool)13371 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
13372                          apr_int64_t *repos_id,
13373                          apr_int64_t *wc_id,
13374                          svn_wc__db_t *wc_db,
13375                          const char *dir_abspath,
13376                          const char *repos_root_url,
13377                          const char *repos_uuid,
13378                          apr_pool_t *scratch_pool)
13379 {
13380   svn_wc__db_wcroot_t *wcroot;
13381 
13382   /* Upgrade is inherently exclusive so specify exclusive locking. */
13383   SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
13384                     repos_root_url, repos_uuid,
13385                     SDB_FILE,
13386                     NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
13387                     TRUE /* exclusive */,
13388                     0 /* timeout */,
13389                     wc_db->state_pool, scratch_pool));
13390 
13391   SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
13392                                        apr_pstrdup(wc_db->state_pool,
13393                                                    dir_abspath),
13394                                        *sdb, *wc_id, FORMAT_FROM_SDB,
13395                                        FALSE /* auto-upgrade */,
13396                                        wc_db->state_pool, scratch_pool));
13397 
13398   /* The WCROOT is complete. Stash it into DB.  */
13399   svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
13400 
13401   return SVN_NO_ERROR;
13402 }
13403 
13404 svn_error_t *
svn_wc__db_upgrade_insert_external(svn_wc__db_t * db,const char * local_abspath,svn_node_kind_t kind,const char * parent_abspath,const char * def_local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t def_peg_revision,svn_revnum_t def_revision,apr_pool_t * scratch_pool)13405 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13406                                    const char *local_abspath,
13407                                    svn_node_kind_t kind,
13408                                    const char *parent_abspath,
13409                                    const char *def_local_abspath,
13410                                    const char *repos_relpath,
13411                                    const char *repos_root_url,
13412                                    const char *repos_uuid,
13413                                    svn_revnum_t def_peg_revision,
13414                                    svn_revnum_t def_revision,
13415                                    apr_pool_t *scratch_pool)
13416 {
13417   svn_wc__db_wcroot_t *wcroot;
13418   const char *def_local_relpath;
13419   svn_sqlite__stmt_t *stmt;
13420   svn_boolean_t have_row;
13421   apr_int64_t repos_id;
13422 
13423   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13424 
13425   /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13426    * WC, i.e. where the svn:externals prop is set. The external target path
13427    * itself may be "hidden behind" other working copies. */
13428   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13429                                                 db, def_local_abspath,
13430                                                 scratch_pool, scratch_pool));
13431   VERIFY_USABLE_WCROOT(wcroot);
13432 
13433 
13434   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13435                                     STMT_SELECT_REPOSITORY));
13436   SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13437   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13438 
13439   if (have_row)
13440     repos_id = svn_sqlite__column_int64(stmt, 0);
13441   SVN_ERR(svn_sqlite__reset(stmt));
13442 
13443   if (!have_row)
13444     {
13445       /* Need to set up a new repository row. */
13446       SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13447                               wcroot->sdb, scratch_pool));
13448     }
13449 
13450   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13451                                     STMT_INSERT_EXTERNAL));
13452 
13453   /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13454    * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13455   SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13456                             wcroot->wc_id,
13457                             svn_dirent_skip_ancestor(wcroot->abspath,
13458                                                      local_abspath),
13459                             svn_dirent_skip_ancestor(wcroot->abspath,
13460                                                      parent_abspath),
13461                             "normal",
13462                             kind_map, kind,
13463                             def_local_relpath,
13464                             repos_id,
13465                             repos_relpath));
13466 
13467   if (SVN_IS_VALID_REVNUM(def_peg_revision))
13468     SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13469 
13470   if (SVN_IS_VALID_REVNUM(def_revision))
13471     SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13472 
13473   SVN_ERR(svn_sqlite__insert(NULL, stmt));
13474 
13475   return SVN_NO_ERROR;
13476 }
13477 
13478 svn_error_t *
svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t * wcroot,const svn_skel_t * work_item,apr_pool_t * scratch_pool)13479 svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot,
13480                            const svn_skel_t *work_item,
13481                            apr_pool_t *scratch_pool)
13482 {
13483   /* Add the work item(s) to the WORK_QUEUE.  */
13484   return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13485                                         scratch_pool));
13486 }
13487 
13488 svn_error_t *
svn_wc__db_wq_add(svn_wc__db_t * db,const char * wri_abspath,const svn_skel_t * work_item,apr_pool_t * scratch_pool)13489 svn_wc__db_wq_add(svn_wc__db_t *db,
13490                   const char *wri_abspath,
13491                   const svn_skel_t *work_item,
13492                   apr_pool_t *scratch_pool)
13493 {
13494   svn_wc__db_wcroot_t *wcroot;
13495   const char *local_relpath;
13496 
13497   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13498 
13499   /* Quick exit, if there are no work items to queue up.  */
13500   if (work_item == NULL)
13501     return SVN_NO_ERROR;
13502 
13503   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13504                               wri_abspath, scratch_pool, scratch_pool));
13505   VERIFY_USABLE_WCROOT(wcroot);
13506 
13507   /* Add the work item(s) to the WORK_QUEUE.  */
13508   return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13509                                         scratch_pool));
13510 }
13511 
13512 /* The body of svn_wc__db_wq_fetch_next().
13513  */
13514 static svn_error_t *
wq_fetch_next(apr_uint64_t * id,svn_skel_t ** work_item,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_uint64_t completed_id,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13515 wq_fetch_next(apr_uint64_t *id,
13516               svn_skel_t **work_item,
13517               svn_wc__db_wcroot_t *wcroot,
13518               const char *local_relpath,
13519               apr_uint64_t completed_id,
13520               apr_pool_t *result_pool,
13521               apr_pool_t *scratch_pool)
13522 {
13523   svn_sqlite__stmt_t *stmt;
13524   svn_boolean_t have_row;
13525 
13526   if (completed_id != 0)
13527     {
13528       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13529                                         STMT_DELETE_WORK_ITEM));
13530       SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13531 
13532       SVN_ERR(svn_sqlite__step_done(stmt));
13533     }
13534 
13535   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13536                                     STMT_SELECT_WORK_ITEM));
13537   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13538 
13539   if (!have_row)
13540     {
13541       *id = 0;
13542       *work_item = NULL;
13543     }
13544   else
13545     {
13546       apr_size_t len;
13547       const void *val;
13548 
13549       *id = svn_sqlite__column_int64(stmt, 0);
13550 
13551       val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13552 
13553       *work_item = svn_skel__parse(val, len, result_pool);
13554     }
13555 
13556   return svn_error_trace(svn_sqlite__reset(stmt));
13557 }
13558 
13559 svn_error_t *
svn_wc__db_wq_fetch_next(apr_uint64_t * id,svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,apr_uint64_t completed_id,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13560 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13561                          svn_skel_t **work_item,
13562                          svn_wc__db_t *db,
13563                          const char *wri_abspath,
13564                          apr_uint64_t completed_id,
13565                          apr_pool_t *result_pool,
13566                          apr_pool_t *scratch_pool)
13567 {
13568   svn_wc__db_wcroot_t *wcroot;
13569   const char *local_relpath;
13570 
13571   SVN_ERR_ASSERT(id != NULL);
13572   SVN_ERR_ASSERT(work_item != NULL);
13573   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13574 
13575   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13576                               wri_abspath, scratch_pool, scratch_pool));
13577   VERIFY_USABLE_WCROOT(wcroot);
13578 
13579   SVN_WC__DB_WITH_TXN(
13580     wq_fetch_next(id, work_item,
13581                   wcroot, local_relpath, completed_id,
13582                   result_pool, scratch_pool),
13583     wcroot);
13584 
13585   return SVN_NO_ERROR;
13586 }
13587 
13588 /* Records timestamp and date for one or more files in wcroot */
13589 static svn_error_t *
wq_record(svn_wc__db_wcroot_t * wcroot,apr_hash_t * record_map,apr_pool_t * scratch_pool)13590 wq_record(svn_wc__db_wcroot_t *wcroot,
13591           apr_hash_t *record_map,
13592           apr_pool_t *scratch_pool)
13593 {
13594   apr_hash_index_t *hi;
13595   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13596 
13597   for (hi = apr_hash_first(scratch_pool, record_map); hi;
13598        hi = apr_hash_next(hi))
13599     {
13600       const char *local_abspath = apr_hash_this_key(hi);
13601       const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
13602       const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13603                                                            local_abspath);
13604 
13605       svn_pool_clear(iterpool);
13606 
13607       if (! local_relpath)
13608         continue;
13609 
13610       SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13611                                  dirent->filesize, dirent->mtime,
13612                                  iterpool));
13613     }
13614 
13615   svn_pool_destroy(iterpool);
13616   return SVN_NO_ERROR;
13617 }
13618 
13619 svn_error_t *
svn_wc__db_wq_record_and_fetch_next(apr_uint64_t * id,svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,apr_uint64_t completed_id,apr_hash_t * record_map,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13620 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13621                                     svn_skel_t **work_item,
13622                                     svn_wc__db_t *db,
13623                                     const char *wri_abspath,
13624                                     apr_uint64_t completed_id,
13625                                     apr_hash_t *record_map,
13626                                     apr_pool_t *result_pool,
13627                                     apr_pool_t *scratch_pool)
13628 {
13629   svn_wc__db_wcroot_t *wcroot;
13630   const char *local_relpath;
13631 
13632   SVN_ERR_ASSERT(id != NULL);
13633   SVN_ERR_ASSERT(work_item != NULL);
13634   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13635 
13636   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13637                               wri_abspath, scratch_pool, scratch_pool));
13638   VERIFY_USABLE_WCROOT(wcroot);
13639 
13640   SVN_WC__DB_WITH_TXN(
13641     svn_error_compose_create(
13642             wq_fetch_next(id, work_item,
13643                           wcroot, local_relpath, completed_id,
13644                           result_pool, scratch_pool),
13645             wq_record(wcroot, record_map, scratch_pool)),
13646     wcroot);
13647 
13648   return SVN_NO_ERROR;
13649 }
13650 
13651 
13652 
13653 /* ### temporary API. remove before release.  */
13654 svn_error_t *
svn_wc__db_temp_get_format(int * format,svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13655 svn_wc__db_temp_get_format(int *format,
13656                            svn_wc__db_t *db,
13657                            const char *local_dir_abspath,
13658                            apr_pool_t *scratch_pool)
13659 {
13660   svn_wc__db_wcroot_t *wcroot;
13661   const char *local_relpath;
13662   svn_error_t *err;
13663 
13664   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13665   /* ### assert that we were passed a directory?  */
13666 
13667   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13668                                 local_dir_abspath, scratch_pool, scratch_pool);
13669 
13670   /* If we hit an error examining this directory, then declare this
13671      directory to not be a working copy.  */
13672   if (err)
13673     {
13674       if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13675         return svn_error_trace(err);
13676       svn_error_clear(err);
13677 
13678       /* Remap the returned error.  */
13679       *format = 0;
13680       return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13681                                _("'%s' is not a working copy"),
13682                                svn_dirent_local_style(local_dir_abspath,
13683                                                       scratch_pool));
13684     }
13685 
13686   SVN_ERR_ASSERT(wcroot != NULL);
13687   SVN_ERR_ASSERT(wcroot->format >= 1);
13688 
13689   *format = wcroot->format;
13690 
13691   return SVN_NO_ERROR;
13692 }
13693 
13694 /* ### temporary API. remove before release.  */
13695 svn_wc_adm_access_t *
svn_wc__db_temp_get_access(svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13696 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13697                            const char *local_dir_abspath,
13698                            apr_pool_t *scratch_pool)
13699 {
13700   const char *local_relpath;
13701   svn_wc__db_wcroot_t *wcroot;
13702   svn_error_t *err;
13703 
13704   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13705 
13706   /* ### we really need to assert that we were passed a directory. sometimes
13707      ### adm_retrieve_internal is asked about a file, and then it asks us
13708      ### for an access baton for it. we should definitely return NULL, but
13709      ### ideally: the caller would never ask us about a non-directory.  */
13710 
13711   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13712                             db, local_dir_abspath, scratch_pool, scratch_pool);
13713   if (err)
13714     {
13715       svn_error_clear(err);
13716       return NULL;
13717     }
13718 
13719   if (!wcroot)
13720     return NULL;
13721 
13722   return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13723 }
13724 
13725 
13726 /* ### temporary API. remove before release.  */
13727 void
svn_wc__db_temp_set_access(svn_wc__db_t * db,const char * local_dir_abspath,svn_wc_adm_access_t * adm_access,apr_pool_t * scratch_pool)13728 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13729                            const char *local_dir_abspath,
13730                            svn_wc_adm_access_t *adm_access,
13731                            apr_pool_t *scratch_pool)
13732 {
13733   const char *local_relpath;
13734   svn_wc__db_wcroot_t *wcroot;
13735   svn_error_t *err;
13736 
13737   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13738   /* ### assert that we were passed a directory?  */
13739 
13740   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13741                             db, local_dir_abspath, scratch_pool, scratch_pool);
13742   if (err)
13743     {
13744       /* We don't even have a wcroot, so just bail. */
13745       svn_error_clear(err);
13746       return;
13747     }
13748 
13749   /* Better not override something already there.  */
13750   SVN_ERR_ASSERT_NO_RETURN(
13751     svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13752   );
13753   svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13754 }
13755 
13756 
13757 /* ### temporary API. remove before release.  */
13758 svn_error_t *
svn_wc__db_temp_close_access(svn_wc__db_t * db,const char * local_dir_abspath,svn_wc_adm_access_t * adm_access,apr_pool_t * scratch_pool)13759 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13760                              const char *local_dir_abspath,
13761                              svn_wc_adm_access_t *adm_access,
13762                              apr_pool_t *scratch_pool)
13763 {
13764   const char *local_relpath;
13765   svn_wc__db_wcroot_t *wcroot;
13766 
13767   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13768   /* ### assert that we were passed a directory?  */
13769 
13770   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13771                               local_dir_abspath, scratch_pool, scratch_pool));
13772   svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13773 
13774   return SVN_NO_ERROR;
13775 }
13776 
13777 
13778 /* ### temporary API. remove before release.  */
13779 void
svn_wc__db_temp_clear_access(svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13780 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13781                              const char *local_dir_abspath,
13782                              apr_pool_t *scratch_pool)
13783 {
13784   const char *local_relpath;
13785   svn_wc__db_wcroot_t *wcroot;
13786   svn_error_t *err;
13787 
13788   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13789   /* ### assert that we were passed a directory?  */
13790 
13791   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13792                             db, local_dir_abspath, scratch_pool, scratch_pool);
13793   if (err)
13794     {
13795       svn_error_clear(err);
13796       return;
13797     }
13798 
13799   svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13800 }
13801 
13802 
13803 apr_hash_t *
svn_wc__db_temp_get_all_access(svn_wc__db_t * db,apr_pool_t * result_pool)13804 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13805                                apr_pool_t *result_pool)
13806 {
13807   apr_hash_t *result = apr_hash_make(result_pool);
13808   apr_hash_index_t *hi;
13809 
13810   for (hi = apr_hash_first(result_pool, db->dir_data);
13811        hi;
13812        hi = apr_hash_next(hi))
13813     {
13814       const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
13815 
13816       /* This is highly redundant, 'cause the same WCROOT will appear many
13817          times in dir_data. */
13818       result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13819     }
13820 
13821   return result;
13822 }
13823 
13824 
13825 svn_error_t *
svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t ** sdb,svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13826 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13827                            svn_wc__db_t *db,
13828                            const char *local_dir_abspath,
13829                            apr_pool_t *scratch_pool)
13830 {
13831   svn_wc__db_wcroot_t *wcroot;
13832   const char *local_relpath;
13833 
13834   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13835 
13836   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13837                             local_dir_abspath, scratch_pool, scratch_pool));
13838   VERIFY_USABLE_WCROOT(wcroot);
13839 
13840   *sdb = wcroot->sdb;
13841 
13842   return SVN_NO_ERROR;
13843 }
13844 
13845 
13846 svn_error_t *
svn_wc__db_read_conflict_victims(const apr_array_header_t ** victims,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13847 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13848                                  svn_wc__db_t *db,
13849                                  const char *local_abspath,
13850                                  apr_pool_t *result_pool,
13851                                  apr_pool_t *scratch_pool)
13852 {
13853   svn_wc__db_wcroot_t *wcroot;
13854   const char *local_relpath;
13855   svn_sqlite__stmt_t *stmt;
13856   svn_boolean_t have_row;
13857   apr_array_header_t *new_victims;
13858 
13859   /* The parent should be a working copy directory. */
13860   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13861                               local_abspath, scratch_pool, scratch_pool));
13862   VERIFY_USABLE_WCROOT(wcroot);
13863 
13864   /* ### This will be much easier once we have all conflicts in one
13865          field of actual*/
13866 
13867   /* Look for text, tree and property conflicts in ACTUAL */
13868   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13869                                     STMT_SELECT_CONFLICT_VICTIMS));
13870   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13871 
13872   new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13873 
13874   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13875   while (have_row)
13876     {
13877       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13878 
13879       APR_ARRAY_PUSH(new_victims, const char *) =
13880                             svn_relpath_basename(child_relpath, result_pool);
13881 
13882       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13883     }
13884 
13885   SVN_ERR(svn_sqlite__reset(stmt));
13886 
13887   *victims = new_victims;
13888   return SVN_NO_ERROR;
13889 }
13890 
13891 /* The body of svn_wc__db_get_conflict_marker_files().
13892  */
13893 static svn_error_t *
get_conflict_marker_files(apr_hash_t ** marker_files_p,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13894 get_conflict_marker_files(apr_hash_t **marker_files_p,
13895                           svn_wc__db_wcroot_t *wcroot,
13896                           const char *local_relpath,
13897                           svn_wc__db_t *db,
13898                           apr_pool_t *result_pool,
13899                           apr_pool_t *scratch_pool)
13900 {
13901   svn_sqlite__stmt_t *stmt;
13902   svn_boolean_t have_row;
13903   apr_hash_t *marker_files = apr_hash_make(result_pool);
13904 
13905   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13906                                     STMT_SELECT_ACTUAL_NODE));
13907   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13908   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13909 
13910   if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13911     {
13912       apr_size_t len;
13913       const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13914       svn_skel_t *conflicts;
13915       const apr_array_header_t *markers;
13916       int i;
13917 
13918       conflicts = svn_skel__parse(data, len, scratch_pool);
13919 
13920       /* ### ADD markers to *marker_files */
13921       SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13922                                             conflicts,
13923                                             result_pool, scratch_pool));
13924 
13925       for (i = 0; markers && (i < markers->nelts); i++)
13926         {
13927           const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13928 
13929           svn_hash_sets(marker_files, marker_abspath, "");
13930         }
13931     }
13932   SVN_ERR(svn_sqlite__reset(stmt));
13933 
13934   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13935                                     STMT_SELECT_CONFLICT_VICTIMS));
13936   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13937   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13938 
13939   while (have_row)
13940     {
13941       apr_size_t len;
13942       const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13943 
13944       const apr_array_header_t *markers;
13945       int i;
13946 
13947       if (data)
13948         {
13949           svn_skel_t *conflicts;
13950           conflicts = svn_skel__parse(data, len, scratch_pool);
13951 
13952           SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13953                                                 conflicts,
13954                                                 result_pool, scratch_pool));
13955 
13956           for (i = 0; markers && (i < markers->nelts); i++)
13957             {
13958               const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13959 
13960               svn_hash_sets(marker_files, marker_abspath, "");
13961             }
13962         }
13963 
13964       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13965     }
13966 
13967   if (apr_hash_count(marker_files))
13968     *marker_files_p = marker_files;
13969   else
13970     *marker_files_p = NULL;
13971 
13972   return svn_error_trace(svn_sqlite__reset(stmt));
13973 }
13974 
13975 svn_error_t *
svn_wc__db_get_conflict_marker_files(apr_hash_t ** marker_files,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13976 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13977                                      svn_wc__db_t *db,
13978                                      const char *local_abspath,
13979                                      apr_pool_t *result_pool,
13980                                      apr_pool_t *scratch_pool)
13981 {
13982   svn_wc__db_wcroot_t *wcroot;
13983   const char *local_relpath;
13984 
13985   /* The parent should be a working copy directory. */
13986   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13987                               local_abspath, scratch_pool, scratch_pool));
13988   VERIFY_USABLE_WCROOT(wcroot);
13989 
13990   SVN_WC__DB_WITH_TXN(
13991     get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13992                               result_pool, scratch_pool),
13993     wcroot);
13994 
13995   return SVN_NO_ERROR;
13996 }
13997 
13998 
13999 svn_error_t *
svn_wc__db_read_conflict(svn_skel_t ** conflict,svn_node_kind_t * kind,apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14000 svn_wc__db_read_conflict(svn_skel_t **conflict,
14001                          svn_node_kind_t *kind,
14002                          apr_hash_t **props,
14003                          svn_wc__db_t *db,
14004                          const char *local_abspath,
14005                          apr_pool_t *result_pool,
14006                          apr_pool_t *scratch_pool)
14007 {
14008   svn_wc__db_wcroot_t *wcroot;
14009   const char *local_relpath;
14010 
14011   /* The parent should be a working copy directory. */
14012   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14013                               local_abspath, scratch_pool, scratch_pool));
14014   VERIFY_USABLE_WCROOT(wcroot);
14015 
14016   return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props,
14017                                                            wcroot, local_relpath,
14018                                                            result_pool,
14019                                                            scratch_pool));
14020 }
14021 
14022 svn_error_t *
svn_wc__db_read_conflict_internal(svn_skel_t ** conflict,svn_node_kind_t * kind,apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14023 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
14024                                   svn_node_kind_t *kind,
14025                                   apr_hash_t **props,
14026                                   svn_wc__db_wcroot_t *wcroot,
14027                                   const char *local_relpath,
14028                                   apr_pool_t *result_pool,
14029                                   apr_pool_t *scratch_pool)
14030 {
14031   svn_sqlite__stmt_t *stmt;
14032   svn_boolean_t have_row;
14033 
14034   if (kind)
14035     *kind = svn_node_none;
14036   if (props)
14037     *props = NULL;
14038 
14039   /* Check if we have a conflict in ACTUAL */
14040   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14041                                     STMT_SELECT_ACTUAL_NODE));
14042   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14043 
14044   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14045 
14046   if (have_row)
14047     {
14048       apr_size_t cfl_len;
14049       const void *cfl_data;
14050 
14051       /* svn_skel__parse doesn't copy data, so store in result_pool */
14052       cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
14053 
14054       if (cfl_data)
14055         *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
14056       else
14057         *conflict = NULL;
14058 
14059       if (props)
14060         {
14061           svn_error_t *err;
14062 
14063           err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1,
14064                                                               result_pool,
14065                                                               scratch_pool));
14066 
14067           if (err)
14068             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
14069         }
14070     }
14071   else
14072     *conflict = NULL;
14073 
14074   SVN_ERR(svn_sqlite__reset(stmt));
14075 
14076   if (!have_row || kind || (props && !*props))
14077     {
14078       svn_error_t *err = NULL;
14079       svn_boolean_t have_info = FALSE;
14080 
14081       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14082                                         STMT_SELECT_NODE_INFO));
14083 
14084       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
14085                                 local_relpath));
14086 
14087       SVN_ERR(svn_sqlite__step(&have_info, stmt));
14088 
14089       if (have_info)
14090         {
14091           if (kind)
14092             {
14093               svn_wc__db_status_t status;
14094               int op_depth = svn_sqlite__column_int(stmt, 0);
14095 
14096               status = svn_sqlite__column_token(stmt, 3, presence_map);
14097 
14098               if (op_depth > 0)
14099                 err = convert_to_working_status(&status, status);
14100 
14101               if (!err && (status == svn_wc__db_status_normal
14102                            || status == svn_wc__db_status_added
14103                            || status == svn_wc__db_status_deleted
14104                            || status == svn_wc__db_status_incomplete))
14105                 {
14106                   *kind = svn_sqlite__column_token(stmt, 4, kind_map);
14107                 }
14108             }
14109 
14110           /* Need props, and no props in ACTUAL? */
14111           if (!err && (props && !*props))
14112             {
14113               err = svn_sqlite__column_properties(props, stmt, 14,
14114                                                   result_pool, scratch_pool);
14115             }
14116         }
14117 
14118       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14119 
14120       if (!have_row && !have_info)
14121         {
14122           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14123                                    _("The node '%s' was not found."),
14124                                    path_for_error_message(wcroot,
14125                                                           local_relpath,
14126                                                           scratch_pool));
14127         }
14128     }
14129 
14130   return SVN_NO_ERROR;
14131 }
14132 
14133 
14134 svn_error_t *
svn_wc__db_read_kind(svn_node_kind_t * kind,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t allow_missing,svn_boolean_t show_deleted,svn_boolean_t show_hidden,apr_pool_t * scratch_pool)14135 svn_wc__db_read_kind(svn_node_kind_t *kind,
14136                      svn_wc__db_t *db,
14137                      const char *local_abspath,
14138                      svn_boolean_t allow_missing,
14139                      svn_boolean_t show_deleted,
14140                      svn_boolean_t show_hidden,
14141                      apr_pool_t *scratch_pool)
14142 {
14143   svn_wc__db_wcroot_t *wcroot;
14144   const char *local_relpath;
14145   svn_sqlite__stmt_t *stmt_info;
14146   svn_boolean_t have_info;
14147   svn_wc__db_status_t status;
14148 
14149   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14150 
14151   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14152                               local_abspath, scratch_pool, scratch_pool));
14153   VERIFY_USABLE_WCROOT(wcroot);
14154 
14155   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
14156                                     STMT_SELECT_NODE_INFO));
14157   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
14158   SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14159 
14160   if (!have_info)
14161     {
14162       if (allow_missing)
14163         {
14164           *kind = svn_node_unknown;
14165           SVN_ERR(svn_sqlite__reset(stmt_info));
14166           return SVN_NO_ERROR;
14167         }
14168       else
14169         {
14170           SVN_ERR(svn_sqlite__reset(stmt_info));
14171           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14172                                    _("The node '%s' was not found."),
14173                                    path_for_error_message(wcroot,
14174                                                           local_relpath,
14175                                                           scratch_pool));
14176         }
14177     }
14178 
14179   status = svn_sqlite__column_token(stmt_info, 3, presence_map);
14180 
14181   if (show_deleted && status == svn_wc__db_status_base_deleted)
14182     {
14183       /* Let's return the kind of what is really deleted insead of what
14184          we have cached in the base-deleted record */
14185 
14186       SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14187 
14188       if (!have_info)
14189         {
14190           /* No lower layer deleted? Database inconsistency! */
14191           *kind = svn_node_none;
14192           return svn_error_trace(svn_sqlite__reset(stmt_info));
14193         }
14194     }
14195 
14196   if (!(show_deleted && show_hidden))
14197     {
14198       int op_depth = svn_sqlite__column_int(stmt_info, 0);
14199       svn_boolean_t report_none = FALSE;
14200 
14201       if (op_depth > 0)
14202         SVN_ERR(convert_to_working_status(&status, status));
14203 
14204       switch (status)
14205         {
14206           case svn_wc__db_status_not_present:
14207             if (! (show_hidden && show_deleted))
14208               report_none = TRUE;
14209             break;
14210           case svn_wc__db_status_excluded:
14211           case svn_wc__db_status_server_excluded:
14212             if (! show_hidden)
14213               report_none = TRUE;
14214             break;
14215           case svn_wc__db_status_deleted:
14216             if (! show_deleted)
14217               report_none = TRUE;
14218             break;
14219           default:
14220             break;
14221         }
14222 
14223       if (report_none)
14224         {
14225           *kind = svn_node_none;
14226           return svn_error_trace(svn_sqlite__reset(stmt_info));
14227         }
14228     }
14229 
14230   *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
14231 
14232   return svn_error_trace(svn_sqlite__reset(stmt_info));
14233 }
14234 
14235 svn_error_t *
svn_wc__db_is_wcroot(svn_boolean_t * is_wcroot,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14236 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
14237                      svn_wc__db_t *db,
14238                      const char *local_abspath,
14239                      apr_pool_t *scratch_pool)
14240 {
14241   svn_wc__db_wcroot_t *wcroot;
14242   const char *local_relpath;
14243 
14244   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14245 
14246   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14247                               local_abspath, scratch_pool, scratch_pool));
14248   VERIFY_USABLE_WCROOT(wcroot);
14249 
14250   if (*local_relpath != '\0')
14251     {
14252       *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
14253                            the same wcroot */
14254       return SVN_NO_ERROR;
14255     }
14256 
14257    *is_wcroot = TRUE;
14258 
14259    return SVN_NO_ERROR;
14260 }
14261 
14262 /* Find a node's kind and whether it is switched, putting the outputs in
14263  * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
14264  */
14265 static svn_error_t *
db_is_switched(svn_boolean_t * is_switched,svn_node_kind_t * kind,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)14266 db_is_switched(svn_boolean_t *is_switched,
14267                svn_node_kind_t *kind,
14268                svn_wc__db_wcroot_t *wcroot,
14269                const char *local_relpath,
14270                apr_pool_t *scratch_pool)
14271 {
14272   svn_wc__db_status_t status;
14273   apr_int64_t repos_id;
14274   const char *repos_relpath;
14275   const char *name;
14276   const char *parent_local_relpath;
14277   apr_int64_t parent_repos_id;
14278   const char *parent_repos_relpath;
14279 
14280   SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
14281 
14282   SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
14283                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14284                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14285                     wcroot, local_relpath, scratch_pool, scratch_pool));
14286 
14287   if (status == svn_wc__db_status_server_excluded
14288       || status == svn_wc__db_status_excluded
14289       || status == svn_wc__db_status_not_present)
14290     {
14291       return svn_error_createf(
14292                     SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14293                     _("The node '%s' was not found."),
14294                     path_for_error_message(wcroot, local_relpath,
14295                                            scratch_pool));
14296     }
14297   else if (! repos_relpath)
14298     {
14299       /* Node is shadowed; easy out */
14300       if (is_switched)
14301         *is_switched = FALSE;
14302 
14303       return SVN_NO_ERROR;
14304     }
14305 
14306   if (! is_switched)
14307     return SVN_NO_ERROR;
14308 
14309   svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
14310 
14311   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14312                                             &parent_repos_relpath,
14313                                             &parent_repos_id, NULL, NULL, NULL,
14314                                             NULL, NULL, NULL, NULL, NULL,
14315                                             NULL, NULL,
14316                                             wcroot, parent_local_relpath,
14317                                             scratch_pool, scratch_pool));
14318 
14319   if (repos_id != parent_repos_id)
14320     *is_switched = TRUE;
14321   else
14322     {
14323       const char *expected_relpath;
14324 
14325       expected_relpath = svn_relpath_join(parent_repos_relpath, name,
14326                                           scratch_pool);
14327 
14328       *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
14329     }
14330 
14331   return SVN_NO_ERROR;
14332 }
14333 
14334 svn_error_t *
svn_wc__db_is_switched(svn_boolean_t * is_wcroot,svn_boolean_t * is_switched,svn_node_kind_t * kind,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14335 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
14336                        svn_boolean_t *is_switched,
14337                        svn_node_kind_t *kind,
14338                        svn_wc__db_t *db,
14339                        const char *local_abspath,
14340                        apr_pool_t *scratch_pool)
14341 {
14342   svn_wc__db_wcroot_t *wcroot;
14343   const char *local_relpath;
14344 
14345   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14346 
14347   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14348                               local_abspath, scratch_pool, scratch_pool));
14349   VERIFY_USABLE_WCROOT(wcroot);
14350 
14351   if (is_switched)
14352     *is_switched = FALSE;
14353 
14354   if (*local_relpath == '\0')
14355     {
14356       /* Easy out */
14357       if (is_wcroot)
14358         *is_wcroot = TRUE;
14359 
14360       if (kind)
14361         *kind = svn_node_dir;
14362       return SVN_NO_ERROR;
14363     }
14364 
14365   if (is_wcroot)
14366     *is_wcroot = FALSE;
14367 
14368   if (! is_switched && ! kind)
14369     return SVN_NO_ERROR;
14370 
14371   SVN_WC__DB_WITH_TXN(
14372     db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
14373     wcroot);
14374   return SVN_NO_ERROR;
14375 }
14376 
14377 
14378 svn_error_t *
svn_wc__db_temp_wcroot_tempdir(const char ** temp_dir_abspath,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14379 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14380                                svn_wc__db_t *db,
14381                                const char *wri_abspath,
14382                                apr_pool_t *result_pool,
14383                                apr_pool_t *scratch_pool)
14384 {
14385   svn_wc__db_wcroot_t *wcroot;
14386   const char *local_relpath;
14387 
14388   SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14389   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14390 
14391   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14392                               wri_abspath, scratch_pool, scratch_pool));
14393   VERIFY_USABLE_WCROOT(wcroot);
14394 
14395   *temp_dir_abspath = svn_dirent_join_many(result_pool,
14396                                            wcroot->abspath,
14397                                            svn_wc_get_adm_dir(scratch_pool),
14398                                            WCROOT_TEMPDIR_RELPATH,
14399                                            SVN_VA_NULL);
14400   return SVN_NO_ERROR;
14401 }
14402 
14403 
14404 /* Helper for wclock_obtain_cb() to steal an existing lock */
14405 static svn_error_t *
wclock_steal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)14406 wclock_steal(svn_wc__db_wcroot_t *wcroot,
14407              const char *local_relpath,
14408              apr_pool_t *scratch_pool)
14409 {
14410   svn_sqlite__stmt_t *stmt;
14411 
14412   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14413   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14414 
14415   SVN_ERR(svn_sqlite__step_done(stmt));
14416 
14417   return SVN_NO_ERROR;
14418 }
14419 
14420 
14421 /* The body of svn_wc__db_wclock_obtain().
14422  */
14423 static svn_error_t *
wclock_obtain_cb(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int levels_to_lock,svn_boolean_t steal_lock,svn_boolean_t enforce_empty_wq,apr_pool_t * scratch_pool)14424 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14425                  const char *local_relpath,
14426                  int levels_to_lock,
14427                  svn_boolean_t steal_lock,
14428                  svn_boolean_t enforce_empty_wq,
14429                  apr_pool_t *scratch_pool)
14430 {
14431   svn_sqlite__stmt_t *stmt;
14432   svn_error_t *err;
14433   const char *lock_relpath;
14434   int max_depth;
14435   int lock_depth;
14436   svn_boolean_t got_row;
14437 
14438   svn_wc__db_wclock_t lock;
14439 
14440   /* Upgrade locks the root before the node exists.  Apart from that
14441      the root node always exists so we will just skip the check.
14442 
14443      ### Perhaps the lock for upgrade should be created when the db is
14444          created?  1.6 used to lock .svn on creation. */
14445   if (local_relpath[0])
14446     {
14447       svn_boolean_t exists;
14448 
14449       SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14450       if (!exists)
14451         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14452                                  _("The node '%s' was not found."),
14453                                  path_for_error_message(wcroot,
14454                                                         local_relpath,
14455                                                         scratch_pool));
14456     }
14457 
14458   if (enforce_empty_wq)
14459     SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb));
14460 
14461   /* Check if there are nodes locked below the new lock root */
14462   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14463   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14464 
14465   lock_depth = relpath_depth(local_relpath);
14466   max_depth = lock_depth + levels_to_lock;
14467 
14468   SVN_ERR(svn_sqlite__step(&got_row, stmt));
14469 
14470   while (got_row)
14471     {
14472       svn_boolean_t own_lock;
14473 
14474       lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14475 
14476       /* If we are not locking with depth infinity, check if this lock
14477          voids our lock request */
14478       if (levels_to_lock >= 0
14479           && relpath_depth(lock_relpath) > max_depth)
14480         {
14481           SVN_ERR(svn_sqlite__step(&got_row, stmt));
14482           continue;
14483         }
14484 
14485       /* Check if we are the lock owner, because we should be able to
14486          extend our lock. */
14487       err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot,
14488                                                  lock_relpath,
14489                                                  TRUE, scratch_pool);
14490 
14491       if (err)
14492         SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14493 
14494       if (!own_lock && !steal_lock)
14495         {
14496           SVN_ERR(svn_sqlite__reset(stmt));
14497           err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14498                                    _("'%s' is already locked."),
14499                                    path_for_error_message(wcroot,
14500                                                           lock_relpath,
14501                                                           scratch_pool));
14502           return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14503                                    _("Working copy '%s' locked."),
14504                                    path_for_error_message(wcroot,
14505                                                           local_relpath,
14506                                                           scratch_pool));
14507         }
14508       else if (!own_lock)
14509         {
14510           err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14511 
14512           if (err)
14513             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14514         }
14515 
14516       SVN_ERR(svn_sqlite__step(&got_row, stmt));
14517     }
14518 
14519   SVN_ERR(svn_sqlite__reset(stmt));
14520 
14521   if (steal_lock)
14522     SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14523 
14524   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14525   lock_relpath = local_relpath;
14526 
14527   while (TRUE)
14528     {
14529       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14530 
14531       SVN_ERR(svn_sqlite__step(&got_row, stmt));
14532 
14533       if (got_row)
14534         {
14535           int levels = svn_sqlite__column_int(stmt, 0);
14536           if (levels >= 0)
14537             levels += relpath_depth(lock_relpath);
14538 
14539           SVN_ERR(svn_sqlite__reset(stmt));
14540 
14541           if (levels == -1 || levels >= lock_depth)
14542             {
14543 
14544               err = svn_error_createf(
14545                               SVN_ERR_WC_LOCKED, NULL,
14546                               _("'%s' is already locked."),
14547                               svn_dirent_local_style(
14548                                        svn_dirent_join(wcroot->abspath,
14549                                                        lock_relpath,
14550                                                        scratch_pool),
14551                               scratch_pool));
14552               return svn_error_createf(
14553                               SVN_ERR_WC_LOCKED, err,
14554                               _("Working copy '%s' locked."),
14555                               path_for_error_message(wcroot,
14556                                                      local_relpath,
14557                                                      scratch_pool));
14558             }
14559 
14560           break; /* There can't be interesting locks on higher nodes */
14561         }
14562       else
14563         SVN_ERR(svn_sqlite__reset(stmt));
14564 
14565       if (!*lock_relpath)
14566         break;
14567 
14568       lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14569     }
14570 
14571   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14572   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14573                             levels_to_lock));
14574   err = svn_sqlite__insert(NULL, stmt);
14575   if (err)
14576     return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14577                              _("Failed to lock working copy '%s'."),
14578                              path_for_error_message(wcroot,
14579                                                     local_relpath,
14580                                                     scratch_pool));
14581 
14582   /* And finally store that we obtained the lock */
14583   lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14584   lock.levels = levels_to_lock;
14585   APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14586 
14587   return SVN_NO_ERROR;
14588 }
14589 
14590 
14591 svn_error_t *
svn_wc__db_wclock_obtain(svn_wc__db_t * db,const char * local_abspath,int levels_to_lock,svn_boolean_t steal_lock,apr_pool_t * scratch_pool)14592 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14593                          const char *local_abspath,
14594                          int levels_to_lock,
14595                          svn_boolean_t steal_lock,
14596                          apr_pool_t *scratch_pool)
14597 {
14598   svn_wc__db_wcroot_t *wcroot;
14599   const char *local_relpath;
14600 
14601   SVN_ERR_ASSERT(levels_to_lock >= -1);
14602   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14603 
14604   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14605                                              db, local_abspath,
14606                                              scratch_pool, scratch_pool));
14607   VERIFY_USABLE_WCROOT(wcroot);
14608 
14609   if (!steal_lock)
14610     {
14611       int i;
14612       int depth = relpath_depth(local_relpath);
14613 
14614       for (i = 0; i < wcroot->owned_locks->nelts; i++)
14615         {
14616           svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14617                                                      i, svn_wc__db_wclock_t);
14618 
14619           if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14620               && (lock->levels == -1
14621                   || (lock->levels + relpath_depth(lock->local_relpath))
14622                             >= depth))
14623             {
14624               return svn_error_createf(
14625                 SVN_ERR_WC_LOCKED, NULL,
14626                 _("'%s' is already locked via '%s'."),
14627                 svn_dirent_local_style(local_abspath, scratch_pool),
14628                 path_for_error_message(wcroot, lock->local_relpath,
14629                                        scratch_pool));
14630             }
14631         }
14632     }
14633 
14634   SVN_WC__DB_WITH_TXN(
14635     wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14636                      db->enforce_empty_wq, scratch_pool),
14637     wcroot);
14638   return SVN_NO_ERROR;
14639 }
14640 
14641 
14642 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14643 static svn_error_t *
find_wclock(const char ** lock_relpath,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14644 find_wclock(const char **lock_relpath,
14645             svn_wc__db_wcroot_t *wcroot,
14646             const char *dir_relpath,
14647             apr_pool_t *result_pool,
14648             apr_pool_t *scratch_pool)
14649 {
14650   svn_sqlite__stmt_t *stmt;
14651   svn_boolean_t have_row;
14652   int dir_depth = relpath_depth(dir_relpath);
14653   const char *first_relpath;
14654 
14655   /* Check for locks on all directories that might be ancestors.
14656      As our new apis only use recursive locks the number of locks stored
14657      in the DB will be very low */
14658   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14659                                     STMT_SELECT_ANCESTOR_WCLOCKS));
14660 
14661   /* Get the top level relpath to reduce the worst case number of results
14662      to the number of directories below this node plus two.
14663      (1: the node itself and 2: the wcroot). */
14664   first_relpath = strchr(dir_relpath, '/');
14665 
14666   if (first_relpath != NULL)
14667     first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14668                                  first_relpath - dir_relpath);
14669   else
14670     first_relpath = dir_relpath;
14671 
14672   SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14673                             wcroot->wc_id,
14674                             dir_relpath,
14675                             first_relpath));
14676 
14677   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14678 
14679   while (have_row)
14680     {
14681       const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14682 
14683       if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14684         {
14685           int locked_levels = svn_sqlite__column_int(stmt, 1);
14686           int row_depth = relpath_depth(relpath);
14687 
14688           if (locked_levels == -1
14689               || locked_levels + row_depth >= dir_depth)
14690             {
14691               *lock_relpath = apr_pstrdup(result_pool, relpath);
14692               SVN_ERR(svn_sqlite__reset(stmt));
14693               return SVN_NO_ERROR;
14694             }
14695         }
14696 
14697       SVN_ERR(svn_sqlite__step(&have_row, stmt));
14698     }
14699 
14700   *lock_relpath = NULL;
14701 
14702   return svn_error_trace(svn_sqlite__reset(stmt));
14703 }
14704 
14705 static svn_error_t *
is_wclocked(svn_boolean_t * locked,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_pool_t * scratch_pool)14706 is_wclocked(svn_boolean_t *locked,
14707             svn_wc__db_wcroot_t *wcroot,
14708             const char *dir_relpath,
14709             apr_pool_t *scratch_pool)
14710 {
14711   const char *lock_relpath;
14712 
14713   SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14714                       scratch_pool, scratch_pool));
14715   *locked = (lock_relpath != NULL);
14716   return SVN_NO_ERROR;
14717 }
14718 
14719 
14720 svn_error_t*
svn_wc__db_wclock_find_root(const char ** lock_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14721 svn_wc__db_wclock_find_root(const char **lock_abspath,
14722                             svn_wc__db_t *db,
14723                             const char *local_abspath,
14724                             apr_pool_t *result_pool,
14725                             apr_pool_t *scratch_pool)
14726 {
14727   svn_wc__db_wcroot_t *wcroot;
14728   const char *local_relpath;
14729   const char *lock_relpath;
14730 
14731   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14732                               local_abspath, scratch_pool, scratch_pool));
14733   VERIFY_USABLE_WCROOT(wcroot);
14734 
14735   SVN_WC__DB_WITH_TXN(
14736     find_wclock(&lock_relpath, wcroot, local_relpath,
14737                 scratch_pool, scratch_pool),
14738     wcroot);
14739 
14740   if (!lock_relpath)
14741     *lock_abspath = NULL;
14742   else
14743     SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14744                                     lock_relpath, result_pool, scratch_pool));
14745   return SVN_NO_ERROR;
14746 }
14747 
14748 
14749 svn_error_t *
svn_wc__db_wclocked(svn_boolean_t * locked,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14750 svn_wc__db_wclocked(svn_boolean_t *locked,
14751                     svn_wc__db_t *db,
14752                     const char *local_abspath,
14753                     apr_pool_t *scratch_pool)
14754 {
14755   svn_wc__db_wcroot_t *wcroot;
14756   const char *local_relpath;
14757 
14758   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14759                               local_abspath, scratch_pool, scratch_pool));
14760   VERIFY_USABLE_WCROOT(wcroot);
14761 
14762   SVN_WC__DB_WITH_TXN(
14763     is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14764     wcroot);
14765 
14766   return SVN_NO_ERROR;
14767 }
14768 
14769 
14770 svn_error_t *
svn_wc__db_wclock_release(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14771 svn_wc__db_wclock_release(svn_wc__db_t *db,
14772                           const char *local_abspath,
14773                           apr_pool_t *scratch_pool)
14774 {
14775   svn_sqlite__stmt_t *stmt;
14776   svn_wc__db_wcroot_t *wcroot;
14777   const char *local_relpath;
14778   int i;
14779   apr_array_header_t *owned_locks;
14780 
14781   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14782                               local_abspath, scratch_pool, scratch_pool));
14783 
14784   VERIFY_USABLE_WCROOT(wcroot);
14785 
14786   /* First check and remove the owns-lock information as failure in
14787      removing the db record implies that we have to steal the lock later. */
14788   owned_locks = wcroot->owned_locks;
14789   for (i = 0; i < owned_locks->nelts; i++)
14790     {
14791       svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14792                                                  svn_wc__db_wclock_t);
14793 
14794       if (strcmp(lock->local_relpath, local_relpath) == 0)
14795         break;
14796     }
14797 
14798   if (i >= owned_locks->nelts)
14799     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14800                              _("Working copy not locked at '%s'."),
14801                              svn_dirent_local_style(local_abspath,
14802                                                     scratch_pool));
14803 
14804   if (i < owned_locks->nelts)
14805     {
14806       owned_locks->nelts--;
14807 
14808       /* Move the last item in the array to the deleted place */
14809       if (owned_locks->nelts > 0)
14810         APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14811            APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14812     }
14813 
14814   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14815                                     STMT_DELETE_WC_LOCK));
14816 
14817   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14818 
14819   SVN_ERR(svn_sqlite__step_done(stmt));
14820 
14821   return SVN_NO_ERROR;
14822 }
14823 
14824 
14825 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14826    of DB+LOCAL_ABSPATH.  */
14827 svn_error_t *
svn_wc__db_wclock_owns_lock_internal(svn_boolean_t * own_lock,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t exact,apr_pool_t * scratch_pool)14828 svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock,
14829                                      svn_wc__db_wcroot_t *wcroot,
14830                                      const char *local_relpath,
14831                                      svn_boolean_t exact,
14832                                      apr_pool_t *scratch_pool)
14833 {
14834   apr_array_header_t *owned_locks;
14835   int lock_level;
14836   int i;
14837 
14838   *own_lock = FALSE;
14839   owned_locks = wcroot->owned_locks;
14840   lock_level = relpath_depth(local_relpath);
14841 
14842   if (exact)
14843     {
14844       for (i = 0; i < owned_locks->nelts; i++)
14845         {
14846           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14847                                                      svn_wc__db_wclock_t);
14848 
14849           if (strcmp(lock->local_relpath, local_relpath) == 0)
14850             {
14851               *own_lock = TRUE;
14852               return SVN_NO_ERROR;
14853             }
14854         }
14855     }
14856   else
14857     {
14858       for (i = 0; i < owned_locks->nelts; i++)
14859         {
14860           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14861                                                      svn_wc__db_wclock_t);
14862 
14863           if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14864               && (lock->levels == -1
14865                   || ((relpath_depth(lock->local_relpath) + lock->levels)
14866                       >= lock_level)))
14867             {
14868               *own_lock = TRUE;
14869               return SVN_NO_ERROR;
14870             }
14871         }
14872     }
14873 
14874   return SVN_NO_ERROR;
14875 }
14876 
14877 
14878 svn_error_t *
svn_wc__db_wclock_owns_lock(svn_boolean_t * own_lock,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t exact,apr_pool_t * scratch_pool)14879 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14880                             svn_wc__db_t *db,
14881                             const char *local_abspath,
14882                             svn_boolean_t exact,
14883                             apr_pool_t *scratch_pool)
14884 {
14885   svn_wc__db_wcroot_t *wcroot;
14886   const char *local_relpath;
14887 
14888   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14889                               local_abspath, scratch_pool, scratch_pool));
14890 
14891   if (!wcroot)
14892     return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14893                              _("The node '%s' was not found."),
14894                              svn_dirent_local_style(local_abspath,
14895                                                     scratch_pool));
14896 
14897   VERIFY_USABLE_WCROOT(wcroot);
14898 
14899   SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath,
14900                                                exact, scratch_pool));
14901 
14902   return SVN_NO_ERROR;
14903 }
14904 
14905 /* The body of svn_wc__db_temp_op_end_directory_update().
14906  */
14907 static svn_error_t *
end_directory_update(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)14908 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14909                      const char *local_relpath,
14910                      apr_pool_t *scratch_pool)
14911 {
14912   svn_sqlite__stmt_t *stmt;
14913   svn_wc__db_status_t base_status;
14914 
14915   SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14916                                             NULL, NULL, NULL, NULL, NULL,
14917                                             NULL, NULL, NULL, NULL, NULL, NULL,
14918                                             wcroot, local_relpath,
14919                                             scratch_pool, scratch_pool));
14920 
14921   if (base_status == svn_wc__db_status_normal)
14922     return SVN_NO_ERROR;
14923 
14924   SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14925 
14926   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14927                                     STMT_UPDATE_NODE_BASE_PRESENCE));
14928   SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14929                             presence_map, svn_wc__db_status_normal));
14930   SVN_ERR(svn_sqlite__step_done(stmt));
14931 
14932   return SVN_NO_ERROR;
14933 }
14934 
14935 svn_error_t *
svn_wc__db_temp_op_end_directory_update(svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)14936 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14937                                         const char *local_dir_abspath,
14938                                         apr_pool_t *scratch_pool)
14939 {
14940   svn_wc__db_wcroot_t *wcroot;
14941   const char *local_relpath;
14942 
14943   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14944 
14945   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14946                               local_dir_abspath, scratch_pool, scratch_pool));
14947   VERIFY_USABLE_WCROOT(wcroot);
14948 
14949   SVN_WC__DB_WITH_TXN(
14950     end_directory_update(wcroot, local_relpath, scratch_pool),
14951     wcroot);
14952 
14953   SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14954                         scratch_pool));
14955 
14956   return SVN_NO_ERROR;
14957 }
14958 
14959 
14960 /* The body of svn_wc__db_temp_op_start_directory_update().
14961  */
14962 static svn_error_t *
start_directory_update_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * new_repos_relpath,svn_revnum_t new_rev,apr_pool_t * scratch_pool)14963 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14964                            const char *local_relpath,
14965                            const char *new_repos_relpath,
14966                            svn_revnum_t new_rev,
14967                            apr_pool_t *scratch_pool)
14968 {
14969   svn_sqlite__stmt_t *stmt;
14970 
14971   /* Note: In the majority of calls, the repos_relpath is unchanged. */
14972   /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14973   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14974                     STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14975 
14976   SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14977                             wcroot->wc_id,
14978                             local_relpath,
14979                             presence_map, svn_wc__db_status_incomplete,
14980                             new_rev,
14981                             new_repos_relpath));
14982   SVN_ERR(svn_sqlite__step_done(stmt));
14983 
14984   return SVN_NO_ERROR;
14985 
14986 }
14987 
14988 svn_error_t *
svn_wc__db_temp_op_start_directory_update(svn_wc__db_t * db,const char * local_abspath,const char * new_repos_relpath,svn_revnum_t new_rev,apr_pool_t * scratch_pool)14989 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14990                                           const char *local_abspath,
14991                                           const char *new_repos_relpath,
14992                                           svn_revnum_t new_rev,
14993                                           apr_pool_t *scratch_pool)
14994 {
14995   svn_wc__db_wcroot_t *wcroot;
14996   const char *local_relpath;
14997 
14998   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14999   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
15000   SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
15001 
15002   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15003                               local_abspath, scratch_pool, scratch_pool));
15004   VERIFY_USABLE_WCROOT(wcroot);
15005 
15006   SVN_WC__DB_WITH_TXN(
15007     start_directory_update_txn(wcroot, local_relpath,
15008                                new_repos_relpath, new_rev, scratch_pool),
15009     wcroot);
15010 
15011   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
15012 
15013   return SVN_NO_ERROR;
15014 }
15015 
15016 /* Helper for svn_wc__db_op_make_copy_internal */
15017 static svn_error_t *
db_move_moved_to(svn_wc__db_wcroot_t * wcroot,const char * src1_relpath,int src1_op_depth,const char * src2_relpath,int src2_op_depth,const char * dst_relpath,apr_pool_t * scratch_pool)15018 db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
15019                  const char *src1_relpath,
15020                  int src1_op_depth,
15021                  const char *src2_relpath,
15022                  int src2_op_depth,
15023                  const char *dst_relpath,
15024                  apr_pool_t *scratch_pool)
15025 {
15026   svn_sqlite__stmt_t *stmt;
15027   int affected_rows;
15028 
15029   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15030                                      STMT_UPDATE_MOVED_TO_RELPATH));
15031   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
15032                             src1_relpath, src1_op_depth));
15033   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15034 
15035   if (affected_rows == 1)
15036     {
15037       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15038                                      STMT_UPDATE_MOVED_TO_RELPATH));
15039       SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
15040                                 src2_relpath, src2_op_depth,
15041                                 dst_relpath));
15042       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15043     }
15044   if (affected_rows != 1)
15045     return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
15046 
15047   return SVN_NO_ERROR;
15048 }
15049 
15050 static svn_error_t *
db_move_moved_to_down_recursive(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int new_shadow_layer,apr_pool_t * scratch_pool)15051 db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
15052                                 const char *local_relpath,
15053                                 int new_shadow_layer,
15054                                 apr_pool_t *scratch_pool)
15055 {
15056   svn_sqlite__stmt_t *stmt;
15057   svn_boolean_t have_row;
15058   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15059 
15060   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15061                         STMT_SELECT_MOVED_DESCENDANTS_SRC));
15062   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15063                             new_shadow_layer));
15064   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15065 
15066   while (have_row)
15067     {
15068       int del_op_depth;
15069       const char *src_relpath;
15070       const char *dst_relpath;
15071       svn_error_t *err;
15072 
15073       svn_pool_clear(iterpool);
15074 
15075       del_op_depth = svn_sqlite__column_int(stmt, 0);
15076       src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15077       dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
15078 
15079       err = svn_error_trace(
15080                db_move_moved_to(
15081                              wcroot,
15082                              src_relpath, del_op_depth,
15083                              src_relpath, new_shadow_layer,
15084                              dst_relpath, iterpool));
15085 
15086       if (err)
15087         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
15088 
15089       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15090     }
15091 
15092   SVN_ERR(svn_sqlite__reset(stmt));
15093 
15094   return SVN_NO_ERROR;
15095 }
15096 
15097 
15098 /* The body of svn_wc__db_temp_op_make_copy().  This is
15099    used by the update editor when deleting a base node tree would be a
15100    tree-conflict because there are changes to subtrees.  This function
15101    inserts a copy of the base node tree below any existing working
15102    subtrees.  Given a tree:
15103 
15104              0            1           2            3
15105     /     normal          -
15106     A     normal          -
15107     A/B   normal          -         normal
15108     A/B/C normal          -         base-del       normal
15109     A/F   normal          -         normal
15110     A/F/G normal          -         normal
15111     A/F/H normal          -         base-deleted   normal
15112     A/F/E normal          -         not-present
15113     A/X   normal          -
15114     A/X/Y incomplete      -
15115 
15116     This function adds layers to A and some of its descendants in an attempt
15117     to make the working copy look like as if it were a copy of the BASE nodes.
15118 
15119              0            1              2            3
15120     /     normal        -
15121     A     normal        norm
15122     A/B   normal        norm        norm
15123     A/B/C normal        norm        base-del       normal
15124     A/F   normal        norm        norm
15125     A/F/G normal        norm        norm
15126     A/F/H normal        norm        not-pres
15127     A/F/E normal        norm        base-del
15128     A/X   normal        norm
15129     A/X/Y incomplete  incomplete
15130  */
15131 static svn_error_t *
make_copy_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_int64_t last_repos_id,const char * last_repos_relpath,svn_revnum_t last_revision,int last_op_depth,svn_boolean_t shadowed,int root_shadow_depth,apr_pool_t * scratch_pool)15132 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
15133               const char *local_relpath,
15134               apr_int64_t last_repos_id,
15135               const char *last_repos_relpath,
15136               svn_revnum_t last_revision,
15137               int last_op_depth,
15138               svn_boolean_t shadowed,
15139               int root_shadow_depth,
15140               apr_pool_t *scratch_pool)
15141 {
15142   svn_sqlite__stmt_t *stmt;
15143   svn_boolean_t have_row = FALSE;
15144   svn_revnum_t revision;
15145   apr_int64_t repos_id;
15146   const char *repos_relpath;
15147   svn_node_kind_t kind;
15148   int op_depth = relpath_depth(local_relpath);
15149 
15150   if (last_op_depth != op_depth)
15151     {
15152       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15153                                         STMT_SELECT_DEPTH_NODE));
15154       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15155                                 op_depth));
15156       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15157       SVN_ERR(svn_sqlite__reset(stmt));
15158       if (have_row)
15159         shadowed = TRUE;
15160     }
15161 
15162   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
15163                                             &repos_relpath, &repos_id, NULL,
15164                                             NULL, NULL, NULL, NULL, NULL, NULL,
15165                                             NULL, NULL, NULL,
15166                                             wcroot, local_relpath,
15167                                             scratch_pool, scratch_pool));
15168 
15169   if (last_repos_relpath
15170       && repos_id == last_repos_id
15171       && revision == last_revision)
15172     {
15173       const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
15174                                                    repos_relpath);
15175 
15176       if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
15177         op_depth = last_op_depth;
15178     }
15179 
15180   /* Can we add a new copy node at the wanted op-depth? */
15181   if (!have_row || op_depth == last_op_depth)
15182     {
15183       int i;
15184 
15185       SVN_ERR(svn_sqlite__get_statement(
15186                     &stmt, wcroot->sdb,
15187                     STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
15188       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15189                                 op_depth));
15190       SVN_ERR(svn_sqlite__step_done(stmt));
15191 
15192       if (shadowed)
15193         SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
15194                                         op_depth, scratch_pool));
15195 
15196       if (kind == svn_node_dir)
15197         {
15198           const apr_array_header_t *children;
15199           apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15200 
15201           SVN_ERR(gather_children(&children, wcroot, local_relpath,
15202                                   STMT_SELECT_OP_DEPTH_CHILDREN, 0,
15203                                   scratch_pool, iterpool));
15204 
15205           for (i = 0; i < children->nelts; i++)
15206             {
15207               const char *name = APR_ARRAY_IDX(children, i, const char *);
15208               const char *copy_relpath;
15209 
15210               svn_pool_clear(iterpool);
15211 
15212               copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
15213 
15214               SVN_ERR(make_copy_txn(wcroot, copy_relpath,
15215                                     repos_id, repos_relpath, revision,
15216                                     op_depth, shadowed, root_shadow_depth,
15217                                     scratch_pool));
15218             }
15219           svn_pool_destroy(iterpool);
15220         }
15221     }
15222   else
15223     {
15224       /* Auch... we can't make a copy of whatever comes deeper, as this
15225          op-depth is already filled by something else. Let's hope
15226          the user doesn't mind.
15227 
15228          Luckily we know that the moves are already moved to the shadowing
15229          layer, so we can just remove dangling base-deletes if there are
15230          any.
15231        */
15232       /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
15233       SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15234                                               root_shadow_depth,
15235                                               scratch_pool));
15236 
15237       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15238                     STMT_DELETE_WORKING_BASE_DELETE));
15239       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15240                                 last_op_depth));
15241       SVN_ERR(svn_sqlite__step_done(stmt));
15242       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15243                     STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
15244       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15245                                 last_op_depth));
15246       SVN_ERR(svn_sqlite__step_done(stmt));
15247     }
15248 
15249   /* Insert a not-present node to mark that we don't know what exists here.
15250 
15251      We do this last (after recursing), to allow the move fix-up code to
15252      see the original moves. */
15253   if (last_op_depth > 0 && last_op_depth != op_depth)
15254     {
15255       insert_working_baton_t iwb;
15256 
15257       blank_iwb(&iwb);
15258       iwb.presence = svn_wc__db_status_not_present;
15259       iwb.op_depth = last_op_depth;
15260 
15261       iwb.original_repos_id = repos_id;
15262       iwb.original_repos_relpath = repos_relpath;
15263       iwb.original_revnum = revision;
15264       iwb.kind = kind;
15265 
15266       SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
15267     }
15268 
15269   return SVN_NO_ERROR;
15270 }
15271 
15272 
15273 svn_error_t *
svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t move_move_info,const svn_skel_t * conflicts,const svn_skel_t * work_items,apr_pool_t * scratch_pool)15274 svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
15275                                  const char *local_relpath,
15276                                  svn_boolean_t move_move_info,
15277                                  const svn_skel_t *conflicts,
15278                                  const svn_skel_t *work_items,
15279                                  apr_pool_t *scratch_pool)
15280 {
15281   svn_sqlite__stmt_t *stmt;
15282   svn_boolean_t have_row;
15283   int op_depth = -1;
15284 
15285   /* The update editor is supposed to call this function when there is
15286      no working node for LOCAL_ABSPATH. */
15287   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15288                                     STMT_SELECT_WORKING_NODE));
15289   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15290   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15291   if (have_row)
15292     op_depth = svn_sqlite__column_int(stmt, 0);
15293   SVN_ERR(svn_sqlite__reset(stmt));
15294 
15295   if (have_row)
15296     {
15297       if (op_depth == relpath_depth(local_relpath))
15298         return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
15299                              _("Modification of '%s' already exists"),
15300                              path_for_error_message(wcroot,
15301                                                     local_relpath,
15302                                                     scratch_pool));
15303 
15304       /* We have a working layer, but not one at the op-depth of local-relpath,
15305          so we can create a copy by just copying the lower layer */
15306 
15307       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15308                                         STMT_COPY_OP_DEPTH_RECURSIVE));
15309       SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
15310                                 op_depth, relpath_depth(local_relpath)));
15311       SVN_ERR(svn_sqlite__step_done(stmt));
15312     }
15313   else
15314     {
15315       int affected_rows;
15316 
15317       op_depth = relpath_depth(local_relpath);
15318       /* We don't allow copies to contain server-excluded nodes;
15319          the update editor is going to have to bail out. */
15320       SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
15321                                             scratch_pool));
15322 
15323       /* Insert a shadowing layer */
15324       SVN_ERR(svn_sqlite__get_statement(
15325                         &stmt, wcroot->sdb,
15326                         STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
15327 
15328       /* As we are keeping whatever is below, move the*/
15329 
15330       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
15331                                 wcroot->wc_id, local_relpath,
15332                                 0, op_depth));
15333       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15334       SVN_ERR_ASSERT(affected_rows > 0);
15335 
15336       if (!move_move_info)
15337         SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15338                                                 op_depth, scratch_pool));
15339 
15340 
15341       SVN_ERR(make_copy_txn(wcroot, local_relpath,
15342                             INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
15343                             op_depth, FALSE, op_depth,
15344                             scratch_pool));
15345     }
15346 
15347   if (conflicts)
15348     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
15349                                               conflicts, scratch_pool));
15350 
15351   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
15352 
15353   return SVN_NO_ERROR;
15354 }
15355 
15356 
15357 svn_error_t *
svn_wc__db_op_make_copy(svn_wc__db_t * db,const char * local_abspath,const svn_skel_t * conflicts,const svn_skel_t * work_items,apr_pool_t * scratch_pool)15358 svn_wc__db_op_make_copy(svn_wc__db_t *db,
15359                         const char *local_abspath,
15360                         const svn_skel_t *conflicts,
15361                         const svn_skel_t *work_items,
15362                         apr_pool_t *scratch_pool)
15363 {
15364   svn_wc__db_wcroot_t *wcroot;
15365   const char *local_relpath;
15366 
15367   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15368 
15369   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15370                               local_abspath, scratch_pool, scratch_pool));
15371   VERIFY_USABLE_WCROOT(wcroot);
15372 
15373   SVN_WC__DB_WITH_TXN(
15374     svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE,
15375                                      conflicts, work_items,
15376                                      scratch_pool),
15377     wcroot);
15378 
15379   SVN_ERR(flush_entries(wcroot, local_abspath,
15380                         svn_depth_infinity, scratch_pool));
15381 
15382   return SVN_NO_ERROR;
15383 }
15384 
15385 svn_error_t *
svn_wc__db_info_below_working(svn_boolean_t * have_base,svn_boolean_t * have_work,svn_wc__db_status_t * status,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)15386 svn_wc__db_info_below_working(svn_boolean_t *have_base,
15387                               svn_boolean_t *have_work,
15388                               svn_wc__db_status_t *status,
15389                               svn_wc__db_t *db,
15390                               const char *local_abspath,
15391                               apr_pool_t *scratch_pool)
15392 {
15393   svn_wc__db_wcroot_t *wcroot;
15394   const char *local_relpath;
15395 
15396   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15397 
15398   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15399                               local_abspath, scratch_pool, scratch_pool));
15400   VERIFY_USABLE_WCROOT(wcroot);
15401   SVN_ERR(info_below_working(have_base, have_work, status,
15402                              wcroot, local_relpath, -1, scratch_pool));
15403 
15404   return SVN_NO_ERROR;
15405 }
15406 
15407 svn_error_t *
svn_wc__db_get_not_present_descendants(const apr_array_header_t ** descendants,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)15408 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
15409                                        svn_wc__db_t *db,
15410                                        const char *local_abspath,
15411                                        apr_pool_t *result_pool,
15412                                        apr_pool_t *scratch_pool)
15413 {
15414   svn_wc__db_wcroot_t *wcroot;
15415   const char *local_relpath;
15416   svn_sqlite__stmt_t *stmt;
15417   svn_boolean_t have_row;
15418 
15419   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15420 
15421   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15422                               local_abspath, scratch_pool, scratch_pool));
15423   VERIFY_USABLE_WCROOT(wcroot);
15424 
15425   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15426                                     STMT_SELECT_NOT_PRESENT_DESCENDANTS));
15427 
15428   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
15429                             wcroot->wc_id,
15430                             local_relpath,
15431                             relpath_depth(local_relpath)));
15432 
15433   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15434 
15435   if (have_row)
15436     {
15437       apr_array_header_t *paths;
15438 
15439       paths = apr_array_make(result_pool, 4, sizeof(const char*));
15440       while (have_row)
15441         {
15442           const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
15443 
15444           APR_ARRAY_PUSH(paths, const char *)
15445               = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
15446                                            local_relpath, found_relpath));
15447 
15448           SVN_ERR(svn_sqlite__step(&have_row, stmt));
15449         }
15450 
15451       *descendants = paths;
15452     }
15453   else
15454     *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
15455 
15456   return svn_error_trace(svn_sqlite__reset(stmt));
15457 }
15458 
15459 
15460 /* Like svn_wc__db_min_max_revisions(),
15461  * but accepts a WCROOT/LOCAL_RELPATH pair. */
15462 static svn_error_t *
get_min_max_revisions(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t committed,apr_pool_t * scratch_pool)15463 get_min_max_revisions(svn_revnum_t *min_revision,
15464                       svn_revnum_t *max_revision,
15465                       svn_wc__db_wcroot_t *wcroot,
15466                       const char *local_relpath,
15467                       svn_boolean_t committed,
15468                       apr_pool_t *scratch_pool)
15469 {
15470   svn_sqlite__stmt_t *stmt;
15471   svn_revnum_t min_rev, max_rev;
15472 
15473   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15474                                     STMT_SELECT_MIN_MAX_REVISIONS));
15475   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15476   SVN_ERR(svn_sqlite__step_row(stmt));
15477 
15478   if (committed)
15479     {
15480       min_rev = svn_sqlite__column_revnum(stmt, 2);
15481       max_rev = svn_sqlite__column_revnum(stmt, 3);
15482     }
15483   else
15484     {
15485       min_rev = svn_sqlite__column_revnum(stmt, 0);
15486       max_rev = svn_sqlite__column_revnum(stmt, 1);
15487     }
15488 
15489   /* The statement returns exactly one row. */
15490   SVN_ERR(svn_sqlite__reset(stmt));
15491 
15492   if (min_revision)
15493     *min_revision = min_rev;
15494   if (max_revision)
15495     *max_revision = max_rev;
15496 
15497   return SVN_NO_ERROR;
15498 }
15499 
15500 
15501 svn_error_t *
svn_wc__db_min_max_revisions(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t committed,apr_pool_t * scratch_pool)15502 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
15503                              svn_revnum_t *max_revision,
15504                              svn_wc__db_t *db,
15505                              const char *local_abspath,
15506                              svn_boolean_t committed,
15507                              apr_pool_t *scratch_pool)
15508 {
15509   svn_wc__db_wcroot_t *wcroot;
15510   const char *local_relpath;
15511 
15512   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15513 
15514   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15515                                                 db, local_abspath,
15516                                                 scratch_pool, scratch_pool));
15517   VERIFY_USABLE_WCROOT(wcroot);
15518 
15519   return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
15520                                                wcroot, local_relpath,
15521                                                committed, scratch_pool));
15522 }
15523 
15524 
15525 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
15526  * within LOCAL_RELPATH is sparse, FALSE otherwise. */
15527 static svn_error_t *
is_sparse_checkout_internal(svn_boolean_t * is_sparse_checkout,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)15528 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
15529                             svn_wc__db_wcroot_t *wcroot,
15530                             const char *local_relpath,
15531                             apr_pool_t *scratch_pool)
15532 {
15533   svn_sqlite__stmt_t *stmt;
15534   svn_boolean_t have_row;
15535 
15536   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15537                                     STMT_HAS_SPARSE_NODES));
15538   SVN_ERR(svn_sqlite__bindf(stmt, "is",
15539                             wcroot->wc_id,
15540                             local_relpath));
15541   /* If this query returns a row, the working copy is sparse. */
15542   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15543   *is_sparse_checkout = have_row;
15544   SVN_ERR(svn_sqlite__reset(stmt));
15545 
15546   return SVN_NO_ERROR;
15547 }
15548 
15549 
15550 /* Like svn_wc__db_has_switched_subtrees(),
15551  * but accepts a WCROOT/LOCAL_RELPATH pair. */
15552 static svn_error_t *
has_switched_subtrees(svn_boolean_t * is_switched,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * trail_url,apr_pool_t * scratch_pool)15553 has_switched_subtrees(svn_boolean_t *is_switched,
15554                       svn_wc__db_wcroot_t *wcroot,
15555                       const char *local_relpath,
15556                       const char *trail_url,
15557                       apr_pool_t *scratch_pool)
15558 {
15559   svn_sqlite__stmt_t *stmt;
15560   svn_boolean_t have_row;
15561   apr_int64_t repos_id;
15562   const char *repos_relpath;
15563 
15564   /* Optional argument handling for caller */
15565   if (!is_switched)
15566     return SVN_NO_ERROR;
15567 
15568   *is_switched = FALSE;
15569 
15570   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15571                                             &repos_relpath, &repos_id,
15572                                             NULL, NULL, NULL, NULL, NULL,
15573                                             NULL, NULL, NULL, NULL, NULL,
15574                                             wcroot, local_relpath,
15575                                             scratch_pool, scratch_pool));
15576 
15577   /* First do the cheap check where we only need info on the origin itself */
15578   if (trail_url != NULL)
15579     {
15580       const char *repos_root_url;
15581       const char *url;
15582       apr_size_t len1, len2;
15583 
15584       /* If the trailing part of the URL of the working copy directory
15585          does not match the given trailing URL then the whole working
15586          copy is switched. */
15587 
15588       SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
15589                                           repos_id, scratch_pool));
15590       url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15591                                         scratch_pool);
15592 
15593       len1 = strlen(trail_url);
15594       len2 = strlen(url);
15595       if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15596         {
15597           *is_switched = TRUE;
15598           return SVN_NO_ERROR;
15599         }
15600     }
15601 
15602   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15603   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15604   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15605   if (have_row)
15606     *is_switched = TRUE;
15607   SVN_ERR(svn_sqlite__reset(stmt));
15608 
15609   return SVN_NO_ERROR;
15610 }
15611 
15612 
15613 svn_error_t *
svn_wc__db_has_switched_subtrees(svn_boolean_t * is_switched,svn_wc__db_t * db,const char * local_abspath,const char * trail_url,apr_pool_t * scratch_pool)15614 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15615                                  svn_wc__db_t *db,
15616                                  const char *local_abspath,
15617                                  const char *trail_url,
15618                                  apr_pool_t *scratch_pool)
15619 {
15620   svn_wc__db_wcroot_t *wcroot;
15621   const char *local_relpath;
15622 
15623   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15624 
15625   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15626                                                 db, local_abspath,
15627                                                 scratch_pool, scratch_pool));
15628   VERIFY_USABLE_WCROOT(wcroot);
15629 
15630   return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15631                                                local_relpath, trail_url,
15632                                                scratch_pool));
15633 }
15634 
15635 svn_error_t *
svn_wc__db_get_excluded_subtrees(apr_hash_t ** excluded_subtrees,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)15636 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15637                                  svn_wc__db_t *db,
15638                                  const char *local_abspath,
15639                                  apr_pool_t *result_pool,
15640                                  apr_pool_t *scratch_pool)
15641 {
15642   svn_wc__db_wcroot_t *wcroot;
15643   const char *local_relpath;
15644   svn_sqlite__stmt_t *stmt;
15645   svn_boolean_t have_row;
15646 
15647   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15648   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15649                                                 db, local_abspath,
15650                                                 scratch_pool, scratch_pool));
15651   VERIFY_USABLE_WCROOT(wcroot);
15652 
15653   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15654                                     STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15655   SVN_ERR(svn_sqlite__bindf(stmt, "is",
15656                             wcroot->wc_id,
15657                             local_relpath));
15658   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15659 
15660   if (have_row)
15661     *excluded_subtrees = apr_hash_make(result_pool);
15662   else
15663     *excluded_subtrees = NULL;
15664 
15665   while (have_row)
15666     {
15667       const char *abs_path =
15668         svn_dirent_join(wcroot->abspath,
15669                         svn_sqlite__column_text(stmt, 0, NULL),
15670                         result_pool);
15671       svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15672       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15673     }
15674 
15675   SVN_ERR(svn_sqlite__reset(stmt));
15676   return SVN_NO_ERROR;
15677 }
15678 
15679 /* Like svn_wc__db_has_db_mods(),
15680  * but accepts a WCROOT/LOCAL_RELPATH pair.
15681  * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15682 static svn_error_t *
has_db_mods(svn_boolean_t * is_modified,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)15683 has_db_mods(svn_boolean_t *is_modified,
15684             svn_wc__db_wcroot_t *wcroot,
15685             const char *local_relpath,
15686             apr_pool_t *scratch_pool)
15687 {
15688   svn_sqlite__stmt_t *stmt;
15689 
15690   /* Check for additions or deletions. */
15691   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15692                                     STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15693   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15694   /* If this query returns a row, the working copy is modified. */
15695   SVN_ERR(svn_sqlite__step(is_modified, stmt));
15696   SVN_ERR(svn_sqlite__reset(stmt));
15697 
15698   if (! *is_modified)
15699     {
15700       /* Check for property modifications. */
15701       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15702                                         STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15703       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15704       /* If this query returns a row, the working copy is modified. */
15705       SVN_ERR(svn_sqlite__step(is_modified, stmt));
15706       SVN_ERR(svn_sqlite__reset(stmt));
15707     }
15708 
15709   return SVN_NO_ERROR;
15710 }
15711 
15712 
15713 svn_error_t *
svn_wc__db_has_db_mods(svn_boolean_t * is_modified,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)15714 svn_wc__db_has_db_mods(svn_boolean_t *is_modified,
15715                        svn_wc__db_t *db,
15716                        const char *local_abspath,
15717                        apr_pool_t *scratch_pool)
15718 {
15719   svn_wc__db_wcroot_t *wcroot;
15720   const char *local_relpath;
15721 
15722   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15723 
15724   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15725                                                 db, local_abspath,
15726                                                 scratch_pool, scratch_pool));
15727   VERIFY_USABLE_WCROOT(wcroot);
15728 
15729   return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath,
15730                                      scratch_pool));
15731 }
15732 
15733 
15734 /* The body of svn_wc__db_revision_status().
15735  */
15736 static svn_error_t *
revision_status_txn(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_boolean_t * is_sparse_checkout,svn_boolean_t * is_modified,svn_boolean_t * is_switched,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,const char * trail_url,svn_boolean_t committed,apr_pool_t * scratch_pool)15737 revision_status_txn(svn_revnum_t *min_revision,
15738                     svn_revnum_t *max_revision,
15739                     svn_boolean_t *is_sparse_checkout,
15740                     svn_boolean_t *is_modified,
15741                     svn_boolean_t *is_switched,
15742                     svn_wc__db_wcroot_t *wcroot,
15743                     const char *local_relpath,
15744                     svn_wc__db_t *db,
15745                     const char *trail_url,
15746                     svn_boolean_t committed,
15747                     apr_pool_t *scratch_pool)
15748 {
15749   svn_error_t *err;
15750   svn_boolean_t exists;
15751 
15752   SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15753 
15754   if (!exists)
15755     {
15756       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15757                                _("The node '%s' was not found."),
15758                                path_for_error_message(wcroot, local_relpath,
15759                                                       scratch_pool));
15760     }
15761 
15762   /* Determine mixed-revisionness. */
15763   SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15764                                 local_relpath, committed, scratch_pool));
15765 
15766   /* Determine sparseness. */
15767   SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15768                                       local_relpath, scratch_pool));
15769 
15770   /* Check for switched nodes. */
15771   {
15772     err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15773                                 trail_url, scratch_pool);
15774 
15775     if (err)
15776       {
15777         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15778           return svn_error_trace(err);
15779 
15780         svn_error_clear(err); /* No Base node, but no fatal error */
15781         *is_switched = FALSE;
15782       }
15783   }
15784 
15785   /* Check for db mods. */
15786   SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool));
15787 
15788   return SVN_NO_ERROR;
15789 }
15790 
15791 
15792 svn_error_t *
svn_wc__db_revision_status(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_boolean_t * is_sparse_checkout,svn_boolean_t * is_modified,svn_boolean_t * is_switched,svn_wc__db_t * db,const char * local_abspath,const char * trail_url,svn_boolean_t committed,apr_pool_t * scratch_pool)15793 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15794                            svn_revnum_t *max_revision,
15795                            svn_boolean_t *is_sparse_checkout,
15796                            svn_boolean_t *is_modified,
15797                            svn_boolean_t *is_switched,
15798                            svn_wc__db_t *db,
15799                            const char *local_abspath,
15800                            const char *trail_url,
15801                            svn_boolean_t committed,
15802                            apr_pool_t *scratch_pool)
15803 {
15804   svn_wc__db_wcroot_t *wcroot;
15805   const char *local_relpath;
15806 
15807   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15808 
15809   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15810                                                 db, local_abspath,
15811                                                 scratch_pool, scratch_pool));
15812   VERIFY_USABLE_WCROOT(wcroot);
15813 
15814   SVN_WC__DB_WITH_TXN(
15815     revision_status_txn(min_revision, max_revision,
15816                         is_sparse_checkout, is_modified, is_switched,
15817                         wcroot, local_relpath, db,
15818                         trail_url, committed,
15819                         scratch_pool),
15820     wcroot);
15821   return SVN_NO_ERROR;
15822 }
15823 
15824 
15825 svn_error_t *
svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t ** lock_tokens,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)15826 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15827                                           svn_wc__db_t *db,
15828                                           const char *local_abspath,
15829                                           apr_pool_t *result_pool,
15830                                           apr_pool_t *scratch_pool)
15831 {
15832   svn_wc__db_wcroot_t *wcroot;
15833   const char *local_relpath;
15834   svn_sqlite__stmt_t *stmt;
15835   svn_boolean_t have_row;
15836   apr_int64_t last_repos_id = INVALID_REPOS_ID;
15837   const char *last_repos_root_url = NULL;
15838 
15839   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15840 
15841   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15842                                                 db, local_abspath,
15843                                                 scratch_pool, scratch_pool));
15844   VERIFY_USABLE_WCROOT(wcroot);
15845 
15846   *lock_tokens = apr_hash_make(result_pool);
15847 
15848   /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15849   SVN_ERR(svn_sqlite__get_statement(
15850               &stmt, wcroot->sdb,
15851               STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15852 
15853   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15854   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15855   while (have_row)
15856     {
15857       apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15858       const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15859       const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15860 
15861       if (child_repos_id != last_repos_id)
15862         {
15863           svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15864                                                          NULL, wcroot,
15865                                                          child_repos_id,
15866                                                          scratch_pool);
15867 
15868           if (err)
15869             {
15870               return svn_error_trace(
15871                             svn_error_compose_create(err,
15872                                                      svn_sqlite__reset(stmt)));
15873             }
15874 
15875           last_repos_id = child_repos_id;
15876         }
15877 
15878       SVN_ERR_ASSERT(last_repos_root_url != NULL);
15879       svn_hash_sets(*lock_tokens,
15880                     svn_path_url_add_component2(last_repos_root_url,
15881                                                 child_relpath, result_pool),
15882                     lock_token);
15883 
15884       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15885     }
15886   return svn_sqlite__reset(stmt);
15887 }
15888 
15889 
15890 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15891  * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15892 #define VERIFY(expression) \
15893   do { \
15894     if (! (expression)) \
15895       return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15896         _("database inconsistency at local_relpath='%s' verifying " \
15897           "expression '%s'"), local_relpath, #expression); \
15898   } while (0)
15899 
15900 
15901 /* Verify consistency of the metadata concerning WCROOT.  This is intended
15902  * for use only during testing and debugging, so is not intended to be
15903  * blazingly fast.
15904  *
15905  * This code is a complement to any verification that we can do in SQLite
15906  * triggers.  See, for example, 'wc-checks.sql'.
15907  *
15908  * Some more verification steps we might want to add are:
15909  *
15910  *   * on every ACTUAL row (except root): a NODES row exists at its parent path
15911  *   * the op-depth root must always exist and every intermediate too
15912  */
15913 static svn_error_t *
verify_wcroot(svn_wc__db_wcroot_t * wcroot,apr_pool_t * scratch_pool)15914 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15915               apr_pool_t *scratch_pool)
15916 {
15917   svn_sqlite__stmt_t *stmt;
15918   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15919 
15920   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15921                                     STMT_SELECT_ALL_NODES));
15922   SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15923   while (TRUE)
15924     {
15925       svn_boolean_t have_row;
15926       const char *local_relpath, *parent_relpath;
15927       int op_depth;
15928 
15929       svn_pool_clear(iterpool);
15930 
15931       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15932       if (!have_row)
15933         break;
15934 
15935       op_depth = svn_sqlite__column_int(stmt, 0);
15936       local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15937       parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15938 
15939       /* Verify parent_relpath is the parent path of local_relpath */
15940       VERIFY((parent_relpath == NULL)
15941              ? (local_relpath[0] == '\0')
15942              : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15943                        parent_relpath) == 0));
15944 
15945       /* Verify op_depth <= the tree depth of local_relpath */
15946       VERIFY(op_depth <= relpath_depth(local_relpath));
15947 
15948       /* Verify parent_relpath refers to a row that exists */
15949       /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15950        * the child's and a suitable presence */
15951       if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15952         {
15953           svn_sqlite__stmt_t *stmt2;
15954           svn_boolean_t have_a_parent_row;
15955 
15956           SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15957                                             STMT_SELECT_NODE_INFO));
15958           SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15959                                     parent_relpath));
15960           SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15961           VERIFY(have_a_parent_row);
15962           SVN_ERR(svn_sqlite__reset(stmt2));
15963         }
15964     }
15965   svn_pool_destroy(iterpool);
15966 
15967   return svn_error_trace(svn_sqlite__reset(stmt));
15968 }
15969 
15970 svn_error_t *
svn_wc__db_verify(svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * scratch_pool)15971 svn_wc__db_verify(svn_wc__db_t *db,
15972                   const char *wri_abspath,
15973                   apr_pool_t *scratch_pool)
15974 {
15975   svn_wc__db_wcroot_t *wcroot;
15976   const char *local_relpath;
15977 
15978   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15979                                                 db, wri_abspath,
15980                                                 scratch_pool, scratch_pool));
15981   VERIFY_USABLE_WCROOT(wcroot);
15982 
15983   SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15984   return SVN_NO_ERROR;
15985 }
15986 
15987 
15988 svn_error_t *
svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t * wcroot,svn_wc__db_verify_cb_t callback,void * baton,apr_pool_t * scratch_pool)15989 svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot,
15990                                    svn_wc__db_verify_cb_t callback,
15991                                    void *baton,
15992                                    apr_pool_t *scratch_pool)
15993 {
15994   svn_sqlite__stmt_t *stmt;
15995   svn_boolean_t have_row;
15996   svn_error_t *err = NULL;
15997   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15998 
15999   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY));
16000   SVN_ERR(svn_sqlite__step(&have_row, stmt));
16001 
16002   while (have_row)
16003     {
16004       const char *local_relpath;
16005       int op_depth = svn_sqlite__column_int(stmt, 1);
16006       int id = svn_sqlite__column_int(stmt, 2);
16007       const char *msg;
16008 
16009       svn_pool_clear(iterpool);
16010 
16011       local_relpath =  svn_sqlite__column_text(stmt, 0, iterpool);
16012       msg = svn_sqlite__column_text(stmt, 3, scratch_pool);
16013 
16014       err = callback(baton, wcroot->abspath, local_relpath, op_depth,
16015                      id, msg, iterpool);
16016 
16017       if (err)
16018         break;
16019 
16020       SVN_ERR(svn_sqlite__step(&have_row, stmt));
16021     }
16022 
16023   svn_pool_destroy(iterpool);
16024 
16025   return svn_error_trace(
16026             svn_error_compose_create(err, svn_sqlite__reset(stmt)));
16027 }
16028 
16029 svn_error_t *
svn_wc__db_verify_db_full(svn_wc__db_t * db,const char * wri_abspath,svn_wc__db_verify_cb_t callback,void * baton,apr_pool_t * scratch_pool)16030 svn_wc__db_verify_db_full(svn_wc__db_t *db,
16031                           const char *wri_abspath,
16032                           svn_wc__db_verify_cb_t callback,
16033                           void *baton,
16034                           apr_pool_t *scratch_pool)
16035 {
16036   svn_wc__db_wcroot_t *wcroot;
16037   const char *local_relpath;
16038 
16039   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16040 
16041   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16042                               wri_abspath, scratch_pool, scratch_pool));
16043   VERIFY_USABLE_WCROOT(wcroot);
16044 
16045   return svn_error_trace(
16046             svn_wc__db_verify_db_full_internal(wcroot, callback, baton,
16047                                                scratch_pool));
16048 }
16049 
16050 svn_error_t *
svn_wc__db_bump_format(int * result_format,svn_boolean_t * bumped_format,svn_wc__db_t * db,const char * wcroot_abspath,apr_pool_t * scratch_pool)16051 svn_wc__db_bump_format(int *result_format,
16052                        svn_boolean_t *bumped_format,
16053                        svn_wc__db_t *db,
16054                        const char *wcroot_abspath,
16055                        apr_pool_t *scratch_pool)
16056 {
16057   svn_sqlite__db_t *sdb;
16058   svn_error_t *err;
16059   int format;
16060 
16061   if (bumped_format)
16062     *bumped_format = FALSE;
16063 
16064   /* Do not scan upwards for a working copy root here to prevent accidental
16065    * upgrades of any working copies the WCROOT might be nested in.
16066    * Just try to open a DB at the specified path instead. */
16067   err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
16068                                 svn_sqlite__mode_readwrite,
16069                                 TRUE, /* exclusive */
16070                                 0, /* default timeout */
16071                                 NULL, /* my statements */
16072                                 scratch_pool, scratch_pool);
16073   if (err)
16074     {
16075       svn_error_t *err2;
16076       apr_hash_t *entries;
16077 
16078       /* Could not open an sdb. Check for an entries file instead. */
16079       err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
16080                                       scratch_pool, scratch_pool);
16081       if (err2 || apr_hash_count(entries) == 0)
16082         return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
16083                   svn_error_compose_create(err, err2),
16084                   _("Can't upgrade '%s' as it is not a working copy root"),
16085                   svn_dirent_local_style(wcroot_abspath, scratch_pool));
16086 
16087       /* An entries file was found. This is a pre-wc-ng working copy
16088        * so suggest an upgrade. */
16089       return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
16090                 _("Working copy '%s' is too old and must be upgraded to "
16091                   "at least format %d, as created by Subversion %s"),
16092                 svn_dirent_local_style(wcroot_abspath, scratch_pool),
16093                 SVN_WC__WC_NG_VERSION,
16094                 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
16095     }
16096 
16097   SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
16098   err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
16099                             sdb, format, scratch_pool);
16100 
16101   if (err == SVN_NO_ERROR && bumped_format)
16102     *bumped_format = (*result_format > format);
16103 
16104   /* Make sure we return a different error than expected for upgrades from
16105      entries */
16106   if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
16107     err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
16108                            _("Working copy upgrade failed"));
16109 
16110   err = svn_error_compose_create(err, svn_sqlite__close(sdb));
16111 
16112   return svn_error_trace(err);
16113 }
16114 
16115 svn_error_t *
svn_wc__db_vacuum(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)16116 svn_wc__db_vacuum(svn_wc__db_t *db,
16117                   const char *local_abspath,
16118                   apr_pool_t *scratch_pool)
16119 {
16120   svn_wc__db_wcroot_t *wcroot;
16121   const char *local_relpath;
16122 
16123   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
16124                                                 db, local_abspath,
16125                                                 scratch_pool, scratch_pool));
16126   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
16127 
16128   return SVN_NO_ERROR;
16129 }
16130 
16131 /* Item queued with svn_wc__db_commit_queue_add */
16132 typedef struct commit_queue_item_t
16133 {
16134   const char *local_relpath;
16135   svn_boolean_t recurse; /* Use legacy recursion */
16136   svn_boolean_t committed; /* Process the node as committed */
16137   svn_boolean_t remove_lock; /* Remove existing lock on node */
16138   svn_boolean_t remove_changelist; /* Remove changelist on node */
16139 
16140   /* The pristine text checksum. NULL if the old value should be kept
16141      and for directories */
16142   const svn_checksum_t *new_sha1_checksum;
16143 
16144   apr_hash_t *new_dav_cache; /* New DAV cache for the node */
16145 } commit_queue_item_t;
16146 
16147 /* The queue definition for vn_wc__db_create_commit_queue,
16148    svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */
16149 struct svn_wc__db_commit_queue_t
16150 {
16151   svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */
16152   apr_array_header_t *items; /* List of commit_queue_item_t* */
16153   svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */
16154 };
16155 
16156 /* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the
16157    working copy specified with WRI_ABSPATH */
16158 svn_error_t *
svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t ** queue,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16159 svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue,
16160                                svn_wc__db_t *db,
16161                                const char *wri_abspath,
16162                                apr_pool_t *result_pool,
16163                                apr_pool_t *scratch_pool)
16164 {
16165   svn_wc__db_wcroot_t *wcroot;
16166   const char *local_relpath;
16167   svn_wc__db_commit_queue_t *q;
16168 
16169   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16170 
16171   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16172                               wri_abspath, result_pool, scratch_pool));
16173   VERIFY_USABLE_WCROOT(wcroot);
16174 
16175   q = apr_pcalloc(result_pool, sizeof(*q));
16176 
16177   SVN_ERR_ASSERT(wcroot->sdb);
16178 
16179   q->wcroot = wcroot;
16180   q->items = apr_array_make(result_pool, 64,
16181                             sizeof(commit_queue_item_t*));
16182   q->have_recurse = FALSE;
16183 
16184   *queue = q;
16185   return SVN_NO_ERROR;
16186 }
16187 
16188 svn_error_t *
svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t * queue,const char * local_abspath,svn_boolean_t recurse,svn_boolean_t is_commited,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * new_sha1_checksum,apr_hash_t * new_dav_cache,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16189 svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue,
16190                             const char *local_abspath,
16191                             svn_boolean_t recurse,
16192                             svn_boolean_t is_commited,
16193                             svn_boolean_t remove_lock,
16194                             svn_boolean_t remove_changelist,
16195                             const svn_checksum_t *new_sha1_checksum,
16196                             apr_hash_t *new_dav_cache,
16197                             apr_pool_t *result_pool,
16198                             apr_pool_t *scratch_pool)
16199 {
16200   commit_queue_item_t *cqi;
16201   const char *local_relpath;
16202 
16203   local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath,
16204                                            local_abspath);
16205 
16206   if (! local_relpath)
16207     return svn_error_createf(
16208                 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
16209                 _("The path '%s' is not in the working copy '%s'"),
16210                 svn_dirent_local_style(local_abspath, scratch_pool),
16211                 svn_dirent_local_style(queue->wcroot->abspath, scratch_pool));
16212 
16213   cqi = apr_pcalloc(result_pool, sizeof(*cqi));
16214   cqi->local_relpath = local_relpath;
16215   cqi->recurse = recurse;
16216   cqi->committed = is_commited;
16217   cqi->remove_lock = remove_lock;
16218   cqi->remove_changelist = remove_changelist;
16219   cqi->new_sha1_checksum = new_sha1_checksum;
16220   cqi->new_dav_cache = new_dav_cache;
16221 
16222   queue->have_recurse |= recurse;
16223 
16224   APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi;
16225   return SVN_NO_ERROR;
16226 }
16227 
16228 /*** Finishing updates and commits. ***/
16229 
16230 /* Post process an item that is committed in the repository. Collapse layers into
16231  * BASE. Queue work items that will finish a commit of the file or directory
16232  * LOCAL_ABSPATH in DB:
16233  */
16234 static svn_error_t *
process_committed_leaf(svn_wc__db_t * db,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t via_recurse,svn_wc__db_status_t status,svn_node_kind_t kind,svn_boolean_t prop_mods,const svn_checksum_t * old_checksum,svn_revnum_t new_revnum,apr_time_t new_changed_date,const char * new_changed_author,apr_hash_t * new_dav_cache,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * checksum,apr_pool_t * scratch_pool)16235 process_committed_leaf(svn_wc__db_t *db,
16236                        svn_wc__db_wcroot_t *wcroot,
16237                        const char *local_relpath,
16238                        svn_boolean_t via_recurse,
16239                        svn_wc__db_status_t status,
16240                        svn_node_kind_t kind,
16241                        svn_boolean_t prop_mods,
16242                        const svn_checksum_t *old_checksum,
16243                        svn_revnum_t new_revnum,
16244                        apr_time_t new_changed_date,
16245                        const char *new_changed_author,
16246                        apr_hash_t *new_dav_cache,
16247                        svn_boolean_t remove_lock,
16248                        svn_boolean_t remove_changelist,
16249                        const svn_checksum_t *checksum,
16250                        apr_pool_t *scratch_pool)
16251 {
16252   svn_revnum_t new_changed_rev = new_revnum;
16253   svn_skel_t *work_item = NULL;
16254 
16255   {
16256     const char *lock_relpath;
16257     svn_boolean_t locked;
16258 
16259     if (kind == svn_node_dir)
16260       lock_relpath = local_relpath;
16261     else
16262       lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
16263 
16264     SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
16265                                                  lock_relpath, FALSE,
16266                                                  scratch_pool));
16267 
16268     if (!locked)
16269       return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
16270                              _("No write-lock in '%s'"),
16271                              path_for_error_message(wcroot, local_relpath,
16272                                                     scratch_pool));
16273 
16274     SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty,
16275                           scratch_pool));
16276   }
16277 
16278   if (status == svn_wc__db_status_not_present)
16279     {
16280       /* We are committing the leaf of a copy operation.
16281          We leave the not-present marker to allow pulling in excluded
16282          children of a copy.
16283 
16284          The next update will remove the not-present marker. */
16285 
16286       return SVN_NO_ERROR;
16287     }
16288 
16289   SVN_ERR_ASSERT(status == svn_wc__db_status_normal
16290                  || status == svn_wc__db_status_incomplete
16291                  || status == svn_wc__db_status_added
16292                  || status == svn_wc__db_status_deleted);
16293 
16294   if (kind != svn_node_dir
16295       && status != svn_wc__db_status_deleted)
16296     {
16297       /* If we sent a delta (meaning: post-copy modification),
16298          then this file will appear in the queue and so we should have
16299          its checksum already. */
16300       if (checksum == NULL)
16301         {
16302           /* It was copied and not modified. We must have a text
16303              base for it. And the node should have a checksum. */
16304           SVN_ERR_ASSERT(old_checksum != NULL);
16305 
16306           checksum = old_checksum;
16307 
16308           /* Is the node completely unmodified and are we recursing? */
16309           if (via_recurse && !prop_mods)
16310             {
16311               /* If a copied node itself is not modified, but the op_root of
16312                  the copy is committed we have to make sure that changed_rev,
16313                  changed_date and changed_author don't change or the working
16314                  copy used for committing will show different last modified
16315                  information then a clean checkout of exactly the same
16316                  revisions. (Issue #3676) */
16317 
16318               SVN_ERR(svn_wc__db_read_info_internal(
16319                                            NULL, NULL, NULL, NULL, NULL,
16320                                            &new_changed_rev,
16321                                            &new_changed_date,
16322                                            &new_changed_author, NULL, NULL,
16323                                            NULL, NULL, NULL, NULL, NULL,
16324                                            NULL, NULL, NULL, NULL,
16325                                            NULL, NULL, NULL, NULL,
16326                                            NULL, NULL,
16327                                            wcroot, local_relpath,
16328                                            scratch_pool, scratch_pool));
16329             }
16330         }
16331 
16332       SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
16333                                            db, svn_dirent_join(wcroot->abspath,
16334                                                                local_relpath,
16335                                                                scratch_pool),
16336                                            prop_mods,
16337                                            scratch_pool, scratch_pool));
16338     }
16339 
16340   /* The new text base will be found in the pristine store by its checksum. */
16341   SVN_ERR(commit_node(wcroot, local_relpath,
16342                       new_revnum, new_changed_rev,
16343                       new_changed_date, new_changed_author,
16344                       checksum,
16345                       new_dav_cache,
16346                       !remove_changelist,
16347                       !remove_lock,
16348                       work_item,
16349                       scratch_pool));
16350 
16351   return SVN_NO_ERROR;
16352 }
16353 
16354 /** Internal helper for svn_wc_process_committed_queue2().
16355  * Bump a commit item, collapsing local changes with the new repository
16356  * information to a new BASE node.
16357  *
16358  * @a new_date is the (server-side) date of the new revision, or 0.
16359  *
16360  * @a rev_author is the (server-side) author of the new
16361  * revision; it may be @c NULL.
16362  *
16363  * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH.
16364  *
16365  * If @a remove_lock is set, release any user locks on @a
16366  * local_abspath; otherwise keep them during processing.
16367  *
16368  * If @a remove_changelist is set, clear any changeset assignments
16369  * from @a local_abspath; otherwise, keep such assignments.
16370  *
16371  * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine
16372  * text.
16373  *
16374  * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
16375  * recursive commit operation. (Part of the legacy recurse handling)
16376  */
16377 static svn_error_t *
process_committed_internal(svn_wc__db_t * db,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t recurse,svn_boolean_t top_of_recurse,svn_revnum_t new_revnum,apr_time_t new_date,const char * rev_author,apr_hash_t * new_dav_cache,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * new_sha1_checksum,apr_hash_t * items_by_relpath,apr_pool_t * scratch_pool)16378 process_committed_internal(svn_wc__db_t *db,
16379                            svn_wc__db_wcroot_t *wcroot,
16380                            const char *local_relpath,
16381                            svn_boolean_t recurse,
16382                            svn_boolean_t top_of_recurse,
16383                            svn_revnum_t new_revnum,
16384                            apr_time_t new_date,
16385                            const char *rev_author,
16386                            apr_hash_t *new_dav_cache,
16387                            svn_boolean_t remove_lock,
16388                            svn_boolean_t remove_changelist,
16389                            const svn_checksum_t *new_sha1_checksum,
16390                            apr_hash_t *items_by_relpath,
16391                            apr_pool_t *scratch_pool)
16392 {
16393   svn_wc__db_status_t status;
16394   svn_node_kind_t kind;
16395   const svn_checksum_t *old_checksum;
16396   svn_boolean_t prop_mods;
16397 
16398   SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL,
16399                                         NULL, NULL, &old_checksum, NULL, NULL,
16400                                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
16401                                         NULL, &prop_mods, NULL, NULL, NULL,
16402                                         wcroot, local_relpath,
16403                                         scratch_pool, scratch_pool));
16404 
16405   /* NOTE: be wary of making crazy semantic changes in this function, since
16406      svn_wc_process_committed4() calls this.  */
16407 
16408   SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse,
16409                                  status, kind, prop_mods, old_checksum,
16410                                  new_revnum, new_date, rev_author,
16411                                  new_dav_cache,
16412                                  remove_lock, remove_changelist,
16413                                  new_sha1_checksum,
16414                                  scratch_pool));
16415 
16416   /* Only check for recursion on nodes that have children */
16417   if (kind != svn_node_dir
16418       || status == svn_wc__db_status_not_present
16419       || status == svn_wc__db_status_excluded
16420       || status == svn_wc__db_status_server_excluded
16421       /* Node deleted -> then no longer a directory */
16422       || status == svn_wc__db_status_deleted)
16423     {
16424       return SVN_NO_ERROR;
16425     }
16426 
16427   if (recurse)
16428     {
16429       const apr_array_header_t *children;
16430       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16431       int i;
16432 
16433       /* Read PATH's entries;  this is the absolute path. */
16434       SVN_ERR(gather_children(&children, wcroot, local_relpath,
16435                               STMT_SELECT_NODE_CHILDREN, -1,
16436                               scratch_pool, iterpool));
16437 
16438       /* Recursively loop over all children. */
16439       for (i = 0; i < children->nelts; i++)
16440         {
16441           const char *name = APR_ARRAY_IDX(children, i, const char *);
16442           const char *this_relpath;
16443           const commit_queue_item_t *cqi;
16444 
16445           svn_pool_clear(iterpool);
16446 
16447           this_relpath = svn_dirent_join(local_relpath, name, iterpool);
16448 
16449           new_sha1_checksum = NULL;
16450           cqi = svn_hash_gets(items_by_relpath, this_relpath);
16451 
16452           if (cqi != NULL)
16453             new_sha1_checksum = cqi->new_sha1_checksum;
16454 
16455           /* Recurse.  Pass NULL for NEW_DAV_CACHE, because the
16456              ones present in the current call are only applicable to
16457              this one committed item. */
16458           SVN_ERR(process_committed_internal(
16459                     db, wcroot, this_relpath,
16460                     TRUE /* recurse */,
16461                     FALSE /* top_of_recurse */,
16462                     new_revnum, new_date,
16463                     rev_author,
16464                     NULL /* new_dav_cache */,
16465                     FALSE /* remove_lock */,
16466                     remove_changelist,
16467                     new_sha1_checksum,
16468                     items_by_relpath,
16469                     iterpool));
16470         }
16471 
16472       svn_pool_destroy(iterpool);
16473     }
16474 
16475   return SVN_NO_ERROR;
16476 }
16477 
16478 /* Return TRUE if any item of QUEUE is a parent of ITEM and will be
16479    processed recursively, return FALSE otherwise.
16480 
16481    The algorithmic complexity of this search implementation is O(queue
16482    length), but it's quite quick.
16483 */
16484 static svn_boolean_t
have_recursive_parent(const apr_array_header_t * all_items,const commit_queue_item_t * item,apr_pool_t * scratch_pool)16485 have_recursive_parent(const apr_array_header_t *all_items,
16486                       const commit_queue_item_t *item,
16487                       apr_pool_t *scratch_pool)
16488 {
16489   const char *local_relpath = item->local_relpath;
16490   int i;
16491 
16492   for (i = 0; i < all_items->nelts; i++)
16493     {
16494       const commit_queue_item_t *qi
16495         = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *);
16496 
16497       if (qi == item)
16498         continue;
16499 
16500       if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath,
16501                                                    local_relpath))
16502         {
16503           return TRUE;
16504         }
16505     }
16506 
16507   return FALSE;
16508 }
16509 
16510 /* Compare function for svn_sort__array */
16511 static int
compare_queue_items(const void * v1,const void * v2)16512 compare_queue_items(const void *v1,
16513                     const void *v2)
16514 {
16515   const commit_queue_item_t *cqi1
16516               = *(const commit_queue_item_t **)v1;
16517   const commit_queue_item_t *cqi2
16518               = *(const commit_queue_item_t **)v2;
16519 
16520   return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath);
16521 }
16522 
16523 /* Internal, locked version of svn_wc__db_process_commit_queue */
16524 static svn_error_t *
db_process_commit_queue(svn_wc__db_t * db,svn_wc__db_commit_queue_t * queue,svn_revnum_t new_revnum,apr_time_t new_date,const char * new_author,apr_pool_t * scratch_pool)16525 db_process_commit_queue(svn_wc__db_t *db,
16526                         svn_wc__db_commit_queue_t *queue,
16527                         svn_revnum_t new_revnum,
16528                         apr_time_t new_date,
16529                         const char *new_author,
16530                         apr_pool_t *scratch_pool)
16531 {
16532   apr_hash_t *items_by_relpath = NULL;
16533   int j;
16534   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16535 
16536   svn_sort__array(queue->items, compare_queue_items);
16537 
16538   if (queue->have_recurse)
16539     {
16540       items_by_relpath = apr_hash_make(scratch_pool);
16541 
16542       for (j = 0; j < queue->items->nelts; j++)
16543         {
16544           commit_queue_item_t *cqi
16545             = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16546 
16547           svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi);
16548         }
16549     }
16550 
16551   for (j = 0; j < queue->items->nelts; j++)
16552     {
16553       commit_queue_item_t *cqi
16554         = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16555 
16556       svn_pool_clear(iterpool);
16557 
16558       /* Skip this item if it is a child of a recursive item, because it has
16559          been (or will be) accounted for when that recursive item was (or
16560          will be) processed. */
16561       if (queue->have_recurse && have_recursive_parent(queue->items, cqi,
16562                                                        iterpool))
16563         continue;
16564 
16565       if (!cqi->committed)
16566         {
16567           if (cqi->remove_lock)
16568             {
16569               svn_skel_t *work_item;
16570 
16571               SVN_ERR(svn_wc__wq_build_sync_file_flags(
16572                                                     &work_item,
16573                                                     db,
16574                                                     svn_dirent_join(
16575                                                         queue->wcroot->abspath,
16576                                                         cqi->local_relpath,
16577                                                         iterpool),
16578                                                     iterpool, iterpool));
16579 
16580               lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item,
16581                               iterpool);
16582             }
16583           if (cqi->remove_changelist)
16584             SVN_ERR(svn_wc__db_op_set_changelist(db,
16585                                                  svn_dirent_join(
16586                                                         queue->wcroot->abspath,
16587                                                         cqi->local_relpath,
16588                                                         iterpool),
16589                                                  NULL, NULL,
16590                                                  svn_depth_empty,
16591                                                  NULL, NULL, /* notify */
16592                                                  NULL, NULL, /* cancel */
16593                                                  iterpool));
16594         }
16595       else
16596         {
16597           SVN_ERR(process_committed_internal(
16598                                   db, queue->wcroot, cqi->local_relpath,
16599                                   cqi->recurse,
16600                                   TRUE /* top_of_recurse */,
16601                                   new_revnum, new_date, new_author,
16602                                   cqi->new_dav_cache,
16603                                   cqi->remove_lock,
16604                                   cqi->remove_changelist,
16605                                   cqi->new_sha1_checksum,
16606                                   items_by_relpath,
16607                                   iterpool));
16608         }
16609     }
16610 
16611   svn_pool_destroy(iterpool);
16612 
16613   return SVN_NO_ERROR;
16614 }
16615 
16616 svn_error_t *
svn_wc__db_process_commit_queue(svn_wc__db_t * db,svn_wc__db_commit_queue_t * queue,svn_revnum_t new_revnum,apr_time_t new_date,const char * new_author,apr_pool_t * scratch_pool)16617 svn_wc__db_process_commit_queue(svn_wc__db_t *db,
16618                                 svn_wc__db_commit_queue_t *queue,
16619                                 svn_revnum_t new_revnum,
16620                                 apr_time_t new_date,
16621                                 const char *new_author,
16622                                 apr_pool_t *scratch_pool)
16623 {
16624   SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue,
16625                                               new_revnum, new_date,
16626                                               new_author, scratch_pool),
16627                         queue->wcroot);
16628 
16629   return SVN_NO_ERROR;
16630 }
16631