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 * ====================================================================
21 #include <apr_tables.h>
22 #include <apr_pools.h>
24 #include "svn_pools.h"
27 #include "svn_props.h"
36 #include "revs-txns.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"
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
57 get_txn(transaction_t
**txn_p
,
60 svn_boolean_t expect_dead
,
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
);
77 /* This is only for symmetry with the get_txn() helper. */
78 #define put_txn svn_fs_bdb__put_txn
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. */
88 get_rev_txn(transaction_t
**txn_p
,
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
);
109 *txn_id
= revision
->txn_id
;
115 svn_fs_base__rev_get_root(const svn_fs_id_t
**root_id_p
,
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
;
133 svn_fs_base__rev_get_txn_id(const char **txn_id_p
,
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
;
151 txn_body_youngest_rev(void *baton
, trail_t
*trail
)
153 return svn_fs_bdb__youngest_rev(baton
, trail
->fs
, trail
, trail
->pool
);
158 svn_fs_base__youngest_rev(svn_revnum_t
*youngest_p
,
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
,
166 *youngest_p
= youngest
;
171 struct revision_proplist_args
{
172 apr_hash_t
**table_p
;
178 txn_body_revision_proplist(void *baton
, trail_t
*trail
)
180 struct revision_proplist_args
*args
= baton
;
183 SVN_ERR(get_rev_txn(&txn
, NULL
, trail
->fs
, args
->rev
, trail
, trail
->pool
));
184 *(args
->table_p
) = txn
->proplist
;
190 svn_fs_base__revision_proplist(apr_hash_t
**table_p
,
195 struct revision_proplist_args args
;
198 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
200 args
.table_p
= &table
;
202 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_revision_proplist
, &args
,
205 *table_p
= table
? table
: apr_hash_make(pool
);
211 svn_fs_base__revision_prop(svn_string_t
**value_p
,
214 const char *propname
,
217 struct revision_proplist_args args
;
220 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
222 /* Get the proplist. */
223 args
.table_p
= &table
;
225 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_revision_proplist
, &args
,
228 /* And then the prop from that list (if there was a list). */
231 *value_p
= apr_hash_get(table
, propname
, APR_HASH_KEY_STRING
);
238 svn_fs_base__set_rev_prop(svn_fs_t
*fs
,
241 const svn_string_t
*value
,
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
))
254 /* Now, if there's no proplist, we know we need to make one. */
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
{
269 const svn_string_t
*value
;
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
));
287 svn_fs_base__change_rev_prop(svn_fs_t
*fs
,
290 const svn_string_t
*value
,
293 struct change_rev_prop_args args
;
295 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
300 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_change_rev_prop
, &args
, pool
));
307 /*** Transactions ***/
310 svn_fs_base__txn_make_committed(svn_fs_t
*fs
,
311 const char *txn_name
,
312 svn_revnum_t revision
,
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. */
328 txn
->revision
= revision
;
329 txn
->kind
= transaction_kind_committed
;
330 return put_txn(fs
, txn
, txn_name
, trail
, pool
);
335 svn_fs_base__txn_get_revision(svn_revnum_t
*revision
,
337 const char *txn_name
,
342 SVN_ERR(get_txn(&txn
, fs
, txn_name
, FALSE
, trail
, pool
));
343 *revision
= txn
->revision
;
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
,
352 const char *txn_name
,
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
;
369 svn_fs_base__set_txn_root(svn_fs_t
*fs
,
370 const char *txn_name
,
371 const svn_fs_id_t
*new_id
,
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
));
391 svn_fs_base__set_txn_base(svn_fs_t
*fs
,
392 const char *txn_name
,
393 const svn_fs_id_t
*new_id
,
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
));
413 svn_fs_base__add_txn_copy(svn_fs_t
*fs
,
414 const char *txn_name
,
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. */
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
;
448 txn_body_txn_proplist(void *baton
, trail_t
*trail
)
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
;
464 svn_fs_base__txn_proplist_in_trail(apr_hash_t
**table_p
,
468 struct txn_proplist_args args
;
471 args
.table_p
= &table
;
473 SVN_ERR(txn_body_txn_proplist(&args
, trail
));
475 *table_p
= table
? table
: apr_hash_make(trail
->pool
);
482 svn_fs_base__txn_proplist(apr_hash_t
**table_p
,
486 struct txn_proplist_args args
;
488 svn_fs_t
*fs
= txn
->fs
;
490 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
492 args
.table_p
= &table
;
494 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_txn_proplist
, &args
, pool
));
496 *table_p
= table
? table
: apr_hash_make(pool
);
502 svn_fs_base__txn_prop(svn_string_t
**value_p
,
504 const char *propname
,
507 struct txn_proplist_args args
;
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
;
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). */
521 *value_p
= apr_hash_get(table
, propname
, APR_HASH_KEY_STRING
);
527 struct change_txn_prop_args
{
531 const svn_string_t
*value
;
536 svn_fs_base__set_txn_prop(svn_fs_t
*fs
,
537 const char *txn_name
,
539 const svn_string_t
*value
,
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
))
553 /* Now, if there's no proplist, we know we need to make one. */
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
);
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
);
575 svn_fs_base__change_txn_prop(svn_fs_txn_t
*txn
,
577 const svn_string_t
*value
,
580 struct change_txn_prop_args args
;
581 svn_fs_t
*fs
= txn
->fs
;
583 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
588 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_change_txn_prop
, &args
, pool
));
595 svn_fs_base__change_txn_props(svn_fs_txn_t
*txn
,
596 apr_array_header_t
*props
,
599 apr_pool_t
*iterpool
= svn_pool_create(pool
);
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
);
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
,
635 svn_revnum_t base_rev
,
638 svn_fs_txn_t
*txn
= apr_pcalloc(pool
, sizeof(*txn
));
642 txn
->base_rev
= base_rev
;
643 txn
->vtable
= &txn_vtable
;
644 txn
->fsap_data
= NULL
;
650 struct begin_txn_args
652 svn_fs_txn_t
**txn_p
;
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
;
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
;
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
;
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
);
699 /* Note: it is acceptable for this function to call back into
700 public FS API interfaces because it does not itself use trails. */
702 svn_fs_base__begin_txn(svn_fs_txn_t
**txn_p
,
709 struct begin_txn_args args
;
712 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
717 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_begin_txn
, &args
, pool
));
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
,
737 svn_fs_txn_t
**txn_p
;
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
;
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
);
764 svn_fs_base__open_txn(svn_fs_txn_t
**txn_p
,
770 struct open_txn_args args
;
772 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
776 SVN_ERR(svn_fs_base__retry_txn(fs
, txn_body_open_txn
, &args
, pool
));
783 struct cleanup_txn_args
785 transaction_t
**txn_p
;
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
,
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
,
805 /* Copy doesn't exist? No sweat. */
806 if (err
&& (err
->apr_err
== SVN_ERR_FS_NO_SUCH_COPY
))
808 svn_error_clear(err
);
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
;
831 txn_body_get_dirents(void *baton
, trail_t
*trail
)
833 struct get_dirents_args
*args
= baton
;
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
))
844 /* If a directory, do nothing and return. */
845 *(args
->dirents
) = NULL
;
846 if (svn_fs_base__dag_node_kind(node
) != svn_node_dir
)
849 /* Else it's mutable. Get its dirents. */
850 return svn_fs_base__dag_dir_entries(args
->dirents
, node
,
855 struct remove_node_args
857 const svn_fs_id_t
*id
;
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
,
872 delete_txn_tree(svn_fs_t
*fs
,
873 const svn_fs_id_t
*id
,
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
;
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)
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
;
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
);
901 /* If there are dirents upon which to recurse ... recurse. */
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
))
910 svn_fs_dirent_t
*dirent
;
912 svn_pool_clear(subpool
);
913 apr_hash_this(hi
, NULL
, NULL
, &val
);
915 SVN_ERR(delete_txn_tree(fs
, dirent
->id
, txn_id
, subpool
));
917 svn_pool_destroy(subpool
);
920 /* Remove the node. */
922 rm_args
.txn_id
= txn_id
;
923 return svn_fs_base__retry_txn(fs
, txn_body_remove_node
, &rm_args
, pool
);
928 txn_body_delete_txn(void *baton
, trail_t
*trail
)
930 return svn_fs_bdb__delete_txn(trail
->fs
, baton
, trail
, trail
->pool
);
935 svn_fs_base__purge_txn(svn_fs_t
*fs
,
939 struct cleanup_txn_args args
;
943 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
945 /* Open the transaction, expecting it to be dead. */
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
953 SVN_ERR(delete_txn_tree(fs
, txn
->root_id
, txn_id
, pool
));
955 /* Kill the transaction's changes (which should gracefully recover
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...). */
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 *),
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
,
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
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
);
997 svn_fs_base__abort_txn(svn_fs_txn_t
*txn
,
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
;
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
,
1028 svn_fs_base__list_transactions(apr_array_header_t
**names_p
,
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
;
1039 SVN_ERR(svn_fs_base__retry(fs
, txn_body_list_transactions
, &args
, pool
));
1042 return SVN_NO_ERROR
;