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