1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2004,2008 Oracle. All rights reserved. 5 * 6 * $Id: tcl_seq.c,v 12.11 2008/01/08 20:58:52 bostic Exp $ 7 */ 8 9#include "db_config.h" 10#ifdef HAVE_64BIT_TYPES 11 12#include "db_int.h" 13#ifdef HAVE_SYSTEM_INCLUDE_FILES 14#include <tcl.h> 15#endif 16#include "dbinc/tcl_db.h" 17#include "dbinc_auto/sequence_ext.h" 18 19/* 20 * Prototypes for procedures defined later in this file: 21 */ 22static int tcl_SeqClose __P((Tcl_Interp *, 23 int, Tcl_Obj * CONST*, DB_SEQUENCE *, DBTCL_INFO *)); 24static int tcl_SeqGet __P((Tcl_Interp *, 25 int, Tcl_Obj * CONST*, DB_SEQUENCE *)); 26static int tcl_SeqRemove __P((Tcl_Interp *, 27 int, Tcl_Obj * CONST*, DB_SEQUENCE *, DBTCL_INFO *)); 28static int tcl_SeqStat __P((Tcl_Interp *, 29 int, Tcl_Obj * CONST*, DB_SEQUENCE *)); 30static int tcl_SeqGetFlags __P((Tcl_Interp *, 31 int, Tcl_Obj * CONST*, DB_SEQUENCE *)); 32 33/* 34 * 35 * PUBLIC: int seq_Cmd __P((ClientData, Tcl_Interp *, int, Tcl_Obj * CONST*)); 36 * 37 * seq_Cmd -- 38 * Implements the "seq" widget. 39 */ 40int 41seq_Cmd(clientData, interp, objc, objv) 42 ClientData clientData; /* SEQ handle */ 43 Tcl_Interp *interp; /* Interpreter */ 44 int objc; /* How many arguments? */ 45 Tcl_Obj *CONST objv[]; /* The argument objects */ 46{ 47 static const char *seqcmds[] = { 48 "close", 49 "get", 50 "get_cachesize", 51 "get_db", 52 "get_flags", 53 "get_key", 54 "get_range", 55 "remove", 56 "stat", 57 NULL 58 }; 59 enum seqcmds { 60 SEQCLOSE, 61 SEQGET, 62 SEQGETCACHESIZE, 63 SEQGETDB, 64 SEQGETFLAGS, 65 SEQGETKEY, 66 SEQGETRANGE, 67 SEQREMOVE, 68 SEQSTAT 69 }; 70 DB *dbp; 71 DBT key; 72 DBTCL_INFO *dbip, *ip; 73 DB_SEQUENCE *seq; 74 Tcl_Obj *myobjv[2], *res; 75 db_seq_t min, max; 76 int cmdindex, ncache, result, ret; 77 78 Tcl_ResetResult(interp); 79 seq = (DB_SEQUENCE *)clientData; 80 result = TCL_OK; 81 dbip = NULL; 82 if (objc <= 1) { 83 Tcl_WrongNumArgs(interp, 1, objv, "command cmdargs"); 84 return (TCL_ERROR); 85 } 86 if (seq == NULL) { 87 Tcl_SetResult(interp, "NULL sequence pointer", TCL_STATIC); 88 return (TCL_ERROR); 89 } 90 91 ip = _PtrToInfo((void *)seq); 92 if (ip == NULL) { 93 Tcl_SetResult(interp, "NULL info pointer", TCL_STATIC); 94 return (TCL_ERROR); 95 } 96 97 /* 98 * Get the command name index from the object based on the dbcmds 99 * defined above. 100 */ 101 if (Tcl_GetIndexFromObj(interp, 102 objv[1], seqcmds, "command", TCL_EXACT, &cmdindex) != TCL_OK) 103 return (IS_HELP(objv[1])); 104 105 res = NULL; 106 switch ((enum seqcmds)cmdindex) { 107 case SEQGETRANGE: 108 ret = seq->get_range(seq, &min, &max); 109 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), 110 "sequence get_range")) == TCL_OK) { 111 myobjv[0] = Tcl_NewWideIntObj(min); 112 myobjv[1] = Tcl_NewWideIntObj(max); 113 res = Tcl_NewListObj(2, myobjv); 114 } 115 break; 116 case SEQCLOSE: 117 result = tcl_SeqClose(interp, objc, objv, seq, ip); 118 break; 119 case SEQREMOVE: 120 result = tcl_SeqRemove(interp, objc, objv, seq, ip); 121 break; 122 case SEQGET: 123 result = tcl_SeqGet(interp, objc, objv, seq); 124 break; 125 case SEQSTAT: 126 result = tcl_SeqStat(interp, objc, objv, seq); 127 break; 128 case SEQGETCACHESIZE: 129 if (objc != 2) { 130 Tcl_WrongNumArgs(interp, 1, objv, NULL); 131 return (TCL_ERROR); 132 } 133 ret = seq->get_cachesize(seq, &ncache); 134 if ((result = _ReturnSetup(interp, ret, 135 DB_RETOK_STD(ret), "sequence get_cachesize")) == TCL_OK) 136 res = Tcl_NewIntObj(ncache); 137 break; 138 case SEQGETDB: 139 if (objc != 2) { 140 Tcl_WrongNumArgs(interp, 1, objv, NULL); 141 return (TCL_ERROR); 142 } 143 ret = seq->get_db(seq, &dbp); 144 if (ret == 0 && (dbip = _PtrToInfo((void *)dbp)) == NULL) { 145 Tcl_SetResult(interp, 146 "NULL db info pointer", TCL_STATIC); 147 return (TCL_ERROR); 148 } 149 150 if ((result = _ReturnSetup(interp, ret, 151 DB_RETOK_STD(ret), "sequence get_db")) == TCL_OK) 152 res = NewStringObj(dbip->i_name, strlen(dbip->i_name)); 153 break; 154 case SEQGETKEY: 155 if (objc != 2) { 156 Tcl_WrongNumArgs(interp, 1, objv, NULL); 157 return (TCL_ERROR); 158 } 159 ret = seq->get_key(seq, &key); 160 if ((result = _ReturnSetup(interp, ret, 161 DB_RETOK_STD(ret), "sequence get_key")) == TCL_OK) 162 res = Tcl_NewByteArrayObj( 163 (u_char *)key.data, (int)key.size); 164 break; 165 case SEQGETFLAGS: 166 result = tcl_SeqGetFlags(interp, objc, objv, seq); 167 break; 168 } 169 170 /* 171 * Only set result if we have a res. Otherwise, lower functions have 172 * already done so. 173 */ 174 if (result == TCL_OK && res) 175 Tcl_SetObjResult(interp, res); 176 return (result); 177} 178 179/* 180 * tcl_db_stat -- 181 */ 182static int 183tcl_SeqStat(interp, objc, objv, seq) 184 Tcl_Interp *interp; /* Interpreter */ 185 int objc; /* How many arguments? */ 186 Tcl_Obj *CONST objv[]; /* The argument objects */ 187 DB_SEQUENCE *seq; /* Database pointer */ 188{ 189 DB_SEQUENCE_STAT *sp; 190 u_int32_t flag; 191 Tcl_Obj *res, *flaglist, *myobjv[2]; 192 int result, ret; 193 char *arg; 194 195 result = TCL_OK; 196 flag = 0; 197 198 if (objc > 3) { 199 Tcl_WrongNumArgs(interp, 2, objv, "?-clear?"); 200 return (TCL_ERROR); 201 } 202 203 if (objc == 3) { 204 arg = Tcl_GetStringFromObj(objv[2], NULL); 205 if (strcmp(arg, "-clear") == 0) 206 flag = DB_STAT_CLEAR; 207 else { 208 Tcl_SetResult(interp, 209 "db stat: unknown arg", TCL_STATIC); 210 return (TCL_ERROR); 211 } 212 } 213 214 _debug_check(); 215 ret = seq->stat(seq, &sp, flag); 216 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "db stat"); 217 if (result == TCL_ERROR) 218 return (result); 219 220 res = Tcl_NewObj(); 221 MAKE_STAT_LIST("Wait", sp->st_wait); 222 MAKE_STAT_LIST("No wait", sp->st_nowait); 223 MAKE_WSTAT_LIST("Current", sp->st_current); 224 MAKE_WSTAT_LIST("Cached", sp->st_value); 225 MAKE_WSTAT_LIST("Max Cached", sp->st_last_value); 226 MAKE_WSTAT_LIST("Min", sp->st_min); 227 MAKE_WSTAT_LIST("Max", sp->st_max); 228 MAKE_STAT_LIST("Cache size", sp->st_cache_size); 229 /* 230 * Construct a {name {flag1 flag2 ... flagN}} list for the 231 * seq flags. 232 */ 233 myobjv[0] = NewStringObj("Flags", strlen("Flags")); 234 myobjv[1] = 235 _GetFlagsList(interp, sp->st_flags, __db_get_seq_flags_fn()); 236 flaglist = Tcl_NewListObj(2, myobjv); 237 if (flaglist == NULL) { 238 result = TCL_ERROR; 239 goto error; 240 } 241 if ((result = 242 Tcl_ListObjAppendElement(interp, res, flaglist)) != TCL_OK) 243 goto error; 244 245 Tcl_SetObjResult(interp, res); 246 247error: __os_ufree(seq->seq_dbp->env, sp); 248 return (result); 249} 250 251/* 252 * tcl_db_close -- 253 */ 254static int 255tcl_SeqClose(interp, objc, objv, seq, ip) 256 Tcl_Interp *interp; /* Interpreter */ 257 int objc; /* How many arguments? */ 258 Tcl_Obj *CONST objv[]; /* The argument objects */ 259 DB_SEQUENCE *seq; /* Database pointer */ 260 DBTCL_INFO *ip; /* Info pointer */ 261{ 262 int result, ret; 263 264 result = TCL_OK; 265 if (objc > 2) { 266 Tcl_WrongNumArgs(interp, 2, objv, ""); 267 return (TCL_ERROR); 268 } 269 270 _DeleteInfo(ip); 271 _debug_check(); 272 273 ret = seq->close(seq, 0); 274 result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), "sequence close"); 275 return (result); 276} 277 278/* 279 * tcl_SeqGet -- 280 */ 281static int 282tcl_SeqGet(interp, objc, objv, seq) 283 Tcl_Interp *interp; /* Interpreter */ 284 int objc; /* How many arguments? */ 285 Tcl_Obj *CONST objv[]; /* The argument objects */ 286 DB_SEQUENCE *seq; /* Sequence pointer */ 287{ 288 static const char *seqgetopts[] = { 289 "-nosync", 290 "-txn", 291 NULL 292 }; 293 enum seqgetopts { 294 SEQGET_NOSYNC, 295 SEQGET_TXN 296 }; 297 DB_TXN *txn; 298 Tcl_Obj *res; 299 db_seq_t value; 300 u_int32_t aflag, delta; 301 int i, end, optindex, result, ret; 302 char *arg, msg[MSG_SIZE]; 303 304 result = TCL_OK; 305 txn = NULL; 306 aflag = 0; 307 308 if (objc < 3) { 309 Tcl_WrongNumArgs(interp, 2, objv, "?-args? delta"); 310 return (TCL_ERROR); 311 } 312 313 /* 314 * Get the command name index from the object based on the options 315 * defined above. 316 */ 317 i = 2; 318 end = objc; 319 while (i < end) { 320 if (Tcl_GetIndexFromObj(interp, objv[i], seqgetopts, "option", 321 TCL_EXACT, &optindex) != TCL_OK) { 322 arg = Tcl_GetStringFromObj(objv[i], NULL); 323 if (arg[0] == '-') { 324 result = IS_HELP(objv[i]); 325 goto out; 326 } else 327 Tcl_ResetResult(interp); 328 break; 329 } 330 i++; 331 switch ((enum seqgetopts)optindex) { 332 case SEQGET_NOSYNC: 333 aflag |= DB_TXN_NOSYNC; 334 break; 335 case SEQGET_TXN: 336 if (i >= end) { 337 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?"); 338 result = TCL_ERROR; 339 break; 340 } 341 arg = Tcl_GetStringFromObj(objv[i++], NULL); 342 txn = NAME_TO_TXN(arg); 343 if (txn == NULL) { 344 snprintf(msg, MSG_SIZE, 345 "Get: Invalid txn: %s\n", arg); 346 Tcl_SetResult(interp, msg, TCL_VOLATILE); 347 result = TCL_ERROR; 348 } 349 break; 350 } /* switch */ 351 if (result != TCL_OK) 352 break; 353 } 354 if (result != TCL_OK) 355 goto out; 356 357 if (i != objc - 1) { 358 Tcl_SetResult(interp, 359 "Wrong number of key/data given\n", TCL_STATIC); 360 result = TCL_ERROR; 361 goto out; 362 } 363 364 if ((result = _GetUInt32(interp, objv[objc - 1], &delta)) != TCL_OK) 365 goto out; 366 367 ret = seq->get(seq, txn, (int32_t)delta, &value, aflag); 368 result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret), "sequence get"); 369 if (ret == 0) { 370 res = Tcl_NewWideIntObj((Tcl_WideInt)value); 371 Tcl_SetObjResult(interp, res); 372 } 373out: 374 return (result); 375} 376/* 377 */ 378static int 379tcl_SeqRemove(interp, objc, objv, seq, ip) 380 Tcl_Interp *interp; /* Interpreter */ 381 int objc; /* How many arguments? */ 382 Tcl_Obj *CONST objv[]; /* The argument objects */ 383 DB_SEQUENCE *seq; /* Sequence pointer */ 384 DBTCL_INFO *ip; /* Info pointer */ 385{ 386 static const char *seqgetopts[] = { 387 "-nosync", 388 "-txn", 389 NULL 390 }; 391 enum seqgetopts { 392 SEQGET_NOSYNC, 393 SEQGET_TXN 394 }; 395 DB_TXN *txn; 396 u_int32_t aflag; 397 int i, end, optindex, result, ret; 398 char *arg, msg[MSG_SIZE]; 399 400 result = TCL_OK; 401 txn = NULL; 402 aflag = 0; 403 404 _DeleteInfo(ip); 405 406 if (objc < 2) { 407 Tcl_WrongNumArgs(interp, 2, objv, "?-args?"); 408 return (TCL_ERROR); 409 } 410 411 /* 412 * Get the command name index from the object based on the options 413 * defined above. 414 */ 415 i = 2; 416 end = objc; 417 while (i < end) { 418 if (Tcl_GetIndexFromObj(interp, objv[i], seqgetopts, "option", 419 TCL_EXACT, &optindex) != TCL_OK) { 420 arg = Tcl_GetStringFromObj(objv[i], NULL); 421 if (arg[0] == '-') { 422 result = IS_HELP(objv[i]); 423 goto out; 424 } else 425 Tcl_ResetResult(interp); 426 break; 427 } 428 i++; 429 switch ((enum seqgetopts)optindex) { 430 case SEQGET_NOSYNC: 431 aflag |= DB_TXN_NOSYNC; 432 break; 433 case SEQGET_TXN: 434 if (i >= end) { 435 Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?"); 436 result = TCL_ERROR; 437 break; 438 } 439 arg = Tcl_GetStringFromObj(objv[i++], NULL); 440 txn = NAME_TO_TXN(arg); 441 if (txn == NULL) { 442 snprintf(msg, MSG_SIZE, 443 "Remove: Invalid txn: %s\n", arg); 444 Tcl_SetResult(interp, msg, TCL_VOLATILE); 445 result = TCL_ERROR; 446 } 447 break; 448 } /* switch */ 449 if (result != TCL_OK) 450 break; 451 } 452 if (result != TCL_OK) 453 goto out; 454 455 ret = seq->remove(seq, txn, aflag); 456 result = _ReturnSetup(interp, 457 ret, DB_RETOK_DBGET(ret), "sequence remove"); 458out: 459 return (result); 460} 461 462/* 463 * tcl_SeqGetFlags -- 464 */ 465static int 466tcl_SeqGetFlags(interp, objc, objv, seq) 467 Tcl_Interp *interp; /* Interpreter */ 468 int objc; /* How many arguments? */ 469 Tcl_Obj *CONST objv[]; /* The argument objects */ 470 DB_SEQUENCE *seq; /* Sequence pointer */ 471{ 472 int i, ret, result; 473 u_int32_t flags; 474 char buf[512]; 475 Tcl_Obj *res; 476 477 static const struct { 478 u_int32_t flag; 479 char *arg; 480 } seq_flags[] = { 481 { DB_SEQ_INC, "-inc" }, 482 { DB_SEQ_DEC, "-dec" }, 483 { DB_SEQ_WRAP, "-wrap" }, 484 { 0, NULL } 485 }; 486 487 if (objc != 2) { 488 Tcl_WrongNumArgs(interp, 1, objv, NULL); 489 return (TCL_ERROR); 490 } 491 492 ret = seq->get_flags(seq, &flags); 493 if ((result = _ReturnSetup(interp, ret, DB_RETOK_STD(ret), 494 "db get_flags")) == TCL_OK) { 495 buf[0] = '\0'; 496 497 for (i = 0; seq_flags[i].flag != 0; i++) 498 if (LF_ISSET(seq_flags[i].flag)) { 499 if (strlen(buf) > 0) 500 (void)strncat(buf, " ", sizeof(buf)); 501 (void)strncat( 502 buf, seq_flags[i].arg, sizeof(buf)); 503 } 504 505 res = NewStringObj(buf, strlen(buf)); 506 Tcl_SetObjResult(interp, res); 507 } 508 509 return (result); 510} 511#endif /* HAVE_64BIT_TYPES */ 512