Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / libsvn_fs_base / bdb / txn-table.c
blob4f401abf6c1eea4ff96a9048320c9f19410d3991
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 * ====================================================================
18 #include <string.h>
19 #include <assert.h>
20 #include "bdb_compat.h"
22 #include "svn_pools.h"
23 #include "dbt.h"
24 #include "../err.h"
25 #include "../fs.h"
26 #include "../key-gen.h"
27 #include "../util/skel.h"
28 #include "../util/fs_skels.h"
29 #include "../trail.h"
30 #include "../../libsvn_fs/fs-loader.h"
31 #include "bdb-err.h"
32 #include "txn-table.h"
34 #include "svn_private_config.h"
37 static svn_boolean_t
38 is_committed(transaction_t *txn)
40 return (txn->kind == transaction_kind_committed) ? TRUE : FALSE;
44 int
45 svn_fs_bdb__open_transactions_table(DB **transactions_p,
46 DB_ENV *env,
47 svn_boolean_t create)
49 const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0);
50 DB *txns;
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,
56 open_flags, 0666));
58 /* Create the `next-key' table entry. */
59 if (create)
61 DBT key, value;
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;
69 return 0;
73 svn_error_t *
74 svn_fs_bdb__put_txn(svn_fs_t *fs,
75 const transaction_t *txn,
76 const char *txn_name,
77 trail_t *trail,
78 apr_pool_t *pool)
80 base_fs_data_t *bfd = fs->fsap_data;
81 skel_t *txn_skel;
82 DBT key, value;
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,
94 &key, &value, 0)));
96 return SVN_NO_ERROR;
100 /* Allocate a Subversion transaction ID in FS, as part of TRAIL. Set
101 *ID_P to the new transaction ID, allocated in POOL. */
102 static svn_error_t *
103 allocate_txn_id(const char **id_p,
104 svn_fs_t *fs,
105 trail_t *trail,
106 apr_pool_t *pool)
108 base_fs_data_t *bfd = fs->fsap_data;
109 DBT query, result;
110 apr_size_t len;
111 char next_key[MAX_KEY_SIZE];
112 int db_err;
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,
120 &query,
121 svn_fs_base__result_dbt(&result),
122 0)));
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. */
129 len = result.size;
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,
135 &query, &result, 0);
137 SVN_ERR(BDB_WRAP(fs, "bumping next transaction key", db_err));
138 return SVN_NO_ERROR;
142 svn_error_t *
143 svn_fs_bdb__create_txn(const char **txn_name_p,
144 svn_fs_t *fs,
145 const svn_fs_id_t *root_id,
146 trail_t *trail,
147 apr_pool_t *pool)
149 const char *txn_name;
150 transaction_t txn;
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;
156 txn.proplist = NULL;
157 txn.copies = NULL;
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;
162 return SVN_NO_ERROR;
166 svn_error_t *
167 svn_fs_bdb__delete_txn(svn_fs_t *fs,
168 const char *txn_name,
169 trail_t *trail,
170 apr_pool_t *pool)
172 base_fs_data_t *bfd = fs->fsap_data;
173 DBT key;
174 transaction_t *txn;
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)));
188 return SVN_NO_ERROR;
192 svn_error_t *
193 svn_fs_bdb__get_txn(transaction_t **txn_p,
194 svn_fs_t *fs,
195 const char *txn_name,
196 trail_t *trail,
197 apr_pool_t *pool)
199 base_fs_data_t *bfd = fs->fsap_data;
200 DBT key, value;
201 int db_err;
202 skel_t *skel;
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);
220 if (! skel)
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;
226 return SVN_NO_ERROR;
230 svn_error_t *
231 svn_fs_bdb__get_txn_list(apr_array_header_t **names_p,
232 svn_fs_t *fs,
233 trail_t *trail,
234 apr_pool_t *pool)
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;
240 DBC *cursor;
241 DBT key, value;
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),
257 DB_FIRST);
258 db_err == 0;
259 db_err = svn_bdb_dbc_get(cursor,
260 svn_fs_base__result_dbt(&key),
261 svn_fs_base__result_dbt(&value),
262 DB_NEXT))
264 transaction_t *txn;
265 skel_t *txn_skel;
266 svn_error_t *err;
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
273 freed. */
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))
280 continue;
282 /* Parse TRANSACTION skel */
283 txn_skel = svn_fs_base__parse_skel(value.data, value.size, subpool);
284 if (! txn_skel)
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,
293 subpool)))
295 svn_bdb_dbc_close(cursor);
296 return err;
299 /* If this is an immutable "committed" transaction, ignore it. */
300 if (is_committed(txn))
301 continue;
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,
305 key.size);
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)",
313 db_err));
315 SVN_ERR(BDB_WRAP(fs, "reading transaction list (closing cursor)",
316 db_c_err));
318 /* Destroy the per-iteration subpool */
319 svn_pool_destroy(subpool);
321 *names_p = names;
322 return SVN_NO_ERROR;