Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

tcl_db.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1999-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: tcl_db.c,v 12.15 2005/11/10 20:13:53 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <stdlib.h>
00016 #include <string.h>
00017 #include <tcl.h>
00018 #endif
00019 
00020 #include "db_int.h"
00021 #include "dbinc/db_page.h"
00022 #include "dbinc/db_am.h"
00023 #include "dbinc/tcl_db.h"
00024 
00025 /*
00026  * Prototypes for procedures defined later in this file:
00027  */
00028 static int      tcl_DbAssociate __P((Tcl_Interp *,
00029     int, Tcl_Obj * CONST*, DB *));
00030 static int      tcl_DbClose __P((Tcl_Interp *,
00031     int, Tcl_Obj * CONST*, DB *, DBTCL_INFO *));
00032 static int      tcl_DbDelete __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00033 static int      tcl_DbGet __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *, int));
00034 #ifdef CONFIG_TEST
00035 static int      tcl_DbKeyRange __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00036 #endif
00037 static int      tcl_DbPut __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00038 static int      tcl_DbStat __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00039 static int      tcl_DbTruncate __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00040 #ifdef CONFIG_TEST
00041 static int      tcl_DbCompact __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00042 static int      tcl_DbCompactStat __P((Tcl_Interp *,
00043     int, Tcl_Obj * CONST*, DB *));
00044 #endif
00045 static int      tcl_DbCursor __P((Tcl_Interp *,
00046     int, Tcl_Obj * CONST*, DB *, DBC **));
00047 static int      tcl_DbJoin __P((Tcl_Interp *,
00048     int, Tcl_Obj * CONST*, DB *, DBC **));
00049 static int      tcl_DbGetFlags __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00050 static int      tcl_DbGetOpenFlags __P((Tcl_Interp *,
00051     int, Tcl_Obj * CONST*, DB *));
00052 static int      tcl_DbGetjoin __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00053 static int      tcl_DbCount __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
00054 static int      tcl_second_call __P((DB *, const DBT *, const DBT *, DBT *));
00055 
00056 /*
00057  * _DbInfoDelete --
00058  *
00059  * PUBLIC: void _DbInfoDelete __P((Tcl_Interp *, DBTCL_INFO *));
00060  */
00061 void
00062 _DbInfoDelete(interp, dbip)
00063         Tcl_Interp *interp;
00064         DBTCL_INFO *dbip;
00065 {
00066         DBTCL_INFO *nextp, *p;
00067         /*
00068          * First we have to close any open cursors.  Then we close
00069          * our db.
00070          */
00071         for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
00072                 nextp = LIST_NEXT(p, entries);
00073                 /*
00074                  * Check if this is a cursor info structure and if
00075                  * it is, if it belongs to this DB.  If so, remove
00076                  * its commands and info structure.
00077                  */
00078                 if (p->i_parent == dbip && p->i_type == I_DBC) {
00079                         (void)Tcl_DeleteCommand(interp, p->i_name);
00080                         _DeleteInfo(p);
00081                 }
00082         }
00083         (void)Tcl_DeleteCommand(interp, dbip->i_name);
00084         _DeleteInfo(dbip);
00085 }
00086 
00087 /*
00088  *
00089  * PUBLIC: int db_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
00090  *
00091  * db_Cmd --
00092  *      Implements the "db" widget.
00093  */
00094 int
00095 db_Cmd(clientData, interp, objc, objv)
00096         ClientData clientData;          /* DB handle */
00097         Tcl_Interp *interp;             /* Interpreter */
00098         int objc;                       /* How many arguments? */
00099         Tcl_Obj *CONST objv[];          /* The argument objects */
00100 {
00101         static const char *dbcmds[] = {
00102 #ifdef CONFIG_TEST
00103                 "keyrange",
00104                 "pget",
00105                 "rpcid",
00106                 "test",
00107                 "compact",
00108                 "compact_stat",
00109 #endif
00110                 "associate",
00111                 "close",
00112                 "count",
00113                 "cursor",
00114                 "del",
00115                 "get",
00116                 "get_bt_minkey",
00117                 "get_cachesize",
00118                 "get_dbname",
00119                 "get_encrypt_flags",
00120                 "get_env",
00121                 "get_errpfx",
00122                 "get_flags",
00123                 "get_h_ffactor",
00124                 "get_h_nelem",
00125                 "get_join",
00126                 "get_lorder",
00127                 "get_open_flags",
00128                 "get_pagesize",
00129                 "get_q_extentsize",
00130                 "get_re_delim",
00131                 "get_re_len",
00132                 "get_re_pad",
00133                 "get_re_source",
00134                 "get_type",
00135                 "is_byteswapped",
00136                 "join",
00137                 "put",
00138                 "stat",
00139                 "sync",
00140                 "truncate",
00141                 NULL
00142         };
00143         enum dbcmds {
00144 #ifdef CONFIG_TEST
00145                 DBKEYRANGE,
00146                 DBPGET,
00147                 DBRPCID,
00148                 DBTEST,
00149                 DBCOMPACT,
00150                 DBCOMPACT_STAT,
00151 #endif
00152                 DBASSOCIATE,
00153                 DBCLOSE,
00154                 DBCOUNT,
00155                 DBCURSOR,
00156                 DBDELETE,
00157                 DBGET,
00158                 DBGETBTMINKEY,
00159                 DBGETCACHESIZE,
00160                 DBGETDBNAME,
00161                 DBGETENCRYPTFLAGS,
00162                 DBGETENV,
00163                 DBGETERRPFX,
00164                 DBGETFLAGS,
00165                 DBGETHFFACTOR,
00166                 DBGETHNELEM,
00167                 DBGETJOIN,
00168                 DBGETLORDER,
00169                 DBGETOPENFLAGS,
00170                 DBGETPAGESIZE,
00171                 DBGETQEXTENTSIZE,
00172                 DBGETREDELIM,
00173                 DBGETRELEN,
00174                 DBGETREPAD,
00175                 DBGETRESOURCE,
00176                 DBGETTYPE,
00177                 DBSWAPPED,
00178                 DBJOIN,
00179                 DBPUT,
00180                 DBSTAT,
00181                 DBSYNC,
00182                 DBTRUNCATE
00183         };
00184         DB *dbp;
00185         DB_ENV *dbenv;
00186         DBC *dbc;
00187         DBTCL_INFO *dbip, *ip;
00188         DBTYPE type;
00189         Tcl_Obj *res, *myobjv[3];
00190         int cmdindex, intval, ncache, result, ret;
00191         char newname[MSG_SIZE];
00192         u_int32_t bytes, gbytes, value;
00193         const char *strval, *filename, *dbname, *envid;
00194 
00195         Tcl_ResetResult(interp);
00196         dbp = (DB *)clientData;
00197         dbip = _PtrToInfo((void *)dbp);
00198         memset(newname, 0, MSG_SIZE);
00199         result = TCL_OK;
00200         if (objc <= 1) {
00201                 Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
00202                 return (TCL_ERROR);
00203         }
00204         if (dbp == NULL) {
00205                 Tcl_SetResult(interp, "NULL db pointer", TCL_STATIC);
00206                 return (TCL_ERROR);
00207         }
00208         if (dbip == NULL) {
00209                 Tcl_SetResult(interp, "NULL db info pointer", TCL_STATIC);
00210                 return (TCL_ERROR);
00211         }
00212 
00213         /*
00214          * Get the command name index from the object based on the dbcmds
00215          * defined above.
00216          */
00217         if (Tcl_GetIndexFromObj(interp,
00218             objv[1], dbcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
00219                 return (IS_HELP(objv[1]));
00220 
00221         res = NULL;
00222         switch ((enum dbcmds)cmdindex) {
00223 #ifdef CONFIG_TEST
00224         case DBKEYRANGE:
00225                 result = tcl_DbKeyRange(interp, objc, objv, dbp);
00226                 break;
00227         case DBPGET:
00228                 result = tcl_DbGet(interp, objc, objv, dbp, 1);
00229                 break;
00230         case DBRPCID:
00231                 /*
00232                  * No args for this.  Error if there are some.
00233                  */
00234                 if (objc > 2) {
00235                         Tcl_WrongNumArgs(interp, 2, objv, NULL);
00236                         return (TCL_ERROR);
00237                 }
00238                 /*
00239                  * !!! Retrieve the client ID from the dbp handle directly.
00240                  * This is for testing purposes only.  It is dbp-private data.
00241                  */
00242                 res = Tcl_NewLongObj((long)dbp->cl_id);
00243                 break;
00244         case DBTEST:
00245                 result = tcl_EnvTest(interp, objc, objv, dbp->dbenv);
00246                 break;
00247 
00248         case DBCOMPACT:
00249                 result = tcl_DbCompact(interp, objc, objv, dbp);
00250                 break;
00251 
00252         case DBCOMPACT_STAT:
00253                 result = tcl_DbCompactStat(interp, objc, objv, dbp);
00254                 break;
00255 
00256 #endif
00257         case DBASSOCIATE:
00258                 result = tcl_DbAssociate(interp, objc, objv, dbp);
00259                 break;
00260         case DBCLOSE:
00261                 result = tcl_DbClose(interp, objc, objv, dbp, dbip);
00262                 break;
00263         case DBDELETE:
00264                 result = tcl_DbDelete(interp, objc, objv, dbp);
00265                 break;
00266         case DBGET:
00267                 result = tcl_DbGet(interp, objc, objv, dbp, 0);
00268                 break;
00269         case DBPUT:
00270                 result = tcl_DbPut(interp, objc, objv, dbp);
00271                 break;
00272         case DBCOUNT:
00273                 result = tcl_DbCount(interp, objc, objv, dbp);
00274                 break;
00275         case DBSWAPPED:
00276                 /*
00277                  * No args for this.  Error if there are some.
00278                  */
00279                 if (objc > 2) {
00280                         Tcl_WrongNumArgs(interp, 2, objv, NULL);
00281                         return (TCL_ERROR);
00282                 }
00283                 _debug_check();
00284                 ret = dbp->get_byteswapped(dbp, &intval);
00285                 res = Tcl_NewIntObj(intval);
00286                 break;
00287         case DBGETTYPE:
00288                 /*
00289                  * No args for this.  Error if there are some.
00290                  */
00291                 if (objc > 2) {
00292                         Tcl_WrongNumArgs(interp, 2, objv, NULL);
00293                         return (TCL_ERROR);
00294                 }
00295                 _debug_check();
00296                 ret = dbp->get_type(dbp, &type);
00297                 if (type == DB_BTREE)
00298                         res = NewStringObj("btree", strlen("btree"));
00299                 else if (type == DB_HASH)
00300                         res = NewStringObj("hash", strlen("hash"));
00301                 else if (type == DB_RECNO)
00302                         res = NewStringObj("recno", strlen("recno"));
00303                 else if (type == DB_QUEUE)
00304                         res = NewStringObj("queue", strlen("queue"));
00305                 else {
00306                         Tcl_SetResult(interp,
00307                             "db gettype: Returned unknown type\n", TCL_STATIC);
00308                         result = TCL_ERROR;
00309                 }
00310                 break;
00311         case DBSTAT:
00312                 result = tcl_DbStat(interp, objc, objv, dbp);
00313                 break;
00314         case DBSYNC:
00315                 /*
00316                  * No args for this.  Error if there are some.
00317                  */
00318                 if (objc > 2) {
00319                         Tcl_WrongNumArgs(interp, 2, objv, NULL);
00320                         return (TCL_ERROR);
00321                 }
00322                 _debug_check();
00323                 ret = dbp->sync(dbp, 0);
00324                 res = Tcl_NewIntObj(ret);
00325                 if (ret != 0) {
00326                         Tcl_SetObjResult(interp, res);
00327                         result = TCL_ERROR;
00328                 }
00329                 break;
00330         case DBCURSOR:
00331                 snprintf(newname, sizeof(newname),
00332                     "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
00333                 ip = _NewInfo(interp, NULL, newname, I_DBC);
00334                 if (ip != NULL) {
00335                         result = tcl_DbCursor(interp, objc, objv, dbp, &dbc);
00336                         if (result == TCL_OK) {
00337                                 dbip->i_dbdbcid++;
00338                                 ip->i_parent = dbip;
00339                                 (void)Tcl_CreateObjCommand(interp, newname,
00340                                     (Tcl_ObjCmdProc *)dbc_Cmd,
00341                                     (ClientData)dbc, NULL);
00342                                 res = NewStringObj(newname, strlen(newname));
00343                                 _SetInfoData(ip, dbc);
00344                         } else
00345                                 _DeleteInfo(ip);
00346                 } else {
00347                         Tcl_SetResult(interp,
00348                             "Could not set up info", TCL_STATIC);
00349                         result = TCL_ERROR;
00350                 }
00351                 break;
00352         case DBJOIN:
00353                 snprintf(newname, sizeof(newname),
00354                     "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
00355                 ip = _NewInfo(interp, NULL, newname, I_DBC);
00356                 if (ip != NULL) {
00357                         result = tcl_DbJoin(interp, objc, objv, dbp, &dbc);
00358                         if (result == TCL_OK) {
00359                                 dbip->i_dbdbcid++;
00360                                 ip->i_parent = dbip;
00361                                 (void)Tcl_CreateObjCommand(interp, newname,
00362                                     (Tcl_ObjCmdProc *)dbc_Cmd,
00363                                     (ClientData)dbc, NULL);
00364                                 res = NewStringObj(newname, strlen(newname));
00365                                 _SetInfoData(ip, dbc);
00366                         } else
00367                                 _DeleteInfo(ip);
00368                 } else {
00369                         Tcl_SetResult(interp,
00370                             "Could not set up info", TCL_STATIC);
00371                         result = TCL_ERROR;
00372                 }
00373                 break;
00374         case DBGETBTMINKEY:
00375                 if (objc != 2) {
00376                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00377                         return (TCL_ERROR);
00378                 }
00379                 ret = dbp->get_bt_minkey(dbp, &value);
00380                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00381                     "db get_bt_minkey")) == TCL_OK)
00382                         res = Tcl_NewIntObj((int)value);
00383                 break;
00384         case DBGETCACHESIZE:
00385                 if (objc != 2) {
00386                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00387                         return (TCL_ERROR);
00388                 }
00389                 ret = dbp->get_cachesize(dbp, &gbytes, &bytes, &ncache);
00390                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00391                     "db get_cachesize")) == TCL_OK) {
00392                         myobjv[0] = Tcl_NewIntObj((int)gbytes);
00393                         myobjv[1] = Tcl_NewIntObj((int)bytes);
00394                         myobjv[2] = Tcl_NewIntObj((int)ncache);
00395                         res = Tcl_NewListObj(3, myobjv);
00396                 }
00397                 break;
00398         case DBGETDBNAME:
00399                 if (objc != 2) {
00400                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00401                         return (TCL_ERROR);
00402                 }
00403                 ret = dbp->get_dbname(dbp, &filename, &dbname);
00404                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00405                     "db get_dbname")) == TCL_OK) {
00406                         myobjv[0] = NewStringObj(filename, strlen(filename));
00407                         myobjv[1] = NewStringObj(dbname, strlen(dbname));
00408                         res = Tcl_NewListObj(2, myobjv);
00409                 }
00410                 break;
00411         case DBGETENCRYPTFLAGS:
00412                 result = tcl_EnvGetEncryptFlags(interp, objc, objv, dbp->dbenv);
00413                 break;
00414         case DBGETENV:
00415                 if (objc != 2) {
00416                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00417                         return (TCL_ERROR);
00418                 }
00419                 dbenv = dbp->get_env(dbp);
00420                 if (dbenv != NULL && (ip = _PtrToInfo(dbenv)) != NULL) {
00421                         envid = ip->i_name;
00422                         res = NewStringObj(envid, strlen(envid));
00423                 } else
00424                         Tcl_ResetResult(interp);
00425                 break;
00426         case DBGETERRPFX:
00427                 if (objc != 2) {
00428                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00429                         return (TCL_ERROR);
00430                 }
00431                 dbp->get_errpfx(dbp, &strval);
00432                 res = NewStringObj(strval, strlen(strval));
00433                 break;
00434         case DBGETFLAGS:
00435                 result = tcl_DbGetFlags(interp, objc, objv, dbp);
00436                 break;
00437         case DBGETHFFACTOR:
00438                 if (objc != 2) {
00439                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00440                         return (TCL_ERROR);
00441                 }
00442                 ret = dbp->get_h_ffactor(dbp, &value);
00443                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00444                     "db get_h_ffactor")) == TCL_OK)
00445                         res = Tcl_NewIntObj((int)value);
00446                 break;
00447         case DBGETHNELEM:
00448                 if (objc != 2) {
00449                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00450                         return (TCL_ERROR);
00451                 }
00452                 ret = dbp->get_h_nelem(dbp, &value);
00453                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00454                     "db get_h_nelem")) == TCL_OK)
00455                         res = Tcl_NewIntObj((int)value);
00456                 break;
00457         case DBGETJOIN:
00458                 result = tcl_DbGetjoin(interp, objc, objv, dbp);
00459                 break;
00460         case DBGETLORDER:
00461                 /*
00462                  * No args for this.  Error if there are some.
00463                  */
00464                 if (objc > 2) {
00465                         Tcl_WrongNumArgs(interp, 2, objv, NULL);
00466                         return (TCL_ERROR);
00467                 }
00468                 _debug_check();
00469                 ret = dbp->get_lorder(dbp, &intval);
00470                 res = Tcl_NewIntObj(intval);
00471                 break;
00472         case DBGETOPENFLAGS:
00473                 result = tcl_DbGetOpenFlags(interp, objc, objv, dbp);
00474                 break;
00475         case DBGETPAGESIZE:
00476                 if (objc != 2) {
00477                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00478                         return (TCL_ERROR);
00479                 }
00480                 ret = dbp->get_pagesize(dbp, &value);
00481                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00482                     "db get_pagesize")) == TCL_OK)
00483                         res = Tcl_NewIntObj((int)value);
00484                 break;
00485         case DBGETQEXTENTSIZE:
00486                 if (objc != 2) {
00487                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00488                         return (TCL_ERROR);
00489                 }
00490                 ret = dbp->get_q_extentsize(dbp, &value);
00491                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00492                     "db get_q_extentsize")) == TCL_OK)
00493                         res = Tcl_NewIntObj((int)value);
00494                 break;
00495         case DBGETREDELIM:
00496                 if (objc != 2) {
00497                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00498                         return (TCL_ERROR);
00499                 }
00500                 ret = dbp->get_re_delim(dbp, &intval);
00501                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00502                     "db get_re_delim")) == TCL_OK)
00503                         res = Tcl_NewIntObj(intval);
00504                 break;
00505         case DBGETRELEN:
00506                 if (objc != 2) {
00507                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00508                         return (TCL_ERROR);
00509                 }
00510                 ret = dbp->get_re_len(dbp, &value);
00511                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00512                     "db get_re_len")) == TCL_OK)
00513                         res = Tcl_NewIntObj((int)value);
00514                 break;
00515         case DBGETREPAD:
00516                 if (objc != 2) {
00517                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00518                         return (TCL_ERROR);
00519                 }
00520                 ret = dbp->get_re_pad(dbp, &intval);
00521                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00522                     "db get_re_pad")) == TCL_OK)
00523                         res = Tcl_NewIntObj((int)intval);
00524                 break;
00525         case DBGETRESOURCE:
00526                 if (objc != 2) {
00527                         Tcl_WrongNumArgs(interp, 1, objv, NULL);
00528                         return (TCL_ERROR);
00529                 }
00530                 ret = dbp->get_re_source(dbp, &strval);
00531                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00532                     "db get_re_source")) == TCL_OK)
00533                         res = NewStringObj(strval, strlen(strval));
00534                 break;
00535         case DBTRUNCATE:
00536                 result = tcl_DbTruncate(interp, objc, objv, dbp);
00537                 break;
00538         }
00539         /*
00540          * Only set result if we have a res.  Otherwise, lower
00541          * functions have already done so.
00542          */
00543         if (result == TCL_OK && res)
00544                 Tcl_SetObjResult(interp, res);
00545         return (result);
00546 }
00547 
00548 /*
00549  * tcl_db_stat --
00550  */
00551 static int
00552 tcl_DbStat(interp, objc, objv, dbp)
00553         Tcl_Interp *interp;             /* Interpreter */
00554         int objc;                       /* How many arguments? */
00555         Tcl_Obj *CONST objv[];          /* The argument objects */
00556         DB *dbp;                        /* Database pointer */
00557 {
00558         static const char *dbstatopts[] = {
00559 #ifdef CONFIG_TEST
00560                 "-read_committed",
00561                 "-read_uncommitted",
00562 #endif
00563                 "-faststat",
00564                 "-txn",
00565                 NULL
00566         };
00567         enum dbstatopts {
00568 #ifdef CONFIG_TEST
00569                 DBCUR_READ_COMMITTED,
00570                 DBCUR_READ_UNCOMMITTED,
00571 #endif
00572                 DBCUR_FASTSTAT,
00573                 DBCUR_TXN
00574         };
00575         DBTYPE type;
00576         DB_BTREE_STAT *bsp;
00577         DB_HASH_STAT *hsp;
00578         DB_QUEUE_STAT *qsp;
00579         DB_TXN *txn;
00580         Tcl_Obj *res, *flaglist, *myobjv[2];
00581         u_int32_t flag;
00582         int i, optindex, result, ret;
00583         char *arg, msg[MSG_SIZE];
00584         void *sp;
00585 
00586         result = TCL_OK;
00587         flag = 0;
00588         txn = NULL;
00589         sp = NULL;
00590         i = 2;
00591         while (i < objc) {
00592                 if (Tcl_GetIndexFromObj(interp, objv[i], dbstatopts, "option",
00593                     TCL_EXACT, &optindex) != TCL_OK) {
00594                         result = IS_HELP(objv[i]);
00595                         goto error;
00596                 }
00597                 i++;
00598                 switch ((enum dbstatopts)optindex) {
00599 #ifdef CONFIG_TEST
00600                 case DBCUR_READ_COMMITTED:
00601                         flag |= DB_READ_COMMITTED;
00602                         break;
00603                 case DBCUR_READ_UNCOMMITTED:
00604                         flag |= DB_READ_UNCOMMITTED;
00605                         break;
00606 #endif
00607                 case DBCUR_FASTSTAT:
00608                         flag |= DB_FAST_STAT;
00609                         break;
00610                 case DBCUR_TXN:
00611                         if (i == objc) {
00612                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
00613                                 result = TCL_ERROR;
00614                                 break;
00615                         }
00616                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
00617                         txn = NAME_TO_TXN(arg);
00618                         if (txn == NULL) {
00619                                 snprintf(msg, MSG_SIZE,
00620                                     "Stat: Invalid txn: %s\n", arg);
00621                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
00622                                 result = TCL_ERROR;
00623                         }
00624                         break;
00625                 }
00626                 if (result != TCL_OK)
00627                         break;
00628         }
00629         if (result != TCL_OK)
00630                 goto error;
00631 
00632         _debug_check();
00633         ret = dbp->stat(dbp, txn, &sp, flag);
00634         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db stat");
00635         if (result == TCL_ERROR)
00636                 return (result);
00637 
00638         (void)dbp->get_type(dbp, &type);
00639         /*
00640          * Have our stats, now construct the name value
00641          * list pairs and free up the memory.
00642          */
00643         res = Tcl_NewObj();
00644 
00645         /*
00646          * MAKE_STAT_LIST assumes 'res' and 'error' label.
00647          */
00648         if (type == DB_HASH) {
00649                 hsp = (DB_HASH_STAT *)sp;
00650                 MAKE_STAT_LIST("Magic", hsp->hash_magic);
00651                 MAKE_STAT_LIST("Version", hsp->hash_version);
00652                 MAKE_STAT_LIST("Page size", hsp->hash_pagesize);
00653                 MAKE_STAT_LIST("Number of keys", hsp->hash_nkeys);
00654                 MAKE_STAT_LIST("Number of records", hsp->hash_ndata);
00655                 MAKE_STAT_LIST("Fill factor", hsp->hash_ffactor);
00656                 MAKE_STAT_LIST("Buckets", hsp->hash_buckets);
00657                 if (flag != DB_FAST_STAT) {
00658                         MAKE_STAT_LIST("Free pages", hsp->hash_free);
00659                         MAKE_STAT_LIST("Bytes free", hsp->hash_bfree);
00660                         MAKE_STAT_LIST("Number of big pages",
00661                             hsp->hash_bigpages);
00662                         MAKE_STAT_LIST("Big pages bytes free",
00663                             hsp->hash_big_bfree);
00664                         MAKE_STAT_LIST("Overflow pages", hsp->hash_overflows);
00665                         MAKE_STAT_LIST("Overflow bytes free",
00666                             hsp->hash_ovfl_free);
00667                         MAKE_STAT_LIST("Duplicate pages", hsp->hash_dup);
00668                         MAKE_STAT_LIST("Duplicate pages bytes free",
00669                             hsp->hash_dup_free);
00670                 }
00671         } else if (type == DB_QUEUE) {
00672                 qsp = (DB_QUEUE_STAT *)sp;
00673                 MAKE_STAT_LIST("Magic", qsp->qs_magic);
00674                 MAKE_STAT_LIST("Version", qsp->qs_version);
00675                 MAKE_STAT_LIST("Page size", qsp->qs_pagesize);
00676                 MAKE_STAT_LIST("Extent size", qsp->qs_extentsize);
00677                 MAKE_STAT_LIST("Number of records", qsp->qs_nkeys);
00678                 MAKE_STAT_LIST("Record length", qsp->qs_re_len);
00679                 MAKE_STAT_LIST("Record pad", qsp->qs_re_pad);
00680                 MAKE_STAT_LIST("First record number", qsp->qs_first_recno);
00681                 MAKE_STAT_LIST("Last record number", qsp->qs_cur_recno);
00682                 if (flag != DB_FAST_STAT) {
00683                         MAKE_STAT_LIST("Number of pages", qsp->qs_pages);
00684                         MAKE_STAT_LIST("Bytes free", qsp->qs_pgfree);
00685                 }
00686         } else {        /* BTREE and RECNO are same stats */
00687                 bsp = (DB_BTREE_STAT *)sp;
00688                 MAKE_STAT_LIST("Magic", bsp->bt_magic);
00689                 MAKE_STAT_LIST("Version", bsp->bt_version);
00690                 MAKE_STAT_LIST("Number of keys", bsp->bt_nkeys);
00691                 MAKE_STAT_LIST("Number of records", bsp->bt_ndata);
00692                 MAKE_STAT_LIST("Minimum keys per page", bsp->bt_minkey);
00693                 MAKE_STAT_LIST("Fixed record length", bsp->bt_re_len);
00694                 MAKE_STAT_LIST("Record pad", bsp->bt_re_pad);
00695                 MAKE_STAT_LIST("Page size", bsp->bt_pagesize);
00696                 if (flag != DB_FAST_STAT) {
00697                         MAKE_STAT_LIST("Levels", bsp->bt_levels);
00698                         MAKE_STAT_LIST("Internal pages", bsp->bt_int_pg);
00699                         MAKE_STAT_LIST("Leaf pages", bsp->bt_leaf_pg);
00700                         MAKE_STAT_LIST("Duplicate pages", bsp->bt_dup_pg);
00701                         MAKE_STAT_LIST("Overflow pages", bsp->bt_over_pg);
00702                         MAKE_STAT_LIST("Empty pages", bsp->bt_empty_pg);
00703                         MAKE_STAT_LIST("Pages on freelist", bsp->bt_free);
00704                         MAKE_STAT_LIST("Internal pages bytes free",
00705                             bsp->bt_int_pgfree);
00706                         MAKE_STAT_LIST("Leaf pages bytes free",
00707                             bsp->bt_leaf_pgfree);
00708                         MAKE_STAT_LIST("Duplicate pages bytes free",
00709                             bsp->bt_dup_pgfree);
00710                         MAKE_STAT_LIST("Bytes free in overflow pages",
00711                             bsp->bt_over_pgfree);
00712                 }
00713         }
00714 
00715         /*
00716          * Construct a {name {flag1 flag2 ... flagN}} list for the
00717          * dbp flags.  These aren't access-method dependent, but they
00718          * include all the interesting flags, and the integer value
00719          * isn't useful from Tcl--return the strings instead.
00720          */
00721         myobjv[0] = NewStringObj("Flags", strlen("Flags"));
00722         myobjv[1] = _GetFlagsList(interp, dbp->flags, __db_get_flags_fn());
00723         flaglist = Tcl_NewListObj(2, myobjv);
00724         if (flaglist == NULL) {
00725                 result = TCL_ERROR;
00726                 goto error;
00727         }
00728         if ((result =
00729             Tcl_ListObjAppendElement(interp, res, flaglist)) != TCL_OK)
00730                 goto error;
00731 
00732         Tcl_SetObjResult(interp, res);
00733 error:
00734         if (sp != NULL)
00735                 __os_ufree(dbp->dbenv, sp);
00736         return (result);
00737 }
00738 
00739 /*
00740  * tcl_db_close --
00741  */
00742 static int
00743 tcl_DbClose(interp, objc, objv, dbp, dbip)
00744         Tcl_Interp *interp;             /* Interpreter */
00745         int objc;                       /* How many arguments? */
00746         Tcl_Obj *CONST objv[];          /* The argument objects */
00747         DB *dbp;                        /* Database pointer */
00748         DBTCL_INFO *dbip;               /* Info pointer */
00749 {
00750         static const char *dbclose[] = {
00751                 "-nosync", "--", NULL
00752         };
00753         enum dbclose {
00754                 TCL_DBCLOSE_NOSYNC,
00755                 TCL_DBCLOSE_ENDARG
00756         };
00757         u_int32_t flag;
00758         int endarg, i, optindex, result, ret;
00759         char *arg;
00760 
00761         result = TCL_OK;
00762         endarg = 0;
00763         flag = 0;
00764         if (objc > 4) {
00765                 Tcl_WrongNumArgs(interp, 2, objv, "?-nosync?");
00766                 return (TCL_ERROR);
00767         }
00768 
00769         for (i = 2; i < objc; ++i) {
00770                 if (Tcl_GetIndexFromObj(interp, objv[i], dbclose,
00771                     "option", TCL_EXACT, &optindex) != TCL_OK) {
00772                         arg = Tcl_GetStringFromObj(objv[i], NULL);
00773                         if (arg[0] == '-')
00774                                 return (IS_HELP(objv[i]));
00775                         else
00776                                 Tcl_ResetResult(interp);
00777                         break;
00778                 }
00779                 switch ((enum dbclose)optindex) {
00780                 case TCL_DBCLOSE_NOSYNC:
00781                         flag = DB_NOSYNC;
00782                         break;
00783                 case TCL_DBCLOSE_ENDARG:
00784                         endarg = 1;
00785                         break;
00786                 }
00787                 /*
00788                  * If, at any time, parsing the args we get an error,
00789                  * bail out and return.
00790                  */
00791                 if (result != TCL_OK)
00792                         return (result);
00793                 if (endarg)
00794                         break;
00795         }
00796         if (dbip->i_cdata != NULL)
00797                 __os_free(dbp->dbenv, dbip->i_cdata);
00798         _DbInfoDelete(interp, dbip);
00799         _debug_check();
00800 
00801         /* Paranoia. */
00802         dbp->api_internal = NULL;
00803 
00804         ret = (dbp)->close(dbp, flag);
00805         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db close");
00806         return (result);
00807 }
00808 
00809 /*
00810  * tcl_db_put --
00811  */
00812 static int
00813 tcl_DbPut(interp, objc, objv, dbp)
00814         Tcl_Interp *interp;             /* Interpreter */
00815         int objc;                       /* How many arguments? */
00816         Tcl_Obj *CONST objv[];          /* The argument objects */
00817         DB *dbp;                        /* Database pointer */
00818 {
00819         static const char *dbputopts[] = {
00820 #ifdef CONFIG_TEST
00821                 "-nodupdata",
00822 #endif
00823                 "-append",
00824                 "-nooverwrite",
00825                 "-partial",
00826                 "-txn",
00827                 NULL
00828         };
00829         enum dbputopts {
00830 #ifdef CONFIG_TEST
00831                 DBGET_NODUPDATA,
00832 #endif
00833                 DBPUT_APPEND,
00834                 DBPUT_NOOVER,
00835                 DBPUT_PART,
00836                 DBPUT_TXN
00837         };
00838         static const char *dbputapp[] = {
00839                 "-append",      NULL
00840         };
00841         enum dbputapp { DBPUT_APPEND0 };
00842         DBT key, data;
00843         DBTYPE type;
00844         DB_TXN *txn;
00845         Tcl_Obj **elemv, *res;
00846         void *dtmp, *ktmp;
00847         db_recno_t recno;
00848         u_int32_t flag;
00849         int elemc, end, freekey, freedata;
00850         int i, optindex, result, ret;
00851         char *arg, msg[MSG_SIZE];
00852 
00853         txn = NULL;
00854         result = TCL_OK;
00855         flag = 0;
00856         if (objc <= 3) {
00857                 Tcl_WrongNumArgs(interp, 2, objv, "?-args? key data");
00858                 return (TCL_ERROR);
00859         }
00860 
00861         dtmp = ktmp = NULL;
00862         freekey = freedata = 0;
00863         memset(&key, 0, sizeof(key));
00864         memset(&data, 0, sizeof(data));
00865 
00866         /*
00867          * If it is a QUEUE or RECNO database, the key is a record number
00868          * and must be setup up to contain a db_recno_t.  Otherwise the
00869          * key is a "string".
00870          */
00871         (void)dbp->get_type(dbp, &type);
00872 
00873         /*
00874          * We need to determine where the end of required args are.  If we
00875          * are using a QUEUE/RECNO db and -append, then there is just one
00876          * req arg (data).  Otherwise there are two (key data).
00877          *
00878          * We preparse the list to determine this since we need to know
00879          * to properly check # of args for other options below.
00880          */
00881         end = objc - 2;
00882         if (type == DB_QUEUE || type == DB_RECNO) {
00883                 i = 2;
00884                 while (i < objc - 1) {
00885                         if (Tcl_GetIndexFromObj(interp, objv[i++], dbputapp,
00886                             "option", TCL_EXACT, &optindex) != TCL_OK)
00887                                 continue;
00888                         switch ((enum dbputapp)optindex) {
00889                         case DBPUT_APPEND0:
00890                                 end = objc - 1;
00891                                 break;
00892                         }
00893                 }
00894         }
00895         Tcl_ResetResult(interp);
00896 
00897         /*
00898          * Get the command name index from the object based on the options
00899          * defined above.
00900          */
00901         i = 2;
00902         while (i < end) {
00903                 if (Tcl_GetIndexFromObj(interp, objv[i],
00904                     dbputopts, "option", TCL_EXACT, &optindex) != TCL_OK)
00905                         return (IS_HELP(objv[i]));
00906                 i++;
00907                 switch ((enum dbputopts)optindex) {
00908 #ifdef CONFIG_TEST
00909                 case DBGET_NODUPDATA:
00910                         FLAG_CHECK(flag);
00911                         flag = DB_NODUPDATA;
00912                         break;
00913 #endif
00914                 case DBPUT_TXN:
00915                         if (i > (end - 1)) {
00916                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
00917                                 result = TCL_ERROR;
00918                                 break;
00919                         }
00920                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
00921                         txn = NAME_TO_TXN(arg);
00922                         if (txn == NULL) {
00923                                 snprintf(msg, MSG_SIZE,
00924                                     "Put: Invalid txn: %s\n", arg);
00925                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
00926                                 result = TCL_ERROR;
00927                         }
00928                         break;
00929                 case DBPUT_APPEND:
00930                         FLAG_CHECK(flag);
00931                         flag = DB_APPEND;
00932                         break;
00933                 case DBPUT_NOOVER:
00934                         FLAG_CHECK(flag);
00935                         flag = DB_NOOVERWRITE;
00936                         break;
00937                 case DBPUT_PART:
00938                         if (i > (end - 1)) {
00939                                 Tcl_WrongNumArgs(interp, 2, objv,
00940                                     "?-partial {offset length}?");
00941                                 result = TCL_ERROR;
00942                                 break;
00943                         }
00944                         /*
00945                          * Get sublist as {offset length}
00946                          */
00947                         result = Tcl_ListObjGetElements(interp, objv[i++],
00948                             &elemc, &elemv);
00949                         if (elemc != 2) {
00950                                 Tcl_SetResult(interp,
00951                                     "List must be {offset length}", TCL_STATIC);
00952                                 result = TCL_ERROR;
00953                                 break;
00954                         }
00955                         data.flags = DB_DBT_PARTIAL;
00956                         result = _GetUInt32(interp, elemv[0], &data.doff);
00957                         if (result != TCL_OK)
00958                                 break;
00959                         result = _GetUInt32(interp, elemv[1], &data.dlen);
00960                         /*
00961                          * NOTE: We don't check result here because all we'd
00962                          * do is break anyway, and we are doing that.  If you
00963                          * add code here, you WILL need to add the check
00964                          * for result.  (See the check for save.doff, a few
00965                          * lines above and copy that.)
00966                          */
00967                         break;
00968                 }
00969                 if (result != TCL_OK)
00970                         break;
00971         }
00972 
00973         if (result == TCL_ERROR)
00974                 return (result);
00975 
00976         /*
00977          * If we are a recno db and we are NOT using append, then the 2nd
00978          * last arg is the key.
00979          */
00980         if (type == DB_QUEUE || type == DB_RECNO) {
00981                 key.data = &recno;
00982                 key.ulen = key.size = sizeof(db_recno_t);
00983                 key.flags = DB_DBT_USERMEM;
00984                 if (flag == DB_APPEND)
00985                         recno = 0;
00986                 else {
00987                         result = _GetUInt32(interp, objv[objc-2], &recno);
00988                         if (result != TCL_OK)
00989                                 return (result);
00990                 }
00991         } else {
00992                 COMPQUIET(recno, 0);
00993 
00994                 ret = _CopyObjBytes(interp, objv[objc-2], &ktmp,
00995                     &key.size, &freekey);
00996                 if (ret != 0) {
00997                         result = _ReturnSetup(interp, ret,
00998                             DB_RETOK_DBPUT(ret), "db put");
00999                         return (result);
01000                 }
01001                 key.data = ktmp;
01002         }
01003         ret = _CopyObjBytes(interp, objv[objc-1], &dtmp, &data.size, &freedata);
01004         if (ret != 0) {
01005                 result = _ReturnSetup(interp, ret,
01006                     DB_RETOK_DBPUT(ret), "db put");
01007                 goto out;
01008         }
01009         data.data = dtmp;
01010         _debug_check();
01011         ret = dbp->put(dbp, txn, &key, &data, flag);
01012         result = _ReturnSetup(interp, ret, DB_RETOK_DBPUT(ret), "db put");
01013 
01014         /* We may have a returned record number. */
01015         if (ret == 0 &&
01016             (type == DB_QUEUE || type == DB_RECNO) && flag == DB_APPEND) {
01017                 res = Tcl_NewWideIntObj((Tcl_WideInt)recno);
01018                 Tcl_SetObjResult(interp, res);
01019         }
01020 
01021 out:    if (dtmp != NULL && freedata)
01022                 __os_free(dbp->dbenv, dtmp);
01023         if (ktmp != NULL && freekey)
01024                 __os_free(dbp->dbenv, ktmp);
01025         return (result);
01026 }
01027 
01028 /*
01029  * tcl_db_get --
01030  */
01031 static int
01032 tcl_DbGet(interp, objc, objv, dbp, ispget)
01033         Tcl_Interp *interp;             /* Interpreter */
01034         int objc;                       /* How many arguments? */
01035         Tcl_Obj *CONST objv[];          /* The argument objects */
01036         DB *dbp;                        /* Database pointer */
01037         int ispget;                     /* 1 for pget, 0 for get */
01038 {
01039         static const char *dbgetopts[] = {
01040 #ifdef CONFIG_TEST
01041                 "-multi",
01042                 "-read_committed",
01043                 "-read_uncommitted",
01044 #endif
01045                 "-consume",
01046                 "-consume_wait",
01047                 "-get_both",
01048                 "-glob",
01049                 "-partial",
01050                 "-recno",
01051                 "-rmw",
01052                 "-txn",
01053                 "--",
01054                 NULL
01055         };
01056         enum dbgetopts {
01057 #ifdef CONFIG_TEST
01058                 DBGET_MULTI,
01059                 DBGET_READ_COMMITTED,
01060                 DBGET_READ_UNCOMMITTED,
01061 #endif
01062                 DBGET_CONSUME,
01063                 DBGET_CONSUME_WAIT,
01064                 DBGET_BOTH,
01065                 DBGET_GLOB,
01066                 DBGET_PART,
01067                 DBGET_RECNO,
01068                 DBGET_RMW,
01069                 DBGET_TXN,
01070                 DBGET_ENDARG
01071         };
01072         DBC *dbc;
01073         DBT key, pkey, data, save;
01074         DBTYPE ptype, type;
01075         DB_TXN *txn;
01076         Tcl_Obj **elemv, *retlist;
01077         db_recno_t precno, recno;
01078         u_int32_t flag, cflag, isdup, mflag, rmw;
01079         int elemc, end, endarg, freekey, freedata, i;
01080         int optindex, result, ret, useglob, useprecno, userecno;
01081         char *arg, *pattern, *prefix, msg[MSG_SIZE];
01082         void *dtmp, *ktmp;
01083 #ifdef CONFIG_TEST
01084         int bufsize;
01085 #endif
01086 
01087         result = TCL_OK;
01088         freekey = freedata = 0;
01089         cflag = endarg = flag = mflag = rmw = 0;
01090         useglob = userecno = 0;
01091         txn = NULL;
01092         pattern = prefix = NULL;
01093         dtmp = ktmp = NULL;
01094 #ifdef CONFIG_TEST
01095         COMPQUIET(bufsize, 0);
01096 #endif
01097 
01098         if (objc < 3) {
01099                 Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
01100                 return (TCL_ERROR);
01101         }
01102 
01103         memset(&key, 0, sizeof(key));
01104         memset(&data, 0, sizeof(data));
01105         memset(&save, 0, sizeof(save));
01106 
01107         /* For the primary key in a pget call. */
01108         memset(&pkey, 0, sizeof(pkey));
01109 
01110         /*
01111          * Get the command name index from the object based on the options
01112          * defined above.
01113          */
01114         i = 2;
01115         (void)dbp->get_type(dbp, &type);
01116         end = objc;
01117         while (i < end) {
01118                 if (Tcl_GetIndexFromObj(interp, objv[i], dbgetopts, "option",
01119                     TCL_EXACT, &optindex) != TCL_OK) {
01120                         arg = Tcl_GetStringFromObj(objv[i], NULL);
01121                         if (arg[0] == '-') {
01122                                 result = IS_HELP(objv[i]);
01123                                 goto out;
01124                         } else
01125                                 Tcl_ResetResult(interp);
01126                         break;
01127                 }
01128                 i++;
01129                 switch ((enum dbgetopts)optindex) {
01130 #ifdef CONFIG_TEST
01131                 case DBGET_MULTI:
01132                         mflag |= DB_MULTIPLE;
01133                         result = Tcl_GetIntFromObj(interp, objv[i], &bufsize);
01134                         if (result != TCL_OK)
01135                                 goto out;
01136                         i++;
01137                         break;
01138                 case DBGET_READ_COMMITTED:
01139                         rmw |= DB_READ_COMMITTED;
01140                         break;
01141                 case DBGET_READ_UNCOMMITTED:
01142                         rmw |= DB_READ_UNCOMMITTED;
01143                         break;
01144 #endif
01145                 case DBGET_BOTH:
01146                         /*
01147                          * Change 'end' and make sure we aren't already past
01148                          * the new end.
01149                          */
01150                         if (i > objc - 2) {
01151                                 Tcl_WrongNumArgs(interp, 2, objv,
01152                                     "?-get_both key data?");
01153                                 result = TCL_ERROR;
01154                                 break;
01155                         }
01156                         end = objc - 2;
01157                         FLAG_CHECK(flag);
01158                         flag = DB_GET_BOTH;
01159                         break;
01160                 case DBGET_TXN:
01161                         if (i >= end) {
01162                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
01163                                 result = TCL_ERROR;
01164                                 break;
01165                         }
01166                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
01167                         txn = NAME_TO_TXN(arg);
01168                         if (txn == NULL) {
01169                                 snprintf(msg, MSG_SIZE,
01170                                     "Get: Invalid txn: %s\n", arg);
01171                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
01172                                 result = TCL_ERROR;
01173                         }
01174                         break;
01175                 case DBGET_GLOB:
01176                         useglob = 1;
01177                         end = objc - 1;
01178                         break;
01179                 case DBGET_CONSUME:
01180                         FLAG_CHECK(flag);
01181                         flag = DB_CONSUME;
01182                         break;
01183                 case DBGET_CONSUME_WAIT:
01184                         FLAG_CHECK(flag);
01185                         flag = DB_CONSUME_WAIT;
01186                         break;
01187                 case DBGET_RECNO:
01188                         end = objc - 1;
01189                         userecno = 1;
01190                         if (type != DB_RECNO && type != DB_QUEUE) {
01191                                 FLAG_CHECK(flag);
01192                                 flag = DB_SET_RECNO;
01193                                 key.flags |= DB_DBT_MALLOC;
01194                         }
01195                         break;
01196                 case DBGET_RMW:
01197                         rmw |= DB_RMW;
01198                         break;
01199                 case DBGET_PART:
01200                         end = objc - 1;
01201                         if (i == end) {
01202                                 Tcl_WrongNumArgs(interp, 2, objv,
01203                                     "?-partial {offset length}?");
01204                                 result = TCL_ERROR;
01205                                 break;
01206                         }
01207                         /*
01208                          * Get sublist as {offset length}
01209                          */
01210                         result = Tcl_ListObjGetElements(interp, objv[i++],
01211                             &elemc, &elemv);
01212                         if (elemc != 2) {
01213                                 Tcl_SetResult(interp,
01214                                     "List must be {offset length}", TCL_STATIC);
01215                                 result = TCL_ERROR;
01216                                 break;
01217                         }
01218                         save.flags = DB_DBT_PARTIAL;
01219                         result = _GetUInt32(interp, elemv[0], &save.doff);
01220                         if (result != TCL_OK)
01221                                 break;
01222                         result = _GetUInt32(interp, elemv[1], &save.dlen);
01223                         /*
01224                          * NOTE: We don't check result here because all we'd
01225                          * do is break anyway, and we are doing that.  If you
01226                          * add code here, you WILL need to add the check
01227                          * for result.  (See the check for save.doff, a few
01228                          * lines above and copy that.)
01229                          */
01230                         break;
01231                 case DBGET_ENDARG:
01232                         endarg = 1;
01233                         break;
01234                 }
01235                 if (result != TCL_OK)
01236                         break;
01237                 if (endarg)
01238                         break;
01239         }
01240         if (result != TCL_OK)
01241                 goto out;
01242 
01243         if (type == DB_RECNO || type == DB_QUEUE)
01244                 userecno = 1;
01245 
01246         /*
01247          * Check args we have left versus the flags we were given.
01248          * We might have 0, 1 or 2 left.  If we have 0, it must
01249          * be DB_CONSUME*, if 2, then DB_GET_BOTH, all others should
01250          * be 1.
01251          */
01252         if (((flag == DB_CONSUME || flag == DB_CONSUME_WAIT) && i != objc) ||
01253             (flag == DB_GET_BOTH && i != objc - 2)) {
01254                 Tcl_SetResult(interp,
01255                     "Wrong number of key/data given based on flags specified\n",
01256                     TCL_STATIC);
01257                 result = TCL_ERROR;
01258                 goto out;
01259         } else if (flag == 0 && i != objc - 1) {
01260                 Tcl_SetResult(interp,
01261                     "Wrong number of key/data given\n", TCL_STATIC);
01262                 result = TCL_ERROR;
01263                 goto out;
01264         }
01265 
01266         /*
01267          * Find out whether the primary key should also be a recno.
01268          */
01269         if (ispget && dbp->s_primary != NULL) {
01270                 (void)dbp->s_primary->get_type(dbp->s_primary, &ptype);
01271                 useprecno = ptype == DB_RECNO || ptype == DB_QUEUE;
01272         } else
01273                 useprecno = 0;
01274 
01275         /*
01276          * Check for illegal combos of options.
01277          */
01278         if (useglob && (userecno || flag == DB_SET_RECNO ||
01279             type == DB_RECNO || type == DB_QUEUE)) {
01280                 Tcl_SetResult(interp,
01281                     "Cannot use -glob and record numbers.\n",
01282                     TCL_STATIC);
01283                 result = TCL_ERROR;
01284                 goto out;
01285         }
01286         if (useglob && flag == DB_GET_BOTH) {
01287                 Tcl_SetResult(interp,
01288                     "Only one of -glob or -get_both can be specified.\n",
01289                     TCL_STATIC);
01290                 result = TCL_ERROR;
01291                 goto out;
01292         }
01293 
01294         if (useglob)
01295                 pattern = Tcl_GetStringFromObj(objv[objc - 1], NULL);
01296 
01297         /*
01298          * This is the list we return
01299          */
01300         retlist = Tcl_NewListObj(0, NULL);
01301         save.flags |= DB_DBT_MALLOC;
01302 
01303         /*
01304          * isdup is used to know if we support duplicates.  If not, we
01305          * can just do a db->get call and avoid using cursors.
01306          */
01307         if ((ret = dbp->get_flags(dbp, &isdup)) != 0) {
01308                 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db get");
01309                 goto out;
01310         }
01311         isdup &= DB_DUP;
01312 
01313         /*
01314          * If the database doesn't support duplicates or we're performing
01315          * ops that don't require returning multiple items, use DB->get
01316          * instead of a cursor operation.
01317          */
01318         if (pattern == NULL && (isdup == 0 || mflag != 0 ||
01319             flag == DB_SET_RECNO || flag == DB_GET_BOTH ||
01320             flag == DB_CONSUME || flag == DB_CONSUME_WAIT)) {
01321                 if (flag == DB_GET_BOTH) {
01322                         if (userecno) {
01323                                 result = _GetUInt32(interp,
01324                                     objv[(objc - 2)], &recno);
01325                                 if (result == TCL_OK) {
01326                                         key.data = &recno;
01327                                         key.size = sizeof(db_recno_t);
01328                                 } else
01329                                         goto out;
01330                         } else {
01331                                 /*
01332                                  * Some get calls (SET_*) can change the
01333                                  * key pointers.  So, we need to store
01334                                  * the allocated key space in a tmp.
01335                                  */
01336                                 ret = _CopyObjBytes(interp, objv[objc-2],
01337                                     &ktmp, &key.size, &freekey);
01338                                 if (ret != 0) {
01339                                         result = _ReturnSetup(interp, ret,
01340                                             DB_RETOK_DBGET(ret), "db get");
01341                                         goto out;
01342                                 }
01343                                 key.data = ktmp;
01344                         }
01345                         /*
01346                          * Already checked args above.  Fill in key and save.
01347                          * Save is used in the dbp->get call below to fill in
01348                          * data.
01349                          *
01350                          * If the "data" here is really a primary key--that
01351                          * is, if we're in a pget--and that primary key
01352                          * is a recno, treat it appropriately as an int.
01353                          */
01354                         if (useprecno) {
01355                                 result = _GetUInt32(interp,
01356                                     objv[objc - 1], &precno);
01357                                 if (result == TCL_OK) {
01358                                         save.data = &precno;
01359                                         save.size = sizeof(db_recno_t);
01360                                 } else
01361                                         goto out;
01362                         } else {
01363                                 ret = _CopyObjBytes(interp, objv[objc-1],
01364                                     &dtmp, &save.size, &freedata);
01365                                 if (ret != 0) {
01366                                         result = _ReturnSetup(interp, ret,
01367                                             DB_RETOK_DBGET(ret), "db get");
01368                                         goto out;
01369                                 }
01370                                 save.data = dtmp;
01371                         }
01372                 } else if (flag != DB_CONSUME && flag != DB_CONSUME_WAIT) {
01373                         if (userecno) {
01374                                 result = _GetUInt32(
01375                                     interp, objv[(objc - 1)], &recno);
01376                                 if (result == TCL_OK) {
01377                                         key.data = &recno;
01378                                         key.size = sizeof(db_recno_t);
01379                                 } else
01380                                         goto out;
01381                         } else {
01382                                 /*
01383                                  * Some get calls (SET_*) can change the
01384                                  * key pointers.  So, we need to store
01385                                  * the allocated key space in a tmp.
01386                                  */
01387                                 ret = _CopyObjBytes(interp, objv[objc-1],
01388                                     &ktmp, &key.size, &freekey);
01389                                 if (ret != 0) {
01390                                         result = _ReturnSetup(interp, ret,
01391                                             DB_RETOK_DBGET(ret), "db get");
01392                                         goto out;
01393                                 }
01394                                 key.data = ktmp;
01395                         }
01396 #ifdef CONFIG_TEST
01397                         if (mflag & DB_MULTIPLE) {
01398                                 if ((ret = __os_malloc(dbp->dbenv,
01399                                     (size_t)bufsize, &save.data)) != 0) {
01400                                         Tcl_SetResult(interp,
01401                                             db_strerror(ret), TCL_STATIC);
01402                                         goto out;
01403                                 }
01404                                 save.ulen = (u_int32_t)bufsize;
01405                                 F_CLR(&save, DB_DBT_MALLOC);
01406                                 F_SET(&save, DB_DBT_USERMEM);
01407                         }
01408 #endif
01409                 }
01410 
01411                 data = save;
01412 
01413                 if (ispget) {
01414                         if (flag == DB_GET_BOTH) {
01415                                 pkey.data = save.data;
01416                                 pkey.size = save.size;
01417                                 data.data = NULL;
01418                                 data.size = 0;
01419                         }
01420                         F_SET(&pkey, DB_DBT_MALLOC);
01421                         _debug_check();
01422                         ret = dbp->pget(dbp,
01423                             txn, &key, &pkey, &data, flag | rmw);
01424                 } else {
01425                         _debug_check();
01426                         ret = dbp->get(dbp,
01427                             txn, &key, &data, flag | rmw | mflag);
01428                 }
01429                 result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret),
01430                     "db get");
01431                 if (ret == 0) {
01432                         /*
01433                          * Success.  Return a list of the form {name value}
01434                          * If it was a recno in key.data, we need to convert
01435                          * into a string/object representation of that recno.
01436                          */
01437                         if (mflag & DB_MULTIPLE)
01438                                 result = _SetMultiList(interp,
01439                                     retlist, &key, &data, type, flag);
01440                         else if (type == DB_RECNO || type == DB_QUEUE)
01441                                 if (ispget)
01442                                         result = _Set3DBTList(interp,
01443                                             retlist, &key, 1, &pkey,
01444                                             useprecno, &data);
01445                                 else
01446                                         result = _SetListRecnoElem(interp,
01447                                             retlist, *(db_recno_t *)key.data,
01448                                             data.data, data.size);
01449                         else {
01450                                 if (ispget)
01451                                         result = _Set3DBTList(interp,
01452                                             retlist, &key, 0, &pkey,
01453                                             useprecno, &data);
01454                                 else
01455                                         result = _SetListElem(interp, retlist,
01456                                             key.data, key.size,
01457                                             data.data, data.size);
01458                         }
01459                 }
01460                 /*
01461                  * Free space from DBT.
01462                  *
01463                  * If we set DB_DBT_MALLOC, we need to free the space if and
01464                  * only if we succeeded and if DB allocated anything (the
01465                  * pointer has changed from what we passed in).  If
01466                  * DB_DBT_MALLOC is not set, this is a bulk get buffer, and
01467                  * needs to be freed no matter what.
01468                  */
01469                 if (F_ISSET(&key, DB_DBT_MALLOC) && ret == 0 &&
01470                     key.data != ktmp)
01471                         __os_ufree(dbp->dbenv, key.data);
01472                 if (F_ISSET(&data, DB_DBT_MALLOC) && ret == 0 &&
01473                     data.data != dtmp)
01474                         __os_ufree(dbp->dbenv, data.data);
01475                 else if (!F_ISSET(&data, DB_DBT_MALLOC))
01476                         __os_free(dbp->dbenv, data.data);
01477                 if (ispget && ret == 0 && pkey.data != save.data)
01478                         __os_ufree(dbp->dbenv, pkey.data);
01479                 if (result == TCL_OK)
01480                         Tcl_SetObjResult(interp, retlist);
01481                 goto out;
01482         }
01483 
01484         if (userecno) {
01485                 result = _GetUInt32(interp, objv[(objc - 1)], &recno);
01486                 if (result == TCL_OK) {
01487                         key.data = &recno;
01488                         key.size = sizeof(db_recno_t);
01489                 } else
01490                         goto out;
01491         } else {
01492                 /*
01493                  * Some get calls (SET_*) can change the
01494                  * key pointers.  So, we need to store
01495                  * the allocated key space in a tmp.
01496                  */
01497                 ret = _CopyObjBytes(interp, objv[objc-1], &ktmp,
01498                     &key.size, &freekey);
01499                 if (ret != 0) {
01500                         result = _ReturnSetup(interp, ret,
01501                             DB_RETOK_DBGET(ret), "db get");
01502                         return (result);
01503                 }
01504                 key.data = ktmp;
01505         }
01506         ret = dbp->cursor(dbp, txn, &dbc, 0);
01507         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db cursor");
01508         if (result == TCL_ERROR)
01509                 goto out;
01510 
01511         /*
01512          * At this point, we have a cursor, if we have a pattern,
01513          * we go to the nearest one and step forward until we don't
01514          * have any more that match the pattern prefix.  If we have
01515          * an exact key, we go to that key position, and step through
01516          * all the duplicates.  In either case we build up a list of
01517          * the form {{key data} {key data}...} along the way.
01518          */
01519         memset(&data, 0, sizeof(data));
01520         /*
01521          * Restore any "partial" info we have saved.
01522          */
01523         data = save;
01524         if (pattern) {
01525                 /*
01526                  * Note, prefix is returned in new space.  Must free it.
01527                  */
01528                 ret = _GetGlobPrefix(pattern, &prefix);
01529                 if (ret) {
01530                         result = TCL_ERROR;
01531                         Tcl_SetResult(interp,
01532                             "Unable to allocate pattern space", TCL_STATIC);
01533                         goto out1;
01534                 }
01535                 key.data = prefix;
01536                 key.size = strlen(prefix);
01537                 /*
01538                  * If they give us an empty pattern string
01539                  * (i.e. -glob *), go through entire DB.
01540                  */
01541                 if (strlen(prefix) == 0)
01542                         cflag = DB_FIRST;
01543                 else
01544                         cflag = DB_SET_RANGE;
01545         } else
01546                 cflag = DB_SET;
01547         if (ispget) {
01548                 _debug_check();
01549                 F_SET(&pkey, DB_DBT_MALLOC);
01550                 ret = dbc->c_pget(dbc, &key, &pkey, &data, cflag | rmw);
01551         } else {
01552                 _debug_check();
01553                 ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
01554         }
01555         result = _ReturnSetup(interp, ret, DB_RETOK_DBCGET(ret),
01556             "db get (cursor)");
01557         if (result == TCL_ERROR)
01558                 goto out1;
01559         if (pattern) {
01560                 if (ret == 0 && prefix != NULL &&
01561                     memcmp(key.data, prefix, strlen(prefix)) != 0) {
01562                         /*
01563                          * Free space from DB_DBT_MALLOC
01564                          */
01565                         __os_ufree(dbp->dbenv, data.data);
01566                         goto out1;
01567                 }
01568                 cflag = DB_NEXT;
01569         } else
01570                 cflag = DB_NEXT_DUP;
01571 
01572         while (ret == 0 && result == TCL_OK) {
01573                 /*
01574                  * Build up our {name value} sublist
01575                  */
01576                 if (ispget)
01577                         result = _Set3DBTList(interp, retlist, &key, 0,
01578                             &pkey, useprecno, &data);
01579                 else
01580                         result = _SetListElem(interp, retlist,
01581                             key.data, key.size, data.data, data.size);
01582                 /*
01583                  * Free space from DB_DBT_MALLOC
01584                  */
01585                 if (ispget)
01586                         __os_ufree(dbp->dbenv, pkey.data);
01587                 __os_ufree(dbp->dbenv, data.data);
01588                 if (result != TCL_OK)
01589                         break;
01590                 /*
01591                  * Append {name value} to return list
01592                  */
01593                 memset(&key, 0, sizeof(key));
01594                 memset(&pkey, 0, sizeof(pkey));
01595                 memset(&data, 0, sizeof(data));
01596                 /*
01597                  * Restore any "partial" info we have saved.
01598                  */
01599                 data = save;
01600                 if (ispget) {
01601                         F_SET(&pkey, DB_DBT_MALLOC);
01602                         ret = dbc->c_pget(dbc, &key, &pkey, &data, cflag | rmw);
01603                 } else
01604                         ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
01605                 if (ret == 0 && prefix != NULL &&
01606                     memcmp(key.data, prefix, strlen(prefix)) != 0) {
01607                         /*
01608                          * Free space from DB_DBT_MALLOC
01609                          */
01610                         __os_ufree(dbp->dbenv, data.data);
01611                         break;
01612                 }
01613         }
01614 out1:
01615         (void)dbc->c_close(dbc);
01616         if (result == TCL_OK)
01617                 Tcl_SetObjResult(interp, retlist);
01618 out:
01619         /*
01620          * _GetGlobPrefix(), the function which allocates prefix, works
01621          * by copying and condensing another string.  Thus prefix may
01622          * have multiple nuls at the end, so we free using __os_free().
01623          */
01624         if (prefix != NULL)
01625                 __os_free(dbp->dbenv, prefix);
01626         if (dtmp != NULL && freedata)
01627                 __os_free(dbp->dbenv, dtmp);
01628         if (ktmp != NULL && freekey)
01629                 __os_free(dbp->dbenv, ktmp);
01630         return (result);
01631 }
01632 
01633 /*
01634  * tcl_db_delete --
01635  */
01636 static int
01637 tcl_DbDelete(interp, objc, objv, dbp)
01638         Tcl_Interp *interp;             /* Interpreter */
01639         int objc;                       /* How many arguments? */
01640         Tcl_Obj *CONST objv[];          /* The argument objects */
01641         DB *dbp;                        /* Database pointer */
01642 {
01643         static const char *dbdelopts[] = {
01644                 "-glob",
01645                 "-txn",
01646                 NULL
01647         };
01648         enum dbdelopts {
01649                 DBDEL_GLOB,
01650                 DBDEL_TXN
01651         };
01652         DBC *dbc;
01653         DBT key, data;
01654         DBTYPE type;
01655         DB_TXN *txn;
01656         void *ktmp;
01657         db_recno_t recno;
01658         int freekey, i, optindex, result, ret;
01659         u_int32_t flag;
01660         char *arg, *pattern, *prefix, msg[MSG_SIZE];
01661 
01662         result = TCL_OK;
01663         freekey = 0;
01664         pattern = prefix = NULL;
01665         txn = NULL;
01666         if (objc < 3) {
01667                 Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
01668                 return (TCL_ERROR);
01669         }
01670 
01671         ktmp = NULL;
01672         memset(&key, 0, sizeof(key));
01673         /*
01674          * The first arg must be -glob, -txn or a list of keys.
01675          */
01676         i = 2;
01677         while (i < objc) {
01678                 if (Tcl_GetIndexFromObj(interp, objv[i], dbdelopts, "option",
01679                     TCL_EXACT, &optindex) != TCL_OK) {
01680                         /*
01681                          * If we don't have a -glob or -txn, then the remaining
01682                          * args must be exact keys.  Reset the result so we
01683                          * don't get an errant error message if there is another
01684                          * error.
01685                          */
01686                         if (IS_HELP(objv[i]) == TCL_OK)
01687                                 return (TCL_OK);
01688                         Tcl_ResetResult(interp);
01689                         break;
01690                 }
01691                 i++;
01692                 switch ((enum dbdelopts)optindex) {
01693                 case DBDEL_TXN:
01694                         if (i == objc) {
01695                                 /*
01696                                  * Someone could conceivably have a key of
01697                                  * the same name.  So just break and use it.
01698                                  */
01699                                 i--;
01700                                 break;
01701                         }
01702                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
01703                         txn = NAME_TO_TXN(arg);
01704                         if (txn == NULL) {
01705                                 snprintf(msg, MSG_SIZE,
01706                                     "Delete: Invalid txn: %s\n", arg);
01707                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
01708                                 result = TCL_ERROR;
01709                         }
01710                         break;
01711                 case DBDEL_GLOB:
01712                         /*
01713                          * Get the pattern.  Get the prefix and use cursors to
01714                          * get all the data items.
01715                          */
01716                         if (i == objc) {
01717                                 /*
01718                                  * Someone could conceivably have a key of
01719                                  * the same name.  So just break and use it.
01720                                  */
01721                                 i--;
01722                                 break;
01723                         }
01724                         pattern = Tcl_GetStringFromObj(objv[i++], NULL);
01725                         break;
01726                 }
01727                 if (result != TCL_OK)
01728                         break;
01729         }
01730 
01731         if (result != TCL_OK)
01732                 goto out;
01733         /*
01734          * XXX
01735          * For consistency with get, we have decided for the moment, to
01736          * allow -glob, or one key, not many.  The code was originally
01737          * written to take many keys and we'll leave it that way, because
01738          * tcl_DbGet may one day accept many disjoint keys to get, rather
01739          * than one, and at that time we'd make delete be consistent.  In
01740          * any case, the code is already here and there is no need to remove,
01741          * just check that we only have one arg left.
01742          *
01743          * If we have a pattern AND more keys to process, there is an error.
01744          * Either we have some number of exact keys, or we have a pattern.
01745          */
01746         if (pattern == NULL) {
01747                 if (i != (objc - 1)) {
01748                         Tcl_WrongNumArgs(
01749                             interp, 2, objv, "?args? -glob pattern | key");
01750                         result = TCL_ERROR;
01751                         goto out;
01752                 }
01753         } else {
01754                 if (i != objc) {
01755                         Tcl_WrongNumArgs(
01756                             interp, 2, objv, "?args? -glob pattern | key");
01757                         result = TCL_ERROR;
01758                         goto out;
01759                 }
01760         }
01761 
01762         /*
01763          * If we have remaining args, they are all exact keys.  Call
01764          * DB->del on each of those keys.
01765          *
01766          * If it is a RECNO database, the key is a record number and must be
01767          * setup up to contain a db_recno_t.  Otherwise the key is a "string".
01768          */
01769         (void)dbp->get_type(dbp, &type);
01770         ret = 0;
01771         while (i < objc && ret == 0) {
01772                 memset(&key, 0, sizeof(key));
01773                 if (type == DB_RECNO || type == DB_QUEUE) {
01774                         result = _GetUInt32(interp, objv[i++], &recno);
01775                         if (result == TCL_OK) {
01776                                 key.data = &recno;
01777                                 key.size = sizeof(db_recno_t);
01778                         } else
01779                                 return (result);
01780                 } else {
01781                         ret = _CopyObjBytes(interp, objv[i++], &ktmp,
01782                             &key.size, &freekey);
01783                         if (ret != 0) {
01784                                 result = _ReturnSetup(interp, ret,
01785                                     DB_RETOK_DBDEL(ret), "db del");
01786                                 return (result);
01787                         }
01788                         key.data = ktmp;
01789                 }
01790                 _debug_check();
01791                 ret = dbp->del(dbp, txn, &key, 0);
01792                 /*
01793                  * If we have any error, set up return result and stop
01794                  * processing keys.
01795                  */
01796                 if (ktmp != NULL && freekey)
01797                         __os_free(dbp->dbenv, ktmp);
01798                 if (ret != 0)
01799                         break;
01800         }
01801         result = _ReturnSetup(interp, ret, DB_RETOK_DBDEL(ret), "db del");
01802 
01803         /*
01804          * At this point we've either finished or, if we have a pattern,
01805          * we go to the nearest one and step forward until we don't
01806          * have any more that match the pattern prefix.
01807          */
01808         if (pattern) {
01809                 ret = dbp->cursor(dbp, txn, &dbc, 0);
01810                 if (ret != 0) {
01811                         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
01812                             "db cursor");
01813                         goto out;
01814                 }
01815                 /*
01816                  * Note, prefix is returned in new space.  Must free it.
01817                  */
01818                 memset(&key, 0, sizeof(key));
01819                 memset(&data, 0, sizeof(data));
01820                 ret = _GetGlobPrefix(pattern, &prefix);
01821                 if (ret) {
01822                         result = TCL_ERROR;
01823                         Tcl_SetResult(interp,
01824                             "Unable to allocate pattern space", TCL_STATIC);
01825                         goto out;
01826                 }
01827                 key.data = prefix;
01828                 key.size = strlen(prefix);
01829                 if (strlen(prefix) == 0)
01830                         flag = DB_FIRST;
01831                 else
01832                         flag = DB_SET_RANGE;
01833                 ret = dbc->c_get(dbc, &key, &data, flag);
01834                 while (ret == 0 &&
01835                     memcmp(key.data, prefix, strlen(prefix)) == 0) {
01836                         /*
01837                          * Each time through here the cursor is pointing
01838                          * at the current valid item.  Delete it and
01839                          * move ahead.
01840                          */
01841                         _debug_check();
01842                         ret = dbc->c_del(dbc, 0);
01843                         if (ret != 0) {
01844                                 result = _ReturnSetup(interp, ret,
01845                                     DB_RETOK_DBCDEL(ret), "db c_del");
01846                                 break;
01847                         }
01848                         /*
01849                          * Deleted the current, now move to the next item
01850                          * in the list, check if it matches the prefix pattern.
01851                          */
01852                         memset(&key, 0, sizeof(key));
01853                         memset(&data, 0, sizeof(data));
01854                         ret = dbc->c_get(dbc, &key, &data, DB_NEXT);
01855                 }
01856                 if (ret == DB_NOTFOUND)
01857                         ret = 0;
01858                 /*
01859                  * _GetGlobPrefix(), the function which allocates prefix, works
01860                  * by copying and condensing another string.  Thus prefix may
01861                  * have multiple nuls at the end, so we free using __os_free().
01862                  */
01863                 __os_free(dbp->dbenv, prefix);
01864                 (void)dbc->c_close(dbc);
01865                 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db del");
01866         }
01867 out:
01868         return (result);
01869 }
01870 
01871 /*
01872  * tcl_db_cursor --
01873  */
01874 static int
01875 tcl_DbCursor(interp, objc, objv, dbp, dbcp)
01876         Tcl_Interp *interp;             /* Interpreter */
01877         int objc;                       /* How many arguments? */
01878         Tcl_Obj *CONST objv[];          /* The argument objects */
01879         DB *dbp;                        /* Database pointer */
01880         DBC **dbcp;                     /* Return cursor pointer */
01881 {
01882         static const char *dbcuropts[] = {
01883 #ifdef CONFIG_TEST
01884                 "-read_committed",
01885                 "-read_uncommitted",
01886                 "-update",
01887 #endif
01888                 "-txn",
01889                 NULL
01890         };
01891         enum dbcuropts {
01892 #ifdef CONFIG_TEST
01893                 DBCUR_READ_COMMITTED,
01894                 DBCUR_READ_UNCOMMITTED,
01895                 DBCUR_UPDATE,
01896 #endif
01897                 DBCUR_TXN
01898         };
01899         DB_TXN *txn;
01900         u_int32_t flag;
01901         int i, optindex, result, ret;
01902         char *arg, msg[MSG_SIZE];
01903 
01904         result = TCL_OK;
01905         flag = 0;
01906         txn = NULL;
01907         i = 2;
01908         while (i < objc) {
01909                 if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
01910                     TCL_EXACT, &optindex) != TCL_OK) {
01911                         result = IS_HELP(objv[i]);
01912                         goto out;
01913                 }
01914                 i++;
01915                 switch ((enum dbcuropts)optindex) {
01916 #ifdef CONFIG_TEST
01917                 case DBCUR_READ_COMMITTED:
01918                         flag |= DB_READ_COMMITTED;
01919                         break;
01920                 case DBCUR_READ_UNCOMMITTED:
01921                         flag |= DB_READ_UNCOMMITTED;
01922                         break;
01923                 case DBCUR_UPDATE:
01924                         flag |= DB_WRITECURSOR;
01925                         break;
01926 #endif
01927                 case DBCUR_TXN:
01928                         if (i == objc) {
01929                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
01930                                 result = TCL_ERROR;
01931                                 break;
01932                         }
01933                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
01934                         txn = NAME_TO_TXN(arg);
01935                         if (txn == NULL) {
01936                                 snprintf(msg, MSG_SIZE,
01937                                     "Cursor: Invalid txn: %s\n", arg);
01938                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
01939                                 result = TCL_ERROR;
01940                         }
01941                         break;
01942                 }
01943                 if (result != TCL_OK)
01944                         break;
01945         }
01946         if (result != TCL_OK)
01947                 goto out;
01948 
01949         _debug_check();
01950         ret = dbp->cursor(dbp, txn, dbcp, flag);
01951         if (ret != 0)
01952                 result = _ErrorSetup(interp, ret, "db cursor");
01953 out:
01954         return (result);
01955 }
01956 
01957 /*
01958  * tcl_DbAssociate --
01959  *      Call DB->associate().
01960  */
01961 static int
01962 tcl_DbAssociate(interp, objc, objv, dbp)
01963         Tcl_Interp *interp;
01964         int objc;
01965         Tcl_Obj *CONST objv[];
01966         DB *dbp;
01967 {
01968         static const char *dbaopts[] = {
01969                 "-create",
01970                 "-immutable_key",
01971                 "-txn",
01972                 NULL
01973         };
01974         enum dbaopts {
01975                 DBA_CREATE,
01976                 DBA_IMMUTABLE_KEY,
01977                 DBA_TXN
01978         };
01979         DB *sdbp;
01980         DB_TXN *txn;
01981         DBTCL_INFO *sdbip;
01982         int i, optindex, result, ret;
01983         char *arg, msg[MSG_SIZE];
01984         u_int32_t flag;
01985 #ifdef CONFIG_TEST
01986         /*
01987          * When calling DB->associate over RPC, the Tcl API uses
01988          * special flags that the RPC server interprets to set the
01989          * callback correctly.
01990          */
01991         const char *cbname;
01992         struct {
01993                 const char *name;
01994                 u_int32_t flag;
01995         } *cb, callbacks[] = {
01996                 { "", 0 }, /* A NULL callback in Tcl. */
01997                 { "_s_reversedata", DB_RPC2ND_REVERSEDATA },
01998                 { "_s_noop", DB_RPC2ND_NOOP },
01999                 { "_s_concatkeydata", DB_RPC2ND_CONCATKEYDATA },
02000                 { "_s_concatdatakey", DB_RPC2ND_CONCATDATAKEY },
02001                 { "_s_reverseconcat", DB_RPC2ND_REVERSECONCAT },
02002                 { "_s_truncdata", DB_RPC2ND_TRUNCDATA },
02003                 { "_s_reversedata", DB_RPC2ND_REVERSEDATA },
02004                 { "_s_constant", DB_RPC2ND_CONSTANT },
02005                 { "sj_getzip", DB_RPC2ND_GETZIP },
02006                 { "sj_getname", DB_RPC2ND_GETNAME },
02007                 { NULL, 0 }
02008         };
02009 #endif
02010 
02011         txn = NULL;
02012         result = TCL_OK;
02013         flag = 0;
02014         if (objc < 2) {
02015                 Tcl_WrongNumArgs(interp, 2, objv, "[callback] secondary");
02016                 return (TCL_ERROR);
02017         }
02018 
02019         i = 2;
02020         while (i < objc) {
02021                 if (Tcl_GetIndexFromObj(interp, objv[i], dbaopts, "option",
02022                     TCL_EXACT, &optindex) != TCL_OK) {
02023                         result = IS_HELP(objv[i]);
02024                         if (result == TCL_OK)
02025                                 return (result);
02026                         result = TCL_OK;
02027                         Tcl_ResetResult(interp);
02028                         break;
02029                 }
02030                 i++;
02031                 switch ((enum dbaopts)optindex) {
02032                 case DBA_CREATE:
02033                         flag |= DB_CREATE;
02034                         break;
02035                 case DBA_IMMUTABLE_KEY:
02036                         flag |= DB_IMMUTABLE_KEY;
02037                         break;
02038                 case DBA_TXN:
02039                         if (i > (objc - 1)) {
02040                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
02041                                 result = TCL_ERROR;
02042                                 break;
02043                         }
02044                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
02045                         txn = NAME_TO_TXN(arg);
02046                         if (txn == NULL) {
02047                                 snprintf(msg, MSG_SIZE,
02048                                     "Associate: Invalid txn: %s\n", arg);
02049                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
02050                                 result = TCL_ERROR;
02051                         }
02052                         break;
02053                 }
02054         }
02055         if (result != TCL_OK)
02056                 return (result);
02057 
02058         /*
02059          * Better be 1 or 2 args left.  The last arg must be the sdb
02060          * handle.  If 2 args then objc-2 is the callback proc, else
02061          * we have a NULL callback.
02062          */
02063         /* Get the secondary DB handle. */
02064         arg = Tcl_GetStringFromObj(objv[objc - 1], NULL);
02065         sdbp = NAME_TO_DB(arg);
02066         if (sdbp == NULL) {
02067                 snprintf(msg, MSG_SIZE,
02068                     "Associate: Invalid database handle: %s\n", arg);
02069                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
02070                 return (TCL_ERROR);
02071         }
02072 
02073         /*
02074          * The callback is simply a Tcl object containing the name
02075          * of the callback proc, which is the second-to-last argument.
02076          *
02077          * Note that the callback needs to go in the *secondary* DB handle's
02078          * info struct;  we may have multiple secondaries with different
02079          * callbacks.
02080          */
02081         sdbip = (DBTCL_INFO *)sdbp->api_internal;
02082 
02083 #ifdef CONFIG_TEST
02084         if (i != objc - 1 && RPC_ON(dbp->dbenv)) {
02085                 /*
02086                  * The flag values allowed to DB->associate may have changed to
02087                  * overlap with the range we've chosen.  If this happens, we
02088                  * need to reset all of the RPC_2ND_* flags to a new range.
02089                  */
02090                 if ((flag & DB_RPC2ND_MASK) != 0) {
02091                         snprintf(msg, MSG_SIZE,
02092                             "RPC secondary flags overlap -- recalculate!\n");
02093                         Tcl_SetResult(interp, msg, TCL_VOLATILE);
02094                         return (TCL_ERROR);
02095                 }
02096 
02097                 cbname = Tcl_GetStringFromObj(objv[objc - 2], NULL);
02098                 for (cb = callbacks; cb->name != NULL; cb++)
02099                         if (strcmp(cb->name, cbname) == 0) {
02100                                 flag |= cb->flag;
02101                                 break;
02102                         }
02103 
02104                 if (cb->name == NULL) {
02105                         snprintf(msg, MSG_SIZE,
02106                             "Associate: unknown callback: %s\n", cbname);
02107                         Tcl_SetResult(interp, msg, TCL_VOLATILE);
02108                         return (TCL_ERROR);
02109                 }
02110 
02111                 ret = dbp->associate(dbp, txn, sdbp, NULL, flag);
02112 
02113                 /*
02114                  * The primary reference isn't set when calling through
02115                  * the RPC server, but the Tcl API peeks at it in other
02116                  * places (see tcl_DbGet).
02117                  */
02118                 if (ret == 0)
02119                         sdbp->s_primary = dbp;
02120         } else if (i != objc - 1) {
02121 #else
02122         if (i != objc - 1) {
02123 #endif
02124                 /*
02125                  * We have 2 args, get the callback.
02126                  */
02127                 sdbip->i_second_call = objv[objc - 2];
02128                 Tcl_IncrRefCount(sdbip->i_second_call);
02129 
02130                 /* Now call associate. */
02131                 _debug_check();
02132                 ret = dbp->associate(dbp, txn, sdbp, tcl_second_call, flag);
02133         } else {
02134                 /*
02135                  * We have a NULL callback.
02136                  */
02137                 sdbip->i_second_call = NULL;
02138                 ret = dbp->associate(dbp, txn, sdbp, NULL, flag);
02139         }
02140         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "associate");
02141 
02142         return (result);
02143 }
02144 
02145 /*
02146  * tcl_second_call --
02147  *      Callback function for secondary indices.  Get the callback
02148  *      out of ip->i_second_call and call it.
02149  */
02150 static int
02151 tcl_second_call(dbp, pkey, data, skey)
02152         DB *dbp;
02153         const DBT *pkey, *data;
02154         DBT *skey;
02155 {
02156         DBTCL_INFO *ip;
02157         Tcl_Interp *interp;
02158         Tcl_Obj *pobj, *dobj, *objv[3];
02159         size_t len;
02160         int ilen, result, ret;
02161         void *retbuf, *databuf;
02162 
02163         ip = (DBTCL_INFO *)dbp->api_internal;
02164         interp = ip->i_interp;
02165         objv[0] = ip->i_second_call;
02166 
02167         /*
02168          * Create two ByteArray objects, with the contents of the pkey
02169          * and data DBTs that are our inputs.
02170          */
02171         pobj = Tcl_NewByteArrayObj(pkey->data, (int)pkey->size);
02172         Tcl_IncrRefCount(pobj);
02173         dobj = Tcl_NewByteArrayObj(data->data, (int)data->size);
02174         Tcl_IncrRefCount(dobj);
02175 
02176         objv[1] = pobj;
02177         objv[2] = dobj;
02178 
02179         result = Tcl_EvalObjv(interp, 3, objv, 0);
02180 
02181         Tcl_DecrRefCount(pobj);
02182         Tcl_DecrRefCount(dobj);
02183 
02184         if (result != TCL_OK) {
02185                 __db_err(dbp->dbenv,
02186                     "Tcl callback function failed with code %d", result);
02187                 return (EINVAL);
02188         }
02189 
02190         retbuf = Tcl_GetByteArrayFromObj(Tcl_GetObjResult(interp), &ilen);
02191         len = (size_t)ilen;
02192 
02193         /*
02194          * retbuf is owned by Tcl; copy it into malloc'ed memory.
02195          * We need to use __os_umalloc rather than ufree because this will
02196          * be freed by DB using __os_ufree--the DB_DBT_APPMALLOC flag
02197          * tells DB to free application-allocated memory.
02198          */
02199         if ((ret = __os_umalloc(dbp->dbenv, len, &databuf)) != 0)
02200                 return (ret);
02201         memcpy(databuf, retbuf, len);
02202 
02203         skey->data = databuf;
02204         skey->size = len;
02205         F_SET(skey, DB_DBT_APPMALLOC);
02206 
02207         return (0);
02208 }
02209 
02210 /*
02211  * tcl_db_join --
02212  */
02213 static int
02214 tcl_DbJoin(interp, objc, objv, dbp, dbcp)
02215         Tcl_Interp *interp;             /* Interpreter */
02216         int objc;                       /* How many arguments? */
02217         Tcl_Obj *CONST objv[];          /* The argument objects */
02218         DB *dbp;                        /* Database pointer */
02219         DBC **dbcp;                     /* Cursor pointer */
02220 {
02221         static const char *dbjopts[] = {
02222                 "-nosort",
02223                 NULL
02224         };
02225         enum dbjopts {
02226                 DBJ_NOSORT
02227         };
02228         DBC **listp;
02229         size_t size;
02230         u_int32_t flag;
02231         int adj, i, j, optindex, result, ret;
02232         char *arg, msg[MSG_SIZE];
02233 
02234         result = TCL_OK;
02235         flag = 0;
02236         if (objc < 3) {
02237                 Tcl_WrongNumArgs(interp, 2, objv, "curs1 curs2 ...");
02238                 return (TCL_ERROR);
02239         }
02240 
02241         for (adj = i = 2; i < objc; i++) {
02242                 if (Tcl_GetIndexFromObj(interp, objv[i], dbjopts, "option",
02243                     TCL_EXACT, &optindex) != TCL_OK) {
02244                         result = IS_HELP(objv[i]);
02245                         if (result == TCL_OK)
02246                                 return (result);
02247                         result = TCL_OK;
02248                         Tcl_ResetResult(interp);
02249                         break;
02250                 }
02251                 switch ((enum dbjopts)optindex) {
02252                 case DBJ_NOSORT:
02253                         flag |= DB_JOIN_NOSORT;
02254                         adj++;
02255                         break;
02256                 }
02257         }
02258         if (result != TCL_OK)
02259                 return (result);
02260         /*
02261          * Allocate one more for NULL ptr at end of list.
02262          */
02263         size = sizeof(DBC *) * (size_t)((objc - adj) + 1);
02264         ret = __os_malloc(dbp->dbenv, size, &listp);
02265         if (ret != 0) {
02266                 Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
02267                 return (TCL_ERROR);
02268         }
02269 
02270         memset(listp, 0, size);
02271         for (j = 0, i = adj; i < objc; i++, j++) {
02272                 arg = Tcl_GetStringFromObj(objv[i], NULL);
02273                 listp[j] = NAME_TO_DBC(arg);
02274                 if (listp[j] == NULL) {
02275                         snprintf(msg, MSG_SIZE,
02276                             "Join: Invalid cursor: %s\n", arg);
02277                         Tcl_SetResult(interp, msg, TCL_VOLATILE);
02278                         result = TCL_ERROR;
02279                         goto out;
02280                 }
02281         }
02282         listp[j] = NULL;
02283         _debug_check();
02284         ret = dbp->join(dbp, listp, dbcp, flag);
02285         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db join");
02286 
02287 out:
02288         __os_free(dbp->dbenv, listp);
02289         return (result);
02290 }
02291 
02292 /*
02293  * tcl_db_getjoin --
02294  */
02295 static int
02296 tcl_DbGetjoin(interp, objc, objv, dbp)
02297         Tcl_Interp *interp;             /* Interpreter */
02298         int objc;                       /* How many arguments? */
02299         Tcl_Obj *CONST objv[];          /* The argument objects */
02300         DB *dbp;                        /* Database pointer */
02301 {
02302         static const char *dbgetjopts[] = {
02303 #ifdef CONFIG_TEST
02304                 "-nosort",
02305 #endif
02306                 "-txn",
02307                 NULL
02308         };
02309         enum dbgetjopts {
02310 #ifdef CONFIG_TEST
02311                 DBGETJ_NOSORT,
02312 #endif
02313                 DBGETJ_TXN
02314         };
02315         DB_TXN *txn;
02316         DB *elemdbp;
02317         DBC **listp;
02318         DBC *dbc;
02319         DBT key, data;
02320         Tcl_Obj **elemv, *retlist;
02321         void *ktmp;
02322         size_t size;
02323         u_int32_t flag;
02324         int adj, elemc, freekey, i, j, optindex, result, ret;
02325         char *arg, msg[MSG_SIZE];
02326 
02327         result = TCL_OK;
02328         flag = 0;
02329         ktmp = NULL;
02330         freekey = 0;
02331         if (objc < 3) {
02332                 Tcl_WrongNumArgs(interp, 2, objv, "{db1 key1} {db2 key2} ...");
02333                 return (TCL_ERROR);
02334         }
02335 
02336         txn = NULL;
02337         i = 2;
02338         adj = i;
02339         while (i < objc) {
02340                 if (Tcl_GetIndexFromObj(interp, objv[i], dbgetjopts, "option",
02341                     TCL_EXACT, &optindex) != TCL_OK) {
02342                         result = IS_HELP(objv[i]);
02343                         if (result == TCL_OK)
02344                                 return (result);
02345                         result = TCL_OK;
02346                         Tcl_ResetResult(interp);
02347                         break;
02348                 }
02349                 i++;
02350                 switch ((enum dbgetjopts)optindex) {
02351 #ifdef CONFIG_TEST
02352                 case DBGETJ_NOSORT:
02353                         flag |= DB_JOIN_NOSORT;
02354                         adj++;
02355                         break;
02356 #endif
02357                 case DBGETJ_TXN:
02358                         if (i == objc) {
02359                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
02360                                 result = TCL_ERROR;
02361                                 break;
02362                         }
02363                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
02364                         txn = NAME_TO_TXN(arg);
02365                         adj += 2;
02366                         if (txn == NULL) {
02367                                 snprintf(msg, MSG_SIZE,
02368                                     "GetJoin: Invalid txn: %s\n", arg);
02369                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
02370                                 result = TCL_ERROR;
02371                         }
02372                         break;
02373                 }
02374         }
02375         if (result != TCL_OK)
02376                 return (result);
02377         size = sizeof(DBC *) * (size_t)((objc - adj) + 1);
02378         ret = __os_malloc(NULL, size, &listp);
02379         if (ret != 0) {
02380                 Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
02381                 return (TCL_ERROR);
02382         }
02383 
02384         memset(listp, 0, size);
02385         for (j = 0, i = adj; i < objc; i++, j++) {
02386                 /*
02387                  * Get each sublist as {db key}
02388                  */
02389                 result = Tcl_ListObjGetElements(interp, objv[i],
02390                     &elemc, &elemv);
02391                 if (elemc != 2) {
02392                         Tcl_SetResult(interp, "Lists must be {db key}",
02393                             TCL_STATIC);
02394                         result = TCL_ERROR;
02395                         goto out;
02396                 }
02397                 /*
02398                  * Get a pointer to that open db.  Then, open a cursor in
02399                  * that db, and go to the "key" place.
02400                  */
02401                 elemdbp = NAME_TO_DB(Tcl_GetStringFromObj(elemv[0], NULL));
02402                 if (elemdbp == NULL) {
02403                         snprintf(msg, MSG_SIZE, "Get_join: Invalid db: %s\n",
02404                             Tcl_GetStringFromObj(elemv[0], NULL));
02405                         Tcl_SetResult(interp, msg, TCL_VOLATILE);
02406                         result = TCL_ERROR;
02407                         goto out;
02408                 }
02409                 ret = elemdbp->cursor(elemdbp, txn, &listp[j], 0);
02410                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
02411                     "db cursor")) == TCL_ERROR)
02412                         goto out;
02413                 memset(&key, 0, sizeof(key));
02414                 memset(&data, 0, sizeof(data));
02415                 ret = _CopyObjBytes(interp, elemv[elemc-1], &ktmp,
02416                     &key.size, &freekey);
02417                 if (ret != 0) {
02418                         result = _ReturnSetup(interp, ret,
02419                             DB_RETOK_STD(ret), "db join");
02420                         goto out;
02421                 }
02422                 key.data = ktmp;
02423                 ret = (listp[j])->c_get(listp[j], &key, &data, DB_SET);
02424                 if ((result = _ReturnSetup(interp, ret, DB_RETOK_DBCGET(ret),
02425                     "db cget")) == TCL_ERROR)
02426                         goto out;
02427         }
02428         listp[j] = NULL;
02429         _debug_check();
02430         ret = dbp->join(dbp, listp, &dbc, flag);
02431         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db join");
02432         if (result == TCL_ERROR)
02433                 goto out;
02434 
02435         retlist = Tcl_NewListObj(0, NULL);
02436         while (ret == 0 && result == TCL_OK) {
02437                 memset(&key, 0, sizeof(key));
02438                 memset(&data, 0, sizeof(data));
02439                 key.flags |= DB_DBT_MALLOC;
02440                 data.flags |= DB_DBT_MALLOC;
02441                 ret = dbc->c_get(dbc, &key, &data, 0);
02442                 /*
02443                  * Build up our {name value} sublist
02444                  */
02445                 if (ret == 0) {
02446                         result = _SetListElem(interp, retlist,
02447                             key.data, key.size,
02448                             data.data, data.size);
02449                         __os_ufree(dbp->dbenv, key.data);
02450                         __os_ufree(dbp->dbenv, data.data);
02451                 }
02452         }
02453         (void)dbc->c_close(dbc);
02454         if (result == TCL_OK)
02455                 Tcl_SetObjResult(interp, retlist);
02456 out:
02457         if (ktmp != NULL && freekey)
02458                 __os_free(dbp->dbenv, ktmp);
02459         while (j) {
02460                 if (listp[j])
02461                         (void)(listp[j])->c_close(listp[j]);
02462                 j--;
02463         }
02464         __os_free(dbp->dbenv, listp);
02465         return (result);
02466 }
02467 
02468 /*
02469  * tcl_DbGetFlags --
02470  */
02471 static int
02472 tcl_DbGetFlags(interp, objc, objv, dbp)
02473         Tcl_Interp *interp;             /* Interpreter */
02474         int objc;                       /* How many arguments? */
02475         Tcl_Obj *CONST objv[];          /* The argument objects */
02476         DB *dbp;                        /* Database pointer */
02477 {
02478         int i, ret, result;
02479         u_int32_t flags;
02480         char buf[512];
02481         Tcl_Obj *res;
02482 
02483         static const struct {
02484                 u_int32_t flag;
02485                 char *arg;
02486         } db_flags[] = {
02487                 { DB_CHKSUM, "-chksum" },
02488                 { DB_DUP, "-dup" },
02489                 { DB_DUPSORT, "-dupsort" },
02490                 { DB_ENCRYPT, "-encrypt" },
02491                 { DB_INORDER, "-inorder" },
02492                 { DB_TXN_NOT_DURABLE, "-notdurable" },
02493                 { DB_RECNUM, "-recnum" },
02494                 { DB_RENUMBER, "-renumber" },
02495                 { DB_REVSPLITOFF, "-revsplitoff" },
02496                 { DB_SNAPSHOT, "-snapshot" },
02497                 { 0, NULL }
02498         };
02499 
02500         if (objc != 2) {
02501                 Tcl_WrongNumArgs(interp, 1, objv, NULL);
02502                 return (TCL_ERROR);
02503         }
02504 
02505         ret = dbp->get_flags(dbp, &flags);
02506         if ((result = _ReturnSetup(
02507             interp, ret, DB_RETOK_STD(ret), "db get_flags")) == TCL_OK) {
02508                 buf[0] = '\0';
02509 
02510                 for (i = 0; db_flags[i].flag != 0; i++)
02511                         if (LF_ISSET(db_flags[i].flag)) {
02512                                 if (strlen(buf) > 0)
02513                                         (void)strncat(buf, " ", sizeof(buf));
02514                                 (void)strncat(
02515                                     buf, db_flags[i].arg, sizeof(buf));
02516                         }
02517 
02518                 res = NewStringObj(buf, strlen(buf));
02519                 Tcl_SetObjResult(interp, res);
02520         }
02521 
02522         return (result);
02523 }
02524 
02525 /*
02526  * tcl_DbGetOpenFlags --
02527  */
02528 static int
02529 tcl_DbGetOpenFlags(interp, objc, objv, dbp)
02530         Tcl_Interp *interp;             /* Interpreter */
02531         int objc;                       /* How many arguments? */
02532         Tcl_Obj *CONST objv[];          /* The argument objects */
02533         DB *dbp;                        /* Database pointer */
02534 {
02535         int i, ret, result;
02536         u_int32_t flags;
02537         char buf[512];
02538         Tcl_Obj *res;
02539 
02540         static const struct {
02541                 u_int32_t flag;
02542                 char *arg;
02543         } open_flags[] = {
02544                 { DB_AUTO_COMMIT,       "-auto_commit" },
02545                 { DB_CREATE,            "-create" },
02546                 { DB_EXCL,              "-excl" },
02547                 { DB_NOMMAP,            "-nommap" },
02548                 { DB_RDONLY,            "-rdonly" },
02549                 { DB_READ_COMMITTED,    "-read_committed" },
02550                 { DB_READ_UNCOMMITTED,  "-read_uncommitted" },
02551                 { DB_THREAD,            "-thread" },
02552                 { DB_TRUNCATE,          "-truncate" },
02553                 { 0, NULL }
02554         };
02555 
02556         if (objc != 2) {
02557                 Tcl_WrongNumArgs(interp, 1, objv, NULL);
02558                 return (TCL_ERROR);
02559         }
02560 
02561         ret = dbp->get_open_flags(dbp, &flags);
02562         if ((result = _ReturnSetup(
02563             interp, ret, DB_RETOK_STD(ret), "db get_open_flags")) == TCL_OK) {
02564                 buf[0] = '\0';
02565 
02566                 for (i = 0; open_flags[i].flag != 0; i++)
02567                         if (LF_ISSET(open_flags[i].flag)) {
02568                                 if (strlen(buf) > 0)
02569                                         (void)strncat(buf, " ", sizeof(buf));
02570                                 (void)strncat(
02571                                     buf, open_flags[i].arg, sizeof(buf));
02572                         }
02573 
02574                 res = NewStringObj(buf, strlen(buf));
02575                 Tcl_SetObjResult(interp, res);
02576         }
02577 
02578         return (result);
02579 }
02580 
02581 /*
02582  * tcl_DbCount --
02583  */
02584 static int
02585 tcl_DbCount(interp, objc, objv, dbp)
02586         Tcl_Interp *interp;             /* Interpreter */
02587         int objc;                       /* How many arguments? */
02588         Tcl_Obj *CONST objv[];          /* The argument objects */
02589         DB *dbp;                        /* Database pointer */
02590 {
02591         Tcl_Obj *res;
02592         DBC *dbc;
02593         DBT key, data;
02594         void *ktmp;
02595         db_recno_t count, recno;
02596         int freekey, result, ret;
02597 
02598         res = NULL;
02599         count = 0;
02600         freekey = ret = 0;
02601         ktmp = NULL;
02602         result = TCL_OK;
02603 
02604         if (objc != 3) {
02605                 Tcl_WrongNumArgs(interp, 2, objv, "key");
02606                 return (TCL_ERROR);
02607         }
02608 
02609         /*
02610          * Get the count for our key.
02611          * We do this by getting a cursor for this DB.  Moving the cursor
02612          * to the set location, and getting a count on that cursor.
02613          */
02614         memset(&key, 0, sizeof(key));
02615         memset(&data, 0, sizeof(data));
02616 
02617         /*
02618          * If it's a queue or recno database, we must make sure to
02619          * treat the key as a recno rather than as a byte string.
02620          */
02621         if (dbp->type == DB_RECNO || dbp->type == DB_QUEUE) {
02622                 result = _GetUInt32(interp, objv[2], &recno);
02623                 if (result == TCL_OK) {
02624                         key.data = &recno;
02625                         key.size = sizeof(db_recno_t);
02626                 } else
02627                         return (result);
02628         } else {
02629                 ret = _CopyObjBytes(interp, objv[2], &ktmp,
02630                     &key.size, &freekey);
02631                 if (ret != 0) {
02632                         result = _ReturnSetup(interp, ret,
02633                             DB_RETOK_STD(ret), "db count");
02634                         return (result);
02635                 }
02636                 key.data = ktmp;
02637         }
02638         _debug_check();
02639         ret = dbp->cursor(dbp, NULL, &dbc, 0);
02640         if (ret != 0) {
02641                 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
02642                     "db cursor");
02643                 goto out;
02644         }
02645         /*
02646          * Move our cursor to the key.
02647          */
02648         ret = dbc->c_get(dbc, &key, &data, DB_SET);
02649         if (ret == DB_KEYEMPTY || ret == DB_NOTFOUND)
02650                 count = 0;
02651         else {
02652                 ret = dbc->c_count(dbc, &count, 0);
02653                 if (ret != 0) {
02654                         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
02655                             "db c count");
02656                         goto out;
02657                 }
02658         }
02659         res = Tcl_NewWideIntObj((Tcl_WideInt)count);
02660         Tcl_SetObjResult(interp, res);
02661 
02662 out:    if (ktmp != NULL && freekey)
02663                 __os_free(dbp->dbenv, ktmp);
02664         (void)dbc->c_close(dbc);
02665         return (result);
02666 }
02667 
02668 #ifdef CONFIG_TEST
02669 /*
02670  * tcl_DbKeyRange --
02671  */
02672 static int
02673 tcl_DbKeyRange(interp, objc, objv, dbp)
02674         Tcl_Interp *interp;             /* Interpreter */
02675         int objc;                       /* How many arguments? */
02676         Tcl_Obj *CONST objv[];          /* The argument objects */
02677         DB *dbp;                        /* Database pointer */
02678 {
02679         static const char *dbkeyropts[] = {
02680                 "-txn",
02681                 NULL
02682         };
02683         enum dbkeyropts {
02684                 DBKEYR_TXN
02685         };
02686         DB_TXN *txn;
02687         DB_KEY_RANGE range;
02688         DBT key;
02689         DBTYPE type;
02690         Tcl_Obj *myobjv[3], *retlist;
02691         void *ktmp;
02692         db_recno_t recno;
02693         u_int32_t flag;
02694         int freekey, i, myobjc, optindex, result, ret;
02695         char *arg, msg[MSG_SIZE];
02696 
02697         ktmp = NULL;
02698         flag = 0;
02699         freekey = 0;
02700         result = TCL_OK;
02701         if (objc < 3) {
02702                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id? key");
02703                 return (TCL_ERROR);
02704         }
02705 
02706         txn = NULL;
02707         for (i = 2; i < objc;) {
02708                 if (Tcl_GetIndexFromObj(interp, objv[i], dbkeyropts, "option",
02709                     TCL_EXACT, &optindex) != TCL_OK) {
02710                         result = IS_HELP(objv[i]);
02711                         if (result == TCL_OK)
02712                                 return (result);
02713                         result = TCL_OK;
02714                         Tcl_ResetResult(interp);
02715                         break;
02716                 }
02717                 i++;
02718                 switch ((enum dbkeyropts)optindex) {
02719                 case DBKEYR_TXN:
02720                         if (i == objc) {
02721                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
02722                                 result = TCL_ERROR;
02723                                 break;
02724                         }
02725                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
02726                         txn = NAME_TO_TXN(arg);
02727                         if (txn == NULL) {
02728                                 snprintf(msg, MSG_SIZE,
02729                                     "KeyRange: Invalid txn: %s\n", arg);
02730                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
02731                                 result = TCL_ERROR;
02732                         }
02733                         break;
02734                 }
02735         }
02736         if (result != TCL_OK)
02737                 return (result);
02738         (void)dbp->get_type(dbp, &type);
02739         ret = 0;
02740         /*
02741          * Make sure we have a key.
02742          */
02743         if (i != (objc - 1)) {
02744                 Tcl_WrongNumArgs(interp, 2, objv, "?args? key");
02745                 result = TCL_ERROR;
02746                 goto out;
02747         }
02748         memset(&key, 0, sizeof(key));
02749         if (type == DB_RECNO || type == DB_QUEUE) {
02750                 result = _GetUInt32(interp, objv[i], &recno);
02751                 if (result == TCL_OK) {
02752                         key.data = &recno;
02753                         key.size = sizeof(db_recno_t);
02754                 } else
02755                         return (result);
02756         } else {
02757                 ret = _CopyObjBytes(interp, objv[i++], &ktmp,
02758                     &key.size, &freekey);
02759                 if (ret != 0) {
02760                         result = _ReturnSetup(interp, ret,
02761                             DB_RETOK_STD(ret), "db keyrange");
02762                         return (result);
02763                 }
02764                 key.data = ktmp;
02765         }
02766         _debug_check();
02767         ret = dbp->key_range(dbp, txn, &key, &range, flag);
02768         result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db keyrange");
02769         if (result == TCL_ERROR)
02770                 goto out;
02771 
02772         /*
02773          * If we succeeded, set up return list.
02774          */
02775         myobjc = 3;
02776         myobjv[0] = Tcl_NewDoubleObj(range.less);
02777         myobjv[1] = Tcl_NewDoubleObj(range.equal);
02778         myobjv[2] = Tcl_NewDoubleObj(range.greater);
02779         retlist = Tcl_NewListObj(myobjc, myobjv);
02780         if (result == TCL_OK)
02781                 Tcl_SetObjResult(interp, retlist);
02782 
02783 out:    if (ktmp != NULL && freekey)
02784                 __os_free(dbp->dbenv, ktmp);
02785         return (result);
02786 }
02787 #endif
02788 
02789 /*
02790  * tcl_DbTruncate --
02791  */
02792 static int
02793 tcl_DbTruncate(interp, objc, objv, dbp)
02794         Tcl_Interp *interp;             /* Interpreter */
02795         int objc;                       /* How many arguments? */
02796         Tcl_Obj *CONST objv[];          /* The argument objects */
02797         DB *dbp;                        /* Database pointer */
02798 {
02799         static const char *dbcuropts[] = {
02800                 "-txn",
02801                 NULL
02802         };
02803         enum dbcuropts {
02804                 DBTRUNC_TXN
02805         };
02806         DB_TXN *txn;
02807         Tcl_Obj *res;
02808         u_int32_t count;
02809         int i, optindex, result, ret;
02810         char *arg, msg[MSG_SIZE];
02811 
02812         txn = NULL;
02813         result = TCL_OK;
02814 
02815         i = 2;
02816         while (i < objc) {
02817                 if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
02818                     TCL_EXACT, &optindex) != TCL_OK) {
02819                         result = IS_HELP(objv[i]);
02820                         goto out;
02821                 }
02822                 i++;
02823                 switch ((enum dbcuropts)optindex) {
02824                 case DBTRUNC_TXN:
02825                         if (i == objc) {
02826                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
02827                                 result = TCL_ERROR;
02828                                 break;
02829                         }
02830                         arg = Tcl_GetStringFromObj(objv[i++], NULL);
02831                         txn = NAME_TO_TXN(arg);
02832                         if (txn == NULL) {
02833                                 snprintf(msg, MSG_SIZE,
02834                                     "Truncate: Invalid txn: %s\n", arg);
02835                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
02836                                 result = TCL_ERROR;
02837                         }
02838                         break;
02839                 }
02840                 if (result != TCL_OK)
02841                         break;
02842         }
02843         if (result != TCL_OK)
02844                 goto out;
02845 
02846         _debug_check();
02847         ret = dbp->truncate(dbp, txn, &count, 0);
02848         if (ret != 0)
02849                 result = _ErrorSetup(interp, ret, "db truncate");
02850 
02851         else {
02852                 res = Tcl_NewWideIntObj((Tcl_WideInt)count);
02853                 Tcl_SetObjResult(interp, res);
02854         }
02855 out:
02856         return (result);
02857 }
02858 
02859 #ifdef CONFIG_TEST
02860 /*
02861  * tcl_DbCompact --
02862  */
02863 static int
02864 tcl_DbCompact(interp, objc, objv, dbp)
02865         Tcl_Interp *interp;             /* Interpreter */
02866         int objc;                       /* How many arguments? */
02867         Tcl_Obj *CONST objv[];          /* The argument objects */
02868         DB *dbp;                        /* Database pointer */
02869 {
02870         static const char *dbcuropts[] = {
02871                 "-fillpercent",
02872                 "-freespace",
02873                 "-freeonly",
02874                 "-pages",
02875                 "-start",
02876                 "-stop",
02877                 "-timeout",
02878                 "-txn",
02879                 NULL
02880         };
02881         enum dbcuropts {
02882                 DBREORG_FILLFACTOR,
02883                 DBREORG_FREESPACE,
02884                 DBREORG_FREEONLY,
02885                 DBREORG_PAGES,
02886                 DBREORG_START,
02887                 DBREORG_STOP,
02888                 DBREORG_TIMEOUT,
02889                 DBREORG_TXN
02890         };
02891         DBTCL_INFO *ip;
02892         DBT *key, end, start, stop;
02893         DBTYPE type;
02894         DB_TXN *txn;
02895         Tcl_Obj *myobj, *retlist;
02896         db_recno_t recno, srecno;
02897         u_int32_t arg, fillfactor, flags, pages, timeout;
02898         char *carg, msg[MSG_SIZE];
02899         int freekey, i, optindex, result, ret;
02900         void *kp;
02901 
02902         flags = 0;
02903         result = TCL_OK;
02904         txn = NULL;
02905         (void)dbp->get_type(dbp, &type);
02906         memset(&start, 0, sizeof(start));
02907         memset(&stop, 0, sizeof(stop));
02908         memset(&end, 0, sizeof(end));
02909         ip = (DBTCL_INFO *)dbp->api_internal;
02910         fillfactor = pages = timeout = 0;
02911 
02912         i = 2;
02913         while (i < objc) {
02914                 if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
02915                     TCL_EXACT, &optindex) != TCL_OK) {
02916                         result = IS_HELP(objv[i]);
02917                         goto out;
02918                 }
02919                 i++;
02920                 switch ((enum dbcuropts)optindex) {
02921                 case DBREORG_FILLFACTOR:
02922                         if (i == objc) {
02923                                 Tcl_WrongNumArgs(interp,
02924                                     2, objv, "?-fillfactor number?");
02925                                 result = TCL_ERROR;
02926                                 break;
02927                         }
02928                         result = _GetUInt32(interp, objv[i++], &arg);
02929                         if (result != TCL_OK)
02930                                 goto out;
02931                         i++;
02932                         fillfactor = arg;
02933                         break;
02934                 case DBREORG_FREESPACE:
02935                         LF_SET(DB_FREE_SPACE);
02936                         break;
02937 
02938                 case DBREORG_FREEONLY:
02939                         LF_SET(DB_FREELIST_ONLY);
02940                         break;
02941 
02942                 case DBREORG_PAGES:
02943                         if (i == objc) {
02944                                 Tcl_WrongNumArgs(interp,
02945                                     2, objv, "?-pages number?");
02946                                 result = TCL_ERROR;
02947                                 break;
02948                         }
02949                         result = _GetUInt32(interp, objv[i++], &arg);
02950                         if (result != TCL_OK)
02951                                 goto out;
02952                         i++;
02953                         pages = arg;
02954                         break;
02955                 case DBREORG_TIMEOUT:
02956                         if (i == objc) {
02957                                 Tcl_WrongNumArgs(interp,
02958                                     2, objv, "?-timeout number?");
02959                                 result = TCL_ERROR;
02960                                 break;
02961                         }
02962                         result = _GetUInt32(interp, objv[i++], &arg);
02963                         if (result != TCL_OK)
02964                                 goto out;
02965                         i++;
02966                         timeout = arg;
02967                         break;
02968 
02969                 case DBREORG_START:
02970                 case DBREORG_STOP:
02971                         if (i == objc) {
02972                                 Tcl_WrongNumArgs(interp, 1, objv,
02973                                     "?-args? -start/stop key");
02974                                 result = TCL_ERROR;
02975                                 goto out;
02976                         }
02977                         if ((enum dbcuropts)optindex == DBREORG_START) {
02978                                 key = &start;
02979                                 key->data = &recno;
02980                         } else {
02981                                 key = &stop;
02982                                 key->data = &srecno;
02983                         }
02984                         if (type == DB_RECNO || type == DB_QUEUE) {
02985                                 result = _GetUInt32(
02986                                     interp, objv[i], key->data);
02987                                 if (result == TCL_OK) {
02988                                         key->size = sizeof(db_recno_t);
02989                                 } else
02990                                         goto out;
02991                         } else {
02992                                 ret = _CopyObjBytes(interp, objv[i],
02993                                     &key->data, &key->size, &freekey);
02994                                 if (ret != 0)
02995                                         goto err;
02996                                 if (freekey == 0) {
02997                                         if ((ret = __os_malloc(NULL,
02998                                              key->size, &kp)) != 0)
02999                                                 goto err;
03000 
03001                                         memcpy(kp, key->data, key->size);
03002                                         key->data = kp;
03003                                         key->ulen = key->size;
03004                                 }
03005                         }
03006                         i++;
03007                         break;
03008                 case DBREORG_TXN:
03009                         if (i == objc) {
03010                                 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
03011                                 result = TCL_ERROR;
03012                                 break;
03013                         }
03014                         carg = Tcl_GetStringFromObj(objv[i++], NULL);
03015                         txn = NAME_TO_TXN(carg);
03016                         if (txn == NULL) {
03017                                 snprintf(msg, MSG_SIZE,
03018                                     "Compact: Invalid txn: %s\n", carg);
03019                                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
03020                                 result = TCL_ERROR;
03021                         }
03022                 }
03023                 if (result != TCL_OK)
03024                         break;
03025         }
03026         if (result != TCL_OK)
03027                 goto out;
03028 
03029         if (ip->i_cdata == NULL)
03030                 if ((ret = __os_calloc(dbp->dbenv,
03031                     1, sizeof(DB_COMPACT), &ip->i_cdata)) != 0) {
03032                         Tcl_SetResult(interp,
03033                             db_strerror(ret), TCL_STATIC);
03034                         goto out;
03035                 }
03036 
03037         ip->i_cdata->compact_fillpercent = fillfactor;
03038         ip->i_cdata->compact_timeout = timeout;
03039         ip->i_cdata->compact_pages = pages;
03040 
03041         _debug_check();
03042         ret = dbp->compact(dbp, txn, &start, &stop, ip->i_cdata, flags, &end);
03043         result = _ReturnSetup(interp, ret, DB_RETOK_DBCGET(ret), "dbp compact");
03044         if (result == TCL_ERROR)
03045                 goto out;
03046 
03047         retlist = Tcl_NewListObj(0, NULL);
03048         if (ret != 0)
03049                 goto out;
03050         if (type == DB_RECNO || type == DB_QUEUE) {
03051                 if (end.size == 0)
03052                         recno  = 0;
03053                 else
03054                         recno = *((db_recno_t *)end.data);
03055                 myobj = Tcl_NewWideIntObj((Tcl_WideInt)recno);
03056         } else
03057                 myobj = Tcl_NewByteArrayObj(end.data, (int)end.size);
03058         result = Tcl_ListObjAppendElement(interp, retlist, myobj);
03059         if (result == TCL_OK)
03060                 Tcl_SetObjResult(interp, retlist);
03061 
03062         if (0) {
03063 err:            result = _ReturnSetup(interp,
03064                      ret, DB_RETOK_DBCGET(ret), "dbc compact");
03065         }
03066 out:
03067         if (start.data != NULL && start.data != &recno)
03068                 __os_free(NULL, start.data);
03069         if (stop.data != NULL && stop.data != &srecno)
03070                 __os_free(NULL, stop.data);
03071 
03072         return (result);
03073 }
03074 
03075 /*
03076  * tcl_DbCompactStat
03077  */
03078 static int
03079 tcl_DbCompactStat(interp, objc, objv, dbp)
03080         Tcl_Interp *interp;             /* Interpreter */
03081         int objc;                       /* How many arguments? */
03082         Tcl_Obj *CONST objv[];          /* The argument objects */
03083         DB *dbp;                        /* Database pointer */
03084 {
03085         DBTCL_INFO *ip;
03086 
03087         COMPQUIET(objc, 0);
03088         COMPQUIET(objv, NULL);
03089 
03090         ip = (DBTCL_INFO *)dbp->api_internal;
03091 
03092         return (tcl_CompactStat(interp, ip));
03093 }
03094 
03095 /*
03096  * PUBLIC: int tcl_CompactStat __P((Tcl_Interp *, DBTCL_INFO *));
03097  */
03098 int
03099 tcl_CompactStat(interp, ip)
03100         Tcl_Interp *interp;             /* Interpreter */
03101         DBTCL_INFO *ip;
03102 {
03103         DB_COMPACT *rp;
03104         Tcl_Obj *res;
03105         int result;
03106         char msg[MSG_SIZE];
03107 
03108         result = TCL_OK;
03109         rp = NULL;
03110 
03111         _debug_check();
03112         if ((rp = ip->i_cdata) == NULL) {
03113                 snprintf(msg, MSG_SIZE,
03114                     "Compact stat: No stats available\n");
03115                 Tcl_SetResult(interp, msg, TCL_VOLATILE);
03116                 result = TCL_ERROR;
03117                 goto error;
03118         }
03119 
03120         res = Tcl_NewObj();
03121 
03122         MAKE_STAT_LIST("Pages freed", rp->compact_pages_free);
03123         MAKE_STAT_LIST("Pages truncated", rp->compact_pages_truncated);
03124         MAKE_STAT_LIST("Pages examined", rp->compact_pages_examine);
03125         MAKE_STAT_LIST("Levels removed", rp->compact_levels);
03126         MAKE_STAT_LIST("Deadlocks encountered", rp->compact_deadlock);
03127 
03128         Tcl_SetObjResult(interp, res);
03129 error:
03130         return (result);
03131 }
03132 #endif

Generated on Sun Dec 25 12:14:50 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2