DB_File.xs revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
DB_File.xs -- Perl 5 interface to Berkeley DB
written by Paul Marquess <Paul.Marquess@btinternet.com>
last modified 17 December 2000
version 1.75
All comments/suggestions/problems are welcome
Copyright (c) 1995-2000 Paul Marquess. All rights reserved.
modify it under the same terms as Perl itself.
Changes:
0.1 - Initial Release
0.2 - No longer bombs out if dbopen returns an error.
0.3 - Added some support for multiple btree compares
1.0 - Complete support for multiple callbacks added.
Fixed a problem with pushing a value onto an empty list.
1.01 - Fixed a SunOS core dump problem.
The return value from TIEHASH wasn't set to NULL when
dbopen returned an error.
1.02 - Use ALIAS to define TIEARRAY.
Removed some redundant commented code.
Merged OS2 code into the main distribution.
Allow negative subscripts with RECNO interface.
Changed the default flags to O_CREAT|O_RDWR
1.03 - Added EXISTS
1.04 - fixed a couple of bugs in hash_cb. Patches supplied by
Dave Hammen, hammen@gothamcity.jsc.nasa.gov
1.05 - Added logic to allow prefix & hash types to be specified via
1.06 - Minor namespace cleanup: Localized PrintBtree.
1.07 - Fixed bug with RECNO, where bval wasn't defaulting to "\n".
1.08 - No change to DB_File.xs
1.09 - Default mode for dbopen changed to 0666
1.10 - Fixed fd method so that it still returns -1 for
in-memory files when db 1.86 is used.
1.11 - No change to DB_File.xs
1.12 - No change to DB_File.xs
1.13 - Tidied up a few casts.
1.14 - Made it illegal to tie an associative array to a RECNO
database and an ordinary array to a HASH or BTREE database.
1.50 - Make work with both DB 1.x or DB 2.x
1.51 - Fixed a bug in mapping 1.x O_RDONLY flag to 2.x DB_RDONLY equivalent
1.52 - Patch from Gisle Aas <gisle@aas.no> to suppress "use of
undefined value" warning with db_get and db_seq.
1.53 - Added DB_RENUMBER to flags for recno.
1.54 - Fixed bug in the fd method
1.55 - Fix for AIX from Jarkko Hietaniemi
1.56 - No change to DB_File.xs
1.57 - added the #undef op to allow building with Threads support.
1.58 - Fixed a problem with the use of sv_setpvn. When the
size is specified as 0, it does a strlen on the data.
This was ok for DB 1.x, but isn't for DB 2.x.
1.59 - No change to DB_File.xs
1.60 - Some code tidy up
1.61 - added flagSet macro for DB 2.5.x
fixed typo in O_RDONLY test.
1.62 - No change to DB_File.xs
1.63 - Fix to alllow DB 2.6.x to build.
1.64 - Tidied up the 1.x to 2.x flags mapping code.
Added a patch from Mark Kettenis <kettenis@wins.uva.nl>
to fix a flag mapping problem with O_RDONLY on the Hurd
1.65 - Fixed a bug in the PUSH logic.
Added BOOT check that using 2.3.4 or greater
1.66 - Added DBM filter code
1.67 - Backed off the use of newSVpvn.
Fixed DBM Filter code for Perl 5.004.
Fixed a small memory leak in the filter code.
1.68 - fixed backward compatability bug with R_IAFTER & R_IBEFORE
merged in the 5.005_58 changes
1.69 - fixed a bug in push -- DB_APPEND wasn't working properly.
Fixed the R_SETCURSOR bug introduced in 1.68
Added a new Perl variable $DB_File::db_ver
1.70 - Initialise $DB_File::db_ver and $DB_File::db_version with
GV_ADD|GV_ADDMULT -- bug spotted by Nick Ing-Simmons.
Added a BOOT check to test for equivalent versions of db.h &
1.71 - Support for Berkeley DB version 3.
Support for Berkeley DB 2/3's backward compatability mode.
Rewrote push
1.72 - No change to DB_File.xs
1.73 - No change to DB_File.xs
1.74 - A call to open needed parenthesised to stop it clashing
with a win32 macro.
Added Perl core patches 7703 & 7801.
1.75 - Fixed Perl core patch 7703.
Added suppport to allow DB_File to be built with
Berkeley DB 3.2 -- btree_compare, btree_prefix and hash_cb
needed to be changed.
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifndef PERL_VERSION
# include "patchlevel.h"
# define PERL_REVISION 5
# define PERL_VERSION PATCHLEVEL
# define PERL_SUBVERSION SUBVERSION
#endif
# define PL_sv_undef sv_undef
#endif
/* DEFSV appears first in 5.004_56 */
#ifndef DEFSV
#endif
* shortly #included by the <db.h>) __attribute__ to the possibly
* already defined __attribute__, for example by GNUC or by Perl. */
/* If Perl has been compiled with Threads support,the symbol op will
be defined here. This clashes with a field name in db.h, so get rid of it.
*/
#ifdef op
#endif
#ifdef COMPAT185
# include <db_185.h>
#else
# include <db.h>
#endif
#ifdef CAN_PROTOTYPE
extern void __getBerkeleyDBInfo(void);
#endif
#ifndef pTHX
# define pTHX
# define pTHX_
# define aTHX
# define aTHX_
#endif
#ifndef newSVpvn
#endif
#include <fcntl.h>
/* #define TRACE */
#define DBM_FILTERING
#ifdef TRACE
#else
# define Trace(x)
#endif
#ifdef DB_VERSION_MAJOR
#if DB_VERSION_MAJOR == 2
# define BERKELEY_DB_1_OR_2
#endif
# define AT_LEAST_DB_3_2
#endif
/* map version 2 features & constants onto their version 1 equivalent */
#ifdef DB_Prefix_t
#endif
#define DB_Prefix_t size_t
#ifdef DB_Hash_t
#endif
/* DBTYPE stays the same */
/* HASHINFO, RECNOINFO and BTREEINFO map to DB_INFO */
#if DB_VERSION_MAJOR == 2
#else /* DB_VERSION_MAJOR > 2 */
# define DB_FIXEDLEN (0x8000)
#endif /* DB_VERSION_MAJOR == 2 */
/* version 2 has db_recno_t in place of recno_t */
typedef db_recno_t recno_t;
#define R_CURSOR DB_SET_RANGE
#define R_NOOVERWRITE DB_NOOVERWRITE
# define R_SETCURSOR 0x800000
#else
# define R_SETCURSOR (-100)
#endif
#define R_RECNOSYNC 0
#define R_FIXEDLEN DB_FIXEDLEN
#define db_HA_hash h_hash
#define db_HA_ffactor h_ffactor
#define db_HA_nelem h_nelem
#define db_HA_bsize db_pagesize
#define db_HA_cachesize db_cachesize
#define db_HA_lorder db_lorder
#define db_BT_compare bt_compare
#define db_BT_prefix bt_prefix
#define db_BT_flags flags
#define db_BT_psize db_pagesize
#define db_BT_cachesize db_cachesize
#define db_BT_lorder db_lorder
#define db_BT_maxkeypage
#define db_BT_minkeypage
#define db_RE_reclen re_len
#define db_RE_flags flags
#define db_RE_bval re_pad
#define db_RE_bfname re_source
#define db_RE_psize db_pagesize
#define db_RE_cachesize db_cachesize
#define db_RE_lorder db_lorder
#define DB_flags(x, v) x |= v
#else
#endif
#else /* db version 1.x */
#define BERKELEY_DB_1
#define BERKELEY_DB_1_OR_2
typedef union INFO {
} INFO ;
#ifdef mDB_Prefix_t
# ifdef DB_Prefix_t
# endif
# define DB_Prefix_t mDB_Prefix_t
#endif
#ifdef mDB_Hash_t
# ifdef DB_Hash_t
# endif
# define DB_Hash_t mDB_Hash_t
#endif
#define TXN
#define DBT_flags(x)
#define DB_flags(x, v)
#endif /* db version 1 */
#ifdef DB_VERSION_MAJOR
#else /* ! DB_VERSION_MAJOR */
#endif /* ! DB_VERSION_MAJOR */
typedef struct {
int in_memory ;
#ifdef BERKELEY_DB_1_OR_2
#endif
#ifdef DB_VERSION_MAJOR
#endif
#ifdef DBM_FILTERING
SV * filter_fetch_key ;
SV * filter_store_key ;
SV * filter_fetch_value ;
SV * filter_store_value ;
int filtering ;
#endif /* DBM_FILTERING */
} DB_File_type;
typedef DB_File_type * DB_File ;
#ifdef DBM_FILTERING
SV * save_defsv ; \
/* printf("filtering %s\n", name) ;*/ \
SvREFCNT_dec(save_defsv) ; \
/*printf("end of filtering %s\n", name) ;*/ \
}
#else
#endif /* DBM_FILTERING */
{ if (RETVAL == 0) { \
} \
}
{ if (RETVAL == 0) \
{ \
} \
else \
} \
}
/* Internal Global Data */
#ifdef DB_VERSION_MAJOR
static int
#ifdef CAN_PROTOTYPE
#else
#endif
{
int status ;
DBC * temp_cursor ;
#else
#endif
return (-1) ;
return (-1);
}
return (status) ;
}
}
return -1 ;
}
}
#endif /* DB_VERSION_MAJOR */
static int
#ifdef AT_LEAST_DB_3_2
#ifdef CAN_PROTOTYPE
#else
#endif /* CAN_PROTOTYPE */
#else /* Berkeley DB < 3.2 */
#ifdef CAN_PROTOTYPE
#else
#endif
#endif
{
#ifdef dTHX
dTHX;
#endif
dSP ;
int retval ;
int count ;
#ifndef newSVpvn
/* As newSVpv will assume that the data pointer is a null terminated C
string if the size parameter is 0, make sure that data points to an
empty string if the length is 0
*/
data1 = "" ;
data2 = "" ;
#endif
ENTER ;
PUTBACK ;
SPAGAIN ;
if (count != 1)
PUTBACK ;
FREETMPS ;
LEAVE ;
return (retval) ;
}
static DB_Prefix_t
#ifdef AT_LEAST_DB_3_2
#ifdef CAN_PROTOTYPE
#else
#endif
#else /* Berkeley DB < 3.2 */
#ifdef CAN_PROTOTYPE
#else
#endif
#endif
{
#ifdef dTHX
dTHX;
#endif
dSP ;
int retval ;
int count ;
#ifndef newSVpvn
/* As newSVpv will assume that the data pointer is a null terminated C
string if the size parameter is 0, make sure that data points to an
empty string if the length is 0
*/
data1 = "" ;
data2 = "" ;
#endif
ENTER ;
PUTBACK ;
SPAGAIN ;
if (count != 1)
PUTBACK ;
FREETMPS ;
LEAVE ;
return (retval) ;
}
#ifdef BERKELEY_DB_1
# define HASH_CB_SIZE_TYPE size_t
#else
# define HASH_CB_SIZE_TYPE u_int32_t
#endif
static DB_Hash_t
#ifdef AT_LEAST_DB_3_2
#ifdef CAN_PROTOTYPE
#else
const void * data ;
#endif
#else /* Berkeley DB < 3.2 */
#ifdef CAN_PROTOTYPE
#else
const void * data ;
#endif
#endif
{
#ifdef dTHX
dTHX;
#endif
dSP ;
int retval ;
int count ;
#ifndef newSVpvn
if (size == 0)
data = "" ;
#endif
/* DGH - Next two lines added to fix corrupted stack problem */
ENTER ;
PUTBACK ;
SPAGAIN ;
if (count != 1)
PUTBACK ;
FREETMPS ;
LEAVE ;
return (retval) ;
}
#if defined(TRACE) && defined(BERKELEY_DB_1_OR_2)
static void
#ifdef CAN_PROTOTYPE
#else
#endif
{
printf ("HASH Info\n") ;
printf (" hash = %s\n",
}
static void
#ifdef CAN_PROTOTYPE
#else
#endif
{
printf ("RECNO Info\n") ;
}
static void
#ifdef CAN_PROTOTYPE
#else
#endif
{
printf ("BTREE Info\n") ;
printf (" compare = %s\n",
printf (" prefix = %s\n",
#ifndef DB_VERSION_MAJOR
#endif
}
#else
#define PrintRecno(recno)
#define PrintBtree(btree)
#endif /* TRACE */
static I32
#ifdef CAN_PROTOTYPE
#else
#endif
{
int RETVAL ;
if (RETVAL == 0)
else /* No key means empty file */
RETVAL = 0 ;
}
static recno_t
#ifdef CAN_PROTOTYPE
#else
#endif
{
if (value < 0) {
/* Get the length of the array */
/* check for attempt to write before start of array */
}
else
++ value ;
return value ;
}
static DB_File
#ifdef CAN_PROTOTYPE
#else
int isHASH ;
char * name ;
int flags ;
int mode ;
#endif
{
#ifdef BERKELEY_DB_1_OR_2 /* Berkeley DB Version 1 or 2 */
/* printf("In ParseOpenInfo name=[%s] flags=[%d] mode = [%d]\n", name, flags, mode) ; */
/* Default to HASH */
#ifdef DBM_FILTERING
#endif /* DBM_FILTERING */
/* DGH - Next line added to avoid SEGV on existing hash DB */
/* fd for 1.86 hash in memory files doesn't return -1 like 1.85 */
if (sv)
{
croak ("type parameter is not a reference") ;
else
croak("internal error") ;
{
if (!isHASH)
croak("DB_File can only tie an associative array to a DB_HASH database") ;
{
}
else
}
{
if (!isHASH)
croak("DB_File can only tie an associative array to a DB_BTREE database");
{
}
else
{
}
else
#ifndef DB_VERSION_MAJOR
#endif
PrintBtree(info) ;
}
{
if (isHASH)
croak("DB_File can only tie an array to a DB_RECNO database");
info->db_RE_flags = 0 ;
#ifdef DB_VERSION_MAJOR
#endif
#ifdef DB_VERSION_MAJOR
#else
#endif
}
else
#ifdef DB_VERSION_MAJOR
#else
#endif
#ifdef DB_VERSION_MAJOR
{
int value ;
else
}
else {
}
}
#else
{
else
}
else
{
else
}
#endif
#ifdef DB_RENUMBER
#endif
PrintRecno(info) ;
}
else
croak("type is not of type DB_File::HASHINFO, DB_File::BTREEINFO or DB_File::RECNOINFO");
}
/* OS2 Specific Code */
#ifdef OS2
#ifdef __EMX__
#endif /* __EMX__ */
#endif /* OS2 */
#ifdef DB_VERSION_MAJOR
{
int Flags = 0 ;
int status ;
/* Map 1.x flags to 2.x flags */
#if O_RDONLY == 0
#else
#endif
#ifdef O_TRUNC
Flags |= DB_TRUNCATE ;
#endif
if (status == 0)
#else
0) ;
#endif
if (status)
}
#else
#else
#endif /* DB_LIBRARY_COMPATIBILITY_API */
#endif
return (RETVAL) ;
#else /* Berkeley DB Version > 2 */
int status ;
/* printf("In ParseOpenInfo name=[%s] flags=[%d] mode = [%d]\n", name, flags, mode) ; */
/* Default to HASH */
#ifdef DBM_FILTERING
#endif /* DBM_FILTERING */
/* DGH - Next line added to avoid SEGV on existing hash DB */
/* fd for 1.86 hash in memory files doesn't return -1 like 1.85 */
/* printf("db_create returned %d %s\n", status, db_strerror(status)) ; */
if (status) {
return (RETVAL) ;
}
if (sv)
{
croak ("type parameter is not a reference") ;
else
croak("internal error") ;
{
if (!isHASH)
croak("DB_File can only tie an associative array to a DB_HASH database") ;
{
}
if (svp)
if (svp)
if (svp)
if (svp)
if (svp)
}
{
if (!isHASH)
croak("DB_File can only tie an associative array to a DB_BTREE database");
{
}
{
}
if (svp)
if (svp)
if (svp)
if (svp)
PrintBtree(info) ;
}
{
if (isHASH)
croak("DB_File can only tie an array to a DB_RECNO database");
if (svp) {
/* remove FIXDLEN, if present */
if (flags & DB_FIXEDLEN) {
flags &= ~DB_FIXEDLEN ;
}
}
if (svp) {
}
if (svp) {
}
if (svp) {
}
{
int value ;
else
if (fixed) {
}
else {
}
}
if (fixed) {
if (svp) {
}
}
}
}
else
if (flags){
}
PrintRecno(info) ;
}
else
croak("type is not of type DB_File::HASHINFO, DB_File::BTREEINFO or DB_File::RECNOINFO");
}
{
int Flags = 0 ;
int status ;
/* Map 1.x flags to 3.x flags */
#if O_RDONLY == 0
#else
#endif
#ifdef O_TRUNC
Flags |= DB_TRUNCATE ;
#endif
/* printf("open returned %d %s\n", status, db_strerror(status)) ; */
if (status == 0)
0) ;
/* printf("cursor returned %d %s\n", status, db_strerror(status)) ; */
if (status)
}
return (RETVAL) ;
#endif /* Berkeley DB Version > 2 */
} /* ParseOpenInfo */
static double
#ifdef CAN_PROTOTYPE
#else
char *name;
int arg;
#endif
{
errno = 0;
switch (*name) {
case 'A':
break;
case 'B':
#ifdef BTREEMAGIC
return BTREEMAGIC;
#else
goto not_there;
#endif
#ifdef BTREEVERSION
return BTREEVERSION;
#else
goto not_there;
#endif
break;
case 'C':
break;
case 'D':
#ifdef DB_LOCK
return DB_LOCK;
#else
goto not_there;
#endif
#ifdef DB_SHMEM
return DB_SHMEM;
#else
goto not_there;
#endif
#ifdef DB_TXN
#else
goto not_there;
#endif
break;
case 'E':
break;
case 'F':
break;
case 'G':
break;
case 'H':
#ifdef HASHMAGIC
return HASHMAGIC;
#else
goto not_there;
#endif
#ifdef HASHVERSION
return HASHVERSION;
#else
goto not_there;
#endif
break;
case 'I':
break;
case 'J':
break;
case 'K':
break;
case 'L':
break;
case 'M':
#ifdef MAX_PAGE_NUMBER
return (U32)MAX_PAGE_NUMBER;
#else
goto not_there;
#endif
#ifdef MAX_PAGE_OFFSET
return MAX_PAGE_OFFSET;
#else
goto not_there;
#endif
#ifdef MAX_REC_NUMBER
return (U32)MAX_REC_NUMBER;
#else
goto not_there;
#endif
break;
case 'N':
break;
case 'O':
break;
case 'P':
break;
case 'Q':
break;
case 'R':
#ifdef RET_ERROR
return RET_ERROR;
#else
goto not_there;
#endif
#ifdef RET_SPECIAL
return RET_SPECIAL;
#else
goto not_there;
#endif
#ifdef RET_SUCCESS
return RET_SUCCESS;
#else
goto not_there;
#endif
#ifdef R_CURSOR
return R_CURSOR;
#else
goto not_there;
#endif
#ifdef R_DUP
return R_DUP;
#else
goto not_there;
#endif
#ifdef R_FIRST
return R_FIRST;
#else
goto not_there;
#endif
#ifdef R_FIXEDLEN
return R_FIXEDLEN;
#else
goto not_there;
#endif
#ifdef R_IAFTER
return R_IAFTER;
#else
goto not_there;
#endif
#ifdef R_IBEFORE
return R_IBEFORE;
#else
goto not_there;
#endif
#ifdef R_LAST
return R_LAST;
#else
goto not_there;
#endif
#ifdef R_NEXT
return R_NEXT;
#else
goto not_there;
#endif
#ifdef R_NOKEY
return R_NOKEY;
#else
goto not_there;
#endif
#ifdef R_NOOVERWRITE
return R_NOOVERWRITE;
#else
goto not_there;
#endif
#ifdef R_PREV
return R_PREV;
#else
goto not_there;
#endif
#ifdef R_RECNOSYNC
return R_RECNOSYNC;
#else
goto not_there;
#endif
#ifdef R_SETCURSOR
return R_SETCURSOR;
#else
goto not_there;
#endif
#ifdef R_SNAPSHOT
return R_SNAPSHOT;
#else
goto not_there;
#endif
break;
case 'S':
break;
case 'T':
break;
case 'U':
break;
case 'V':
break;
case 'W':
break;
case 'X':
break;
case 'Y':
break;
case 'Z':
break;
case '_':
break;
}
return 0;
return 0;
}
BOOT:
{
}
double
char * name
int arg
int isHASH
char * dbtype
int flags
int mode
CODE:
{
if (items == 6)
}
int
INIT:
#ifdef DBM_FILTERING
if (db->filter_fetch_key)
if (db->filter_store_key)
if (db->filter_fetch_value)
if (db->filter_store_value)
#endif /* DBM_FILTERING */
#ifdef DB_VERSION_MAJOR
if (RETVAL > 0)
RETVAL = -1 ;
#endif
int
INIT:
int
CODE:
{
}
int
CODE:
{
/* RETVAL = ((db->dbp)->get)(db->dbp, TXN &key, &value, flags) ; */
ST(0) = sv_newmortal();
}
int
INIT:
int
CODE:
{
ST(0) = sv_newmortal();
}
int
CODE:
{
ST(0) = sv_newmortal();
}
#
#
int
CODE:
{
int i ;
int One ;
#ifdef DB_VERSION_MAJOR
/* get the first value */
RETVAL = 0 ;
#else
RETVAL = -1 ;
#endif
for (i = items-1 ; i > 0 ; --i)
{
One = 1 ;
#ifdef DB_VERSION_MAJOR
#else
#endif
if (RETVAL != 0)
break;
}
}
CODE:
{
/* First get the final value */
ST(0) = sv_newmortal();
/* Now delete it */
if (RETVAL == 0)
{
/* the call to del will trash value, so take a copy now */
if (RETVAL != 0)
}
}
CODE:
{
/* get the first value */
ST(0) = sv_newmortal();
/* Now delete it */
if (RETVAL == 0)
{
/* the call to del will trash value, so take a copy now */
if (RETVAL != 0)
}
}
CODE:
{
int i ;
int keyval ;
/* Set the Cursor to the Last element */
#ifndef DB_VERSION_MAJOR
if (RETVAL >= 0)
#endif
{
if (RETVAL == 0)
else
keyval = 0 ;
for (i = 1 ; i < items ; ++i)
{
++ keyval ;
if (RETVAL != 0)
break;
}
}
}
CODE:
#
#
int
CODE:
#ifdef DB_VERSION_MAJOR
if (RETVAL > 0)
RETVAL = -1 ;
else if (RETVAL == DB_NOTFOUND)
RETVAL = 1 ;
#endif
int
CODE:
#ifdef DB_VERSION_MAJOR
if (RETVAL > 0)
RETVAL = -1 ;
else if (RETVAL == DB_NOTFOUND)
RETVAL = 1 ;
#endif
int
CODE:
#ifdef DB_VERSION_MAJOR
if (RETVAL > 0)
RETVAL = -1 ;
else if (RETVAL == DB_KEYEXIST)
RETVAL = 1 ;
#endif
int
int status = 0 ;
CODE:
#ifdef DB_VERSION_MAJOR
RETVAL = -1 ;
? -1
if (status != 0)
RETVAL = -1 ;
#else
? -1
#endif
int
CODE:
#ifdef DB_VERSION_MAJOR
if (RETVAL > 0)
RETVAL = -1 ;
#endif
int
CODE:
#ifdef DB_VERSION_MAJOR
if (RETVAL > 0)
RETVAL = -1 ;
else if (RETVAL == DB_NOTFOUND)
RETVAL = 1 ;
#endif
#ifdef DBM_FILTERING
{ \
} \
else if (code) { \
else \
} \
}
SV *
CODE:
SV *
CODE:
SV *
CODE:
SV *
CODE:
#endif /* DBM_FILTERING */