This makes transition from tdb1 much simpler.
ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
if (ecode != TDB_SUCCESS) {
ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
if (ecode != TDB_SUCCESS) {
+ return tdb->last_error = ecode;
}
ecode = tdb_lock_expand(tdb, F_RDLCK);
if (ecode != TDB_SUCCESS) {
tdb_allrecord_unlock(tdb, F_RDLCK);
}
ecode = tdb_lock_expand(tdb, F_RDLCK);
if (ecode != TDB_SUCCESS) {
tdb_allrecord_unlock(tdb, F_RDLCK);
+ return tdb->last_error = ecode;
}
ecode = check_header(tdb, &recovery, &features);
}
ecode = check_header(tdb, &recovery, &features);
tdb_unlock_expand(tdb, F_RDLCK);
free(fr);
free(used);
tdb_unlock_expand(tdb, F_RDLCK);
free(fr);
free(used);
+ return tdb->last_error = ecode;
contention - it cannot guarantee how many records will be locked */
enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
{
contention - it cannot guarantee how many records will be locked */
enum TDB_ERROR tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
{
- return chainlock(tdb, &key, F_WRLCK, TDB_LOCK_WAIT, "tdb_chainlock");
+ return tdb->last_error = chainlock(tdb, &key, F_WRLCK, TDB_LOCK_WAIT,
+ "tdb_chainlock");
}
enum TDB_ERROR tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
}
enum TDB_ERROR tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
lockstart = hlock_range(group, &locksize);
tdb_trace_1rec(tdb, "tdb_chainunlock", key);
lockstart = hlock_range(group, &locksize);
tdb_trace_1rec(tdb, "tdb_chainunlock", key);
- return tdb_unlock_hashes(tdb, lockstart, locksize, F_WRLCK);
+ return tdb->last_error = tdb_unlock_hashes(tdb, lockstart, locksize,
+ F_WRLCK);
tdb->transaction = NULL;
tdb->stats = NULL;
tdb->access = NULL;
tdb->transaction = NULL;
tdb->stats = NULL;
tdb->access = NULL;
+ tdb->last_error = TDB_SUCCESS;
tdb->file = NULL;
tdb_hash_init(tdb);
tdb_io_init(tdb);
tdb->file = NULL;
tdb_hash_init(tdb);
tdb_io_init(tdb);
/* Direct access information */
struct tdb_access_hdr *access;
/* Direct access information */
struct tdb_access_hdr *access;
+ /* Last error we returned. */
+ enum TDB_ERROR last_error;
+
/* The actual file information */
struct tdb_file *file;
};
/* The actual file information */
struct tdb_file *file;
};
ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
if (ecode != TDB_SUCCESS) {
ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
if (ecode != TDB_SUCCESS) {
+ return tdb->last_error = ecode;
}
ecode = tdb_lock_expand(tdb, F_RDLCK);
if (ecode != TDB_SUCCESS) {
tdb_allrecord_unlock(tdb, F_RDLCK);
}
ecode = tdb_lock_expand(tdb, F_RDLCK);
if (ecode != TDB_SUCCESS) {
tdb_allrecord_unlock(tdb, F_RDLCK);
+ return tdb->last_error = ecode;
}
/* Start stats off empty. */
}
/* Start stats off empty. */
tdb_allrecord_unlock(tdb, F_RDLCK);
tdb_unlock_expand(tdb, F_RDLCK);
tdb_allrecord_unlock(tdb, F_RDLCK);
tdb_unlock_expand(tdb, F_RDLCK);
+ return tdb->last_error = ecode;
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
+ return tdb->last_error = off;
}
/* Now we have lock on this hash bucket. */
}
/* Now we have lock on this hash bucket. */
}
tdb_unlock_hashes(tdb, h.hlock_start,
h.hlock_range, F_WRLCK);
}
tdb_unlock_hashes(tdb, h.hlock_start,
h.hlock_range, F_WRLCK);
+ return tdb->last_error = TDB_SUCCESS;
}
} else {
if (flag == TDB_MODIFY) {
}
} else {
if (flag == TDB_MODIFY) {
ecode = replace_data(tdb, &h, key, dbuf, off, old_room, off);
out:
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
ecode = replace_data(tdb, &h, key, dbuf, off, old_room, off);
out:
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
+ return tdb->last_error = ecode;
}
enum TDB_ERROR tdb_append(struct tdb_context *tdb,
}
enum TDB_ERROR tdb_append(struct tdb_context *tdb,
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
+ return tdb->last_error = off;
free(newdata);
out:
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
free(newdata);
out:
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
+ return tdb->last_error = ecode;
}
enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
}
enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key,
off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
+ return tdb->last_error = off;
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
+ return tdb->last_error = ecode;
}
bool tdb_exists(struct tdb_context *tdb, TDB_DATA key)
}
bool tdb_exists(struct tdb_context *tdb, TDB_DATA key)
off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
return false;
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
return false;
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
+ tdb->last_error = TDB_SUCCESS;
return off ? true : false;
}
return off ? true : false;
}
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
+ return tdb->last_error = off;
unlock:
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
unlock:
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_WRLCK);
+ return tdb->last_error = ecode;
}
unsigned int tdb_get_flags(struct tdb_context *tdb)
}
unsigned int tdb_get_flags(struct tdb_context *tdb)
void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
{
if (tdb->flags & TDB_INTERNAL) {
void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
{
if (tdb->flags & TDB_INTERNAL) {
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_add_flag: internal db");
+ tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_add_flag: internal db");
return;
}
switch (flag) {
return;
}
switch (flag) {
tdb->flags |= TDB_SEQNUM;
break;
default:
tdb->flags |= TDB_SEQNUM;
break;
default:
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_add_flag: Unknown flag %u", flag);
+ tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_add_flag: Unknown flag %u",
+ flag);
}
}
void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
{
if (tdb->flags & TDB_INTERNAL) {
}
}
void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
{
if (tdb->flags & TDB_INTERNAL) {
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_remove_flag: internal db");
+ tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_remove_flag: internal db");
return;
}
switch (flag) {
return;
}
switch (flag) {
tdb->flags &= ~TDB_SEQNUM;
break;
default:
tdb->flags &= ~TDB_SEQNUM;
break;
default:
- tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_remove_flag: Unknown flag %u", flag);
+ tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_remove_flag: Unknown flag %u",
+ flag);
off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL);
if (TDB_OFF_IS_ERR(off)) {
+ return tdb->last_error = off;
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
+ return tdb->last_error = ecode;
}
const char *tdb_name(const struct tdb_context *tdb)
}
const char *tdb_name(const struct tdb_context *tdb)
int64_t tdb_get_seqnum(struct tdb_context *tdb)
{
int64_t tdb_get_seqnum(struct tdb_context *tdb)
{
- return tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
+ tdb_off_t off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum));
+ if (TDB_OFF_IS_ERR(off))
+ tdb->last_error = off;
+ else
+ tdb->last_error = TDB_SUCCESS;
+ return off;
void *private),
void *private);
void *private),
void *private);
+/**
+ * tdb_error - get the last error (not threadsafe)
+ * @tdb: the tdb context returned from tdb_open()
+ *
+ * Returns the last error returned by a TDB function.
+ *
+ * This makes porting from TDB1 easier, but note that the last error is not
+ * reliable in threaded programs.
+ */
+enum TDB_ERROR tdb_error(struct tdb_context *tdb);
+
/**
* enum tdb_summary_flags - flags for tdb_summary.
*/
/**
* enum tdb_summary_flags - flags for tdb_summary.
*/
/* some sanity checks */
if (tdb->read_only || (tdb->flags & TDB_INTERNAL)) {
/* some sanity checks */
if (tdb->read_only || (tdb->flags & TDB_INTERNAL)) {
- return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_transaction_start: cannot start a"
- " transaction on a read-only or internal db");
+ return tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_transaction_start:"
+ " cannot start a"
+ " transaction on a "
+ "read-only or internal db");
}
/* cope with nested tdb_transaction_start() calls */
if (tdb->transaction != NULL) {
}
/* cope with nested tdb_transaction_start() calls */
if (tdb->transaction != NULL) {
- return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_USE_ERROR,
- "tdb_transaction_start:"
- " already inside transaction");
+ return tdb->last_error = tdb_logerr(tdb, TDB_ERR_IO,
+ TDB_LOG_USE_ERROR,
+ "tdb_transaction_start:"
+ " already inside"
+ " transaction");
}
if (tdb_has_hash_locks(tdb)) {
/* the caller must not have any locks when starting a
transaction as otherwise we'll be screwed by lack
of nested locks in POSIX */
}
if (tdb_has_hash_locks(tdb)) {
/* the caller must not have any locks when starting a
transaction as otherwise we'll be screwed by lack
of nested locks in POSIX */
- return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
- "tdb_transaction_start: cannot start a"
- " transaction with locks held");
+ return tdb->last_error = tdb_logerr(tdb, TDB_ERR_LOCK,
+ TDB_LOG_USE_ERROR,
+ "tdb_transaction_start:"
+ " cannot start a"
+ " transaction with locks"
+ " held");
}
tdb->transaction = (struct tdb_transaction *)
calloc(sizeof(struct tdb_transaction), 1);
if (tdb->transaction == NULL) {
}
tdb->transaction = (struct tdb_transaction *)
calloc(sizeof(struct tdb_transaction), 1);
if (tdb->transaction == NULL) {
- return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
- "tdb_transaction_start: cannot allocate");
+ return tdb->last_error = tdb_logerr(tdb, TDB_ERR_OOM,
+ TDB_LOG_ERROR,
+ "tdb_transaction_start:"
+ " cannot allocate");
}
/* get the transaction write lock. This is a blocking lock. As
}
/* get the transaction write lock. This is a blocking lock. As
if (ecode != TDB_SUCCESS) {
SAFE_FREE(tdb->transaction->blocks);
SAFE_FREE(tdb->transaction);
if (ecode != TDB_SUCCESS) {
SAFE_FREE(tdb->transaction->blocks);
SAFE_FREE(tdb->transaction);
+ return tdb->last_error = ecode;
}
/* get a read lock over entire file. This is upgraded to a write
}
/* get a read lock over entire file. This is upgraded to a write
transaction specific methods */
tdb->transaction->io_methods = tdb->methods;
tdb->methods = &transaction_methods;
transaction specific methods */
tdb->transaction->io_methods = tdb->methods;
tdb->methods = &transaction_methods;
+ return tdb->last_error = TDB_SUCCESS;
fail_allrecord_lock:
tdb_transaction_unlock(tdb, F_WRLCK);
SAFE_FREE(tdb->transaction->blocks);
SAFE_FREE(tdb->transaction);
fail_allrecord_lock:
tdb_transaction_unlock(tdb, F_WRLCK);
SAFE_FREE(tdb->transaction->blocks);
SAFE_FREE(tdb->transaction);
+ return tdb->last_error = ecode;
enum TDB_ERROR ecode;
if (tdb->transaction == NULL) {
enum TDB_ERROR ecode;
if (tdb->transaction == NULL) {
- return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
- "tdb_transaction_commit: no transaction");
+ return tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_transaction_commit:"
+ " no transaction");
}
tdb_trace(tdb, "tdb_transaction_commit");
if (tdb->transaction->nesting != 0) {
tdb->transaction->nesting--;
}
tdb_trace(tdb, "tdb_transaction_commit");
if (tdb->transaction->nesting != 0) {
tdb->transaction->nesting--;
+ return tdb->last_error = TDB_SUCCESS;
}
/* check for a null transaction */
if (tdb->transaction->blocks == NULL) {
_tdb_transaction_cancel(tdb);
}
/* check for a null transaction */
if (tdb->transaction->blocks == NULL) {
_tdb_transaction_cancel(tdb);
+ return tdb->last_error = TDB_SUCCESS;
}
if (!tdb->transaction->prepared) {
ecode = _tdb_transaction_prepare_commit(tdb);
if (ecode != TDB_SUCCESS)
}
if (!tdb->transaction->prepared) {
ecode = _tdb_transaction_prepare_commit(tdb);
if (ecode != TDB_SUCCESS)
+ return tdb->last_error = ecode;
}
methods = tdb->transaction->io_methods;
}
methods = tdb->transaction->io_methods;
_tdb_transaction_cancel(tdb);
_tdb_transaction_cancel(tdb);
+ return tdb->last_error = ecode;
}
SAFE_FREE(tdb->transaction->blocks[i]);
}
}
SAFE_FREE(tdb->transaction->blocks[i]);
}
/* ensure the new data is on disk */
ecode = transaction_sync(tdb, 0, tdb->file->map_size);
if (ecode != TDB_SUCCESS) {
/* ensure the new data is on disk */
ecode = transaction_sync(tdb, 0, tdb->file->map_size);
if (ecode != TDB_SUCCESS) {
+ return tdb->last_error = ecode;
transaction locks */
_tdb_transaction_cancel(tdb);
transaction locks */
_tdb_transaction_cancel(tdb);
+ return tdb->last_error = TDB_SUCCESS;
count++;
if (fn && fn(tdb, k, d, p)) {
free(k.dptr);
count++;
if (fn && fn(tdb, k, d, p)) {
free(k.dptr);
+ tdb->last_error = TDB_SUCCESS;
return count;
}
free(k.dptr);
}
if (ecode != TDB_ERR_NOEXIST) {
return count;
}
free(k.dptr);
}
if (ecode != TDB_ERR_NOEXIST) {
+ return tdb->last_error = ecode;
+ tdb->last_error = TDB_SUCCESS;
{
struct traverse_info tinfo;
{
struct traverse_info tinfo;
- return first_in_hash(tdb, &tinfo, key, NULL);
+ return tdb->last_error = first_in_hash(tdb, &tinfo, key, NULL);
}
/* We lock twice, not very efficient. We could keep last key & tinfo cached. */
}
/* We lock twice, not very efficient. We could keep last key & tinfo cached. */
tinfo.prev = find_and_lock(tdb, *key, F_RDLCK, &h, &rec, &tinfo);
free(key->dptr);
if (TDB_OFF_IS_ERR(tinfo.prev)) {
tinfo.prev = find_and_lock(tdb, *key, F_RDLCK, &h, &rec, &tinfo);
free(key->dptr);
if (TDB_OFF_IS_ERR(tinfo.prev)) {
+ return tdb->last_error = tinfo.prev;
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
}
tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK);
- return next_in_hash(tdb, &tinfo, key, NULL);
+ return tdb->last_error = next_in_hash(tdb, &tinfo, key, NULL);
}
static int wipe_one(struct tdb_context *tdb,
}
static int wipe_one(struct tdb_context *tdb,
ecode = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
if (ecode != TDB_SUCCESS)
ecode = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
if (ecode != TDB_SUCCESS)
+ return tdb->last_error = ecode;
/* FIXME: Be smarter. */
count = tdb_traverse(tdb, wipe_one, &ecode);
if (count < 0)
ecode = count;
tdb_allrecord_unlock(tdb, F_WRLCK);
/* FIXME: Be smarter. */
count = tdb_traverse(tdb, wipe_one, &ecode);
if (count < 0)
ecode = count;
tdb_allrecord_unlock(tdb, F_WRLCK);
+ return tdb->last_error = ecode;