mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
347 lines
8.8 KiB
C
347 lines
8.8 KiB
C
/*-
|
|
* See the file LICENSE for redistribution information.
|
|
*
|
|
* Copyright (c) 1996, 1997, 1998
|
|
* Sleepycat Software. All rights reserved.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifndef lint
|
|
static const char sccsid[] = "@(#)tcl_txn.c 10.15 (Sleepycat) 5/31/98";
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* This file is divided up into 5 sets of functions:
|
|
* 1. The txn command and its support functions.
|
|
* 2. The txn_unlink command.
|
|
* 3. The txn manager widget commands.
|
|
* 4. The txn widget commands.
|
|
*/
|
|
|
|
#ifndef NO_SYSTEM_INCLUDES
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#endif
|
|
#include <tcl.h>
|
|
|
|
#include "db_int.h"
|
|
#include "dbtest.h"
|
|
#include "test_ext.h"
|
|
|
|
typedef struct _txn_data {
|
|
DB_ENV *env;
|
|
DB_TXNMGR *txnp;
|
|
} txn_data;
|
|
|
|
/*
|
|
* txn_cmd --
|
|
* Implements txn_open for dbtest. Txn_open creates a transaction
|
|
* manager and all the necessary files in the file system. It then creates
|
|
* a command that implements the other transaction functions.
|
|
*/
|
|
|
|
#define TXNMGR_USAGE "txn path flags mode [options]\n\toptions:\n"
|
|
|
|
int
|
|
txnmgr_cmd(notused, interp, argc, argv)
|
|
ClientData notused;
|
|
Tcl_Interp *interp;
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
static int mgr_number = 0;
|
|
DB_ENV *env;
|
|
DB_TXNMGR *mgrp;
|
|
txn_data *td;
|
|
int mode, tclint;
|
|
u_int32_t flags;
|
|
char mgrname[50];
|
|
|
|
notused = NULL;
|
|
debug_check();
|
|
|
|
/* Check number of arguments. */
|
|
USAGE_GE(argc, 4, TXNMGR_USAGE, DO_ENV);
|
|
if (Tcl_GetInt(interp, argv[2], &tclint) != TCL_OK)
|
|
return (TCL_ERROR);
|
|
flags = (u_int32_t)tclint;
|
|
|
|
/* Don't specify DB_THREAD if the architecture can't do spinlocks. */
|
|
#ifndef HAVE_SPINLOCKS
|
|
LF_CLR(DB_THREAD);
|
|
#endif
|
|
if (Tcl_GetInt(interp, argv[3], &mode) != TCL_OK)
|
|
return (TCL_ERROR);
|
|
|
|
/*
|
|
* Call txn_open.
|
|
* For now the recovery proc is NULL
|
|
*/
|
|
if (process_env_options(interp, argc, argv, &env)) {
|
|
Tcl_SetResult(interp, "NULL", TCL_STATIC);
|
|
return (TCL_OK);
|
|
}
|
|
|
|
if (F_ISSET(env, DB_ENV_STANDALONE))
|
|
mgrp = env->tx_info;
|
|
else if (txn_open(argv[1], flags, mode, env, &mgrp) != 0) {
|
|
Tcl_SetResult(interp, "NULL", TCL_STATIC);
|
|
return (TCL_OK);
|
|
} else
|
|
env->tx_info = mgrp;
|
|
|
|
td = (txn_data *)malloc(sizeof(txn_data));
|
|
if (td == NULL) {
|
|
if (!F_ISSET(env, DB_ENV_STANDALONE)) {
|
|
(void)db_appexit(env);
|
|
if (env->lk_conflicts)
|
|
free(env->lk_conflicts);
|
|
free(env);
|
|
}
|
|
Tcl_SetResult(interp, "txn_open: ", TCL_STATIC);
|
|
errno = ENOMEM;
|
|
Tcl_AppendResult(interp, Tcl_PosixError(interp), 0);
|
|
return (TCL_ERROR);
|
|
}
|
|
td->txnp = mgrp;
|
|
td->env = env;
|
|
|
|
/* Create new command name. */
|
|
sprintf(&mgrname[0], "mgr%d", mgr_number);
|
|
mgr_number++;
|
|
|
|
/* Create widget command. */
|
|
Tcl_CreateCommand(interp, mgrname, txnwidget_cmd, (int *)td, NULL);
|
|
Tcl_SetResult(interp, mgrname, TCL_VOLATILE);
|
|
return (TCL_OK);
|
|
}
|
|
|
|
/*
|
|
* txnunlink_cmd --
|
|
* Implements txn_unlink for dbtest.
|
|
*/
|
|
|
|
#define TXNUNLINK_USAGE "txn_unlink path force"
|
|
|
|
int
|
|
txnunlink_cmd(notused, interp, argc, argv)
|
|
ClientData notused;
|
|
Tcl_Interp *interp;
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int force;
|
|
|
|
notused = NULL;
|
|
debug_check();
|
|
|
|
USAGE_GE(argc, 3, TXNUNLINK_USAGE, 0);
|
|
|
|
if (Tcl_GetInt(interp, argv[2], &force) != TCL_OK)
|
|
return (TCL_ERROR);
|
|
|
|
if (txn_unlink(argv[1], force, NULL) != 0)
|
|
Tcl_SetResult(interp, "-1", TCL_STATIC);
|
|
else
|
|
Tcl_SetResult(interp, "0", TCL_STATIC);
|
|
return (TCL_OK);
|
|
}
|
|
|
|
/*
|
|
* txnwidget --
|
|
* This is that command that implements the txn widget. If we
|
|
* ever add new "methods" we add new widget commands here.
|
|
*/
|
|
#define TXNWIDGET_USAGE "mgrN option ?arg arg ...?"
|
|
#define TXNBEGIN_USAGE "mgrN begin [parent]"
|
|
#define TXNCHECK_USAGE "mgrN checkpoint [kbytes] [minutes]"
|
|
#define TXNCLOSE_USAGE "mgrN close"
|
|
#define TXNSTAT_USAGE "mgrN stat"
|
|
|
|
int
|
|
txnwidget_cmd(cd_mgr, interp, argc, argv)
|
|
ClientData cd_mgr;
|
|
Tcl_Interp *interp;
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
static int id = 0;
|
|
DB_ENV *env;
|
|
DB_TXN *txn, *parent;
|
|
DB_TXNMGR *mgr;
|
|
DB_TXN_STAT *statp;
|
|
Tcl_CmdInfo info;
|
|
u_int32_t i, kbytes, minutes;
|
|
int ret, tclint;
|
|
char *p, *statbuf, txnname[128];
|
|
|
|
debug_check();
|
|
|
|
mgr = ((txn_data *)cd_mgr)->txnp;
|
|
|
|
USAGE_GE(argc, 2, TXNWIDGET_USAGE, 0);
|
|
|
|
if (strcmp(argv[1], "close") == 0) {
|
|
USAGE(argc, 2, TXNCLOSE_USAGE, 0);
|
|
env = ((txn_data *)cd_mgr)->env;
|
|
if (!F_ISSET(env, DB_ENV_STANDALONE)) {
|
|
(void)db_appexit(env);
|
|
if (env->lk_conflicts)
|
|
free(env->lk_conflicts);
|
|
free(env);
|
|
}
|
|
ret = Tcl_DeleteCommand(interp, argv[0]);
|
|
if (ret)
|
|
Tcl_SetResult(interp, "-1", TCL_STATIC);
|
|
else
|
|
Tcl_SetResult(interp, "0", TCL_STATIC);
|
|
return (ret);
|
|
} else if (strcmp(argv[1], "begin") == 0) {
|
|
USAGE_GE(argc, 2, TXNBEGIN_USAGE, 0);
|
|
if (argc == 3) {
|
|
if (Tcl_GetCommandInfo(interp, argv[2], &info) == 0) {
|
|
Tcl_SetResult(interp,
|
|
"txn_begin: Invalid argument ", TCL_STATIC);
|
|
Tcl_AppendResult(interp, argv[2],
|
|
" not a transaction.", 0);
|
|
return (TCL_ERROR);
|
|
}
|
|
parent = (DB_TXN *)(info.clientData);
|
|
} else {
|
|
USAGE(argc, 2, TXNBEGIN_USAGE, 0);
|
|
parent = NULL;
|
|
}
|
|
|
|
if ((ret = txn_begin(mgr, parent, &txn)) != 0) {
|
|
Tcl_SetResult(interp, "NULL", TCL_STATIC);
|
|
return (TCL_OK);
|
|
}
|
|
|
|
sprintf(&txnname[0], "%s.txn%d", argv[0], id);
|
|
id++;
|
|
|
|
Tcl_CreateCommand(interp, txnname, txn_cmd, (int *)txn, NULL);
|
|
Tcl_SetResult(interp, txnname, TCL_VOLATILE);
|
|
} else if (strcmp(argv[1], "check") == 0) {
|
|
USAGE_GE(argc, 2, TXNCHECK_USAGE, 0);
|
|
if (argc <= 2 ||
|
|
(Tcl_GetInt(interp, argv[2], &tclint) != TCL_OK))
|
|
tclint = 0;
|
|
kbytes = (u_int32_t)tclint;
|
|
|
|
if (argc <= 3 ||
|
|
(Tcl_GetInt(interp, argv[3], &tclint) != TCL_OK))
|
|
tclint = 0;
|
|
minutes = (u_int32_t)tclint;
|
|
|
|
if ((ret = txn_checkpoint(mgr, kbytes, minutes)) != 0) {
|
|
Tcl_SetResult(interp, "txn_checkpoint: ", TCL_STATIC);
|
|
if (ret > 0) {
|
|
errno = ret;
|
|
Tcl_AppendResult(interp,
|
|
Tcl_PosixError(interp), 0);
|
|
} else
|
|
Tcl_AppendResult(interp,
|
|
"Checkpoint pending", 0);
|
|
return (TCL_ERROR);
|
|
}
|
|
} else if (strcmp(argv[1], "stat") == 0) {
|
|
USAGE_GE(argc, 2, TXNSTAT_USAGE, 0);
|
|
if ((ret = txn_stat(mgr, &statp, NULL)) != 0) {
|
|
errno = ret;
|
|
Tcl_AppendResult(interp, Tcl_PosixError(interp), 0);
|
|
return (TCL_ERROR);
|
|
}
|
|
/*
|
|
* Allocate space for return message. Assume every u_int32_t
|
|
* will be printed out maximum size and with a label of 12
|
|
* bytes. Then figure that we need 3 u_int32_t's for each
|
|
* active transaction. Leave plenty of room for newlines
|
|
* and space.
|
|
*/
|
|
statbuf = (char *)malloc(sizeof(DB_TXN_STAT) / sizeof(u_int32_t)
|
|
+ (statp->st_nactive * 3) * (24 + 16));
|
|
sprintf(statbuf, "%s %lx\n%s [%lu, %lu]\n%s %lu\n%s %lu\n%s %lu\n%s %lu\n%s %lu\n%s [%lu, %lu]\n",
|
|
"last txn id ", (unsigned long)statp->st_last_txnid,
|
|
"last ckp ", (unsigned long)statp->st_last_ckp.file,
|
|
(unsigned long)statp->st_last_ckp.offset,
|
|
"max txns ", (unsigned long)statp->st_maxtxns,
|
|
"Aborted txns ", (unsigned long)statp->st_naborts,
|
|
"Begun txns ", (unsigned long)statp->st_nbegins,
|
|
"Commited txns", (unsigned long)statp->st_ncommits,
|
|
"Active txns ", (unsigned long)statp->st_nactive,
|
|
"Pending ckp ", (unsigned long)statp->st_pending_ckp.file,
|
|
(unsigned long)statp->st_pending_ckp.offset);
|
|
Tcl_AppendResult(interp, "last ckp time ",
|
|
ctime(&statp->st_time_ckp), NULL);
|
|
p = statbuf + strlen(statbuf);
|
|
for (i = 0; i < statp->st_nactive; i++) {
|
|
sprintf(p, "%lx: %lu/%lu\n",
|
|
(unsigned long)statp->st_txnarray[i].txnid,
|
|
(unsigned long)statp->st_txnarray[i].lsn.file,
|
|
(unsigned long)statp->st_txnarray[i].lsn.offset);
|
|
p += strlen(p);
|
|
}
|
|
Tcl_SetResult(interp, statbuf, TCL_DYNAMIC);
|
|
} else {
|
|
Tcl_SetResult(interp, TXNWIDGET_USAGE, TCL_STATIC);
|
|
return (TCL_ERROR);
|
|
}
|
|
return (TCL_OK);
|
|
}
|
|
|
|
#define TXN_USAGE "mgrN.txnM cmd ?arg arg ...?"
|
|
#define TXNCOMMIT_USAGE "mgrN.txnM commit"
|
|
#define TXNABORT_USAGE "mgrN.txnM abort"
|
|
#define TXNID_USAGE "mgrN.txnM id"
|
|
#define TXNPREP_USAGE "mgrN.txnM prep"
|
|
int
|
|
txn_cmd(cd_txn, interp, argc, argv)
|
|
ClientData cd_txn;
|
|
Tcl_Interp *interp;
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
DB_TXN *txn;
|
|
int ret;
|
|
char idbuf[16];
|
|
|
|
debug_check();
|
|
|
|
USAGE_GE(argc, 2, TXN_USAGE, 0);
|
|
|
|
txn = (DB_TXN *)cd_txn;
|
|
|
|
if (strcmp(argv[1], "commit") == 0) {
|
|
USAGE(argc, 2, TXNCOMMIT_USAGE, 0);
|
|
Tcl_DeleteCommand(interp, argv[0]);
|
|
ret = txn_commit(txn);
|
|
} else if (strcmp(argv[1], "abort") == 0) {
|
|
USAGE(argc, 2, TXNABORT_USAGE, 0);
|
|
Tcl_DeleteCommand(interp, argv[0]);
|
|
ret = txn_abort(txn);
|
|
} else if (strcmp(argv[1], "id") == 0) {
|
|
USAGE(argc, 2, TXNID_USAGE, 0);
|
|
ret = txn_id(txn);
|
|
sprintf(idbuf, "%d", ret);
|
|
Tcl_SetResult(interp, idbuf, TCL_VOLATILE);
|
|
return (TCL_OK);
|
|
} else if (strcmp(argv[1], "prepare") == 0) {
|
|
USAGE(argc, 2, TXNPREP_USAGE, 0);
|
|
ret = txn_prepare(txn);
|
|
} else {
|
|
Tcl_SetResult(interp, TXN_USAGE, TCL_STATIC);
|
|
return (TCL_ERROR);
|
|
}
|
|
if (ret)
|
|
Tcl_SetResult(interp, "-1", TCL_STATIC);
|
|
else
|
|
Tcl_SetResult(interp, "0", TCL_STATIC);
|
|
return (TCL_OK);
|
|
}
|