tcl_db.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:57k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1999-2002
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: tcl_db.c,v 11.107 2002/08/06 06:20:31 bostic Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <tcl.h>
  16. #endif
  17. #include "db_int.h"
  18. #include "dbinc/db_page.h"
  19. #include "dbinc/db_am.h"
  20. #include "dbinc/tcl_db.h"
  21. /*
  22.  * Prototypes for procedures defined later in this file:
  23.  */
  24. static int tcl_DbAssociate __P((Tcl_Interp *,
  25.     int, Tcl_Obj * CONST*, DB *));
  26. static int tcl_DbClose __P((Tcl_Interp *,
  27.     int, Tcl_Obj * CONST*, DB *, DBTCL_INFO *));
  28. static int tcl_DbDelete __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  29. static int tcl_DbGet __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *, int));
  30. static int tcl_DbKeyRange __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  31. static int tcl_DbPut __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  32. static int tcl_DbStat __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  33. static int tcl_DbTruncate __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  34. static int tcl_DbCursor __P((Tcl_Interp *,
  35.     int, Tcl_Obj * CONST*, DB *, DBC **));
  36. static int tcl_DbJoin __P((Tcl_Interp *,
  37.     int, Tcl_Obj * CONST*, DB *, DBC **));
  38. static int tcl_DbGetjoin __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  39. static int tcl_DbCount __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  40. static int tcl_second_call __P((DB *, const DBT *, const DBT *, DBT *));
  41. /*
  42.  * _DbInfoDelete --
  43.  *
  44.  * PUBLIC: void _DbInfoDelete __P((Tcl_Interp *, DBTCL_INFO *));
  45.  */
  46. void
  47. _DbInfoDelete(interp, dbip)
  48. Tcl_Interp *interp;
  49. DBTCL_INFO *dbip;
  50. {
  51. DBTCL_INFO *nextp, *p;
  52. /*
  53.  * First we have to close any open cursors.  Then we close
  54.  * our db.
  55.  */
  56. for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
  57. nextp = LIST_NEXT(p, entries);
  58. /*
  59.  * Check if this is a cursor info structure and if
  60.  * it is, if it belongs to this DB.  If so, remove
  61.  * its commands and info structure.
  62.  */
  63. if (p->i_parent == dbip && p->i_type == I_DBC) {
  64. (void)Tcl_DeleteCommand(interp, p->i_name);
  65. _DeleteInfo(p);
  66. }
  67. }
  68. (void)Tcl_DeleteCommand(interp, dbip->i_name);
  69. _DeleteInfo(dbip);
  70. }
  71. /*
  72.  *
  73.  * PUBLIC: int db_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
  74.  *
  75.  * db_Cmd --
  76.  * Implements the "db" widget.
  77.  */
  78. int
  79. db_Cmd(clientData, interp, objc, objv)
  80. ClientData clientData; /* DB handle */
  81. Tcl_Interp *interp; /* Interpreter */
  82. int objc; /* How many arguments? */
  83. Tcl_Obj *CONST objv[]; /* The argument objects */
  84. {
  85. static char *dbcmds[] = {
  86. #if CONFIG_TEST
  87. "keyrange",
  88. "pget",
  89. "rpcid",
  90. "test",
  91. #endif
  92. "associate",
  93. "close",
  94. "count",
  95. "cursor",
  96. "del",
  97. "get",
  98. "get_join",
  99. "get_type",
  100. "is_byteswapped",
  101. "join",
  102. "put",
  103. "stat",
  104. "sync",
  105. "truncate",
  106. NULL
  107. };
  108. enum dbcmds {
  109. #if CONFIG_TEST
  110. DBKEYRANGE,
  111. DBPGET,
  112. DBRPCID,
  113. DBTEST,
  114. #endif
  115. DBASSOCIATE,
  116. DBCLOSE,
  117. DBCOUNT,
  118. DBCURSOR,
  119. DBDELETE,
  120. DBGET,
  121. DBGETJOIN,
  122. DBGETTYPE,
  123. DBSWAPPED,
  124. DBJOIN,
  125. DBPUT,
  126. DBSTAT,
  127. DBSYNC,
  128. DBTRUNCATE
  129. };
  130. DB *dbp;
  131. DBC *dbc;
  132. DBTCL_INFO *dbip;
  133. DBTCL_INFO *ip;
  134. DBTYPE type;
  135. Tcl_Obj *res;
  136. int cmdindex, isswapped, result, ret;
  137. char newname[MSG_SIZE];
  138. Tcl_ResetResult(interp);
  139. dbp = (DB *)clientData;
  140. dbip = _PtrToInfo((void *)dbp);
  141. memset(newname, 0, MSG_SIZE);
  142. result = TCL_OK;
  143. if (objc <= 1) {
  144. Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
  145. return (TCL_ERROR);
  146. }
  147. if (dbp == NULL) {
  148. Tcl_SetResult(interp, "NULL db pointer", TCL_STATIC);
  149. return (TCL_ERROR);
  150. }
  151. if (dbip == NULL) {
  152. Tcl_SetResult(interp, "NULL db info pointer", TCL_STATIC);
  153. return (TCL_ERROR);
  154. }
  155. /*
  156.  * Get the command name index from the object based on the dbcmds
  157.  * defined above.
  158.  */
  159. if (Tcl_GetIndexFromObj(interp,
  160.     objv[1], dbcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
  161. return (IS_HELP(objv[1]));
  162. res = NULL;
  163. switch ((enum dbcmds)cmdindex) {
  164. #if CONFIG_TEST
  165. case DBKEYRANGE:
  166. result = tcl_DbKeyRange(interp, objc, objv, dbp);
  167. break;
  168. case DBPGET:
  169. result = tcl_DbGet(interp, objc, objv, dbp, 1);
  170. break;
  171. case DBRPCID:
  172. /*
  173.  * No args for this.  Error if there are some.
  174.  */
  175. if (objc > 2) {
  176. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  177. return (TCL_ERROR);
  178. }
  179. /*
  180.  * !!! Retrieve the client ID from the dbp handle directly.
  181.  * This is for testing purposes only.  It is dbp-private data.
  182.  */
  183. res = Tcl_NewLongObj(dbp->cl_id);
  184. break;
  185. case DBTEST:
  186. result = tcl_EnvTest(interp, objc, objv, dbp->dbenv);
  187. break;
  188. #endif
  189. case DBASSOCIATE:
  190. result = tcl_DbAssociate(interp, objc, objv, dbp);
  191. break;
  192. case DBCLOSE:
  193. result = tcl_DbClose(interp, objc, objv, dbp, dbip);
  194. break;
  195. case DBDELETE:
  196. result = tcl_DbDelete(interp, objc, objv, dbp);
  197. break;
  198. case DBGET:
  199. result = tcl_DbGet(interp, objc, objv, dbp, 0);
  200. break;
  201. case DBPUT:
  202. result = tcl_DbPut(interp, objc, objv, dbp);
  203. break;
  204. case DBCOUNT:
  205. result = tcl_DbCount(interp, objc, objv, dbp);
  206. break;
  207. case DBSWAPPED:
  208. /*
  209.  * No args for this.  Error if there are some.
  210.  */
  211. if (objc > 2) {
  212. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  213. return (TCL_ERROR);
  214. }
  215. _debug_check();
  216. ret = dbp->get_byteswapped(dbp, &isswapped);
  217. res = Tcl_NewIntObj(isswapped);
  218. break;
  219. case DBGETTYPE:
  220. /*
  221.  * No args for this.  Error if there are some.
  222.  */
  223. if (objc > 2) {
  224. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  225. return (TCL_ERROR);
  226. }
  227. _debug_check();
  228. ret = dbp->get_type(dbp, &type);
  229. if (type == DB_BTREE)
  230. res = Tcl_NewStringObj("btree", strlen("btree"));
  231. else if (type == DB_HASH)
  232. res = Tcl_NewStringObj("hash", strlen("hash"));
  233. else if (type == DB_RECNO)
  234. res = Tcl_NewStringObj("recno", strlen("recno"));
  235. else if (type == DB_QUEUE)
  236. res = Tcl_NewStringObj("queue", strlen("queue"));
  237. else {
  238. Tcl_SetResult(interp,
  239.     "db gettype: Returned unknown typen", TCL_STATIC);
  240. result = TCL_ERROR;
  241. }
  242. break;
  243. case DBSTAT:
  244. result = tcl_DbStat(interp, objc, objv, dbp);
  245. break;
  246. case DBSYNC:
  247. /*
  248.  * No args for this.  Error if there are some.
  249.  */
  250. if (objc > 2) {
  251. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  252. return (TCL_ERROR);
  253. }
  254. _debug_check();
  255. ret = dbp->sync(dbp, 0);
  256. res = Tcl_NewIntObj(ret);
  257. if (ret != 0) {
  258. Tcl_SetObjResult(interp, res);
  259. result = TCL_ERROR;
  260. }
  261. break;
  262. case DBCURSOR:
  263. snprintf(newname, sizeof(newname),
  264.     "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
  265. ip = _NewInfo(interp, NULL, newname, I_DBC);
  266. if (ip != NULL) {
  267. result = tcl_DbCursor(interp, objc, objv, dbp, &dbc);
  268. if (result == TCL_OK) {
  269. dbip->i_dbdbcid++;
  270. ip->i_parent = dbip;
  271. Tcl_CreateObjCommand(interp, newname,
  272.     (Tcl_ObjCmdProc *)dbc_Cmd,
  273.     (ClientData)dbc, NULL);
  274. res =
  275.     Tcl_NewStringObj(newname, strlen(newname));
  276. _SetInfoData(ip, dbc);
  277. } else
  278. _DeleteInfo(ip);
  279. } else {
  280. Tcl_SetResult(interp,
  281.     "Could not set up info", TCL_STATIC);
  282. result = TCL_ERROR;
  283. }
  284. break;
  285. case DBJOIN:
  286. snprintf(newname, sizeof(newname),
  287.     "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
  288. ip = _NewInfo(interp, NULL, newname, I_DBC);
  289. if (ip != NULL) {
  290. result = tcl_DbJoin(interp, objc, objv, dbp, &dbc);
  291. if (result == TCL_OK) {
  292. dbip->i_dbdbcid++;
  293. ip->i_parent = dbip;
  294. Tcl_CreateObjCommand(interp, newname,
  295.     (Tcl_ObjCmdProc *)dbc_Cmd,
  296.     (ClientData)dbc, NULL);
  297. res =
  298.     Tcl_NewStringObj(newname, strlen(newname));
  299. _SetInfoData(ip, dbc);
  300. } else
  301. _DeleteInfo(ip);
  302. } else {
  303. Tcl_SetResult(interp,
  304.     "Could not set up info", TCL_STATIC);
  305. result = TCL_ERROR;
  306. }
  307. break;
  308. case DBGETJOIN:
  309. result = tcl_DbGetjoin(interp, objc, objv, dbp);
  310. break;
  311. case DBTRUNCATE:
  312. result = tcl_DbTruncate(interp, objc, objv, dbp);
  313. break;
  314. }
  315. /*
  316.  * Only set result if we have a res.  Otherwise, lower
  317.  * functions have already done so.
  318.  */
  319. if (result == TCL_OK && res)
  320. Tcl_SetObjResult(interp, res);
  321. return (result);
  322. }
  323. /*
  324.  * tcl_db_stat --
  325.  */
  326. static int
  327. tcl_DbStat(interp, objc, objv, dbp)
  328. Tcl_Interp *interp; /* Interpreter */
  329. int objc; /* How many arguments? */
  330. Tcl_Obj *CONST objv[]; /* The argument objects */
  331. DB *dbp; /* Database pointer */
  332. {
  333. DB_BTREE_STAT *bsp;
  334. DB_HASH_STAT *hsp;
  335. DB_QUEUE_STAT *qsp;
  336. void *sp;
  337. Tcl_Obj *res, *flaglist, *myobjv[2];
  338. DBTYPE type;
  339. u_int32_t flag;
  340. int result, ret;
  341. char *arg;
  342. result = TCL_OK;
  343. flag = 0;
  344. if (objc > 3) {
  345. Tcl_WrongNumArgs(interp, 2, objv, "?-faststat?");
  346. return (TCL_ERROR);
  347. }
  348. if (objc == 3) {
  349. arg = Tcl_GetStringFromObj(objv[2], NULL);
  350. if (strcmp(arg, "-faststat") == 0)
  351. flag = DB_FAST_STAT;
  352. else {
  353. Tcl_SetResult(interp,
  354.     "db stat: unknown arg", TCL_STATIC);
  355. return (TCL_ERROR);
  356. }
  357. }
  358. _debug_check();
  359. ret = dbp->stat(dbp, &sp, flag);
  360. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db stat");
  361. if (result == TCL_ERROR)
  362. return (result);
  363. (void)dbp->get_type(dbp, &type);
  364. /*
  365.  * Have our stats, now construct the name value
  366.  * list pairs and free up the memory.
  367.  */
  368. res = Tcl_NewObj();
  369. /*
  370.  * MAKE_STAT_LIST assumes 'res' and 'error' label.
  371.  */
  372. if (type == DB_HASH) {
  373. hsp = (DB_HASH_STAT *)sp;
  374. MAKE_STAT_LIST("Magic", hsp->hash_magic);
  375. MAKE_STAT_LIST("Version", hsp->hash_version);
  376. MAKE_STAT_LIST("Page size", hsp->hash_pagesize);
  377. MAKE_STAT_LIST("Number of keys", hsp->hash_nkeys);
  378. MAKE_STAT_LIST("Number of records", hsp->hash_ndata);
  379. MAKE_STAT_LIST("Fill factor", hsp->hash_ffactor);
  380. MAKE_STAT_LIST("Buckets", hsp->hash_buckets);
  381. if (flag != DB_FAST_STAT) {
  382. MAKE_STAT_LIST("Free pages", hsp->hash_free);
  383. MAKE_STAT_LIST("Bytes free", hsp->hash_bfree);
  384. MAKE_STAT_LIST("Number of big pages",
  385.     hsp->hash_bigpages);
  386. MAKE_STAT_LIST("Big pages bytes free",
  387.     hsp->hash_big_bfree);
  388. MAKE_STAT_LIST("Overflow pages", hsp->hash_overflows);
  389. MAKE_STAT_LIST("Overflow bytes free",
  390.     hsp->hash_ovfl_free);
  391. MAKE_STAT_LIST("Duplicate pages", hsp->hash_dup);
  392. MAKE_STAT_LIST("Duplicate pages bytes free",
  393.     hsp->hash_dup_free);
  394. }
  395. } else if (type == DB_QUEUE) {
  396. qsp = (DB_QUEUE_STAT *)sp;
  397. MAKE_STAT_LIST("Magic", qsp->qs_magic);
  398. MAKE_STAT_LIST("Version", qsp->qs_version);
  399. MAKE_STAT_LIST("Page size", qsp->qs_pagesize);
  400. MAKE_STAT_LIST("Extent size", qsp->qs_extentsize);
  401. MAKE_STAT_LIST("Number of records", qsp->qs_nkeys);
  402. MAKE_STAT_LIST("Record length", qsp->qs_re_len);
  403. MAKE_STAT_LIST("Record pad", qsp->qs_re_pad);
  404. MAKE_STAT_LIST("First record number", qsp->qs_first_recno);
  405. MAKE_STAT_LIST("Last record number", qsp->qs_cur_recno);
  406. if (flag != DB_FAST_STAT) {
  407. MAKE_STAT_LIST("Number of pages", qsp->qs_pages);
  408. MAKE_STAT_LIST("Bytes free", qsp->qs_pgfree);
  409. }
  410. } else { /* BTREE and RECNO are same stats */
  411. bsp = (DB_BTREE_STAT *)sp;
  412. MAKE_STAT_LIST("Magic", bsp->bt_magic);
  413. MAKE_STAT_LIST("Version", bsp->bt_version);
  414. MAKE_STAT_LIST("Number of keys", bsp->bt_nkeys);
  415. MAKE_STAT_LIST("Number of records", bsp->bt_ndata);
  416. MAKE_STAT_LIST("Minimum keys per page", bsp->bt_minkey);
  417. MAKE_STAT_LIST("Fixed record length", bsp->bt_re_len);
  418. MAKE_STAT_LIST("Record pad", bsp->bt_re_pad);
  419. MAKE_STAT_LIST("Page size", bsp->bt_pagesize);
  420. if (flag != DB_FAST_STAT) {
  421. MAKE_STAT_LIST("Levels", bsp->bt_levels);
  422. MAKE_STAT_LIST("Internal pages", bsp->bt_int_pg);
  423. MAKE_STAT_LIST("Leaf pages", bsp->bt_leaf_pg);
  424. MAKE_STAT_LIST("Duplicate pages", bsp->bt_dup_pg);
  425. MAKE_STAT_LIST("Overflow pages", bsp->bt_over_pg);
  426. MAKE_STAT_LIST("Pages on freelist", bsp->bt_free);
  427. MAKE_STAT_LIST("Internal pages bytes free",
  428.     bsp->bt_int_pgfree);
  429. MAKE_STAT_LIST("Leaf pages bytes free",
  430.     bsp->bt_leaf_pgfree);
  431. MAKE_STAT_LIST("Duplicate pages bytes free",
  432.     bsp->bt_dup_pgfree);
  433. MAKE_STAT_LIST("Bytes free in overflow pages",
  434.     bsp->bt_over_pgfree);
  435. }
  436. }
  437. /*
  438.  * Construct a {name {flag1 flag2 ... flagN}} list for the
  439.  * dbp flags.  These aren't access-method dependent, but they
  440.  * include all the interesting flags, and the integer value
  441.  * isn't useful from Tcl--return the strings instead.
  442.  */
  443. myobjv[0] = Tcl_NewStringObj("Flags", strlen("Flags"));
  444. myobjv[1] = _GetFlagsList(interp, dbp->flags, __db_inmemdbflags);
  445. flaglist = Tcl_NewListObj(2, myobjv);
  446. if (flaglist == NULL) {
  447. result = TCL_ERROR;
  448. goto error;
  449. }
  450. if ((result =
  451.     Tcl_ListObjAppendElement(interp, res, flaglist)) != TCL_OK)
  452. goto error;
  453. Tcl_SetObjResult(interp, res);
  454. error:
  455. free(sp);
  456. return (result);
  457. }
  458. /*
  459.  * tcl_db_close --
  460.  */
  461. static int
  462. tcl_DbClose(interp, objc, objv, dbp, dbip)
  463. Tcl_Interp *interp; /* Interpreter */
  464. int objc; /* How many arguments? */
  465. Tcl_Obj *CONST objv[]; /* The argument objects */
  466. DB *dbp; /* Database pointer */
  467. DBTCL_INFO *dbip; /* Info pointer */
  468. {
  469. static char *dbclose[] = {
  470. "-nosync", "--", NULL
  471. };
  472. enum dbclose {
  473. TCL_DBCLOSE_NOSYNC,
  474. TCL_DBCLOSE_ENDARG
  475. };
  476. u_int32_t flag;
  477. int endarg, i, optindex, result, ret;
  478. char *arg;
  479. result = TCL_OK;
  480. endarg = 0;
  481. flag = 0;
  482. if (objc > 4) {
  483. Tcl_WrongNumArgs(interp, 2, objv, "?-nosync?");
  484. return (TCL_ERROR);
  485. }
  486. i = 2;
  487. while (i < objc) {
  488. if (Tcl_GetIndexFromObj(interp, objv[i], dbclose,
  489.     "option", TCL_EXACT, &optindex) != TCL_OK) {
  490. arg = Tcl_GetStringFromObj(objv[i], NULL);
  491. if (arg[0] == '-')
  492. return (IS_HELP(objv[i]));
  493. else
  494. Tcl_ResetResult(interp);
  495. break;
  496. }
  497. i++;
  498. switch ((enum dbclose)optindex) {
  499. case TCL_DBCLOSE_NOSYNC:
  500. flag = DB_NOSYNC;
  501. break;
  502. case TCL_DBCLOSE_ENDARG:
  503. endarg = 1;
  504. break;
  505. }
  506. /*
  507.  * If, at any time, parsing the args we get an error,
  508.  * bail out and return.
  509.  */
  510. if (result != TCL_OK)
  511. return (result);
  512. if (endarg)
  513. break;
  514. }
  515. _DbInfoDelete(interp, dbip);
  516. _debug_check();
  517. /* Paranoia. */
  518. dbp->api_internal = NULL;
  519. ret = (dbp)->close(dbp, flag);
  520. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db close");
  521. return (result);
  522. }
  523. /*
  524.  * tcl_db_put --
  525.  */
  526. static int
  527. tcl_DbPut(interp, objc, objv, dbp)
  528. Tcl_Interp *interp; /* Interpreter */
  529. int objc; /* How many arguments? */
  530. Tcl_Obj *CONST objv[]; /* The argument objects */
  531. DB *dbp; /* Database pointer */
  532. {
  533. static char *dbputopts[] = {
  534. #if CONFIG_TEST
  535. "-nodupdata",
  536. #endif
  537. "-append",
  538. "-auto_commit",
  539. "-nooverwrite",
  540. "-partial",
  541. "-txn",
  542. NULL
  543. };
  544. enum dbputopts {
  545. #if CONFIG_TEST
  546. DBGET_NODUPDATA,
  547. #endif
  548. DBPUT_APPEND,
  549. DBPUT_AUTO_COMMIT,
  550. DBPUT_NOOVER,
  551. DBPUT_PART,
  552. DBPUT_TXN
  553. };
  554. static char *dbputapp[] = {
  555. "-append", NULL
  556. };
  557. enum dbputapp { DBPUT_APPEND0 };
  558. DBT key, data;
  559. DBTYPE type;
  560. DB_TXN *txn;
  561. Tcl_Obj **elemv, *res;
  562. void *dtmp, *ktmp;
  563. db_recno_t recno;
  564. u_int32_t flag;
  565. int auto_commit, elemc, end, freekey, freedata;
  566. int i, optindex, result, ret;
  567. char *arg, msg[MSG_SIZE];
  568. txn = NULL;
  569. result = TCL_OK;
  570. flag = 0;
  571. if (objc <= 3) {
  572. Tcl_WrongNumArgs(interp, 2, objv, "?-args? key data");
  573. return (TCL_ERROR);
  574. }
  575. freekey = freedata = 0;
  576. memset(&key, 0, sizeof(key));
  577. memset(&data, 0, sizeof(data));
  578. /*
  579.  * If it is a QUEUE or RECNO database, the key is a record number
  580.  * and must be setup up to contain a db_recno_t.  Otherwise the
  581.  * key is a "string".
  582.  */
  583. (void)dbp->get_type(dbp, &type);
  584. /*
  585.  * We need to determine where the end of required args are.  If we
  586.  * are using a QUEUE/RECNO db and -append, then there is just one
  587.  * req arg (data).  Otherwise there are two (key data).
  588.  *
  589.  * We preparse the list to determine this since we need to know
  590.  * to properly check # of args for other options below.
  591.  */
  592. end = objc - 2;
  593. if (type == DB_QUEUE || type == DB_RECNO) {
  594. i = 2;
  595. while (i < objc - 1) {
  596. if (Tcl_GetIndexFromObj(interp, objv[i++], dbputapp,
  597.     "option", TCL_EXACT, &optindex) != TCL_OK)
  598. continue;
  599. switch ((enum dbputapp)optindex) {
  600. case DBPUT_APPEND0:
  601. end = objc - 1;
  602. break;
  603. }
  604. }
  605. }
  606. Tcl_ResetResult(interp);
  607. /*
  608.  * Get the command name index from the object based on the options
  609.  * defined above.
  610.  */
  611. i = 2;
  612. auto_commit = 0;
  613. while (i < end) {
  614. if (Tcl_GetIndexFromObj(interp, objv[i],
  615.     dbputopts, "option", TCL_EXACT, &optindex) != TCL_OK)
  616. return (IS_HELP(objv[i]));
  617. i++;
  618. switch ((enum dbputopts)optindex) {
  619. #if CONFIG_TEST
  620. case DBGET_NODUPDATA:
  621. FLAG_CHECK(flag);
  622. flag = DB_NODUPDATA;
  623. break;
  624. #endif
  625. case DBPUT_TXN:
  626. if (i > (end - 1)) {
  627. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  628. result = TCL_ERROR;
  629. break;
  630. }
  631. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  632. txn = NAME_TO_TXN(arg);
  633. if (txn == NULL) {
  634. snprintf(msg, MSG_SIZE,
  635.     "Put: Invalid txn: %sn", arg);
  636. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  637. result = TCL_ERROR;
  638. }
  639. break;
  640. case DBPUT_AUTO_COMMIT:
  641. auto_commit = 1;
  642. break;
  643. case DBPUT_APPEND:
  644. FLAG_CHECK(flag);
  645. flag = DB_APPEND;
  646. break;
  647. case DBPUT_NOOVER:
  648. FLAG_CHECK(flag);
  649. flag = DB_NOOVERWRITE;
  650. break;
  651. case DBPUT_PART:
  652. if (i > (end - 1)) {
  653. Tcl_WrongNumArgs(interp, 2, objv,
  654.     "?-partial {offset length}?");
  655. result = TCL_ERROR;
  656. break;
  657. }
  658. /*
  659.  * Get sublist as {offset length}
  660.  */
  661. result = Tcl_ListObjGetElements(interp, objv[i++],
  662.     &elemc, &elemv);
  663. if (elemc != 2) {
  664. Tcl_SetResult(interp,
  665.     "List must be {offset length}", TCL_STATIC);
  666. result = TCL_ERROR;
  667. break;
  668. }
  669. data.flags = DB_DBT_PARTIAL;
  670. result = _GetUInt32(interp, elemv[0], &data.doff);
  671. if (result != TCL_OK)
  672. break;
  673. result = _GetUInt32(interp, elemv[1], &data.dlen);
  674. /*
  675.  * NOTE: We don't check result here because all we'd
  676.  * do is break anyway, and we are doing that.  If you
  677.  * add code here, you WILL need to add the check
  678.  * for result.  (See the check for save.doff, a few
  679.  * lines above and copy that.)
  680.  */
  681. break;
  682. }
  683. if (result != TCL_OK)
  684. break;
  685. }
  686. if (auto_commit)
  687. flag |= DB_AUTO_COMMIT;
  688. if (result == TCL_ERROR)
  689. return (result);
  690. /*
  691.  * If we are a recno db and we are NOT using append, then the 2nd
  692.  * last arg is the key.
  693.  */
  694. if (type == DB_QUEUE || type == DB_RECNO) {
  695. key.data = &recno;
  696. key.ulen = key.size = sizeof(db_recno_t);
  697. key.flags = DB_DBT_USERMEM;
  698. if (flag == DB_APPEND)
  699. recno = 0;
  700. else {
  701. result = _GetUInt32(interp, objv[objc-2], &recno);
  702. if (result != TCL_OK)
  703. return (result);
  704. }
  705. } else {
  706. ret = _CopyObjBytes(interp, objv[objc-2], &ktmp,
  707.     &key.size, &freekey);
  708. if (ret != 0) {
  709. result = _ReturnSetup(interp, ret,
  710.     DB_RETOK_DBPUT(ret), "db put");
  711. return (result);
  712. }
  713. key.data = ktmp;
  714. }
  715. ret = _CopyObjBytes(interp, objv[objc-1], &dtmp,
  716.     &data.size, &freedata);
  717. if (ret != 0) {
  718. result = _ReturnSetup(interp, ret,
  719.     DB_RETOK_DBPUT(ret), "db put");
  720. goto out;
  721. }
  722. data.data = dtmp;
  723. _debug_check();
  724. ret = dbp->put(dbp, txn, &key, &data, flag);
  725. result = _ReturnSetup(interp, ret, DB_RETOK_DBPUT(ret), "db put");
  726. if (ret == 0 &&
  727.     (type == DB_RECNO || type == DB_QUEUE) && flag == DB_APPEND) {
  728. res = Tcl_NewLongObj((long)recno);
  729. Tcl_SetObjResult(interp, res);
  730. }
  731. out:
  732. if (freedata)
  733. (void)__os_free(dbp->dbenv, dtmp);
  734. if (freekey)
  735. (void)__os_free(dbp->dbenv, ktmp);
  736. return (result);
  737. }
  738. /*
  739.  * tcl_db_get --
  740.  */
  741. static int
  742. tcl_DbGet(interp, objc, objv, dbp, ispget)
  743. Tcl_Interp *interp; /* Interpreter */
  744. int objc; /* How many arguments? */
  745. Tcl_Obj *CONST objv[]; /* The argument objects */
  746. DB *dbp; /* Database pointer */
  747. int ispget; /* 1 for pget, 0 for get */
  748. {
  749. static char *dbgetopts[] = {
  750. #if CONFIG_TEST
  751. "-dirty",
  752. "-multi",
  753. #endif
  754. "-consume",
  755. "-consume_wait",
  756. "-get_both",
  757. "-glob",
  758. "-partial",
  759. "-recno",
  760. "-rmw",
  761. "-txn",
  762. "--",
  763. NULL
  764. };
  765. enum dbgetopts {
  766. #if CONFIG_TEST
  767. DBGET_DIRTY,
  768. DBGET_MULTI,
  769. #endif
  770. DBGET_CONSUME,
  771. DBGET_CONSUME_WAIT,
  772. DBGET_BOTH,
  773. DBGET_GLOB,
  774. DBGET_PART,
  775. DBGET_RECNO,
  776. DBGET_RMW,
  777. DBGET_TXN,
  778. DBGET_ENDARG
  779. };
  780. DBC *dbc;
  781. DBT key, pkey, data, save;
  782. DBTYPE type;
  783. DB_TXN *txn;
  784. Tcl_Obj **elemv, *retlist;
  785. void *dtmp, *ktmp;
  786. u_int32_t flag, cflag, isdup, mflag, rmw;
  787. int bufsize, elemc, end, endarg, freekey, freedata, i;
  788. int optindex, result, ret, useglob, useprecno, userecno;
  789. char *arg, *pattern, *prefix, msg[MSG_SIZE];
  790. db_recno_t precno, recno;
  791. result = TCL_OK;
  792. freekey = freedata = 0;
  793. cflag = endarg = flag = mflag = rmw = 0;
  794. useglob = userecno = useprecno = 0;
  795. txn = NULL;
  796. pattern = prefix = NULL;
  797. if (objc < 3) {
  798. Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
  799. return (TCL_ERROR);
  800. }
  801. memset(&key, 0, sizeof(key));
  802. memset(&data, 0, sizeof(data));
  803. memset(&save, 0, sizeof(save));
  804. /* For the primary key in a pget call. */
  805. memset(&pkey, 0, sizeof(pkey));
  806. /*
  807.  * Get the command name index from the object based on the options
  808.  * defined above.
  809.  */
  810. i = 2;
  811. (void)dbp->get_type(dbp, &type);
  812. end = objc;
  813. while (i < end) {
  814. if (Tcl_GetIndexFromObj(interp, objv[i], dbgetopts, "option",
  815.     TCL_EXACT, &optindex) != TCL_OK) {
  816. arg = Tcl_GetStringFromObj(objv[i], NULL);
  817. if (arg[0] == '-') {
  818. result = IS_HELP(objv[i]);
  819. goto out;
  820. } else
  821. Tcl_ResetResult(interp);
  822. break;
  823. }
  824. i++;
  825. switch ((enum dbgetopts)optindex) {
  826. #if CONFIG_TEST
  827. case DBGET_DIRTY:
  828. rmw |= DB_DIRTY_READ;
  829. break;
  830. case DBGET_MULTI:
  831. mflag |= DB_MULTIPLE;
  832. result = Tcl_GetIntFromObj(interp, objv[i], &bufsize);
  833. if (result != TCL_OK)
  834. goto out;
  835. i++;
  836. break;
  837. #endif
  838. case DBGET_BOTH:
  839. /*
  840.  * Change 'end' and make sure we aren't already past
  841.  * the new end.
  842.  */
  843. if (i > objc - 2) {
  844. Tcl_WrongNumArgs(interp, 2, objv,
  845.     "?-get_both key data?");
  846. result = TCL_ERROR;
  847. break;
  848. }
  849. end = objc - 2;
  850. FLAG_CHECK(flag);
  851. flag = DB_GET_BOTH;
  852. break;
  853. case DBGET_TXN:
  854. if (i >= end) {
  855. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  856. result = TCL_ERROR;
  857. break;
  858. }
  859. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  860. txn = NAME_TO_TXN(arg);
  861. if (txn == NULL) {
  862. snprintf(msg, MSG_SIZE,
  863.     "Get: Invalid txn: %sn", arg);
  864. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  865. result = TCL_ERROR;
  866. }
  867. break;
  868. case DBGET_GLOB:
  869. useglob = 1;
  870. end = objc - 1;
  871. break;
  872. case DBGET_CONSUME:
  873. FLAG_CHECK(flag);
  874. flag = DB_CONSUME;
  875. break;
  876. case DBGET_CONSUME_WAIT:
  877. FLAG_CHECK(flag);
  878. flag = DB_CONSUME_WAIT;
  879. break;
  880. case DBGET_RECNO:
  881. end = objc - 1;
  882. userecno = 1;
  883. if (type != DB_RECNO && type != DB_QUEUE) {
  884. FLAG_CHECK(flag);
  885. flag = DB_SET_RECNO;
  886. }
  887. break;
  888. case DBGET_RMW:
  889. rmw |= DB_RMW;
  890. break;
  891. case DBGET_PART:
  892. end = objc - 1;
  893. if (i == end) {
  894. Tcl_WrongNumArgs(interp, 2, objv,
  895.     "?-partial {offset length}?");
  896. result = TCL_ERROR;
  897. break;
  898. }
  899. /*
  900.  * Get sublist as {offset length}
  901.  */
  902. result = Tcl_ListObjGetElements(interp, objv[i++],
  903.     &elemc, &elemv);
  904. if (elemc != 2) {
  905. Tcl_SetResult(interp,
  906.     "List must be {offset length}", TCL_STATIC);
  907. result = TCL_ERROR;
  908. break;
  909. }
  910. save.flags = DB_DBT_PARTIAL;
  911. result = _GetUInt32(interp, elemv[0], &save.doff);
  912. if (result != TCL_OK)
  913. break;
  914. result = _GetUInt32(interp, elemv[1], &save.dlen);
  915. /*
  916.  * NOTE: We don't check result here because all we'd
  917.  * do is break anyway, and we are doing that.  If you
  918.  * add code here, you WILL need to add the check
  919.  * for result.  (See the check for save.doff, a few
  920.  * lines above and copy that.)
  921.  */
  922. break;
  923. case DBGET_ENDARG:
  924. endarg = 1;
  925. break;
  926. } /* switch */
  927. if (result != TCL_OK)
  928. break;
  929. if (endarg)
  930. break;
  931. }
  932. if (result != TCL_OK)
  933. goto out;
  934. if (type == DB_RECNO || type == DB_QUEUE)
  935. userecno = 1;
  936. /*
  937.  * Check args we have left versus the flags we were given.
  938.  * We might have 0, 1 or 2 left.  If we have 0, it must
  939.  * be DB_CONSUME*, if 2, then DB_GET_BOTH, all others should
  940.  * be 1.
  941.  */
  942. if (((flag == DB_CONSUME || flag == DB_CONSUME_WAIT) && i != objc) ||
  943.     (flag == DB_GET_BOTH && i != objc - 2)) {
  944. Tcl_SetResult(interp,
  945.     "Wrong number of key/data given based on flags specifiedn",
  946.     TCL_STATIC);
  947. result = TCL_ERROR;
  948. goto out;
  949. } else if (flag == 0 && i != objc - 1) {
  950. Tcl_SetResult(interp,
  951.     "Wrong number of key/data givenn", TCL_STATIC);
  952. result = TCL_ERROR;
  953. goto out;
  954. }
  955. /*
  956.  * XXX
  957.  * We technically shouldn't be looking inside the dbp like this,
  958.  * but this is the only way to figure out whether the primary
  959.  * key should also be a recno.
  960.  */
  961. if (ispget) {
  962. if (dbp->s_primary != NULL &&
  963.     (dbp->s_primary->type == DB_RECNO ||
  964.     dbp->s_primary->type == DB_QUEUE))
  965. useprecno = 1;
  966. }
  967. /*
  968.  * Check for illegal combos of options.
  969.  */
  970. if (useglob && (userecno || flag == DB_SET_RECNO ||
  971.     type == DB_RECNO || type == DB_QUEUE)) {
  972. Tcl_SetResult(interp,
  973.     "Cannot use -glob and record numbers.n",
  974.     TCL_STATIC);
  975. result = TCL_ERROR;
  976. goto out;
  977. }
  978. if (useglob && flag == DB_GET_BOTH) {
  979. Tcl_SetResult(interp,
  980.     "Only one of -glob or -get_both can be specified.n",
  981.     TCL_STATIC);
  982. result = TCL_ERROR;
  983. goto out;
  984. }
  985. if (useglob)
  986. pattern = Tcl_GetStringFromObj(objv[objc - 1], NULL);
  987. /*
  988.  * This is the list we return
  989.  */
  990. retlist = Tcl_NewListObj(0, NULL);
  991. save.flags |= DB_DBT_MALLOC;
  992. /*
  993.  * isdup is used to know if we support duplicates.  If not, we
  994.  * can just do a db->get call and avoid using cursors.
  995.  * XXX
  996.  * When there is a db->get_flags method, it should be used.
  997.  * isdup = dbp->get_flags(dbp) & DB_DUP;
  998.  * For now we illegally peek.
  999.  * XXX
  1000.  */
  1001. isdup = dbp->flags & DB_AM_DUP;
  1002. /*
  1003.  * If the database doesn't support duplicates or we're performing
  1004.  * ops that don't require returning multiple items, use DB->get
  1005.  * instead of a cursor operation.
  1006.  */
  1007. if (pattern == NULL && (isdup == 0 || mflag != 0 ||
  1008.     flag == DB_SET_RECNO || flag == DB_GET_BOTH ||
  1009.     flag == DB_CONSUME || flag == DB_CONSUME_WAIT)) {
  1010. if (flag == DB_GET_BOTH) {
  1011. if (userecno) {
  1012. result = _GetUInt32(interp,
  1013.     objv[(objc - 2)], &recno);
  1014. if (result == TCL_OK) {
  1015. key.data = &recno;
  1016. key.size = sizeof(db_recno_t);
  1017. } else
  1018. goto out;
  1019. } else {
  1020. /*
  1021.  * Some get calls (SET_*) can change the
  1022.  * key pointers.  So, we need to store
  1023.  * the allocated key space in a tmp.
  1024.  */
  1025. ret = _CopyObjBytes(interp, objv[objc-2],
  1026.     &ktmp, &key.size, &freekey);
  1027. if (ret != 0) {
  1028. result = _ReturnSetup(interp, ret,
  1029.     DB_RETOK_DBGET(ret), "db get");
  1030. goto out;
  1031. }
  1032. key.data = ktmp;
  1033. }
  1034. /*
  1035.  * Already checked args above.  Fill in key and save.
  1036.  * Save is used in the dbp->get call below to fill in
  1037.  * data.
  1038.  *
  1039.  * If the "data" here is really a primary key--that
  1040.  * is, if we're in a pget--and that primary key
  1041.  * is a recno, treat it appropriately as an int.
  1042.  */
  1043. if (useprecno) {
  1044. result = _GetUInt32(interp,
  1045.     objv[objc - 1], &precno);
  1046. if (result == TCL_OK) {
  1047. save.data = &precno;
  1048. save.size = sizeof(db_recno_t);
  1049. } else
  1050. goto out;
  1051. } else {
  1052. ret = _CopyObjBytes(interp, objv[objc-1],
  1053.     &dtmp, &save.size, &freedata);
  1054. if (ret != 0) {
  1055. result = _ReturnSetup(interp, ret,
  1056.     DB_RETOK_DBGET(ret), "db get");
  1057. goto out;
  1058. }
  1059. save.data = dtmp;
  1060. }
  1061. } else if (flag != DB_CONSUME && flag != DB_CONSUME_WAIT) {
  1062. if (userecno) {
  1063. result = _GetUInt32(
  1064.     interp, objv[(objc - 1)], &recno);
  1065. if (result == TCL_OK) {
  1066. key.data = &recno;
  1067. key.size = sizeof(db_recno_t);
  1068. } else
  1069. goto out;
  1070. } else {
  1071. /*
  1072.  * Some get calls (SET_*) can change the
  1073.  * key pointers.  So, we need to store
  1074.  * the allocated key space in a tmp.
  1075.  */
  1076. ret = _CopyObjBytes(interp, objv[objc-1],
  1077.     &ktmp, &key.size, &freekey);
  1078. if (ret != 0) {
  1079. result = _ReturnSetup(interp, ret,
  1080.     DB_RETOK_DBGET(ret), "db get");
  1081. goto out;
  1082. }
  1083. key.data = ktmp;
  1084. }
  1085. if (mflag & DB_MULTIPLE) {
  1086. if ((ret = __os_malloc(dbp->dbenv,
  1087.     bufsize, &save.data)) != 0) {
  1088. Tcl_SetResult(interp,
  1089.     db_strerror(ret), TCL_STATIC);
  1090. goto out;
  1091. }
  1092. save.ulen = bufsize;
  1093. F_CLR(&save, DB_DBT_MALLOC);
  1094. F_SET(&save, DB_DBT_USERMEM);
  1095. }
  1096. }
  1097. data = save;
  1098. if (ispget) {
  1099. if (flag == DB_GET_BOTH) {
  1100. pkey.data = save.data;
  1101. pkey.size = save.size;
  1102. data.data = NULL;
  1103. data.size = 0;
  1104. }
  1105. F_SET(&pkey, DB_DBT_MALLOC);
  1106. _debug_check();
  1107. ret = dbp->pget(dbp,
  1108.     txn, &key, &pkey, &data, flag | rmw);
  1109. } else {
  1110. _debug_check();
  1111. ret = dbp->get(dbp,
  1112.     txn, &key, &data, flag | rmw | mflag);
  1113. }
  1114. result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret),
  1115.     "db get");
  1116. if (ret == 0) {
  1117. /*
  1118.  * Success.  Return a list of the form {name value}
  1119.  * If it was a recno in key.data, we need to convert
  1120.  * into a string/object representation of that recno.
  1121.  */
  1122. if (mflag & DB_MULTIPLE)
  1123. result = _SetMultiList(interp,
  1124.     retlist, &key, &data, type, flag);
  1125. else if (type == DB_RECNO || type == DB_QUEUE)
  1126. if (ispget)
  1127. result = _Set3DBTList(interp,
  1128.     retlist, &key, 1, &pkey,
  1129.     useprecno, &data);
  1130. else
  1131. result = _SetListRecnoElem(interp,
  1132.     retlist, *(db_recno_t *)key.data,
  1133.     data.data, data.size);
  1134. else {
  1135. if (ispget)
  1136. result = _Set3DBTList(interp,
  1137.     retlist, &key, 0, &pkey,
  1138.     useprecno, &data);
  1139. else
  1140. result = _SetListElem(interp, retlist,
  1141.     key.data, key.size,
  1142.     data.data, data.size);
  1143. }
  1144. }
  1145. /*
  1146.  * Free space from DBT.
  1147.  *
  1148.  * If we set DB_DBT_MALLOC, we need to free the space if
  1149.  * and only if we succeeded (and thus if DB allocated
  1150.  * anything).  If DB_DBT_MALLOC is not set, this is a bulk
  1151.  * get buffer, and needs to be freed no matter what.
  1152.  */
  1153. if (F_ISSET(&data, DB_DBT_MALLOC) && ret == 0)
  1154. __os_ufree(dbp->dbenv, data.data);
  1155. else if (!F_ISSET(&data, DB_DBT_MALLOC))
  1156. __os_free(dbp->dbenv, data.data);
  1157. if (ispget && ret == 0)
  1158. __os_ufree(dbp->dbenv, pkey.data);
  1159. if (result == TCL_OK)
  1160. Tcl_SetObjResult(interp, retlist);
  1161. goto out;
  1162. }
  1163. if (userecno) {
  1164. result = _GetUInt32(interp, objv[(objc - 1)], &recno);
  1165. if (result == TCL_OK) {
  1166. key.data = &recno;
  1167. key.size = sizeof(db_recno_t);
  1168. } else
  1169. goto out;
  1170. } else {
  1171. /*
  1172.  * Some get calls (SET_*) can change the
  1173.  * key pointers.  So, we need to store
  1174.  * the allocated key space in a tmp.
  1175.  */
  1176. ret = _CopyObjBytes(interp, objv[objc-1], &ktmp,
  1177.     &key.size, &freekey);
  1178. if (ret != 0) {
  1179. result = _ReturnSetup(interp, ret,
  1180.     DB_RETOK_DBGET(ret), "db get");
  1181. return (result);
  1182. }
  1183. key.data = ktmp;
  1184. }
  1185. ret = dbp->cursor(dbp, txn, &dbc, 0);
  1186. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db cursor");
  1187. if (result == TCL_ERROR)
  1188. goto out;
  1189. /*
  1190.  * At this point, we have a cursor, if we have a pattern,
  1191.  * we go to the nearest one and step forward until we don't
  1192.  * have any more that match the pattern prefix.  If we have
  1193.  * an exact key, we go to that key position, and step through
  1194.  * all the duplicates.  In either case we build up a list of
  1195.  * the form {{key data} {key data}...} along the way.
  1196.  */
  1197. memset(&data, 0, sizeof(data));
  1198. /*
  1199.  * Restore any "partial" info we have saved.
  1200.  */
  1201. data = save;
  1202. if (pattern) {
  1203. /*
  1204.  * Note, prefix is returned in new space.  Must free it.
  1205.  */
  1206. ret = _GetGlobPrefix(pattern, &prefix);
  1207. if (ret) {
  1208. result = TCL_ERROR;
  1209. Tcl_SetResult(interp,
  1210.     "Unable to allocate pattern space", TCL_STATIC);
  1211. goto out1;
  1212. }
  1213. key.data = prefix;
  1214. key.size = strlen(prefix);
  1215. /*
  1216.  * If they give us an empty pattern string
  1217.  * (i.e. -glob *), go through entire DB.
  1218.  */
  1219. if (strlen(prefix) == 0)
  1220. cflag = DB_FIRST;
  1221. else
  1222. cflag = DB_SET_RANGE;
  1223. } else
  1224. cflag = DB_SET;
  1225. if (ispget) {
  1226. _debug_check();
  1227. F_SET(&pkey, DB_DBT_MALLOC);
  1228. ret = dbc->c_pget(dbc, &key, &pkey, &data, cflag | rmw);
  1229. } else {
  1230. _debug_check();
  1231. ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
  1232. }
  1233. result = _ReturnSetup(interp, ret, DB_RETOK_DBCGET(ret),
  1234.     "db get (cursor)");
  1235. if (result == TCL_ERROR)
  1236. goto out1;
  1237. if (ret == 0 && pattern &&
  1238.     memcmp(key.data, prefix, strlen(prefix)) != 0) {
  1239. /*
  1240.  * Free space from DB_DBT_MALLOC
  1241.  */
  1242. free(data.data);
  1243. goto out1;
  1244. }
  1245. if (pattern)
  1246. cflag = DB_NEXT;
  1247.  else
  1248. cflag = DB_NEXT_DUP;
  1249. while (ret == 0 && result == TCL_OK) {
  1250. /*
  1251.  * Build up our {name value} sublist
  1252.  */
  1253. if (ispget)
  1254. result = _Set3DBTList(interp, retlist, &key, 0,
  1255.     &pkey, useprecno, &data);
  1256. else
  1257. result = _SetListElem(interp, retlist,
  1258.     key.data, key.size, data.data, data.size);
  1259. /*
  1260.  * Free space from DB_DBT_MALLOC
  1261.  */
  1262. if (ispget)
  1263. free(pkey.data);
  1264. free(data.data);
  1265. if (result != TCL_OK)
  1266. break;
  1267. /*
  1268.  * Append {name value} to return list
  1269.  */
  1270. memset(&key, 0, sizeof(key));
  1271. memset(&pkey, 0, sizeof(pkey));
  1272. memset(&data, 0, sizeof(data));
  1273. /*
  1274.  * Restore any "partial" info we have saved.
  1275.  */
  1276. data = save;
  1277. if (ispget) {
  1278. F_SET(&pkey, DB_DBT_MALLOC);
  1279. ret = dbc->c_pget(dbc, &key, &pkey, &data, cflag | rmw);
  1280. } else
  1281. ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
  1282. if (ret == 0 && pattern &&
  1283.     memcmp(key.data, prefix, strlen(prefix)) != 0) {
  1284. /*
  1285.  * Free space from DB_DBT_MALLOC
  1286.  */
  1287. free(data.data);
  1288. break;
  1289. }
  1290. }
  1291. out1:
  1292. dbc->c_close(dbc);
  1293. if (result == TCL_OK)
  1294. Tcl_SetObjResult(interp, retlist);
  1295. out:
  1296. /*
  1297.  * _GetGlobPrefix(), the function which allocates prefix, works
  1298.  * by copying and condensing another string.  Thus prefix may
  1299.  * have multiple nuls at the end, so we free using __os_free().
  1300.  */
  1301. if (prefix != NULL)
  1302. __os_free(dbp->dbenv, prefix);
  1303. if (freedata)
  1304. (void)__os_free(dbp->dbenv, dtmp);
  1305. if (freekey)
  1306. (void)__os_free(dbp->dbenv, ktmp);
  1307. return (result);
  1308. }
  1309. /*
  1310.  * tcl_db_delete --
  1311.  */
  1312. static int
  1313. tcl_DbDelete(interp, objc, objv, dbp)
  1314. Tcl_Interp *interp; /* Interpreter */
  1315. int objc; /* How many arguments? */
  1316. Tcl_Obj *CONST objv[]; /* The argument objects */
  1317. DB *dbp; /* Database pointer */
  1318. {
  1319. static char *dbdelopts[] = {
  1320. "-auto_commit",
  1321. "-glob",
  1322. "-txn",
  1323. NULL
  1324. };
  1325. enum dbdelopts {
  1326. DBDEL_AUTO_COMMIT,
  1327. DBDEL_GLOB,
  1328. DBDEL_TXN
  1329. };
  1330. DBC *dbc;
  1331. DBT key, data;
  1332. DBTYPE type;
  1333. DB_TXN *txn;
  1334. void *ktmp;
  1335. db_recno_t recno;
  1336. int freekey, i, optindex, result, ret;
  1337. u_int32_t flag;
  1338. char *arg, *pattern, *prefix, msg[MSG_SIZE];
  1339. result = TCL_OK;
  1340. freekey = 0;
  1341. flag = 0;
  1342. pattern = prefix = NULL;
  1343. txn = NULL;
  1344. if (objc < 3) {
  1345. Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
  1346. return (TCL_ERROR);
  1347. }
  1348. memset(&key, 0, sizeof(key));
  1349. /*
  1350.  * The first arg must be -auto_commit, -glob, -txn or a list of keys.
  1351.  */
  1352. i = 2;
  1353. while (i < objc) {
  1354. if (Tcl_GetIndexFromObj(interp, objv[i], dbdelopts, "option",
  1355.     TCL_EXACT, &optindex) != TCL_OK) {
  1356. /*
  1357.  * If we don't have a -auto_commit, -glob or -txn,
  1358.  * then the remaining args must be exact keys.
  1359.  * Reset the result so we don't get an errant error
  1360.  * message if there is another error.
  1361.  */
  1362. if (IS_HELP(objv[i]) == TCL_OK)
  1363. return (TCL_OK);
  1364. Tcl_ResetResult(interp);
  1365. break;
  1366. }
  1367. i++;
  1368. switch ((enum dbdelopts)optindex) {
  1369. case DBDEL_TXN:
  1370. if (i == objc) {
  1371. /*
  1372.  * Someone could conceivably have a key of
  1373.  * the same name.  So just break and use it.
  1374.  */
  1375. i--;
  1376. break;
  1377. }
  1378. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1379. txn = NAME_TO_TXN(arg);
  1380. if (txn == NULL) {
  1381. snprintf(msg, MSG_SIZE,
  1382.     "Delete: Invalid txn: %sn", arg);
  1383. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1384. result = TCL_ERROR;
  1385. }
  1386. break;
  1387. case DBDEL_AUTO_COMMIT:
  1388. flag |= DB_AUTO_COMMIT;
  1389. break;
  1390. case DBDEL_GLOB:
  1391. /*
  1392.  * Get the pattern.  Get the prefix and use cursors to
  1393.  * get all the data items.
  1394.  */
  1395. if (i == objc) {
  1396. /*
  1397.  * Someone could conceivably have a key of
  1398.  * the same name.  So just break and use it.
  1399.  */
  1400. i--;
  1401. break;
  1402. }
  1403. pattern = Tcl_GetStringFromObj(objv[i++], NULL);
  1404. break;
  1405. }
  1406. if (result != TCL_OK)
  1407. break;
  1408. }
  1409. if (result != TCL_OK)
  1410. goto out;
  1411. /*
  1412.  * XXX
  1413.  * For consistency with get, we have decided for the moment, to
  1414.  * allow -glob, or one key, not many.  The code was originally
  1415.  * written to take many keys and we'll leave it that way, because
  1416.  * tcl_DbGet may one day accept many disjoint keys to get, rather
  1417.  * than one, and at that time we'd make delete be consistent.  In
  1418.  * any case, the code is already here and there is no need to remove,
  1419.  * just check that we only have one arg left.
  1420.  *
  1421.  * If we have a pattern AND more keys to process, there is an error.
  1422.  * Either we have some number of exact keys, or we have a pattern.
  1423.  *
  1424.  * If we have a pattern and an auto commit flag, there is an error.
  1425.  */
  1426. if (pattern == NULL) {
  1427. if (i != (objc - 1)) {
  1428. Tcl_WrongNumArgs(
  1429.     interp, 2, objv, "?args? -glob pattern | key");
  1430. result = TCL_ERROR;
  1431. goto out;
  1432. }
  1433. } else {
  1434. if (i != objc) {
  1435. Tcl_WrongNumArgs(
  1436.     interp, 2, objv, "?args? -glob pattern | key");
  1437. result = TCL_ERROR;
  1438. goto out;
  1439. }
  1440. if (flag & DB_AUTO_COMMIT) {
  1441. Tcl_SetResult(interp,
  1442.     "Cannot use -auto_commit and patterns.n",
  1443.     TCL_STATIC);
  1444. result = TCL_ERROR;
  1445. goto out;
  1446. }
  1447. }
  1448. /*
  1449.  * If we have remaining args, they are all exact keys.  Call
  1450.  * DB->del on each of those keys.
  1451.  *
  1452.  * If it is a RECNO database, the key is a record number and must be
  1453.  * setup up to contain a db_recno_t.  Otherwise the key is a "string".
  1454.  */
  1455. (void)dbp->get_type(dbp, &type);
  1456. ret = 0;
  1457. while (i < objc && ret == 0) {
  1458. memset(&key, 0, sizeof(key));
  1459. if (type == DB_RECNO || type == DB_QUEUE) {
  1460. result = _GetUInt32(interp, objv[i++], &recno);
  1461. if (result == TCL_OK) {
  1462. key.data = &recno;
  1463. key.size = sizeof(db_recno_t);
  1464. } else
  1465. return (result);
  1466. } else {
  1467. ret = _CopyObjBytes(interp, objv[i++], &ktmp,
  1468.     &key.size, &freekey);
  1469. if (ret != 0) {
  1470. result = _ReturnSetup(interp, ret,
  1471.     DB_RETOK_DBDEL(ret), "db del");
  1472. return (result);
  1473. }
  1474. key.data = ktmp;
  1475. }
  1476. _debug_check();
  1477. ret = dbp->del(dbp, txn, &key, flag);
  1478. /*
  1479.  * If we have any error, set up return result and stop
  1480.  * processing keys.
  1481.  */
  1482. if (freekey)
  1483. (void)__os_free(dbp->dbenv, ktmp);
  1484. if (ret != 0)
  1485. break;
  1486. }
  1487. result = _ReturnSetup(interp, ret, DB_RETOK_DBDEL(ret), "db del");
  1488. /*
  1489.  * At this point we've either finished or, if we have a pattern,
  1490.  * we go to the nearest one and step forward until we don't
  1491.  * have any more that match the pattern prefix.
  1492.  */
  1493. if (pattern) {
  1494. ret = dbp->cursor(dbp, txn, &dbc, 0);
  1495. if (ret != 0) {
  1496. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  1497.     "db cursor");
  1498. goto out;
  1499. }
  1500. /*
  1501.  * Note, prefix is returned in new space.  Must free it.
  1502.  */
  1503. memset(&key, 0, sizeof(key));
  1504. memset(&data, 0, sizeof(data));
  1505. ret = _GetGlobPrefix(pattern, &prefix);
  1506. if (ret) {
  1507. result = TCL_ERROR;
  1508. Tcl_SetResult(interp,
  1509.     "Unable to allocate pattern space", TCL_STATIC);
  1510. goto out;
  1511. }
  1512. key.data = prefix;
  1513. key.size = strlen(prefix);
  1514. if (strlen(prefix) == 0)
  1515. flag = DB_FIRST;
  1516. else
  1517. flag = DB_SET_RANGE;
  1518. ret = dbc->c_get(dbc, &key, &data, flag);
  1519. while (ret == 0 &&
  1520.     memcmp(key.data, prefix, strlen(prefix)) == 0) {
  1521. /*
  1522.  * Each time through here the cursor is pointing
  1523.  * at the current valid item.  Delete it and
  1524.  * move ahead.
  1525.  */
  1526. _debug_check();
  1527. ret = dbc->c_del(dbc, 0);
  1528. if (ret != 0) {
  1529. result = _ReturnSetup(interp, ret,
  1530.     DB_RETOK_DBCDEL(ret), "db c_del");
  1531. break;
  1532. }
  1533. /*
  1534.  * Deleted the current, now move to the next item
  1535.  * in the list, check if it matches the prefix pattern.
  1536.  */
  1537. memset(&key, 0, sizeof(key));
  1538. memset(&data, 0, sizeof(data));
  1539. ret = dbc->c_get(dbc, &key, &data, DB_NEXT);
  1540. }
  1541. if (ret == DB_NOTFOUND)
  1542. ret = 0;
  1543. /*
  1544.  * _GetGlobPrefix(), the function which allocates prefix, works
  1545.  * by copying and condensing another string.  Thus prefix may
  1546.  * have multiple nuls at the end, so we free using __os_free().
  1547.  */
  1548. __os_free(dbp->dbenv, prefix);
  1549. dbc->c_close(dbc);
  1550. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db del");
  1551. }
  1552. out:
  1553. return (result);
  1554. }
  1555. /*
  1556.  * tcl_db_cursor --
  1557.  */
  1558. static int
  1559. tcl_DbCursor(interp, objc, objv, dbp, dbcp)
  1560. Tcl_Interp *interp; /* Interpreter */
  1561. int objc; /* How many arguments? */
  1562. Tcl_Obj *CONST objv[]; /* The argument objects */
  1563. DB *dbp; /* Database pointer */
  1564. DBC **dbcp; /* Return cursor pointer */
  1565. {
  1566. static char *dbcuropts[] = {
  1567. #if CONFIG_TEST
  1568. "-dirty",
  1569. "-update",
  1570. #endif
  1571. "-txn",
  1572. NULL
  1573. };
  1574. enum dbcuropts {
  1575. #if CONFIG_TEST
  1576. DBCUR_DIRTY,
  1577. DBCUR_UPDATE,
  1578. #endif
  1579. DBCUR_TXN
  1580. };
  1581. DB_TXN *txn;
  1582. u_int32_t flag;
  1583. int i, optindex, result, ret;
  1584. char *arg, msg[MSG_SIZE];
  1585. result = TCL_OK;
  1586. flag = 0;
  1587. txn = NULL;
  1588. i = 2;
  1589. while (i < objc) {
  1590. if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
  1591.     TCL_EXACT, &optindex) != TCL_OK) {
  1592. result = IS_HELP(objv[i]);
  1593. goto out;
  1594. }
  1595. i++;
  1596. switch ((enum dbcuropts)optindex) {
  1597. #if CONFIG_TEST
  1598. case DBCUR_DIRTY:
  1599. flag |= DB_DIRTY_READ;
  1600. break;
  1601. case DBCUR_UPDATE:
  1602. flag |= DB_WRITECURSOR;
  1603. break;
  1604. #endif
  1605. case DBCUR_TXN:
  1606. if (i == objc) {
  1607. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  1608. result = TCL_ERROR;
  1609. break;
  1610. }
  1611. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1612. txn = NAME_TO_TXN(arg);
  1613. if (txn == NULL) {
  1614. snprintf(msg, MSG_SIZE,
  1615.     "Cursor: Invalid txn: %sn", arg);
  1616. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1617. result = TCL_ERROR;
  1618. }
  1619. break;
  1620. }
  1621. if (result != TCL_OK)
  1622. break;
  1623. }
  1624. if (result != TCL_OK)
  1625. goto out;
  1626. _debug_check();
  1627. ret = dbp->cursor(dbp, txn, dbcp, flag);
  1628. if (ret != 0)
  1629. result = _ErrorSetup(interp, ret, "db cursor");
  1630. out:
  1631. return (result);
  1632. }
  1633. /*
  1634.  * tcl_DbAssociate --
  1635.  * Call DB->associate().
  1636.  */
  1637. static int
  1638. tcl_DbAssociate(interp, objc, objv, dbp)
  1639. Tcl_Interp *interp;
  1640. int objc;
  1641. Tcl_Obj *CONST objv[];
  1642. DB *dbp;
  1643. {
  1644. static char *dbaopts[] = {
  1645. "-auto_commit",
  1646. "-create",
  1647. "-txn",
  1648. NULL
  1649. };
  1650. enum dbaopts {
  1651. DBA_AUTO_COMMIT,
  1652. DBA_CREATE,
  1653. DBA_TXN
  1654. };
  1655. DB *sdbp;
  1656. DB_TXN *txn;
  1657. DBTCL_INFO *sdbip;
  1658. int i, optindex, result, ret;
  1659. char *arg, msg[MSG_SIZE];
  1660. u_int32_t flag;
  1661. txn = NULL;
  1662. result = TCL_OK;
  1663. flag = 0;
  1664. if (objc < 2) {
  1665. Tcl_WrongNumArgs(interp, 2, objv, "[callback] secondary");
  1666. return (TCL_ERROR);
  1667. }
  1668. i = 2;
  1669. while (i < objc) {
  1670. if (Tcl_GetIndexFromObj(interp, objv[i], dbaopts, "option",
  1671.     TCL_EXACT, &optindex) != TCL_OK) {
  1672. result = IS_HELP(objv[i]);
  1673. if (result == TCL_OK)
  1674. return (result);
  1675. result = TCL_OK;
  1676. Tcl_ResetResult(interp);
  1677. break;
  1678. }
  1679. i++;
  1680. switch ((enum dbaopts)optindex) {
  1681. case DBA_AUTO_COMMIT:
  1682. flag |= DB_AUTO_COMMIT;
  1683. break;
  1684. case DBA_CREATE:
  1685. flag |= DB_CREATE;
  1686. break;
  1687. case DBA_TXN:
  1688. if (i > (objc - 1)) {
  1689. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  1690. result = TCL_ERROR;
  1691. break;
  1692. }
  1693. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1694. txn = NAME_TO_TXN(arg);
  1695. if (txn == NULL) {
  1696. snprintf(msg, MSG_SIZE,
  1697.     "Associate: Invalid txn: %sn", arg);
  1698. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1699. result = TCL_ERROR;
  1700. }
  1701. break;
  1702. }
  1703. }
  1704. if (result != TCL_OK)
  1705. return (result);
  1706. /*
  1707.  * Better be 1 or 2 args left.  The last arg must be the sdb
  1708.  * handle.  If 2 args then objc-2 is the callback proc, else
  1709.  * we have a NULL callback.
  1710.  */
  1711. /* Get the secondary DB handle. */
  1712. arg = Tcl_GetStringFromObj(objv[objc - 1], NULL);
  1713. sdbp = NAME_TO_DB(arg);
  1714. if (sdbp == NULL) {
  1715. snprintf(msg, MSG_SIZE,
  1716.     "Associate: Invalid database handle: %sn", arg);
  1717. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1718. return (TCL_ERROR);
  1719. }
  1720. /*
  1721.  * The callback is simply a Tcl object containing the name
  1722.  * of the callback proc, which is the second-to-last argument.
  1723.  *
  1724.  * Note that the callback needs to go in the *secondary* DB handle's
  1725.  * info struct;  we may have multiple secondaries with different
  1726.  * callbacks.
  1727.  */
  1728. sdbip = (DBTCL_INFO *)sdbp->api_internal;
  1729. if (i != objc - 1) {
  1730. /*
  1731.  * We have 2 args, get the callback.
  1732.  */
  1733. sdbip->i_second_call = objv[objc - 2];
  1734. Tcl_IncrRefCount(sdbip->i_second_call);
  1735. /* Now call associate. */
  1736. _debug_check();
  1737. ret = dbp->associate(dbp, txn, sdbp, tcl_second_call, flag);
  1738. } else {
  1739. /*
  1740.  * We have a NULL callback.
  1741.  */
  1742. sdbip->i_second_call = NULL;
  1743. ret = dbp->associate(dbp, txn, sdbp, NULL, flag);
  1744. }
  1745. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "associate");
  1746. return (result);
  1747. }
  1748. /*
  1749.  * tcl_second_call --
  1750.  * Callback function for secondary indices.  Get the callback
  1751.  * out of ip->i_second_call and call it.
  1752.  */
  1753. static int
  1754. tcl_second_call(dbp, pkey, data, skey)
  1755. DB *dbp;
  1756. const DBT *pkey, *data;
  1757. DBT *skey;
  1758. {
  1759. DBTCL_INFO *ip;
  1760. Tcl_Interp *interp;
  1761. Tcl_Obj *pobj, *dobj, *objv[3];
  1762. int len, result, ret;
  1763. void *retbuf, *databuf;
  1764. ip = (DBTCL_INFO *)dbp->api_internal;
  1765. interp = ip->i_interp;
  1766. objv[0] = ip->i_second_call;
  1767. /*
  1768.  * Create two ByteArray objects, with the contents of the pkey
  1769.  * and data DBTs that are our inputs.
  1770.  */
  1771. pobj = Tcl_NewByteArrayObj(pkey->data, pkey->size);
  1772. Tcl_IncrRefCount(pobj);
  1773. dobj = Tcl_NewByteArrayObj(data->data, data->size);
  1774. Tcl_IncrRefCount(dobj);
  1775. objv[1] = pobj;
  1776. objv[2] = dobj;
  1777. result = Tcl_EvalObjv(interp, 3, objv, 0);
  1778. Tcl_DecrRefCount(pobj);
  1779. Tcl_DecrRefCount(dobj);
  1780. if (result != TCL_OK) {
  1781. __db_err(dbp->dbenv,
  1782.     "Tcl callback function failed with code %d", result);
  1783. return (EINVAL);
  1784. }
  1785. retbuf =
  1786.     Tcl_GetByteArrayFromObj(Tcl_GetObjResult(interp), &len);
  1787. /*
  1788.  * retbuf is owned by Tcl; copy it into malloc'ed memory.
  1789.  * We need to use __os_umalloc rather than ufree because this will
  1790.  * be freed by DB using __os_ufree--the DB_DBT_APPMALLOC flag
  1791.  * tells DB to free application-allocated memory.
  1792.  */
  1793. if ((ret = __os_umalloc(dbp->dbenv, len, &databuf)) != 0)
  1794. return (ret);
  1795. memcpy(databuf, retbuf, len);
  1796. skey->data = databuf;
  1797. skey->size = len;
  1798. F_SET(skey, DB_DBT_APPMALLOC);
  1799. return (0);
  1800. }
  1801. /*
  1802.  * tcl_db_join --
  1803.  */
  1804. static int
  1805. tcl_DbJoin(interp, objc, objv, dbp, dbcp)
  1806. Tcl_Interp *interp; /* Interpreter */
  1807. int objc; /* How many arguments? */
  1808. Tcl_Obj *CONST objv[]; /* The argument objects */
  1809. DB *dbp; /* Database pointer */
  1810. DBC **dbcp; /* Cursor pointer */
  1811. {
  1812. static char *dbjopts[] = {
  1813. "-nosort",
  1814. NULL
  1815. };
  1816. enum dbjopts {
  1817. DBJ_NOSORT
  1818. };
  1819. DBC **listp;
  1820. u_int32_t flag;
  1821. int adj, i, j, optindex, size, result, ret;
  1822. char *arg, msg[MSG_SIZE];
  1823. result = TCL_OK;
  1824. flag = 0;
  1825. if (objc < 3) {
  1826. Tcl_WrongNumArgs(interp, 2, objv, "curs1 curs2 ...");
  1827. return (TCL_ERROR);
  1828. }
  1829. i = 2;
  1830. adj = i;
  1831. while (i < objc) {
  1832. if (Tcl_GetIndexFromObj(interp, objv[i], dbjopts, "option",
  1833.     TCL_EXACT, &optindex) != TCL_OK) {
  1834. result = IS_HELP(objv[i]);
  1835. if (result == TCL_OK)
  1836. return (result);
  1837. result = TCL_OK;
  1838. Tcl_ResetResult(interp);
  1839. break;
  1840. }
  1841. i++;
  1842. switch ((enum dbjopts)optindex) {
  1843. case DBJ_NOSORT:
  1844. flag |= DB_JOIN_NOSORT;
  1845. adj++;
  1846. break;
  1847. }
  1848. }
  1849. if (result != TCL_OK)
  1850. return (result);
  1851. /*
  1852.  * Allocate one more for NULL ptr at end of list.
  1853.  */
  1854. size = sizeof(DBC *) * ((objc - adj) + 1);
  1855. ret = __os_malloc(dbp->dbenv, size, &listp);
  1856. if (ret != 0) {
  1857. Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
  1858. return (TCL_ERROR);
  1859. }
  1860. memset(listp, 0, size);
  1861. for (j = 0, i = adj; i < objc; i++, j++) {
  1862. arg = Tcl_GetStringFromObj(objv[i], NULL);
  1863. listp[j] = NAME_TO_DBC(arg);
  1864. if (listp[j] == NULL) {
  1865. snprintf(msg, MSG_SIZE,
  1866.     "Join: Invalid cursor: %sn", arg);
  1867. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1868. result = TCL_ERROR;
  1869. goto out;
  1870. }
  1871. }
  1872. listp[j] = NULL;
  1873. _debug_check();
  1874. ret = dbp->join(dbp, listp, dbcp, flag);
  1875. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db join");
  1876. out:
  1877. __os_free(dbp->dbenv, listp);
  1878. return (result);
  1879. }
  1880. /*
  1881.  * tcl_db_getjoin --
  1882.  */
  1883. static int
  1884. tcl_DbGetjoin(interp, objc, objv, dbp)
  1885. Tcl_Interp *interp; /* Interpreter */
  1886. int objc; /* How many arguments? */
  1887. Tcl_Obj *CONST objv[]; /* The argument objects */
  1888. DB *dbp; /* Database pointer */
  1889. {
  1890. static char *dbgetjopts[] = {
  1891. #if CONFIG_TEST
  1892. "-nosort",
  1893. #endif
  1894. "-txn",
  1895. NULL
  1896. };
  1897. enum dbgetjopts {
  1898. #if CONFIG_TEST
  1899. DBGETJ_NOSORT,
  1900. #endif
  1901. DBGETJ_TXN
  1902. };
  1903. DB_TXN *txn;
  1904. DB *elemdbp;
  1905. DBC **listp;
  1906. DBC *dbc;
  1907. DBT key, data;
  1908. Tcl_Obj **elemv, *retlist;
  1909. void *ktmp;
  1910. u_int32_t flag;
  1911. int adj, elemc, freekey, i, j, optindex, result, ret, size;
  1912. char *arg, msg[MSG_SIZE];
  1913. result = TCL_OK;
  1914. flag = 0;
  1915. freekey = 0;
  1916. if (objc < 3) {
  1917. Tcl_WrongNumArgs(interp, 2, objv, "{db1 key1} {db2 key2} ...");
  1918. return (TCL_ERROR);
  1919. }
  1920. txn = NULL;
  1921. i = 2;
  1922. adj = i;
  1923. while (i < objc) {
  1924. if (Tcl_GetIndexFromObj(interp, objv[i], dbgetjopts, "option",
  1925.     TCL_EXACT, &optindex) != TCL_OK) {
  1926. result = IS_HELP(objv[i]);
  1927. if (result == TCL_OK)
  1928. return (result);
  1929. result = TCL_OK;
  1930. Tcl_ResetResult(interp);
  1931. break;
  1932. }
  1933. i++;
  1934. switch ((enum dbgetjopts)optindex) {
  1935. #if CONFIG_TEST
  1936. case DBGETJ_NOSORT:
  1937. flag |= DB_JOIN_NOSORT;
  1938. adj++;
  1939. break;
  1940. #endif
  1941. case DBGETJ_TXN:
  1942. if (i == objc) {
  1943. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  1944. result = TCL_ERROR;
  1945. break;
  1946. }
  1947. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1948. txn = NAME_TO_TXN(arg);
  1949. adj += 2;
  1950. if (txn == NULL) {
  1951. snprintf(msg, MSG_SIZE,
  1952.     "GetJoin: Invalid txn: %sn", arg);
  1953. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1954. result = TCL_ERROR;
  1955. }
  1956. break;
  1957. }
  1958. }
  1959. if (result != TCL_OK)
  1960. return (result);
  1961. size = sizeof(DBC *) * ((objc - adj) + 1);
  1962. ret = __os_malloc(NULL, size, &listp);
  1963. if (ret != 0) {
  1964. Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
  1965. return (TCL_ERROR);
  1966. }
  1967. memset(listp, 0, size);
  1968. for (j = 0, i = adj; i < objc; i++, j++) {
  1969. /*
  1970.  * Get each sublist as {db key}
  1971.  */
  1972. result = Tcl_ListObjGetElements(interp, objv[i],
  1973.     &elemc, &elemv);
  1974. if (elemc != 2) {
  1975. Tcl_SetResult(interp, "Lists must be {db key}",
  1976.     TCL_STATIC);
  1977. result = TCL_ERROR;
  1978. goto out;
  1979. }
  1980. /*
  1981.  * Get a pointer to that open db.  Then, open a cursor in
  1982.  * that db, and go to the "key" place.
  1983.  */
  1984. elemdbp = NAME_TO_DB(Tcl_GetStringFromObj(elemv[0], NULL));
  1985. if (elemdbp == NULL) {
  1986. snprintf(msg, MSG_SIZE, "Get_join: Invalid db: %sn",
  1987.     Tcl_GetStringFromObj(elemv[0], NULL));
  1988. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1989. result = TCL_ERROR;
  1990. goto out;
  1991. }
  1992. ret = elemdbp->cursor(elemdbp, txn, &listp[j], 0);
  1993. if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  1994.     "db cursor")) == TCL_ERROR)
  1995. goto out;
  1996. memset(&key, 0, sizeof(key));
  1997. memset(&data, 0, sizeof(data));
  1998. ret = _CopyObjBytes(interp, elemv[elemc-1], &ktmp,
  1999.     &key.size, &freekey);
  2000. if (ret != 0) {
  2001. result = _ReturnSetup(interp, ret,
  2002.     DB_RETOK_STD(ret), "db join");
  2003. goto out;
  2004. }
  2005. key.data = ktmp;
  2006. ret = (listp[j])->c_get(listp[j], &key, &data, DB_SET);
  2007. if ((result = _ReturnSetup(interp, ret, DB_RETOK_DBCGET(ret),
  2008.     "db cget")) == TCL_ERROR)
  2009. goto out;
  2010. }
  2011. listp[j] = NULL;
  2012. _debug_check();
  2013. ret = dbp->join(dbp, listp, &dbc, flag);
  2014. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db join");
  2015. if (result == TCL_ERROR)
  2016. goto out;
  2017. retlist = Tcl_NewListObj(0, NULL);
  2018. while (ret == 0 && result == TCL_OK) {
  2019. memset(&key, 0, sizeof(key));
  2020. memset(&data, 0, sizeof(data));
  2021. key.flags |= DB_DBT_MALLOC;
  2022. data.flags |= DB_DBT_MALLOC;
  2023. ret = dbc->c_get(dbc, &key, &data, 0);
  2024. /*
  2025.  * Build up our {name value} sublist
  2026.  */
  2027. if (ret == 0) {
  2028. result = _SetListElem(interp, retlist,
  2029.     key.data, key.size,
  2030.     data.data, data.size);
  2031. free(key.data);
  2032. free(data.data);
  2033. }
  2034. }
  2035. dbc->c_close(dbc);
  2036. if (result == TCL_OK)
  2037. Tcl_SetObjResult(interp, retlist);
  2038. out:
  2039. if (freekey)
  2040. (void)__os_free(dbp->dbenv, ktmp);
  2041. while (j) {
  2042. if (listp[j])
  2043. (listp[j])->c_close(listp[j]);
  2044. j--;
  2045. }
  2046. __os_free(dbp->dbenv, listp);
  2047. return (result);
  2048. }
  2049. /*
  2050.  * tcl_DbCount --
  2051.  */
  2052. static int
  2053. tcl_DbCount(interp, objc, objv, dbp)
  2054. Tcl_Interp *interp; /* Interpreter */
  2055. int objc; /* How many arguments? */
  2056. Tcl_Obj *CONST objv[]; /* The argument objects */
  2057. DB *dbp; /* Database pointer */
  2058. {
  2059. Tcl_Obj *res;
  2060. DBC *dbc;
  2061. DBT key, data;
  2062. void *ktmp;
  2063. db_recno_t count, recno;
  2064. int freekey, result, ret;
  2065. result = TCL_OK;
  2066. count = 0;
  2067. freekey = 0;
  2068. res = NULL;
  2069. if (objc != 3) {
  2070. Tcl_WrongNumArgs(interp, 2, objv, "key");
  2071. return (TCL_ERROR);
  2072. }
  2073. memset(&key, 0, sizeof(key));
  2074. /*
  2075.  * Get the count for our key.
  2076.  * We do this by getting a cursor for this DB.  Moving the cursor
  2077.  * to the set location, and getting a count on that cursor.
  2078.  */
  2079. ret = 0;
  2080. memset(&key, 0, sizeof(key));
  2081. memset(&data, 0, sizeof(data));
  2082. /*
  2083.  * If it's a queue or recno database, we must make sure to
  2084.  * treat the key as a recno rather than as a byte string.
  2085.  */
  2086. if (dbp->type == DB_RECNO || dbp->type == DB_QUEUE) {
  2087. result = _GetUInt32(interp, objv[2], &recno);
  2088. if (result == TCL_OK) {
  2089. key.data = &recno;
  2090. key.size = sizeof(db_recno_t);
  2091. } else
  2092. return (result);
  2093. } else {
  2094. ret = _CopyObjBytes(interp, objv[2], &ktmp,
  2095.     &key.size, &freekey);
  2096. if (ret != 0) {
  2097. result = _ReturnSetup(interp, ret,
  2098.     DB_RETOK_STD(ret), "db count");
  2099. return (result);
  2100. }
  2101. key.data = ktmp;
  2102. }
  2103. _debug_check();
  2104. ret = dbp->cursor(dbp, NULL, &dbc, 0);
  2105. if (ret != 0) {
  2106. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  2107.     "db cursor");
  2108. goto out;
  2109. }
  2110. /*
  2111.  * Move our cursor to the key.
  2112.  */
  2113. ret = dbc->c_get(dbc, &key, &data, DB_SET);
  2114. if (ret == DB_NOTFOUND)
  2115. count = 0;
  2116. else {
  2117. ret = dbc->c_count(dbc, &count, 0);
  2118. if (ret != 0) {
  2119. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  2120.     "db c count");
  2121. goto out;
  2122. }
  2123. }
  2124. res = Tcl_NewLongObj((long)count);
  2125. Tcl_SetObjResult(interp, res);
  2126. out:
  2127. if (freekey)
  2128. (void)__os_free(dbp->dbenv, ktmp);
  2129. (void)dbc->c_close(dbc);
  2130. return (result);
  2131. }
  2132. #if CONFIG_TEST
  2133. /*
  2134.  * tcl_DbKeyRange --
  2135.  */
  2136. static int
  2137. tcl_DbKeyRange(interp, objc, objv, dbp)
  2138. Tcl_Interp *interp; /* Interpreter */
  2139. int objc; /* How many arguments? */
  2140. Tcl_Obj *CONST objv[]; /* The argument objects */
  2141. DB *dbp; /* Database pointer */
  2142. {
  2143. static char *dbkeyropts[] = {
  2144. "-txn",
  2145. NULL
  2146. };
  2147. enum dbkeyropts {
  2148. DBKEYR_TXN
  2149. };
  2150. DB_TXN *txn;
  2151. DB_KEY_RANGE range;
  2152. DBT key;
  2153. DBTYPE type;
  2154. Tcl_Obj *myobjv[3], *retlist;
  2155. void *ktmp;
  2156. db_recno_t recno;
  2157. u_int32_t flag;
  2158. int freekey, i, myobjc, optindex, result, ret;
  2159. char *arg, msg[MSG_SIZE];
  2160. result = TCL_OK;
  2161. flag = 0;
  2162. freekey = 0;
  2163. if (objc < 3) {
  2164. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id? key");
  2165. return (TCL_ERROR);
  2166. }
  2167. txn = NULL;
  2168. i = 2;
  2169. while (i < objc) {
  2170. if (Tcl_GetIndexFromObj(interp, objv[i], dbkeyropts, "option",
  2171.     TCL_EXACT, &optindex) != TCL_OK) {
  2172. result = IS_HELP(objv[i]);
  2173. if (result == TCL_OK)
  2174. return (result);
  2175. result = TCL_OK;
  2176. Tcl_ResetResult(interp);
  2177. break;
  2178. }
  2179. i++;
  2180. switch ((enum dbkeyropts)optindex) {
  2181. case DBKEYR_TXN:
  2182. if (i == objc) {
  2183. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  2184. result = TCL_ERROR;
  2185. break;
  2186. }
  2187. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  2188. txn = NAME_TO_TXN(arg);
  2189. if (txn == NULL) {
  2190. snprintf(msg, MSG_SIZE,
  2191.     "KeyRange: Invalid txn: %sn", arg);
  2192. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  2193. result = TCL_ERROR;
  2194. }
  2195. break;
  2196. }
  2197. }
  2198. if (result != TCL_OK)
  2199. return (result);
  2200. (void)dbp->get_type(dbp, &type);
  2201. ret = 0;
  2202. /*
  2203.  * Make sure we have a key.
  2204.  */
  2205. if (i != (objc - 1)) {
  2206. Tcl_WrongNumArgs(interp, 2, objv, "?args? key");
  2207. result = TCL_ERROR;
  2208. goto out;
  2209. }
  2210. memset(&key, 0, sizeof(key));
  2211. if (type == DB_RECNO || type == DB_QUEUE) {
  2212. result = _GetUInt32(interp, objv[i], &recno);
  2213. if (result == TCL_OK) {
  2214. key.data = &recno;
  2215. key.size = sizeof(db_recno_t);
  2216. } else
  2217. return (result);
  2218. } else {
  2219. ret = _CopyObjBytes(interp, objv[i++], &ktmp,
  2220.     &key.size, &freekey);
  2221. if (ret != 0) {
  2222. result = _ReturnSetup(interp, ret,
  2223.     DB_RETOK_STD(ret), "db keyrange");
  2224. return (result);
  2225. }
  2226. key.data = ktmp;
  2227. }
  2228. _debug_check();
  2229. ret = dbp->key_range(dbp, txn, &key, &range, flag);
  2230. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db keyrange");
  2231. if (result == TCL_ERROR)
  2232. goto out;
  2233. /*
  2234.  * If we succeeded, set up return list.
  2235.  */
  2236. myobjc = 3;
  2237. myobjv[0] = Tcl_NewDoubleObj(range.less);
  2238. myobjv[1] = Tcl_NewDoubleObj(range.equal);
  2239. myobjv[2] = Tcl_NewDoubleObj(range.greater);
  2240. retlist = Tcl_NewListObj(myobjc, myobjv);
  2241. if (result == TCL_OK)
  2242. Tcl_SetObjResult(interp, retlist);
  2243. out:
  2244. if (freekey)
  2245. (void)__os_free(dbp->dbenv, ktmp);
  2246. return (result);
  2247. }
  2248. #endif
  2249. /*
  2250.  * tcl_DbTruncate --
  2251.  */
  2252. static int
  2253. tcl_DbTruncate(interp, objc, objv, dbp)
  2254. Tcl_Interp *interp; /* Interpreter */
  2255. int objc; /* How many arguments? */
  2256. Tcl_Obj *CONST objv[]; /* The argument objects */
  2257. DB *dbp; /* Database pointer */
  2258. {
  2259. static char *dbcuropts[] = {
  2260. "-auto_commit",
  2261. "-txn",
  2262. NULL
  2263. };
  2264. enum dbcuropts {
  2265. DBTRUNC_AUTO_COMMIT,
  2266. DBTRUNC_TXN
  2267. };
  2268. DB_TXN *txn;
  2269. Tcl_Obj *res;
  2270. u_int32_t count, flag;
  2271. int i, optindex, result, ret;
  2272. char *arg, msg[MSG_SIZE];
  2273. txn = NULL;
  2274. flag = 0;
  2275. result = TCL_OK;
  2276. i = 2;
  2277. while (i < objc) {
  2278. if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
  2279.     TCL_EXACT, &optindex) != TCL_OK) {
  2280. result = IS_HELP(objv[i]);
  2281. goto out;
  2282. }
  2283. i++;
  2284. switch ((enum dbcuropts)optindex) {
  2285. case DBTRUNC_AUTO_COMMIT:
  2286. flag |= DB_AUTO_COMMIT;
  2287. break;
  2288. case DBTRUNC_TXN:
  2289. if (i == objc) {
  2290. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  2291. result = TCL_ERROR;
  2292. break;
  2293. }
  2294. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  2295. txn = NAME_TO_TXN(arg);
  2296. if (txn == NULL) {
  2297. snprintf(msg, MSG_SIZE,
  2298.     "Truncate: Invalid txn: %sn", arg);
  2299. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  2300. result = TCL_ERROR;
  2301. }
  2302. break;
  2303. }
  2304. if (result != TCL_OK)
  2305. break;
  2306. }
  2307. if (result != TCL_OK)
  2308. goto out;
  2309. _debug_check();
  2310. ret = dbp->truncate(dbp, txn, &count, flag);
  2311. if (ret != 0)
  2312. result = _ErrorSetup(interp, ret, "db truncate");
  2313. else {
  2314. res = Tcl_NewLongObj((long)count);
  2315. Tcl_SetObjResult(interp, res);
  2316. }
  2317. out:
  2318. return (result);
  2319. }