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