~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_fs_base/bdb/strings-table.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* strings-table.c : operations on the `strings' table
 
2
 *
 
3
 * ====================================================================
 
4
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
5
 *
 
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.
 
11
 *
 
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
 * ====================================================================
 
16
 */
 
17
 
 
18
#include "bdb_compat.h"
 
19
#include "svn_fs.h"
 
20
#include "svn_pools.h"
 
21
#include "../fs.h"
 
22
#include "../err.h"
 
23
#include "dbt.h"
 
24
#include "../trail.h"
 
25
#include "../key-gen.h"
 
26
#include "../../libsvn_fs/fs-loader.h"
 
27
#include "bdb-err.h"
 
28
#include "strings-table.h"
 
29
 
 
30
#include "svn_private_config.h"
 
31
 
 
32
 
 
33
/*** Creating and opening the strings table. ***/
 
34
 
 
35
int
 
36
svn_fs_bdb__open_strings_table (DB **strings_p,
 
37
                                DB_ENV *env,
 
38
                                svn_boolean_t create)
 
39
{
 
40
  const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0);
 
41
  DB *strings;
 
42
 
 
43
  BDB_ERR (svn_fs_bdb__check_version());
 
44
  BDB_ERR (db_create (&strings, env, 0));
 
45
 
 
46
  /* Enable duplicate keys. This allows the data to be spread out across
 
47
     multiple records. Note: this must occur before ->open().  */
 
48
  BDB_ERR (strings->set_flags (strings, DB_DUP));
 
49
 
 
50
  BDB_ERR (strings->open (SVN_BDB_OPEN_PARAMS(strings, NULL),
 
51
                         "strings", 0, DB_BTREE,
 
52
                         open_flags | SVN_BDB_AUTO_COMMIT,
 
53
                         0666));
 
54
 
 
55
  if (create)
 
56
    {
 
57
      DBT key, value;
 
58
 
 
59
      /* Create the `next-key' table entry.  */
 
60
      BDB_ERR (strings->put
 
61
              (strings, 0,
 
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));
 
65
    }
 
66
 
 
67
  *strings_p = strings;
 
68
  return 0;
 
69
}
 
70
 
 
71
 
 
72
 
 
73
/*** Storing and retrieving strings.  ***/
 
74
 
 
75
/* Allocate *CURSOR and advance it to first row in the set of rows
 
76
   whose key is defined by QUERY.  Set *LENGTH to the size of that
 
77
   first row.  */
 
78
static svn_error_t *
 
79
locate_key (apr_size_t *length,
 
80
            DBC **cursor,
 
81
            DBT *query,
 
82
            svn_fs_t *fs,
 
83
            trail_t *trail,
 
84
            apr_pool_t *pool)
 
85
{
 
86
  base_fs_data_t *bfd = fs->fsap_data;
 
87
  int db_err;
 
88
  DBT result;
 
89
 
 
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,
 
93
                                           cursor, 0)));
 
94
 
 
95
  /* Set up the DBT for reading the length of the record. */
 
96
  svn_fs_base__clear_dbt (&result);
 
97
  result.ulen = 0;
 
98
  result.flags |= DB_DBT_USERMEM;
 
99
 
 
100
  /* Advance the cursor to the key that we're looking for. */
 
101
  db_err = (*cursor)->c_get (*cursor, query, &result, DB_SET);
 
102
 
 
103
  /* We don't need to svn_fs_base__track_dbt() the result, because nothing
 
104
     was allocated in it. */
 
105
 
 
106
  /* If there's no such node, return an appropriately specific error.  */
 
107
  if (db_err == DB_NOTFOUND)
 
108
    {
 
109
      (*cursor)->c_close (*cursor);
 
110
      return svn_error_createf
 
111
        (SVN_ERR_FS_NO_SUCH_STRING, 0,
 
112
         "No such string '%s'", (const char *)query->data);
 
113
    }
 
114
  if (db_err)
 
