33
33
/*** Creating and opening the strings table. ***/
36
svn_fs_bdb__open_strings_table (DB **strings_p,
36
svn_fs_bdb__open_strings_table(DB **strings_p,
40
40
const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0);
43
BDB_ERR (svn_fs_bdb__check_version());
44
BDB_ERR (db_create (&strings, env, 0));
43
BDB_ERR(svn_fs_bdb__check_version());
44
BDB_ERR(db_create(&strings, env, 0));
46
46
/* Enable duplicate keys. This allows the data to be spread out across
47
47
multiple records. Note: this must occur before ->open(). */
48
BDB_ERR (strings->set_flags (strings, DB_DUP));
48
BDB_ERR(strings->set_flags(strings, DB_DUP));
50
BDB_ERR (strings->open (SVN_BDB_OPEN_PARAMS(strings, NULL),
51
"strings", 0, DB_BTREE,
52
open_flags | SVN_BDB_AUTO_COMMIT,
50
BDB_ERR(strings->open(SVN_BDB_OPEN_PARAMS(strings, NULL),
51
"strings", 0, DB_BTREE,
59
58
/* Create the `next-key' table entry. */
62
svn_fs_base__str_to_dbt (&key, NEXT_KEY_KEY),
63
svn_fs_base__str_to_dbt (&value, "0"),
64
SVN_BDB_AUTO_COMMIT));
61
svn_fs_base__str_to_dbt(&key, NEXT_KEY_KEY),
62
svn_fs_base__str_to_dbt(&value, "0"), 0));
67
65
*strings_p = strings;
76
74
whose key is defined by QUERY. Set *LENGTH to the size of that
78
76
static svn_error_t *
79
locate_key (apr_size_t *length,
77
locate_key(apr_size_t *length,
86
84
base_fs_data_t *bfd = fs->fsap_data;
90
svn_fs_base__trail_debug (trail, "strings", "cursor");
91
SVN_ERR (BDB_WRAP (fs, _("creating cursor for reading a string"),
92
bfd->strings->cursor (bfd->strings, trail->db_txn,
88
svn_fs_base__trail_debug(trail, "strings", "cursor");
89
SVN_ERR(BDB_WRAP(fs, _("creating cursor for reading a string"),
90
bfd->strings->cursor(bfd->strings, trail->db_txn,
95
93
/* Set up the DBT for reading the length of the record. */
96
svn_fs_base__clear_dbt (&result);
94
svn_fs_base__clear_dbt(&result);
98
96
result.flags |= DB_DBT_USERMEM;
100
98
/* Advance the cursor to the key that we're looking for. */
101
db_err = (*cursor)->c_get (*cursor, query, &result, DB_SET);
99
db_err = (*cursor)->c_get(*cursor, query, &result, DB_SET);
103
101
/* We don't need to svn_fs_base__track_dbt() the result, because nothing
104
102
was allocated in it. */
118
116
if (db_err != SVN_BDB_DB_BUFFER_SMALL)
120
(*cursor)->c_close (*cursor);
121
return BDB_WRAP (fs, "moving cursor", db_err);
118
(*cursor)->c_close(*cursor);
119
return BDB_WRAP(fs, "moving cursor", db_err);
124
122
/* We got an SVN_BDB_DB_BUFFER_SMALL (typical since we have a
125
123
zero length buf), so we need to re-run the operation to make
127
svn_fs_base__clear_dbt (&rerun);
125
svn_fs_base__clear_dbt(&rerun);
128
126
rerun.flags |= DB_DBT_USERMEM | DB_DBT_PARTIAL;
129
db_err = (*cursor)->c_get (*cursor, query, &rerun, DB_SET);
127
db_err = (*cursor)->c_get(*cursor, query, &rerun, DB_SET);
132
(*cursor)->c_close (*cursor);
133
return BDB_WRAP (fs, "rerunning cursor move", db_err);
130
(*cursor)->c_close(*cursor);
131
return BDB_WRAP(fs, "rerunning cursor move", db_err);
145
143
CURSOR's current location. Set *LENGTH to the size of that next
146
144
row. If any error occurs, CURSOR will be destroyed. */
148
get_next_length (apr_size_t *length, DBC *cursor, DBT *query)
146
get_next_length(apr_size_t *length, DBC *cursor, DBT *query)
153
151
/* Set up the DBT for reading the length of the record. */
154
svn_fs_base__clear_dbt (&result);
152
svn_fs_base__clear_dbt(&result);
156
154
result.flags |= DB_DBT_USERMEM;
158
156
/* Note: this may change the QUERY DBT, but that's okay: we're going
159
157
to be sticking with the same key anyways. */
160
db_err = cursor->c_get (cursor, query, &result, DB_NEXT_DUP);
158
db_err = cursor->c_get(cursor, query, &result, DB_NEXT_DUP);
162
160
/* Note that we exit on DB_NOTFOUND. The caller uses that to end a loop. */
167
165
if (db_err != SVN_BDB_DB_BUFFER_SMALL)
169
cursor->c_close (cursor);
167
cursor->c_close(cursor);
173
171
/* We got an SVN_BDB_DB_BUFFER_SMALL (typical since we have a
174
172
zero length buf), so we need to re-run the operation to make
176
svn_fs_base__clear_dbt (&rerun);
174
svn_fs_base__clear_dbt(&rerun);
177
175
rerun.flags |= DB_DBT_USERMEM | DB_DBT_PARTIAL;
178
db_err = cursor->c_get (cursor, query, &rerun, DB_NEXT_DUP);
176
db_err = cursor->c_get(cursor, query, &rerun, DB_NEXT_DUP);
180
cursor->c_close (cursor);
178
cursor->c_close(cursor);
183
181
/* ### this cast might not be safe? */
190
svn_fs_bdb__string_read (svn_fs_t *fs,
193
svn_filesize_t offset,
188
svn_fs_bdb__string_read(svn_fs_t *fs,
191
svn_filesize_t offset,
199
197
DBT query, result;
201
199
apr_size_t length, bytes_read = 0;
203
svn_fs_base__str_to_dbt (&query, key);
201
svn_fs_base__str_to_dbt(&query, key);
205
SVN_ERR (locate_key (&length, &cursor, &query, fs, trail, pool));
203
SVN_ERR(locate_key(&length, &cursor, &query, fs, trail, pool));
207
205
/* Seek through the records for this key, trying to find the record that
208
206
includes OFFSET. Note that we don't require reading from more than
231
229
read successive records until we've filled the request. */
234
svn_fs_base__clear_dbt (&result);
232
svn_fs_base__clear_dbt(&result);
235
233
result.data = buf + bytes_read;
236
234
result.ulen = *len - bytes_read;
237
235
result.doff = (u_int32_t)offset;
238
236
result.dlen = *len - bytes_read;
239
237
result.flags |= (DB_DBT_USERMEM | DB_DBT_PARTIAL);
240
db_err = cursor->c_get (cursor, &query, &result, DB_CURRENT);
238
db_err = cursor->c_get(cursor, &query, &result, DB_CURRENT);
243
cursor->c_close (cursor);
244
return BDB_WRAP (fs, "reading string", db_err);
241
cursor->c_close(cursor);
242
return BDB_WRAP(fs, "reading string", db_err);
247
245
bytes_read += result.size;
248
246
if (bytes_read == *len)
250
248
/* Done with the cursor. */
251
SVN_ERR (BDB_WRAP (fs, "closing string-reading cursor",
252
cursor->c_close (cursor)));
249
SVN_ERR(BDB_WRAP(fs, "closing string-reading cursor",
250
cursor->c_close(cursor)));
256
254
/* Remember, if any error happens, our cursor has been closed
258
db_err = get_next_length (&length, cursor, &query);
256
db_err = get_next_length(&length, cursor, &query);
259
257
if (db_err == DB_NOTFOUND)
262
return BDB_WRAP (fs, "reading string", db_err);
260
return BDB_WRAP(fs, "reading string", db_err);
264
262
/* We'll be reading from the beginning of the next record */
293
291
this database allows duplicates, we can't do an arbitrary 'put' to
294
292
write the new value -- that would append, not overwrite. */
296
svn_fs_base__trail_debug (trail, "strings", "cursor");
297
SVN_ERR (BDB_WRAP (fs, "creating cursor for reading a string",
298
bfd->strings->cursor (bfd->strings, trail->db_txn,
294
svn_fs_base__trail_debug(trail, "strings", "cursor");
295
SVN_ERR(BDB_WRAP(fs, "creating cursor for reading a string",
296
bfd->strings->cursor(bfd->strings, trail->db_txn,
301
299
/* Advance the cursor to 'next-key' and read it. */
303
db_err = cursor->c_get (cursor,
304
svn_fs_base__str_to_dbt (&query, NEXT_KEY_KEY),
305
svn_fs_base__result_dbt (&result),
301
db_err = cursor->c_get(cursor,
302
svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY),
303
svn_fs_base__result_dbt(&result),
309
cursor->c_close (cursor);
310
return BDB_WRAP (fs, "getting next-key value", db_err);
307
cursor->c_close(cursor);
308
return BDB_WRAP(fs, "getting next-key value", db_err);
313
svn_fs_base__track_dbt (&result, pool);
314
*key = apr_pstrmemdup (pool, result.data, result.size);
311
svn_fs_base__track_dbt(&result, pool);
312
*key = apr_pstrmemdup(pool, result.data, result.size);
316
314
/* Bump to future key. */
317
315
key_len = result.size;
318
svn_fs_base__next_key (result.data, &key_len, next_key);
316
svn_fs_base__next_key(result.data, &key_len, next_key);
320
318
/* Shove the new key back into the database, at the cursor position. */
321
db_err = cursor->c_put (cursor, &query,
322
svn_fs_base__str_to_dbt (&result, next_key),
319
db_err = cursor->c_put(cursor, &query,
320
svn_fs_base__str_to_dbt(&result, next_key),
326
cursor->c_close (cursor); /* ignore the error, the original is
324
cursor->c_close(cursor); /* ignore the error, the original is
327
325
more important. */
328
return BDB_WRAP (fs, "bumping next string key", db_err);
326
return BDB_WRAP(fs, "bumping next string key", db_err);
331
return BDB_WRAP (fs, "closing string-reading cursor",
332
cursor->c_close (cursor));
329
return BDB_WRAP(fs, "closing string-reading cursor",
330
cursor->c_close(cursor));
336
svn_fs_bdb__string_append (svn_fs_t *fs,
334
svn_fs_bdb__string_append(svn_fs_t *fs,
343
341
base_fs_data_t *bfd = fs->fsap_data;
344
342
DBT query, result;
347
345
using the value of the `next-key' record in the strings table. */
348
346
if (*key == NULL)
350
SVN_ERR (get_key_and_bump (fs, key, trail, pool));
348
SVN_ERR(get_key_and_bump(fs, key, trail, pool));
353
351
/* Store a new record into the database. */
354
svn_fs_base__trail_debug (trail, "strings", "put");
355
SVN_ERR (BDB_WRAP (fs, "appending string",
357
(bfd->strings, trail->db_txn,
358
svn_fs_base__str_to_dbt (&query, *key),
359
svn_fs_base__set_dbt (&result, buf, len),
352
svn_fs_base__trail_debug(trail, "strings", "put");
353
SVN_ERR(BDB_WRAP(fs, "appending string",
355
(bfd->strings, trail->db_txn,
356
svn_fs_base__str_to_dbt(&query, *key),
357
svn_fs_base__set_dbt(&result, buf, len),
362
360
return SVN_NO_ERROR;
367
svn_fs_bdb__string_clear (svn_fs_t *fs,
365
svn_fs_bdb__string_clear(svn_fs_t *fs,
372
370
base_fs_data_t *bfd = fs->fsap_data;
374
372
DBT query, result;
376
svn_fs_base__str_to_dbt (&query, key);
374
svn_fs_base__str_to_dbt(&query, key);
378
376
/* Torch the prior contents */
379
svn_fs_base__trail_debug (trail, "strings", "del");
380
db_err = bfd->strings->del (bfd->strings, trail->db_txn, &query, 0);
377
svn_fs_base__trail_debug(trail, "strings", "del");
378
db_err = bfd->strings->del(bfd->strings, trail->db_txn, &query, 0);
382
380
/* If there's no such node, return an appropriately specific error. */
383
381
if (db_err == DB_NOTFOUND)
386
384
"No such string '%s'", key);
388
386
/* Handle any other error conditions. */
389
SVN_ERR (BDB_WRAP (fs, "clearing string", db_err));
387
SVN_ERR(BDB_WRAP(fs, "clearing string", db_err));
391
389
/* Shove empty data back in for this key. */
392
svn_fs_base__clear_dbt (&result);
390
svn_fs_base__clear_dbt(&result);
395
393
result.flags |= DB_DBT_USERMEM;
397
svn_fs_base__trail_debug (trail, "strings", "put");
398
return BDB_WRAP (fs, "storing empty contents",
399
bfd->strings->put (bfd->strings, trail->db_txn,
400
&query, &result, 0));
395
svn_fs_base__trail_debug(trail, "strings", "put");
396
return BDB_WRAP(fs, "storing empty contents",
397
bfd->strings->put(bfd->strings, trail->db_txn,
398
&query, &result, 0));
405
svn_fs_bdb__string_size (svn_filesize_t *size,
403
svn_fs_bdb__string_size(svn_filesize_t *size,
414
412
apr_size_t length;
415
413
svn_filesize_t total;
417
svn_fs_base__str_to_dbt (&query, key);
415
svn_fs_base__str_to_dbt(&query, key);
419
SVN_ERR (locate_key (&length, &cursor, &query, fs, trail, pool));
417
SVN_ERR(locate_key(&length, &cursor, &query, fs, trail, pool));
424
422
/* Remember, if any error happens, our cursor has been closed
426
db_err = get_next_length (&length, cursor, &query);
424
db_err = get_next_length(&length, cursor, &query);
428
426
/* No more records? Then return the total length. */
429
427
if (db_err == DB_NOTFOUND)
445
svn_fs_bdb__string_delete (svn_fs_t *fs,
443
svn_fs_bdb__string_delete(svn_fs_t *fs,
450
448
base_fs_data_t *bfd = fs->fsap_data;
454
svn_fs_base__trail_debug (trail, "strings", "del");
455
db_err = bfd->strings->del (bfd->strings, trail->db_txn,
456
svn_fs_base__str_to_dbt (&query, key), 0);
452
svn_fs_base__trail_debug(trail, "strings", "del");
453
db_err = bfd->strings->del(bfd->strings, trail->db_txn,
454
svn_fs_base__str_to_dbt(&query, key), 0);
458
456
/* If there's no such node, return an appropriately specific error. */
459
457
if (db_err == DB_NOTFOUND)
462
460
"No such string '%s'", key);
464
462
/* Handle any other error conditions. */
465
SVN_ERR (BDB_WRAP (fs, "deleting string", db_err));
463
SVN_ERR(BDB_WRAP(fs, "deleting string", db_err));
467
465
return SVN_NO_ERROR;
472
svn_fs_bdb__string_copy (svn_fs_t *fs,
473
const char **new_key,
470
svn_fs_bdb__string_copy(svn_fs_t *fs,
471
const char **new_key,
478
476
base_fs_data_t *bfd = fs->fsap_data;
485
483
/* Copy off the old key in case the caller is sharing storage
486
484
between the old and new keys. */
487
const char *old_key = apr_pstrdup (pool, key);
489
SVN_ERR (get_key_and_bump (fs, new_key, trail, pool));
491
svn_fs_base__trail_debug (trail, "strings", "cursor");
492
SVN_ERR (BDB_WRAP (fs, "creating cursor for reading a string",
493
bfd->strings->cursor (bfd->strings, trail->db_txn,
496
svn_fs_base__str_to_dbt (&query, old_key);
497
svn_fs_base__str_to_dbt (©key, *new_key);
499
svn_fs_base__clear_dbt (&result);
485
const char *old_key = apr_pstrdup(pool, key);
487
SVN_ERR(get_key_and_bump(fs, new_key, trail, pool));
489
svn_fs_base__trail_debug(trail, "strings", "cursor");
490
SVN_ERR(BDB_WRAP(fs, "creating cursor for reading a string",
491
bfd->strings->cursor(bfd->strings, trail->db_txn,
494
svn_fs_base__str_to_dbt(&query, old_key);
495
svn_fs_base__str_to_dbt(©key, *new_key);
497
svn_fs_base__clear_dbt(&result);
501
499
/* Move to the first record and fetch its data (under BDB's mem mgmt). */
502
db_err = cursor->c_get (cursor, &query, &result, DB_SET);
500
db_err = cursor->c_get(cursor, &query, &result, DB_SET);
505
cursor->c_close (cursor);
506
return BDB_WRAP (fs, "getting next-key value", db_err);
503
cursor->c_close(cursor);
504
return BDB_WRAP(fs, "getting next-key value", db_err);
518
516
/* Write the data to the database */
519
svn_fs_base__trail_debug (trail, "strings", "put");
520
db_err = bfd->strings->put (bfd->strings, trail->db_txn,
521
©key, &result, 0);
517
svn_fs_base__trail_debug(trail, "strings", "put");
518
db_err = bfd->strings->put(bfd->strings, trail->db_txn,
519
©key, &result, 0);
524
cursor->c_close (cursor);
525
return BDB_WRAP (fs, "writing copied data", db_err);
522
cursor->c_close(cursor);
523
return BDB_WRAP(fs, "writing copied data", db_err);
528
526
/* Read the next chunk. Terminate loop if we're done. */
529
svn_fs_base__clear_dbt (&result);
530
db_err = cursor->c_get (cursor, &query, &result, DB_NEXT_DUP);
527
svn_fs_base__clear_dbt(&result);
528
db_err = cursor->c_get(cursor, &query, &result, DB_NEXT_DUP);
531
529
if (db_err == DB_NOTFOUND)
535
cursor->c_close (cursor);
536
return BDB_WRAP (fs, "fetching string data for a copy", db_err);
533
cursor->c_close(cursor);
534
return BDB_WRAP(fs, "fetching string data for a copy", db_err);
540
return BDB_WRAP (fs, "closing string-reading cursor",
541
cursor->c_close (cursor));
538
return BDB_WRAP(fs, "closing string-reading cursor",
539
cursor->c_close(cursor));