tcl_db.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:42k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1999, 2000
  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.55 2000/11/28 20:12: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 "tcl_db.h"
  19. /*
  20.  * Prototypes for procedures defined later in this file:
  21.  */
  22. static int tcl_DbClose __P((Tcl_Interp *,
  23.     int, Tcl_Obj * CONST*, DB *, DBTCL_INFO *));
  24. static int tcl_DbDelete __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  25. static int tcl_DbGet __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  26. static int tcl_DbKeyRange __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  27. static int tcl_DbPut __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  28. static int tcl_DbStat __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  29. static int tcl_DbCursor __P((Tcl_Interp *,
  30.     int, Tcl_Obj * CONST*, DB *, DBC **));
  31. static int tcl_DbJoin __P((Tcl_Interp *,
  32.     int, Tcl_Obj * CONST*, DB *, DBC **));
  33. static int tcl_DbGetjoin __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  34. static int tcl_DbCount __P((Tcl_Interp *, int, Tcl_Obj * CONST*, DB *));
  35. /*
  36.  *
  37.  * PUBLIC: int db_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*));
  38.  *
  39.  * db_Cmd --
  40.  * Implements the "db" widget.
  41.  */
  42. int
  43. db_Cmd(clientData, interp, objc, objv)
  44. ClientData clientData; /* DB handle */
  45. Tcl_Interp *interp; /* Interpreter */
  46. int objc; /* How many arguments? */
  47. Tcl_Obj *CONST objv[]; /* The argument objects */
  48. {
  49. static char *dbcmds[] = {
  50. "close",
  51. "count",
  52. "cursor",
  53. "del",
  54. "get",
  55. "get_join",
  56. "get_type",
  57. "is_byteswapped",
  58. "join",
  59. "keyrange",
  60. "put",
  61. "stat",
  62. "sync",
  63. #if CONFIG_TEST
  64. "test",
  65. #endif
  66. NULL
  67. };
  68. enum dbcmds {
  69. DBCLOSE,
  70. DBCOUNT,
  71. DBCURSOR,
  72. DBDELETE,
  73. DBGET,
  74. DBGETJOIN,
  75. DBGETTYPE,
  76. DBSWAPPED,
  77. DBJOIN,
  78. DBKEYRANGE,
  79. DBPUT,
  80. DBSTAT,
  81. DBSYNC
  82. #if CONFIG_TEST
  83. , DBTEST
  84. #endif
  85. };
  86. DB *dbp;
  87. DBC *dbc;
  88. DBTCL_INFO *dbip;
  89. DBTCL_INFO *ip;
  90. Tcl_Obj *res;
  91. int cmdindex, result, ret;
  92. char newname[MSG_SIZE];
  93. Tcl_ResetResult(interp);
  94. dbp = (DB *)clientData;
  95. dbip = _PtrToInfo((void *)dbp);
  96. memset(newname, 0, MSG_SIZE);
  97. result = TCL_OK;
  98. if (objc <= 1) {
  99. Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs");
  100. return (TCL_ERROR);
  101. }
  102. if (dbp == NULL) {
  103. Tcl_SetResult(interp, "NULL db pointer", TCL_STATIC);
  104. return (TCL_ERROR);
  105. }
  106. if (dbip == NULL) {
  107. Tcl_SetResult(interp, "NULL db info pointer", TCL_STATIC);
  108. return (TCL_ERROR);
  109. }
  110. /*
  111.  * Get the command name index from the object based on the dbcmds
  112.  * defined above.
  113.  */
  114. if (Tcl_GetIndexFromObj(interp,
  115.     objv[1], dbcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
  116. return (IS_HELP(objv[1]));
  117. res = NULL;
  118. switch ((enum dbcmds)cmdindex) {
  119. case DBCLOSE:
  120. result = tcl_DbClose(interp, objc, objv, dbp, dbip);
  121. break;
  122. case DBDELETE:
  123. result = tcl_DbDelete(interp, objc, objv, dbp);
  124. break;
  125. case DBGET:
  126. result = tcl_DbGet(interp, objc, objv, dbp);
  127. break;
  128. case DBKEYRANGE:
  129. result = tcl_DbKeyRange(interp, objc, objv, dbp);
  130. break;
  131. case DBPUT:
  132. result = tcl_DbPut(interp, objc, objv, dbp);
  133. break;
  134. case DBCOUNT:
  135. result = tcl_DbCount(interp, objc, objv, dbp);
  136. break;
  137. case DBSWAPPED:
  138. /*
  139.  * No args for this.  Error if there are some.
  140.  */
  141. if (objc > 2) {
  142. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  143. return (TCL_ERROR);
  144. }
  145. _debug_check();
  146. ret = dbp->get_byteswapped(dbp);
  147. res = Tcl_NewIntObj(ret);
  148. break;
  149. case DBGETTYPE:
  150. /*
  151.  * No args for this.  Error if there are some.
  152.  */
  153. if (objc > 2) {
  154. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  155. return (TCL_ERROR);
  156. }
  157. _debug_check();
  158. ret = dbp->get_type(dbp);
  159. if (ret == DB_BTREE)
  160. res = Tcl_NewStringObj("btree", strlen("btree"));
  161. else if (ret == DB_HASH)
  162. res = Tcl_NewStringObj("hash", strlen("hash"));
  163. else if (ret == DB_RECNO)
  164. res = Tcl_NewStringObj("recno", strlen("recno"));
  165. else if (ret == DB_QUEUE)
  166. res = Tcl_NewStringObj("queue", strlen("queue"));
  167. else {
  168. Tcl_SetResult(interp,
  169.     "db gettype: Returned unknown typen", TCL_STATIC);
  170. result = TCL_ERROR;
  171. }
  172. break;
  173. case DBSTAT:
  174. result = tcl_DbStat(interp, objc, objv, dbp);
  175. break;
  176. case DBSYNC:
  177. /*
  178.  * No args for this.  Error if there are some.
  179.  */
  180. if (objc > 2) {
  181. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  182. return (TCL_ERROR);
  183. }
  184. _debug_check();
  185. ret = dbp->sync(dbp, 0);
  186. res = Tcl_NewIntObj(ret);
  187. if (ret != 0) {
  188. Tcl_SetObjResult(interp, res);
  189. result = TCL_ERROR;
  190. }
  191. break;
  192. case DBCURSOR:
  193. snprintf(newname, sizeof(newname),
  194.     "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
  195. ip = _NewInfo(interp, NULL, newname, I_DBC);
  196. if (ip != NULL) {
  197. result = tcl_DbCursor(interp, objc, objv, dbp, &dbc);
  198. if (result == TCL_OK) {
  199. dbip->i_dbdbcid++;
  200. ip->i_parent = dbip;
  201. Tcl_CreateObjCommand(interp, newname,
  202.     (Tcl_ObjCmdProc *)dbc_Cmd,
  203.     (ClientData)dbc, NULL);
  204. res =
  205.     Tcl_NewStringObj(newname, strlen(newname));
  206. _SetInfoData(ip, dbc);
  207. } else
  208. _DeleteInfo(ip);
  209. } else {
  210. Tcl_SetResult(interp,
  211.     "Could not set up info", TCL_STATIC);
  212. result = TCL_ERROR;
  213. }
  214. break;
  215. case DBJOIN:
  216. snprintf(newname, sizeof(newname),
  217.     "%s.c%d", dbip->i_name, dbip->i_dbdbcid);
  218. ip = _NewInfo(interp, NULL, newname, I_DBC);
  219. if (ip != NULL) {
  220. result = tcl_DbJoin(interp, objc, objv, dbp, &dbc);
  221. if (result == TCL_OK) {
  222. dbip->i_dbdbcid++;
  223. ip->i_parent = dbip;
  224. Tcl_CreateObjCommand(interp, newname,
  225.     (Tcl_ObjCmdProc *)dbc_Cmd,
  226.     (ClientData)dbc, NULL);
  227. res =
  228.     Tcl_NewStringObj(newname, strlen(newname));
  229. _SetInfoData(ip, dbc);
  230. } else
  231. _DeleteInfo(ip);
  232. } else {
  233. Tcl_SetResult(interp,
  234.     "Could not set up info", TCL_STATIC);
  235. result = TCL_ERROR;
  236. }
  237. break;
  238. case DBGETJOIN:
  239. result = tcl_DbGetjoin(interp, objc, objv, dbp);
  240. break;
  241. #if CONFIG_TEST
  242. case DBTEST:
  243. result = tcl_EnvTest(interp, objc, objv, dbp->dbenv);
  244. break;
  245. #endif
  246. }
  247. /*
  248.  * Only set result if we have a res.  Otherwise, lower
  249.  * functions have already done so.
  250.  */
  251. if (result == TCL_OK && res)
  252. Tcl_SetObjResult(interp, res);
  253. return (result);
  254. }
  255. /*
  256.  * tcl_db_stat --
  257.  */
  258. static int
  259. tcl_DbStat(interp, objc, objv, dbp)
  260. Tcl_Interp *interp; /* Interpreter */
  261. int objc; /* How many arguments? */
  262. Tcl_Obj *CONST objv[]; /* The argument objects */
  263. DB *dbp; /* Database pointer */
  264. {
  265. DB_BTREE_STAT *bsp;
  266. DB_HASH_STAT *hsp;
  267. DB_QUEUE_STAT *qsp;
  268. void *sp;
  269. Tcl_Obj *res;
  270. DBTYPE type;
  271. u_int32_t flag;
  272. int result, ret;
  273. char *arg;
  274. result = TCL_OK;
  275. flag = 0;
  276. if (objc > 3) {
  277. Tcl_WrongNumArgs(interp, 2, objv, "?-recordcount?");
  278. return (TCL_ERROR);
  279. }
  280. if (objc == 3) {
  281. arg = Tcl_GetStringFromObj(objv[2], NULL);
  282. if (strcmp(arg, "-recordcount") == 0)
  283. flag = DB_RECORDCOUNT;
  284. else if (strcmp(arg, "-cachedcounts") == 0)
  285. flag = DB_CACHED_COUNTS;
  286. else {
  287. Tcl_SetResult(interp,
  288.     "db stat: unknown arg", TCL_STATIC);
  289. return (TCL_ERROR);
  290. }
  291. }
  292. _debug_check();
  293. ret = dbp->stat(dbp, &sp, NULL, flag);
  294. result = _ReturnSetup(interp, ret, "db stat");
  295. if (result == TCL_ERROR)
  296. return (result);
  297. type = dbp->get_type(dbp);
  298. /*
  299.  * Have our stats, now construct the name value
  300.  * list pairs and free up the memory.
  301.  */
  302. res = Tcl_NewObj();
  303. /*
  304.  * MAKE_STAT_LIST assumes 'res' and 'error' label.
  305.  */
  306. if (type == DB_HASH) {
  307. hsp = (DB_HASH_STAT *)sp;
  308. MAKE_STAT_LIST("Magic", hsp->hash_magic);
  309. MAKE_STAT_LIST("Version", hsp->hash_version);
  310. MAKE_STAT_LIST("Page size", hsp->hash_pagesize);
  311. MAKE_STAT_LIST("Number of keys", hsp->hash_nkeys);
  312. MAKE_STAT_LIST("Number of records", hsp->hash_ndata);
  313. MAKE_STAT_LIST("Estim. number of elements", hsp->hash_nelem);
  314. MAKE_STAT_LIST("Fill factor", hsp->hash_ffactor);
  315. MAKE_STAT_LIST("Buckets", hsp->hash_buckets);
  316. MAKE_STAT_LIST("Free pages", hsp->hash_free);
  317. MAKE_STAT_LIST("Bytes free", hsp->hash_bfree);
  318. MAKE_STAT_LIST("Number of big pages", hsp->hash_bigpages);
  319. MAKE_STAT_LIST("Big pages bytes free", hsp->hash_big_bfree);
  320. MAKE_STAT_LIST("Overflow pages", hsp->hash_overflows);
  321. MAKE_STAT_LIST("Overflow bytes free", hsp->hash_ovfl_free);
  322. MAKE_STAT_LIST("Duplicate pages", hsp->hash_dup);
  323. MAKE_STAT_LIST("Duplicate pages bytes free",
  324.     hsp->hash_dup_free);
  325. } else if (type == DB_QUEUE) {
  326. qsp = (DB_QUEUE_STAT *)sp;
  327. MAKE_STAT_LIST("Magic", qsp->qs_magic);
  328. MAKE_STAT_LIST("Version", qsp->qs_version);
  329. MAKE_STAT_LIST("Page size", qsp->qs_pagesize);
  330. MAKE_STAT_LIST("Number of records", qsp->qs_ndata);
  331. MAKE_STAT_LIST("Number of pages", qsp->qs_pages);
  332. MAKE_STAT_LIST("Bytes free", qsp->qs_pgfree);
  333. MAKE_STAT_LIST("Record length", qsp->qs_re_len);
  334. MAKE_STAT_LIST("Record pad", qsp->qs_re_pad);
  335. MAKE_STAT_LIST("First record number", qsp->qs_first_recno);
  336. MAKE_STAT_LIST("Last record number", qsp->qs_cur_recno);
  337. } else { /* BTREE and RECNO are same stats */
  338. bsp = (DB_BTREE_STAT *)sp;
  339. MAKE_STAT_LIST("Number of keys", bsp->bt_nkeys);
  340. MAKE_STAT_LIST("Number of records", bsp->bt_ndata);
  341. if (flag != DB_RECORDCOUNT) {
  342. MAKE_STAT_LIST("Magic", bsp->bt_magic);
  343. MAKE_STAT_LIST("Version", bsp->bt_version);
  344. MAKE_STAT_LIST("Flags", bsp->bt_metaflags);
  345. MAKE_STAT_LIST("Minimum keys per page", bsp->bt_minkey);
  346. MAKE_STAT_LIST("Fixed record length", bsp->bt_re_len);
  347. MAKE_STAT_LIST("Record pad", bsp->bt_re_pad);
  348. MAKE_STAT_LIST("Page size", bsp->bt_pagesize);
  349. MAKE_STAT_LIST("Levels", bsp->bt_levels);
  350. MAKE_STAT_LIST("Internal pages", bsp->bt_int_pg);
  351. MAKE_STAT_LIST("Leaf pages", bsp->bt_leaf_pg);
  352. MAKE_STAT_LIST("Duplicate pages", bsp->bt_dup_pg);
  353. MAKE_STAT_LIST("Overflow pages", bsp->bt_over_pg);
  354. MAKE_STAT_LIST("Pages on freelist", bsp->bt_free);
  355. MAKE_STAT_LIST("Internal pages bytes free",
  356.     bsp->bt_int_pgfree);
  357. MAKE_STAT_LIST("Leaf pages bytes free",
  358.     bsp->bt_leaf_pgfree);
  359. MAKE_STAT_LIST("Duplicate pages bytes free",
  360.     bsp->bt_dup_pgfree);
  361. MAKE_STAT_LIST("Bytes free in overflow pages",
  362.     bsp->bt_over_pgfree);
  363. }
  364. }
  365. Tcl_SetObjResult(interp, res);
  366. error:
  367. __os_free(sp, 0);
  368. return (result);
  369. }
  370. /*
  371.  * tcl_db_close --
  372.  */
  373. static int
  374. tcl_DbClose(interp, objc, objv, dbp, dbip)
  375. Tcl_Interp *interp; /* Interpreter */
  376. int objc; /* How many arguments? */
  377. Tcl_Obj *CONST objv[]; /* The argument objects */
  378. DB *dbp; /* Database pointer */
  379. DBTCL_INFO *dbip; /* Info pointer */
  380. {
  381. DBTCL_INFO *p, *nextp;
  382. u_int32_t flag;
  383. int result, ret;
  384. char *arg;
  385. result = TCL_OK;
  386. flag = 0;
  387. if (objc > 3) {
  388. Tcl_WrongNumArgs(interp, 2, objv, "?-nosync?");
  389. return (TCL_ERROR);
  390. }
  391. if (objc == 3) {
  392. arg = Tcl_GetStringFromObj(objv[2], NULL);
  393. if (strcmp(arg, "-nosync") == 0)
  394. flag = DB_NOSYNC;
  395. else {
  396. Tcl_SetResult(interp,
  397.     "dbclose: unknown arg", TCL_STATIC);
  398. return (TCL_ERROR);
  399. }
  400. }
  401. /*
  402.  * First we have to close any open cursors.  Then we close
  403.  * our db.
  404.  */
  405. for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
  406. nextp = LIST_NEXT(p, entries);
  407. /*
  408.  * Check if this is a cursor info structure and if
  409.  * it is, if it belongs to this DB.  If so, remove
  410.  * its commands and info structure.
  411.  */
  412. if (p->i_parent == dbip && p->i_type == I_DBC) {
  413. (void)Tcl_DeleteCommand(interp, p->i_name);
  414. _DeleteInfo(p);
  415. }
  416. }
  417. (void)Tcl_DeleteCommand(interp, dbip->i_name);
  418. _DeleteInfo(dbip);
  419. _debug_check();
  420. ret = (dbp)->close(dbp, flag);
  421. result = _ReturnSetup(interp, ret, "db close");
  422. return (result);
  423. }
  424. /*
  425.  * tcl_db_put --
  426.  */
  427. static int
  428. tcl_DbPut(interp, objc, objv, dbp)
  429. Tcl_Interp *interp; /* Interpreter */
  430. int objc; /* How many arguments? */
  431. Tcl_Obj *CONST objv[]; /* The argument objects */
  432. DB *dbp; /* Database pointer */
  433. {
  434. static char *dbputopts[] = {
  435. "-append",
  436. "-nodupdata",
  437. "-nooverwrite",
  438. "-partial",
  439. "-txn",
  440. NULL
  441. };
  442. enum dbputopts {
  443. DBPUT_APPEND,
  444. DBGET_NODUPDATA,
  445. DBPUT_NOOVER,
  446. DBPUT_PART,
  447. DBPUT_TXN
  448. };
  449. static char *dbputapp[] = {
  450. "-append", NULL
  451. };
  452. enum dbputapp { DBPUT_APPEND0 };
  453. DBT key, data;
  454. DBTYPE type;
  455. DB_TXN *txn;
  456. Tcl_Obj **elemv, *res;
  457. db_recno_t recno;
  458. u_int32_t flag;
  459. int elemc, end, i, itmp, optindex, result, ret;
  460. char *arg, msg[MSG_SIZE];
  461. txn = NULL;
  462. result = TCL_OK;
  463. flag = 0;
  464. if (objc <= 3) {
  465. Tcl_WrongNumArgs(interp, 2, objv, "?-args? key data");
  466. return (TCL_ERROR);
  467. }
  468. memset(&key, 0, sizeof(key));
  469. memset(&data, 0, sizeof(data));
  470. /*
  471.  * If it is a QUEUE or RECNO database, the key is a record number
  472.  * and must be setup up to contain a db_recno_t.  Otherwise the
  473.  * key is a "string".
  474.  */
  475. type = dbp->get_type(dbp);
  476. /*
  477.  * We need to determine where the end of required args are.  If we
  478.  * are using a QUEUE/RECNO db and -append, then there is just one
  479.  * req arg (data).  Otherwise there are two (key data).
  480.  *
  481.  * We preparse the list to determine this since we need to know
  482.  * to properly check # of args for other options below.
  483.  */
  484. end = objc - 2;
  485. if (type == DB_QUEUE || type == DB_RECNO) {
  486. i = 2;
  487. while (i < objc - 1) {
  488. if (Tcl_GetIndexFromObj(interp, objv[i++], dbputapp,
  489.     "option", TCL_EXACT, &optindex) != TCL_OK)
  490. continue;
  491. switch ((enum dbputapp)optindex) {
  492. case DBPUT_APPEND0:
  493. end = objc - 1;
  494. break;
  495. }
  496. }
  497. }
  498. Tcl_ResetResult(interp);
  499. /*
  500.  * Get the command name index from the object based on the options
  501.  * defined above.
  502.  */
  503. i = 2;
  504. while (i < end) {
  505. if (Tcl_GetIndexFromObj(interp, objv[i],
  506.     dbputopts, "option", TCL_EXACT, &optindex) != TCL_OK)
  507. return (IS_HELP(objv[i]));
  508. i++;
  509. switch ((enum dbputopts)optindex) {
  510. case DBPUT_TXN:
  511. if (i > (end - 1)) {
  512. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  513. result = TCL_ERROR;
  514. break;
  515. }
  516. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  517. txn = NAME_TO_TXN(arg);
  518. if (txn == NULL) {
  519. snprintf(msg, MSG_SIZE,
  520.     "Put: Invalid txn: %sn", arg);
  521. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  522. result = TCL_ERROR;
  523. }
  524. break;
  525. case DBPUT_APPEND:
  526. FLAG_CHECK(flag);
  527. flag = DB_APPEND;
  528. break;
  529. case DBGET_NODUPDATA:
  530. FLAG_CHECK(flag);
  531. flag = DB_NODUPDATA;
  532. break;
  533. case DBPUT_NOOVER:
  534. FLAG_CHECK(flag);
  535. flag = DB_NOOVERWRITE;
  536. break;
  537. case DBPUT_PART:
  538. if (i > (end - 1)) {
  539. Tcl_WrongNumArgs(interp, 2, objv,
  540.     "?-partial {offset length}?");
  541. result = TCL_ERROR;
  542. break;
  543. }
  544. /*
  545.  * Get sublist as {offset length}
  546.  */
  547. result = Tcl_ListObjGetElements(interp, objv[i++],
  548.     &elemc, &elemv);
  549. if (elemc != 2) {
  550. Tcl_SetResult(interp,
  551.     "List must be {offset length}", TCL_STATIC);
  552. result = TCL_ERROR;
  553. break;
  554. }
  555. data.flags = DB_DBT_PARTIAL;
  556. result = Tcl_GetIntFromObj(interp, elemv[0], &itmp);
  557. data.doff = itmp;
  558. if (result != TCL_OK)
  559. break;
  560. result = Tcl_GetIntFromObj(interp, elemv[1], &itmp);
  561. data.dlen = itmp;
  562. /*
  563.  * NOTE: We don't check result here because all we'd
  564.  * do is break anyway, and we are doing that.  If you
  565.  * add code here, you WILL need to add the check
  566.  * for result.  (See the check for save.doff, a few
  567.  * lines above and copy that.)
  568.  */
  569. break;
  570. }
  571. if (result != TCL_OK)
  572. break;
  573. }
  574. if (result == TCL_ERROR)
  575. return (result);
  576. /*
  577.  * If we are a recno db and we are NOT using append, then the 2nd
  578.  * last arg is the key.
  579.  */
  580. if (type == DB_QUEUE || type == DB_RECNO) {
  581. key.data = &recno;
  582. key.ulen = key.size = sizeof(db_recno_t);
  583. key.flags = DB_DBT_USERMEM;
  584. if (flag == DB_APPEND)
  585. recno = 0;
  586. else {
  587. result = Tcl_GetIntFromObj(interp, objv[objc-2], &itmp);
  588. recno = itmp;
  589. if (result != TCL_OK)
  590. return (result);
  591. }
  592. } else {
  593. key.data = Tcl_GetByteArrayFromObj(objv[objc-2], &itmp);
  594. key.size = itmp;
  595. }
  596. /*
  597.  * XXX
  598.  * Tcl 8.1 Tcl_GetByteArrayFromObj/Tcl_GetIntFromObj bug.
  599.  *
  600.  * This line (and the line for key.data above) were moved from
  601.  * the beginning of the function to here.
  602.  *
  603.  * There is a bug in Tcl 8.1 and byte arrays in that if it happens
  604.  * to use an object as both a byte array and something else like
  605.  * an int, and you've done a Tcl_GetByteArrayFromObj, then you
  606.  * do a Tcl_GetIntFromObj, your memory is deleted.
  607.  *
  608.  * Workaround is to make sure all Tcl_GetByteArrayFromObj calls
  609.  * are done last.
  610.  */
  611. data.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
  612. data.size = itmp;
  613. _debug_check();
  614. ret = dbp->put(dbp, txn, &key, &data, flag);
  615. result = _ReturnSetup(interp, ret, "db put");
  616. if (ret == 0 &&
  617.     (type == DB_RECNO || type == DB_QUEUE) && flag == DB_APPEND) {
  618. res = Tcl_NewIntObj(recno);
  619. Tcl_SetObjResult(interp, res);
  620. }
  621. return (result);
  622. }
  623. /*
  624.  * tcl_db_get --
  625.  */
  626. static int
  627. tcl_DbGet(interp, objc, objv, dbp)
  628. Tcl_Interp *interp; /* Interpreter */
  629. int objc; /* How many arguments? */
  630. Tcl_Obj *CONST objv[]; /* The argument objects */
  631. DB *dbp; /* Database pointer */
  632. {
  633. static char *dbgetopts[] = {
  634. "-consume",
  635. "-consume_wait",
  636. "-get_both",
  637. "-glob",
  638. "-partial",
  639. "-recno",
  640. "-rmw",
  641. "-txn",
  642. NULL
  643. };
  644. enum dbgetopts {
  645. DBGET_CONSUME,
  646. DBGET_CONSUME_WAIT,
  647. DBGET_BOTH,
  648. DBGET_GLOB,
  649. DBGET_PART,
  650. DBGET_RECNO,
  651. DBGET_RMW,
  652. DBGET_TXN
  653. };
  654. DBC *dbc;
  655. DBT key, data, save;
  656. DBTYPE type;
  657. DB_TXN *txn;
  658. Tcl_Obj **elemv, *retlist;
  659. db_recno_t recno;
  660. u_int32_t flag, cflag, isdup, rmw;
  661. int elemc, end, i, itmp, optindex, result, ret, useglob, userecno;
  662. char *arg, *pattern, *prefix, msg[MSG_SIZE];
  663. result = TCL_OK;
  664. cflag = flag = rmw = 0;
  665. useglob = userecno = 0;
  666. txn = NULL;
  667. pattern = prefix = NULL;
  668. if (objc < 3) {
  669. Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
  670. return (TCL_ERROR);
  671. }
  672. memset(&key, 0, sizeof(key));
  673. memset(&data, 0, sizeof(data));
  674. memset(&save, 0, sizeof(save));
  675. /*
  676.  * Get the command name index from the object based on the options
  677.  * defined above.
  678.  */
  679. i = 2;
  680. type = dbp->get_type(dbp);
  681. end = objc;
  682. while (i < end) {
  683. if (Tcl_GetIndexFromObj(interp, objv[i], dbgetopts, "option",
  684.     TCL_EXACT, &optindex) != TCL_OK) {
  685. if (IS_HELP(objv[i]) == TCL_OK)
  686. return (TCL_OK);
  687. Tcl_ResetResult(interp);
  688. break;
  689. }
  690. i++;
  691. switch ((enum dbgetopts)optindex) {
  692. case DBGET_BOTH:
  693. /*
  694.  * Change 'end' and make sure we aren't already past
  695.  * the new end.
  696.  */
  697. if (i > objc - 2) {
  698. Tcl_WrongNumArgs(interp, 2, objv,
  699.     "?-get_both key data?");
  700. result = TCL_ERROR;
  701. break;
  702. }
  703. end = objc - 2;
  704. FLAG_CHECK(flag);
  705. flag = DB_GET_BOTH;
  706. break;
  707. case DBGET_TXN:
  708. if (i == end - 1) {
  709. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  710. result = TCL_ERROR;
  711. break;
  712. }
  713. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  714. txn = NAME_TO_TXN(arg);
  715. if (txn == NULL) {
  716. snprintf(msg, MSG_SIZE,
  717.     "Get: Invalid txn: %sn", arg);
  718. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  719. result = TCL_ERROR;
  720. }
  721. break;
  722. case DBGET_GLOB:
  723. useglob = 1;
  724. end = objc - 1;
  725. break;
  726. case DBGET_CONSUME:
  727. FLAG_CHECK(flag);
  728. flag = DB_CONSUME;
  729. break;
  730. case DBGET_CONSUME_WAIT:
  731. FLAG_CHECK(flag);
  732. flag = DB_CONSUME_WAIT;
  733. break;
  734. case DBGET_RECNO:
  735. end = objc - 1;
  736. userecno = 1;
  737. if (type != DB_RECNO && type != DB_QUEUE) {
  738. FLAG_CHECK(flag);
  739. flag = DB_SET_RECNO;
  740. }
  741. break;
  742. case DBGET_RMW:
  743. rmw = DB_RMW;
  744. break;
  745. case DBGET_PART:
  746. end = objc - 1;
  747. if (i == end) {
  748. Tcl_WrongNumArgs(interp, 2, objv,
  749.     "?-partial {offset length}?");
  750. result = TCL_ERROR;
  751. break;
  752. }
  753. /*
  754.  * Get sublist as {offset length}
  755.  */
  756. result = Tcl_ListObjGetElements(interp, objv[i++],
  757.     &elemc, &elemv);
  758. if (elemc != 2) {
  759. Tcl_SetResult(interp,
  760.     "List must be {offset length}", TCL_STATIC);
  761. result = TCL_ERROR;
  762. break;
  763. }
  764. save.flags = DB_DBT_PARTIAL;
  765. result = Tcl_GetIntFromObj(interp, elemv[0], &itmp);
  766. save.doff = itmp;
  767. if (result != TCL_OK)
  768. break;
  769. result = Tcl_GetIntFromObj(interp, elemv[1], &itmp);
  770. save.dlen = itmp;
  771. /*
  772.  * NOTE: We don't check result here because all we'd
  773.  * do is break anyway, and we are doing that.  If you
  774.  * add code here, you WILL need to add the check
  775.  * for result.  (See the check for save.doff, a few
  776.  * lines above and copy that.)
  777.  */
  778. break;
  779. }
  780. if (result != TCL_OK)
  781. break;
  782. }
  783. if (result != TCL_OK)
  784. goto out;
  785. if (type == DB_RECNO || type == DB_QUEUE)
  786. userecno = 1;
  787. /*
  788.  * Check for illegal combos of options.
  789.  */
  790. if (useglob && (userecno || flag == DB_SET_RECNO ||
  791.     type == DB_RECNO || type == DB_QUEUE)) {
  792. Tcl_SetResult(interp,
  793.     "Cannot use -glob and record numbers.n",
  794.     TCL_STATIC);
  795. result = TCL_ERROR;
  796. goto out;
  797. }
  798. if (useglob && flag == DB_GET_BOTH) {
  799. Tcl_SetResult(interp,
  800.     "Only one of -glob or -get_both can be specified.n",
  801.     TCL_STATIC);
  802. result = TCL_ERROR;
  803. goto out;
  804. }
  805. if (useglob)
  806. pattern = Tcl_GetStringFromObj(objv[objc - 1], NULL);
  807. /*
  808.  * This is the list we return
  809.  */
  810. retlist = Tcl_NewListObj(0, NULL);
  811. save.flags |= DB_DBT_MALLOC;
  812. /*
  813.  * isdup is used to know if we support duplicates.  If not, we
  814.  * can just do a db->get call and avoid using cursors.
  815.  * XXX
  816.  * When there is a db->get_flags method, it should be used.
  817.  * isdup = dbp->get_flags(dbp) & DB_DUP;
  818.  * For now we illegally peek.
  819.  * XXX
  820.  */
  821. isdup = dbp->flags & DB_AM_DUP;
  822. /*
  823.  * If the database doesn't support duplicates or we're performing
  824.  * ops that don't require returning multiple items, use DB->get
  825.  * instead of a cursor operation.
  826.  */
  827. if (pattern == NULL && (isdup == 0 ||
  828.     flag == DB_SET_RECNO || flag == DB_GET_BOTH ||
  829.     flag == DB_CONSUME || flag == DB_CONSUME_WAIT)) {
  830. if (flag == DB_GET_BOTH) {
  831. if (userecno) {
  832. result = Tcl_GetIntFromObj(interp,
  833.     objv[(objc - 2)], &itmp);
  834. recno = itmp;
  835. if (result == TCL_OK) {
  836. key.data = &recno;
  837. key.size = sizeof(db_recno_t);
  838. } else
  839. return (result);
  840. } else {
  841. key.data =
  842.     Tcl_GetByteArrayFromObj(objv[objc-2],
  843.     &itmp);
  844. key.size = itmp;
  845. }
  846. /*
  847.  * Already checked args above.  Fill in key and save.
  848.  * Save is used in the dbp->get call below to fill in
  849.  * data.
  850.  */
  851. save.data =
  852.     Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
  853. save.size = itmp;
  854. } else if (flag != DB_CONSUME && flag != DB_CONSUME_WAIT) {
  855. if (userecno) {
  856. result = Tcl_GetIntFromObj(
  857.     interp, objv[(objc - 1)], &itmp);
  858. recno = itmp;
  859. if (result == TCL_OK) {
  860. key.data = &recno;
  861. key.size = sizeof(db_recno_t);
  862. } else
  863. return (result);
  864. } else {
  865. key.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
  866. key.size = itmp;
  867. }
  868. }
  869. memset(&data, 0, sizeof(data));
  870. data = save;
  871. _debug_check();
  872. ret = dbp->get(dbp, txn, &key, &data, flag | rmw);
  873. result = _ReturnSetup(interp, ret, "db get");
  874. if (ret == 0) {
  875. /*
  876.  * Success.  Return a list of the form {name value}
  877.  * If it was a recno in key.data, we need to convert
  878.  * into a string/object representation of that recno.
  879.  */
  880. if (type == DB_RECNO || type == DB_QUEUE)
  881. result = _SetListRecnoElem(interp, retlist,
  882.     *(db_recno_t *)key.data, data.data,
  883.     data.size);
  884. else
  885. result = _SetListElem(interp, retlist,
  886.     key.data, key.size, data.data, data.size);
  887. /*
  888.  * Free space from DB_DBT_MALLOC
  889.  */
  890. __os_free(data.data, data.size);
  891. }
  892. if (result == TCL_OK)
  893. Tcl_SetObjResult(interp, retlist);
  894. goto out;
  895. }
  896. if (userecno) {
  897. result = Tcl_GetIntFromObj(interp, objv[(objc - 1)], &itmp);
  898. recno = itmp;
  899. if (result == TCL_OK) {
  900. key.data = &recno;
  901. key.size = sizeof(db_recno_t);
  902. } else
  903. return (result);
  904. } else {
  905. key.data = Tcl_GetByteArrayFromObj(objv[objc-1], &itmp);
  906. key.size = itmp;
  907. }
  908. ret = dbp->cursor(dbp, txn, &dbc, 0);
  909. result = _ReturnSetup(interp, ret, "db cursor");
  910. if (result == TCL_ERROR)
  911. goto out;
  912. /*
  913.  * At this point, we have a cursor, if we have a pattern,
  914.  * we go to the nearest one and step forward until we don't
  915.  * have any more that match the pattern prefix.  If we have
  916.  * an exact key, we go to that key position, and step through
  917.  * all the duplicates.  In either case we build up a list of
  918.  * the form {{key data} {key data}...} along the way.
  919.  */
  920. memset(&data, 0, sizeof(data));
  921. /*
  922.  * Restore any "partial" info we have saved.
  923.  */
  924. data = save;
  925. if (pattern) {
  926. /*
  927.  * Note, prefix is returned in new space.  Must free it.
  928.  */
  929. ret = _GetGlobPrefix(pattern, &prefix);
  930. if (ret) {
  931. result = TCL_ERROR;
  932. Tcl_SetResult(interp,
  933.     "Unable to allocate pattern space", TCL_STATIC);
  934. goto out1;
  935. }
  936. key.data = prefix;
  937. key.size = strlen(prefix);
  938. /*
  939.  * If they give us an empty pattern string
  940.  * (i.e. -glob *), go through entire DB.
  941.  */
  942. if (strlen(prefix) == 0)
  943. cflag = DB_FIRST;
  944. else
  945. cflag = DB_SET_RANGE;
  946. } else
  947. cflag = DB_SET;
  948. _debug_check();
  949. ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
  950. result = _ReturnSetup(interp, ret, "db get (cursor)");
  951. if (result == TCL_ERROR)
  952. goto out1;
  953. if (pattern)
  954. cflag = DB_NEXT;
  955.  else
  956. cflag = DB_NEXT_DUP;
  957. while (ret == 0 && result == TCL_OK) {
  958. /*
  959.  * Build up our {name value} sublist
  960.  */
  961. result = _SetListElem(interp, retlist,
  962. key.data, key.size,
  963. data.data, data.size);
  964. /*
  965.  * Free space from DB_DBT_MALLOC
  966.  */
  967. __os_free(data.data, data.size);
  968. if (result != TCL_OK)
  969. break;
  970. /*
  971.  * Append {name value} to return list
  972.  */
  973. memset(&key, 0, sizeof(key));
  974. memset(&data, 0, sizeof(data));
  975. /*
  976.  * Restore any "partial" info we have saved.
  977.  */
  978. data = save;
  979. ret = dbc->c_get(dbc, &key, &data, cflag | rmw);
  980. if (ret == 0 && pattern &&
  981.     memcmp(key.data, prefix, strlen(prefix)) != 0) {
  982. /*
  983.  * Free space from DB_DBT_MALLOC
  984.  */
  985. __os_free(data.data, data.size);
  986. break;
  987. }
  988. }
  989. dbc->c_close(dbc);
  990. out1:
  991. if (result == TCL_OK)
  992. Tcl_SetObjResult(interp, retlist);
  993. out:
  994. /*
  995.  * _GetGlobPrefix(), the function which allocates prefix, works
  996.  * by copying and condensing another string.  Thus prefix may
  997.  * have multiple nuls at the end, so we free using __os_free().
  998.  */
  999. if (prefix != NULL)
  1000. __os_free(prefix,0);
  1001. return (result);
  1002. }
  1003. /*
  1004.  * tcl_db_delete --
  1005.  */
  1006. static int
  1007. tcl_DbDelete(interp, objc, objv, dbp)
  1008. Tcl_Interp *interp; /* Interpreter */
  1009. int objc; /* How many arguments? */
  1010. Tcl_Obj *CONST objv[]; /* The argument objects */
  1011. DB *dbp; /* Database pointer */
  1012. {
  1013. static char *dbdelopts[] = {
  1014. "-glob",
  1015. "-txn",
  1016. NULL
  1017. };
  1018. enum dbdelopts {
  1019. DBDEL_GLOB,
  1020. DBDEL_TXN
  1021. };
  1022. DBC *dbc;
  1023. DBT key, data;
  1024. DBTYPE type;
  1025. DB_TXN *txn;
  1026. db_recno_t recno;
  1027. int i, itmp, optindex, result, ret;
  1028. u_int32_t flag;
  1029. char *arg, *pattern, *prefix, msg[MSG_SIZE];
  1030. result = TCL_OK;
  1031. flag = 0;
  1032. pattern = prefix = NULL;
  1033. txn = NULL;
  1034. if (objc < 3) {
  1035. Tcl_WrongNumArgs(interp, 2, objv, "?-args? key");
  1036. return (TCL_ERROR);
  1037. }
  1038. memset(&key, 0, sizeof(key));
  1039. /*
  1040.  * The first arg must be -txn, -glob or a list of keys.
  1041.  */
  1042. i = 2;
  1043. while (i < objc) {
  1044. if (Tcl_GetIndexFromObj(interp, objv[i], dbdelopts, "option",
  1045.     TCL_EXACT, &optindex) != TCL_OK) {
  1046. /*
  1047.  * If we don't have a -glob or -txn, then the
  1048.  * remaining args must be exact keys.
  1049.  * Reset the result so we don't get
  1050.  * an errant error message if there is another error.
  1051.  */
  1052. if (IS_HELP(objv[i]) == TCL_OK)
  1053. return (TCL_OK);
  1054. Tcl_ResetResult(interp);
  1055. break;
  1056. }
  1057. i++;
  1058. switch ((enum dbdelopts)optindex) {
  1059. case DBDEL_TXN:
  1060. if (i == objc) {
  1061. /*
  1062.  * Someone could conceivably have a key of
  1063.  * the same name.  So just break and use it.
  1064.  */
  1065. i--;
  1066. break;
  1067. }
  1068. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1069. txn = NAME_TO_TXN(arg);
  1070. if (txn == NULL) {
  1071. snprintf(msg, MSG_SIZE,
  1072.     "Delete: Invalid txn: %sn", arg);
  1073. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1074. result = TCL_ERROR;
  1075. }
  1076. break;
  1077. case DBDEL_GLOB:
  1078. /*
  1079.  * Get the pattern.  Get the prefix and use cursors to
  1080.  * get all the data items.
  1081.  */
  1082. if (i == objc) {
  1083. /*
  1084.  * Someone could conceivably have a key of
  1085.  * the same name.  So just break and use it.
  1086.  */
  1087. i--;
  1088. break;
  1089. }
  1090. pattern = Tcl_GetStringFromObj(objv[i++], NULL);
  1091. break;
  1092. }
  1093. if (result != TCL_OK)
  1094. break;
  1095. }
  1096. if (result != TCL_OK)
  1097. goto out;
  1098. /*
  1099.  * If we have a pattern AND more keys to process, then there
  1100.  * is an error.  Either we have some number of exact keys,
  1101.  * or we have a pattern.
  1102.  */
  1103. if (pattern != NULL && i != objc) {
  1104. Tcl_WrongNumArgs(interp, 2, objv, "?args? -glob pattern | key");
  1105. result = TCL_ERROR;
  1106. goto out;
  1107. }
  1108. /*
  1109.  * XXX
  1110.  * For consistency with get, we have decided for the moment, to
  1111.  * allow -glob, or one key, not many.  The code was originally
  1112.  * written to take many keys and we'll leave it that way, because
  1113.  * tcl_DbGet may one day accept many disjoint keys to get, rather
  1114.  * than one, and at that time we'd make delete be consistent.  In
  1115.  * any case, the code is already here and there is no need to remove,
  1116.  * just check that we only have one arg left.
  1117.  */
  1118. if (pattern == NULL && i != (objc - 1)) {
  1119. Tcl_WrongNumArgs(interp, 2, objv, "?args? -glob pattern | key");
  1120. result = TCL_ERROR;
  1121. goto out;
  1122. }
  1123. /*
  1124.  * If we have remaining args, they are all exact keys.  Call
  1125.  * DB->del on each of those keys.
  1126.  *
  1127.  * If it is a RECNO database, the key is a record number and must be
  1128.  * setup up to contain a db_recno_t.  Otherwise the key is a "string".
  1129.  */
  1130. type = dbp->get_type(dbp);
  1131. ret = 0;
  1132. while (i < objc && ret == 0) {
  1133. memset(&key, 0, sizeof(key));
  1134. if (type == DB_RECNO || type == DB_QUEUE) {
  1135. result = Tcl_GetIntFromObj(interp, objv[i++], &itmp);
  1136. recno = itmp;
  1137. if (result == TCL_OK) {
  1138. key.data = &recno;
  1139. key.size = sizeof(db_recno_t);
  1140. } else
  1141. return (result);
  1142. } else {
  1143. key.data = Tcl_GetByteArrayFromObj(objv[i++], &itmp);
  1144. key.size = itmp;
  1145. }
  1146. _debug_check();
  1147. ret = dbp->del(dbp, txn, &key, 0);
  1148. /*
  1149.  * If we have any error, set up return result and stop
  1150.  * processing keys.
  1151.  */
  1152. if (ret != 0)
  1153. break;
  1154. }
  1155. result = _ReturnSetup(interp, ret, "db del");
  1156. /*
  1157.  * At this point we've either finished or, if we have a pattern,
  1158.  * we go to the nearest one and step forward until we don't
  1159.  * have any more that match the pattern prefix.
  1160.  */
  1161. if (pattern) {
  1162. ret = dbp->cursor(dbp, txn, &dbc, 0);
  1163. if (ret != 0) {
  1164. result = _ReturnSetup(interp, ret, "db cursor");
  1165. goto out;
  1166. }
  1167. /*
  1168.  * Note, prefix is returned in new space.  Must free it.
  1169.  */
  1170. memset(&key, 0, sizeof(key));
  1171. memset(&data, 0, sizeof(data));
  1172. ret = _GetGlobPrefix(pattern, &prefix);
  1173. if (ret) {
  1174. result = TCL_ERROR;
  1175. Tcl_SetResult(interp,
  1176.     "Unable to allocate pattern space", TCL_STATIC);
  1177. goto out;
  1178. }
  1179. key.data = prefix;
  1180. key.size = strlen(prefix);
  1181. if (strlen(prefix) == 0)
  1182. flag = DB_FIRST;
  1183. else
  1184. flag = DB_SET_RANGE;
  1185. ret = dbc->c_get(dbc, &key, &data, flag);
  1186. while (ret == 0 &&
  1187.     memcmp(key.data, prefix, strlen(prefix)) == 0) {
  1188. /*
  1189.  * Each time through here the cursor is pointing
  1190.  * at the current valid item.  Delete it and
  1191.  * move ahead.
  1192.  */
  1193. _debug_check();
  1194. ret = dbc->c_del(dbc, 0);
  1195. if (ret != 0) {
  1196. result = _ReturnSetup(interp, ret, "db c_del");
  1197. break;
  1198. }
  1199. /*
  1200.  * Deleted the current, now move to the next item
  1201.  * in the list, check if it matches the prefix pattern.
  1202.  */
  1203. memset(&key, 0, sizeof(key));
  1204. memset(&data, 0, sizeof(data));
  1205. ret = dbc->c_get(dbc, &key, &data, DB_NEXT);
  1206. }
  1207. if (ret == DB_NOTFOUND)
  1208. ret = 0;
  1209. /*
  1210.  * _GetGlobPrefix(), the function which allocates prefix, works
  1211.  * by copying and condensing another string.  Thus prefix may
  1212.  * have multiple nuls at the end, so we free using __os_free().
  1213.  */
  1214. __os_free(prefix,0);
  1215. dbc->c_close(dbc);
  1216. result = _ReturnSetup(interp, ret, "db del");
  1217. }
  1218. out:
  1219. return (result);
  1220. }
  1221. /*
  1222.  * tcl_db_cursor --
  1223.  */
  1224. static int
  1225. tcl_DbCursor(interp, objc, objv, dbp, dbcp)
  1226. Tcl_Interp *interp; /* Interpreter */
  1227. int objc; /* How many arguments? */
  1228. Tcl_Obj *CONST objv[]; /* The argument objects */
  1229. DB *dbp; /* Database pointer */
  1230. DBC **dbcp; /* Return cursor pointer */
  1231. {
  1232. static char *dbcuropts[] = {
  1233. "-txn", "-update",
  1234. NULL
  1235. };
  1236. enum dbcuropts {
  1237. DBCUR_TXN, DBCUR_UPDATE
  1238. };
  1239. DB_TXN *txn;
  1240. u_int32_t flag;
  1241. int i, optindex, result, ret;
  1242. char *arg, msg[MSG_SIZE];
  1243. result = TCL_OK;
  1244. flag = 0;
  1245. txn = NULL;
  1246. /*
  1247.  * If the user asks for -glob or -recno, it MUST be the second
  1248.  * last arg given.  If it isn't given, then we must check if
  1249.  * they gave us a correct key.
  1250.  */
  1251. i = 2;
  1252. while (i < objc) {
  1253. if (Tcl_GetIndexFromObj(interp, objv[i], dbcuropts, "option",
  1254.     TCL_EXACT, &optindex) != TCL_OK) {
  1255. result = IS_HELP(objv[i]);
  1256. goto out;
  1257. }
  1258. i++;
  1259. switch ((enum dbcuropts)optindex) {
  1260. case DBCUR_TXN:
  1261. if (i == objc) {
  1262. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  1263. result = TCL_ERROR;
  1264. break;
  1265. }
  1266. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1267. txn = NAME_TO_TXN(arg);
  1268. if (txn == NULL) {
  1269. snprintf(msg, MSG_SIZE,
  1270.     "Cursor: Invalid txn: %sn", arg);
  1271. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1272. result = TCL_ERROR;
  1273. }
  1274. break;
  1275. case DBCUR_UPDATE:
  1276. flag = DB_WRITECURSOR;
  1277. break;
  1278. }
  1279. if (result != TCL_OK)
  1280. break;
  1281. }
  1282. if (result != TCL_OK)
  1283. goto out;
  1284. _debug_check();
  1285. ret = dbp->cursor(dbp, txn, dbcp, flag);
  1286. if (ret != 0)
  1287. result = _ErrorSetup(interp, ret, "db cursor");
  1288. out:
  1289. return (result);
  1290. }
  1291. /*
  1292.  * tcl_db_join --
  1293.  */
  1294. static int
  1295. tcl_DbJoin(interp, objc, objv, dbp, dbcp)
  1296. Tcl_Interp *interp; /* Interpreter */
  1297. int objc; /* How many arguments? */
  1298. Tcl_Obj *CONST objv[]; /* The argument objects */
  1299. DB *dbp; /* Database pointer */
  1300. DBC **dbcp; /* Cursor pointer */
  1301. {
  1302. static char *dbjopts[] = {
  1303. "-nosort",
  1304. NULL
  1305. };
  1306. enum dbjopts {
  1307. DBJ_NOSORT
  1308. };
  1309. DBC **listp;
  1310. u_int32_t flag;
  1311. int adj, i, j, optindex, size, result, ret;
  1312. char *arg, msg[MSG_SIZE];
  1313. result = TCL_OK;
  1314. flag = 0;
  1315. if (objc < 3) {
  1316. Tcl_WrongNumArgs(interp, 2, objv, "curs1 curs2 ...");
  1317. return (TCL_ERROR);
  1318. }
  1319. i = 2;
  1320. adj = i;
  1321. while (i < objc) {
  1322. if (Tcl_GetIndexFromObj(interp, objv[i], dbjopts, "option",
  1323.     TCL_EXACT, &optindex) != TCL_OK) {
  1324. result = IS_HELP(objv[i]);
  1325. if (result == TCL_OK)
  1326. return (result);
  1327. result = TCL_OK;
  1328. Tcl_ResetResult(interp);
  1329. break;
  1330. }
  1331. i++;
  1332. switch ((enum dbjopts)optindex) {
  1333. case DBJ_NOSORT:
  1334. flag |= DB_JOIN_NOSORT;
  1335. adj++;
  1336. break;
  1337. }
  1338. }
  1339. if (result != TCL_OK)
  1340. return (result);
  1341. /*
  1342.  * Allocate one more for NULL ptr at end of list.
  1343.  */
  1344. size = sizeof(DBC *) * ((objc - adj) + 1);
  1345. ret = __os_malloc(dbp->dbenv, size, NULL, &listp);
  1346. if (ret != 0) {
  1347. Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
  1348. return (TCL_ERROR);
  1349. }
  1350. memset(listp, 0, size);
  1351. for (j = 0, i = adj; i < objc; i++, j++) {
  1352. arg = Tcl_GetStringFromObj(objv[i], NULL);
  1353. listp[j] = NAME_TO_DBC(arg);
  1354. if (listp[j] == NULL) {
  1355. snprintf(msg, MSG_SIZE,
  1356.     "Join: Invalid cursor: %sn", arg);
  1357. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1358. result = TCL_ERROR;
  1359. goto out;
  1360. }
  1361. }
  1362. listp[j] = NULL;
  1363. _debug_check();
  1364. ret = dbp->join(dbp, listp, dbcp, flag);
  1365. result = _ReturnSetup(interp, ret, "db join");
  1366. out:
  1367. __os_free(listp, size);
  1368. return (result);
  1369. }
  1370. /*
  1371.  * tcl_db_getjoin --
  1372.  */
  1373. static int
  1374. tcl_DbGetjoin(interp, objc, objv, dbp)
  1375. Tcl_Interp *interp; /* Interpreter */
  1376. int objc; /* How many arguments? */
  1377. Tcl_Obj *CONST objv[]; /* The argument objects */
  1378. DB *dbp; /* Database pointer */
  1379. {
  1380. static char *dbgetjopts[] = {
  1381. "-nosort",
  1382. "-txn",
  1383. NULL
  1384. };
  1385. enum dbgetjopts {
  1386. DBGETJ_NOSORT,
  1387. DBGETJ_TXN
  1388. };
  1389. DB_TXN *txn;
  1390. DB *elemdbp;
  1391. DBC **listp;
  1392. DBC *dbc;
  1393. DBT key, data;
  1394. Tcl_Obj **elemv, *retlist;
  1395. u_int32_t flag;
  1396. int adj, elemc, i, itmp, j, optindex, result, ret, size;
  1397. char *arg, msg[MSG_SIZE];
  1398. result = TCL_OK;
  1399. flag = 0;
  1400. if (objc < 3) {
  1401. Tcl_WrongNumArgs(interp, 2, objv, "{db1 key1} {db2 key2} ...");
  1402. return (TCL_ERROR);
  1403. }
  1404. txn = NULL;
  1405. i = 2;
  1406. adj = i;
  1407. while (i < objc) {
  1408. if (Tcl_GetIndexFromObj(interp, objv[i], dbgetjopts, "option",
  1409.     TCL_EXACT, &optindex) != TCL_OK) {
  1410. result = IS_HELP(objv[i]);
  1411. if (result == TCL_OK)
  1412. return (result);
  1413. result = TCL_OK;
  1414. Tcl_ResetResult(interp);
  1415. break;
  1416. }
  1417. i++;
  1418. switch ((enum dbgetjopts)optindex) {
  1419. case DBGETJ_NOSORT:
  1420. flag |= DB_JOIN_NOSORT;
  1421. adj++;
  1422. break;
  1423. case DBGETJ_TXN:
  1424. if (i == objc) {
  1425. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  1426. result = TCL_ERROR;
  1427. break;
  1428. }
  1429. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1430. txn = NAME_TO_TXN(arg);
  1431. adj += 2;
  1432. if (txn == NULL) {
  1433. snprintf(msg, MSG_SIZE,
  1434.     "GetJoin: Invalid txn: %sn", arg);
  1435. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1436. result = TCL_ERROR;
  1437. }
  1438. break;
  1439. }
  1440. }
  1441. if (result != TCL_OK)
  1442. return (result);
  1443. size = sizeof(DBC *) * ((objc - adj) + 1);
  1444. ret = __os_malloc(NULL, size, NULL, &listp);
  1445. if (ret != 0) {
  1446. Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC);
  1447. return (TCL_ERROR);
  1448. }
  1449. memset(listp, 0, size);
  1450. for (j = 0, i = adj; i < objc; i++, j++) {
  1451. /*
  1452.  * Get each sublist as {db key}
  1453.  */
  1454. result = Tcl_ListObjGetElements(interp, objv[i],
  1455.     &elemc, &elemv);
  1456. if (elemc != 2) {
  1457. Tcl_SetResult(interp, "Lists must be {db key}",
  1458.     TCL_STATIC);
  1459. result = TCL_ERROR;
  1460. goto out;
  1461. }
  1462. /*
  1463.  * Get a pointer to that open db.  Then, open a cursor in
  1464.  * that db, and go to the "key" place.
  1465.  */
  1466. elemdbp = NAME_TO_DB(Tcl_GetStringFromObj(elemv[0], NULL));
  1467. if (elemdbp == NULL) {
  1468. snprintf(msg, MSG_SIZE, "Get_join: Invalid db: %sn",
  1469.     Tcl_GetStringFromObj(elemv[0], NULL));
  1470. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1471. result = TCL_ERROR;
  1472. goto out;
  1473. }
  1474. ret = elemdbp->cursor(elemdbp, txn, &listp[j], 0);
  1475. if ((result = _ReturnSetup(interp, ret, "db cursor")) ==
  1476.     TCL_ERROR)
  1477. goto out;
  1478. memset(&key, 0, sizeof(key));
  1479. memset(&data, 0, sizeof(data));
  1480. key.data = Tcl_GetByteArrayFromObj(elemv[elemc-1], &itmp);
  1481. key.size = itmp;
  1482. ret = (listp[j])->c_get(listp[j], &key, &data, DB_SET);
  1483. if ((result = _ReturnSetup(interp, ret, "db cget")) ==
  1484.     TCL_ERROR)
  1485. goto out;
  1486. }
  1487. listp[j] = NULL;
  1488. _debug_check();
  1489. ret = dbp->join(dbp, listp, &dbc, flag);
  1490. result = _ReturnSetup(interp, ret, "db join");
  1491. if (result == TCL_ERROR)
  1492. goto out;
  1493. retlist = Tcl_NewListObj(0, NULL);
  1494. while (ret == 0 && result == TCL_OK) {
  1495. memset(&key, 0, sizeof(key));
  1496. memset(&data, 0, sizeof(data));
  1497. key.flags |= DB_DBT_MALLOC;
  1498. data.flags |= DB_DBT_MALLOC;
  1499. ret = dbc->c_get(dbc, &key, &data, 0);
  1500. /*
  1501.  * Build up our {name value} sublist
  1502.  */
  1503. if (ret == 0) {
  1504. result = _SetListElem(interp, retlist,
  1505.     key.data, key.size,
  1506.     data.data, data.size);
  1507. __os_free(key.data, key.size);
  1508. __os_free(data.data, data.size);
  1509. }
  1510. }
  1511. dbc->c_close(dbc);
  1512. if (result == TCL_OK)
  1513. Tcl_SetObjResult(interp, retlist);
  1514. out:
  1515. while (j) {
  1516. if (listp[j])
  1517. (listp[j])->c_close(listp[j]);
  1518. j--;
  1519. }
  1520. __os_free(listp, size);
  1521. return (result);
  1522. }
  1523. /*
  1524.  * tcl_DbCount --
  1525.  */
  1526. static int
  1527. tcl_DbCount(interp, objc, objv, dbp)
  1528. Tcl_Interp *interp; /* Interpreter */
  1529. int objc; /* How many arguments? */
  1530. Tcl_Obj *CONST objv[]; /* The argument objects */
  1531. DB *dbp; /* Database pointer */
  1532. {
  1533. Tcl_Obj *res;
  1534. DBC *dbc;
  1535. DBT key, data;
  1536. db_recno_t count, recno;
  1537. int itmp, len, result, ret;
  1538. result = TCL_OK;
  1539. count = 0;
  1540. res = NULL;
  1541. if (objc != 3) {
  1542. Tcl_WrongNumArgs(interp, 2, objv, "key");
  1543. return (TCL_ERROR);
  1544. }
  1545. memset(&key, 0, sizeof(key));
  1546. /*
  1547.  * Get the count for our key.
  1548.  * We do this by getting a cursor for this DB.  Moving the cursor
  1549.  * to the set location, and getting a count on that cursor.
  1550.  */
  1551. ret = 0;
  1552. memset(&key, 0, sizeof(key));
  1553. memset(&data, 0, sizeof(data));
  1554. /*
  1555.  * If it's a queue or recno database, we must make sure to
  1556.  * treat the key as a recno rather than as a byte string.
  1557.  */
  1558. if (dbp->type == DB_RECNO || dbp->type == DB_QUEUE) {
  1559. result = Tcl_GetIntFromObj(interp, objv[2], &itmp);
  1560. recno = itmp;
  1561. if (result == TCL_OK) {
  1562. key.data = &recno;
  1563. key.size = sizeof(db_recno_t);
  1564. } else
  1565. return (result);
  1566. } else {
  1567. key.data = Tcl_GetByteArrayFromObj(objv[2], &len);
  1568. key.size = len;
  1569. }
  1570. _debug_check();
  1571. ret = dbp->cursor(dbp, NULL, &dbc, 0);
  1572. if (ret != 0) {
  1573. result = _ReturnSetup(interp, ret, "db cursor");
  1574. goto out;
  1575. }
  1576. /*
  1577.  * Move our cursor to the key.
  1578.  */
  1579. ret = dbc->c_get(dbc, &key, &data, DB_SET);
  1580. if (ret == DB_NOTFOUND)
  1581. count = 0;
  1582. else {
  1583. ret = dbc->c_count(dbc, &count, 0);
  1584. if (ret != 0) {
  1585. result = _ReturnSetup(interp, ret, "db cursor");
  1586. goto out;
  1587. }
  1588. }
  1589. res = Tcl_NewIntObj(count);
  1590. Tcl_SetObjResult(interp, res);
  1591. out:
  1592. return (result);
  1593. }
  1594. /*
  1595.  * tcl_DbKeyRange --
  1596.  */
  1597. static int
  1598. tcl_DbKeyRange(interp, objc, objv, dbp)
  1599. Tcl_Interp *interp; /* Interpreter */
  1600. int objc; /* How many arguments? */
  1601. Tcl_Obj *CONST objv[]; /* The argument objects */
  1602. DB *dbp; /* Database pointer */
  1603. {
  1604. static char *dbkeyropts[] = {
  1605. "-txn",
  1606. NULL
  1607. };
  1608. enum dbkeyropts {
  1609. DBKEYR_TXN
  1610. };
  1611. DB_TXN *txn;
  1612. DB_KEY_RANGE range;
  1613. DBT key;
  1614. DBTYPE type;
  1615. Tcl_Obj *myobjv[3], *retlist;
  1616. db_recno_t recno;
  1617. u_int32_t flag;
  1618. int i, itmp, myobjc, optindex, result, ret;
  1619. char *arg, msg[MSG_SIZE];
  1620. result = TCL_OK;
  1621. flag = 0;
  1622. if (objc < 3) {
  1623. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id? key");
  1624. return (TCL_ERROR);
  1625. }
  1626. txn = NULL;
  1627. i = 2;
  1628. while (i < objc) {
  1629. if (Tcl_GetIndexFromObj(interp, objv[i], dbkeyropts, "option",
  1630.     TCL_EXACT, &optindex) != TCL_OK) {
  1631. result = IS_HELP(objv[i]);
  1632. if (result == TCL_OK)
  1633. return (result);
  1634. result = TCL_OK;
  1635. Tcl_ResetResult(interp);
  1636. break;
  1637. }
  1638. i++;
  1639. switch ((enum dbkeyropts)optindex) {
  1640. case DBKEYR_TXN:
  1641. if (i == objc) {
  1642. Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?");
  1643. result = TCL_ERROR;
  1644. break;
  1645. }
  1646. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  1647. txn = NAME_TO_TXN(arg);
  1648. if (txn == NULL) {
  1649. snprintf(msg, MSG_SIZE,
  1650.     "KeyRange: Invalid txn: %sn", arg);
  1651. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  1652. result = TCL_ERROR;
  1653. }
  1654. break;
  1655. }
  1656. }
  1657. if (result != TCL_OK)
  1658. return (result);
  1659. type = dbp->get_type(dbp);
  1660. ret = 0;
  1661. /*
  1662.  * Make sure we have a key.
  1663.  */
  1664. if (i != (objc - 1)) {
  1665. Tcl_WrongNumArgs(interp, 2, objv, "?args? key");
  1666. result = TCL_ERROR;
  1667. goto out;
  1668. }
  1669. memset(&key, 0, sizeof(key));
  1670. if (type == DB_RECNO || type == DB_QUEUE) {
  1671. result = Tcl_GetIntFromObj(interp, objv[i], &itmp);
  1672. recno = itmp;
  1673. if (result == TCL_OK) {
  1674. key.data = &recno;
  1675. key.size = sizeof(db_recno_t);
  1676. } else
  1677. return (result);
  1678. } else {
  1679. key.data = Tcl_GetByteArrayFromObj(objv[i++], &itmp);
  1680. key.size = itmp;
  1681. }
  1682. _debug_check();
  1683. ret = dbp->key_range(dbp, txn, &key, &range, flag);
  1684. result = _ReturnSetup(interp, ret, "db join");
  1685. if (result == TCL_ERROR)
  1686. goto out;
  1687. /*
  1688.  * If we succeeded, set up return list.
  1689.  */
  1690. myobjc = 3;
  1691. myobjv[0] = Tcl_NewDoubleObj(range.less);
  1692. myobjv[1] = Tcl_NewDoubleObj(range.equal);
  1693. myobjv[2] = Tcl_NewDoubleObj(range.greater);
  1694. retlist = Tcl_NewListObj(myobjc, myobjv);
  1695. if (result == TCL_OK)
  1696. Tcl_SetObjResult(interp, retlist);
  1697. out:
  1698. return (result);
  1699. }