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

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1999-2001
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: tcl_txn.c,v 11.57 2002/08/06 06:21:36 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/tcl_db.h"
  19. static int tcl_TxnCommit __P((Tcl_Interp *,
  20.        int, Tcl_Obj * CONST *, DB_TXN *, DBTCL_INFO *));
  21. static int txn_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST *));
  22. /*
  23.  * _TxnInfoDelete --
  24.  * Removes nested txn info structures that are children
  25.  * of this txn.
  26.  * RECURSIVE:  Transactions can be arbitrarily nested, so we
  27.  * must recurse down until we get them all.
  28.  *
  29.  * PUBLIC: void _TxnInfoDelete __P((Tcl_Interp *, DBTCL_INFO *));
  30.  */
  31. void
  32. _TxnInfoDelete(interp, txnip)
  33. Tcl_Interp *interp; /* Interpreter */
  34. DBTCL_INFO *txnip; /* Info for txn */
  35. {
  36. DBTCL_INFO *nextp, *p;
  37. for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
  38. /*
  39.  * Check if this info structure "belongs" to this
  40.  * txn.  Remove its commands and info structure.
  41.  */
  42. nextp = LIST_NEXT(p, entries);
  43.  if (p->i_parent == txnip && p->i_type == I_TXN) {
  44. _TxnInfoDelete(interp, p);
  45. (void)Tcl_DeleteCommand(interp, p->i_name);
  46. _DeleteInfo(p);
  47. }
  48. }
  49. }
  50. /*
  51.  * tcl_TxnCheckpoint --
  52.  *
  53.  * PUBLIC: int tcl_TxnCheckpoint __P((Tcl_Interp *, int,
  54.  * PUBLIC:    Tcl_Obj * CONST*, DB_ENV *));
  55.  */
  56. int
  57. tcl_TxnCheckpoint(interp, objc, objv, envp)
  58. Tcl_Interp *interp; /* Interpreter */
  59. int objc; /* How many arguments? */
  60. Tcl_Obj *CONST objv[]; /* The argument objects */
  61. DB_ENV *envp; /* Environment pointer */
  62. {
  63. static char *txnckpopts[] = {
  64. "-kbyte", "-min",
  65. NULL
  66. };
  67. enum txnckpopts {
  68. TXNCKP_KB, TXNCKP_MIN
  69. };
  70. int i, kb, min, optindex, result, ret;
  71. result = TCL_OK;
  72. kb = min = 0;
  73. /*
  74.  * Get the flag index from the object based on the options
  75.  * defined above.
  76.  */
  77. i = 2;
  78. while (i < objc) {
  79. if (Tcl_GetIndexFromObj(interp, objv[i],
  80.     txnckpopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
  81. return (IS_HELP(objv[i]));
  82. }
  83. i++;
  84. switch ((enum txnckpopts)optindex) {
  85. case TXNCKP_KB:
  86. if (i == objc) {
  87. Tcl_WrongNumArgs(interp, 2, objv,
  88.     "?-kbyte kb?");
  89. result = TCL_ERROR;
  90. break;
  91. }
  92. result = Tcl_GetIntFromObj(interp, objv[i++], &kb);
  93. break;
  94. case TXNCKP_MIN:
  95. if (i == objc) {
  96. Tcl_WrongNumArgs(interp, 2, objv, "?-min min?");
  97. result = TCL_ERROR;
  98. break;
  99. }
  100. result = Tcl_GetIntFromObj(interp, objv[i++], &min);
  101. break;
  102. }
  103. }
  104. _debug_check();
  105. ret = envp->txn_checkpoint(envp, (u_int32_t)kb, (u_int32_t)min, 0);
  106. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  107.     "txn checkpoint");
  108. return (result);
  109. }
  110. /*
  111.  * tcl_Txn --
  112.  *
  113.  * PUBLIC: int tcl_Txn __P((Tcl_Interp *, int,
  114.  * PUBLIC:    Tcl_Obj * CONST*, DB_ENV *, DBTCL_INFO *));
  115.  */
  116. int
  117. tcl_Txn(interp, objc, objv, envp, envip)
  118. Tcl_Interp *interp; /* Interpreter */
  119. int objc; /* How many arguments? */
  120. Tcl_Obj *CONST objv[]; /* The argument objects */
  121. DB_ENV *envp; /* Environment pointer */
  122. DBTCL_INFO *envip; /* Info pointer */
  123. {
  124. static char *txnopts[] = {
  125. #if CONFIG_TEST
  126. "-dirty",
  127. "-lock_timeout",
  128. "-txn_timeout",
  129. #endif
  130. "-nosync",
  131. "-nowait",
  132. "-parent",
  133. "-sync",
  134. NULL
  135. };
  136. enum txnopts {
  137. #if CONFIG_TEST
  138. TXNDIRTY,
  139. TXN_LOCK_TIMEOUT,
  140. TXN_TIMEOUT,
  141. #endif
  142. TXNNOSYNC,
  143. TXNNOWAIT,
  144. TXNPARENT,
  145. TXNSYNC
  146. };
  147. DBTCL_INFO *ip;
  148. DB_TXN *parent;
  149. DB_TXN *txn;
  150. Tcl_Obj *res;
  151. db_timeout_t lk_time, tx_time;
  152. u_int32_t flag, lk_timeflag, tx_timeflag;
  153. int i, optindex, result, ret;
  154. char *arg, msg[MSG_SIZE], newname[MSG_SIZE];
  155. result = TCL_OK;
  156. memset(newname, 0, MSG_SIZE);
  157. parent = NULL;
  158. flag = 0;
  159. lk_timeflag = tx_timeflag = 0;
  160. i = 2;
  161. while (i < objc) {
  162. if (Tcl_GetIndexFromObj(interp, objv[i],
  163.     txnopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
  164. return (IS_HELP(objv[i]));
  165. }
  166. i++;
  167. switch ((enum txnopts)optindex) {
  168. #ifdef CONFIG_TEST
  169. case TXNDIRTY:
  170. flag |= DB_DIRTY_READ;
  171. break;
  172. case TXN_LOCK_TIMEOUT:
  173. lk_timeflag = DB_SET_LOCK_TIMEOUT;
  174. goto getit;
  175. case TXN_TIMEOUT:
  176. tx_timeflag = DB_SET_TXN_TIMEOUT;
  177. getit:
  178. if (i >= objc) {
  179. Tcl_WrongNumArgs(interp, 2, objv,
  180.     "?-txn_timestamp time?");
  181. return (TCL_ERROR);
  182. }
  183. result = Tcl_GetLongFromObj(interp, objv[i++],
  184.     (long *)(optindex == TXN_LOCK_TIMEOUT ?
  185.     &lk_time : &tx_time));
  186. if (result != TCL_OK)
  187. return (TCL_ERROR);
  188. break;
  189. #endif
  190. case TXNNOSYNC:
  191. FLAG_CHECK2(flag, DB_DIRTY_READ);
  192. flag |= DB_TXN_NOSYNC;
  193. break;
  194. case TXNNOWAIT:
  195. FLAG_CHECK2(flag, DB_DIRTY_READ);
  196. flag |= DB_TXN_NOWAIT;
  197. break;
  198. case TXNPARENT:
  199. if (i == objc) {
  200. Tcl_WrongNumArgs(interp, 2, objv,
  201.     "?-parent txn?");
  202. result = TCL_ERROR;
  203. break;
  204. }
  205. arg = Tcl_GetStringFromObj(objv[i++], NULL);
  206. parent = NAME_TO_TXN(arg);
  207. if (parent == NULL) {
  208. snprintf(msg, MSG_SIZE,
  209.     "Invalid parent txn: %sn",
  210.     arg);
  211. Tcl_SetResult(interp, msg, TCL_VOLATILE);
  212. return (TCL_ERROR);
  213. }
  214. break;
  215. case TXNSYNC:
  216. FLAG_CHECK2(flag, DB_DIRTY_READ);
  217. flag |= DB_TXN_SYNC;
  218. break;
  219. }
  220. }
  221. snprintf(newname, sizeof(newname), "%s.txn%d",
  222.     envip->i_name, envip->i_envtxnid);
  223. ip = _NewInfo(interp, NULL, newname, I_TXN);
  224. if (ip == NULL) {
  225. Tcl_SetResult(interp, "Could not set up info",
  226.     TCL_STATIC);
  227. return (TCL_ERROR);
  228. }
  229. _debug_check();
  230. ret = envp->txn_begin(envp, parent, &txn, flag);
  231. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  232.     "txn");
  233. if (result == TCL_ERROR)
  234. _DeleteInfo(ip);
  235. else {
  236. /*
  237.  * Success.  Set up return.  Set up new info
  238.  * and command widget for this txn.
  239.  */
  240. envip->i_envtxnid++;
  241. if (parent)
  242. ip->i_parent = _PtrToInfo(parent);
  243. else
  244. ip->i_parent = envip;
  245. _SetInfoData(ip, txn);
  246. Tcl_CreateObjCommand(interp, newname,
  247.     (Tcl_ObjCmdProc *)txn_Cmd, (ClientData)txn, NULL);
  248. res = Tcl_NewStringObj(newname, strlen(newname));
  249. Tcl_SetObjResult(interp, res);
  250. if (tx_timeflag != 0) {
  251. ret = txn->set_timeout(txn, tx_time, tx_timeflag);
  252. if (ret != 0) {
  253. result =
  254.     _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  255. "set_timeout");
  256. _DeleteInfo(ip);
  257. }
  258. }
  259. if (lk_timeflag != 0) {
  260. ret = txn->set_timeout(txn, lk_time, lk_timeflag);
  261. if (ret != 0) {
  262. result =
  263.     _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  264. "set_timeout");
  265. _DeleteInfo(ip);
  266. }
  267. }
  268. }
  269. return (result);
  270. }
  271. /*
  272.  * tcl_TxnStat --
  273.  *
  274.  * PUBLIC: int tcl_TxnStat __P((Tcl_Interp *, int,
  275.  * PUBLIC:    Tcl_Obj * CONST*, DB_ENV *));
  276.  */
  277. int
  278. tcl_TxnStat(interp, objc, objv, envp)
  279. Tcl_Interp *interp; /* Interpreter */
  280. int objc; /* How many arguments? */
  281. Tcl_Obj *CONST objv[]; /* The argument objects */
  282. DB_ENV *envp; /* Environment pointer */
  283. {
  284. DBTCL_INFO *ip;
  285. DB_TXN_ACTIVE *p;
  286. DB_TXN_STAT *sp;
  287. Tcl_Obj *myobjv[2], *res, *thislist, *lsnlist;
  288. u_int32_t i;
  289. int myobjc, result, ret;
  290. result = TCL_OK;
  291. /*
  292.  * No args for this.  Error if there are some.
  293.  */
  294. if (objc != 2) {
  295. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  296. return (TCL_ERROR);
  297. }
  298. _debug_check();
  299. ret = envp->txn_stat(envp, &sp, 0);
  300. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  301.     "txn stat");
  302. if (result == TCL_ERROR)
  303. return (result);
  304. /*
  305.  * Have our stats, now construct the name value
  306.  * list pairs and free up the memory.
  307.  */
  308. res = Tcl_NewObj();
  309. /*
  310.  * MAKE_STAT_LIST assumes 'res' and 'error' label.
  311.  */
  312. MAKE_STAT_LIST("Region size", sp->st_regsize);
  313. MAKE_STAT_LSN("LSN of last checkpoint", &sp->st_last_ckp);
  314. MAKE_STAT_LIST("Time of last checkpoint", sp->st_time_ckp);
  315. MAKE_STAT_LIST("Last txn ID allocated", sp->st_last_txnid);
  316. MAKE_STAT_LIST("Max Txns", sp->st_maxtxns);
  317. MAKE_STAT_LIST("Number aborted txns", sp->st_naborts);
  318. MAKE_STAT_LIST("Number active txns", sp->st_nactive);
  319. MAKE_STAT_LIST("Maximum  active txns", sp->st_maxnactive);
  320. MAKE_STAT_LIST("Number txns begun", sp->st_nbegins);
  321. MAKE_STAT_LIST("Number committed txns", sp->st_ncommits);
  322. MAKE_STAT_LIST("Number restored txns", sp->st_nrestores);
  323. MAKE_STAT_LIST("Number of region lock waits", sp->st_region_wait);
  324. MAKE_STAT_LIST("Number of region lock nowaits", sp->st_region_nowait);
  325. for (i = 0, p = sp->st_txnarray; i < sp->st_nactive; i++, p++)
  326. for (ip = LIST_FIRST(&__db_infohead); ip != NULL;
  327.     ip = LIST_NEXT(ip, entries)) {
  328. if (ip->i_type != I_TXN)
  329. continue;
  330. if (ip->i_type == I_TXN &&
  331.     (ip->i_txnp->id(ip->i_txnp) == p->txnid)) {
  332. MAKE_STAT_LSN(ip->i_name, &p->lsn);
  333. if (p->parentid != 0)
  334. MAKE_STAT_STRLIST("Parent",
  335.     ip->i_parent->i_name);
  336. else
  337. MAKE_STAT_LIST("Parent", 0);
  338. break;
  339. }
  340. }
  341. Tcl_SetObjResult(interp, res);
  342. error:
  343. free(sp);
  344. return (result);
  345. }
  346. /*
  347.  * tcl_TxnTimeout --
  348.  *
  349.  * PUBLIC: int tcl_TxnTimeout __P((Tcl_Interp *, int,
  350.  * PUBLIC:    Tcl_Obj * CONST*, DB_ENV *));
  351.  */
  352. int
  353. tcl_TxnTimeout(interp, objc, objv, envp)
  354. Tcl_Interp *interp; /* Interpreter */
  355. int objc; /* How many arguments? */
  356. Tcl_Obj *CONST objv[]; /* The argument objects */
  357. DB_ENV *envp; /* Environment pointer */
  358. {
  359. long timeout;
  360. int result, ret;
  361. /*
  362.  * One arg, the timeout.
  363.  */
  364. if (objc != 3) {
  365. Tcl_WrongNumArgs(interp, 2, objv, "?timeout?");
  366. return (TCL_ERROR);
  367. }
  368. result = Tcl_GetLongFromObj(interp, objv[2], &timeout);
  369. if (result != TCL_OK)
  370. return (result);
  371. _debug_check();
  372. ret = envp->set_timeout(envp, (u_int32_t)timeout, DB_SET_TXN_TIMEOUT);
  373. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  374.     "lock timeout");
  375. return (result);
  376. }
  377. /*
  378.  * txn_Cmd --
  379.  * Implements the "txn" widget.
  380.  */
  381. static int
  382. txn_Cmd(clientData, interp, objc, objv)
  383. ClientData clientData; /* Txn handle */
  384. Tcl_Interp *interp; /* Interpreter */
  385. int objc; /* How many arguments? */
  386. Tcl_Obj *CONST objv[]; /* The argument objects */
  387. {
  388. static char *txncmds[] = {
  389. #if CONFIG_TEST
  390. "discard",
  391. "id",
  392. "prepare",
  393. #endif
  394. "abort",
  395. "commit",
  396. NULL
  397. };
  398. enum txncmds {
  399. #if CONFIG_TEST
  400. TXNDISCARD,
  401. TXNID,
  402. TXNPREPARE,
  403. #endif
  404. TXNABORT,
  405. TXNCOMMIT
  406. };
  407. DBTCL_INFO *txnip;
  408. DB_TXN *txnp;
  409. Tcl_Obj *res;
  410. int cmdindex, result, ret;
  411. u_int8_t *gid;
  412. Tcl_ResetResult(interp);
  413. txnp = (DB_TXN *)clientData;
  414. txnip = _PtrToInfo((void *)txnp);
  415. result = TCL_OK;
  416. if (txnp == NULL) {
  417. Tcl_SetResult(interp, "NULL txn pointer", TCL_STATIC);
  418. return (TCL_ERROR);
  419. }
  420. if (txnip == NULL) {
  421. Tcl_SetResult(interp, "NULL txn info pointer", TCL_STATIC);
  422. return (TCL_ERROR);
  423. }
  424. /*
  425.  * Get the command name index from the object based on the dbcmds
  426.  * defined above.
  427.  */
  428. if (Tcl_GetIndexFromObj(interp,
  429.     objv[1], txncmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
  430. return (IS_HELP(objv[1]));
  431. res = NULL;
  432. switch ((enum txncmds)cmdindex) {
  433. #if CONFIG_TEST
  434. case TXNDISCARD:
  435. if (objc != 2) {
  436. Tcl_WrongNumArgs(interp, 1, objv, NULL);
  437. return (TCL_ERROR);
  438. }
  439. _debug_check();
  440. ret = txnp->discard(txnp, 0);
  441. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  442.     "txn discard");
  443. _TxnInfoDelete(interp, txnip);
  444. (void)Tcl_DeleteCommand(interp, txnip->i_name);
  445. _DeleteInfo(txnip);
  446. break;
  447. case TXNID:
  448. if (objc != 2) {
  449. Tcl_WrongNumArgs(interp, 1, objv, NULL);
  450. return (TCL_ERROR);
  451. }
  452. _debug_check();
  453. ret = txnp->id(txnp);
  454. res = Tcl_NewIntObj(ret);
  455. break;
  456. case TXNPREPARE:
  457. if (objc != 3) {
  458. Tcl_WrongNumArgs(interp, 1, objv, NULL);
  459. return (TCL_ERROR);
  460. }
  461. _debug_check();
  462. gid = (u_int8_t *)Tcl_GetByteArrayFromObj(objv[2], NULL);
  463. ret = txnp->prepare(txnp, gid);
  464. /*
  465.  * !!!
  466.  * DB_TXN->prepare commits all outstanding children.  But it
  467.  * does NOT destroy the current txn handle.  So, we must call
  468.  * _TxnInfoDelete to recursively remove all nested txn handles,
  469.  * we do not call _DeleteInfo on ourselves.
  470.  */
  471. _TxnInfoDelete(interp, txnip);
  472. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  473.     "txn prepare");
  474. break;
  475. #endif
  476. case TXNABORT:
  477. if (objc != 2) {
  478. Tcl_WrongNumArgs(interp, 1, objv, NULL);
  479. return (TCL_ERROR);
  480. }
  481. _debug_check();
  482. ret = txnp->abort(txnp);
  483. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  484.     "txn abort");
  485. _TxnInfoDelete(interp, txnip);
  486. (void)Tcl_DeleteCommand(interp, txnip->i_name);
  487. _DeleteInfo(txnip);
  488. break;
  489. case TXNCOMMIT:
  490. result = tcl_TxnCommit(interp, objc, objv, txnp, txnip);
  491. _TxnInfoDelete(interp, txnip);
  492. (void)Tcl_DeleteCommand(interp, txnip->i_name);
  493. _DeleteInfo(txnip);
  494. break;
  495. }
  496. /*
  497.  * Only set result if we have a res.  Otherwise, lower
  498.  * functions have already done so.
  499.  */
  500. if (result == TCL_OK && res)
  501. Tcl_SetObjResult(interp, res);
  502. return (result);
  503. }
  504. static int
  505. tcl_TxnCommit(interp, objc, objv, txnp, txnip)
  506. Tcl_Interp *interp; /* Interpreter */
  507. int objc; /* How many arguments? */
  508. Tcl_Obj *CONST objv[]; /* The argument objects */
  509. DB_TXN *txnp; /* Transaction pointer */
  510. DBTCL_INFO *txnip; /* Info pointer */
  511. {
  512. static char *commitopt[] = {
  513. "-nosync",
  514. "-sync",
  515. NULL
  516. };
  517. enum commitopt {
  518. COMSYNC,
  519. COMNOSYNC
  520. };
  521. u_int32_t flag;
  522. int optindex, result, ret;
  523. COMPQUIET(txnip, NULL);
  524. result = TCL_OK;
  525. flag = 0;
  526. if (objc != 2 && objc != 3) {
  527. Tcl_WrongNumArgs(interp, 1, objv, NULL);
  528. return (TCL_ERROR);
  529. }
  530. if (objc == 3) {
  531. if (Tcl_GetIndexFromObj(interp, objv[2], commitopt,
  532.     "option", TCL_EXACT, &optindex) != TCL_OK)
  533. return (IS_HELP(objv[2]));
  534. switch ((enum commitopt)optindex) {
  535. case COMSYNC:
  536. FLAG_CHECK(flag);
  537. flag = DB_TXN_SYNC;
  538. break;
  539. case COMNOSYNC:
  540. FLAG_CHECK(flag);
  541. flag = DB_TXN_NOSYNC;
  542. break;
  543. }
  544. }
  545. _debug_check();
  546. ret = txnp->commit(txnp, flag);
  547. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  548.     "txn commit");
  549. return (result);
  550. }
  551. #if CONFIG_TEST
  552. /*
  553.  * tcl_TxnRecover --
  554.  *
  555.  * PUBLIC: int tcl_TxnRecover __P((Tcl_Interp *, int,
  556.  * PUBLIC:    Tcl_Obj * CONST*, DB_ENV *, DBTCL_INFO *));
  557.  */
  558. int
  559. tcl_TxnRecover(interp, objc, objv, envp, envip)
  560. Tcl_Interp *interp; /* Interpreter */
  561. int objc; /* How many arguments? */
  562. Tcl_Obj *CONST objv[]; /* The argument objects */
  563. DB_ENV *envp; /* Environment pointer */
  564. DBTCL_INFO *envip; /* Info pointer */
  565. {
  566. #define DO_PREPLIST(count)
  567. for (i = 0; i < count; i++) {
  568. snprintf(newname, sizeof(newname), "%s.txn%d",
  569.     envip->i_name, envip->i_envtxnid);
  570. ip = _NewInfo(interp, NULL, newname, I_TXN);
  571. if (ip == NULL) {
  572. Tcl_SetResult(interp, "Could not set up info",
  573.     TCL_STATIC);
  574. return (TCL_ERROR);
  575. }
  576. envip->i_envtxnid++;
  577. ip->i_parent = envip;
  578. p = &prep[i];
  579. _SetInfoData(ip, p->txn);
  580. Tcl_CreateObjCommand(interp, newname,
  581.     (Tcl_ObjCmdProc *)txn_Cmd, (ClientData)p->txn, NULL);
  582. result = _SetListElem(interp, res, newname, strlen(newname),
  583.     p->gid, DB_XIDDATASIZE);
  584. if (result != TCL_OK)
  585. goto error;
  586. }
  587. DBTCL_INFO *ip;
  588. DB_PREPLIST prep[DBTCL_PREP], *p;
  589. Tcl_Obj *res;
  590. long count, i;
  591. int result, ret;
  592. char newname[MSG_SIZE];
  593. result = TCL_OK;
  594. /*
  595.  * No args for this.  Error if there are some.
  596.  */
  597. if (objc != 2) {
  598. Tcl_WrongNumArgs(interp, 2, objv, NULL);
  599. return (TCL_ERROR);
  600. }
  601. _debug_check();
  602. ret = envp->txn_recover(envp, prep, DBTCL_PREP, &count, DB_FIRST);
  603. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  604.     "txn recover");
  605. if (result == TCL_ERROR)
  606. return (result);
  607. res = Tcl_NewObj();
  608. DO_PREPLIST(count);
  609. /*
  610.  * If count returned is the maximum size we have, then there
  611.  * might be more.  Keep going until we get them all.
  612.  */
  613. while (count == DBTCL_PREP) {
  614. ret = envp->txn_recover(
  615.     envp, prep, DBTCL_PREP, &count, DB_NEXT);
  616. result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
  617.     "txn recover");
  618. if (result == TCL_ERROR)
  619. return (result);
  620. DO_PREPLIST(count);
  621. }
  622. Tcl_SetObjResult(interp, res);
  623. error:
  624. return (result);
  625. }
  626. #endif