📄 tcl_db.c
字号:
/* * If it is a QUEUE or RECNO database, the key is a record number * and must be setup up to contain a db_recno_t. Otherwise the * key is a "string". */ (void)dbp->get_type(dbp, &type); /* * We need to determine where the end of required args are. If we * are using a QUEUE/RECNO db and -append, then there is just one * req arg (data). Otherwise there are two (key data). * * We preparse the list to determine this since we need to know * to properly check # of args for other options below. */ end = objc - 2; if (type == DB_QUEUE || type == DB_RECNO) { i = 2; while (i < objc - 1) { if (Tcl_GetIndexFromObj(interp, objv[i++], dbputapp, "option", TCL_EXACT, &optindex) != TCL_OK) continue; switch ((enum dbputapp)optindex) { case DBPUT_APPEND0: end = objc - 1; break; } } } Tcl_ResetResult(interp); /* * Get the command name index from the object based on the options * defined above. */ i = 2; auto_commit = 0; while (i < end) { if (Tcl_GetIndexFromObj(interp, objv[i], dbputopts, "option", TCL_EXACT, &optindex) != TCL_OK) return (IS_HELP(objv[i])); i++; switch ((enum dbputopts)optindex) {#if CONFIG_TEST case DBGET_NODUPDATA: FLAG_CHECK(flag); flag = DB_NODUPDATA; break;#endif case DBPUT_TXN: if (i > (end - 1)) { Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?"); result = TCL_ERROR; break; } arg = Tcl_GetStringFromObj(objv[i++], NULL); txn = NAME_TO_TXN(arg); if (txn == NULL) { snprintf(msg, MSG_SIZE, "Put: Invalid txn: %s\n", arg); Tcl_SetResult(interp, msg, TCL_VOLATILE); result = TCL_ERROR; } break; case DBPUT_AUTO_COMMIT: auto_commit = 1; break; case DBPUT_APPEND: FLAG_CHECK(flag); flag = DB_APPEND; break; case DBPUT_NOOVER: FLAG_CHECK(flag); flag = DB_NOOVERWRITE; break; case DBPUT_PART: if (i > (end - 1)) { Tcl_WrongNumArgs(interp, 2, objv, "?-partial {offset length}?"); result = TCL_ERROR; break; } /* * Get sublist as {offset length} */ result = Tcl_ListObjGetElements(interp, objv[i++], &elemc, &elemv); if (elemc != 2) { Tcl_SetResult(interp, "List must be {offset length}", TCL_STATIC); result = TCL_ERROR; break; } data.flags = DB_DBT_PARTIAL; result = _GetUInt32(interp, elemv[0], &data.doff); if (result != TCL_OK) break; result = _GetUInt32(interp, elemv[1], &data.dlen); /* * NOTE: We don't check result here because all we'd * do is break anyway, and we are doing that. If you * add code here, you WILL need to add the check * for result. (See the check for save.doff, a few * lines above and copy that.) */ break; } if (result != TCL_OK) break; } if (auto_commit) flag |= DB_AUTO_COMMIT; if (result == TCL_ERROR) return (result); /* * If we are a recno db and we are NOT using append, then the 2nd * last arg is the key. */ if (type == DB_QUEUE || type == DB_RECNO) { key.data = &recno; key.ulen = key.size = sizeof(db_recno_t); key.flags = DB_DBT_USERMEM; if (flag == DB_APPEND) recno = 0; else { result = _GetUInt32(interp, objv[objc-2], &recno); if (result != TCL_OK) return (result); } } else { ret = _CopyObjBytes(interp, objv[objc-2], &ktmp, &key.size, &freekey); if (ret != 0) { result = _ReturnSetup(interp, ret, DB_RETOK_DBPUT(ret), "db put"); return (result); } key.data = ktmp; } ret = _CopyObjBytes(interp, objv[objc-1], &dtmp, &data.size, &freedata); if (ret != 0) { result = _ReturnSetup(interp, ret, DB_RETOK_DBPUT(ret), "db put"); goto out; } data.data = dtmp; _debug_check(); ret = dbp->put(dbp, txn, &key, &data, flag); result = _ReturnSetup(interp, ret, DB_RETOK_DBPUT(ret), "db put"); if (ret == 0 && (type == DB_RECNO || type == DB_QUEUE) && flag == DB_APPEND) { res = Tcl_NewLongObj((long)recno); Tcl_SetObjResult(interp, res); }out: if (freedata) (void)__os_free(dbp->dbenv, dtmp); if (freekey) (void)__os_free(dbp->dbenv, ktmp); return (result);}/* * tcl_db_get -- */static inttcl_DbGet(interp, objc, objv, dbp, ispget) Tcl_Interp *interp; /* Interpreter */ int objc; /* How many arguments? */ Tcl_Obj *CONST objv[]; /* The argument objects */ DB *dbp; /* Database pointer */ int ispget; /* 1 for pget, 0 for get */{ static char *dbgetopts[] = {#if CONFIG_TEST "-dirty", "-multi",#endif "-consume", "-consume_wait", "-get_both", "-glob", "-partial", "-recno", "-rmw", "-txn", "--", NULL }; enum dbgetopts {#if CONFIG_TEST DBGET_DIRTY, DBGET_MULTI,#endif DBGET_CONSUME, DBGET_CONSUME_WAIT, DBGET_BOTH, DBGET_GLOB, DBGET_PART, DBGET_RECNO, DBGET_RMW, DBGET_TXN, DBGET_ENDARG }; DBC *dbc; DBT key, pkey, data, save; DBTYPE type; DB_TXN *txn; Tcl_Obj **elemv, *retlist; void *dtmp, *ktmp; u_int32_t flag, cflag, isdup, mflag, rmw; int bufsize, elemc, end, endarg, freekey, freedata, i; int optindex, result, ret, useglob, useprecno, userecno; char *arg, *pattern, *prefix, msg[MSG_SIZE]; db_recno_t precno, recno; result = TCL_OK; freekey = freedata = 0; cflag = endarg = flag = mflag = rmw = 0; useglob = userecno = useprecno = 0; txn = NULL; pattern = prefix = NULL; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "?-args? key"); return (TCL_ERROR); } memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); memset(&save, 0, sizeof(save)); /* For the primary key in a pget call. */ memset(&pkey, 0, sizeof(pkey)); /* * Get the command name index from the object based on the options * defined above. */ i = 2; (void)dbp->get_type(dbp, &type); end = objc; while (i < end) { if (Tcl_GetIndexFromObj(interp, objv[i], dbgetopts, "option", TCL_EXACT, &optindex) != TCL_OK) { arg = Tcl_GetStringFromObj(objv[i], NULL); if (arg[0] == '-') { result = IS_HELP(objv[i]); goto out; } else Tcl_ResetResult(interp); break; } i++; switch ((enum dbgetopts)optindex) {#if CONFIG_TEST case DBGET_DIRTY: rmw |= DB_DIRTY_READ; break; case DBGET_MULTI: mflag |= DB_MULTIPLE; result = Tcl_GetIntFromObj(interp, objv[i], &bufsize); if (result != TCL_OK) goto out; i++; break;#endif case DBGET_BOTH: /* * Change 'end' and make sure we aren't already past * the new end. */ if (i > objc - 2) { Tcl_WrongNumArgs(interp, 2, objv, "?-get_both key data?"); result = TCL_ERROR; break; } end = objc - 2; FLAG_CHECK(flag); flag = DB_GET_BOTH; break; case DBGET_TXN: if (i >= end) { Tcl_WrongNumArgs(interp, 2, objv, "?-txn id?"); result = TCL_ERROR; break; } arg = Tcl_GetStringFromObj(objv[i++], NULL); txn = NAME_TO_TXN(arg); if (txn == NULL) { snprintf(msg, MSG_SIZE, "Get: Invalid txn: %s\n", arg); Tcl_SetResult(interp, msg, TCL_VOLATILE); result = TCL_ERROR; } break; case DBGET_GLOB: useglob = 1; end = objc - 1; break; case DBGET_CONSUME: FLAG_CHECK(flag); flag = DB_CONSUME; break; case DBGET_CONSUME_WAIT: FLAG_CHECK(flag); flag = DB_CONSUME_WAIT; break; case DBGET_RECNO: end = objc - 1; userecno = 1; if (type != DB_RECNO && type != DB_QUEUE) { FLAG_CHECK(flag); flag = DB_SET_RECNO; } break; case DBGET_RMW: rmw |= DB_RMW; break; case DBGET_PART: end = objc - 1; if (i == end) { Tcl_WrongNumArgs(interp, 2, objv, "?-partial {offset length}?"); result = TCL_ERROR; break; } /* * Get sublist as {offset length} */ result = Tcl_ListObjGetElements(interp, objv[i++], &elemc, &elemv); if (elemc != 2) { Tcl_SetResult(interp, "List must be {offset length}", TCL_STATIC); result = TCL_ERROR; break; } save.flags = DB_DBT_PARTIAL; result = _GetUInt32(interp, elemv[0], &save.doff); if (result != TCL_OK) break; result = _GetUInt32(interp, elemv[1], &save.dlen); /* * NOTE: We don't check result here because all we'd * do is break anyway, and we are doing that. If you * add code here, you WILL need to add the check * for result. (See the check for save.doff, a few * lines above and copy that.) */ break; case DBGET_ENDARG: endarg = 1; break; } /* switch */ if (result != TCL_OK) break; if (endarg) break; } if (result != TCL_OK) goto out; if (type == DB_RECNO || type == DB_QUEUE) userecno = 1; /* * Check args we have left versus the flags we were given. * We might have 0, 1 or 2 left. If we have 0, it must * be DB_CONSUME*, if 2, then DB_GET_BOTH, all others should * be 1. */ if (((flag == DB_CONSUME || flag == DB_CONSUME_WAIT) && i != objc) || (flag == DB_GET_BOTH && i != objc - 2)) { Tcl_SetResult(interp, "Wrong number of key/data given based on flags specified\n", TCL_STATIC); result = TCL_ERROR; goto out; } else if (flag == 0 && i != objc - 1) { Tcl_SetResult(interp, "Wrong number of key/data given\n", TCL_STATIC); result = TCL_ERROR; goto out; } /* * XXX * We technically shouldn't be looking inside the dbp like this, * but this is the only way to figure out whether the primary * key should also be a recno. */ if (ispget) { if (dbp->s_primary != NULL && (dbp->s_primary->type == DB_RECNO || dbp->s_primary->type == DB_QUEUE)) useprecno = 1; } /* * Check for illegal combos of options. */ if (useglob && (userecno || flag == DB_SET_RECNO || type == DB_RECNO || type == DB_QUEUE)) { Tcl_SetResult(interp, "Cannot use -glob and record numbers.\n", TCL_STATIC); result = TCL_ERROR; goto out; } if (useglob && flag == DB_GET_BOTH) { Tcl_SetResult(interp, "Only one of -glob or -get_both can be specified.\n", TCL_STATIC); result = TCL_ERROR; goto out; } if (useglob) pattern = Tcl_GetStringFromObj(objv[objc - 1], NULL); /* * This is the list we return */ retlist = Tcl_NewListObj(0, NULL); save.flags |= DB_DBT_MALLOC; /* * isdup is used to know if we support duplicates. If not, we * can just do a db->get call and avoid using cursors. * XXX * When there is a db->get_flags method, it should be used. * isdup = dbp->get_flags(dbp) & DB_DUP; * For now we illegally peek. * XXX */ isdup = dbp->flags & DB_AM_DUP; /* * If the database doesn't support duplicates or we're performing * ops that don't require returning multiple items, use DB->get * instead of a cursor operation. */ if (pattern == NULL && (isdup == 0 || mflag != 0 || flag == DB_SET_RECNO || flag == DB_GET_BOTH || flag == DB_CONSUME || flag == DB_CONSUME_WAIT)) { if (flag == DB_GET_BOTH) { if (userecno) { result = _GetUInt32(interp, objv[(objc - 2)], &recno); if (result == TCL_OK) { key.data = &recno; key.size = sizeof(db_recno_t); } else goto out; } else { /* * Some get calls (SET_*) can change the * key pointers. So, we need to store * the allocated key space in a tmp. */ ret = _CopyObjBytes(interp, objv[objc-2], &ktmp, &key.size, &freekey); if (ret != 0) { result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret), "db get"); goto out; } key.data = ktmp; } /* * Already checked args above. Fill in key and save. * Save is used in the dbp->get call below to fill in * data. * * If the "data" here is really a primary key--that * is, if we're in a pget--and that primary key * is a recno, treat it appropriately as an int. */ if (useprecno) { result = _GetUInt32(interp, objv[objc - 1], &precno); if (result == TCL_OK) { save.data = &precno; save.size = sizeof(db_recno_t); } else goto out; } else { ret = _CopyObjBytes(interp, objv[objc-1], &dtmp, &save.size, &freedata); if (ret != 0) { result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret), "db get"); goto out; } save.data = dtmp; } } else if (flag != DB_CONSUME && flag != DB_CONSUME_WAIT) { if (userecno) { result = _GetUInt32( interp, objv[(objc - 1)], &recno); if (result == TCL_OK) { key.data = &recno; key.size = sizeof(db_recno_t); } else goto out; } else { /* * Some get calls (SET_*) can change the * key pointers. So, we need to store * the allocated key space in a tmp. */ ret = _CopyObjBytes(interp, objv[objc-1], &ktmp, &key.size, &freekey); if (ret != 0) { result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret), "db get"); goto out; } key.data = ktmp; } if (mflag & DB_MULTIPLE) { if ((ret = __os_malloc(dbp->dbenv, bufsize, &save.data)) != 0) { Tcl_SetResult(interp, db_strerror(ret), TCL_STATIC); goto out; } save.ulen = bufsize; F_CLR(&save, DB_DBT_MALLOC); F_SET(&save, DB_DBT_USERMEM); } } data = save; if (ispget) { if (flag == DB_GET_BOTH) { pkey.data = save.data; pkey.size = save.size; data.data = NULL; data.size = 0; } F_SET(&pkey, DB_DBT_MALLOC); _debug_check(); ret = dbp->pget(dbp, txn, &key, &pkey, &data, flag | rmw); } else { _debug_check(); ret = dbp->get(dbp, txn, &key, &data, flag | rmw | mflag); } result = _ReturnSetup(interp, ret, DB_RETOK_DBGET(ret), "db get"); if (ret == 0) { /* * Success. Return a list of the form {name value} * If it was a recno in key.data, we need to convert * into a string/object representation of that recno. */ if (mflag & DB_MULTIPLE) result = _SetMultiList(interp, retlist, &key, &data, type, flag); else if (type == DB_RECNO || type == DB_QUEUE) if (ispget) result = _Set3DBTList(interp, retlist, &key, 1, &pkey, useprecno, &data); else result = _SetListRecnoElem(interp, retlist, *(db_recno_t *)key.data, data.data, data.size); else { if (ispget) result = _Set3DBTList(interp, retlist, &key, 0, &pkey, useprecno, &data); else result = _SetListElem(interp, retlist, key.data, key.size, data.data, data.size); } } /* * Free space from DBT. * * If we set DB_DBT_MALLOC, we need to free the space if * and only if we succeeded (and thus if DB allocated * anything). If DB_DBT_MALLOC is not set, this is a bulk * get buffer, and needs to be freed no matter what. */ if (F_ISSET(&data, DB_DBT_MALLOC) && ret == 0) __os_ufree(dbp->dbenv, data.data); else if (!F_ISSET(&data, DB_DBT_MALLOC)) __os_free(dbp->dbenv, data.data); if (ispget && ret == 0) __os_ufree(dbp->dbenv, pkey.data); if (result == TCL_OK) Tcl_SetObjResult(interp, retlist); goto out; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -