Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / libsvn_fs_base / revs-txns.c
blobd5e95b87f08ba32b500354b40106156fcc4ff4d7
1 /* revs-txns.c : operations on revision and transactions
3 * ====================================================================
4 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
18 #include <assert.h>
19 #include <string.h>
21 #include <apr_tables.h>
22 #include <apr_pools.h>
24 #include "svn_pools.h"
25 #include "svn_time.h"
26 #include "svn_fs.h"
27 #include "svn_props.h"
28 #include "svn_hash.h"
29 #include "svn_io.h"
31 #include "fs.h"
32 #include "dag.h"
33 #include "err.h"
34 #include "trail.h"
35 #include "tree.h"
36 #include "revs-txns.h"
37 #include "key-gen.h"
38 #include "id.h"
39 #include "bdb/rev-table.h"
40 #include "bdb/txn-table.h"
41 #include "bdb/copies-table.h"
42 #include "bdb/changes-table.h"
43 #include "../libsvn_fs/fs-loader.h"
45 #include "svn_private_config.h"
46 #include "private/svn_fs_util.h"
49 /*** Helpers ***/
51 /* Set *txn_p to a transaction object allocated in POOL for the
52 transaction in FS whose id is TXN_ID. If EXPECT_DEAD is set, this
53 transaction must be a dead one, else an error is returned. If
54 EXPECT_DEAD is not set, an error is thrown if the transaction is
55 *not* dead. */
56 static svn_error_t *
57 get_txn(transaction_t **txn_p,
58 svn_fs_t *fs,
59 const char *txn_id,
60 svn_boolean_t expect_dead,
61 trail_t *trail,
62 apr_pool_t *pool)
64 transaction_t *txn;
65 SVN_ERR(svn_fs_bdb__get_txn(&txn, fs, txn_id, trail, pool));
66 if (expect_dead && (txn->kind != transaction_kind_dead))
67 return svn_error_createf(SVN_ERR_FS_TRANSACTION_NOT_DEAD, 0,
68 _("Transaction is not dead: '%s'"), txn_id);
69 if ((! expect_dead) && (txn->kind == transaction_kind_dead))
70 return svn_error_createf(SVN_ERR_FS_TRANSACTION_DEAD, 0,
71 _("Transaction is dead: '%s'"), txn_id);
72 *txn_p = txn;
73 return SVN_NO_ERROR;
77 /* This is only for symmetry with the get_txn() helper. */
78 #define put_txn svn_fs_bdb__put_txn
82 /*** Revisions ***/
84 /* Return the committed transaction record *TXN_P and its ID *TXN_ID
85 (as long as those parameters aren't NULL) for the revision REV in
86 FS as part of TRAIL. */
87 static svn_error_t *
88 get_rev_txn(transaction_t **txn_p,
89 const char **txn_id,
90 svn_fs_t *fs,
91 svn_revnum_t rev,
92 trail_t *trail,
93 apr_pool_t *pool)
95 revision_t *revision;
96 transaction_t *txn;
98 SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool));
99 if (revision->txn_id == NULL)
100 return svn_fs_base__err_corrupt_fs_revision(fs, rev);
102 SVN_ERR(get_txn(&txn, fs, revision->txn_id, FALSE, trail, pool));
103 if (txn->revision != rev)
104 return svn_fs_base__err_corrupt_txn(fs, revision->txn_id);
106 if (txn_p)
107 *txn_p = txn;
108 if (txn_id)
109 *txn_id = revision->txn_id;
110 return SVN_NO_ERROR;
114 svn_error_t *
115 svn_fs_base__rev_get_root(const svn_fs_id_t **root_id_p,
116 svn_fs_t *fs,
117 svn_revnum_t rev,
118 trail_t *trail,
119 apr_pool_t *pool)
121 transaction_t *txn;
123 SVN_ERR(get_rev_txn(&txn, NULL, fs, rev, trail, pool));
124 if (txn->root_id == NULL)
125 return svn_fs_base__err_corrupt_fs_revision(fs, rev);
127 *root_id_p = txn->root_id;
128 return SVN_NO_ERROR;
132 svn_error_t *
133 svn_fs_base__rev_get_txn_id(const char **txn_id_p,
134 svn_fs_t *fs,
135 svn_revnum_t rev,
136 trail_t *trail,
137 apr_pool_t *pool)
139 revision_t *revision;
141 SVN_ERR(svn_fs_bdb__get_rev(&revision, fs, rev, trail, pool));
142 if (revision->txn_id == NULL)
143 return svn_fs_base__err_corrupt_fs_revision(fs, rev);
145 *txn_id_p = revision->txn_id;
146 return SVN_NO_ERROR;
150 static svn_error_t *
151 txn_body_youngest_rev(void *baton, trail_t *trail)
153 return svn_fs_bdb__youngest_rev(baton, trail->fs, trail, trail->pool);
157 svn_error_t *
158 svn_fs_base__youngest_rev(svn_revnum_t *youngest_p,
159 svn_fs_t *fs,
160 apr_pool_t *pool)
162 svn_revnum_t youngest;
163 SVN_ERR(svn_fs__check_fs(fs, TRUE));
164 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_youngest_rev, &youngest,
165 pool));
166 *youngest_p = youngest;
167 return SVN_NO_ERROR;
171 struct revision_proplist_args {
172 apr_hash_t **table_p;
173 svn_revnum_t rev;
177 static svn_error_t *
178 txn_body_revision_proplist(void *baton, trail_t *trail)
180 struct revision_proplist_args *args = baton;
181 transaction_t *txn;
183 SVN_ERR(get_rev_txn(&txn, NULL, trail->fs, args->rev, trail, trail->pool));
184 *(args->table_p) = txn->proplist;
185 return SVN_NO_ERROR;
189 svn_error_t *
190 svn_fs_base__revision_proplist(apr_hash_t **table_p,
191 svn_fs_t *fs,
192 svn_revnum_t rev,
193 apr_pool_t *pool)
195 struct revision_proplist_args args;
196 apr_hash_t *table;
198 SVN_ERR(svn_fs__check_fs(fs, TRUE));
200 args.table_p = &table;
201 args.rev = rev;
202 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args,
203 pool));
205 *table_p = table ? table : apr_hash_make(pool);
206 return SVN_NO_ERROR;
210 svn_error_t *
211 svn_fs_base__revision_prop(svn_string_t **value_p,
212 svn_fs_t *fs,
213 svn_revnum_t rev,
214 const char *propname,
215 apr_pool_t *pool)
217 struct revision_proplist_args args;
218 apr_hash_t *table;
220 SVN_ERR(svn_fs__check_fs(fs, TRUE));
222 /* Get the proplist. */
223 args.table_p = &table;
224 args.rev = rev;
225 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_proplist, &args,
226 pool));
228 /* And then the prop from that list (if there was a list). */
229 *value_p = NULL;
230 if (table)
231 *value_p = apr_hash_get(table, propname, APR_HASH_KEY_STRING);
233 return SVN_NO_ERROR;
237 svn_error_t *
238 svn_fs_base__set_rev_prop(svn_fs_t *fs,
239 svn_revnum_t rev,
240 const char *name,
241 const svn_string_t *value,
242 trail_t *trail,
243 apr_pool_t *pool)
245 transaction_t *txn;
246 const char *txn_id;
248 SVN_ERR(get_rev_txn(&txn, &txn_id, fs, rev, trail, pool));
250 /* If there's no proplist, but we're just deleting a property, exit now. */
251 if ((! txn->proplist) && (! value))
252 return SVN_NO_ERROR;
254 /* Now, if there's no proplist, we know we need to make one. */
255 if (! txn->proplist)
256 txn->proplist = apr_hash_make(pool);
258 /* Set the property. */
259 apr_hash_set(txn->proplist, name, APR_HASH_KEY_STRING, value);
261 /* Overwrite the revision. */
262 return put_txn(fs, txn, txn_id, trail, pool);
266 struct change_rev_prop_args {
267 svn_revnum_t rev;
268 const char *name;
269 const svn_string_t *value;
273 static svn_error_t *
274 txn_body_change_rev_prop(void *baton, trail_t *trail)
276 struct change_rev_prop_args *args = baton;
278 SVN_ERR(svn_fs_base__set_rev_prop(trail->fs, args->rev,
279 args->name, args->value,
280 trail, trail->pool));
282 return SVN_NO_ERROR;
286 svn_error_t *
287 svn_fs_base__change_rev_prop(svn_fs_t *fs,
288 svn_revnum_t rev,
289 const char *name,
290 const svn_string_t *value,
291 apr_pool_t *pool)
293 struct change_rev_prop_args args;
295 SVN_ERR(svn_fs__check_fs(fs, TRUE));
297 args.rev = rev;
298 args.name = name;
299 args.value = value;
300 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_change_rev_prop, &args, pool));
302 return SVN_NO_ERROR;
307 /*** Transactions ***/
309 svn_error_t *
310 svn_fs_base__txn_make_committed(svn_fs_t *fs,
311 const char *txn_name,
312 svn_revnum_t revision,
313 trail_t *trail,
314 apr_pool_t *pool)
316 transaction_t *txn;
318 /* Don't you dare call this with an invalid REVISION. */
319 assert(SVN_IS_VALID_REVNUM(revision));
321 /* Make sure the TXN is not committed already. */
322 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
323 if (txn->kind != transaction_kind_normal)
324 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
326 /* Convert TXN to a committed transaction. */
327 txn->base_id = NULL;
328 txn->revision = revision;
329 txn->kind = transaction_kind_committed;
330 return put_txn(fs, txn, txn_name, trail, pool);
334 svn_error_t *
335 svn_fs_base__txn_get_revision(svn_revnum_t *revision,
336 svn_fs_t *fs,
337 const char *txn_name,
338 trail_t *trail,
339 apr_pool_t *pool)
341 transaction_t *txn;
342 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
343 *revision = txn->revision;
344 return SVN_NO_ERROR;
348 svn_error_t *
349 svn_fs_base__get_txn_ids(const svn_fs_id_t **root_id_p,
350 const svn_fs_id_t **base_root_id_p,
351 svn_fs_t *fs,
352 const char *txn_name,
353 trail_t *trail,
354 apr_pool_t *pool)
356 transaction_t *txn;
358 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
359 if (txn->kind != transaction_kind_normal)
360 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
362 *root_id_p = txn->root_id;
363 *base_root_id_p = txn->base_id;
364 return SVN_NO_ERROR;
368 svn_error_t *
369 svn_fs_base__set_txn_root(svn_fs_t *fs,
370 const char *txn_name,
371 const svn_fs_id_t *new_id,
372 trail_t *trail,
373 apr_pool_t *pool)
375 transaction_t *txn;
377 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
378 if (txn->kind != transaction_kind_normal)
379 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
381 if (! svn_fs_base__id_eq(txn->root_id, new_id))
383 txn->root_id = new_id;
384 SVN_ERR(put_txn(fs, txn, txn_name, trail, pool));
386 return SVN_NO_ERROR;
390 svn_error_t *
391 svn_fs_base__set_txn_base(svn_fs_t *fs,
392 const char *txn_name,
393 const svn_fs_id_t *new_id,
394 trail_t *trail,
395 apr_pool_t *pool)
397 transaction_t *txn;
399 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
400 if (txn->kind != transaction_kind_normal)
401 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
403 if (! svn_fs_base__id_eq(txn->base_id, new_id))
405 txn->base_id = new_id;
406 SVN_ERR(put_txn(fs, txn, txn_name, trail, pool));
408 return SVN_NO_ERROR;
412 svn_error_t *
413 svn_fs_base__add_txn_copy(svn_fs_t *fs,
414 const char *txn_name,
415 const char *copy_id,
416 trail_t *trail,
417 apr_pool_t *pool)
419 transaction_t *txn;
421 /* Get the transaction and ensure its mutability. */
422 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
423 if (txn->kind != transaction_kind_normal)
424 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
426 /* Allocate a new array if this transaction has no copies. */
427 if (! txn->copies)
428 txn->copies = apr_array_make(pool, 1, sizeof(copy_id));
430 /* Add COPY_ID to the array. */
431 APR_ARRAY_PUSH(txn->copies, const char *) = copy_id;
433 /* Finally, write out the transaction. */
434 return put_txn(fs, txn, txn_name, trail, pool);
439 /* Generic transaction operations. */
441 struct txn_proplist_args {
442 apr_hash_t **table_p;
443 const char *id;
447 static svn_error_t *
448 txn_body_txn_proplist(void *baton, trail_t *trail)
450 transaction_t *txn;
451 struct txn_proplist_args *args = baton;
453 SVN_ERR(get_txn(&txn, trail->fs, args->id, FALSE, trail, trail->pool));
454 if (txn->kind != transaction_kind_normal)
455 return svn_fs_base__err_txn_not_mutable(trail->fs, args->id);
457 *(args->table_p) = txn->proplist;
458 return SVN_NO_ERROR;
463 svn_error_t *
464 svn_fs_base__txn_proplist_in_trail(apr_hash_t **table_p,
465 const char *txn_id,
466 trail_t *trail)
468 struct txn_proplist_args args;
469 apr_hash_t *table;
471 args.table_p = &table;
472 args.id = txn_id;
473 SVN_ERR(txn_body_txn_proplist(&args, trail));
475 *table_p = table ? table : apr_hash_make(trail->pool);
476 return SVN_NO_ERROR;
481 svn_error_t *
482 svn_fs_base__txn_proplist(apr_hash_t **table_p,
483 svn_fs_txn_t *txn,
484 apr_pool_t *pool)
486 struct txn_proplist_args args;
487 apr_hash_t *table;
488 svn_fs_t *fs = txn->fs;
490 SVN_ERR(svn_fs__check_fs(fs, TRUE));
492 args.table_p = &table;
493 args.id = txn->id;
494 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args, pool));
496 *table_p = table ? table : apr_hash_make(pool);
497 return SVN_NO_ERROR;
501 svn_error_t *
502 svn_fs_base__txn_prop(svn_string_t **value_p,
503 svn_fs_txn_t *txn,
504 const char *propname,
505 apr_pool_t *pool)
507 struct txn_proplist_args args;
508 apr_hash_t *table;
509 svn_fs_t *fs = txn->fs;
511 SVN_ERR(svn_fs__check_fs(fs, TRUE));
513 /* Get the proplist. */
514 args.table_p = &table;
515 args.id = txn->id;
516 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_proplist, &args, pool));
518 /* And then the prop from that list (if there was a list). */
519 *value_p = NULL;
520 if (table)
521 *value_p = apr_hash_get(table, propname, APR_HASH_KEY_STRING);
522 return SVN_NO_ERROR;
527 struct change_txn_prop_args {
528 svn_fs_t *fs;
529 const char *id;
530 const char *name;
531 const svn_string_t *value;
535 svn_error_t *
536 svn_fs_base__set_txn_prop(svn_fs_t *fs,
537 const char *txn_name,
538 const char *name,
539 const svn_string_t *value,
540 trail_t *trail,
541 apr_pool_t *pool)
543 transaction_t *txn;
545 SVN_ERR(get_txn(&txn, fs, txn_name, FALSE, trail, pool));
546 if (txn->kind != transaction_kind_normal)
547 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
549 /* If there's no proplist, but we're just deleting a property, exit now. */
550 if ((! txn->proplist) && (! value))
551 return SVN_NO_ERROR;
553 /* Now, if there's no proplist, we know we need to make one. */
554 if (! txn->proplist)
555 txn->proplist = apr_hash_make(pool);
557 /* Set the property. */
558 apr_hash_set(txn->proplist, name, APR_HASH_KEY_STRING, value);
560 /* Now overwrite the transaction. */
561 return put_txn(fs, txn, txn_name, trail, pool);
565 static svn_error_t *
566 txn_body_change_txn_prop(void *baton, trail_t *trail)
568 struct change_txn_prop_args *args = baton;
569 return svn_fs_base__set_txn_prop(trail->fs, args->id, args->name,
570 args->value, trail, trail->pool);
574 svn_error_t *
575 svn_fs_base__change_txn_prop(svn_fs_txn_t *txn,
576 const char *name,
577 const svn_string_t *value,
578 apr_pool_t *pool)
580 struct change_txn_prop_args args;
581 svn_fs_t *fs = txn->fs;
583 SVN_ERR(svn_fs__check_fs(fs, TRUE));
585 args.id = txn->id;
586 args.name = name;
587 args.value = value;
588 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_change_txn_prop, &args, pool));
590 return SVN_NO_ERROR;
594 svn_error_t *
595 svn_fs_base__change_txn_props(svn_fs_txn_t *txn,
596 apr_array_header_t *props,
597 apr_pool_t *pool)
599 apr_pool_t *iterpool = svn_pool_create(pool);
600 int i;
602 for (i = 0; i < props->nelts; i++)
604 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
606 svn_pool_clear(iterpool);
608 SVN_ERR(svn_fs_base__change_txn_prop(txn, prop->name,
609 prop->value, iterpool));
611 svn_pool_destroy(iterpool);
613 return SVN_NO_ERROR;
617 /* Creating a transaction */
619 static txn_vtable_t txn_vtable = {
620 svn_fs_base__commit_txn,
621 svn_fs_base__abort_txn,
622 svn_fs_base__txn_prop,
623 svn_fs_base__txn_proplist,
624 svn_fs_base__change_txn_prop,
625 svn_fs_base__txn_root,
626 svn_fs_base__change_txn_props
630 /* Allocate and return a new transaction object in POOL for FS whose
631 transaction ID is ID. ID is not copied. */
632 static svn_fs_txn_t *
633 make_txn(svn_fs_t *fs,
634 const char *id,
635 svn_revnum_t base_rev,
636 apr_pool_t *pool)
638 svn_fs_txn_t *txn = apr_pcalloc(pool, sizeof(*txn));
640 txn->fs = fs;
641 txn->id = id;
642 txn->base_rev = base_rev;
643 txn->vtable = &txn_vtable;
644 txn->fsap_data = NULL;
646 return txn;
650 struct begin_txn_args
652 svn_fs_txn_t **txn_p;
653 svn_revnum_t rev;
654 apr_uint32_t flags;
658 static svn_error_t *
659 txn_body_begin_txn(void *baton, trail_t *trail)
661 struct begin_txn_args *args = baton;
662 const svn_fs_id_t *root_id;
663 const char *txn_id;
665 SVN_ERR(svn_fs_base__rev_get_root(&root_id, trail->fs, args->rev,
666 trail, trail->pool));
667 SVN_ERR(svn_fs_bdb__create_txn(&txn_id, trail->fs, root_id,
668 trail, trail->pool));
670 if (args->flags & SVN_FS_TXN_CHECK_OOD)
672 struct change_txn_prop_args cpargs;
673 cpargs.fs = trail->fs;
674 cpargs.id = txn_id;
675 cpargs.name = SVN_FS__PROP_TXN_CHECK_OOD;
676 cpargs.value = svn_string_create("true", trail->pool);
678 SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
681 if (args->flags & SVN_FS_TXN_CHECK_LOCKS)
683 struct change_txn_prop_args cpargs;
684 cpargs.fs = trail->fs;
685 cpargs.id = txn_id;
686 cpargs.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
687 cpargs.value = svn_string_create("true", trail->pool);
689 SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
692 *args->txn_p = make_txn(trail->fs, txn_id, args->rev, trail->pool);
693 return SVN_NO_ERROR;
699 /* Note: it is acceptable for this function to call back into
700 public FS API interfaces because it does not itself use trails. */
701 svn_error_t *
702 svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,
703 svn_fs_t *fs,
704 svn_revnum_t rev,
705 apr_uint32_t flags,
706 apr_pool_t *pool)
708 svn_fs_txn_t *txn;
709 struct begin_txn_args args;
710 svn_string_t date;
712 SVN_ERR(svn_fs__check_fs(fs, TRUE));
714 args.txn_p = &txn;
715 args.rev = rev;
716 args.flags = flags;
717 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_begin_txn, &args, pool));
719 *txn_p = txn;
721 /* Put a datestamp on the newly created txn, so we always know
722 exactly how old it is. (This will help sysadmins identify
723 long-abandoned txns that may need to be manually removed.) When
724 a txn is promoted to a revision, this property will be
725 automatically overwritten with a revision datestamp. */
726 date.data = svn_time_to_cstring(apr_time_now(), pool);
727 date.len = strlen(date.data);
728 SVN_ERR(svn_fs_base__change_txn_prop(txn, SVN_PROP_REVISION_DATE,
729 &date, pool));
731 return SVN_NO_ERROR;
735 struct open_txn_args
737 svn_fs_txn_t **txn_p;
738 const char *name;
742 static svn_error_t *
743 txn_body_open_txn(void *baton, trail_t *trail)
745 struct open_txn_args *args = baton;
746 transaction_t *fstxn;
747 svn_revnum_t base_rev = SVN_INVALID_REVNUM;
748 const char *txn_id;
750 SVN_ERR(get_txn(&fstxn, trail->fs, args->name, FALSE, trail, trail->pool));
751 if (fstxn->kind != transaction_kind_committed)
753 txn_id = svn_fs_base__id_txn_id(fstxn->base_id);
754 SVN_ERR(svn_fs_base__txn_get_revision(&base_rev, trail->fs, txn_id,
755 trail, trail->pool));
758 *args->txn_p = make_txn(trail->fs, args->name, base_rev, trail->pool);
759 return SVN_NO_ERROR;
763 svn_error_t *
764 svn_fs_base__open_txn(svn_fs_txn_t **txn_p,
765 svn_fs_t *fs,
766 const char *name,
767 apr_pool_t *pool)
769 svn_fs_txn_t *txn;
770 struct open_txn_args args;
772 SVN_ERR(svn_fs__check_fs(fs, TRUE));
774 args.txn_p = &txn;
775 args.name = name;
776 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_open_txn, &args, pool));
778 *txn_p = txn;
779 return SVN_NO_ERROR;
783 struct cleanup_txn_args
785 transaction_t **txn_p;
786 const char *name;
790 static svn_error_t *
791 txn_body_cleanup_txn(void *baton, trail_t *trail)
793 struct cleanup_txn_args *args = baton;
794 return get_txn(args->txn_p, trail->fs, args->name, TRUE,
795 trail, trail->pool);
799 static svn_error_t *
800 txn_body_cleanup_txn_copy(void *baton, trail_t *trail)
802 svn_error_t *err = svn_fs_bdb__delete_copy(trail->fs, baton, trail,
803 trail->pool);
805 /* Copy doesn't exist? No sweat. */
806 if (err && (err->apr_err == SVN_ERR_FS_NO_SUCH_COPY))
808 svn_error_clear(err);
809 err = SVN_NO_ERROR;
811 return err;
815 static svn_error_t *
816 txn_body_cleanup_txn_changes(void *baton, trail_t *trail)
818 return svn_fs_bdb__changes_delete(trail->fs, baton, trail, trail->pool);
822 struct get_dirents_args
824 apr_hash_t **dirents;
825 const svn_fs_id_t *id;
826 const char *txn_id;
830 static svn_error_t *
831 txn_body_get_dirents(void *baton, trail_t *trail)
833 struct get_dirents_args *args = baton;
834 dag_node_t *node;
836 /* Get the node. */
837 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
838 trail, trail->pool));
840 /* If immutable, do nothing and return. */
841 if (! svn_fs_base__dag_check_mutable(node, args->txn_id))
842 return SVN_NO_ERROR;
844 /* If a directory, do nothing and return. */
845 *(args->dirents) = NULL;
846 if (svn_fs_base__dag_node_kind(node) != svn_node_dir)
847 return SVN_NO_ERROR;
849 /* Else it's mutable. Get its dirents. */
850 return svn_fs_base__dag_dir_entries(args->dirents, node,
851 trail, trail->pool);
855 struct remove_node_args
857 const svn_fs_id_t *id;
858 const char *txn_id;
862 static svn_error_t *
863 txn_body_remove_node(void *baton, trail_t *trail)
865 struct remove_node_args *args = baton;
866 return svn_fs_base__dag_remove_node(trail->fs, args->id, args->txn_id,
867 trail, trail->pool);
871 static svn_error_t *
872 delete_txn_tree(svn_fs_t *fs,
873 const svn_fs_id_t *id,
874 const char *txn_id,
875 apr_pool_t *pool)
877 struct get_dirents_args dirent_args;
878 struct remove_node_args rm_args;
879 apr_hash_t *dirents = NULL;
880 apr_hash_index_t *hi;
881 svn_error_t *err;
883 /* If this sucker isn't mutable, there's nothing to do. */
884 if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(id), txn_id) != 0)
885 return SVN_NO_ERROR;
887 /* See if the thing has dirents that need to be recursed upon. If
888 you can't find the thing itself, don't sweat it. We probably
889 already cleaned it up. */
890 dirent_args.dirents = &dirents;
891 dirent_args.id = id;
892 dirent_args.txn_id = txn_id;
893 err = svn_fs_base__retry_txn(fs, txn_body_get_dirents, &dirent_args, pool);
894 if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND))
896 svn_error_clear(err);
897 return SVN_NO_ERROR;
899 SVN_ERR(err);
901 /* If there are dirents upon which to recurse ... recurse. */
902 if (dirents)
904 apr_pool_t *subpool = svn_pool_create(pool);
906 /* Loop over hash entries */
907 for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
909 void *val;
910 svn_fs_dirent_t *dirent;
912 svn_pool_clear(subpool);
913 apr_hash_this(hi, NULL, NULL, &val);
914 dirent = val;
915 SVN_ERR(delete_txn_tree(fs, dirent->id, txn_id, subpool));
917 svn_pool_destroy(subpool);
920 /* Remove the node. */
921 rm_args.id = id;
922 rm_args.txn_id = txn_id;
923 return svn_fs_base__retry_txn(fs, txn_body_remove_node, &rm_args, pool);
927 static svn_error_t *
928 txn_body_delete_txn(void *baton, trail_t *trail)
930 return svn_fs_bdb__delete_txn(trail->fs, baton, trail, trail->pool);
934 svn_error_t *
935 svn_fs_base__purge_txn(svn_fs_t *fs,
936 const char *txn_id,
937 apr_pool_t *pool)
939 struct cleanup_txn_args args;
940 transaction_t *txn;
941 int i;
943 SVN_ERR(svn_fs__check_fs(fs, TRUE));
945 /* Open the transaction, expecting it to be dead. */
946 args.txn_p = &txn;
947 args.name = txn_id;
948 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn, &args, pool));
950 /* Delete the mutable portion of the tree hanging from the
951 transaction (which should gracefully recover if we've already
952 done this). */
953 SVN_ERR(delete_txn_tree(fs, txn->root_id, txn_id, pool));
955 /* Kill the transaction's changes (which should gracefully recover
956 if...). */
957 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_cleanup_txn_changes,
958 (void *)txn_id, pool));
960 /* Kill the transaction's copies (which should gracefully...). */
961 if (txn->copies)
963 for (i = 0; i < txn->copies->nelts; i++)
965 SVN_ERR(svn_fs_base__retry_txn
966 (fs, txn_body_cleanup_txn_copy,
967 (void *)APR_ARRAY_IDX(txn->copies, i, const char *),
968 pool));
972 /* Kill the transaction itself (which ... just kidding -- this has
973 no graceful failure mode). */
974 return svn_fs_base__retry_txn(fs, txn_body_delete_txn, (void *)txn_id,
975 pool);
979 static svn_error_t *
980 txn_body_abort_txn(void *baton, trail_t *trail)
982 svn_fs_txn_t *txn = baton;
983 transaction_t *fstxn;
985 /* Get the transaction by its id, set it to "dead", and store the
986 transaction. */
987 SVN_ERR(get_txn(&fstxn, txn->fs, txn->id, FALSE, trail, trail->pool));
988 if (fstxn->kind != transaction_kind_normal)
989 return svn_fs_base__err_txn_not_mutable(txn->fs, txn->id);
991 fstxn->kind = transaction_kind_dead;
992 return put_txn(txn->fs, fstxn, txn->id, trail, trail->pool);
996 svn_error_t *
997 svn_fs_base__abort_txn(svn_fs_txn_t *txn,
998 apr_pool_t *pool)
1000 SVN_ERR(svn_fs__check_fs(txn->fs, TRUE));
1002 /* Set the transaction to "dead". */
1003 SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_abort_txn, txn, pool));
1005 /* Now, purge it. */
1006 SVN_ERR_W(svn_fs_base__purge_txn(txn->fs, txn->id, pool),
1007 _("Transaction aborted, but cleanup failed"));
1009 return SVN_NO_ERROR;
1013 struct list_transactions_args
1015 apr_array_header_t **names_p;
1016 apr_pool_t *pool;
1019 static svn_error_t *
1020 txn_body_list_transactions(void* baton, trail_t *trail)
1022 struct list_transactions_args *args = baton;
1023 return svn_fs_bdb__get_txn_list(args->names_p, trail->fs,
1024 trail, args->pool);
1027 svn_error_t *
1028 svn_fs_base__list_transactions(apr_array_header_t **names_p,
1029 svn_fs_t *fs,
1030 apr_pool_t *pool)
1032 apr_array_header_t *names;
1033 struct list_transactions_args args;
1035 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1037 args.names_p = &names;
1038 args.pool = pool;
1039 SVN_ERR(svn_fs_base__retry(fs, txn_body_list_transactions, &args, pool));
1041 *names_p = names;
1042 return SVN_NO_ERROR;