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