wc_db.c revision 299742
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 115int 116svn_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 */ 123typedef 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 */ 182typedef 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 */ 224typedef 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 */ 275static svn_error_t * 276add_work_items(svn_sqlite__db_t *sdb, 277 const svn_skel_t *skel, 278 apr_pool_t *scratch_pool); 279 280static svn_error_t * 281set_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 286static svn_error_t * 287insert_incomplete_children(svn_sqlite__db_t *sdb, 288 apr_int64_t wc_id, 289 const char *local_relpath, 290 apr_int64_t repos_id, 291 const char *repos_relpath, 292 svn_revnum_t revision, 293 const apr_array_header_t *children, 294 int op_depth, 295 apr_pool_t *scratch_pool); 296 297static svn_error_t * 298db_read_pristine_props(apr_hash_t **props, 299 svn_wc__db_wcroot_t *wcroot, 300 const char *local_relpath, 301 svn_boolean_t deleted_ok, 302 apr_pool_t *result_pool, 303 apr_pool_t *scratch_pool); 304 305static svn_error_t * 306read_info(svn_wc__db_status_t *status, 307 svn_node_kind_t *kind, 308 svn_revnum_t *revision, 309 const char **repos_relpath, 310 apr_int64_t *repos_id, 311 svn_revnum_t *changed_rev, 312 apr_time_t *changed_date, 313 const char **changed_author, 314 svn_depth_t *depth, 315 const svn_checksum_t **checksum, 316 const char **target, 317 const char **original_repos_relpath, 318 apr_int64_t *original_repos_id, 319 svn_revnum_t *original_revision, 320 svn_wc__db_lock_t **lock, 321 svn_filesize_t *recorded_size, 322 apr_time_t *recorded_time, 323 const char **changelist, 324 svn_boolean_t *conflicted, 325 svn_boolean_t *op_root, 326 svn_boolean_t *had_props, 327 svn_boolean_t *props_mod, 328 svn_boolean_t *have_base, 329 svn_boolean_t *have_more_work, 330 svn_boolean_t *have_work, 331 svn_wc__db_wcroot_t *wcroot, 332 const char *local_relpath, 333 apr_pool_t *result_pool, 334 apr_pool_t *scratch_pool); 335 336static svn_error_t * 337scan_addition(svn_wc__db_status_t *status, 338 const char **op_root_relpath, 339 const char **repos_relpath, 340 apr_int64_t *repos_id, 341 const char **original_repos_relpath, 342 apr_int64_t *original_repos_id, 343 svn_revnum_t *original_revision, 344 const char **moved_from_relpath, 345 const char **moved_from_op_root_relpath, 346 int *moved_from_op_depth, 347 svn_wc__db_wcroot_t *wcroot, 348 const char *local_relpath, 349 apr_pool_t *result_pool, 350 apr_pool_t *scratch_pool); 351 352static svn_error_t * 353convert_to_working_status(svn_wc__db_status_t *working_status, 354 svn_wc__db_status_t status); 355 356static svn_error_t * 357db_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. */ 366static const char * 367path_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. */ 380static svn_filesize_t 381get_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. */ 391static svn_wc__db_lock_t * 392lock_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 417svn_error_t * 418svn_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. */ 460static void 461repos_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. */ 491static svn_error_t * 492create_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. */ 531static void 532blank_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 */ 583static svn_error_t * 584db_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 */ 640static svn_error_t * 641db_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. */ 706static svn_error_t * 707insert_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. */ 896static void 897blank_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. */ 915static svn_error_t * 916insert_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. */ 998static svn_error_t * 999insert_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. */ 1158static svn_error_t * 1159gather_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. */ 1196static svn_boolean_t 1197is_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. */ 1209static void 1210remove_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. */ 1229static svn_error_t * 1230flush_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. */ 1279static svn_error_t * 1280add_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. */ 1298static svn_error_t * 1299add_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. */ 1332static svn_error_t * 1333does_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 1346svn_error_t * 1347svn_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 */ 1357static svn_error_t * 1358init_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 */ 1425static svn_error_t * 1426create_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 1457svn_error_t * 1458svn_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 1533svn_error_t * 1534svn_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 1567svn_error_t * 1568svn_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 1596svn_error_t * 1597svn_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 1619svn_error_t * 1620svn_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 1703svn_error_t * 1704svn_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 1758svn_error_t * 1759svn_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 1846svn_error_t * 1847svn_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 1933static svn_error_t * 1934add_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 2006svn_error_t * 2007svn_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 2028svn_error_t * 2029svn_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 */ 2049static svn_error_t * 2050clear_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 2073svn_error_t * 2074svn_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 */ 2105static svn_error_t * 2106db_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 2439svn_error_t * 2440svn_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 2475svn_error_t * 2476svn_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 2617svn_error_t * 2618svn_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 */ 2669static svn_error_t * 2670base_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 2743svn_error_t * 2744svn_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 2768svn_error_t * 2769svn_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 2796svn_error_t * 2797svn_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 2820svn_error_t * 2821svn_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 2854svn_error_t * 2855svn_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 2889svn_error_t * 2890svn_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 2913svn_error_t * 2914svn_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. */ 3047typedef 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(). */ 3053struct 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. */ 3067static svn_error_t * 3068with_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(). */ 3088typedef 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. */ 3110static svn_error_t * 3111with_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. */ 3152static void 3153blank_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. */ 3167static svn_error_t * 3168insert_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 3270svn_error_t * 3271svn_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 3368svn_error_t * 3369svn_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 3454svn_error_t * 3455svn_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(). */ 3512static svn_error_t * 3513db_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 3538svn_error_t * 3539svn_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 3568svn_error_t * 3569svn_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 3657svn_error_t * 3658svn_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 3717svn_error_t * 3718svn_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 3762svn_error_t * 3763svn_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. */ 3842static svn_error_t * 3843copy_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 */ 3887static svn_error_t * 3888cross_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. */ 3961static svn_error_t * 3962get_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 */ 4012static svn_error_t * 4013scan_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 4164svn_error_t * 4165svn_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 4183svn_error_t * 4184svn_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. */ 4248static svn_error_t * 4249get_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. */ 4381static svn_error_t * 4382op_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 */ 4418static svn_error_t * 4419op_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. */ 4513static svn_error_t * 4514db_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(©from_id, ©from_relpath, ©from_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(). */ 4776struct 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(). */ 4791static svn_error_t * 4792op_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 4823svn_error_t * 4824svn_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 */ 4864static svn_error_t * 4865clear_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 4929svn_error_t * 4930svn_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 */ 5093static svn_error_t * 5094handle_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 5267svn_error_t * 5268svn_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. */ 5315static svn_error_t * 5316db_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(). */ 5531static svn_error_t * 5532op_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 5605svn_error_t * 5606svn_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. */ 5647static svn_error_t * 5648catch_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 5676svn_error_t * 5677svn_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 5753svn_error_t * 5754svn_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 5838svn_error_t * 5839svn_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 5912svn_error_t * 5913svn_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 5959svn_error_t * 5960svn_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 6003svn_error_t * 6004svn_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 */ 6053static svn_error_t * 6054db_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 6075svn_error_t * 6076svn_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. */ 6107static svn_error_t * 6108set_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 6149svn_error_t * 6150svn_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*/ 6176static svn_error_t * 6177set_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 6215svn_error_t * 6216svn_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 6241svn_error_t * 6242svn_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/* */ 6252static svn_error_t * 6253populate_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 6384static svn_error_t * 6385dump_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 6408struct 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. */ 6419static svn_error_t * 6420set_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. */ 6479static svn_error_t * 6480do_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 6531svn_error_t * 6532svn_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() */ 6574svn_error_t * 6575svn_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 6621svn_error_t * 6622svn_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 */ 6652svn_error_t * 6653svn_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 6739svn_error_t * 6740svn_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 */ 6775static svn_error_t * 6776clear_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 */ 6806static svn_error_t * 6807revert_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 */ 6872struct 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. */ 6881static svn_error_t * 6882op_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. */ 7037static svn_error_t * 7038op_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 7189svn_error_t * 7190svn_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 */ 7239static svn_error_t * 7240revert_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 7318svn_error_t * 7319svn_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 */ 7346static svn_error_t * 7347revert_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 7391svn_error_t * 7392svn_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 7413svn_error_t * 7414svn_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 7487svn_error_t * 7488svn_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 */ 7506static svn_error_t * 7507remove_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 7734svn_error_t * 7735svn_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 */ 7772static svn_error_t * 7773db_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 7798svn_error_t * 7799svn_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 7826static svn_error_t * 7827info_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. */ 7839static svn_error_t * 7840convert_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. */ 7883static svn_error_t * 7884info_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 */ 7938static svn_error_t * 7939delete_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 7967struct 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 */ 7995struct 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. */ 8012static svn_error_t * 8013resolve_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 8084static svn_error_t * 8085delete_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 8511static svn_error_t * 8512op_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 8524struct 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 8530static svn_error_t * 8531op_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 8566static svn_error_t * 8567do_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 8618svn_error_t * 8619svn_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 8699svn_error_t * 8700svn_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 */ 8775static int 8776column_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. */ 8805static svn_error_t * 8806read_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 9130svn_error_t * 9131svn_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 9173svn_error_t * 9174svn_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 9236static svn_error_t * 9237is_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 */ 9243static svn_error_t * 9244find_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. */ 9265struct 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 */ 9274static svn_error_t * 9275read_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 9573svn_error_t * 9574svn_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*/ 9614static svn_error_t * 9615read_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 9775svn_error_t * 9776svn_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 9801svn_error_t * 9802svn_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 9963svn_error_t * 9964svn_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 10021svn_error_t * 10022svn_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 */ 10103static svn_error_t * 10104db_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 10221svn_error_t * 10222svn_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*/ 10265typedef 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 10275static svn_error_t * 10276cache_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 10304svn_error_t * 10305svn_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 */ 10396svn_error_t * 10397svn_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 10442svn_error_t * 10443svn_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 10468static svn_error_t * 10469db_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 10537svn_error_t * 10538svn_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 10558svn_error_t * 10559svn_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(). */ 10618static svn_error_t * 10619db_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 10649svn_error_t * 10650svn_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. */ 10681static void 10682filter_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 */ 10701static svn_error_t * 10702db_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(). */ 10725static svn_error_t * 10726db_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 10913svn_error_t * 10914svn_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 */ 10942static svn_error_t * 10943get_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 11051svn_error_t * 11052svn_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 11077svn_error_t * 11078svn_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 11100svn_error_t * 11101svn_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 */ 11126static svn_error_t * 11127check_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 11254svn_error_t * 11255svn_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 11290svn_error_t * 11291svn_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 */ 11314static svn_error_t * 11315relocate_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 11432svn_error_t * 11433svn_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. */ 11464static svn_error_t * 11465determine_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 11541static svn_error_t * 11542moved_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 */ 11595static svn_error_t * 11596moved_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 */ 11677static svn_error_t * 11678descendant_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 */ 11708static svn_error_t * 11709commit_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 12020svn_error_t * 12021svn_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 12058svn_error_t * 12059svn_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 */ 12124static svn_error_t * 12125db_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 */ 12194static svn_error_t * 12195bump_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 */ 12329static svn_error_t * 12330bump_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 12403svn_error_t * 12404svn_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 */ 12445static svn_error_t * 12446lock_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 12481svn_error_t * 12482svn_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 */ 12510static svn_error_t * 12511lock_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 12539svn_error_t * 12540svn_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. */ 12575static svn_error_t * 12576get_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. */ 12667static svn_error_t * 12668scan_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 12944svn_error_t * 12945svn_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 12965svn_error_t * 12966svn_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 13023svn_error_t * 13024svn_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 */ 13101static svn_error_t * 13102follow_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 13193svn_error_t * 13194svn_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 13222svn_error_t * 13223svn_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. */ 13306svn_error_t * 13307svn_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 13370svn_error_t * 13371svn_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 13404svn_error_t * 13405svn_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 13478svn_error_t * 13479svn_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 13488svn_error_t * 13489svn_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 */ 13514static svn_error_t * 13515wq_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 13559svn_error_t * 13560svn_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 */ 13589static svn_error_t * 13590wq_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 13619svn_error_t * 13620svn_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. */ 13654svn_error_t * 13655svn_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. */ 13695svn_wc_adm_access_t * 13696svn_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. */ 13727void 13728svn_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. */ 13758svn_error_t * 13759svn_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. */ 13779void 13780svn_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 13803apr_hash_t * 13804svn_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 13825svn_error_t * 13826svn_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 13846svn_error_t * 13847svn_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 */ 13893static svn_error_t * 13894get_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 13975svn_error_t * 13976svn_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 13999svn_error_t * 14000svn_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 14022svn_error_t * 14023svn_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 14134svn_error_t * 14135svn_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 14235svn_error_t * 14236svn_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 */ 14265static svn_error_t * 14266db_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 14334svn_error_t * 14335svn_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 14378svn_error_t * 14379svn_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 */ 14405static svn_error_t * 14406wclock_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 */ 14423static svn_error_t * 14424wclock_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 14591svn_error_t * 14592svn_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(). */ 14643static svn_error_t * 14644find_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 14705static svn_error_t * 14706is_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 14720svn_error_t* 14721svn_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 14749svn_error_t * 14750svn_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 14770svn_error_t * 14771svn_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. */ 14827svn_error_t * 14828svn_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 14878svn_error_t * 14879svn_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 */ 14907static svn_error_t * 14908end_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 14935svn_error_t * 14936svn_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 */ 14962static svn_error_t * 14963start_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 14988svn_error_t * 14989svn_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 */ 15017static svn_error_t * 15018db_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 15050static svn_error_t * 15051db_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 */ 15131static svn_error_t * 15132make_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 15273svn_error_t * 15274svn_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 15357svn_error_t * 15358svn_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 15385svn_error_t * 15386svn_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 15407svn_error_t * 15408svn_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. */ 15462static svn_error_t * 15463get_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 15501svn_error_t * 15502svn_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. */ 15527static svn_error_t * 15528is_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. */ 15552static svn_error_t * 15553has_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 15613svn_error_t * 15614svn_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 15635svn_error_t * 15636svn_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... */ 15682static svn_error_t * 15683has_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 15713svn_error_t * 15714svn_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 */ 15736static svn_error_t * 15737revision_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 15792svn_error_t * 15793svn_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 15825svn_error_t * 15826svn_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 */ 15913static svn_error_t * 15914verify_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 15970svn_error_t * 15971svn_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 15988svn_error_t * 15989svn_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 16029svn_error_t * 16030svn_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 16050svn_error_t * 16051svn_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 16115svn_error_t * 16116svn_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 */ 16132typedef 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 */ 16149struct 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 */ 16158svn_error_t * 16159svn_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 16188svn_error_t * 16189svn_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 */ 16234static svn_error_t * 16235process_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 */ 16377static svn_error_t * 16378process_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*/ 16484static svn_boolean_t 16485have_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 */ 16511static int 16512compare_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 */ 16524static svn_error_t * 16525db_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 16616svn_error_t * 16617svn_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