115
    {
 
116
      DBT rerun;
 
117
 
 
118
      if (db_err != SVN_BDB_DB_BUFFER_SMALL)
 
119
        {
 
120
          (*cursor)->c_close (*cursor);
 
121
          return BDB_WRAP (fs, "moving cursor", db_err);
 
122
        }
 
123
 
 
124
      /* We got an SVN_BDB_DB_BUFFER_SMALL (typical since we have a
 
125
         zero length buf), so we need to re-run the operation to make
 
126
         it happen. */
 
127
      svn_fs_base__clear_dbt (&rerun);
 
128
      rerun.flags |= DB_DBT_USERMEM | DB_DBT_PARTIAL;
 
129
      db_err = (*cursor)->c_get (*cursor, query, &rerun, DB_SET);
 
130
      if (db_err)
 
131
        {
 
132
          (*cursor)->c_close (*cursor);
 
133
          return BDB_WRAP (fs, "rerunning cursor move", db_err);
 
134
        }
 
135
    }
 
136
 
 
137
  /* ### this cast might not be safe? */
 
138
  *length = (apr_size_t) result.size;
 
139
 
 
140
  return SVN_NO_ERROR;
 
141
}
 
142
 
 
143
 
 
144
/* Advance CURSOR by a single row in the set of rows whose keys match
 
145
   CURSOR's current location.  Set *LENGTH to the size of that next
 
146
   row.  If any error occurs, CURSOR will be destroyed.  */
 
147
static int
 
148
get_next_length (apr_size_t *length, DBC *cursor, DBT *query)
 
149
{
 
150
  DBT result;
 
151
  int db_err;
 
152
 
 
153
  /* Set up the DBT for reading the length of the record. */
 
154
  svn_fs_base__clear_dbt (&result);
 
155
  result.ulen = 0;
 
156
  result.flags |= DB_DBT_USERMEM;
 
157
 
 
158
  /* Note: this may change the QUERY DBT, but that's okay: we're going
 
159
     to be sticking with the same key anyways.  */
 
160
  db_err = cursor->c_get (cursor, query, &result, DB_NEXT_DUP);
 
161
 
 
162
  /* Note that we exit on DB_NOTFOUND. The caller uses that to end a loop. */
 
163
  if (db_err)
 
164
    {
 
165
      DBT rerun;
 
166
 
 
167
      if (db_err != SVN_BDB_DB_BUFFER_SMALL)
 
168
        {
 
169
          cursor->c_close (cursor);
 
170
          return db_err;
 
171
        }
 
172
 
 
173
      /* We got an SVN_BDB_DB_BUFFER_SMALL (typical since we have a
 
174
         zero length buf), so we need to re-run the operation to make
 
175
         it happen. */
 
176
      svn_fs_base__clear_dbt (&rerun);
 
177
      rerun.flags |= DB_DBT_USERMEM | DB_DBT_PARTIAL;
 
178
      db_err = cursor->c_get (cursor, query, &rerun, DB_NEXT_DUP);
 
179
      if (db_err)
 
180
        cursor->c_close (cursor);
 
181
    }
 
182
 
 
183
  /* ### this cast might not be safe? */
 
184
  *length = (apr_size_t) result.size;
 
185
  return db_err;
 
186
}
 
187
 
 
188
 
 
189
svn_error_t *
 
190
svn_fs_bdb__string_read (svn_fs_t *fs,
 
191
                         const char *key,
 
192
                         char *buf,
 
193
                         svn_filesize_t offset,
 
194
                         apr_size_t *len,
 
195
                         trail_t *trail,
 
196
                         apr_pool_t *pool)
 
