00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "db_config.h"
00011
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014
00015 #include <stdlib.h>
00016 #include <string.h>
00017 #include <tcl.h>
00018 #endif
00019
00020 #include "db_int.h"
00021 #include "dbinc/tcl_db.h"
00022
00023 static int tcl_TxnCommit __P((Tcl_Interp *,
00024 int, Tcl_Obj * CONST *, DB_TXN *, DBTCL_INFO *));
00025 static int txn_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST *));
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 void
00037 _TxnInfoDelete(interp, txnip)
00038 Tcl_Interp *interp;
00039 DBTCL_INFO *txnip;
00040 {
00041 DBTCL_INFO *nextp, *p;
00042
00043 for (p = LIST_FIRST(&__db_infohead); p != NULL; p = nextp) {
00044
00045
00046
00047
00048 nextp = LIST_NEXT(p, entries);
00049 if (p->i_parent == txnip && p->i_type == I_TXN) {
00050 _TxnInfoDelete(interp, p);
00051 (void)Tcl_DeleteCommand(interp, p->i_name);
00052 _DeleteInfo(p);
00053 }
00054 }
00055 }
00056
00057
00058
00059
00060
00061
00062
00063 int
00064 tcl_TxnCheckpoint(interp, objc, objv, envp)
00065 Tcl_Interp *interp;
00066 int objc;
00067 Tcl_Obj *CONST objv[];
00068 DB_ENV *envp;
00069 {
00070 static const char *txnckpopts[] = {
00071 "-force",
00072 "-kbyte",
00073 "-min",
00074 NULL
00075 };
00076 enum txnckpopts {
00077 TXNCKP_FORCE,
00078 TXNCKP_KB,
00079 TXNCKP_MIN
00080 };
00081 u_int32_t flags;
00082 int i, kb, min, optindex, result, ret;
00083
00084 result = TCL_OK;
00085 flags = 0;
00086 kb = min = 0;
00087
00088
00089
00090
00091
00092 i = 2;
00093 while (i < objc) {
00094 if (Tcl_GetIndexFromObj(interp, objv[i],
00095 txnckpopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
00096 return (IS_HELP(objv[i]));
00097 }
00098 i++;
00099 switch ((enum txnckpopts)optindex) {
00100 case TXNCKP_FORCE:
00101 flags = DB_FORCE;
00102 break;
00103 case TXNCKP_KB:
00104 if (i == objc) {
00105 Tcl_WrongNumArgs(interp, 2, objv,
00106 "?-kbyte kb?");
00107 result = TCL_ERROR;
00108 break;
00109 }
00110 result = Tcl_GetIntFromObj(interp, objv[i++], &kb);
00111 break;
00112 case TXNCKP_MIN:
00113 if (i == objc) {
00114 Tcl_WrongNumArgs(interp, 2, objv, "?-min min?");
00115 result = TCL_ERROR;
00116 break;
00117 }
00118 result = Tcl_GetIntFromObj(interp, objv[i++], &min);
00119 break;
00120 }
00121 }
00122 _debug_check();
00123 ret = envp->txn_checkpoint(envp, (u_int32_t)kb, (u_int32_t)min, flags);
00124 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00125 "txn checkpoint");
00126 return (result);
00127 }
00128
00129
00130
00131
00132
00133
00134
00135 int
00136 tcl_Txn(interp, objc, objv, envp, envip)
00137 Tcl_Interp *interp;
00138 int objc;
00139 Tcl_Obj *CONST objv[];
00140 DB_ENV *envp;
00141 DBTCL_INFO *envip;
00142 {
00143 static const char *txnopts[] = {
00144 #ifdef CONFIG_TEST
00145 "-lock_timeout",
00146 "-read_committed",
00147 "-read_uncommitted",
00148 "-txn_timeout",
00149 #endif
00150 "-nosync",
00151 "-nowait",
00152 "-parent",
00153 "-sync",
00154 "-wrnosync",
00155 NULL
00156 };
00157 enum txnopts {
00158 #ifdef CONFIG_TEST
00159 TXNLOCK_TIMEOUT,
00160 TXNREAD_COMMITTED,
00161 TXNREAD_UNCOMMITTED,
00162 TXNTIMEOUT,
00163 #endif
00164 TXNNOSYNC,
00165 TXNNOWAIT,
00166 TXNPARENT,
00167 TXNSYNC,
00168 TXNWRNOSYNC
00169 };
00170 DBTCL_INFO *ip;
00171 DB_TXN *parent;
00172 DB_TXN *txn;
00173 Tcl_Obj *res;
00174 u_int32_t flag;
00175 int i, optindex, result, ret;
00176 char *arg, msg[MSG_SIZE], newname[MSG_SIZE];
00177 #ifdef CONFIG_TEST
00178 db_timeout_t lk_time, tx_time;
00179 u_int32_t lk_timeflag, tx_timeflag;
00180 #endif
00181
00182 result = TCL_OK;
00183 memset(newname, 0, MSG_SIZE);
00184
00185 parent = NULL;
00186 flag = 0;
00187 #ifdef CONFIG_TEST
00188 COMPQUIET(tx_time, 0);
00189 COMPQUIET(lk_time, 0);
00190 lk_timeflag = tx_timeflag = 0;
00191 #endif
00192 i = 2;
00193 while (i < objc) {
00194 if (Tcl_GetIndexFromObj(interp, objv[i],
00195 txnopts, "option", TCL_EXACT, &optindex) != TCL_OK) {
00196 return (IS_HELP(objv[i]));
00197 }
00198 i++;
00199 switch ((enum txnopts)optindex) {
00200 #ifdef CONFIG_TEST
00201 case TXNLOCK_TIMEOUT:
00202 lk_timeflag = DB_SET_LOCK_TIMEOUT;
00203 goto get_timeout;
00204 case TXNTIMEOUT:
00205 tx_timeflag = DB_SET_TXN_TIMEOUT;
00206 get_timeout: if (i >= objc) {
00207 Tcl_WrongNumArgs(interp, 2, objv,
00208 "?-txn_timestamp time?");
00209 return (TCL_ERROR);
00210 }
00211 result = Tcl_GetLongFromObj(interp, objv[i++], (long *)
00212 ((enum txnopts)optindex == TXNLOCK_TIMEOUT ?
00213 &lk_time : &tx_time));
00214 if (result != TCL_OK)
00215 return (TCL_ERROR);
00216 break;
00217 case TXNREAD_COMMITTED:
00218 flag |= DB_READ_COMMITTED;
00219 break;
00220 case TXNREAD_UNCOMMITTED:
00221 flag |= DB_READ_UNCOMMITTED;
00222 break;
00223 #endif
00224 case TXNNOSYNC:
00225 flag |= DB_TXN_NOSYNC;
00226 break;
00227 case TXNNOWAIT:
00228 flag |= DB_TXN_NOWAIT;
00229 break;
00230 case TXNPARENT:
00231 if (i == objc) {
00232 Tcl_WrongNumArgs(interp, 2, objv,
00233 "?-parent txn?");
00234 result = TCL_ERROR;
00235 break;
00236 }
00237 arg = Tcl_GetStringFromObj(objv[i++], NULL);
00238 parent = NAME_TO_TXN(arg);
00239 if (parent == NULL) {
00240 snprintf(msg, MSG_SIZE,
00241 "Invalid parent txn: %s\n",
00242 arg);
00243 Tcl_SetResult(interp, msg, TCL_VOLATILE);
00244 return (TCL_ERROR);
00245 }
00246 break;
00247 case TXNSYNC:
00248 flag |= DB_TXN_SYNC;
00249 break;
00250 case TXNWRNOSYNC:
00251 flag |= DB_TXN_WRITE_NOSYNC;
00252 break;
00253 }
00254 }
00255 snprintf(newname, sizeof(newname), "%s.txn%d",
00256 envip->i_name, envip->i_envtxnid);
00257 ip = _NewInfo(interp, NULL, newname, I_TXN);
00258 if (ip == NULL) {
00259 Tcl_SetResult(interp, "Could not set up info",
00260 TCL_STATIC);
00261 return (TCL_ERROR);
00262 }
00263 _debug_check();
00264 ret = envp->txn_begin(envp, parent, &txn, flag);
00265 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00266 "txn");
00267 if (result == TCL_ERROR)
00268 _DeleteInfo(ip);
00269 else {
00270
00271
00272
00273
00274 envip->i_envtxnid++;
00275 if (parent)
00276 ip->i_parent = _PtrToInfo(parent);
00277 else
00278 ip->i_parent = envip;
00279 _SetInfoData(ip, txn);
00280 (void)Tcl_CreateObjCommand(interp, newname,
00281 (Tcl_ObjCmdProc *)txn_Cmd, (ClientData)txn, NULL);
00282 res = NewStringObj(newname, strlen(newname));
00283 Tcl_SetObjResult(interp, res);
00284 #ifdef CONFIG_TEST
00285 if (tx_timeflag != 0) {
00286 ret = txn->set_timeout(txn, tx_time, tx_timeflag);
00287 if (ret != 0) {
00288 result =
00289 _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00290 "set_timeout");
00291 _DeleteInfo(ip);
00292 }
00293 }
00294 if (lk_timeflag != 0) {
00295 ret = txn->set_timeout(txn, lk_time, lk_timeflag);
00296 if (ret != 0) {
00297 result =
00298 _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00299 "set_timeout");
00300 _DeleteInfo(ip);
00301 }
00302 }
00303 #endif
00304 }
00305 return (result);
00306 }
00307
00308
00309
00310
00311
00312
00313
00314 int
00315 tcl_TxnStat(interp, objc, objv, envp)
00316 Tcl_Interp *interp;
00317 int objc;
00318 Tcl_Obj *CONST objv[];
00319 DB_ENV *envp;
00320 {
00321 DBTCL_INFO *ip;
00322 DB_TXN_ACTIVE *p;
00323 DB_TXN_STAT *sp;
00324 Tcl_Obj *myobjv[2], *res, *thislist, *lsnlist;
00325 u_int32_t i;
00326 int myobjc, result, ret;
00327
00328 result = TCL_OK;
00329
00330
00331
00332 if (objc != 2) {
00333 Tcl_WrongNumArgs(interp, 2, objv, NULL);
00334 return (TCL_ERROR);
00335 }
00336 _debug_check();
00337 ret = envp->txn_stat(envp, &sp, 0);
00338 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00339 "txn stat");
00340 if (result == TCL_ERROR)
00341 return (result);
00342
00343
00344
00345
00346
00347 res = Tcl_NewObj();
00348
00349
00350
00351 MAKE_STAT_LIST("Region size", sp->st_regsize);
00352 MAKE_STAT_LSN("LSN of last checkpoint", &sp->st_last_ckp);
00353 MAKE_STAT_LIST("Time of last checkpoint", sp->st_time_ckp);
00354 MAKE_STAT_LIST("Last txn ID allocated", sp->st_last_txnid);
00355 MAKE_STAT_LIST("Max Txns", sp->st_maxtxns);
00356 MAKE_STAT_LIST("Number aborted txns", sp->st_naborts);
00357 MAKE_STAT_LIST("Number active txns", sp->st_nactive);
00358 MAKE_STAT_LIST("Maximum active txns", sp->st_maxnactive);
00359 MAKE_STAT_LIST("Number txns begun", sp->st_nbegins);
00360 MAKE_STAT_LIST("Number committed txns", sp->st_ncommits);
00361 MAKE_STAT_LIST("Number restored txns", sp->st_nrestores);
00362 MAKE_STAT_LIST("Number of region lock waits", sp->st_region_wait);
00363 MAKE_STAT_LIST("Number of region lock nowaits", sp->st_region_nowait);
00364 for (i = 0, p = sp->st_txnarray; i < sp->st_nactive; i++, p++)
00365 for (ip = LIST_FIRST(&__db_infohead); ip != NULL;
00366 ip = LIST_NEXT(ip, entries)) {
00367 if (ip->i_type != I_TXN)
00368 continue;
00369 if (ip->i_type == I_TXN &&
00370 (ip->i_txnp->id(ip->i_txnp) == p->txnid)) {
00371 MAKE_STAT_LSN(ip->i_name, &p->lsn);
00372 if (p->parentid != 0)
00373 MAKE_STAT_STRLIST("Parent",
00374 ip->i_parent->i_name);
00375 else
00376 MAKE_STAT_LIST("Parent", 0);
00377 break;
00378 }
00379 }
00380 Tcl_SetObjResult(interp, res);
00381 error:
00382 __os_ufree(envp, sp);
00383 return (result);
00384 }
00385
00386
00387
00388
00389
00390
00391
00392 int
00393 tcl_TxnTimeout(interp, objc, objv, envp)
00394 Tcl_Interp *interp;
00395 int objc;
00396 Tcl_Obj *CONST objv[];
00397 DB_ENV *envp;
00398 {
00399 long timeout;
00400 int result, ret;
00401
00402
00403
00404
00405 if (objc != 3) {
00406 Tcl_WrongNumArgs(interp, 2, objv, "?timeout?");
00407 return (TCL_ERROR);
00408 }
00409 result = Tcl_GetLongFromObj(interp, objv[2], &timeout);
00410 if (result != TCL_OK)
00411 return (result);
00412 _debug_check();
00413 ret = envp->set_timeout(envp, (u_int32_t)timeout, DB_SET_TXN_TIMEOUT);
00414 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00415 "lock timeout");
00416 return (result);
00417 }
00418
00419
00420
00421
00422
00423 static int
00424 txn_Cmd(clientData, interp, objc, objv)
00425 ClientData clientData;
00426 Tcl_Interp *interp;
00427 int objc;
00428 Tcl_Obj *CONST objv[];
00429 {
00430 static const char *txncmds[] = {
00431 #ifdef CONFIG_TEST
00432 "discard",
00433 "getname",
00434 "id",
00435 "prepare",
00436 "setname",
00437 #endif
00438 "abort",
00439 "commit",
00440 "getname",
00441 "setname",
00442 NULL
00443 };
00444 enum txncmds {
00445 #ifdef CONFIG_TEST
00446 TXNDISCARD,
00447 TXNGETNAME,
00448 TXNID,
00449 TXNPREPARE,
00450 TXNSETNAME,
00451 #endif
00452 TXNABORT,
00453 TXNCOMMIT
00454 };
00455 DBTCL_INFO *txnip;
00456 DB_TXN *txnp;
00457 Tcl_Obj *res;
00458 int cmdindex, result, ret;
00459 #ifdef CONFIG_TEST
00460 u_int8_t *gid, garray[DB_XIDDATASIZE];
00461 int length;
00462 const char *name;
00463 #endif
00464
00465 Tcl_ResetResult(interp);
00466 txnp = (DB_TXN *)clientData;
00467 txnip = _PtrToInfo((void *)txnp);
00468 result = TCL_OK;
00469 if (txnp == NULL) {
00470 Tcl_SetResult(interp, "NULL txn pointer", TCL_STATIC);
00471 return (TCL_ERROR);
00472 }
00473 if (txnip == NULL) {
00474 Tcl_SetResult(interp, "NULL txn info pointer", TCL_STATIC);
00475 return (TCL_ERROR);
00476 }
00477
00478
00479
00480
00481
00482 if (Tcl_GetIndexFromObj(interp,
00483 objv[1], txncmds, "command", TCL_EXACT, &cmdindex) != TCL_OK)
00484 return (IS_HELP(objv[1]));
00485
00486 res = NULL;
00487 switch ((enum txncmds)cmdindex) {
00488 #ifdef CONFIG_TEST
00489 case TXNDISCARD:
00490 if (objc != 2) {
00491 Tcl_WrongNumArgs(interp, 1, objv, NULL);
00492 return (TCL_ERROR);
00493 }
00494 _debug_check();
00495 ret = txnp->discard(txnp, 0);
00496 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00497 "txn discard");
00498 _TxnInfoDelete(interp, txnip);
00499 (void)Tcl_DeleteCommand(interp, txnip->i_name);
00500 _DeleteInfo(txnip);
00501 break;
00502 case TXNID:
00503 if (objc != 2) {
00504 Tcl_WrongNumArgs(interp, 1, objv, NULL);
00505 return (TCL_ERROR);
00506 }
00507 _debug_check();
00508 res = Tcl_NewIntObj((int)txnp->id(txnp));
00509 break;
00510 case TXNPREPARE:
00511 if (objc != 3) {
00512 Tcl_WrongNumArgs(interp, 1, objv, NULL);
00513 return (TCL_ERROR);
00514 }
00515 _debug_check();
00516 gid = (u_int8_t *)Tcl_GetByteArrayFromObj(objv[2], &length);
00517 memcpy(garray, gid, (size_t)length);
00518 ret = txnp->prepare(txnp, garray);
00519
00520
00521
00522
00523
00524
00525
00526 _TxnInfoDelete(interp, txnip);
00527 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00528 "txn prepare");
00529 break;
00530 case TXNGETNAME:
00531 if (objc != 2) {
00532 Tcl_WrongNumArgs(interp, 2, objv, NULL);
00533 return (TCL_ERROR);
00534 }
00535 _debug_check();
00536 ret = txnp->get_name(txnp, &name);
00537 if ((result = _ReturnSetup(
00538 interp, ret, DB_RETOK_STD(ret), "txn getname")) == TCL_OK)
00539 res = NewStringObj(name, strlen(name));
00540 break;
00541 case TXNSETNAME:
00542 if (objc != 3) {
00543 Tcl_WrongNumArgs(interp, 2, objv, "name");
00544 return (TCL_ERROR);
00545 }
00546 _debug_check();
00547 ret = txnp->set_name(txnp, Tcl_GetStringFromObj(objv[2], NULL));
00548 result =
00549 _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "setname");
00550 break;
00551 #endif
00552 case TXNABORT:
00553 if (objc != 2) {
00554 Tcl_WrongNumArgs(interp, 1, objv, NULL);
00555 return (TCL_ERROR);
00556 }
00557 _debug_check();
00558 ret = txnp->abort(txnp);
00559 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00560 "txn abort");
00561 _TxnInfoDelete(interp, txnip);
00562 (void)Tcl_DeleteCommand(interp, txnip->i_name);
00563 _DeleteInfo(txnip);
00564 break;
00565 case TXNCOMMIT:
00566 result = tcl_TxnCommit(interp, objc, objv, txnp, txnip);
00567 _TxnInfoDelete(interp, txnip);
00568 (void)Tcl_DeleteCommand(interp, txnip->i_name);
00569 _DeleteInfo(txnip);
00570 break;
00571 }
00572
00573
00574
00575
00576 if (result == TCL_OK && res)
00577 Tcl_SetObjResult(interp, res);
00578 return (result);
00579 }
00580
00581 static int
00582 tcl_TxnCommit(interp, objc, objv, txnp, txnip)
00583 Tcl_Interp *interp;
00584 int objc;
00585 Tcl_Obj *CONST objv[];
00586 DB_TXN *txnp;
00587 DBTCL_INFO *txnip;
00588 {
00589 static const char *commitopt[] = {
00590 "-nosync",
00591 "-sync",
00592 "-wrnosync",
00593 NULL
00594 };
00595 enum commitopt {
00596 COMNOSYNC,
00597 COMSYNC,
00598 COMWRNOSYNC
00599 };
00600 u_int32_t flag;
00601 int optindex, result, ret;
00602
00603 COMPQUIET(txnip, NULL);
00604
00605 result = TCL_OK;
00606 flag = 0;
00607 if (objc != 2 && objc != 3) {
00608 Tcl_WrongNumArgs(interp, 1, objv, NULL);
00609 return (TCL_ERROR);
00610 }
00611 if (objc == 3) {
00612 if (Tcl_GetIndexFromObj(interp, objv[2], commitopt,
00613 "option", TCL_EXACT, &optindex) != TCL_OK)
00614 return (IS_HELP(objv[2]));
00615 switch ((enum commitopt)optindex) {
00616 case COMSYNC:
00617 flag = DB_TXN_SYNC;
00618 break;
00619 case COMNOSYNC:
00620 flag = DB_TXN_NOSYNC;
00621 break;
00622 case COMWRNOSYNC:
00623 flag = DB_TXN_WRITE_NOSYNC;
00624 break;
00625 }
00626 }
00627
00628 _debug_check();
00629 ret = txnp->commit(txnp, flag);
00630 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00631 "txn commit");
00632 return (result);
00633 }
00634
00635 #ifdef CONFIG_TEST
00636
00637
00638
00639
00640
00641
00642 int
00643 tcl_TxnRecover(interp, objc, objv, envp, envip)
00644 Tcl_Interp *interp;
00645 int objc;
00646 Tcl_Obj *CONST objv[];
00647 DB_ENV *envp;
00648 DBTCL_INFO *envip;
00649 {
00650 #define DO_PREPLIST(count) \
00651 for (i = 0; i < count; i++) { \
00652 snprintf(newname, sizeof(newname), "%s.txn%d", \
00653 envip->i_name, envip->i_envtxnid); \
00654 ip = _NewInfo(interp, NULL, newname, I_TXN); \
00655 if (ip == NULL) { \
00656 Tcl_SetResult(interp, "Could not set up info", \
00657 TCL_STATIC); \
00658 return (TCL_ERROR); \
00659 } \
00660 envip->i_envtxnid++; \
00661 ip->i_parent = envip; \
00662 p = &prep[i]; \
00663 _SetInfoData(ip, p->txn); \
00664 (void)Tcl_CreateObjCommand(interp, newname, \
00665 (Tcl_ObjCmdProc *)txn_Cmd, (ClientData)p->txn, NULL); \
00666 result = _SetListElem(interp, res, newname, strlen(newname), \
00667 p->gid, DB_XIDDATASIZE); \
00668 if (result != TCL_OK) \
00669 goto error; \
00670 }
00671
00672 DBTCL_INFO *ip;
00673 DB_PREPLIST prep[DBTCL_PREP], *p;
00674 Tcl_Obj *res;
00675 long count, i;
00676 int result, ret;
00677 char newname[MSG_SIZE];
00678
00679 result = TCL_OK;
00680
00681
00682
00683 if (objc != 2) {
00684 Tcl_WrongNumArgs(interp, 2, objv, NULL);
00685 return (TCL_ERROR);
00686 }
00687 _debug_check();
00688 ret = envp->txn_recover(envp, prep, DBTCL_PREP, &count, DB_FIRST);
00689 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00690 "txn recover");
00691 if (result == TCL_ERROR)
00692 return (result);
00693 res = Tcl_NewObj();
00694 DO_PREPLIST(count);
00695
00696
00697
00698
00699
00700 while (count == DBTCL_PREP) {
00701 ret = envp->txn_recover(
00702 envp, prep, DBTCL_PREP, &count, DB_NEXT);
00703 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret),
00704 "txn recover");
00705 if (result == TCL_ERROR)
00706 return (result);
00707 DO_PREPLIST(count);
00708 }
00709 Tcl_SetObjResult(interp, res);
00710 error:
00711 return (result);
00712 }
00713 #endif