DB-Berkeley
view release on metacpan or search on metacpan
Berkeley.xs view on Meta::CPAN
// #define DEBUG_LOG(fmt, ...) fprintf(stderr, "[DB::Berkeley DEBUG] " fmt "\n", ##__VA_ARGS__)
#define DEBUG_LOG(fmt, ...)
/*
* Internal C struct to wrap a Berkeley DB handle.
* We'll store a pointer to this inside a Perl scalar reference.
*/
typedef struct {
DB *dbp; // Pointer to Berkeley DB handle
DBC *cursor; // for iterator
int readonly;
int sync_on_put; /* boolean flag */
int iteration_done; // Reached the end of an each() loop
} Berk;
typedef struct {
DBC *cursor;
DB *dbp;
} BerkIter;
/*
* Helper function to open a Berkeley DB file as a HASH.
* Takes filename, flags, and mode.
* Dies (croaks) on failure.
*/
static DB *
_bdb_open(const char *file, u_int32_t flags, int mode)
Berkeley.xs view on Meta::CPAN
mode = 0666;
dbp = _bdb_open(file, flags, mode); // Open Berkeley DB file
obj = (Berk *)malloc(sizeof(Berk));
if (!obj) {
dbp->close(dbp, 0);
croak("Out of memory");
}
obj->dbp = dbp;
obj->cursor = NULL;
obj->iteration_done = 0;
obj->sync_on_put = sync_on_put;
if(flags&DB_RDONLY) {
obj->readonly = 1;
} else {
obj->readonly = 0;
}
// Bless the object reference
Berkeley.xs view on Meta::CPAN
RETVAL = (ret == 0) ? 1 : 0;
OUTPUT:
RETVAL
AV *
keys(self)
SV *self
PREINIT:
Berk *obj;
DB *dbp;
DBC *cursor;
DBT key, val;
AV *av;
int ret;
CODE:
obj = (Berk *)SvIV(SvRV(self));
dbp = obj->dbp;
av = newAV();
// Clear key and value structures
memset(&key, 0, sizeof(DBT));
memset(&val, 0, sizeof(DBT));
// Open a cursor
ret = dbp->cursor(dbp, NULL, &cursor, 0);
if (ret != 0) {
croak("DB::Berkeley keys(): cursor open failed: %s", db_strerror(ret));
}
// Iterate over the database using the cursor
while ((ret = cursor->get(cursor, &key, &val, DB_NEXT)) == 0) {
av_push(av, newSVpvn((char *)key.data, key.size));
}
// DB_NOTFOUND means clean end
if (ret != DB_NOTFOUND) {
cursor->close(cursor);
croak("DB::Berkeley keys(): cursor iteration failed: %s", db_strerror(ret));
}
cursor->close(cursor);
RETVAL = av;
OUTPUT:
RETVAL
AV *
values(self)
SV *self
PREINIT:
Berk *obj;
DBC *cursor;
DBT k, v;
int ret;
AV *av;
CODE:
obj = (Berk *)SvIV(SvRV(self));
ret = obj->dbp->cursor(obj->dbp, NULL, &cursor, 0);
if (ret != 0) {
croak("db->cursor failed: %s", db_strerror(ret));
}
av = newAV();
memset(&k, 0, sizeof(DBT));
memset(&v, 0, sizeof(DBT));
while ((ret = cursor->get(cursor, &k, &v, DB_NEXT)) == 0) {
SV *sv = newSVpvn((char *)v.data, v.size);
av_push(av, sv);
}
if (ret != DB_NOTFOUND) {
cursor->close(cursor);
croak("cursor->get failed: %s", db_strerror(ret));
}
cursor->close(cursor);
RETVAL = av;
OUTPUT:
RETVAL
void
rewind(self)
SV *self
PREINIT:
Berk *obj;
int ret;
CODE:
obj = (Berk *)SvIV(SvRV(self));
// Close previous cursor if it exists
if (obj->cursor) {
obj->cursor->close(obj->cursor);
obj->cursor = NULL;
}
ret = obj->dbp->cursor(obj->dbp, NULL, &obj->cursor, 0);
if (ret != 0) {
croak("Failed to create cursor: %s", db_strerror(ret));
}
void
iterator_reset(self)
SV *self
PREINIT:
Berk *obj;
int ret;
CODE:
obj = (Berk *)SvIV(SvRV(self));
if (obj->cursor) {
obj->cursor->close(obj->cursor);
obj->cursor = NULL;
}
obj->iteration_done = 0;
ret = obj->dbp->cursor(obj->dbp, NULL, &obj->cursor, 0);
if (ret != 0) {
croak("iterator_reset: Failed to create cursor: %s", db_strerror(ret));
}
SV *
next_key(self)
SV *self
PREINIT:
Berk *obj;
DBT k, v;
int ret;
CODE:
obj = (Berk *)SvIV(SvRV(self));
if (!obj->cursor) {
croak("Iterator not initialized. Call iterator_reset() first.");
}
memset(&k, 0, sizeof(DBT));
memset(&v, 0, sizeof(DBT));
ret = obj->cursor->get(obj->cursor, &k, &v, DB_NEXT);
if (ret == DB_NOTFOUND) {
RETVAL = &PL_sv_undef;
} else if (ret != 0) {
croak("next_key: cursor->get failed: %s", db_strerror(ret));
} else {
RETVAL = newSVpvn((char *)k.data, k.size);
}
OUTPUT:
RETVAL
SV *
each(self)
SV *self
PREINIT:
Berkeley.xs view on Meta::CPAN
AV *av;
CODE:
obj = (Berk *)SvIV(SvRV(self));
if(obj->iteration_done) {
DEBUG_LOG("end() attempt to read beyond end of loop");
XSRETURN_EMPTY;
return;
}
if (!obj->cursor) {
// First call to each() â create cursor
ret = obj->dbp->cursor(obj->dbp, NULL, &obj->cursor, 0);
if (ret != 0) {
croak("each: cursor creation failed: %s", db_strerror(ret));
}
}
memset(&k, 0, sizeof(DBT));
memset(&v, 0, sizeof(DBT));
v.flags = DB_DBT_MALLOC;
ret = obj->cursor->get(obj->cursor, &k, &v, DB_NEXT);
if (ret == DB_NOTFOUND) {
obj->cursor->close(obj->cursor);
obj->cursor = NULL;
obj->iteration_done = 1;
DEBUG_LOG("end() end of loop");
XSRETURN_EMPTY;
} else if (ret != 0) {
croak("each: cursor->get failed: %s", db_strerror(ret));
} else {
av = newAV();
av_push(av, newSVpvn((char *)k.data, k.size));
av_push(av, newSVpvn((char *)v.data, v.size));
free(v.data);
RETVAL = newRV_noinc((SV *)av);
}
OUTPUT:
RETVAL
Berkeley.xs view on Meta::CPAN
BerkIter *it;
SV *ret;
int retcode;
CODE:
obj = (Berk *)SvIV(SvRV(self));
it = malloc(sizeof(BerkIter));
if (!it) croak("Out of memory");
it->dbp = obj->dbp;
it->cursor = NULL;
retcode = obj->dbp->cursor(obj->dbp, NULL, &it->cursor, 0);
if (retcode != 0) {
free(it);
croak("iterator: cursor creation failed: %s", db_strerror(retcode));
}
ret = sv_setref_pv(newSV(0), "DB::Berkeley::Iterator", (void*)it);
RETVAL = ret;
OUTPUT:
RETVAL
void
DESTROY(self)
SV *self
PREINIT:
Berk *obj;
int ret;
CODE:
obj = (Berk *)SvIV(SvRV(self));
DEBUG_LOG("DESTROY() called");
if (obj) {
if (obj->cursor) {
DEBUG_LOG("DESTROY() closing cursor");
obj->cursor->close(obj->cursor);
obj->cursor = NULL;
}
if (obj->dbp) {
DEBUG_LOG("DESTROY() closing handle");
obj->dbp->close(obj->dbp, 0); // Close DB handle
}
DEBUG_LOG("DESTROY() freeing the structure");
free(obj); // Free the struct
}
DEBUG_LOG("DESTROY() left");
Berkeley.xs view on Meta::CPAN
DBT k, v;
AV *av;
int ret;
CODE:
it = (BerkIter *)SvIV(SvRV(self));
memset(&k, 0, sizeof(DBT));
memset(&v, 0, sizeof(DBT));
v.flags = DB_DBT_MALLOC;
ret = it->cursor->get(it->cursor, &k, &v, DB_NEXT);
if (ret == DB_NOTFOUND) {
RETVAL = &PL_sv_undef;
} else if (ret != 0) {
croak("each(): cursor get failed: %s", db_strerror(ret));
} else {
av = newAV();
av_push(av, newSVpvn((char *)k.data, k.size));
av_push(av, newSVpvn((char *)v.data, v.size));
free(v.data);
RETVAL = newRV_noinc((SV *)av);
}
OUTPUT:
RETVAL
void
iterator_reset(self)
SV *self
PREINIT:
BerkIter *iter;
int ret;
CODE:
iter = (BerkIter *)SvIV(SvRV(self));
if (iter->cursor) {
iter->cursor->close(iter->cursor);
iter->cursor = NULL;
}
ret = iter->dbp->cursor(iter->dbp, NULL, &iter->cursor, 0);
if (ret != 0) {
croak("iterator_reset: cursor creation failed: %s", db_strerror(ret));
}
void
DESTROY(self)
SV *self
PREINIT:
BerkIter *it;
CODE:
it = (BerkIter *)SvIV(SvRV(self));
if (it->cursor) {
it->cursor->close(it->cursor);
}
free(it);
while (my $pair = $iter->each()) {
my ($key, $value) = @{$pair};
print "Key: $key, Value: $value\n";
}
You can reset the iterator using:
$iter->iterator_reset();
Note that calling `each()` or other iteration methods directly on the `$db` object
will use an internal cursor that is separate from the object returned by `iterator()`.
This is especially useful for nested iteration or concurrent traversal contexts.
## sync
$db->sync();
Flushes all pending writes to disk.
Useful for ensuring durability between critical updates.
lib/DB/Berkeley.pm view on Meta::CPAN
while (my $pair = $iter->each()) {
my ($key, $value) = @{$pair};
print "Key: $key, Value: $value\n";
}
You can reset the iterator using:
$iter->iterator_reset();
Note that calling C<each()> or other iteration methods directly on the C<$db> object
will use an internal cursor that is separate from the object returned by C<iterator()>.
This is especially useful for nested iteration or concurrent traversal contexts.
=head2 sync
$db->sync();
Flushes all pending writes to disk.
Useful for ensuring durability between critical updates.
( run in 1.436 second using v1.01-cache-2.11-cpan-beeb90c9504 )