1 /* txn-table.c : operations on the `transactions' table
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
26 #include "bdb_compat.h"
28 #include "svn_pools.h"
29 #include "private/svn_skel.h"
34 #include "../key-gen.h"
35 #include "../util/fs_skels.h"
37 #include "../../libsvn_fs/fs-loader.h"
39 #include "txn-table.h"
41 #include "svn_private_config.h"
45 is_committed(transaction_t *txn)
47 return (txn->kind == transaction_kind_committed);
52 svn_fs_bdb__open_transactions_table(DB **transactions_p,
56 const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0);
59 BDB_ERR(svn_fs_bdb__check_version());
60 BDB_ERR(db_create(&txns, env, 0));
61 BDB_ERR((txns->open)(SVN_BDB_OPEN_PARAMS(txns, NULL),
62 "transactions", 0, DB_BTREE,
65 /* Create the `next-key' table entry. */
70 BDB_ERR(txns->put(txns, 0,
71 svn_fs_base__str_to_dbt(&key, NEXT_KEY_KEY),
72 svn_fs_base__str_to_dbt(&value, "0"), 0));
75 *transactions_p = txns;
81 svn_fs_bdb__put_txn(svn_fs_t *fs,
82 const transaction_t *txn,
87 base_fs_data_t *bfd = fs->fsap_data;
91 /* Convert native type to skel. */
92 SVN_ERR(svn_fs_base__unparse_transaction_skel(&txn_skel, txn, pool));
94 /* Only in the context of this function do we know that the DB call
95 will not attempt to modify txn_name, so the cast belongs here. */
96 svn_fs_base__str_to_dbt(&key, txn_name);
97 svn_fs_base__skel_to_dbt(&value, txn_skel, pool);
98 svn_fs_base__trail_debug(trail, "transactions", "put");
99 return BDB_WRAP(fs, N_("storing transaction record"),
100 bfd->transactions->put(bfd->transactions, trail->db_txn,
105 /* Allocate a Subversion transaction ID in FS, as part of TRAIL. Set
106 *ID_P to the new transaction ID, allocated in POOL. */
108 allocate_txn_id(const char **id_p,
113 base_fs_data_t *bfd = fs->fsap_data;
116 char next_key[MAX_KEY_SIZE];
119 svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY);
121 /* Get the current value associated with the `next-key' key in the table. */
122 svn_fs_base__trail_debug(trail, "transactions", "get");
123 SVN_ERR(BDB_WRAP(fs, N_("allocating new transaction ID (getting 'next-key')"),
124 bfd->transactions->get(bfd->transactions, trail->db_txn,
126 svn_fs_base__result_dbt(&result),
128 svn_fs_base__track_dbt(&result, pool);
130 /* Set our return value. */
131 *id_p = apr_pstrmemdup(pool, result.data, result.size);
133 /* Bump to future key. */
135 svn_fs_base__next_key(result.data, &len, next_key);
136 svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY);
137 svn_fs_base__str_to_dbt(&result, next_key);
138 svn_fs_base__trail_debug(trail, "transactions", "put");
139 db_err = bfd->transactions->put(bfd->transactions, trail->db_txn,
142 return BDB_WRAP(fs, N_("bumping next transaction key"), db_err);
147 svn_fs_bdb__create_txn(const char **txn_name_p,
149 const svn_fs_id_t *root_id,
153 const char *txn_name;
156 SVN_ERR(allocate_txn_id(&txn_name, fs, trail, pool));
157 txn.kind = transaction_kind_normal;
158 txn.root_id = root_id;
159 txn.base_id = root_id;
162 txn.revision = SVN_INVALID_REVNUM;
163 SVN_ERR(svn_fs_bdb__put_txn(fs, &txn, txn_name, trail, pool));
165 *txn_name_p = txn_name;
171 svn_fs_bdb__delete_txn(svn_fs_t *fs,
172 const char *txn_name,
176 base_fs_data_t *bfd = fs->fsap_data;
180 /* Make sure TXN is dead. */
181 SVN_ERR(svn_fs_bdb__get_txn(&txn, fs, txn_name, trail, pool));
182 if (is_committed(txn))
183 return svn_fs_base__err_txn_not_mutable(fs, txn_name);
185 /* Delete the transaction from the `transactions' table. */
186 svn_fs_base__str_to_dbt(&key, txn_name);
187 svn_fs_base__trail_debug(trail, "transactions", "del");
188 return BDB_WRAP(fs, N_("deleting entry from 'transactions' table"),
189 bfd->transactions->del(bfd->transactions,
190 trail->db_txn, &key, 0));
195 svn_fs_bdb__get_txn(transaction_t **txn_p,
197 const char *txn_name,
201 base_fs_data_t *bfd = fs->fsap_data;
205 transaction_t *transaction;
207 /* Only in the context of this function do we know that the DB call
208 will not attempt to modify txn_name, so the cast belongs here. */
209 svn_fs_base__trail_debug(trail, "transactions", "get");
210 db_err = bfd->transactions->get(bfd->transactions, trail->db_txn,
211 svn_fs_base__str_to_dbt(&key, txn_name),
212 svn_fs_base__result_dbt(&value),
214 svn_fs_base__track_dbt(&value, pool);
216 if (db_err == DB_NOTFOUND)
217 return svn_fs_base__err_no_such_txn(fs, txn_name);
218 SVN_ERR(BDB_WRAP(fs, N_("reading transaction"), db_err));
220 /* Parse TRANSACTION skel */
221 skel = svn_skel__parse(value.data, value.size, pool);
223 return svn_fs_base__err_corrupt_txn(fs, txn_name);
225 /* Convert skel to native type. */
226 SVN_ERR(svn_fs_base__parse_transaction_skel(&transaction, skel, pool));
227 *txn_p = transaction;
233 svn_fs_bdb__get_txn_list(apr_array_header_t **names_p,
238 base_fs_data_t *bfd = fs->fsap_data;
239 apr_size_t const next_key_key_len = strlen(NEXT_KEY_KEY);
240 apr_pool_t *subpool = svn_pool_create(pool);
241 apr_array_header_t *names;
244 int db_err, db_c_err;
246 /* Allocate the initial names array */
247 names = apr_array_make(pool, 4, sizeof(const char *));
249 /* Create a database cursor to list the transaction names. */
250 svn_fs_base__trail_debug(trail, "transactions", "cursor");
251 SVN_ERR(BDB_WRAP(fs, N_("reading transaction list (opening cursor)"),
252 bfd->transactions->cursor(bfd->transactions,
253 trail->db_txn, &cursor, 0)));
255 /* Build a null-terminated array of keys in the transactions table. */
256 for (db_err = svn_bdb_dbc_get(cursor,
257 svn_fs_base__result_dbt(&key),
258 svn_fs_base__result_dbt(&value),
261 db_err = svn_bdb_dbc_get(cursor,
262 svn_fs_base__result_dbt(&key),
263 svn_fs_base__result_dbt(&value),
267 svn_skel_t *txn_skel;
270 /* Clear the per-iteration subpool */
271 svn_pool_clear(subpool);
273 /* Track the memory alloc'd for fetching the key and value here
274 so that when the containing pool is cleared, this memory is
276 svn_fs_base__track_dbt(&key, subpool);
277 svn_fs_base__track_dbt(&value, subpool);
279 /* Ignore the "next-key" key. */
280 if (key.size == next_key_key_len
281 && 0 == memcmp(key.data, NEXT_KEY_KEY, next_key_key_len))
284 /* Parse TRANSACTION skel */
285 txn_skel = svn_skel__parse(value.data, value.size, subpool);
288 svn_bdb_dbc_close(cursor);
289 return svn_fs_base__err_corrupt_txn
290 (fs, apr_pstrmemdup(pool, key.data, key.size));
293 /* Convert skel to native type. */
294 if ((err = svn_fs_base__parse_transaction_skel(&txn, txn_skel,
297 svn_bdb_dbc_close(cursor);
298 return svn_error_trace(err);
301 /* If this is an immutable "committed" transaction, ignore it. */
302 if (is_committed(txn))
305 /* Add the transaction name to the NAMES array, duping it into POOL. */
306 APR_ARRAY_PUSH(names, const char *) = apr_pstrmemdup(pool, key.data,
310 /* Check for errors, but close the cursor first. */
311 db_c_err = svn_bdb_dbc_close(cursor);
312 if (db_err != DB_NOTFOUND)
314 SVN_ERR(BDB_WRAP(fs, N_("reading transaction list (listing keys)"),
317 SVN_ERR(BDB_WRAP(fs, N_("reading transaction list (closing cursor)"),
320 /* Destroy the per-iteration subpool */
321 svn_pool_destroy(subpool);