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);

README.md  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 `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 )