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