197
{
 
198
  int db_err;
 
199
  DBT query, result;
 
200
  DBC *cursor;
 
201
  apr_size_t length, bytes_read = 0;
 
202
 
 
203
  svn_fs_base__str_to_dbt (&query, key);
 
204
 
 
205
  SVN_ERR (locate_key (&length, &cursor, &query, fs, trail, pool));
 
206
 
 
207
  /* Seek through the records for this key, trying to find the record that
 
208
     includes OFFSET. Note that we don't require reading from more than
 
209
     one record since we're allowed to return partial reads.  */
 
210
  while (length <= offset)
 
211
    {
 
212
      offset -= length;
 
213
 
 
214
      /* Remember, if any error happens, our cursor has been closed
 
215
         for us. */
 
216
      db_err = get_next_length (&length, cursor, &query);
 
217
 
 
218
      /* No more records? They tried to read past the end. */
 
219
      if (db_err == DB_NOTFOUND)
 
220
        {
 
221
          *len = 0;
 
222
          return SVN_NO_ERROR;
 
223
        }
 
224
      if (db_err)
 
225
        return BDB_WRAP (fs, "reading string", db_err);
 
226
    }
 
227
 
 
228
  /* The current record contains OFFSET. Fetch the contents now. Note that
 
229
     OFFSET has been moved to be relative to this record. The length could
 
230
     quite easily extend past this record, so we use DB_DBT_PARTIAL and
 
231
     read successive records until we've filled the request.  */
 
232
  while (1)
 
233
    {
 
234
      svn_fs_base__clear_dbt (&result);
 
235
      result.data = buf + bytes_read;
 
236
      result.ulen = *len - bytes_read;
 
237
      result.doff = (u_int32_t)offset;
 
238
      result.dlen = *len - bytes_read;
 
239
      result.flags |= (DB_DBT_USERMEM | DB_DBT_PARTIAL);
 
240
      db_err = cursor->c_get (cursor, &query, &result, DB_CURRENT);
 
241
      if (db_err)
 
242
        {
 
243
          cursor->c_close (cursor);
 
244
          return BDB_WRAP (fs, "reading string", db_err);
 
245
        }
 
246
 
 
247
      bytes_read += result.size;
 
248
      if (bytes_read == *len)
 
249
        {
 
250
          /* Done with the cursor. */
 
251
          SVN_ERR (BDB_WRAP (fs, "closing string-reading cursor",
 
252
                            cursor->c_close (cursor)));
 
253
          break;
 
254
        }
 
255
 
 
256
      /* Remember, if any error happens, our cursor has been closed
 
257
         for us. */
 
258
      db_err = get_next_length (&length, cursor, &query);
 
259
      if (db_err == DB_NOTFOUND)
 
260
        break;
 
261
      if (db_err)
 
262
        return BDB_WRAP (fs, "reading string", db_err);
 
263
 
 
264
      /* We'll be reading from the beginning of the next record */
 
265
      offset = 0;
 
266
    }
 
267
 
 
268
  *len = bytes_read;
 
269
  return SVN_NO_ERROR;
 
270
}
 
271
 
 
272
 
 
273
/* Get the current 'next-key' value and bump the record. */
 
274
static svn_error_t *
 
275
get_key_and_bump (svn_fs_t *fs, 
 
276
                  const char **key, 
 
277
                  trail_t *trail,
 
278
                  apr_pool_t *pool)
 
279
{
 
280
  base_fs_data_t *bfd = fs->fsap_data;
 
281
  DBC *cursor;
 
282
  char next_key[MAX_KEY_SIZE];
 
283
  apr_size_t key_len;
 
284
  int db_err;
 
285
  DBT query;
 
286
  DBT result;
 
287
 
 
288
  /* ### todo: see issue #409 for why bumping the key as part of this
 
289
     trail is problematic. */
 
290
 
 
291
  /* Open a cursor and move it to the 'next-key' value. We can then fetch
 
292
     the contents and use the cursor to overwrite those contents. Since
 
293
     this database allows duplicates, we can't do an arbitrary 'put' to
 
294
     write the new value -- that would append, not overwrite.  */
 
295
 
 
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,
 
299
                                           &cursor, 0)));
 
300
 
 
301
  /* Advance the cursor to 'next-key' and read it. */
 
302
 
 
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),
 
306
                          DB_SET);
 
307
  if (db_err)
 
308
    {
 
309
      cursor->c_close (cursor);
 
310
      return BDB_WRAP (fs, "getting next-key value", db_err);
 
311
    }
 
312
 
 
313
  svn_fs_base__track_dbt (&result, pool);
 
314
  *key = apr_pstrmemdup (pool, result.data, result.size);
 
