1 /* txn-table.c : operations on the `transactions' table
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 * ====================================================================
20 #include "bdb_compat.h"
22 #include "svn_pools.h"
26 #include "../key-gen.h"
27 #include "../util/skel.h"
28 #include "../util/fs_skels.h"
30 #include "../../libsvn_fs/fs-loader.h"
32 #include "txn-table.h"
34 #include "svn_private_config.h"
38 is_committed(transaction_t
*txn
)
40 return (txn
->kind
== transaction_kind_committed
) ? TRUE
: FALSE
;
45 svn_fs_bdb__open_transactions_table(DB
**transactions_p
,
49 const u_int32_t open_flags
= (create
? (DB_CREATE
| DB_EXCL
) : 0);
52 BDB_ERR(svn_fs_bdb__check_version());
53 BDB_ERR(db_create(&txns
, env
, 0));
54 BDB_ERR((txns
->open
)(SVN_BDB_OPEN_PARAMS(txns
, NULL
),
55 "transactions", 0, DB_BTREE
,
58 /* Create the `next-key' table entry. */
63 BDB_ERR(txns
->put(txns
, 0,
64 svn_fs_base__str_to_dbt(&key
, NEXT_KEY_KEY
),
65 svn_fs_base__str_to_dbt(&value
, "0"), 0));
68 *transactions_p
= txns
;
74 svn_fs_bdb__put_txn(svn_fs_t
*fs
,
75 const transaction_t
*txn
,
80 base_fs_data_t
*bfd
= fs
->fsap_data
;
84 /* Convert native type to skel. */
85 SVN_ERR(svn_fs_base__unparse_transaction_skel(&txn_skel
, txn
, pool
));
87 /* Only in the context of this function do we know that the DB call
88 will not attempt to modify txn_name, so the cast belongs here. */
89 svn_fs_base__str_to_dbt(&key
, txn_name
);
90 svn_fs_base__skel_to_dbt(&value
, txn_skel
, pool
);
91 svn_fs_base__trail_debug(trail
, "transactions", "put");
92 SVN_ERR(BDB_WRAP(fs
, _("storing transaction record"),
93 bfd
->transactions
->put(bfd
->transactions
, trail
->db_txn
,
100 /* Allocate a Subversion transaction ID in FS, as part of TRAIL. Set
101 *ID_P to the new transaction ID, allocated in POOL. */
103 allocate_txn_id(const char **id_p
,
108 base_fs_data_t
*bfd
= fs
->fsap_data
;
111 char next_key
[MAX_KEY_SIZE
];
114 svn_fs_base__str_to_dbt(&query
, NEXT_KEY_KEY
);
116 /* Get the current value associated with the `next-key' key in the table. */
117 svn_fs_base__trail_debug(trail
, "transactions", "get");
118 SVN_ERR(BDB_WRAP(fs
, "allocating new transaction ID (getting 'next-key')",
119 bfd
->transactions
->get(bfd
->transactions
, trail
->db_txn
,
121 svn_fs_base__result_dbt(&result
),
123 svn_fs_base__track_dbt(&result
, pool
);
125 /* Set our return value. */
126 *id_p
= apr_pstrmemdup(pool
, result
.data
, result
.size
);
128 /* Bump to future key. */
130 svn_fs_base__next_key(result
.data
, &len
, next_key
);
131 svn_fs_base__str_to_dbt(&query
, NEXT_KEY_KEY
);
132 svn_fs_base__str_to_dbt(&result
, next_key
);
133 svn_fs_base__trail_debug(trail
, "transactions", "put");
134 db_err
= bfd
->transactions
->put(bfd
->transactions
, trail
->db_txn
,
137 SVN_ERR(BDB_WRAP(fs
, "bumping next transaction key", db_err
));
143 svn_fs_bdb__create_txn(const char **txn_name_p
,
145 const svn_fs_id_t
*root_id
,
149 const char *txn_name
;
152 SVN_ERR(allocate_txn_id(&txn_name
, fs
, trail
, pool
));
153 txn
.kind
= transaction_kind_normal
;
154 txn
.root_id
= root_id
;
155 txn
.base_id
= root_id
;
158 txn
.revision
= SVN_INVALID_REVNUM
;
159 SVN_ERR(svn_fs_bdb__put_txn(fs
, &txn
, txn_name
, trail
, pool
));
161 *txn_name_p
= txn_name
;
167 svn_fs_bdb__delete_txn(svn_fs_t
*fs
,
168 const char *txn_name
,
172 base_fs_data_t
*bfd
= fs
->fsap_data
;
176 /* Make sure TXN is dead. */
177 SVN_ERR(svn_fs_bdb__get_txn(&txn
, fs
, txn_name
, trail
, pool
));
178 if (is_committed(txn
))
179 return svn_fs_base__err_txn_not_mutable(fs
, txn_name
);
181 /* Delete the transaction from the `transactions' table. */
182 svn_fs_base__str_to_dbt(&key
, txn_name
);
183 svn_fs_base__trail_debug(trail
, "transactions", "del");
184 SVN_ERR(BDB_WRAP(fs
, "deleting entry from 'transactions' table",
185 bfd
->transactions
->del(bfd
->transactions
,
186 trail
->db_txn
, &key
, 0)));
193 svn_fs_bdb__get_txn(transaction_t
**txn_p
,
195 const char *txn_name
,
199 base_fs_data_t
*bfd
= fs
->fsap_data
;
203 transaction_t
*transaction
;
205 /* Only in the context of this function do we know that the DB call
206 will not attempt to modify txn_name, so the cast belongs here. */
207 svn_fs_base__trail_debug(trail
, "transactions", "get");
208 db_err
= bfd
->transactions
->get(bfd
->transactions
, trail
->db_txn
,
209 svn_fs_base__str_to_dbt(&key
, txn_name
),
210 svn_fs_base__result_dbt(&value
),
212 svn_fs_base__track_dbt(&value
, pool
);
214 if (db_err
== DB_NOTFOUND
)
215 return svn_fs_base__err_no_such_txn(fs
, txn_name
);
216 SVN_ERR(BDB_WRAP(fs
, "reading transaction", db_err
));
218 /* Parse TRANSACTION skel */
219 skel
= svn_fs_base__parse_skel(value
.data
, value
.size
, pool
);
221 return svn_fs_base__err_corrupt_txn(fs
, txn_name
);
223 /* Convert skel to native type. */
224 SVN_ERR(svn_fs_base__parse_transaction_skel(&transaction
, skel
, pool
));
225 *txn_p
= transaction
;
231 svn_fs_bdb__get_txn_list(apr_array_header_t
**names_p
,
236 base_fs_data_t
*bfd
= fs
->fsap_data
;
237 apr_size_t
const next_key_key_len
= strlen(NEXT_KEY_KEY
);
238 apr_pool_t
*subpool
= svn_pool_create(pool
);
239 apr_array_header_t
*names
;
242 int db_err
, db_c_err
;
244 /* Allocate the initial names array */
245 names
= apr_array_make(pool
, 4, sizeof(const char *));
247 /* Create a database cursor to list the transaction names. */
248 svn_fs_base__trail_debug(trail
, "transactions", "cursor");
249 SVN_ERR(BDB_WRAP(fs
, "reading transaction list (opening cursor)",
250 bfd
->transactions
->cursor(bfd
->transactions
,
251 trail
->db_txn
, &cursor
, 0)));
253 /* Build a null-terminated array of keys in the transactions table. */
254 for (db_err
= svn_bdb_dbc_get(cursor
,
255 svn_fs_base__result_dbt(&key
),
256 svn_fs_base__result_dbt(&value
),
259 db_err
= svn_bdb_dbc_get(cursor
,
260 svn_fs_base__result_dbt(&key
),
261 svn_fs_base__result_dbt(&value
),
268 /* Clear the per-iteration subpool */
269 svn_pool_clear(subpool
);
271 /* Track the memory alloc'd for fetching the key and value here
272 so that when the containing pool is cleared, this memory is
274 svn_fs_base__track_dbt(&key
, subpool
);
275 svn_fs_base__track_dbt(&value
, subpool
);
277 /* Ignore the "next-key" key. */
278 if (key
.size
== next_key_key_len
279 && 0 == memcmp(key
.data
, NEXT_KEY_KEY
, next_key_key_len
))
282 /* Parse TRANSACTION skel */
283 txn_skel
= svn_fs_base__parse_skel(value
.data
, value
.size
, subpool
);
286 svn_bdb_dbc_close(cursor
);
287 return svn_fs_base__err_corrupt_txn
288 (fs
, apr_pstrmemdup(pool
, key
.data
, key
.size
));
291 /* Convert skel to native type. */
292 if ((err
= svn_fs_base__parse_transaction_skel(&txn
, txn_skel
,
295 svn_bdb_dbc_close(cursor
);
299 /* If this is an immutable "committed" transaction, ignore it. */
300 if (is_committed(txn
))
303 /* Add the transaction name to the NAMES array, duping it into POOL. */
304 APR_ARRAY_PUSH(names
, const char *) = apr_pstrmemdup(pool
, key
.data
,
308 /* Check for errors, but close the cursor first. */
309 db_c_err
= svn_bdb_dbc_close(cursor
);
310 if (db_err
!= DB_NOTFOUND
)
312 SVN_ERR(BDB_WRAP(fs
, "reading transaction list (listing keys)",
315 SVN_ERR(BDB_WRAP(fs
, "reading transaction list (closing cursor)",
318 /* Destroy the per-iteration subpool */
319 svn_pool_destroy(subpool
);