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