315
 
 
316
  /* Bump to future key. */
 
317
  key_len = result.size;
 
318
  svn_fs_base__next_key (result.data, &key_len, next_key);
 
319
 
 
320
  /* 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),
 
323
                          DB_CURRENT);
 
324
  if (db_err)
 
325
    {
 
326
      cursor->c_close (cursor); /* ignore the error, the original is
 
327
                                   more important. */
 
328
      return BDB_WRAP (fs, "bumping next string key", db_err);
 
329
    }
 
330
 
 
331
  return BDB_WRAP (fs, "closing string-reading cursor",
 
332
                  cursor->c_close (cursor));
 
333
}
 
334
 
 
335
svn_error_t *
 
336
svn_fs_bdb__string_append (svn_fs_t *fs,
 
337
                           const char **key,
 
338
                           apr_size_t len,
 
339
                           const char *buf,
 
340
                           trail_t *trail,
 
341
                           apr_pool_t *pool)
 
342
{
 
343
  base_fs_data_t *bfd = fs->fsap_data;
 
344
  DBT query, result;
 
345
 
 
346
  /* If the passed-in key is NULL, we graciously generate a new string
 
347
     using the value of the `next-key' record in the strings table. */
 
348
  if (*key == NULL)
 
349
    {
 
350
      SVN_ERR (get_key_and_bump (fs, key, trail, pool));
 
351
    }
 
352
 
 
353
  /* Store a new record into the database. */
 
354
  svn_fs_base__trail_debug (trail, "strings", "put");
 
355
  SVN_ERR (BDB_WRAP (fs, "appending string",
 
356
                     bfd->strings->put
 
357
                     (bfd->strings, trail->db_txn,
 
358
                      svn_fs_base__str_to_dbt (&query, *key),
 
359
                      svn_fs_base__set_dbt (&result, buf, len),
 
360
                      0)));
 
361
 
 
362
  return SVN_NO_ERROR;
 
363
}
 
364
 
 
365
 
 
366
svn_error_t *
 
367
svn_fs_bdb__string_clear (svn_fs_t *fs,
 
368
                          const char *key,
 
369
                          trail_t *trail,
 
370
                          apr_pool_t *pool)
 
371
{
 
372
  base_fs_data_t *bfd = fs->fsap_data;
 
373
  int db_err;
 
374
  DBT query, result;
 
375
 
 
376
  svn_fs_base__str_to_dbt (&query, key);
 
377
 
 
378
  /* 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);
 
381
 
 
382
  /* If there's no such node, return an appropriately specific error.  */
 
383
  if (db_err == DB_NOTFOUND)
 
384
    return svn_error_createf
 
385
      (SVN_ERR_FS_NO_SUCH_STRING, 0,
 
386
       "No such string '%s'", key);
 
387
 
 
388
  /* Handle any other error conditions.  */
 
389
  SVN_ERR (BDB_WRAP (fs, "clearing string", db_err));
 
390
 
 
391
  /* Shove empty data back in for this key. */
 
392
  svn_fs_base__clear_dbt (&result);
 
393
  result.data = 0;
 
394
  result.size = 0;
 
395
  result.flags |= DB_DBT_USERMEM;
 
396
 
 
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));
 
401
}
 
402
 
 
403
 
 
404
svn_error_t *
 
405
svn_fs_bdb__string_size (svn_filesize_t *size,
 
406
                         svn_fs_t *fs,
 
407
                         const char *key,
 
408
                         trail_t *trail,
 
409
                         apr_pool_t *pool)
 
410
{
 
411
  int db_err;
 
412
  DBT query;
 
413
  DBC *cursor;
 
414
  apr_size_t length;
 
415
  svn_filesize_t total;
 
416
 
 
417
  svn_fs_base__str_to_dbt (&query, key);
 
418
 
 
419
  SVN_ERR (locate_key (&length, &cursor, &query, fs, trail, pool));
 
420
 
 
421
  total = length;
 
422
  while (1)
 
423
    {
 
424
      /* Remember, if any error happens, our cursor has been closed
 
425
         for us. */
 
426
      db_err = get_next_length (&length, cursor, &query);
 
427
 
 
428
      /* No more records? Then return the total length. */
 
429
      if (db_err == DB_NOTFOUND)
 
430
        {
 
431
          *size = total;
 
432
          return SVN_NO_ERROR;
 
433
        }
 
434
      if (db_err)
 
435
        return BDB_WRAP (fs, "fetching string length", db_err);
 
436
 
 
437
      total += length;
 
438
    }
 
439
 
 
440
  /* NOTREACHED */
 
441
}
 
442
 
 
443
 
 
444
svn_error_t *
 
445
svn_fs_bdb__string_delete (svn_fs_t *fs,
 
446
                           const char *key,
 
447
                           trail_t *trail,
 
448
                           apr_pool_t *pool)
 
449
{
 
450
  base_fs_data_t *bfd = fs->fsap_data;
 
451
  int db_err;
 
452
  DBT query;
 
453
 
 
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);
 
457
 
 
458
  /* If there's no such node, return an appropriately specific error.  */
 
459
  if (db_err == DB_NOTFOUND)
 
460
    return svn_error_createf
 
461
      (SVN_ERR_FS_NO_SUCH_STRING, 0,
 
462
       "No such string '%s'", key);
 
463
 
 
464
  /* Handle any other error conditions.  */
 
465
  SVN_ERR (BDB_WRAP (fs, "deleting string", db_err));
 
466
 
 
467
  return SVN_NO_ERROR;
 
468
}
 
469
 
 
470
 
 
471
svn_error_t *
 
472
svn_fs_bdb__string_copy (svn_fs_t *fs,
 
473
                         const char **new_key,
 
474
                         const char *key,
 
475
                         trail_t *trail,
 
476
                         apr_pool_t *pool)
 
477
{
 
478
  base_fs_data_t *bfd = fs->fsap_data;
 
479
  DBT query;
 
480
  DBT result;
 
481
  DBT copykey;
 
482
  DBC *cursor;
 
483
  int db_err;
 
484
 
 
485
  /* Copy off the old key in case the caller is sharing storage
 
486
     between the old and new keys. */
 
487
  const char *old_key = apr_pstrdup (pool, key);
 
488
 
 
489
  SVN_ERR (get_key_and_bump (fs, new_key, trail, pool));
 
490
 
 
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,
 
494
                                           &cursor, 0)));
 
495
 
 
496
  svn_fs_base__str_to_dbt (&query, old_key);
 
497
  svn_fs_base__str_to_dbt (&copykey, *new_key);
 
498
 
 
499
  svn_fs_base__clear_dbt (&result);
 
500
 
 
501
  /* 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);
 
503
  if (db_err)
 
504
    {
 
505
      cursor->c_close (cursor);
 
506
      return BDB_WRAP (fs, "getting next-key value", db_err);
 
507
    }
 
508
 
 
509
  while (1)
 
510
    {
 
511
      /* ### can we pass a BDB-provided buffer to another BDB function?
 
512
         ### they are supposed to have a duration up to certain points
 
513
         ### of calling back into BDB, but I'm not sure what the exact
 
514
         ### rules are. it is definitely nicer to use BDB buffers here
 
515
         ### to simplify things and reduce copies, but... hrm.
 
516
      */
 
517
 
 
518
      /* 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
                                  &copykey, &result, 0);
 
522
      if (db_err)
 
523
        {
 
524
          cursor->c_close (cursor);
 
525
          return BDB_WRAP (fs, "writing copied data", db_err);
 
526
        }
 
527
 
 
528
      /* 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);
 
531
      if (db_err == DB_NOTFOUND)
 
532
        break;
 
533
      if (db_err)
 
534
        {
 
535
          cursor->c_close (cursor);
 
536
          return BDB_WRAP (fs, "fetching string data for a copy", db_err);
 
537
        }
 
538
    }
 
539
 
 
540
  return BDB_WRAP (fs, "closing string-reading cursor",
 
541
                  cursor->c_close (cursor));
 
542
}