diff options
author | Anton Adamansky <adamansky@gmail.com> | 2015-02-18 01:11:04 +0600 |
---|---|---|
committer | Anton Adamansky <adamansky@gmail.com> | 2015-02-18 01:11:04 +0600 |
commit | 533d760e05e469db1b1fe0c84ca6090034c9d0ee (patch) | |
tree | c02bacc7d1a60ad76d9195da08ba610b26f9d5db /tcejdb/src/tctdb | |
parent | 240e72e8203dfd4eda2e75b8d974df87b1c05d3f (diff) | |
download | ejdb-533d760e05e469db1b1fe0c84ca6090034c9d0ee.tar.gz ejdb-533d760e05e469db1b1fe0c84ca6090034c9d0ee.tar.bz2 ejdb-533d760e05e469db1b1fe0c84ca6090034c9d0ee.zip |
Shared lib build by CMake
Diffstat (limited to 'tcejdb/src/tctdb')
-rw-r--r-- | tcejdb/src/tctdb/tctdb.c | 6349 | ||||
-rw-r--r-- | tcejdb/src/tctdb/tctdb.h | 1162 | ||||
-rw-r--r-- | tcejdb/src/tctdb/tests/tctmttest.c | 1547 | ||||
-rw-r--r-- | tcejdb/src/tctdb/tests/tcttest.c | 2069 | ||||
-rw-r--r-- | tcejdb/src/tctdb/tools/tctmgr.c | 1223 |
5 files changed, 12350 insertions, 0 deletions
diff --git a/tcejdb/src/tctdb/tctdb.c b/tcejdb/src/tctdb/tctdb.c new file mode 100644 index 0000000..5db0722 --- /dev/null +++ b/tcejdb/src/tctdb/tctdb.c @@ -0,0 +1,6349 @@ +/************************************************************************************************* + * The table database API of Tokyo Cabinet + * Copyright (C) 2006-2012 FAL Labs + * Copyright (C) 2012-2015 Softmotions Ltd <info@softmotions.com> + * This file is part of Tokyo Cabinet. + * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of + * the GNU Lesser General Public License as published by the Free Software Foundation; either + * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * You should have received a copy of the GNU Lesser General Public License along with Tokyo + * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA. + *************************************************************************************************/ + + +#include "tcutil.h" +#include "tchdb.h" +#include "tcbdb.h" +#include "tctdb.h" +#include "myconf.h" + +#define TDBOPAQUESIZ 64 // size of using opaque field +#define TDBLEFTOPQSIZ 64 // size of left opaque field +#define TDBPAGEBUFSIZ 32768 // size of a buffer to read each page + +#define TDBDEFAPOW 4 // default alignment power +#define TDBDEFFPOW 10 // default free block pool power +#define TDBDEFLCNUM 4096 // default number of leaf cache +#define TDBDEFNCNUM 512 // default number of node cache +#define TDBDEFXMSIZ (64LL<<20) // default size of the extra mapped memory +#define TDBIDXSUFFIX "idx" // suffix of column index file +#define TDBIDXLMEMB 64 // number of members in each leaf of the index +#define TDBIDXNMEMB 256 // number of members in each node of the index +#define TDBIDXLSMAX 4096 // maximum size of each leaf of the index +#define TDBIDXICCBNUM 262139 // bucket number of the index cache +#define TDBIDXICCMAX (64LL<<20) // maximum size of the index cache +#define TDBIDXICCSYNC 0.01 // ratio of cache synchronization +#define TDBIDXQGUNIT 3 // unit number of the q-gram index +#define TDBFTSUNITMAX 32 // maximum number of full-text search units +#define TDBFTSOCRUNIT 8192 // maximum number of full-text search units +#define TDBFTSBMNUM 524287 // number of elements of full-text search bitmap +#define TDBNUMCNTCOL "_num" // column name of number counting +#define TDBCOLBUFSIZ 1024 // size of a buffer for a column value +#define TDBNUMCOLMAX 16 // maximum number of columns of the long double +#define TDBHINTUSIZ 256 // unit size of the hint string +#define TDBORDRATIO 0.2 // ratio of records to use the order index + +enum { // enumeration for duplication behavior + TDBPDOVER, // overwrite an existing value + TDBPDKEEP, // keep the existing value + TDBPDCAT // concatenate values +}; + +typedef struct { // type of structure for a sort record + const char *kbuf; // pointer to the primary key + int ksiz; // size of the primary key + char *vbuf; // pointer to the value + int vsiz; // size of the value +} TDBSORTREC; + +typedef struct { // type of structure for a full-text search unit + TCLIST *tokens; // q-gram tokens + bool sign; // positive sign +} TDBFTSUNIT; + +typedef struct { // type of structure for a full-text string occurrence + const char *pkbuf; // primary key string + int32_t pksiz; // size of the primary key + int32_t off; // offset of the token + uint16_t seq; // sequence number + uint16_t hash; // hash value for counting sort +} TDBFTSSTROCR; + +typedef struct { // type of structure for a full-text number occurrence + int64_t pkid; // primery key number + int32_t off; // offset of the token + uint16_t seq; // sequence number + uint16_t hash; // hash value for counting sort +} TDBFTSNUMOCR; + + +/* private macros */ +#define TDBLOCKMETHOD(TC_tdb, TC_wr) \ + ((TC_tdb)->mmtx ? tctdblockmethod((TC_tdb), (TC_wr)) : true) +#define TDBUNLOCKMETHOD(TC_tdb) \ + ((TC_tdb)->mmtx ? tctdbunlockmethod(TC_tdb) : true) +#define TDBTHREADYIELD(TC_tdb) \ + do { if((TC_tdb)->mmtx) sched_yield(); } while(false) + + +/* private function prototypes */ +static void tctdbclear(TCTDB *tdb); +static bool tctdbopenimpl(TCTDB *tdb, const char *path, int omode); +static bool tctdbcloseimpl(TCTDB *tdb); +static bool tctdbputimpl(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols, int dmode); +static bool tctdboutimpl(TCTDB *tdb, const char *pkbuf, int pksiz); +static TCMAP *tctdbgetimpl(TCTDB *tdb, const void *pkbuf, int pksiz); +static char *tctdbgetonecol(TCTDB *tdb, const void *pkbuf, int pksiz, + const void *nbuf, int nsiz, int *sp); +static double tctdbaddnumber(TCTDB *tdb, const void *pkbuf, int pksiz, double num); +static bool tctdboptimizeimpl(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); +static bool tctdbvanishimpl(TCTDB *tdb); +static bool tctdbcopyimpl(TCTDB *tdb, const char *path); +static char* tctdbmaprowldr(TCLIST *tokens, const char *pkbuf, int pksz, + const char *rowdata, int rowdatasz, + const char* cname, int cnamesz, + void *op, int *vsz); +static bool tctdbsetindeximpl(TCTDB *tdb, const char *name, int type, TDBRVALOADER rvldr, void* rvldrop); +static int64_t tctdbgenuidimpl(TCTDB *tdb, int64_t inc); +static TCLIST *tctdbqrysearchimpl(TDBQRY *qry); +static TCMAP *tctdbqryidxfetch(TDBQRY *qry, TDBCOND *cond, TDBIDX *idx); +static bool tctdbqryonecondmatch(TDBQRY *qry, TDBCOND *cond, const char *pkbuf, int pksiz); +static bool tctdbqryallcondmatch(TDBQRY *qry, const char *pkbuf, int pksiz); +static bool tctdbqrycondmatch(TDBCOND *cond, const char *vbuf, int vsiz); +static bool tctdbqrycondcheckstrand(const char *tval, const char *oval); +static bool tctdbqrycondcheckstror(const char *tval, const char *oval); +static bool tctdbqrycondcheckstroreq(const char *vbuf, const char *expr); +static bool tctdbqrycondchecknumbt(const char *vbuf, const char *expr); +static bool tctdbqrycondchecknumoreq(const char *vbuf, const char *expr); +static bool tctdbqrycondcheckfts(const char *vbuf, int vsiz, TDBCOND *cond); +static int tdbcmpsortrecstrasc(const TDBSORTREC *a, const TDBSORTREC *b); +static int tdbcmpsortrecstrdesc(const TDBSORTREC *a, const TDBSORTREC *b); +static int tdbcmpsortrecnumasc(const TDBSORTREC *a, const TDBSORTREC *b); +static int tdbcmpsortrecnumdesc(const TDBSORTREC *a, const TDBSORTREC *b); +static uint16_t tctdbidxhash(const char *pkbuf, int pksiz); +static bool tctdbidxputone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, + const char *vbuf, int vsiz); +static bool tctdbidxputtoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz); +static bool tctdbidxputtoken2(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, TCLIST *tokens); +static bool tctdbidxputqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz); +static bool tctdbidxoutone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, + const char *vbuf, int vsiz); +static bool tctdbidxouttoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz); +static bool tctdbidxouttoken2(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, TCLIST *tokens); +static bool tctdbidxoutqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz); +static bool tctdbidxsyncicc(TCTDB *tdb, TDBIDX *idx, bool all); +static int tctdbidxcmpkey(const char **a, const char **b); + +static TCMAP *tctdbidxgetbyfts(TCTDB *tdb, TDBIDX *idx, TDBCOND *cond, TCXSTR *hint); +static void tctdbidxgetbyftsunion(TDBIDX *idx, const TCLIST *tokens, bool sign, + TCMAP *ores, TCMAP *nres, TCXSTR *hint); +static int tctdbidxftscmpstrocr(TDBFTSSTROCR *a, TDBFTSSTROCR *b); +static int tctdbidxftscmpnumocr(TDBFTSNUMOCR *a, TDBFTSNUMOCR *b); +static TDBFTSUNIT *tctdbftsparseexpr(const char *expr, int esiz, int op, int *np); +static bool tctdbdefragimpl(TCTDB *tdb, int64_t step); +static bool tctdbcacheclearimpl(TCTDB *tdb); +static bool tctdbforeachimpl(TCTDB *tdb, TCITER iter, void *op); +static int tctdbqryprocoutcb(const void *pkbuf, int pksiz, TCMAP *cols, void *op); +static bool tctdblockmethod(TCTDB *tdb, bool wr); +static bool tctdbunlockmethod(TCTDB *tdb); +static long double tctdbatof(const char *str); + + +/* debugging function prototypes */ +void tctdbprintmeta(TCTDB *tdb); + + + +/************************************************************************************************* + * API + *************************************************************************************************/ + +/* Get the message string corresponding to an error code. */ +const char *tctdberrmsg(int ecode) { + return tcerrmsg(ecode); +} + +/* Create a table database object. */ +TCTDB *tctdbnew(void) { + TCTDB *tdb; + TCMALLOC(tdb, sizeof (*tdb)); + tctdbclear(tdb); + tdb->hdb = tchdbnew(); + tchdbtune(tdb->hdb, TDBDEFBNUM, TDBDEFAPOW, TDBDEFFPOW, 0); + tchdbsetxmsiz(tdb->hdb, TDBDEFXMSIZ); + return tdb; +} + +/* Delete a table database object. */ +void tctdbdel(TCTDB *tdb) { + assert(tdb); + if (tdb->open) tctdbclose(tdb); + tchdbdel(tdb->hdb); + if (tdb->mmtx) { + pthread_rwlock_destroy(tdb->mmtx); + TCFREE(tdb->mmtx); + } + TCFREE(tdb); +} + +/* Get the last happened error code of a table database object. */ +int tctdbecode(TCTDB *tdb) { + assert(tdb); + return tchdbecode(tdb->hdb); +} + +/* Set mutual exclusion control of a table database object for threading. */ +bool tctdbsetmutex(TCTDB *tdb) { + assert(tdb); + if (tdb->mmtx || tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + TCMALLOC(tdb->mmtx, sizeof (pthread_rwlock_t)); + bool err = false; + if (pthread_rwlock_init(tdb->mmtx, NULL) != 0) err = true; + if (err) { + TCFREE(tdb->mmtx); + tdb->mmtx = NULL; + return false; + } + return tchdbsetmutex(tdb->hdb); +} + +/* Set the tuning parameters of a table database object. */ +bool tctdbtune(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts) { + assert(tdb); + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + tdb->opts = opts; + uint8_t hopts = 0; + if (opts & TDBTLARGE) hopts |= HDBTLARGE; + if (opts & TDBTDEFLATE) hopts |= HDBTDEFLATE; + if (opts & TDBTBZIP) hopts |= HDBTBZIP; + if (opts & TDBTTCBS) hopts |= HDBTTCBS; + if (opts & TDBTEXCODEC) hopts |= HDBTEXCODEC; + bnum = (bnum > 0) ? bnum : TDBDEFBNUM; + apow = (apow >= 0) ? apow : TDBDEFAPOW; + fpow = (fpow >= 0) ? fpow : TDBDEFFPOW; + return tchdbtune(tdb->hdb, bnum, apow, fpow, hopts); +} + +/* Set the caching parameters of a table database object. */ +bool tctdbsetcache(TCTDB *tdb, int32_t rcnum, int32_t lcnum, int32_t ncnum) { + assert(tdb); + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (lcnum > 0) tdb->lcnum = lcnum; + if (ncnum > 0) tdb->ncnum = ncnum; + return tchdbsetcache(tdb->hdb, rcnum); +} + +/* Set the size of the extra mapped memory of a table database object. */ +bool tctdbsetxmsiz(TCTDB *tdb, int64_t xmsiz) { + assert(tdb); + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + return tchdbsetxmsiz(tdb->hdb, xmsiz); +} + +/* Set the unit step number of auto defragmentation of a table database object. */ +bool tctdbsetdfunit(TCTDB *tdb, int32_t dfunit) { + assert(tdb); + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + return tchdbsetdfunit(tdb->hdb, dfunit); +} + +/* Open a database file and connect a table database object. */ +bool tctdbopen(TCTDB *tdb, const char *path, int omode) { + assert(tdb && path); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbopenimpl(tdb, path, omode); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Close a table database object. */ +bool tctdbclose(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbcloseimpl(tdb); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Store a record into a table database object. */ +bool tctdbput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + int vsiz; + if (tcmapget(cols, "", 0, &vsiz)) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Store a string record into a table database object with a zero separated column string. */ +bool tctdbput2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz) { + assert(tdb && pkbuf && pksiz >= 0 && cbuf && csiz >= 0); + TCMAP *cols = tcstrsplit4(cbuf, csiz); + bool rv = tctdbput(tdb, pkbuf, pksiz, cols); + tcmapdel(cols); + return rv; +} + +/* Store a string record into a table database object with a tab separated column string. */ +bool tctdbput3(TCTDB *tdb, const char *pkstr, const char *cstr) { + assert(tdb && pkstr && cstr); + TCMAP *cols = tcstrsplit3(cstr, "\t"); + bool rv = tctdbput(tdb, pkstr, strlen(pkstr), cols); + tcmapdel(cols); + return rv; +} + +/* Store a new record into a table database object. */ +bool tctdbputkeep(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + int vsiz; + if (tcmapget(cols, "", 0, &vsiz)) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDKEEP); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Store a new string record into a table database object with a zero separated column string. */ +bool tctdbputkeep2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz) { + assert(tdb && pkbuf && pksiz >= 0 && cbuf && csiz >= 0); + TCMAP *cols = tcstrsplit4(cbuf, csiz); + bool rv = tctdbputkeep(tdb, pkbuf, pksiz, cols); + tcmapdel(cols); + return rv; +} + +/* Store a new string record into a table database object with a tab separated column string. */ +bool tctdbputkeep3(TCTDB *tdb, const char *pkstr, const char *cstr) { + assert(tdb && pkstr && cstr); + TCMAP *cols = tcstrsplit3(cstr, "\t"); + bool rv = tctdbputkeep(tdb, pkstr, strlen(pkstr), cols); + tcmapdel(cols); + return rv; +} + +/* Concatenate columns of the existing record in a table database object. */ +bool tctdbputcat(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + int vsiz; + if (tcmapget(cols, "", 0, &vsiz)) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDCAT); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Concatenate columns in a table database object with a zero separated column string. */ +bool tctdbputcat2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz) { + assert(tdb && pkbuf && pksiz >= 0 && cbuf && csiz >= 0); + TCMAP *cols = tcstrsplit4(cbuf, csiz); + bool rv = tctdbputcat(tdb, pkbuf, pksiz, cols); + tcmapdel(cols); + return rv; +} + +/* Concatenate columns in a table database object with with a tab separated column string. */ +bool tctdbputcat3(TCTDB *tdb, const char *pkstr, const char *cstr) { + assert(tdb && pkstr && cstr); + TCMAP *cols = tcstrsplit3(cstr, "\t"); + bool rv = tctdbputcat(tdb, pkstr, strlen(pkstr), cols); + tcmapdel(cols); + return rv; +} + +/* Remove a record of a table database object. */ +bool tctdbout(TCTDB *tdb, const void *pkbuf, int pksiz) { + assert(tdb && pkbuf && pksiz >= 0); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdboutimpl(tdb, pkbuf, pksiz); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Remove a string record of a table database object. */ +bool tctdbout2(TCTDB *tdb, const char *pkstr) { + assert(tdb && pkstr); + return tctdbout(tdb, pkstr, strlen(pkstr)); +} + +/* Retrieve a record in a table database object. */ +TCMAP *tctdbget(TCTDB *tdb, const void *pkbuf, int pksiz) { + assert(tdb && pkbuf && pksiz >= 0); + if (!TDBLOCKMETHOD(tdb, false)) return NULL; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return NULL; + } + TCMAP *rv = tctdbgetimpl(tdb, pkbuf, pksiz); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Retrieve a record in a table database object as a zero separated column string. */ +char *tctdbget2(TCTDB *tdb, const void *pkbuf, int pksiz, int *sp) { + assert(tdb && pkbuf && pksiz >= 0 && sp); + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if (!cols) return NULL; + char *cbuf = tcstrjoin4(cols, sp); + tcmapdel(cols); + return cbuf; +} + +/* Retrieve a string record in a table database object as a tab separated column string. */ +char *tctdbget3(TCTDB *tdb, const char *pkstr) { + assert(tdb && pkstr); + TCMAP *cols = tctdbget(tdb, pkstr, strlen(pkstr)); + if (!cols) return NULL; + char *cstr = tcstrjoin3(cols, '\t'); + tcmapdel(cols); + return cstr; +} + +/* Get the size of the value of a record in a table database object. */ +int tctdbvsiz(TCTDB *tdb, const void *pkbuf, int pksiz) { + assert(tdb && pkbuf && pksiz >= 0); + if (!TDBLOCKMETHOD(tdb, false)) return -1; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return -1; + } + int rv = tchdbvsiz(tdb->hdb, pkbuf, pksiz); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get the size of the value of a string record in a table database object. */ +int tctdbvsiz2(TCTDB *tdb, const char *pkstr) { + assert(tdb && pkstr); + return tctdbvsiz(tdb, pkstr, strlen(pkstr)); +} + +/* Initialize the iterator of a table database object. */ +bool tctdbiterinit(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tchdbiterinit(tdb->hdb); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get the next primary key of the iterator of a table database object. */ +void *tctdbiternext(TCTDB *tdb, int *sp) { + assert(tdb && sp); + if (!TDBLOCKMETHOD(tdb, true)) return NULL; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return NULL; + } + char *rv = tchdbiternext(tdb->hdb, sp); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get the next primary key string of the iterator of a table database object. */ +char *tctdbiternext2(TCTDB *tdb) { + assert(tdb); + int pksiz; + return tctdbiternext(tdb, &pksiz); +} + +/* Get the columns of the next record of the iterator of a table database object. */ +TCMAP *tctdbiternext3(TCTDB *tdb) { + assert(tdb); + TCXSTR *kstr = tcxstrnew(); + TCXSTR *vstr = tcxstrnew(); + TCMAP *cols = NULL; + if (tchdbiternext3(tdb->hdb, kstr, vstr)) { + cols = tcmapload(TCXSTRPTR(vstr), TCXSTRSIZE(vstr)); + tcmapput(cols, "", 0, TCXSTRPTR(kstr), TCXSTRSIZE(kstr)); + } + tcxstrdel(vstr); + tcxstrdel(kstr); + return cols; +} + +/* Get forward matching primary keys in a table database object. */ +TCLIST *tctdbfwmkeys(TCTDB *tdb, const void *pbuf, int psiz, int max) { + assert(tdb && pbuf && psiz >= 0); + if (!TDBLOCKMETHOD(tdb, true)) return tclistnew(); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return tclistnew(); + } + TCLIST *rv = tchdbfwmkeys(tdb->hdb, pbuf, psiz, max); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get forward matching string primary keys in a table database object. */ +TCLIST *tctdbfwmkeys2(TCTDB *tdb, const char *pstr, int max) { + assert(tdb && pstr); + return tctdbfwmkeys(tdb, pstr, strlen(pstr), max); +} + +/* Add an integer to a column of a record in a table database object. */ +int tctdbaddint(TCTDB *tdb, const void *pkbuf, int pksiz, int num) { + assert(tdb && pkbuf && pksiz >= 0); + if (!TDBLOCKMETHOD(tdb, true)) return INT_MIN; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return INT_MIN; + } + double rv = tctdbaddnumber(tdb, pkbuf, pksiz, num); + TDBUNLOCKMETHOD(tdb); + return isnan(rv) ? INT_MIN : (int) rv; +} + +/* Add a real number to a column of a record in a table database object. */ +double tctdbadddouble(TCTDB *tdb, const void *pkbuf, int pksiz, double num) { + assert(tdb && pkbuf && pksiz >= 0); + if (!TDBLOCKMETHOD(tdb, true)) return INT_MIN; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return INT_MIN; + } + double rv = tctdbaddnumber(tdb, pkbuf, pksiz, num); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Synchronize updated contents of a table database object with the file and the device. */ +bool tctdbsync(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode || tdb->tran) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbmemsync(tdb, true); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Optimize the file of a table database object. */ +bool tctdboptimize(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode || tdb->tran) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + TDBTHREADYIELD(tdb); + bool rv = tctdboptimizeimpl(tdb, bnum, apow, fpow, opts); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Remove all records of a table database object. */ +bool tctdbvanish(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode || tdb->tran) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + TDBTHREADYIELD(tdb); + bool rv = tctdbvanishimpl(tdb); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Copy the database file of a table database object. */ +bool tctdbcopy(TCTDB *tdb, const char *path) { + assert(tdb && path); + if (!TDBLOCKMETHOD(tdb, false)) return false; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + TDBTHREADYIELD(tdb); + bool rv = tctdbcopyimpl(tdb, path); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Begin the transaction of a table database object. */ +bool tctdbtranbegin(TCTDB *tdb) { + assert(tdb); + for (double wsec = 1.0 / sysconf_SC_CLK_TCK; true; wsec *= 2) { + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + if (!tdb->tran) break; + TDBUNLOCKMETHOD(tdb); + if (wsec > 1.0) wsec = 1.0; + tcsleep(wsec); + } + if (!tctdbtranbeginimpl(tdb)) { + TDBUNLOCKMETHOD(tdb); + return false; + } + tdb->tran = true; + TDBUNLOCKMETHOD(tdb); + return true; +} + +/* Commit the transaction of a table database object. */ +bool tctdbtrancommit(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode || !tdb->tran) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + tdb->tran = false; + bool err = false; + if (!tctdbtrancommitimpl(tdb)) err = true; + TDBUNLOCKMETHOD(tdb); + return !err; +} + +/* Abort the transaction of a table database object. */ +bool tctdbtranabort(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode || !tdb->tran) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + tdb->tran = false; + bool err = false; + if (!tctdbtranabortimpl(tdb)) err = true; + TDBUNLOCKMETHOD(tdb); + return !err; +} + +/* Get the file path of a table database object. */ +const char *tctdbpath(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, false)) return NULL; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return NULL; + } + const char *rv = tchdbpath(tdb->hdb); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get the number of records of a table database object. */ +uint64_t tctdbrnum(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, false)) return 0; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return 0; + } + uint64_t rv = tchdbrnum(tdb->hdb); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get the size of the database file of a table database object. */ +uint64_t tctdbfsiz(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, false)) return 0; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return 0; + } + uint64_t rv = tchdbfsiz(tdb->hdb); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + rv += tcbdbfsiz(idx->db); + break; + } + } + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Set a column index to a table database object. */ +bool tctdbsetindex(TCTDB *tdb, const char *name, int type) { + return tctdbsetindexrldr(tdb, name, type, tctdbmaprowldr, NULL); +} + +bool tctdbsetindexrldr(TCTDB *tdb, const char *name, int type, TDBRVALOADER rvldr, void* rvldrop) { + assert(tdb && name); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode || tdb->tran) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool err = false; + double iccsync = tdb->iccsync; + tdb->iccsync = 1.0; + if (!tctdbsetindeximpl(tdb, name, type, rvldr, rvldrop)) err = true; + if (!tctdbmemsync(tdb, false)) err = true; + tdb->iccsync = iccsync; + TDBUNLOCKMETHOD(tdb); + return !err; +} + +/* Generate a unique ID number of a table database object. */ +int64_t tctdbgenuid(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return -1; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return -1; + } + int64_t rv = tctdbgenuidimpl(tdb, 1); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Create a query object. */ +TDBQRY *tctdbqrynew(TCTDB *tdb) { + assert(tdb); + TDBQRY *qry; + TCMALLOC(qry, sizeof (*qry)); + qry->tdb = tdb; + TCMALLOC(qry->conds, sizeof (qry->conds[0]) * 2); + qry->cnum = 0; + qry->oname = NULL; + qry->otype = TDBQOSTRASC; + qry->max = INT_MAX; + qry->skip = 0; + qry->hint = tcxstrnew3(TDBHINTUSIZ); + qry->count = 0; + return qry; +} + +/* Delete a query object. */ +void tctdbqrydel(TDBQRY *qry) { + assert(qry); + tcxstrdel(qry->hint); + TCFREE(qry->oname); + TDBCOND *conds = qry->conds; + int cnum = qry->cnum; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (cond->ftsunits) { + TDBFTSUNIT *ftsunits = cond->ftsunits; + int ftsnum = cond->ftsnum; + for (int j = 0; j < ftsnum; j++) { + TDBFTSUNIT *ftsunit = ftsunits + j; + tclistdel(ftsunit->tokens); + } + TCFREE(ftsunits); + } + if (cond->regex) { + regfree(cond->regex); + TCFREE(cond->regex); + } + TCFREE(cond->expr); + TCFREE(cond->name); + } + TCFREE(conds); + TCFREE(qry); +} + +/* Add a narrowing condition to a query object. */ +void tctdbqryaddcond(TDBQRY *qry, const char *name, int op, const char *expr) { + assert(qry && name && expr); + int cnum = qry->cnum; + TCREALLOC(qry->conds, qry->conds, sizeof (qry->conds[0]) * (cnum + 1)); + TDBCOND *cond = qry->conds + cnum; + int nsiz = strlen(name); + int esiz = strlen(expr); + TCMEMDUP(cond->name, name, nsiz); + cond->nsiz = nsiz; + bool sign = true; + if (op & TDBQCNEGATE) { + op &= ~TDBQCNEGATE; + sign = false; + } + bool noidx = false; + if (op & TDBQCNOIDX) { + op &= ~TDBQCNOIDX; + noidx = true; + } + cond->op = op; + cond->sign = sign; + cond->noidx = noidx; + TCMEMDUP(cond->expr, expr, esiz); + cond->esiz = esiz; + cond->regex = NULL; + if (op == TDBQCSTRRX) { + const char *rxstr = expr; + int rxopt = REG_EXTENDED | REG_NOSUB; + if (*rxstr == '*') { + rxopt |= REG_ICASE; + rxstr++; + } + regex_t rxbuf; + if (regcomp(&rxbuf, rxstr, rxopt) == 0) { + TCMALLOC(cond->regex, sizeof (rxbuf)); + memcpy(cond->regex, &rxbuf, sizeof (rxbuf)); + } + } + cond->ftsunits = NULL; + cond->ftsnum = 0; + if (op >= TDBQCFTSPH && op <= TDBQCFTSEX) { + cond->op = TDBQCFTSPH; + cond->ftsunits = tctdbftsparseexpr(expr, esiz, op, &(cond->ftsnum)); + } + qry->cnum++; +} + +/* Set the order of a query object. */ +void tctdbqrysetorder(TDBQRY *qry, const char *name, int type) { + assert(qry && name); + if (qry->oname) TCFREE(qry->oname); + qry->oname = tcstrdup(name); + qry->otype = type; +} + +/* Set the limit number of records of the result of a query object. */ +void tctdbqrysetlimit(TDBQRY *qry, int max, int skip) { + assert(qry); + qry->max = (max >= 0) ? max : INT_MAX; + qry->skip = (skip > 0) ? skip : 0; +} + +/* Execute the search of a query object. */ +TCLIST *tctdbqrysearch(TDBQRY *qry) { + assert(qry); + TCTDB *tdb = qry->tdb; + if (!TDBLOCKMETHOD(tdb, false)) return tclistnew(); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return tclistnew(); + } + TCLIST *rv = tctdbqrysearchimpl(qry); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Remove each record corresponding to a query object. */ +bool tctdbqrysearchout(TDBQRY *qry) { + assert(qry); + return tctdbqryproc(qry, tctdbqryprocoutcb, NULL); +} + +/* Process each record corresponding to a query object. */ +bool tctdbqryproc(TDBQRY *qry, TDBQRYPROC proc, void *op) { + assert(qry && proc); + TCTDB *tdb = qry->tdb; + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool err = false; + int64_t getnum = 0; + int64_t putnum = 0; + int64_t outnum = 0; + TCLIST *res = tctdbqrysearchimpl(qry); + int rnum = TCLISTNUM(res); + for (int i = 0; i < rnum; i++) { + const char *pkbuf; + int pksiz; + TCLISTVAL(pkbuf, res, i, pksiz); + TCMAP *cols = tctdbgetimpl(tdb, pkbuf, pksiz); + if (!cols) { + err = true; + continue; + } + getnum++; + int flags = proc(pkbuf, pksiz, cols, op); + if (flags & TDBQPPUT) { + if (tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)) { + putnum++; + } else { + err = true; + } + } else if (flags & TDBQPOUT) { + if (tctdboutimpl(tdb, pkbuf, pksiz)) { + outnum++; + } else { + err = true; + } + } + tcmapdel(cols); + if (flags & TDBQPSTOP) break; + } + tclistdel(res); + tcxstrprintf(qry->hint, "post treatment: get=%" PRIdMAX ", put=%" PRIdMAX ", out=%" PRIdMAX "\n", + (int64_t) getnum, (int64_t) putnum, (int64_t) outnum); + TDBUNLOCKMETHOD(tdb); + return !err; +} + +/* Get the hint string of a query object. */ +const char *tctdbqryhint(TDBQRY *qry) { + assert(qry); + return tcxstrptr(qry->hint); +} + +/* Retrieve records with multiple query objects and get the set of the result. */ +TCLIST *tctdbmetasearch(TDBQRY **qrys, int num, int type) { + assert(qrys && num >= 0); + if (num < 1) return tclistnew2(1); + if (num < 2) return tctdbqrysearch(qrys[0]); + const char *oname = qrys[0]->oname; + int onsiz = oname ? strlen(oname) : 0; + int otype = qrys[0]->otype; + TCMAP *uset = tcmapnew(); + if (type == TDBMSUNION) { + for (int i = 0; i < num; i++) { + TDBQRY *qry = qrys[i]; + TCTDB *tdb = qry->tdb; + int omax = qry->max; + int oskip = qry->skip; + if (qry->max < INT_MAX - qry->skip) qry->max += qry->skip; + qry->skip = 0; + TCLIST *res = tctdbqrysearch(qry); + qry->max = omax; + qry->skip = oskip; + int rnum = TCLISTNUM(res); + for (int j = 0; j < rnum; j++) { + const char *pkbuf; + int pksiz; + TCLISTVAL(pkbuf, res, j, pksiz); + if (oname) { + int csiz; + char *cbuf = tctdbget4(tdb, pkbuf, pksiz, oname, onsiz, &csiz); + if (cbuf) { + tcmapput4(uset, pkbuf, pksiz, "=", 1, cbuf, csiz); + TCFREE(cbuf); + } else { + tcmapput(uset, pkbuf, pksiz, "*", 1); + } + } else { + tcmapputkeep(uset, pkbuf, pksiz, "", 0); + } + } + tclistdel(res); + } + } else if (type == TDBMSISECT) { + int omax = qrys[0]->max; + int oskip = qrys[0]->skip; + qrys[0]->max = INT_MAX; + qrys[0]->skip = 0; + TCLIST *res = tctdbqrysearch(qrys[0]); + qrys[0]->max = omax; + qrys[0]->skip = oskip; + int rnum = TCLISTNUM(res); + for (int i = 0; i < rnum; i++) { + const char *pkbuf; + int pksiz; + TCLISTVAL(pkbuf, res, i, pksiz); + tcmapputkeep(uset, pkbuf, pksiz, "", 0); + } + tclistdel(res); + for (int i = 1; i < num; i++) { + TDBQRY *qry = qrys[i]; + if (TCMAPRNUM(uset) < 1) { + tcxstrclear(qry->hint); + tcxstrprintf(qry->hint, "omitted\n"); + continue; + } + omax = qry->max; + oskip = qry->skip; + qry->max = INT_MAX; + qry->skip = 0; + res = tctdbqrysearch(qry); + qry->max = omax; + qry->skip = oskip; + rnum = TCLISTNUM(res); + TCMAP *nset = tcmapnew2(tclmin(TCMAPRNUM(uset), rnum) + 1); + for (int j = 0; j < rnum; j++) { + const char *pkbuf; + int pksiz; + TCLISTVAL(pkbuf, res, j, pksiz); + int vsiz; + if (tcmapget(uset, pkbuf, pksiz, &vsiz)) tcmapputkeep(nset, pkbuf, pksiz, "", 0); + } + tcmapdel(uset); + uset = nset; + tclistdel(res); + } + } else if (type == TDBMSDIFF) { + int omax = qrys[0]->max; + int oskip = qrys[0]->skip; + qrys[0]->max = INT_MAX; + qrys[0]->skip = 0; + TCLIST *res = tctdbqrysearch(qrys[0]); + qrys[0]->max = omax; + qrys[0]->skip = oskip; + int rnum = TCLISTNUM(res); + for (int i = 0; i < rnum; i++) { + const char *pkbuf; + int pksiz; + TCLISTVAL(pkbuf, res, i, pksiz); + tcmapputkeep(uset, pkbuf, pksiz, "", 0); + } + tclistdel(res); + for (int i = 1; i < num; i++) { + TDBQRY *qry = qrys[i]; + if (TCMAPRNUM(uset) < 1) { + tcxstrclear(qry->hint); + tcxstrprintf(qry->hint, "omitted\n"); + continue; + } + omax = qry->max; + oskip = qry->skip; + qry->max = INT_MAX; + qry->skip = 0; + res = tctdbqrysearch(qry); + qry->max = omax; + qry->skip = oskip; + rnum = TCLISTNUM(res); + for (int j = 0; j < rnum; j++) { + const char *pkbuf; + int pksiz; + TCLISTVAL(pkbuf, res, j, pksiz); + tcmapout(uset, pkbuf, pksiz); + } + tclistdel(res); + } + } + int max = qrys[0]->max; + int skip = qrys[0]->skip; + TCLIST *res; + if (oname && type == TDBMSUNION) { + int rnum = TCMAPRNUM(uset); + TDBSORTREC *keys; + TCMALLOC(keys, sizeof (*keys) * rnum + 1); + tcmapiterinit(uset); + const char *pkbuf; + int pksiz; + TDBSORTREC *key = keys; + while ((pkbuf = tcmapiternext(uset, &pksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(pkbuf, &vsiz); + key->kbuf = pkbuf; + key->ksiz = pksiz; + if (*vbuf == '=') { + key->vbuf = (char *) vbuf + 1; + key->vsiz = vsiz - 1; + } else { + key->vbuf = NULL; + key->vsiz = 0; + } + key++; + } + int (*compar)(const TDBSORTREC *a, const TDBSORTREC * b) = NULL; + switch (otype) { + case TDBQOSTRASC: + compar = tdbcmpsortrecstrasc; + break; + case TDBQOSTRDESC: + compar = tdbcmpsortrecstrdesc; + break; + case TDBQONUMASC: + compar = tdbcmpsortrecnumasc; + break; + case TDBQONUMDESC: + compar = tdbcmpsortrecnumdesc; + break; + } + if (compar) qsort(keys, rnum, sizeof (*keys), (int (*)(const void *, const void *))compar); + res = tclistnew2(tclmin(rnum, max)); + for (int i = skip; max > 0 && i < rnum; i++) { + key = keys + i; + TCLISTPUSH(res, key->kbuf, key->ksiz); + max--; + } + TCFREE(keys); + } else { + res = tclistnew2(tclmin(tcmaprnum(uset), max)); + tcmapiterinit(uset); + const char *pkbuf; + int pksiz; + while (max > 0 && (pkbuf = tcmapiternext(uset, &pksiz)) != NULL) { + if (skip > 0) { + skip--; + } else { + TCLISTPUSH(res, pkbuf, pksiz); + max--; + } + } + } + tcmapdel(uset); + TCXSTR *hint = tcxstrnew(); + for (int i = 0; i < num; i++) { + TDBQRY *qry = qrys[i]; + TCLIST *lines = tcstrsplit(tctdbqryhint(qry), "\n"); + int lnum = TCLISTNUM(lines); + for (int j = 0; j < lnum; j++) { + const char *line = TCLISTVALPTR(lines, j); + if (*line != 0) tcxstrprintf(hint, "[%d] %s\n", i, line); + } + tclistdel(lines); + tcxstrclear(qry->hint); + qry->count = 0; + } + TCXSTRCAT(qrys[0]->hint, TCXSTRPTR(hint), TCXSTRSIZE(hint)); + qrys[0]->count = TCLISTNUM(res); + tcxstrdel(hint); + return res; +} + + + +/************************************************************************************************* + * features for experts + *************************************************************************************************/ + +/* Set the error code of a table database object. */ +void tctdbsetecode(TCTDB *tdb, int ecode, const char *filename, int line, const char *func) { + tctdbsetecode2(tdb, ecode, filename, line, func, false); +} + +/* Set the error code of a table database object. */ +void tctdbsetecode2(TCTDB *tdb, int ecode, const char *filename, int line, const char *func, bool notfatal) { + assert(tdb && filename && line >= 1 && func); + tchdbsetecode2(tdb->hdb, ecode, filename, line, func, notfatal); +} + +/* Set the file descriptor for debugging output. */ +void tctdbsetdbgfd(TCTDB *tdb, HANDLE fd) { + assert(tdb); + tchdbsetdbgfd(tdb->hdb, fd); +} + +/* Get the file descriptor for debugging output. */ +HANDLE tctdbdbgfd(TCTDB *tdb) { + assert(tdb); + return tchdbdbgfd(tdb->hdb); +} + +/* Check whether mutual exclusion control is set to a table database object. */ +bool tctdbhasmutex(TCTDB *tdb) { + assert(tdb); + return tdb->mmtx != NULL; +} + +/* Synchronize updating contents on memory of a table database object. */ +bool tctdbmemsync(TCTDB *tdb, bool phys) { + assert(tdb); + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + bool err = false; + if (!tchdbmemsync(tdb->hdb, phys)) err = true; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbmemsync(idx->db, phys)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Get the number of elements of the bucket array of a table database object. */ +uint64_t tctdbbnum(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbbnum(tdb->hdb); +} + +/* Get the record alignment of a table database object. */ +uint32_t tctdbalign(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbalign(tdb->hdb); +} + +/* Get the maximum number of the free block pool of a table database object. */ +uint32_t tctdbfbpmax(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbfbpmax(tdb->hdb); +} + +/* Get the inode number of the database file of a table database object. */ +uint64_t tctdbinode(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbinode(tdb->hdb); +} + +/* Get the modification time of the database file of a table database object. */ +time_t tctdbmtime(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbmtime(tdb->hdb); +} + +/* Get the additional flags of a table database object. */ +uint8_t tctdbflags(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbflags(tdb->hdb); +} + +/* Get the options of a table database object. */ +uint8_t tctdbopts(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tdb->opts; +} + +/* Get the number of used elements of the bucket array of a table database object. */ +uint64_t tctdbbnumused(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tchdbbnumused(tdb->hdb); +} + +/* Get the number of column indices of a table database object. */ +int tctdbinum(TCTDB *tdb) { + assert(tdb); + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return 0; + } + return tdb->inum; +} + +/* Get the seed of unique ID unumbers of a table database object. */ +int64_t tctdbuidseed(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, false)) return -1; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return -1; + } + int64_t rv = tctdbgenuidimpl(tdb, 0); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Set the parameters of the inverted cache of a table database object. */ +bool tctdbsetinvcache(TCTDB *tdb, int64_t iccmax, double iccsync) { + assert(tdb); + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + tdb->iccmax = (iccmax > 0) ? iccmax : TDBIDXICCMAX; + tdb->iccsync = (iccsync > 0) ? iccsync : TDBIDXICCSYNC; + return true; +} + +/* Set the seed of unique ID unumbers of a table database object. */ +bool tctdbsetuidseed(TCTDB *tdb, int64_t seed) { + assert(tdb && seed >= 0); + if (!TDBLOCKMETHOD(tdb, true)) return -1; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + tctdbgenuidimpl(tdb, -seed - 1); + TDBUNLOCKMETHOD(tdb); + return true; +} + +/* Set the custom codec functions of a table database object. */ +bool tctdbsetcodecfunc(TCTDB *tdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop) { + assert(tdb && enc && dec); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tchdbsetcodecfunc(tdb->hdb, enc, encop, dec, decop); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Get the unit step number of auto defragmentation of a table database object. */ +uint32_t tctdbdfunit(TCTDB *tdb) { + assert(tdb); + return tchdbdfunit(tdb->hdb); +} + +/* Perform dynamic defragmentation of a table database object. */ +bool tctdbdefrag(TCTDB *tdb, int64_t step) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, false)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbdefragimpl(tdb, step); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Clear the cache of a table tree database object. */ +bool tctdbcacheclear(TCTDB *tdb) { + assert(tdb); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tctdbcacheclearimpl(tdb); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Store a record into a table database object with a duplication handler. */ +bool tctdbputproc(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz, + TCPDPROC proc, void *op) { + assert(tdb && pkbuf && pksiz >= 0 && proc); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool err = false; + TCMAP *cols = tctdbgetimpl(tdb, pkbuf, pksiz); + if (cols) { + int zsiz; + char *zbuf = tcstrjoin4(cols, &zsiz); + int ncsiz; + void *ncbuf = proc(zbuf, zsiz, &ncsiz, op); + if (ncbuf == (void *) - 1) { + if (!tctdboutimpl(tdb, pkbuf, pksiz)) err = true; + } else if (ncbuf) { + TCMAP *ncols = tcstrsplit4(ncbuf, ncsiz); + if (!tctdbputimpl(tdb, pkbuf, pksiz, ncols, TDBPDOVER)) err = true; + tcmapdel(ncols); + TCFREE(ncbuf); + } else { + tctdbsetecode(tdb, TCEKEEP, __FILE__, __LINE__, __func__); + err = true; + } + TCFREE(zbuf); + tcmapdel(cols); + } else { + if (cbuf) { + cols = tcstrsplit4(cbuf, csiz); + if (!tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)) err = true; + tcmapdel(cols); + } else { + tctdbsetecode(tdb, TCENOREC, __FILE__, __LINE__, __func__); + err = true; + } + } + TDBUNLOCKMETHOD(tdb); + return !err; +} + +/* Retrieve the value of a column of a record in a table database object. */ +char *tctdbget4(TCTDB *tdb, const void *pkbuf, int pksiz, const void *nbuf, int nsiz, int *sp) { + assert(tdb && pkbuf && pksiz >= 0 && nbuf && nsiz >= 0 && sp); + if (!TDBLOCKMETHOD(tdb, false)) return NULL; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return NULL; + } + char *rv = tctdbgetonecol(tdb, pkbuf, pksiz, nbuf, nsiz, sp); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Move the iterator to the record corresponding a key of a table database object. */ +bool tctdbiterinit2(TCTDB *tdb, const void *pkbuf, int pksiz) { + assert(tdb && pkbuf && pksiz >= 0); + if (!TDBLOCKMETHOD(tdb, true)) return false; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + bool rv = tchdbiterinit2(tdb->hdb, pkbuf, pksiz); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Move the iterator to the record corresponding a key string of a table database object. */ +bool tctdbiterinit3(TCTDB *tdb, const char *kstr) { + assert(tdb && kstr); + return tctdbiterinit2(tdb, kstr, strlen(kstr)); +} + +/* Process each record atomically of a table database object. */ +bool tctdbforeach(TCTDB *tdb, TCITER iter, void *op) { + assert(tdb && iter); + if (!TDBLOCKMETHOD(tdb, false)) return false; + if (!tdb->open) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + return false; + } + TDBTHREADYIELD(tdb); + bool rv = tctdbforeachimpl(tdb, iter, op); + TDBUNLOCKMETHOD(tdb); + return rv; +} + +/* Process each record corresponding to a query object with non-atomic fashion. */ +bool tctdbqryproc2(TDBQRY *qry, TDBQRYPROC proc, void *op) { + assert(qry && proc); + TCTDB *tdb = qry->tdb; + TDBCOND *conds = qry->conds; + int cnum = qry->cnum; + bool err = false; + int64_t getnum = 0; + int64_t putnum = 0; + int64_t outnum = 0; + TCLIST *res = tctdbqrysearch(qry); + int rnum = TCLISTNUM(res); + for (int i = 0; i < rnum; i++) { + if (!TDBLOCKMETHOD(tdb, true)) { + err = true; + break; + } + if (!tdb->open || !tdb->wmode) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + TDBUNLOCKMETHOD(tdb); + err = true; + break; + } + int pksiz; + const char *pkbuf; + TCLISTVAL(pkbuf, res, i, pksiz); + TCMAP *cols = tctdbgetimpl(tdb, pkbuf, pksiz); + if (cols) { + getnum++; + bool ok = true; + for (int j = 0; j < cnum; j++) { + TDBCOND *cond = conds + j; + if (cond->nsiz < 1) { + if (tctdbqrycondmatch(cond, pkbuf, pksiz) != cond->sign) { + ok = false; + break; + } + } else { + int vsiz; + const char *vbuf = tcmapget(cols, cond->name, cond->nsiz, &vsiz); + if (vbuf) { + if (tctdbqrycondmatch(cond, vbuf, vsiz) != cond->sign) { + ok = false; + break; + } + } else { + if (cond->sign) { + ok = false; + break; + } + } + } + } + if (ok) { + int flags = proc(pkbuf, pksiz, cols, op); + if (flags & TDBQPPUT) { + if (tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)) { + putnum++; + } else { + err = true; + } + } else if (flags & TDBQPOUT) { + if (tctdboutimpl(tdb, pkbuf, pksiz)) { + outnum++; + } else { + err = true; + } + } + if (flags & TDBQPSTOP) i = rnum; + } + tcmapdel(cols); + } + TDBUNLOCKMETHOD(tdb); + } + tclistdel(res); + tcxstrprintf(qry->hint, "post treatment: get=%" PRIdMAX ", put=%" PRIdMAX ", out=%" PRIdMAX "\n", + (int64_t) getnum, (int64_t) putnum, (int64_t) outnum); + return !err; +} + +/* Remove each record corresponding to a query object with non-atomic fashion. */ +bool tctdbqrysearchout2(TDBQRY *qry) { + assert(qry); + return tctdbqryproc2(qry, tctdbqryprocoutcb, NULL); +} + +/* Convert a string into the index type number. */ +int tctdbstrtoindextype(const char *str) { + assert(str); + int type = -1; + int flags = 0; + if (*str == '+') { + flags |= TDBITKEEP; + str++; + } + if (!tcstricmp(str, "LEX") || !tcstricmp(str, "LEXICAL") || !tcstricmp(str, "STR")) { + type = TDBITLEXICAL; + } else if (!tcstricmp(str, "DEC") || !tcstricmp(str, "DECIMAL") || !tcstricmp(str, "NUM")) { + type = TDBITDECIMAL; + } else if (!tcstricmp(str, "TOK") || !tcstricmp(str, "TOKEN")) { + type = TDBITTOKEN; + } else if (!tcstricmp(str, "QGR") || !tcstricmp(str, "QGRAM") || !tcstricmp(str, "FTS")) { + type = TDBITQGRAM; + } else if (!tcstricmp(str, "OPT") || !tcstricmp(str, "OPTIMIZE")) { + type = TDBITOPT; + } else if (!tcstricmp(str, "VOID") || !tcstricmp(str, "NULL")) { + type = TDBITVOID; + } else if (tcstrisnum(str)) { + type = tcatoi(str); + } + return type | flags; +} + +/* Convert a string into the meta search type number. */ +int tctdbstrtometasearcytype(const char *str) { + assert(str); + int type = -1; + if (!tcstricmp(str, "UNION") || !tcstricmp(str, "OR")) { + type = TDBMSUNION; + } else if (!tcstricmp(str, "ISECT") || !tcstricmp(str, "INTERSECTION") || + !tcstricmp(str, "AND")) { + type = TDBMSISECT; + } else if (!tcstricmp(str, "DIFF") || !tcstricmp(str, "DIFFERENCE") || + !tcstricmp(str, "ANDNOT") || !tcstricmp(str, "NOT")) { + type = TDBMSDIFF; + } else if (tcstrisnum(str)) { + type = tcatoi(str); + } + return type; +} + +/* Get the count of corresponding records of a query object. */ +int tctdbqrycount(TDBQRY *qry) { + assert(qry); + return qry->count; +} + +/* Generate keyword-in-context strings from a query object. */ +TCLIST *tctdbqrykwic(TDBQRY *qry, TCMAP *cols, const char *name, int width, int opts) { + assert(qry && cols && width >= 0); + TDBCOND *conds = qry->conds; + int cnum = qry->cnum; + TDBCOND *cond = NULL; + if (name) { + for (int i = 0; i < cnum; i++) { + if (!strcmp(conds[i].name, name)) { + cond = conds + i; + break; + } + } + } else if (cnum > 0) { + cond = conds; + name = cond->name; + } + if (!cond) return tclistnew2(1); + const char *str = tcmapget2(cols, name); + if (!str) return tclistnew2(1); + TCLIST *words; + if (cond->op == TDBQCSTRAND || cond->op == TDBQCSTROR || + cond->op == TDBQCSTROREQ || cond->op == TDBQCNUMOREQ) { + words = tcstrsplit(cond->expr, " ,"); + } else if (cond->op == TDBQCFTSPH) { + TDBFTSUNIT *ftsunits = cond->ftsunits; + int ftsnum = cond->ftsnum; + if (ftsnum > 0) { + words = tclistnew2(ftsnum * 2 + 1); + for (int i = 0; i < ftsnum; i++) { + if (!ftsunits[i].sign) continue; + TCLIST *tokens = ftsunits[i].tokens; + int tnum = TCLISTNUM(tokens); + for (int j = 0; j < tnum; j++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, j, tsiz); + TCLISTPUSH(words, token, tsiz); + } + } + } else { + words = tclistnew2(1); + } + } else { + words = tclistnew3(cond->expr, NULL); + } + TCLIST *texts = tcstrkwic(str, words, width, opts); + tclistdel(words); + return texts; +} + +/* Convert a string into the query operation number. */ +int tctdbqrystrtocondop(const char *str) { + assert(str); + int op = -1; + int flags = 0; + if (*str == '~' || *str == '!') { + flags |= TDBQCNEGATE; + str++; + } + if (*str == '+') { + flags |= TDBQCNOIDX; + str++; + } + if (!tcstricmp(str, "STREQ") || !tcstricmp(str, "STR") || !tcstricmp(str, "EQ")) { + op = TDBQCSTREQ; + } else if (!tcstricmp(str, "STRINC") || !tcstricmp(str, "INC")) { + op = TDBQCSTRINC; + } else if (!tcstricmp(str, "STRBW") || !tcstricmp(str, "BW")) { + op = TDBQCSTRBW; + } else if (!tcstricmp(str, "STREW") || !tcstricmp(str, "EW")) { + op = TDBQCSTREW; + } else if (!tcstricmp(str, "STRAND") || !tcstricmp(str, "AND")) { + op = TDBQCSTRAND; + } else if (!tcstricmp(str, "STROR") || !tcstricmp(str, "OR")) { + op = TDBQCSTROR; + } else if (!tcstricmp(str, "STROREQ") || !tcstricmp(str, "OREQ")) { + op = TDBQCSTROREQ; + } else if (!tcstricmp(str, "STRRX") || !tcstricmp(str, "RX")) { + op = TDBQCSTRRX; + } else if (!tcstricmp(str, "NUMEQ") || !tcstricmp(str, "NUM") || + !tcstricmp(str, "=") || !tcstricmp(str, "==")) { + op = TDBQCNUMEQ; + } else if (!tcstricmp(str, "NUMGT") || !tcstricmp(str, ">")) { + op = TDBQCNUMGT; + } else if (!tcstricmp(str, "NUMGE") || !tcstricmp(str, ">=")) { + op = TDBQCNUMGE; + } else if (!tcstricmp(str, "NUMLT") || !tcstricmp(str, "<")) { + op = TDBQCNUMLT; + } else if (!tcstricmp(str, "NUMLE") || !tcstricmp(str, "<=")) { + op = TDBQCNUMLE; + } else if (!tcstricmp(str, "NUMBT")) { + op = TDBQCNUMBT; + } else if (!tcstricmp(str, "NUMOREQ")) { + op = TDBQCNUMOREQ; + } else if (!tcstricmp(str, "FTSPH") || !tcstricmp(str, "FTS")) { + op = TDBQCFTSPH; + } else if (!tcstricmp(str, "FTSAND")) { + op = TDBQCFTSAND; + } else if (!tcstricmp(str, "FTSOR")) { + op = TDBQCFTSOR; + } else if (!tcstricmp(str, "FTSEX")) { + op = TDBQCFTSEX; + } else if (tcstrisnum(str)) { + op = tcatoi(str); + } + return op | flags; +} + +/* Convert a string into the query order type number. */ +int tctdbqrystrtoordertype(const char *str) { + assert(str); + int type = -1; + if (!tcstricmp(str, "STRASC") || !tcstricmp(str, "STR") || !tcstricmp(str, "ASC")) { + type = TDBQOSTRASC; + } else if (!tcstricmp(str, "STRDESC") || !tcstricmp(str, "DESC")) { + type = TDBQOSTRDESC; + } else if (!tcstricmp(str, "NUMASC") || !tcstricmp(str, "NUM")) { + type = TDBQONUMASC; + } else if (!tcstricmp(str, "NUMDESC")) { + type = TDBQONUMDESC; + } else if (tcstrisnum(str)) { + type = tcatoi(str); + } + return type; +} + +/* Convert a string into the set operation type number. */ +int tctdbmetastrtosettype(const char *str) { + assert(str); + int type = -1; + if (!tcstricmp(str, "UNION") || !tcstricmp(str, "CUP") || !tcstricmp(str, "+")) { + type = TDBMSUNION; + } else if (!tcstricmp(str, "ISECT") || !tcstricmp(str, "INTERSECTION") || + !tcstricmp(str, "CAP") || !tcstricmp(str, "*")) { + type = TDBMSISECT; + } else if (!tcstricmp(str, "DIFF") || !tcstricmp(str, "DIFFERENCE") || + !tcstricmp(str, "MINUS") || !tcstricmp(str, "-")) { + type = TDBMSDIFF; + } else if (tcstrisnum(str)) { + type = tcatoi(str); + } + return type; +} + + + +/************************************************************************************************* + * private features + *************************************************************************************************/ + +/* Clear all members. + `tdb' specifies the table database object. */ +static void tctdbclear(TCTDB *tdb) { + assert(tdb); + tdb->mmtx = NULL; + tdb->hdb = NULL; + tdb->open = false; + tdb->wmode = false; + tdb->opts = 0; + tdb->lcnum = TDBDEFLCNUM; + tdb->ncnum = TDBDEFNCNUM; + tdb->iccmax = TDBIDXICCMAX; + tdb->iccsync = TDBIDXICCSYNC; + tdb->idxs = NULL; + tdb->inum = 0; + tdb->tran = false; +} + +/* Open a database file and connect a table database object. + `tdb' specifies the table database object. + `path' specifies the path of the internal database file. + `omode' specifies the connection mode. + If successful, the return value is true, else, it is false. */ +static bool tctdbopenimpl(TCTDB *tdb, const char *path, int omode) { + assert(tdb && path); + HANDLE dbgfd = tchdbdbgfd(tdb->hdb); + TCCODEC enc, dec; + void *encop, *decop; + tchdbcodecfunc(tdb->hdb, &enc, &encop, &dec, &decop); + int homode = HDBOREADER; + int bomode = BDBOREADER; + if (omode & TDBOWRITER) { + homode = HDBOWRITER; + bomode = BDBOWRITER; + if (omode & TDBOCREAT) { + homode |= HDBOCREAT; + bomode |= BDBOCREAT; + } + if (omode & TDBOTRUNC) { + homode |= HDBOTRUNC; + bomode |= BDBOTRUNC; + } + tdb->wmode = true; + } else { + tdb->wmode = false; + } + if (omode & TDBONOLCK) { + homode |= HDBONOLCK; + bomode |= BDBONOLCK; + } + if (omode & TDBOLCKNB) { + homode |= HDBOLCKNB; + bomode |= BDBOLCKNB; + } + if (omode & TDBOTSYNC) { + homode |= HDBOTSYNC; + bomode |= BDBOTSYNC; + } + tchdbsettype(tdb->hdb, TCDBTTABLE); + if (!tchdbopen(tdb->hdb, path, homode)) return false; + char *tpath = tcsprintf("%s%c%s%c*", path, MYEXTCHR, TDBIDXSUFFIX, MYEXTCHR); + if ((omode & TDBOWRITER) && (omode & TDBOTRUNC)) { + TCLIST *paths = tcglobpat(tpath); + int pnum = TCLISTNUM(paths); + for (int i = 0; i < pnum; i++) { + tcunlinkfile(TCLISTVALPTR(paths, i)); + } + tclistdel(paths); + } + TCLIST *paths = tcglobpat(tpath); + int pnum = TCLISTNUM(paths); + TCMALLOC(tdb->idxs, sizeof (tdb->idxs[0]) * pnum + 1); + TDBIDX *idxs = tdb->idxs; + int inum = 0; + for (int i = 0; i < pnum; i++) { + const char *ipath = TCLISTVALPTR(paths, i); + if (!tcstrfwm(ipath, path)) continue; + const char *rp = ipath + strlen(path); + if (*rp != MYEXTCHR) continue; + rp++; + if (!tcstrfwm(rp, TDBIDXSUFFIX)) continue; + rp += strlen(TDBIDXSUFFIX); + if (*rp != MYEXTCHR) continue; + rp++; + char *stem = tcstrdup(rp); + char *ep = strrchr(stem, MYEXTCHR); + if (!ep) continue; + *(ep++) = '\0'; + int nsiz; + char *name = tcurldecode(stem, &nsiz); + if (!strcmp(ep, "lex") || !strcmp(ep, "dec") || !strcmp(ep, "tok") || !strcmp(ep, "qgr")) { + TCBDB *bdb = tcbdbnew(); + if (!INVALIDHANDLE(dbgfd)) tcbdbsetdbgfd(bdb, dbgfd); + if (tdb->mmtx) tcbdbsetmutex(bdb); + if (enc && dec) tcbdbsetcodecfunc(bdb, enc, encop, dec, decop); + tcbdbsetcache(bdb, tdb->lcnum, tdb->ncnum); + tcbdbsetxmsiz(bdb, tchdbxmsiz(tdb->hdb)); + tcbdbsetdfunit(bdb, tchdbdfunit(tdb->hdb)); + tcbdbsetlsmax(bdb, TDBIDXLSMAX); + if (tcbdbopen(bdb, ipath, bomode)) { + idxs[inum].name = tcstrdup(name); + idxs[inum].type = TDBITLEXICAL; + if (!strcmp(ep, "dec")) { + idxs[inum].type = TDBITDECIMAL; + } else if (!strcmp(ep, "tok")) { + idxs[inum].type = TDBITTOKEN; + } else if (!strcmp(ep, "qgr")) { + idxs[inum].type = TDBITQGRAM; + } + idxs[inum].db = bdb; + idxs[inum].cc = NULL; + if (idxs[inum].type == TDBITTOKEN) { + idxs[inum].cc = tcmapnew2(TDBIDXICCBNUM); + } else if (idxs[inum].type == TDBITQGRAM) { + idxs[inum].cc = tcmapnew2(TDBIDXICCBNUM); + } + inum++; + } else { + tcbdbdel(bdb); + } + } + TCFREE(name); + TCFREE(stem); + } + tclistdel(paths); + TCFREE(tpath); + tdb->inum = inum; + tdb->open = true; + uint8_t hopts = tchdbopts(tdb->hdb); + uint8_t opts = 0; + if (hopts & HDBTLARGE) opts |= TDBTLARGE; + if (hopts & HDBTDEFLATE) opts |= TDBTDEFLATE; + if (hopts & HDBTBZIP) opts |= TDBTBZIP; + if (hopts & HDBTTCBS) opts |= TDBTTCBS; + if (hopts & HDBTEXCODEC) opts |= TDBTEXCODEC; + tdb->opts = opts; + tdb->tran = false; + return true; +} + +/* Close a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +static bool tctdbcloseimpl(TCTDB *tdb) { + assert(tdb); + bool err = false; + if (tdb->tran && !tctdbtranabortimpl(tdb)) err = true; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + tcmapdel(idx->cc); + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbclose(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + tcbdbdel(idx->db); + break; + } + TCFREE(idx->name); + } + TCFREE(idxs); + if (!tchdbclose(tdb->hdb)) err = true; + tdb->open = false; + return !err; +} + +/* Store a record into a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + `dmode' specifies behavior when the key overlaps. + If successful, the return value is true, else, it is false. */ +static bool tctdbputimpl(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols, int dmode) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + bool err = false; + int osiz; + char *obuf = tdb->hdb->rnum > 0 ? tchdbget(tdb->hdb, pkbuf, pksiz, &osiz) : NULL; + if (obuf) { + if (dmode == TDBPDKEEP) { + tctdbsetecode(tdb, TCEKEEP, __FILE__, __LINE__, __func__); + TCFREE(obuf); + return false; + } + TCMAP *ocols = tcmapload(obuf, osiz); + if (dmode == TDBPDCAT) { + TCMAP *ncols = tcmapnew2(TCMAPRNUM(cols) + 1); + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + if (tcmapputkeep(ocols, kbuf, ksiz, vbuf, vsiz)) tcmapput(ncols, kbuf, ksiz, vbuf, vsiz); + } + if (!tctdbidxput(tdb, pkbuf, pksiz, ncols)) err = true; + tcmapdel(ncols); + int csiz; + char *cbuf = tcmapdump(ocols, &csiz); + if (!tchdbput(tdb->hdb, pkbuf, pksiz, cbuf, csiz)) err = true; + TCFREE(cbuf); + } else { + TCMAP *ncols = tcmapnew2(TCMAPRNUM(cols) + 1); + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + int osiz; + const char *obuf = tcmapget(ocols, kbuf, ksiz, &osiz); + if (obuf && osiz == vsiz && !memcmp(obuf, vbuf, osiz)) { + tcmapout(ocols, kbuf, ksiz); + } else { + tcmapput(ncols, kbuf, ksiz, vbuf, vsiz); + } + } + if (!tctdbidxout(tdb, pkbuf, pksiz, ocols)) err = true; + if (!tctdbidxput(tdb, pkbuf, pksiz, ncols)) err = true; + tcmapdel(ncols); + int csiz; + char *cbuf = tcmapdump(cols, &csiz); + if (!tchdbput(tdb->hdb, pkbuf, pksiz, cbuf, csiz)) err = true; + TCFREE(cbuf); + } + tcmapdel(ocols); + TCFREE(obuf); + } else { + if (!tctdbidxput(tdb, pkbuf, pksiz, cols)) err = true; + int csiz; + char *cbuf = tcmapdump(cols, &csiz); + if (!tchdbput(tdb->hdb, pkbuf, pksiz, cbuf, csiz)) err = true; + TCFREE(cbuf); + } + return !err; +} + +/* Remove a record of a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If successful, the return value is true, else, it is false. */ +static bool tctdboutimpl(TCTDB *tdb, const char *pkbuf, int pksiz) { + assert(tdb && pkbuf && pksiz >= 0); + int csiz; + char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); + if (!cbuf) return false; + bool err = false; + TCMAP *cols = tcmapload(cbuf, csiz); + if (!tctdbidxout(tdb, pkbuf, pksiz, cols)) err = true; + if (!tchdbout(tdb->hdb, pkbuf, pksiz)) err = true; + tcmapdel(cols); + TCFREE(cbuf); + return !err; +} + +/* Retrieve a record in a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If successful, the return value is a map object of the columns of the corresponding record. */ +static TCMAP *tctdbgetimpl(TCTDB *tdb, const void *pkbuf, int pksiz) { + assert(tdb && pkbuf && pksiz >= 0); + int csiz; + char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); + if (!cbuf) return NULL; + TCMAP *cols = tcmapload(cbuf, csiz); + TCFREE(cbuf); + return cols; +} + +/* Retrieve the value of a column of a record in a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `nbuf' specifies the pointer to the region of the column name. + `nsiz' specifies the size of the region of the column name. + `sp' specifies the pointer to the variable into which the size of the region of the return + value is assigned. + If successful, the return value is the pointer to the region of the value of the column of the + corresponding record. */ +static char *tctdbgetonecol(TCTDB *tdb, const void *pkbuf, int pksiz, + const void *nbuf, int nsiz, int *sp) { + assert(tdb && pkbuf && pksiz >= 0 && nbuf && nsiz >= 0 && sp); + int csiz; + char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); + if (!cbuf) return NULL; + void *rv = tcmaploadone(cbuf, csiz, nbuf, nsiz, sp); + TCFREE(cbuf); + return rv; +} + +/* Add a real number to a column of a record in a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `num' specifies the additional value. + If successful, the return value is the summation value, else, it is Not-a-Number. */ +static double tctdbaddnumber(TCTDB *tdb, const void *pkbuf, int pksiz, double num) { + assert(tdb && pkbuf && pksiz >= 0); + int csiz; + char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); + TCMAP *cols = cbuf ? tcmapload(cbuf, csiz) : tcmapnew2(1); + if (cbuf) { + const char *vbuf = tcmapget2(cols, TDBNUMCNTCOL); + if (vbuf) num += tctdbatof(vbuf); + TCFREE(cbuf); + } + char numbuf[TDBCOLBUFSIZ]; + int len = tcftoa(num, numbuf, TDBCOLBUFSIZ, 6); + if (len >= TDBCOLBUFSIZ) { //truncation + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + num = nan(""); + } else { + while (--len > 0) { + if (numbuf[len] != '0') break; + numbuf[len] = '\0'; + } + if (numbuf[len] == '.') numbuf[len] = '\0'; + tcmapput2(cols, TDBNUMCNTCOL, numbuf); + if (!tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)) num = nan(""); + } + tcmapdel(cols); + + return num; +} + +/* Optimize the file of a table database object. + `tdb' specifies the table database object. + `bnum' specifies the number of elements of the bucket array. + `apow' specifies the size of record alignment by power of 2. + `fpow' specifies the maximum number of elements of the free block pool by power of 2. + `opts' specifies options by bitwise-or. + If successful, the return value is true, else, it is false. */ +static bool tctdboptimizeimpl(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts) { + assert(tdb); + bool err = false; + TCHDB *hdb = tdb->hdb; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + tcmapclear(idx->cc); + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbvanish(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + const char *path = tchdbpath(tdb->hdb); + char *tpath = tcsprintf("%s%ctmp%c%" PRIuMAX "", path, MYEXTCHR, MYEXTCHR, tchdbinode(tdb->hdb)); + TCHDB *thdb = tchdbnew(); + tchdbsettype(thdb, TCDBTTABLE); + HANDLE dbgfd = tchdbdbgfd(tdb->hdb); + if (!INVALIDHANDLE(dbgfd)) tchdbsetdbgfd(thdb, dbgfd); + TCCODEC enc, dec; + void *encop, *decop; + tchdbcodecfunc(hdb, &enc, &encop, &dec, &decop); + if (enc && dec) tchdbsetcodecfunc(thdb, enc, encop, dec, decop); + if (bnum < 1) bnum = tchdbrnum(hdb) * 2 + 1; + if (apow < 0) apow = tclog2l(tchdbalign(hdb)); + if (fpow < 0) fpow = tclog2l(tchdbfbpmax(hdb)); + if (opts == UINT8_MAX) opts = tdb->opts; + uint8_t hopts = 0; + if (opts & TDBTLARGE) hopts |= HDBTLARGE; + if (opts & TDBTDEFLATE) hopts |= HDBTDEFLATE; + if (opts & TDBTBZIP) hopts |= HDBTBZIP; + if (opts & TDBTTCBS) hopts |= HDBTTCBS; + if (opts & TDBTEXCODEC) hopts |= HDBTEXCODEC; + tchdbtune(thdb, bnum, apow, fpow, hopts); + if (tchdbopen(thdb, tpath, HDBOWRITER | HDBOCREAT | HDBOTRUNC) && tchdbcopyopaque(thdb, hdb, 0, -1)) { + if (!tchdbiterinit(hdb)) err = true; + TCXSTR *kxstr = tcxstrnew(); + TCXSTR *vxstr = tcxstrnew(); + while (tchdbiternext3(hdb, kxstr, vxstr)) { + TCMAP *cols = tcmapload(TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr)); + if (!tctdbidxput(tdb, TCXSTRPTR(kxstr), TCXSTRSIZE(kxstr), cols)) err = true; + tcmapdel(cols); + if (!tchdbput(thdb, TCXSTRPTR(kxstr), TCXSTRSIZE(kxstr), + TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr))) { + tctdbsetecode(tdb, tchdbecode(thdb), __FILE__, __LINE__, __func__); + err = true; + } + } + tcxstrdel(vxstr); + tcxstrdel(kxstr); + if (!tchdbclose(thdb)) { + tctdbsetecode(tdb, tchdbecode(thdb), __FILE__, __LINE__, __func__); + err = true; + } + if (!err) { + char *npath = tcstrdup(path); + int omode = (tchdbomode(hdb) & ~HDBOCREAT) & ~HDBOTRUNC; + if (!tchdbclose(hdb)) err = true; + if (!tcunlinkfile(npath)) { + tctdbsetecode(tdb, TCEUNLINK, __FILE__, __LINE__, __func__); + err = true; + } + if (!tcrenamefile(tpath, npath)) { + tctdbsetecode(tdb, TCERENAME, __FILE__, __LINE__, __func__); + err = true; + } + if (!tchdbopen(hdb, npath, omode)) err = true; + TCFREE(npath); + } + } else { + tctdbsetecode(tdb, tchdbecode(thdb), __FILE__, __LINE__, __func__); + err = true; + } + tchdbdel(thdb); + TCFREE(tpath); + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdboptimize(idx->db, -1, -1, -1, -1, -1, UINT8_MAX)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Remove all records of a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +static bool tctdbvanishimpl(TCTDB *tdb) { + assert(tdb); + bool err = false; + if (!tchdbvanish(tdb->hdb)) err = true; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + tcmapclear(idx->cc); + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbvanish(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Copy the database file of a table database object. + `tdb' specifies the table database object. + `path' specifies the path of the destination file. + If successful, the return value is true, else, it is false. */ +static bool tctdbcopyimpl(TCTDB *tdb, const char *path) { + assert(tdb); + bool err = false; + if (!tchdbcopy(tdb->hdb, path)) err = true; + const char *opath = tchdbpath(tdb->hdb); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + const char *ipath; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (*path == '@') { + if (!tcbdbcopy(idx->db, path)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + } else { + ipath = tcbdbpath(idx->db); + if (tcstrfwm(ipath, opath)) { + char *tpath = tcsprintf("%s%s", path, ipath + strlen(opath)); + if (!tcbdbcopy(idx->db, tpath)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + TCFREE(tpath); + } else { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + } + break; + } + } + return !err; +} + +/* Begin the transaction of a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +bool tctdbtranbeginimpl(TCTDB *tdb) { + assert(tdb); + if (!tctdbmemsync(tdb, false)) return false; + if (!tchdbtranbegin(tdb->hdb)) return false; + bool err = false; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbtranbegin(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Commit the transaction of a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +bool tctdbtrancommitimpl(TCTDB *tdb) { + assert(tdb); + bool err = false; + if (!tctdbmemsync(tdb, false)) err = true; + if (!tchdbtrancommit(tdb->hdb)) err = true; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbtrancommit(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Abort the transaction of a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +bool tctdbtranabortimpl(TCTDB *tdb) { + assert(tdb); + bool err = false; + if (!tchdbtranabort(tdb->hdb)) err = true; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + tcmapclear(idx->cc); + break; + } + } + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbtranabort(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +static char* tctdbmaprowldr(TCLIST *tokens, + const char *pkbuf, int pksz, + const char *rowdata, int rowdatasz, + const char *cname, int cnamesz, + void *op, int *vsz) { + char* res = NULL; + if (cname && *cname == '\0') { + TCMEMDUP(res, pkbuf, pksz); + *vsz = pksz; + } else { + res = tcmaploadone(rowdata, rowdatasz, cname, cnamesz, vsz); + } + if (tokens && res) { + const unsigned char *sp = (unsigned char *) res; + const unsigned char *start = sp; + while (sp - start <= *vsz && *sp != '\0') { + while (sp - start <= *vsz && ((*sp != '\0' && *sp <= ' ') || *sp == ',')) { + sp++; + } + const unsigned char *ep = sp; + while (ep - start <= *vsz && *ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) TCLISTPUSH(tokens, sp, ep - sp); + sp = ep; + } + TCFREE(res); + *vsz = 0; + return NULL; + } + return res; +} + +/* Set a column index to a table database object. + `tdb' specifies the table database object. connected as a writer. + `name' specifies the name of a column. + `type' specifies the index type. + If successful, the return value is true, else, it is false. */ +static bool tctdbsetindeximpl(TCTDB *tdb, const char *name, int type, TDBRVALOADER rvldr, void* rvldrop) { + assert(tdb && name); + bool err = false; + bool keep = false; + if (type & TDBITKEEP) { + type &= ~TDBITKEEP; + keep = true; + } + bool done = false; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + char *path; + if (!strcmp(idx->name, name)) { + if (keep) { + tctdbsetecode(tdb, TCEKEEP, __FILE__, __LINE__, __func__); + return false; + } + if (type == TDBITOPT) { + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + if (!tctdbidxsyncicc(tdb, idx, true)) err = true; + break; + } + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdboptimize(idx->db, -1, -1, -1, -1, -1, UINT8_MAX)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + done = true; + break; + } + switch (idx->type) { + case TDBITTOKEN: + case TDBITQGRAM: + tcmapdel(idx->cc); + break; + } + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + path = tcstrdup(tcbdbpath(idx->db)); + tcbdbdel(idx->db); + if (path && !tcunlinkfile(path)) { + tctdbsetecode(tdb, TCEUNLINK, __FILE__, __LINE__, __func__); + err = true; + } + TCFREE(path); + break; + } + TCFREE(idx->name); + tdb->inum--; + inum = tdb->inum; + memmove(idxs + i, idxs + i + 1, sizeof (*idxs) * (inum - i)); + done = true; + break; + } + } + if (type == TDBITOPT || type == TDBITVOID) { + if (!done) { + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + err = true; + } + return !err; + } + TCXSTR *pbuf = tcxstrnew(); + tcxstrprintf(pbuf, "%s%c%s%c%?", tchdbpath(tdb->hdb), MYEXTCHR, TDBIDXSUFFIX, MYEXTCHR, name); + TCREALLOC(tdb->idxs, tdb->idxs, sizeof (tdb->idxs[0]) * (inum + 1)); + TDBIDX *idx = tdb->idxs + inum; + int homode = tchdbomode(tdb->hdb); + int bomode = BDBOWRITER | BDBOCREAT | BDBOTRUNC; + if (homode & HDBONOLCK) bomode |= BDBONOLCK; + if (homode & HDBOLCKNB) bomode |= BDBOLCKNB; + if (homode & HDBOTSYNC) bomode |= BDBOTSYNC; + HANDLE dbgfd = tchdbdbgfd(tdb->hdb); + TCCODEC enc, dec; + void *encop, *decop; + tchdbcodecfunc(tdb->hdb, &enc, &encop, &dec, &decop); + int64_t bbnum = (tchdbbnum(tdb->hdb) / TDBIDXLMEMB) * 4 + TDBIDXLMEMB; + int64_t bxmsiz = tchdbxmsiz(tdb->hdb); + uint8_t opts = tdb->opts; + uint8_t bopts = 0; + if (opts & TDBTLARGE) bopts |= BDBTLARGE; + if (opts & TDBTDEFLATE) bopts |= BDBTDEFLATE; + if (opts & TDBTBZIP) bopts |= BDBTBZIP; + if (opts & TDBTTCBS) bopts |= BDBTTCBS; + if (opts & TDBTEXCODEC) bopts |= BDBTEXCODEC; + switch (type) { + case TDBITLEXICAL: + idx->db = tcbdbnew(); + idx->name = tcstrdup(name); + tcxstrprintf(pbuf, "%clex", MYEXTCHR); + if (!INVALIDHANDLE(dbgfd)) tcbdbsetdbgfd(idx->db, dbgfd); + if (tdb->mmtx) tcbdbsetmutex(idx->db); + if (enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); + tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); + tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); + tcbdbsetxmsiz(idx->db, bxmsiz); + tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); + tcbdbsetlsmax(idx->db, TDBIDXLSMAX); + if (!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + tdb->inum++; + break; + case TDBITDECIMAL: + idx->db = tcbdbnew(); + idx->name = tcstrdup(name); + tcxstrprintf(pbuf, "%cdec", MYEXTCHR); + if (!INVALIDHANDLE(dbgfd)) tcbdbsetdbgfd(idx->db, dbgfd); + if (tdb->mmtx) tcbdbsetmutex(idx->db); + tcbdbsetcmpfunc(idx->db, tccmpdecimal, NULL); + if (enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); + tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); + tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); + tcbdbsetxmsiz(idx->db, bxmsiz); + tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); + tcbdbsetlsmax(idx->db, TDBIDXLSMAX); + if (!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + tdb->inum++; + break; + case TDBITTOKEN: + idx->db = tcbdbnew(); + idx->cc = tcmapnew2(TDBIDXICCBNUM); + idx->name = tcstrdup(name); + tcxstrprintf(pbuf, "%ctok", MYEXTCHR); + if (!INVALIDHANDLE(dbgfd)) tcbdbsetdbgfd(idx->db, dbgfd); + if (tdb->mmtx) tcbdbsetmutex(idx->db); + if (enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); + tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); + tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); + tcbdbsetxmsiz(idx->db, bxmsiz); + tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); + tcbdbsetlsmax(idx->db, TDBIDXLSMAX); + if (!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + tdb->inum++; + break; + case TDBITQGRAM: + idx->db = tcbdbnew(); + idx->cc = tcmapnew2(TDBIDXICCBNUM); + idx->name = tcstrdup(name); + tcxstrprintf(pbuf, "%cqgr", MYEXTCHR); + if (!INVALIDHANDLE(dbgfd)) tcbdbsetdbgfd(idx->db, dbgfd); + if (tdb->mmtx) tcbdbsetmutex(idx->db); + if (enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); + tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); + tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); + tcbdbsetxmsiz(idx->db, bxmsiz); + tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); + tcbdbsetlsmax(idx->db, TDBIDXLSMAX); + if (!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + tdb->inum++; + break; + default: + tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); + err = true; + break; + } + idx->type = type; + if (!err) { + TCHDB *hdb = tdb->hdb; + if (!tchdbiterinit(hdb)) err = true; + void *db = idx->db; + TCXSTR *kxstr = tcxstrnew(); + TCXSTR *vxstr = tcxstrnew(); + int nsiz = strlen(name); + while (tchdbiternext3(hdb, kxstr, vxstr)) { + TCLIST *tokens = (type == TDBITTOKEN) ? tclistnew() : NULL; + int vsiz; + const char *pkbuf = TCXSTRPTR(kxstr); + int pksiz = TCXSTRSIZE(kxstr); + char *vbuf = rvldr(tokens, pkbuf, pksiz, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr), name, nsiz, rvldrop, &vsiz); + if (nsiz < 1) { + switch (type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + assert(vbuf); + if (!tcbdbput(db, pkbuf, pksiz, vbuf, vsiz)) { + tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); + err = true; + } + break; + case TDBITTOKEN: + if (!tctdbidxputtoken2(tdb, idx, pkbuf, pksiz, tokens)) err = true; + break; + case TDBITQGRAM: + assert(vbuf); + if (!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + } + } else { + switch (type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (vbuf && !tctdbidxputone(tdb, idx, pkbuf, pksiz, tctdbidxhash(pkbuf, pksiz), vbuf, vsiz)) err = true; + break; + case TDBITTOKEN: + if (tokens && !tctdbidxputtoken2(tdb, idx, pkbuf, pksiz, tokens)) err = true; + break; + case TDBITQGRAM: + if (vbuf && !tctdbidxputqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + } + } + if (vbuf) TCFREE(vbuf); + if (tokens) tclistdel(tokens); + } + tcxstrdel(vxstr); + tcxstrdel(kxstr); + } + tcxstrdel(pbuf); + return !err; +} + +/* Generate a unique ID number. + `tdb' specifies the table database object. + `inc' specifies the increment of the seed. + The return value is the new unique ID number or -1 on failure. */ +static int64_t tctdbgenuidimpl(TCTDB *tdb, int64_t inc) { + assert(tdb); + uint64_t llnum, uid; + if (inc < 0) { + uid = -inc - 1; + } else { + tchdbreadopaque(tdb->hdb, &llnum, 0, sizeof (llnum)); + if (inc == 0) return TCITOHLL(llnum); + uid = TCITOHLL(llnum) + inc; + } + llnum = TCITOHLL(uid); + tchdbwriteopaque(tdb->hdb, &llnum, 0, sizeof (llnum)); + return uid; +} + +/* Execute the search of a query object. + `qry' specifies the query object. + The return value is a list object of the primary keys of the corresponding records. */ +static TCLIST *tctdbqrysearchimpl(TDBQRY *qry) { + assert(qry); + TCTDB *tdb = qry->tdb; + TCHDB *hdb = tdb->hdb; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + TDBCOND *conds = qry->conds; + int cnum = qry->cnum; + int acnum = cnum; + int max = qry->max; + if (max < INT_MAX - qry->skip) max += qry->skip; + const char *oname = qry->oname; + int otype = qry->otype; + TCXSTR *hint = qry->hint; + TCLIST *res = NULL; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + cond->alive = true; + } + tcxstrclear(hint); + bool isord = oname != NULL; + TDBCOND *mcond = NULL; + TDBIDX *midx = NULL; + TDBCOND *ncond = NULL; + TDBIDX *nidx = NULL; + TDBCOND *scond = NULL; + TDBIDX *sidx = NULL; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (!cond->sign || cond->noidx) continue; + for (int j = 0; j < inum; j++) { + TDBIDX *idx = idxs + j; + if (!strcmp(cond->name, idx->name)) { + switch (idx->type) { + case TDBITLEXICAL: + switch (cond->op) { + case TDBQCSTREQ: + case TDBQCSTRBW: + case TDBQCSTROREQ: + if (!mcond) { + mcond = cond; + midx = idx; + } else if (!ncond) { + ncond = cond; + nidx = idx; + } + break; + default: + if (!scond) { + scond = cond; + sidx = idx; + } + break; + } + break; + case TDBITDECIMAL: + switch (cond->op) { + case TDBQCNUMEQ: + case TDBQCNUMGT: + case TDBQCNUMGE: + case TDBQCNUMLT: + case TDBQCNUMLE: + case TDBQCNUMBT: + case TDBQCNUMOREQ: + if (!mcond) { + mcond = cond; + midx = idx; + } else if (!ncond) { + ncond = cond; + nidx = idx; + } + break; + default: + if (!scond) { + scond = cond; + sidx = idx; + } + break; + } + break; + case TDBITTOKEN: + switch (cond->op) { + case TDBQCSTRAND: + case TDBQCSTROR: + if (!mcond) { + mcond = cond; + midx = idx; + } else if (!ncond) { + ncond = cond; + nidx = idx; + } + break; + } + break; + case TDBITQGRAM: + switch (cond->op) { + case TDBQCFTSPH: + if (!mcond) { + mcond = cond; + midx = idx; + } else if (!ncond) { + ncond = cond; + nidx = idx; + } + break; + } + break; + } + } + } + } + if (mcond) { + res = tclistnew(); + mcond->alive = false; + acnum--; + TCMAP *nmap = NULL; + if (ncond) { + ncond->alive = false; + acnum--; + nmap = tctdbqryidxfetch(qry, ncond, nidx); + max = tclmin(max, TCMAPRNUM(nmap)); + } + const char *expr = mcond->expr; + int esiz = mcond->esiz; + TDBCOND *ucond = NULL; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (!cond->alive) continue; + if (ucond) { + ucond = NULL; + break; + } + ucond = cond; + } + bool trim = *midx->name != '\0'; + if (mcond->op == TDBQCSTREQ) { + tcxstrprintf(hint, "using an index: \"%s\" asc (STREQ)\n", mcond->name); + BDBCUR *cur = tcbdbcurnew(midx->db); + tcbdbcurjump(cur, expr, esiz + trim); + if (oname && !strcmp(oname, mcond->name)) oname = NULL; + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + trim = *midx->name != '\0'; + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz == esiz && !memcmp(kbuf, expr, esiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (mcond->op == TDBQCSTRBW) { + tcxstrprintf(hint, "using an index: \"%s\" asc (STRBW)\n", mcond->name); + BDBCUR *cur = tcbdbcurnew(midx->db); + tcbdbcurjump(cur, expr, esiz + trim); + bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQOSTRASC); + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz >= esiz && !memcmp(kbuf, expr, esiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + if (oname && !strcmp(oname, mcond->name)) { + if (otype == TDBQOSTRASC) { + oname = NULL; + } else if (otype == TDBQOSTRDESC) { + tclistinvert(res); + oname = NULL; + } + } + } else if (mcond->op == TDBQCSTROREQ) { + tcxstrprintf(hint, "using an index: \"%s\" skip (STROREQ)\n", mcond->name); + BDBCUR *cur = tcbdbcurnew(midx->db); + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + tclistsort(tokens); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + if (oname && !strcmp(oname, mcond->name)) { + if (otype == TDBQOSTRASC) { + oname = NULL; + } else if (otype == TDBQOSTRDESC) { + tclistinvert(tokens); + oname = NULL; + } + } + int tnum = TCLISTNUM(tokens); + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + for (int i = 0; (all || TCLISTNUM(res) < max) && i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + tcbdbcurjump(cur, token, tsiz + trim); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz == tsiz && !memcmp(kbuf, token, tsiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } else { + break; + } + tcbdbcurnext(cur); + } + } + tclistdel(tokens); + tcbdbcurdel(cur); + } else if (mcond->op == TDBQCNUMEQ) { + tcxstrprintf(hint, "using an index: \"%s\" asc (NUMEQ)\n", mcond->name); + BDBCUR *cur = tcbdbcurnew(midx->db); + if (oname && !strcmp(oname, mcond->name)) oname = NULL; + long double xnum = tctdbatof(expr); + tctdbqryidxcurjumpnum(cur, expr, esiz, true); + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (tctdbatof(kbuf) == xnum) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (mcond->op == TDBQCNUMGT || mcond->op == TDBQCNUMGE) { + if (oname && !strcmp(oname, mcond->name) && otype == TDBQONUMDESC) { + tcxstrprintf(hint, "using an index: \"%s\" desc (NUMGT/NUMGE)\n", mcond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(midx->db); + tcbdbcurlast(cur); + if (max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while (TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + long double knum = tctdbatof(kbuf); + if (knum < xnum) break; + if (knum > xnum || (knum >= xnum && mcond->op == TDBQCNUMGE)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } + tcbdbcurprev(cur); + } + tcbdbcurdel(cur); + oname = NULL; + } else { + tcxstrprintf(hint, "using an index: \"%s\" asc (NUMGT/NUMGE)\n", mcond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(midx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, true); + bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQONUMASC); + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + long double knum = tctdbatof(kbuf); + if (knum > xnum || (knum >= xnum && mcond->op == TDBQCNUMGE)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + if (!all) oname = NULL; + } + } else if (mcond->op == TDBQCNUMLT || mcond->op == TDBQCNUMLE) { + if (oname && !strcmp(oname, mcond->name) && otype == TDBQONUMASC) { + tcxstrprintf(hint, "using an index: \"%s\" asc (NUMLT/NUMLE)\n", mcond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(midx->db); + tcbdbcurfirst(cur); + if (max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while (TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + long double knum = tctdbatof(kbuf); + if (knum > xnum) break; + if (knum < xnum || (knum <= xnum && mcond->op == TDBQCNUMLE)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + oname = NULL; + } else { + tcxstrprintf(hint, "using an index: \"%s\" desc (NUMLT/NUMLE)\n", mcond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(midx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, false); + bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQONUMDESC); + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + long double knum = tctdbatof(kbuf); + if (knum < xnum || (knum <= xnum && mcond->op == TDBQCNUMLE)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } + tcbdbcurprev(cur); + } + tcbdbcurdel(cur); + if (!all) oname = NULL; + } + } else if (mcond->op == TDBQCNUMBT) { + tcxstrprintf(hint, "using an index: \"%s\" asc (NUMBT)\n", mcond->name); + while (*expr == ' ' || *expr == ',') { + expr++; + } + const char *pv = expr; + while (*pv != '\0' && *pv != ' ' && *pv != ',') { + pv++; + } + esiz = pv - expr; + if (*pv != ' ' && *pv != ',') pv = " "; + pv++; + while (*pv == ' ' || *pv == ',') { + pv++; + } + long double lower = tctdbatof(expr); + long double upper = tctdbatof(pv); + if (lower > upper) { + expr = pv; + esiz = strlen(expr); + long double swap = lower; + lower = upper; + upper = swap; + } + BDBCUR *cur = tcbdbcurnew(midx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, true); + bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQONUMASC); + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (tctdbatof(kbuf) > upper) break; + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + if (oname && !strcmp(oname, mcond->name)) { + if (otype == TDBQONUMASC) { + oname = NULL; + } else if (otype == TDBQONUMDESC) { + tclistinvert(res); + oname = NULL; + } + } + } else if (mcond->op == TDBQCNUMOREQ) { + tcxstrprintf(hint, "using an index: \"%s\" skip (NUMOREQ)\n", mcond->name); + BDBCUR *cur = tcbdbcurnew(midx->db); + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + tclistsortex(tokens, tdbcmppkeynumasc); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (tctdbatof(TCLISTVALPTR(tokens, i)) == tctdbatof(TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + if (oname && !strcmp(oname, mcond->name)) { + if (otype == TDBQONUMASC) { + oname = NULL; + } else if (otype == TDBQONUMDESC) { + tclistinvert(tokens); + oname = NULL; + } + } + int tnum = TCLISTNUM(tokens); + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + for (int i = 0; (all || TCLISTNUM(res) < max) && i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + long double xnum = tctdbatof(token); + tctdbqryidxcurjumpnum(cur, token, tsiz, true); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (tctdbatof(kbuf) == xnum) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + int nsiz; + if (!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + } else { + break; + } + tcbdbcurnext(cur); + } + } + tclistdel(tokens); + tcbdbcurdel(cur); + } else if (mcond->op == TDBQCSTRAND || mcond->op == TDBQCSTROR) { + tcxstrprintf(hint, "using an index: \"%s\" inverted (%s)\n", + mcond->name, mcond->op == TDBQCSTRAND ? "STRAND" : "STROR"); + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + tclistsort(tokens); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + TCMAP *tres = tctdbidxgetbytokens(tdb, midx, tokens, mcond->op, hint); + tcmapiterinit(tres); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcmapiternext(tres, &ksiz)) != NULL) { + int nsiz; + if (!nmap || tcmapget(nmap, kbuf, ksiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, kbuf, ksiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, kbuf, ksiz)) TCLISTPUSH(res, kbuf, ksiz); + } else if (tctdbqryallcondmatch(qry, kbuf, ksiz)) { + TCLISTPUSH(res, kbuf, ksiz); + } + } + } + tcmapdel(tres); + tclistdel(tokens); + } else if (mcond->op == TDBQCFTSPH) { + tcxstrprintf(hint, "using an index: \"%s\" inverted (FTS)\n", mcond->name); + TCMAP *tres = tctdbidxgetbyfts(tdb, midx, mcond, hint); + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + tcmapiterinit(tres); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcmapiternext(tres, &ksiz)) != NULL) { + int nsiz; + if (!nmap || tcmapget(nmap, kbuf, ksiz, &nsiz)) { + if (acnum < 1) { + TCLISTPUSH(res, kbuf, ksiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, kbuf, ksiz)) TCLISTPUSH(res, kbuf, ksiz); + } else if (tctdbqryallcondmatch(qry, kbuf, ksiz)) { + TCLISTPUSH(res, kbuf, ksiz); + } + } + } + tcmapdel(tres); + } + if (nmap) tcmapdel(nmap); + } + if (!res && scond) { + res = tclistnew(); + scond->alive = false; + acnum--; + TDBCOND *ucond = NULL; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (!cond->alive) continue; + if (ucond) { + ucond = NULL; + break; + } + ucond = cond; + } + bool trim = *sidx->name != '\0'; + bool asc = true; + bool all = true; + if (!oname) { + all = false; + } else if (!strcmp(oname, scond->name)) { + switch (sidx->type) { + case TDBITLEXICAL: + switch (otype) { + case TDBQOSTRASC: + asc = true; + all = false; + break; + case TDBQOSTRDESC: + asc = false; + all = false; + break; + } + break; + case TDBITDECIMAL: + switch (otype) { + case TDBQONUMASC: + asc = true; + all = false; + break; + case TDBQONUMDESC: + asc = false; + all = false; + break; + } + break; + } + } + if (asc) { + tcxstrprintf(hint, "using an index: \"%s\" asc (all)\n", scond->name); + BDBCUR *cur = tcbdbcurnew(sidx->db); + tcbdbcurfirst(cur); + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz < 0) break; + if (tctdbqrycondmatch(scond, kbuf, ksiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else { + tcxstrprintf(hint, "using an index: \"%s\" desc (all)\n", scond->name); + BDBCUR *cur = tcbdbcurnew(sidx->db); + tcbdbcurlast(cur); + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while ((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz < 0) break; + if (tctdbqrycondmatch(scond, kbuf, ksiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + } + tcbdbcurprev(cur); + } + tcbdbcurdel(cur); + } + if (!all) oname = NULL; + } + if (!res && oname && max < tchdbrnum(hdb) * TDBORDRATIO) { + TDBIDX *oidx = NULL; + bool asc = true; + for (int i = 0; !oidx && i < inum; i++) { + TDBIDX *idx = idxs + i; + if (strcmp(idx->name, oname)) continue; + switch (idx->type) { + case TDBITLEXICAL: + switch (otype) { + case TDBQOSTRASC: + oidx = idx; + asc = true; + break; + case TDBQOSTRDESC: + oidx = idx; + asc = false; + break; + } + break; + case TDBITDECIMAL: + switch (otype) { + case TDBQONUMASC: + oidx = idx; + asc = true; + break; + case TDBQONUMDESC: + oidx = idx; + asc = false; + break; + } + break; + } + } + if (oidx) { + res = tclistnew(); + TDBCOND *ucond = NULL; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (!cond->alive) continue; + if (ucond) { + ucond = NULL; + break; + } + ucond = cond; + } + bool trim = *oidx->name != '\0'; + if (asc) { + tcxstrprintf(hint, "using an index: \"%s\" asc (order)\n", oname); + BDBCUR *cur = tcbdbcurnew(oidx->db); + tcbdbcurfirst(cur); + tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while (TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz < 0) break; + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else { + tcxstrprintf(hint, "using an index: \"%s\" desc (order)\n", oname); + BDBCUR *cur = tcbdbcurnew(oidx->db); + tcbdbcurlast(cur); + tcxstrprintf(hint, "limited matching: %d\n", max); + const char *kbuf; + int ksiz; + while (TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz < 0) break; + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + if (acnum < 1) { + TCLISTPUSH(res, vbuf, vsiz); + } else if (ucond) { + if (tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); + } else if (tctdbqryallcondmatch(qry, vbuf, vsiz)) { + TCLISTPUSH(res, vbuf, vsiz); + } + tcbdbcurprev(cur); + } + tcbdbcurdel(cur); + } + int rnum = TCLISTNUM(res); + if (rnum >= max || tcbdbrnum(oidx->db) >= tchdbrnum(hdb)) { + oname = NULL; + } else { + tcxstrprintf(hint, "abort the result: %d\n", rnum); + tclistdel(res); + res = NULL; + } + } + } + if (!res) { + tcxstrprintf(hint, "scanning the whole table\n"); + res = tclistnew(); + TDBCOND *ucond = NULL; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (!cond->alive) continue; + if (ucond) { + ucond = NULL; + break; + } + ucond = cond; + } + char *lkbuf = NULL; + int lksiz = 0; + char *pkbuf; + int pksiz; + const char *cbuf; + int csiz; + bool all = oname != NULL; + if (!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); + while ((all || TCLISTNUM(res) < max) && + (pkbuf = tchdbgetnext3(hdb, lkbuf, lksiz, &pksiz, &cbuf, &csiz)) != NULL) { + if (ucond) { + if (ucond->nsiz < 1) { + char *tkbuf; + TCMEMDUP(tkbuf, pkbuf, pksiz); + if (tctdbqrycondmatch(ucond, tkbuf, pksiz) == ucond->sign) + TCLISTPUSH(res, pkbuf, pksiz); + TCFREE(tkbuf); + } else { + int vsiz; + char *vbuf = tcmaploadone(cbuf, csiz, ucond->name, ucond->nsiz, &vsiz); + if (vbuf) { + if (tctdbqrycondmatch(ucond, vbuf, vsiz) == ucond->sign) + TCLISTPUSH(res, pkbuf, pksiz); + TCFREE(vbuf); + } else { + if (!ucond->sign) TCLISTPUSH(res, pkbuf, pksiz); + } + } + } else { + TCMAP *cols = tcmapload(cbuf, csiz); + bool ok = true; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (cond->nsiz < 1) { + char *tkbuf; + TCMEMDUP(tkbuf, pkbuf, pksiz); + if (tctdbqrycondmatch(cond, tkbuf, pksiz) != cond->sign) { + TCFREE(tkbuf); + ok = false; + break; + } + TCFREE(tkbuf); + } else { + int vsiz; + const char *vbuf = tcmapget(cols, cond->name, cond->nsiz, &vsiz); + if (vbuf) { + if (tctdbqrycondmatch(cond, vbuf, vsiz) != cond->sign) { + ok = false; + break; + } + } else { + if (cond->sign) { + ok = false; + break; + } + } + } + } + if (ok) TCLISTPUSH(res, pkbuf, pksiz); + tcmapdel(cols); + } + TCFREE(lkbuf); + lkbuf = pkbuf; + lksiz = pksiz; + } + TCFREE(lkbuf); + } + int rnum = TCLISTNUM(res); + tcxstrprintf(hint, "result set size: %d\n", rnum); + if (oname) { + if (*oname == '\0') { + tcxstrprintf(hint, "sorting the result set: \"%s\"\n", oname); + switch (otype) { + case TDBQOSTRASC: + tclistsort(res); + break; + case TDBQOSTRDESC: + tclistsort(res); + tclistinvert(res); + break; + case TDBQONUMASC: + tclistsortex(res, tdbcmppkeynumasc); + break; + case TDBQONUMDESC: + tclistsortex(res, tdbcmppkeynumdesc); + break; + } + } else { + tcxstrprintf(hint, "sorting the result set: \"%s\"\n", oname); + TDBSORTREC *keys; + TCMALLOC(keys, sizeof (*keys) * rnum + 1); + int onsiz = strlen(oname); + for (int i = 0; i < rnum; i++) { + TDBSORTREC *key = keys + i; + const char *kbuf; + int ksiz; + TCLISTVAL(kbuf, res, i, ksiz); + char *vbuf = NULL; + int vsiz = 0; + int csiz; + char *cbuf = tchdbget(hdb, kbuf, ksiz, &csiz); + if (cbuf) { + vbuf = tcmaploadone(cbuf, csiz, oname, onsiz, &vsiz); + TCFREE(cbuf); + } + key->kbuf = kbuf; + key->ksiz = ksiz; + key->vbuf = vbuf; + key->vsiz = vsiz; + } + int (*compar)(const TDBSORTREC *a, const TDBSORTREC * b) = NULL; + switch (otype) { + case TDBQOSTRASC: + compar = tdbcmpsortrecstrasc; + break; + case TDBQOSTRDESC: + compar = tdbcmpsortrecstrdesc; + break; + case TDBQONUMASC: + compar = tdbcmpsortrecnumasc; + break; + case TDBQONUMDESC: + compar = tdbcmpsortrecnumdesc; + break; + } + if (compar) { + if (max <= rnum / 16) { + tctopsort(keys, rnum, sizeof (*keys), max, (int (*)(const void *, const void *))compar); + } else { + qsort(keys, rnum, sizeof (*keys), (int (*)(const void *, const void *))compar); + } + } + TCLIST *nres = tclistnew2(rnum); + for (int i = 0; i < rnum; i++) { + TDBSORTREC *key = keys + i; + TCLISTPUSH(nres, key->kbuf, key->ksiz); + TCFREE(key->vbuf); + } + tclistdel(res); + res = nres; + TCFREE(keys); + } + } else if (isord) { + tcxstrprintf(hint, "leaving the index order\n"); + } else { + tcxstrprintf(hint, "leaving the natural order\n"); + } + if (qry->skip > 0) { + int left = tclmin(TCLISTNUM(res), qry->skip); + while (left-- > 0) { + int rsiz; + TCFREE(tclistshift(res, &rsiz)); + } + max -= qry->skip; + } + if (TCLISTNUM(res) > max) { + int left = TCLISTNUM(res) - max; + while (left-- > 0) { + int rsiz; + TCFREE(tclistpop(res, &rsiz)); + } + } + qry->count = TCLISTNUM(res); + return res; +} + +/* Fetch record keys from an index matching to a condition. + `qry' specifies the query object. + `cond' specifies the condition object. + `idx' specifies an index object. + The return value is a map object containing primary keys of the corresponding records. */ +static TCMAP *tctdbqryidxfetch(TDBQRY *qry, TDBCOND *cond, TDBIDX *idx) { + assert(qry && cond && idx); + TCTDB *tdb = qry->tdb; + TCHDB *hdb = tdb->hdb; + TCXSTR *hint = qry->hint; + const char *expr = cond->expr; + int esiz = cond->esiz; + bool trim = *idx->name != '\0'; + TCMAP *nmap = tcmapnew2(tclmin(TDBDEFBNUM, tchdbrnum(hdb)) / 4 + 1); + if (cond->op == TDBQCSTREQ) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" one (STREQ)\n", cond->name); + BDBCUR *cur = tcbdbcurnew(idx->db); + tcbdbcurjump(cur, expr, esiz + trim); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz == esiz && !memcmp(kbuf, expr, esiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (cond->op == TDBQCSTRBW) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (STRBW)\n", cond->name); + BDBCUR *cur = tcbdbcurnew(idx->db); + tcbdbcurjump(cur, expr, esiz + trim); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz >= esiz && !memcmp(kbuf, expr, esiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (cond->op == TDBQCSTROREQ) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" skip (STROREQ)\n", cond->name); + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + tclistsort(tokens); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + int tnum = TCLISTNUM(tokens); + for (int i = 0; i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + BDBCUR *cur = tcbdbcurnew(idx->db); + tcbdbcurjump(cur, token, tsiz + trim); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (trim) ksiz -= 3; + if (ksiz == tsiz && !memcmp(kbuf, token, tsiz)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } + tclistdel(tokens); + } else if (cond->op == TDBQCNUMEQ) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMEQ)\n", cond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(idx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, true); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (tctdbatof(kbuf) == xnum) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (cond->op == TDBQCNUMGT || cond->op == TDBQCNUMGE) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMGT/NUMGE)\n", cond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(idx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, true); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + long double knum = tctdbatof(kbuf); + if (knum > xnum || (knum >= xnum && cond->op == TDBQCNUMGE)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (cond->op == TDBQCNUMLT || cond->op == TDBQCNUMLE) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" desc (NUMLT/NUMLE)\n", cond->name); + long double xnum = tctdbatof(expr); + BDBCUR *cur = tcbdbcurnew(idx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, false); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + long double knum = tctdbatof(kbuf); + if (knum < xnum || (knum <= xnum && cond->op == TDBQCNUMLE)) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } + tcbdbcurprev(cur); + } + tcbdbcurdel(cur); + } else if (cond->op == TDBQCNUMBT) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMBT)\n", cond->name); + while (*expr == ' ' || *expr == ',') { + expr++; + } + const char *pv = expr; + while (*pv != '\0' && *pv != ' ' && *pv != ',') { + pv++; + } + esiz = pv - expr; + if (*pv != ' ' && *pv != ',') pv = " "; + pv++; + while (*pv == ' ' || *pv == ',') { + pv++; + } + long double lower = tctdbatof(expr); + long double upper = tctdbatof(pv); + if (lower > upper) { + expr = pv; + esiz = strlen(expr); + long double swap = lower; + lower = upper; + upper = swap; + } + BDBCUR *cur = tcbdbcurnew(idx->db); + tctdbqryidxcurjumpnum(cur, expr, esiz, true); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (tctdbatof(kbuf) > upper) break; + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (cond->op == TDBQCNUMOREQ) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" skip (NUMOREQ)\n", cond->name); + BDBCUR *cur = tcbdbcurnew(idx->db); + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + tclistsortex(tokens, tdbcmppkeynumasc); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (tctdbatof(TCLISTVALPTR(tokens, i)) == tctdbatof(TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + int tnum = TCLISTNUM(tokens); + for (int i = 0; i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + long double xnum = tctdbatof(token); + tctdbqryidxcurjumpnum(cur, token, tsiz, true); + const char *kbuf; + int ksiz; + while ((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL) { + if (tctdbatof(kbuf) == xnum) { + int vsiz; + const char *vbuf = tcbdbcurval3(cur, &vsiz); + tcmapputkeep(nmap, vbuf, vsiz, "", 0); + } else { + break; + } + tcbdbcurnext(cur); + } + } + tclistdel(tokens); + tcbdbcurdel(cur); + } else if (cond->op == TDBQCSTRAND || cond->op == TDBQCSTROR) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" inverted (%s)\n", + cond->name, cond->op == TDBQCSTRAND ? "STRAND" : "STROR"); + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + tclistsort(tokens); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + tcmapdel(nmap); + nmap = tctdbidxgetbytokens(tdb, idx, tokens, cond->op, hint); + tclistdel(tokens); + } else if (cond->op == TDBQCFTSPH) { + tcxstrprintf(hint, "using an auxiliary index: \"%s\" inverted (FTS)\n", cond->name); + tcmapdel(nmap); + nmap = tctdbidxgetbyfts(tdb, idx, cond, hint); + } + tcxstrprintf(hint, "auxiliary result set size: %" PRIdMAX "\n", (int64_t) TCMAPRNUM(nmap)); + return nmap; +} + +/* Convert a string to a real number. + `str' specifies the string. + The return value is the real number. */ +long double tctdbatof(const char *str) { + assert(str); + while (*str > '\0' && *str <= ' ') { + str++; + } + int sign = 1; + if (*str == '-') { + str++; + sign = -1; + } else if (*str == '+') { + str++; + } + if (tcstrifwm(str, "inf")) return HUGE_VALL * sign; + if (tcstrifwm(str, "nan")) return nanl(""); + long double num = 0; + int col = 0; + while (*str != '\0') { + if (*str < '0' || *str > '9') break; + num = num * 10 + *str - '0'; + str++; + if (num > 0) col++; + } + if (*str == '.') { + str++; + long double fract = 0.0; + long double base = 10; + while (col < TDBNUMCOLMAX && *str != '\0') { + if (*str < '0' || *str > '9') break; + fract += (*str - '0') / base; + str++; + col++; + base *= 10; + } + num += fract; + } + return num * sign; +} + +/* Jump a cursor to the record of a key. + `cur' specifies the cursor object. + `expr' specifies the expression. + `esiz' specifies the size of the expression. + `first' specifies whether to jump the first candidate. + If successful, the return value is true, else, it is false. */ +bool tctdbqryidxcurjumpnum(BDBCUR *cur, const char *expr, int esiz, bool first) { + assert(cur && expr && esiz >= 0); + char stack[TCNUMBUFSIZ], *rbuf; + if (esiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, esiz + 1); + } + rbuf[0] = first ? 0x00 : 0x7f; + memcpy(rbuf + 1, expr, esiz); + bool err = false; + if (first) { + if (!tcbdbcurjump(cur, rbuf, esiz + 1)) err = true; + } else { + if (!tcbdbcurjumpback(cur, rbuf, esiz + 1)) err = true; + } + if (rbuf != stack) TCFREE(rbuf); + return !err; +} + +/* Check matching of one condition and a record. + `qry' specifies the query object. + `cond' specifies the condition object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If they matches, the return value is true, else it is false. */ +static bool tctdbqryonecondmatch(TDBQRY *qry, TDBCOND *cond, const char *pkbuf, int pksiz) { + assert(qry && cond && pkbuf && pksiz >= 0); + if (cond->nsiz < 1) return tctdbqrycondmatch(cond, pkbuf, pksiz) == cond->sign; + int csiz; + char *cbuf = tchdbget(qry->tdb->hdb, pkbuf, pksiz, &csiz); + if (!cbuf) return false; + bool rv; + int vsiz; + char *vbuf = tcmaploadone(cbuf, csiz, cond->name, cond->nsiz, &vsiz); + if (vbuf) { + rv = tctdbqrycondmatch(cond, vbuf, vsiz) == cond->sign; + TCFREE(vbuf); + } else { + rv = !cond->sign; + } + TCFREE(cbuf); + return rv; +} + +/* Check matching of all conditions and a record. + `qry' specifies the query object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If they matches, the return value is true, else it is false. */ +static bool tctdbqryallcondmatch(TDBQRY *qry, const char *pkbuf, int pksiz) { + assert(qry && pkbuf && pksiz >= 0); + TCTDB *tdb = qry->tdb; + TDBCOND *conds = qry->conds; + int cnum = qry->cnum; + int csiz; + char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); + if (!cbuf) return false; + TCMAP *cols = tcmapload(cbuf, csiz); + bool ok = true; + for (int i = 0; i < cnum; i++) { + TDBCOND *cond = conds + i; + if (!cond->alive) continue; + if (cond->nsiz < 1) { + if (tctdbqrycondmatch(cond, pkbuf, pksiz) != cond->sign) { + ok = false; + break; + } + } else { + int vsiz; + const char *vbuf = tcmapget(cols, cond->name, cond->nsiz, &vsiz); + if (vbuf) { + if (tctdbqrycondmatch(cond, vbuf, vsiz) != cond->sign) { + ok = false; + break; + } + } else { + if (cond->sign) { + ok = false; + break; + } + } + } + } + tcmapdel(cols); + TCFREE(cbuf); + return ok; +} + +/* Check matching of a operand expression and a column value. + `cond' specifies the condition object. + `vbuf' specifies the column value. + `vsiz' specifies the size of the column value. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondmatch(TDBCOND *cond, const char *vbuf, int vsiz) { + assert(cond && vbuf && vsiz >= 0); + bool hit = false; + switch (cond->op) { + case TDBQCSTREQ: + hit = vsiz == cond->esiz && !memcmp(vbuf, cond->expr, cond->esiz); + break; + case TDBQCSTRINC: + hit = strstr(vbuf, cond->expr) != NULL; + break; + case TDBQCSTRBW: + hit = tcstrfwm(vbuf, cond->expr); + break; + case TDBQCSTREW: + hit = tcstrbwm(vbuf, cond->expr); + break; + case TDBQCSTRAND: + hit = tctdbqrycondcheckstrand(vbuf, cond->expr); + break; + case TDBQCSTROR: + hit = tctdbqrycondcheckstror(vbuf, cond->expr); + break; + case TDBQCSTROREQ: + hit = tctdbqrycondcheckstroreq(vbuf, cond->expr); + break; + case TDBQCSTRRX: + hit = cond->regex && regexec(cond->regex, vbuf, 0, NULL, 0) == 0; + break; + case TDBQCNUMEQ: + hit = tctdbatof(vbuf) == tctdbatof(cond->expr); + break; + case TDBQCNUMGT: + hit = tctdbatof(vbuf) > tctdbatof(cond->expr); + break; + case TDBQCNUMGE: + hit = tctdbatof(vbuf) >= tctdbatof(cond->expr); + break; + case TDBQCNUMLT: + hit = tctdbatof(vbuf) < tctdbatof(cond->expr); + break; + case TDBQCNUMLE: + hit = tctdbatof(vbuf) <= tctdbatof(cond->expr); + break; + case TDBQCNUMBT: + hit = tctdbqrycondchecknumbt(vbuf, cond->expr); + break; + case TDBQCNUMOREQ: + hit = tctdbqrycondchecknumoreq(vbuf, cond->expr); + break; + case TDBQCFTSPH: + hit = tctdbqrycondcheckfts(vbuf, vsiz, cond); + break; + } + return hit; +} + +/* Check whether a string includes all tokens in another string. + `vbuf' specifies the column value. + `expr' specifies the operand expression. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondcheckstrand(const char *vbuf, const char *expr) { + assert(vbuf && expr); + const unsigned char *sp = (unsigned char *) expr; + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) { + bool hit = false; + const unsigned char *rp = (unsigned char *) vbuf; + while (*rp != '\0') { + const unsigned char *pp; + for (pp = sp; pp < ep; pp++, rp++) { + if (*pp != *rp) break; + } + if (pp == ep && (*rp <= ' ' || *rp == ',')) { + hit = true; + break; + } + while (*rp > ' ' && *rp != ',') { + rp++; + } + while ((*rp != '\0' && *rp <= ' ') || *rp == ',') { + rp++; + } + } + if (!hit) return false; + } + sp = ep; + } + return true; +} + +/* Check whether a string includes at least one token in another string. + `vbuf' specifies the target value. + `expr' specifies the operation value. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondcheckstror(const char *vbuf, const char *expr) { + assert(vbuf && expr); + const unsigned char *sp = (unsigned char *) expr; + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) { + bool hit = false; + const unsigned char *rp = (unsigned char *) vbuf; + while (*rp != '\0') { + const unsigned char *pp; + for (pp = sp; pp < ep; pp++, rp++) { + if (*pp != *rp) break; + } + if (pp == ep && (*rp <= ' ' || *rp == ',')) { + hit = true; + break; + } + while (*rp > ' ' && *rp != ',') { + rp++; + } + while ((*rp != '\0' && *rp <= ' ') || *rp == ',') { + rp++; + } + } + if (hit) return true; + } + sp = ep; + } + return false; +} + +/* Check whether a string is equal to at least one token in another string. + `vbuf' specifies the target value. + `expr' specifies the operation value. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondcheckstroreq(const char *vbuf, const char *expr) { + assert(vbuf && expr); + const unsigned char *sp = (unsigned char *) expr; + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) { + const unsigned char *rp; + for (rp = (unsigned char *) vbuf; *rp != '\0'; rp++) { + if (*sp != *rp || sp >= ep) break; + sp++; + } + if (*rp == '\0' && sp == ep) return true; + } + sp = ep; + } + return false; +} + +/* Check whether a decimal string is between two tokens in another string. + `vbuf' specifies the target value. + `expr' specifies the operation value. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondchecknumbt(const char *vbuf, const char *expr) { + assert(vbuf && expr); + while (*expr == ' ' || *expr == ',') { + expr++; + } + const char *pv = expr; + while (*pv != '\0' && *pv != ' ' && *pv != ',') { + pv++; + } + if (*pv != ' ' && *pv != ',') pv = " "; + pv++; + while (*pv == ' ' || *pv == ',') { + pv++; + } + long double val = tctdbatof(vbuf); + long double lower = tctdbatof(expr); + long double upper = tctdbatof(pv); + if (lower > upper) { + long double swap = lower; + lower = upper; + upper = swap; + } + return val >= lower && val <= upper; +} + +/* Check whether a number is equal to at least one token in another string. + `vbuf' specifies the target value. + `expr' specifies the operation value. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondchecknumoreq(const char *vbuf, const char *expr) { + assert(vbuf && expr); + long double vnum = tctdbatof(vbuf); + const char *sp = expr; + while (*sp != '\0') { + while (*sp == ' ' || *sp == ',') { + sp++; + } + const char *ep = sp; + while (*ep != '\0' && *ep != ' ' && *ep != ',') { + ep++; + } + if (ep > sp && vnum == tctdbatof(sp)) return true; + sp = ep; + } + return false; +} + +/* Check whether a text matches a condition. + `vbuf' specifies the target value. + `vsiz' specifies the size of the target value. + `cond' specifies the condition object. + If they matches, the return value is true, else it is false. */ +static bool tctdbqrycondcheckfts(const char *vbuf, int vsiz, TDBCOND *cond) { + assert(vbuf && cond); + TDBFTSUNIT *ftsunits = cond->ftsunits; + int ftsnum = cond->ftsnum; + if (ftsnum < 1) return false; + if (!ftsunits[0].sign) return false; + char astack[TDBCOLBUFSIZ]; + uint16_t *ary; + int asiz = sizeof (*ary) * (vsiz + 1); + if (asiz < sizeof (astack)) { + ary = (uint16_t *) astack; + } else { + TCMALLOC(ary, asiz + 1); + } + int anum; + tcstrutftoucs(vbuf, ary, &anum); + anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); + char sstack[TDBCOLBUFSIZ], *str; + int ssiz = anum * 3 + 1; + if (ssiz < sizeof (sstack)) { + str = sstack; + } else { + TCMALLOC(str, ssiz + 1); + } + tcstrucstoutf(ary, anum, str); + bool ok = true; + for (int i = 0; i < ftsnum; i++) { + TDBFTSUNIT *ftsunit = ftsunits + i; + TCLIST *tokens = ftsunit->tokens; + int tnum = TCLISTNUM(tokens); + bool hit = false; + for (int j = 0; j < tnum; j++) { + if (strstr(str, TCLISTVALPTR(tokens, j))) { + hit = true; + break; + } + } + if (hit != ftsunit->sign) ok = false; + } + if (str != sstack) TCFREE(str); + if (ary != (uint16_t *) astack) TCFREE(ary); + return ok; +} + +/* Compare two primary keys by number ascending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +int tdbcmppkeynumasc(const TCLISTDATUM *a, const TCLISTDATUM *b) { + assert(a && b); + return tccmpdecimal(a->ptr, a->size, b->ptr, b->size, NULL); +} + +/* Compare two primary keys by number descending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +int tdbcmppkeynumdesc(const TCLISTDATUM *a, const TCLISTDATUM *b) { + assert(a && b); + return tccmpdecimal(b->ptr, b->size, a->ptr, a->size, NULL); +} + +/* Compare two sort records by string ascending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tdbcmpsortrecstrasc(const TDBSORTREC *a, const TDBSORTREC *b) { + assert(a && b); + if (!a->vbuf) { + if (!b->vbuf) return 0; + return 1; + } + if (!b->vbuf) { + if (!a->vbuf) return 0; + return -1; + } + int rv; + TCCMPLEXICAL(rv, a->vbuf, a->vsiz, b->vbuf, b->vsiz); + return rv; +} + +/* Compare two sort records by string descending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tdbcmpsortrecstrdesc(const TDBSORTREC *a, const TDBSORTREC *b) { + assert(a && b); + if (!a->vbuf) { + if (!b->vbuf) return 0; + return 1; + } + if (!b->vbuf) { + if (!a->vbuf) return 0; + return -1; + } + int rv; + TCCMPLEXICAL(rv, a->vbuf, a->vsiz, b->vbuf, b->vsiz); + return -rv; +} + +/* Compare two sort records by number ascending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tdbcmpsortrecnumasc(const TDBSORTREC *a, const TDBSORTREC *b) { + assert(a && b); + if (!a->vbuf) { + if (!b->vbuf) return 0; + return 1; + } + if (!b->vbuf) { + if (!a->vbuf) return 0; + return -1; + } + long double anum = tctdbatof(a->vbuf); + long double bnum = tctdbatof(b->vbuf); + if (anum < bnum) return -1; + if (anum > bnum) return 1; + return 0; +} + +/* Compare two sort records by number descending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tdbcmpsortrecnumdesc(const TDBSORTREC *a, const TDBSORTREC *b) { + assert(a && b); + if (!a->vbuf) { + if (!b->vbuf) return 0; + return 1; + } + if (!b->vbuf) { + if (!a->vbuf) return 0; + return -1; + } + long double anum = tctdbatof(a->vbuf); + long double bnum = tctdbatof(b->vbuf); + if (anum < bnum) return 1; + if (anum > bnum) return -1; + return 0; +} + +/* Get the hash value of a record. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + The return value is the hash value. */ +static uint16_t tctdbidxhash(const char *pkbuf, int pksiz) { + assert(pkbuf && pksiz && pksiz >= 0); + uint32_t hash = 19780211; + while (pksiz--) { + hash = hash * 37 + *(uint8_t *) pkbuf++; + } + return hash; +} + +/* Add a record into indices of a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. */ +bool tctdbidxput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + bool err = false; + uint16_t hash = tctdbidxhash(pkbuf, pksiz); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (*(idx->name) != '\0') continue; + char stack[TDBCOLBUFSIZ], *rbuf; + if (pksiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, pksiz + 1); + } + memcpy(rbuf, pkbuf, pksiz); + rbuf[pksiz] = '\0'; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tcbdbput(idx->db, pkbuf, pksiz, rbuf, pksiz)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + case TDBITTOKEN: + if (!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; + break; + case TDBITQGRAM: + if (!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; + break; + } + if (rbuf != stack) TCFREE(rbuf); + } + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (strcmp(idx->name, kbuf)) continue; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tctdbidxputone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; + break; + case TDBITTOKEN: + if (!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + case TDBITQGRAM: + if (!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + } + } + } + return !err; +} + +bool tctdbidxput2(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + bool err = false; + uint16_t hash = tctdbidxhash(pkbuf, pksiz); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (*(idx->name) != '\0') continue; + char stack[TDBCOLBUFSIZ], *rbuf; + if (pksiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, pksiz + 1); + } + memcpy(rbuf, pkbuf, pksiz); + rbuf[pksiz] = '\0'; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tcbdbput(idx->db, pkbuf, pksiz, rbuf, pksiz)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + case TDBITTOKEN: + if (!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; + break; + case TDBITQGRAM: + if (!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; + break; + } + if (rbuf != stack) TCFREE(rbuf); + } + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (strcmp(idx->name, kbuf)) continue; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tctdbidxputone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; + break; + case TDBITTOKEN: + { + TCLIST *tokens = tclistload(vbuf, vsiz); + if (!tctdbidxputtoken2(tdb, idx, pkbuf, pksiz, tokens)) err = true; + tclistdel(tokens); + break; + } + case TDBITQGRAM: + if (!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + } + } + } + return !err; +} + +/* Add a column of a record into an index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `hash' specifies the hash value of the primary key. + `vbuf' specifies the pointer to the region of the column value. + `vsiz' specifies the size of the region of the column value. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxputone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, + const char *vbuf, int vsiz) { + assert(tdb && pkbuf && pksiz >= 0 && vbuf && vsiz); + bool err = false; + char stack[TDBCOLBUFSIZ], *rbuf; + int rsiz = vsiz + 3; + if (rsiz <= sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, rsiz); + } + memcpy(rbuf, vbuf, vsiz); + rbuf[vsiz] = '\0'; + rbuf[vsiz + 1] = hash >> 8; + rbuf[vsiz + 2] = hash & 0xff; + if (!tcbdbputdup(idx->db, rbuf, rsiz, pkbuf, pksiz)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + if (rbuf != stack) TCFREE(rbuf); + return !err; +} + +/* Add a column of a record into an token inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `vbuf' specifies the pointer to the region of the column value. + `vsiz' specifies the size of the region of the column value. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxputtoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz) { + assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); + bool err = false; + TCMAP *cc = idx->cc; + char stack[TDBCOLBUFSIZ], *rbuf; + int rsiz = pksiz + TCNUMBUFSIZ; + if (rsiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, rsiz); + } + uint64_t pkid = 0; + for (int i = 0; i < pksiz; i++) { + int c = pkbuf[i]; + if (c >= '0' && c <= '9') { + pkid = pkid * 10 + c - '0'; + if (pkid > INT64_MAX) { + pkid = 0; + break; + } + } else { + pkid = 0; + break; + } + } + if (pksiz > 0 && *pkbuf == '0') pkid = 0; + if (pkid > 0) { + TCSETVNUMBUF64(rsiz, rbuf, pkid); + } else { + char *wp = rbuf; + *(wp++) = '\0'; + TCSETVNUMBUF(rsiz, wp, pksiz); + wp += rsiz; + memcpy(wp, pkbuf, pksiz); + wp += pksiz; + rsiz = wp - rbuf; + } + const unsigned char *sp = (unsigned char *) vbuf; + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) tcmapputcat3(cc, sp, ep - sp, rbuf, rsiz); + sp = ep; + } + if (rbuf != stack) TCFREE(rbuf); + if (tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; + return !err; +} + +static bool tctdbidxputtoken2(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + TCLIST *tokens) { + assert(tdb && idx && pkbuf && pksiz >= 0 && tokens); + bool err = false; + TCMAP *cc = idx->cc; + char stack[TDBCOLBUFSIZ], *rbuf; + int rsiz = pksiz + TCNUMBUFSIZ; + if (rsiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, rsiz); + } + uint64_t pkid = 0; + for (int i = 0; i < pksiz; i++) { + int c = pkbuf[i]; + if (c >= '0' && c <= '9') { + pkid = pkid * 10 + c - '0'; + if (pkid > INT64_MAX) { + pkid = 0; + break; + } + } else { + pkid = 0; + break; + } + } + if (pksiz > 0 && *pkbuf == '0') pkid = 0; + if (pkid > 0) { + TCSETVNUMBUF64(rsiz, rbuf, pkid); + } else { + char *wp = rbuf; + *(wp++) = '\0'; + TCSETVNUMBUF(rsiz, wp, pksiz); + wp += rsiz; + memcpy(wp, pkbuf, pksiz); + wp += pksiz; + rsiz = wp - rbuf; + } + + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + tcmapputcat3(cc, TCLISTVALPTR(tokens, i), TCLISTVALSIZ(tokens, i), rbuf, rsiz); + } + + if (rbuf != stack) TCFREE(rbuf); + if (tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; + return !err; +} + +/* Add a column of a record into an q-gram inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `vbuf' specifies the pointer to the region of the column value. + `vsiz' specifies the size of the region of the column value. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxputqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz) { + assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); + bool err = false; + TCMAP *cc = idx->cc; + char stack[TDBCOLBUFSIZ], *rbuf; + int rsiz = pksiz + TCNUMBUFSIZ * 2; + if (rsiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, rsiz); + } + uint64_t pkid = 0; + for (int i = 0; i < pksiz; i++) { + int c = pkbuf[i]; + if (c >= '0' && c <= '9') { + pkid = pkid * 10 + c - '0'; + if (pkid > INT64_MAX) { + pkid = 0; + break; + } + } else { + pkid = 0; + break; + } + } + if (pksiz > 0 && *pkbuf == '0') pkid = 0; + if (pkid > 0) { + TCSETVNUMBUF64(rsiz, rbuf, pkid); + } else { + char *wp = rbuf; + *(wp++) = '\0'; + TCSETVNUMBUF(rsiz, wp, pksiz); + wp += rsiz; + memcpy(wp, pkbuf, pksiz); + wp += pksiz; + rsiz = wp - rbuf; + } + uint16_t *ary; + TCMALLOC(ary, sizeof (*ary) * (vsiz + TDBIDXQGUNIT)); + int anum; + tcstrutftoucs(vbuf, ary, &anum); + anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); + for (int i = 0; i < TDBIDXQGUNIT; i++) { + ary[anum + i] = 0; + } + char *wp = rbuf + rsiz; + char token[TDBIDXQGUNIT * 3 + 1]; + for (int i = 0; i < anum; i++) { + tcstrucstoutf(ary + i, TDBIDXQGUNIT, token); + int step; + TCSETVNUMBUF(step, wp, i); + tcmapputcat3(cc, token, strlen(token), rbuf, rsiz + step); + } + TCFREE(ary); + if (rbuf != stack) TCFREE(rbuf); + if (tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; + return !err; +} + +/* Remove a record from indices of a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. */ +bool tctdbidxout(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + bool err = false; + uint16_t hash = tctdbidxhash(pkbuf, pksiz); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (*(idx->name) != '\0') continue; + char stack[TDBCOLBUFSIZ], *rbuf; + if (pksiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, pksiz + 1); + } + memcpy(rbuf, pkbuf, pksiz); + rbuf[pksiz] = '\0'; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tcbdbout(idx->db, pkbuf, pksiz)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + case TDBITTOKEN: + if (!tctdbidxouttoken(tdb, idx, pkbuf, pksiz, rbuf, pksiz)) err = true; + break; + case TDBITQGRAM: + if (!tctdbidxoutqgram(tdb, idx, pkbuf, pksiz, rbuf, pksiz)) err = true; + break; + } + if (rbuf != stack) TCFREE(rbuf); + } + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (strcmp(idx->name, kbuf)) continue; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tctdbidxoutone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; + break; + case TDBITTOKEN: + if (!tctdbidxouttoken(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + case TDBITQGRAM: + if (!tctdbidxoutqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + } + } + } + return !err; +} + +bool tctdbidxout2(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols) { + assert(tdb && pkbuf && pksiz >= 0 && cols); + bool err = false; + uint16_t hash = tctdbidxhash(pkbuf, pksiz); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (*(idx->name) != '\0') continue; + char stack[TDBCOLBUFSIZ], *rbuf; + if (pksiz < sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, pksiz + 1); + } + memcpy(rbuf, pkbuf, pksiz); + rbuf[pksiz] = '\0'; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tcbdbout(idx->db, pkbuf, pksiz)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + case TDBITTOKEN: + if (!tctdbidxouttoken(tdb, idx, pkbuf, pksiz, rbuf, pksiz)) err = true; + break; + case TDBITQGRAM: + if (!tctdbidxoutqgram(tdb, idx, pkbuf, pksiz, rbuf, pksiz)) err = true; + break; + } + if (rbuf != stack) TCFREE(rbuf); + } + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + if (strcmp(idx->name, kbuf)) continue; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + if (!tctdbidxoutone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; + break; + case TDBITTOKEN: + { + TCLIST *tokens = tclistload(vbuf, vsiz); + if (!tctdbidxouttoken2(tdb, idx, pkbuf, pksiz, tokens)) err = true; + tclistdel(tokens); + break; + } + case TDBITQGRAM: + if (!tctdbidxoutqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; + break; + } + } + } + return !err; +} + +/* Remove a column of a record from an index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `hash' specifies the hash value of the primary key. + `vbuf' specifies the pointer to the region of the column value. + `vsiz' specifies the size of the region of the column value. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxoutone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, + const char *vbuf, int vsiz) { + assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); + bool err = false; + char stack[TDBCOLBUFSIZ], *rbuf; + int rsiz = vsiz + 3; + if (rsiz <= sizeof (stack)) { + rbuf = stack; + } else { + TCMALLOC(rbuf, rsiz); + } + memcpy(rbuf, vbuf, vsiz); + rbuf[vsiz] = '\0'; + rbuf[vsiz + 1] = hash >> 8; + rbuf[vsiz + 2] = hash & 0xff; + int ovsiz; + const char *ovbuf = tcbdbget3(idx->db, rbuf, rsiz, &ovsiz); + if (ovbuf && ovsiz == pksiz && !memcmp(ovbuf, pkbuf, ovsiz)) { + if (!tcbdbout(idx->db, rbuf, rsiz)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + } else { + BDBCUR *cur = tcbdbcurnew(idx->db); + if (tcbdbcurjump(cur, rbuf, rsiz)) { + int oksiz; + const char *okbuf; + while ((okbuf = tcbdbcurkey3(cur, &oksiz)) != NULL) { + if (oksiz != rsiz || memcmp(okbuf, rbuf, oksiz)) break; + ovbuf = tcbdbcurval3(cur, &ovsiz); + if (ovsiz == pksiz && !memcmp(ovbuf, pkbuf, ovsiz)) { + if (!tcbdbcurout(cur)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + tcbdbcurnext(cur); + } + } else { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + tcbdbcurdel(cur); + } + if (rbuf != stack) TCFREE(rbuf); + return !err; +} + +/* Remove a column of a record from a token inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `vbuf' specifies the pointer to the region of the column value. + `vsiz' specifies the size of the region of the column value. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxouttoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz) { + assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); + bool err = false; + TCBDB *db = idx->db; + TCMAP *cc = idx->cc; + uint64_t pkid = 0; + for (int i = 0; i < pksiz; i++) { + int c = pkbuf[i]; + if (c >= '0' && c <= '9') { + pkid = pkid * 10 + c - '0'; + if (pkid > INT64_MAX) { + pkid = 0; + break; + } + } else { + pkid = 0; + break; + } + } + TCXSTR *xstr = tcxstrnew(); + const unsigned char *sp = (unsigned char *) vbuf; + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) { + tcxstrclear(xstr); + int len = ep - sp; + int csiz; + const char *cbuf = tcmapget(cc, sp, len, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pv = cbuf; + bool ok = true; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + if (tid == pkid) ok = false; + cbuf += step; + csiz -= step; + } + if (ok) TCXSTRCAT(xstr, pv, cbuf - pv); + } + if (csiz != 0) { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + } + cbuf = tcbdbget3(db, sp, len, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pv = cbuf; + bool ok = true; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + if (tid == pkid) ok = false; + cbuf += step; + csiz -= step; + } + if (ok) TCXSTRCAT(xstr, pv, cbuf - pv); + } + if (csiz != 0) { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + if (!tcbdbout(db, sp, len)) { + tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); + err = true; + } + } + tcmapput(cc, sp, len, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); + } + sp = ep; + } + tcxstrdel(xstr); + if (tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; + return !err; +} + +static bool tctdbidxouttoken2(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + TCLIST *tokens) { + assert(tdb && idx && pkbuf && pksiz >= 0 && tokens); + bool err = false; + TCBDB *db = idx->db; + TCMAP *cc = idx->cc; + uint64_t pkid = 0; + for (int i = 0; i < pksiz; i++) { + int c = pkbuf[i]; + if (c >= '0' && c <= '9') { + pkid = pkid * 10 + c - '0'; + if (pkid > INT64_MAX) { + pkid = 0; + break; + } + } else { + pkid = 0; + break; + } + } + TCXSTR *xstr = tcxstrnew(); + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + int csiz; + const unsigned char *sp = TCLISTVALPTR(tokens, i); + int len = TCLISTVALSIZ(tokens, i); + tcxstrclear(xstr); + const char *cbuf = tcmapget(cc, sp, len, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pv = cbuf; + bool ok = true; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + if (tid == pkid) ok = false; + cbuf += step; + csiz -= step; + } + if (ok) TCXSTRCAT(xstr, pv, cbuf - pv); + } + if (csiz != 0) { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + } + cbuf = tcbdbget3(db, sp, len, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pv = cbuf; + bool ok = true; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + if (tid == pkid) ok = false; + cbuf += step; + csiz -= step; + } + if (ok) TCXSTRCAT(xstr, pv, cbuf - pv); + } + if (csiz != 0) { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + if (!tcbdbout(db, sp, len)) { + tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); + err = true; + } + } + tcmapput(cc, sp, len, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); + } + tcxstrdel(xstr); + if (tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; + return !err; +} + +/* Remove a column of a record from a q-gram inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `vbuf' specifies the pointer to the region of the column value. + `vsiz' specifies the size of the region of the column value. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxoutqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, + const char *vbuf, int vsiz) { + assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); + bool err = false; + TCBDB *db = idx->db; + TCMAP *cc = idx->cc; + uint64_t pkid = 0; + for (int i = 0; i < pksiz; i++) { + int c = pkbuf[i]; + if (c >= '0' && c <= '9') { + pkid = pkid * 10 + c - '0'; + if (pkid > INT64_MAX) { + pkid = 0; + break; + } + } else { + pkid = 0; + break; + } + } + TCXSTR *xstr = tcxstrnew(); + uint16_t *ary; + TCMALLOC(ary, sizeof (*ary) * (vsiz + TDBIDXQGUNIT)); + int anum; + tcstrutftoucs(vbuf, ary, &anum); + anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); + for (int i = 0; i < TDBIDXQGUNIT; i++) { + ary[anum + i] = 0; + } + char token[TDBIDXQGUNIT * 3 + 1]; + for (int i = 0; i < anum; i++) { + tcstrucstoutf(ary + i, TDBIDXQGUNIT, token); + int tsiz = strlen(token); + tcxstrclear(xstr); + int csiz; + const char *cbuf = tcmapget(cc, token, tsiz, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pv = cbuf; + bool ok = true; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + if (tid == pkid) ok = false; + cbuf += step; + csiz -= step; + } + if (csiz > 0) { + int off, step; + TCREADVNUMBUF(cbuf, off, step); + cbuf += step; + csiz -= step; + if (ok) TCXSTRCAT(xstr, pv, cbuf - pv); + } + } + if (csiz != 0) { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + } + cbuf = tcbdbget3(db, token, tsiz, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pv = cbuf; + bool ok = true; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + if (tid == pkid) ok = false; + cbuf += step; + csiz -= step; + } + if (csiz > 0) { + int off, step; + TCREADVNUMBUF(cbuf, off, step); + cbuf += step; + csiz -= step; + if (ok) TCXSTRCAT(xstr, pv, cbuf - pv); + } + } + if (csiz != 0) { + tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); + err = true; + } + if (!tcbdbout(db, token, tsiz)) { + tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); + err = true; + } + } + tcmapput(cc, token, tsiz, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); + } + TCFREE(ary); + tcxstrdel(xstr); + if (tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; + return !err; +} + +/* Synchronize updated contents of an inverted cache of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `all' specifies whether to sync all tokens. + If successful, the return value is true, else, it is false. */ +static bool tctdbidxsyncicc(TCTDB *tdb, TDBIDX *idx, bool all) { + assert(tdb && idx); + TCBDB *db = idx->db; + TCMAP *cc = idx->cc; + int rnum = TCMAPRNUM(cc); + if (rnum < 1) return true; + bool err = false; + const char **keys; + TCMALLOC(keys, sizeof (*keys) * rnum); + int knum = 0; + int64_t usiz = tcmapmsiz(cc) - sizeof (void *) * TDBIDXICCBNUM; + int64_t max = all ? INT64_MAX : usiz * tdb->iccsync; + int64_t sum = 0; + const char *kbuf; + int ksiz; + tcmapiterinit(cc); + while (sum < max && (kbuf = tcmapiternext(cc, &ksiz)) != NULL) { + int vsiz; + tcmapiterval(kbuf, &vsiz); + keys[knum++] = kbuf; + sum += sizeof (TCMAPREC) + sizeof (void *) +ksiz + vsiz; + } + qsort(keys, knum, sizeof (*keys), (int(*)(const void *, const void *))tctdbidxcmpkey); + for (int i = 0; i < knum; i++) { + const char *kbuf = keys[i]; + int ksiz = strlen(kbuf); + int vsiz; + const char *vbuf = tcmapget(cc, kbuf, ksiz, &vsiz); + if (vsiz > 0 && !tcbdbputcat(db, kbuf, ksiz, vbuf, vsiz)) { + tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); + err = true; + } + tcmapout(cc, kbuf, ksiz); + } + TCFREE(keys); + return !err; +} + +/* Compare two index search keys in lexical order. + `a' specifies the pointer to one element. + `b' specifies the pointer to the other element. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tctdbidxcmpkey(const char **a, const char **b) { + assert(a && b); + const unsigned char *ap = (unsigned char *) *a; + const unsigned char *bp = (unsigned char *) *b; + while (true) { + if (*ap == '\0') return *bp == '\0' ? 0 : -1; + if (*bp == '\0') return *ap == '\0' ? 0 : 1; + if (*ap != *bp) return *ap - *bp; + ap++; + bp++; + } + return 0; +} + +/* Retrieve records by a token inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `token' specifies the list object of tokens. + `op' specifies the operation type. + `hint' specifies the hint object. + The return value is a map object of the primary keys of the corresponding records. */ +TCMAP *tctdbidxgetbytokens(TCTDB *tdb, const TDBIDX *idx, const TCLIST *tokens, int op, + TCXSTR *hint) { + assert(tdb && idx && tokens); + TCBDB *db = idx->db; + TCMAP *cc = idx->cc; + int tnum = TCLISTNUM(tokens); + TCMAP *res = tcmapnew(); + int cnt = 0; + for (int i = 0; i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + int onum = 0; + TCMAP *wring = (cnt > 0 && op == TDBQCSTRAND) ? tcmapnew() : NULL; + int csiz; + const char *cbuf = tcmapget(cc, token, tsiz, &csiz); + if (cbuf) { + while (csiz > 0) { + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (cnt < 1) { + tcmapput(res, cbuf, tsiz, "", 0); + } else if (wring) { + int rsiz; + if (tcmapget(res, cbuf, tsiz, &rsiz)) tcmapput(wring, cbuf, tsiz, "", 0); + } else { + tcmapput(res, cbuf, tsiz, "", 0); + } + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + char pkbuf[TCNUMBUFSIZ]; + int pksiz = sprintf(pkbuf, "%" PRIdMAX "", (int64_t) tid); + if (cnt < 1) { + tcmapput(res, pkbuf, pksiz, "", 0); + } else if (wring) { + int rsiz; + if (tcmapget(res, pkbuf, pksiz, &rsiz)) tcmapput(wring, pkbuf, pksiz, "", 0); + } else { + tcmapput(res, pkbuf, pksiz, "", 0); + } + cbuf += step; + csiz -= step; + } + onum++; + } + } + cbuf = tcbdbget3(db, token, tsiz, &csiz); + if (cbuf) { + while (csiz > 0) { + if (*cbuf == '\0') { + cbuf++; + csiz--; + int tsiz, step; + TCREADVNUMBUF(cbuf, tsiz, step); + cbuf += step; + csiz -= step; + if (cnt < 1) { + tcmapput(res, cbuf, tsiz, "", 0); + } else if (wring) { + int rsiz; + if (tcmapget(res, cbuf, tsiz, &rsiz)) tcmapput(wring, cbuf, tsiz, "", 0); + } else { + tcmapput(res, cbuf, tsiz, "", 0); + } + cbuf += tsiz; + csiz -= tsiz; + } else { + int64_t tid; + int step; + TCREADVNUMBUF64(cbuf, tid, step); + char pkbuf[TCNUMBUFSIZ]; + int pksiz = sprintf(pkbuf, "%" PRIdMAX "", (int64_t) tid); + if (cnt < 1) { + tcmapput(res, pkbuf, pksiz, "", 0); + } else if (wring) { + int rsiz; + if (tcmapget(res, pkbuf, pksiz, &rsiz)) tcmapput(wring, pkbuf, pksiz, "", 0); + } else { + tcmapput(res, pkbuf, pksiz, "", 0); + } + cbuf += step; + csiz -= step; + } + onum++; + } + } + if (wring) { + tcmapdel(res); + res = wring; + } + if (hint) { + tcxstrprintf(hint, "token occurrence: \"%s\" %d\n", token, onum); + } + cnt++; + } + return res; +} + +/* Retrieve records by a token inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `cond' specifies the condition object. + `hint' specifies the hint object. + The return value is a map object of the primary keys of the corresponding records. */ +static TCMAP *tctdbidxgetbyfts(TCTDB *tdb, TDBIDX *idx, TDBCOND *cond, TCXSTR *hint) { + assert(tdb && idx && cond && hint); + TDBFTSUNIT *ftsunits = cond->ftsunits; + int ftsnum = cond->ftsnum; + if (ftsnum < 1) return tcmapnew2(1); + if (!ftsunits[0].sign) return tcmapnew2(1); + TCMAP *res = tcmapnew(); + tctdbidxgetbyftsunion(idx, ftsunits->tokens, true, NULL, res, hint); + for (int i = 1; i < ftsnum; i++) { + TDBFTSUNIT *ftsunit = ftsunits + i; + if (ftsunit->sign) { + TCMAP *nres = tcmapnew2(TCMAPRNUM(res) + 1); + tctdbidxgetbyftsunion(idx, ftsunit->tokens, true, res, nres, hint); + tcmapdel(res); + res = nres; + } else { + tctdbidxgetbyftsunion(idx, ftsunit->tokens, false, res, NULL, hint); + } + } + return res; +} + +/* Retrieve union records by a token inverted index of a table database object. + `idx' specifies the index object. + `tokens' specifies a list object of the union tokens. + `sign' specifies the logical sign. + `ores' specifies a map object of old primary keys. + `nres' specifies a map object of new primary keys. + `hint' specifies the hint object. */ +static void tctdbidxgetbyftsunion(TDBIDX *idx, const TCLIST *tokens, bool sign, + TCMAP *ores, TCMAP *nres, TCXSTR *hint) { + assert(idx && tokens && hint); + TCBDB *db = idx->db; + TCMAP *cc = idx->cc; + int tnum = TCLISTNUM(tokens); + for (int i = 0; i < tnum; i++) { + const char *word; + int wsiz; + TCLISTVAL(word, tokens, i, wsiz); + uint16_t *ary; + TCMALLOC(ary, sizeof (*ary) * (wsiz + TDBIDXQGUNIT)); + int anum; + tcstrutftoucs(word, ary, &anum); + for (int j = 0; j < TDBIDXQGUNIT; j++) { + ary[anum + j] = 0; + } + if (anum >= TDBIDXQGUNIT) { + TDBFTSSTROCR *socrs; + TCMALLOC(socrs, TDBFTSOCRUNIT * sizeof (*socrs)); + int sonum = 0; + int soanum = TDBFTSOCRUNIT; + int sobase = 0; + TDBFTSNUMOCR *nocrs; + TCMALLOC(nocrs, TDBFTSOCRUNIT * sizeof (*nocrs)); + int nonum = 0; + int noanum = TDBFTSOCRUNIT; + int nobase = 0; + TCBITMAP *pkmap = TCBITMAPNEW(TDBFTSBMNUM); + char token[TDBIDXQGUNIT * 3 + 1]; + uint16_t seq = 0; + for (int j = 0; j < anum; j += TDBIDXQGUNIT) { + sobase = sonum; + nobase = nonum; + int diff = anum - j - TDBIDXQGUNIT; + if (diff < 0) { + j += diff; + diff = -diff; + } else { + diff = 0; + } + tcstrucstoutf(ary + j, TDBIDXQGUNIT, token); + int tsiz = strlen(token); + int csiz; + const char *cbuf = tcmapget(cc, token, tsiz, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pkbuf = NULL; + int32_t pksiz = 0; + int64_t pkid = 0; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int step; + TCREADVNUMBUF(cbuf, pksiz, step); + cbuf += step; + csiz -= step; + pkbuf = cbuf; + cbuf += pksiz; + csiz -= pksiz; + } else { + int step; + TCREADVNUMBUF64(cbuf, pkid, step); + cbuf += step; + csiz -= step; + } + if (csiz > 0) { + int off, step; + TCREADVNUMBUF(cbuf, off, step); + cbuf += step; + csiz -= step; + off += diff; + if (pkbuf) { + unsigned int hash = 19780211; + for (int k = 0; k < pksiz; k++) { + hash = hash * 37 + ((unsigned char *) pkbuf)[k]; + } + hash = hash % TDBFTSBMNUM; + if (j == 0 || TCBITMAPCHECK(pkmap, hash)) { + if (sonum >= soanum) { + soanum *= 2; + TCREALLOC(socrs, socrs, soanum * sizeof (*socrs)); + } + TDBFTSSTROCR *ocr = socrs + sonum; + ocr->pkbuf = pkbuf; + ocr->pksiz = pksiz; + ocr->off = off; + ocr->seq = seq; + ocr->hash = hash; + sonum++; + if (j == 0) TCBITMAPON(pkmap, hash); + } + } else { + unsigned int hash = pkid % TDBFTSBMNUM; + if (j == 0 || TCBITMAPCHECK(pkmap, hash)) { + if (nonum >= noanum) { + noanum *= 2; + TCREALLOC(nocrs, nocrs, noanum * sizeof (*nocrs)); + } + TDBFTSNUMOCR *ocr = nocrs + nonum; + ocr->pkid = pkid; + ocr->off = off; + ocr->seq = seq; + ocr->hash = hash; + nonum++; + if (j == 0) TCBITMAPON(pkmap, hash); + } + } + } + } + } + cbuf = tcbdbget3(db, token, tsiz, &csiz); + if (cbuf) { + while (csiz > 0) { + const char *pkbuf = NULL; + int32_t pksiz = 0; + int64_t pkid = 0; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int step; + TCREADVNUMBUF(cbuf, pksiz, step); + cbuf += step; + csiz -= step; + pkbuf = cbuf; + cbuf += pksiz; + csiz -= pksiz; + } else { + int step; + TCREADVNUMBUF64(cbuf, pkid, step); + cbuf += step; + csiz -= step; + } + if (csiz > 0) { + int off, step; + TCREADVNUMBUF(cbuf, off, step); + cbuf += step; + csiz -= step; + off += diff; + if (pkbuf) { + unsigned int hash = 19780211; + for (int k = 0; k < pksiz; k++) { + hash = hash * 37 + ((unsigned char *) pkbuf)[k]; + } + hash = hash % TDBFTSBMNUM; + if (j == 0 || TCBITMAPCHECK(pkmap, hash)) { + if (sonum >= soanum) { + soanum *= 2; + TCREALLOC(socrs, socrs, soanum * sizeof (*socrs)); + } + TDBFTSSTROCR *ocr = socrs + sonum; + ocr->pkbuf = pkbuf; + ocr->pksiz = pksiz; + ocr->off = off; + ocr->seq = seq; + ocr->hash = hash; + sonum++; + if (j == 0) TCBITMAPON(pkmap, hash); + } + } else { + unsigned int hash = pkid % TDBFTSBMNUM; + if (j == 0 || TCBITMAPCHECK(pkmap, hash)) { + if (nonum >= noanum) { + noanum *= 2; + TCREALLOC(nocrs, nocrs, noanum * sizeof (*nocrs)); + } + TDBFTSNUMOCR *ocr = nocrs + nonum; + ocr->pkid = pkid; + ocr->off = off; + ocr->seq = seq; + ocr->hash = hash; + nonum++; + if (j == 0) TCBITMAPON(pkmap, hash); + } + } + } + } + } + seq++; + if (sonum <= sobase && nonum <= nobase) { + sonum = 0; + nonum = 0; + break; + } + } + TCBITMAPDEL(pkmap); + if (seq > 1) { + if (sonum > UINT16_MAX) { + int flnum = sonum * 16 + 1; + TCBITMAP *flmap = TCBITMAPNEW(flnum); + for (int j = sobase; j < sonum; j++) { + TDBFTSSTROCR *ocr = socrs + j; + uint32_t hash = (((uint32_t) ocr->off << 16) | ocr->hash) % flnum; + TCBITMAPON(flmap, hash); + } + int wi = 0; + for (int j = 0; j < sobase; j++) { + TDBFTSSTROCR *ocr = socrs + j; + int rem = (seq - ocr->seq - 1) * TDBIDXQGUNIT; + uint32_t hash = (((uint32_t) (ocr->off + rem) << 16) | ocr->hash) % flnum; + if (TCBITMAPCHECK(flmap, hash)) socrs[wi++] = *ocr; + } + for (int j = sobase; j < sonum; j++) { + socrs[wi++] = socrs[j]; + } + sonum = wi; + TCBITMAPDEL(flmap); + } + if (sonum > UINT16_MAX * 2) { + TDBFTSSTROCR *rocrs; + TCMALLOC(rocrs, sizeof (*rocrs) * sonum); + uint32_t *counts; + TCCALLOC(counts, sizeof (*counts), (UINT16_MAX + 1)); + for (int j = 0; j < sonum; j++) { + counts[socrs[j].hash]++; + } + for (int j = 0; j < UINT16_MAX; j++) { + counts[j + 1] += counts[j]; + } + for (int j = sonum - 1; j >= 0; j--) { + rocrs[--counts[socrs[j].hash]] = socrs[j]; + } + for (int j = 0; j < UINT16_MAX; j++) { + int num = counts[j + 1] - counts[j]; + if (num > 1) qsort(rocrs + counts[j], num, sizeof (*rocrs), + (int (*)(const void *, const void *))tctdbidxftscmpstrocr); + } + int num = sonum - counts[UINT16_MAX]; + if (num > 1) qsort(rocrs + counts[UINT16_MAX], num, sizeof (*rocrs), + (int (*)(const void *, const void *))tctdbidxftscmpstrocr); + TCFREE(counts); + TCFREE(socrs); + socrs = rocrs; + } else if (sonum > 1) { + qsort(socrs, sonum, sizeof (*socrs), + (int (*)(const void *, const void *))tctdbidxftscmpstrocr); + } + if (nonum > UINT16_MAX) { + int flnum = nonum * 16 + 1; + TCBITMAP *flmap = TCBITMAPNEW(flnum); + for (int j = nobase; j < nonum; j++) { + TDBFTSNUMOCR *ocr = nocrs + j; + uint32_t hash = (((uint32_t) ocr->off << 16) | ocr->hash) % flnum; + TCBITMAPON(flmap, hash); + } + int wi = 0; + for (int j = 0; j < nobase; j++) { + TDBFTSNUMOCR *ocr = nocrs + j; + int rem = (seq - ocr->seq - 1) * TDBIDXQGUNIT; + uint32_t hash = (((uint32_t) (ocr->off + rem) << 16) | ocr->hash) % flnum; + if (TCBITMAPCHECK(flmap, hash)) nocrs[wi++] = *ocr; + } + for (int j = nobase; j < nonum; j++) { + nocrs[wi++] = nocrs[j]; + } + nonum = wi; + TCBITMAPDEL(flmap); + } + if (nonum > UINT16_MAX * 2) { + TDBFTSNUMOCR *rocrs; + TCMALLOC(rocrs, sizeof (*rocrs) * nonum); + uint32_t *counts; + TCCALLOC(counts, sizeof (*counts), (UINT16_MAX + 1)); + for (int j = 0; j < nonum; j++) { + counts[nocrs[j].hash]++; + } + for (int j = 0; j < UINT16_MAX; j++) { + counts[j + 1] += counts[j]; + } + for (int j = nonum - 1; j >= 0; j--) { + rocrs[--counts[nocrs[j].hash]] = nocrs[j]; + } + for (int j = 0; j < UINT16_MAX; j++) { + int num = counts[j + 1] - counts[j]; + if (num > 1) qsort(rocrs + counts[j], num, sizeof (*rocrs), + (int (*)(const void *, const void *))tctdbidxftscmpnumocr); + } + int num = nonum - counts[UINT16_MAX]; + if (num > 1) qsort(rocrs + counts[UINT16_MAX], num, sizeof (*rocrs), + (int (*)(const void *, const void *))tctdbidxftscmpnumocr); + TCFREE(counts); + TCFREE(nocrs); + nocrs = rocrs; + } else if (nonum > 1) { + qsort(nocrs, nonum, sizeof (*nocrs), + (int (*)(const void *, const void *))tctdbidxftscmpnumocr); + } + } + int rem = (seq - 1) * TDBIDXQGUNIT; + int onum = 0; + int ri = 0; + while (ri < sonum) { + TDBFTSSTROCR *ocr = socrs + ri; + ri++; + if (ocr->seq > 0) continue; + const char *pkbuf = ocr->pkbuf; + int32_t pksiz = ocr->pksiz; + int32_t off = ocr->off; + uint16_t seq = 1; + for (int j = ri; j < sonum; j++) { + TDBFTSSTROCR *tocr = socrs + j; + if (!tocr->pkbuf || tocr->pksiz != pksiz || memcmp(tocr->pkbuf, pkbuf, pksiz) || + tocr->off > off + TDBIDXQGUNIT) break; + if (tocr->seq == seq && tocr->off == off + TDBIDXQGUNIT) { + off = tocr->off; + seq++; + } + } + if (off == ocr->off + rem) { + onum++; + if (ores) { + int rsiz; + if (tcmapget(ores, pkbuf, pksiz, &rsiz)) { + if (sign) { + tcmapputkeep(nres, pkbuf, pksiz, "", 0); + } else { + tcmapout(ores, pkbuf, pksiz); + } + } + } else { + tcmapputkeep(nres, pkbuf, pksiz, "", 0); + } + while (ri < sonum) { + ocr = socrs + ri; + if (!ocr->pkbuf || ocr->pksiz != pksiz || memcmp(ocr->pkbuf, pkbuf, pksiz)) break; + ri++; + } + } + } + ri = 0; + while (ri < nonum) { + TDBFTSNUMOCR *ocr = nocrs + ri; + ri++; + if (ocr->seq > 0) continue; + int64_t pkid = ocr->pkid; + int32_t off = ocr->off; + uint16_t seq = 1; + for (int j = ri; j < nonum; j++) { + TDBFTSNUMOCR *tocr = nocrs + j; + if (tocr->pkid != pkid || tocr->off > off + TDBIDXQGUNIT) break; + if (tocr->seq == seq && tocr->off == off + TDBIDXQGUNIT) { + off = tocr->off; + seq++; + } + } + if (off == ocr->off + rem) { + onum++; + char pkbuf[TCNUMBUFSIZ]; + int pksiz = sprintf(pkbuf, "%" PRIdMAX "", (int64_t) pkid); + if (ores) { + int rsiz; + if (tcmapget(ores, pkbuf, pksiz, &rsiz)) { + if (sign) { + tcmapputkeep(nres, pkbuf, pksiz, "", 0); + } else { + tcmapout(ores, pkbuf, pksiz); + } + } + } else { + tcmapputkeep(nres, pkbuf, pksiz, "", 0); + } + while (ri < nonum && nocrs[ri].pkid == pkid) { + ri++; + } + } + } + tcxstrprintf(hint, "token occurrence: \"%s\" %d\n", word, onum); + TCFREE(nocrs); + TCFREE(socrs); + } else { + int onum = 0; + TCMAP *uniq = (i > 0 || ores) ? tcmapnew2(UINT16_MAX) : NULL; + tcmapiterinit(cc); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cc, &ksiz)) != NULL) { + if (ksiz < wsiz || memcmp(kbuf, word, wsiz)) continue; + int csiz; + const char *cbuf = tcmapiterval(kbuf, &csiz); + while (csiz > 0) { + const char *pkbuf = NULL; + int32_t pksiz = 0; + int64_t pkid = 0; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int step; + TCREADVNUMBUF(cbuf, pksiz, step); + cbuf += step; + csiz -= step; + pkbuf = cbuf; + cbuf += pksiz; + csiz -= pksiz; + } else { + int step; + TCREADVNUMBUF64(cbuf, pkid, step); + cbuf += step; + csiz -= step; + } + if (csiz > 0) { + int off, step; + TCREADVNUMBUF(cbuf, off, step); + cbuf += step; + csiz -= step; + if (pkbuf) { + if (ores) { + int rsiz; + if (tcmapget(ores, pkbuf, pksiz, &rsiz)) { + if (sign) { + tcmapputkeep(nres, pkbuf, pksiz, "", 0); + } else { + tcmapout(ores, pkbuf, pksiz); + } + } + } else { + if (tcmapputkeep(nres, pkbuf, pksiz, "", 0)) onum++; + } + if (uniq) tcmapputkeep(uniq, pkbuf, pksiz, "", 0); + } else { + char numbuf[TCNUMBUFSIZ]; + int pksiz = sprintf(numbuf, "%" PRIdMAX "", (int64_t) pkid); + if (ores) { + int rsiz; + if (tcmapget(ores, numbuf, pksiz, &rsiz)) { + if (sign) { + tcmapputkeep(nres, numbuf, pksiz, "", 0); + } else { + tcmapout(ores, numbuf, pksiz); + } + } + } else { + if (tcmapputkeep(nres, numbuf, pksiz, "", 0)) onum++; + } + if (uniq) tcmapputkeep(uniq, numbuf, pksiz, "", 0); + } + } + } + } + BDBCUR *cur = tcbdbcurnew(db); + tcbdbcurjump(cur, word, wsiz); + TCXSTR *key = tcxstrnew(); + TCXSTR *val = tcxstrnew(); + while (tcbdbcurrec(cur, key, val)) { + const char *kbuf = TCXSTRPTR(key); + int ksiz = TCXSTRSIZE(key); + if (ksiz < wsiz || memcmp(kbuf, word, wsiz)) break; + const char *cbuf = TCXSTRPTR(val); + int csiz = TCXSTRSIZE(val); + while (csiz > 0) { + const char *pkbuf = NULL; + int32_t pksiz = 0; + int64_t pkid = 0; + if (*cbuf == '\0') { + cbuf++; + csiz--; + int step; + TCREADVNUMBUF(cbuf, pksiz, step); + cbuf += step; + csiz -= step; + pkbuf = cbuf; + cbuf += pksiz; + csiz -= pksiz; + } else { + int step; + TCREADVNUMBUF64(cbuf, pkid, step); + cbuf += step; + csiz -= step; + } + if (csiz > 0) { + int off, step; + TCREADVNUMBUF(cbuf, off, step); + cbuf += step; + csiz -= step; + if (pkbuf) { + if (ores) { + int rsiz; + if (tcmapget(ores, pkbuf, pksiz, &rsiz)) { + if (sign) { + tcmapputkeep(nres, pkbuf, pksiz, "", 0); + } else { + tcmapout(ores, pkbuf, pksiz); + } + } + } else { + if (tcmapputkeep(nres, pkbuf, pksiz, "", 0)) onum++; + } + if (uniq) tcmapputkeep(uniq, pkbuf, pksiz, "", 0); + } else { + char numbuf[TCNUMBUFSIZ]; + int pksiz = sprintf(numbuf, "%" PRIdMAX "", (int64_t) pkid); + if (ores) { + int rsiz; + if (tcmapget(ores, numbuf, pksiz, &rsiz)) { + if (sign) { + tcmapputkeep(nres, numbuf, pksiz, "", 0); + } else { + tcmapout(ores, numbuf, pksiz); + } + } + } else { + if (tcmapputkeep(nres, numbuf, pksiz, "", 0)) onum++; + } + if (uniq) tcmapputkeep(uniq, numbuf, pksiz, "", 0); + } + } + } + tcbdbcurnext(cur); + } + tcxstrdel(val); + tcxstrdel(key); + tcbdbcurdel(cur); + tcxstrprintf(hint, "token occurrence: \"%s\" %d\n", + word, uniq ? (int) tcmaprnum(uniq) : onum); + if (uniq) tcmapdel(uniq); + } + TCFREE(ary); + } +} + +/* Compare two string occurrences of full-text search in identical order. + `a' specifies the pointer to one occurrence. + `b' specifies the pointer to the other occurrence. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tctdbidxftscmpstrocr(TDBFTSSTROCR *a, TDBFTSSTROCR *b) { + assert(a && b); + if (a->pksiz > b->pksiz) return 1; + if (a->pksiz < b->pksiz) return -1; + int diff = memcmp(a->pkbuf, b->pkbuf, a->pksiz); + if (diff != 0) return diff; + return a->off - b->off; +} + +/* Compare two number occurrences of full-text search in identical order. + `a' specifies the pointer to one occurrence. + `b' specifies the pointer to the other occurrence. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +static int tctdbidxftscmpnumocr(TDBFTSNUMOCR *a, TDBFTSNUMOCR *b) { + assert(a && b); + if (a->pkid > b->pkid) return 1; + if (a->pkid < b->pkid) return -1; + return a->off - b->off; +} + +/* Parse an expression of full-text search. + `expr' specifies the expression. + `esiz' specifies the size of the expression. + `np' specifies the pointer to a variable into which the number of elements of the return value + is assigned. + `op' specifies the operation type. + The return value is the pointer to the array of full-text search units. */ +static TDBFTSUNIT *tctdbftsparseexpr(const char *expr, int esiz, int op, int *np) { + assert(expr && esiz >= 0 && np); + TDBFTSUNIT *ftsunits; + TCMALLOC(ftsunits, TDBFTSUNITMAX * sizeof (*ftsunits)); + int ftsnum = 0; + uint16_t *ary; + TCMALLOC(ary, sizeof (*ary) * esiz + 1); + int anum; + tcstrutftoucs(expr, ary, &anum); + anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); + char *str; + TCMALLOC(str, esiz + 1); + tcstrucstoutf(ary, anum, str); + if (op == TDBQCFTSPH) { + TCLIST *tokens = tclistnew2(1); + tclistpush2(tokens, str); + ftsunits[ftsnum].tokens = tokens; + ftsunits[ftsnum].sign = true; + ftsnum++; + } else if (op == TDBQCFTSAND) { + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + int tnum = TCLISTNUM(tokens); + for (int i = 0; i < tnum && ftsnum < TDBFTSUNITMAX; i++) { + const char *token = TCLISTVALPTR(tokens, i); + if (*token == '\0') continue; + TCLIST *ttokens = tclistnew2(1); + tclistpush2(ttokens, token); + ftsunits[ftsnum].tokens = ttokens; + ftsunits[ftsnum].sign = true; + ftsnum++; + } + tclistdel(tokens); + } else if (op == TDBQCFTSOR) { + TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); + int tnum = TCLISTNUM(tokens); + TCLIST *ttokens = tclistnew2(tnum); + for (int i = 0; i < tnum; i++) { + const char *token = TCLISTVALPTR(tokens, i); + if (*token == '\0') continue; + tclistpush2(ttokens, token); + } + ftsunits[ftsnum].tokens = ttokens; + ftsunits[ftsnum].sign = true; + ftsnum++; + tclistdel(tokens); + } else if (op == TDBQCFTSEX) { + TCLIST *tokens = tcstrtokenize(str); + int op = 0; + for (int i = 0; i < tclistnum(tokens); i++) { + const char *token = TCLISTVALPTR(tokens, i); + if (!strcmp(token, "&&")) { + op = 0; + } else if (!strcmp(token, "||")) { + op = 1; + } else if (!strcmp(token, "!!")) { + op = 2; + } else { + if (op == 0 || op == 2) { + if (ftsnum >= TDBFTSUNITMAX) break; + TCLIST *ttokens = tclistnew2(2); + tclistpush2(ttokens, token); + ftsunits[ftsnum].tokens = ttokens; + ftsunits[ftsnum].sign = op == 0; + ftsnum++; + } else if (op == 1) { + if (ftsnum < 1) { + ftsunits[ftsnum].tokens = tclistnew2(2); + ftsunits[ftsnum].sign = op == 0; + ftsnum++; + } + TCLIST *ttokens = ftsunits[ftsnum - 1].tokens; + tclistpush2(ttokens, token); + } + op = 0; + } + } + tclistdel(tokens); + } + TCFREE(str); + TCFREE(ary); + *np = ftsnum; + return ftsunits; +} + +/* Perform dynamic defragmentation of a table database object. + `tdb' specifies the table database object. + `step' specifie the number of steps. + If successful, the return value is true, else, it is false. */ +static bool tctdbdefragimpl(TCTDB *tdb, int64_t step) { + assert(tdb); + bool err = false; + TCHDB *hdb = tdb->hdb; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + if (!tchdbdefrag(hdb, step)) err = true; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbdefrag(idx->db, step)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Clear the cache of a table tree database object. + `tdb' specifies the table tree database object. + If successful, the return value is true, else, it is false. */ +static bool tctdbcacheclearimpl(TCTDB *tdb) { + assert(tdb); + bool err = false; + TCHDB *hdb = tdb->hdb; + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + if (!tchdbcacheclear(hdb)) err = true; + for (int i = 0; i < inum; i++) { + TDBIDX *idx = idxs + i; + switch (idx->type) { + case TDBITLEXICAL: + case TDBITDECIMAL: + case TDBITTOKEN: + case TDBITQGRAM: + if (!tcbdbcacheclear(idx->db)) { + tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); + err = true; + } + break; + } + } + return !err; +} + +/* Process each record atomically of a table database object. + `tdb' specifies the table database object. + `func' specifies the pointer to the iterator function called for each record. + `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. + If successful, the return value is true, else, it is false. */ +static bool tctdbforeachimpl(TCTDB *tdb, TCITER iter, void *op) { + assert(tdb && iter); + TCHDB *hdb = tdb->hdb; + char *lkbuf = NULL; + int lksiz = 0; + char *pkbuf, stack[TDBPAGEBUFSIZ], *rbuf; + int pksiz; + const char *cbuf; + int csiz; + while ((pkbuf = tchdbgetnext3(hdb, lkbuf, lksiz, &pksiz, &cbuf, &csiz)) != NULL) { + if (pksiz < TDBPAGEBUFSIZ) { + rbuf = stack; + } else { + TCMALLOC(rbuf, pksiz + 1); + } + memcpy(rbuf, pkbuf, pksiz); + stack[pksiz] = '\0'; + TCMAP *cols = tcmapload(cbuf, csiz); + int zsiz; + char *zbuf = tcstrjoin4(cols, &zsiz); + bool rv = iter(rbuf, pksiz, zbuf, zsiz, op); + TCFREE(zbuf); + if (rbuf != stack) TCFREE(rbuf); + tcmapdel(cols); + TCFREE(lkbuf); + lkbuf = pkbuf; + lksiz = pksiz; + if (!rv) break; + } + TCFREE(lkbuf); + return true; +} + +/* Answer to remove for each record of a query. + `pkbuf' is ignored. + `pksiz' is ignored. + `op' is ignored. + The return value is always `TDBQPOUT'. */ +static int tctdbqryprocoutcb(const void *pkbuf, int pksiz, TCMAP *cols, void *op) { + assert(pkbuf && pksiz >= 0 && cols); + return TDBQPOUT; +} + +/* Lock a method of the table database object. + `tdb' specifies the table database object. + `wr' specifies whether the lock is writer or not. + If successful, the return value is true, else, it is false. */ +static bool tctdblockmethod(TCTDB *tdb, bool wr) { + assert(tdb); + if (wr ? pthread_rwlock_wrlock(tdb->mmtx) != 0 : pthread_rwlock_rdlock(tdb->mmtx) != 0) { + tctdbsetecode(tdb, TCETHREAD, __FILE__, __LINE__, __func__); + return false; + } + TCTESTYIELD(); + return true; +} + +/* Unlock a method of the table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +static bool tctdbunlockmethod(TCTDB *tdb) { + assert(tdb); + if (pthread_rwlock_unlock(tdb->mmtx) != 0) { + tctdbsetecode(tdb, TCETHREAD, __FILE__, __LINE__, __func__); + return false; + } + TCTESTYIELD(); + return true; +} + + + +/************************************************************************************************* + * debugging functions + *************************************************************************************************/ + +/* Print meta data of the header into the debugging output. + `tdb' specifies the table database object. */ +void tctdbprintmeta(TCTDB *tdb) { + assert(tdb); + HANDLE dbgfd = tchdbdbgfd(tdb->hdb); + if (!INVALIDHANDLE(dbgfd)) { + dbgfd = GET_STDOUT_HANDLE(); + } + char buf[TDBPAGEBUFSIZ]; + char *wp = buf; + wp += sprintf(wp, "META:"); + wp += sprintf(wp, " mmtx=%p", (void *) tdb->mmtx); + wp += sprintf(wp, " hdb=%p", (void *) tdb->hdb); + wp += sprintf(wp, " open=%d", tdb->open); + wp += sprintf(wp, " wmode=%d", tdb->wmode); + wp += sprintf(wp, " opts=%u", tdb->opts); + wp += sprintf(wp, " lcnum=%d", tdb->lcnum); + wp += sprintf(wp, " ncnum=%d", tdb->ncnum); + wp += sprintf(wp, " iccmax=%" PRIdMAX "", (int64_t) tdb->iccmax); + wp += sprintf(wp, " iccsync=%f", tdb->iccsync); + wp += sprintf(wp, " idxs=%p", (void *) tdb->idxs); + wp += sprintf(wp, " inum=%d", tdb->inum); + wp += sprintf(wp, " tran=%d", tdb->tran); + *(wp++) = '\n'; + tcwrite(dbgfd, buf, wp - buf); +} + + + +// END OF FILE diff --git a/tcejdb/src/tctdb/tctdb.h b/tcejdb/src/tctdb/tctdb.h new file mode 100644 index 0000000..82cee8f --- /dev/null +++ b/tcejdb/src/tctdb/tctdb.h @@ -0,0 +1,1162 @@ +/************************************************************************************************* + * The table database API of Tokyo Cabinet + * Copyright (C) 2006-2012 FAL Labs + * Copyright (C) 2012-2015 Softmotions Ltd <info@softmotions.com> + * This file is part of Tokyo Cabinet. + * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of + * the GNU Lesser General Public License as published by the Free Software Foundation; either + * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * You should have received a copy of the GNU Lesser General Public License along with Tokyo + * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA. + *************************************************************************************************/ + + +#ifndef _TCTDB_H /* duplication check */ +#define _TCTDB_H + +#if defined(__cplusplus) +#define __TCTDB_CLINKAGEBEGIN extern "C" { +#define __TCTDB_CLINKAGEEND } +#else +#define __TCTDB_CLINKAGEBEGIN +#define __TCTDB_CLINKAGEEND +#endif + +__TCTDB_CLINKAGEBEGIN + + +#include "tcutil.h" +#include "tchdb.h" +#include "tcbdb.h" + + + +/************************************************************************************************* + * API + *************************************************************************************************/ + + +typedef struct { /* type of structure for a column index */ + char *name; /* column name */ + int type; /* data type */ + void *db; /* internal database object */ + void *cc; /* internal cache object */ +} TDBIDX; + +typedef struct { /* type of structure for a table database */ + void *mmtx; /* mutex for method */ + TCHDB *hdb; /* internal database object */ + bool open; /* whether the internal database is opened */ + bool wmode; /* whether to be writable */ + uint8_t opts; /* options */ + int32_t lcnum; /* max number of cached leaves */ + int32_t ncnum; /* max number of cached nodes */ + int64_t iccmax; /* maximum size of the inverted cache */ + double iccsync; /* synchronization ratio of the inverted cache */ + TDBIDX *idxs; /* column indices */ + int inum; /* number of column indices */ + bool tran; /* whether in the transaction */ +} TCTDB; + +enum { /* enumeration for additional flags */ + TDBFOPEN = HDBFOPEN, /* whether opened */ + TDBFFATAL = HDBFFATAL /* whether with fatal error */ +}; + +enum { /* enumeration for tuning options */ + TDBTLARGE = 1 << 0, /* use 64-bit bucket array */ + TDBTDEFLATE = 1 << 1, /* compress each page with Deflate */ + TDBTBZIP = 1 << 2, /* compress each record with BZIP2 */ + TDBTTCBS = 1 << 3, /* compress each page with TCBS */ + TDBTEXCODEC = 1 << 4 /* compress each record with outer functions */ +}; + +enum { /* enumeration for open modes */ + TDBOREADER = 1 << 0, /* open as a reader */ + TDBOWRITER = 1 << 1, /* open as a writer */ + TDBOCREAT = 1 << 2, /* writer creating */ + TDBOTRUNC = 1 << 3, /* writer truncating */ + TDBONOLCK = 1 << 4, /* open without locking */ + TDBOLCKNB = 1 << 5, /* lock without blocking */ + TDBOTSYNC = 1 << 6 /* synchronize every transaction */ +}; + +enum { /* enumeration for index types */ + TDBITLEXICAL, /* lexical string */ + TDBITDECIMAL, /* decimal string */ + TDBITTOKEN, /* token inverted index */ + TDBITQGRAM, /* q-gram inverted index */ + TDBITOPT = 9998, /* optimize */ + TDBITVOID = 9999, /* void */ + TDBITKEEP = 1 << 24 /* keep existing index */ +}; + +typedef struct { /* type of structure for a condition */ + char *name; /* column name */ + int nsiz; /* size of the column name */ + int op; /* operation type */ + bool sign; /* positive sign */ + bool noidx; /* no index flag */ + char *expr; /* operand expression */ + int esiz; /* size of the operand expression */ + void *regex; /* regular expression object */ + void *ftsunits; /* full-text search units */ + int ftsnum; /* number of full-text search units */ + bool alive; /* alive flag */ +} TDBCOND; + +typedef struct { /* type of structure for a query */ + TCTDB *tdb; /* database object */ + TDBCOND *conds; /* condition objects */ + int cnum; /* number of conditions */ + char *oname; /* column name for ordering */ + int otype; /* type of order */ + int max; /* max number of retrieval */ + int skip; /* skipping number of retrieval */ + TCXSTR *hint; /* hint string */ + int count; /* count of corresponding records */ +} TDBQRY; + +enum { /* enumeration for query conditions */ + TDBQCSTREQ, /* string is equal to */ + TDBQCSTRINC, /* string is included in */ + TDBQCSTRBW, /* string begins with */ + TDBQCSTREW, /* string ends with */ + TDBQCSTRAND, /* string includes all tokens in */ + TDBQCSTROR, /* string includes at least one token in */ + TDBQCSTROREQ, /* string is equal to at least one token in */ + TDBQCSTRRX, /* string matches regular expressions of */ + TDBQCNUMEQ, /* number is equal to */ + TDBQCNUMGT, /* number is greater than */ + TDBQCNUMGE, /* number is greater than or equal to */ + TDBQCNUMLT, /* number is less than */ + TDBQCNUMLE, /* number is less than or equal to */ + TDBQCNUMBT, /* number is between two tokens of */ + TDBQCNUMOREQ, /* number is equal to at least one token in */ + TDBQCFTSPH, /* full-text search with the phrase of */ + TDBQCFTSAND, /* full-text search with all tokens in */ + TDBQCFTSOR, /* full-text search with at least one token in */ + TDBQCFTSEX, /* full-text search with the compound expression of */ + TDBQCEXIST, /* string|number exists */ + TDBQTRUE, /* any field always matched */ + TDBQCSTRNUMOR, /* string includes at least one number token in */ + TDBQCSTRORBW, /* string begins with at least one token in */ + TDBQCNEGATE = 1 << 24, /* negation flag */ + TDBQCNOIDX = 1 << 25 /* no index flag */ +}; + +enum { /* enumeration for order types */ + TDBQOSTRASC, /* string ascending */ + TDBQOSTRDESC, /* string descending */ + TDBQONUMASC, /* number ascending */ + TDBQONUMDESC /* number descending */ +}; + +enum { /* enumeration for set operation types */ + TDBMSUNION, /* union */ + TDBMSISECT, /* intersection */ + TDBMSDIFF /* difference */ +}; + +enum { /* enumeration for post treatments */ + TDBQPPUT = 1 << 0, /* modify the record */ + TDBQPOUT = 1 << 1, /* remove the record */ + TDBQPSTOP = 1 << 24 /* stop the iteration */ +}; + +/* type of the pointer to a iterator function for each table record. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + `op' specifies the pointer to the optional opaque object. + The return value is flags of the post treatment by bitwise-or: `TDBQPPUT' to modify the + record, `TDBQPOUT' to remove the record, `TDBQPSTOP' to stop the iteration. */ +typedef int (*TDBQRYPROC)(const void *pkbuf, int pksiz, TCMAP *cols, void *op); + + +/* custom row data loader function. + `rowdata' row data (TCMAP data). + `rowdata' row data length. + `cname' column name. + `cnamesz' length of column name. + `op' opaque data for this function. + `vsz' resulting value size. */ +typedef char* (*TDBRVALOADER)(TCLIST *tokens, + const char *pkbuf, int pksz, + const char *rowdata, int rowdatasz, + const char *cname, int cnamesz, void *op, int *vsz); + + +/* Get the message string corresponding to an error code. + `ecode' specifies the error code. + The return value is the message string of the error code. */ +EJDB_EXPORT const char *tctdberrmsg(int ecode); + + +/* Create a table database object. + The return value is the new table database object. */ +EJDB_EXPORT TCTDB *tctdbnew(void); + + +/* Delete a table database object. + `tdb' specifies the table database object. + If the database is not closed, it is closed implicitly. Note that the deleted object and its + derivatives can not be used anymore. */ +EJDB_EXPORT void tctdbdel(TCTDB *tdb); + + +/* Get the last happened error code of a table database object. + `tdb' specifies the table database object. + The return value is the last happened error code. + The following error code is defined: `TCESUCCESS' for success, `TCETHREAD' for threading + error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no + permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' + for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync + error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, + `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' + for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for + rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for + miscellaneous error. */ +EJDB_EXPORT int tctdbecode(TCTDB *tdb); + + +/* Set mutual exclusion control of a table database object for threading. + `tdb' specifies the table database object which is not opened. + If successful, the return value is true, else, it is false. + Note that the mutual exclusion control is needed if the object is shared by plural threads and + this function should be called before the database is opened. */ +EJDB_EXPORT bool tctdbsetmutex(TCTDB *tdb); + + +/* Set the tuning parameters of a table database object. + `tdb' specifies the table database object which is not opened. + `bnum' specifies the number of elements of the bucket array. If it is not more than 0, the + default value is specified. The default value is 131071. Suggested size of the bucket array + is about from 0.5 to 4 times of the number of all records to be stored. + `apow' specifies the size of record alignment by power of 2. If it is negative, the default + value is specified. The default value is 4 standing for 2^4=16. + `fpow' specifies the maximum number of elements of the free block pool by power of 2. If it + is negative, the default value is specified. The default value is 10 standing for 2^10=1024. + `opts' specifies options by bitwise-or: `TDBTLARGE' specifies that the size of the database + can be larger than 2GB by using 64-bit bucket array, `TDBTDEFLATE' specifies that each record + is compressed with Deflate encoding, `TDBTBZIP' specifies that each record is compressed with + BZIP2 encoding, `TDBTTCBS' specifies that each record is compressed with TCBS encoding. + If successful, the return value is true, else, it is false. + Note that the tuning parameters should be set before the database is opened. */ +EJDB_EXPORT bool tctdbtune(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); + + +/* Set the caching parameters of a table database object. + `tdb' specifies the table database object which is not opened. + `rcnum' specifies the maximum number of records to be cached. If it is not more than 0, the + record cache is disabled. It is disabled by default. + `lcnum' specifies the maximum number of leaf nodes to be cached. If it is not more than 0, + the default value is specified. The default value is 4096. + `ncnum' specifies the maximum number of non-leaf nodes to be cached. If it is not more than 0, + the default value is specified. The default value is 512. + If successful, the return value is true, else, it is false. + Note that the caching parameters should be set before the database is opened. Leaf nodes and + non-leaf nodes are used in column indices. */ +EJDB_EXPORT bool tctdbsetcache(TCTDB *tdb, int32_t rcnum, int32_t lcnum, int32_t ncnum); + + +/* Set the size of the extra mapped memory of a table database object. + `tdb' specifies the table database object which is not opened. + `xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra + mapped memory is disabled. The default size is 67108864. + If successful, the return value is true, else, it is false. + Note that the mapping parameters should be set before the database is opened. */ +EJDB_EXPORT bool tctdbsetxmsiz(TCTDB *tdb, int64_t xmsiz); + + +/* Set the unit step number of auto defragmentation of a table database object. + `tdb' specifies the table database object which is not opened. + `dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation + is disabled. It is disabled by default. + If successful, the return value is true, else, it is false. + Note that the defragmentation parameters should be set before the database is opened. */ +EJDB_EXPORT bool tctdbsetdfunit(TCTDB *tdb, int32_t dfunit); + + +/* Open a database file and connect a table database object. + `tdb' specifies the table database object which is not opened. + `path' specifies the path of the database file. + `omode' specifies the connection mode: `TDBOWRITER' as a writer, `TDBOREADER' as a reader. + If the mode is `TDBOWRITER', the following may be added by bitwise-or: `TDBOCREAT', which + means it creates a new database if not exist, `TDBOTRUNC', which means it creates a new + database regardless if one exists, `TDBOTSYNC', which means every transaction synchronizes + updated contents with the device. Both of `TDBOREADER' and `TDBOWRITER' can be added to by + bitwise-or: `TDBONOLCK', which means it opens the database file without file locking, or + `TDBOLCKNB', which means locking is performed without blocking. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbopen(TCTDB *tdb, const char *path, int omode); + + +/* Close a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. + Update of a database is assured to be written when the database is closed. If a writer opens + a database but does not close it appropriately, the database will be broken. */ +EJDB_EXPORT bool tctdbclose(TCTDB *tdb); + + +/* Store a record into a table database object. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. + If a record with the same key exists in the database, it is overwritten. */ +EJDB_EXPORT bool tctdbput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); + + +/* Store a string record into a table database object with a zero separated column string. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cbuf' specifies the pointer to the region of the zero separated column string where the name + and the value of each column are situated one after the other. + `csiz' specifies the size of the region of the column string. + If successful, the return value is true, else, it is false. + If a record with the same key exists in the database, it is overwritten. */ +EJDB_EXPORT bool tctdbput2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz); + + +/* Store a string record into a table database object with a tab separated column string. + `tdb' specifies the table database object connected as a writer. + `pkstr' specifies the string of the primary key. + `cstr' specifies the string of the the tab separated column string where the name and the + value of each column are situated one after the other. + If successful, the return value is true, else, it is false. + If a record with the same key exists in the database, it is overwritten. */ +EJDB_EXPORT bool tctdbput3(TCTDB *tdb, const char *pkstr, const char *cstr); + + +/* Store a new record into a table database object. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. + If a record with the same key exists in the database, this function has no effect. */ +EJDB_EXPORT bool tctdbputkeep(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); + + +/* Store a new string record into a table database object with a zero separated column string. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cbuf' specifies the pointer to the region of the zero separated column string where the name + and the value of each column are situated one after the other. + `csiz' specifies the size of the region of the column string. + If successful, the return value is true, else, it is false. + If a record with the same key exists in the database, this function has no effect. */ +EJDB_EXPORT bool tctdbputkeep2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz); + + +/* Store a new string record into a table database object with a tab separated column string. + `tdb' specifies the table database object connected as a writer. + `pkstr' specifies the string of the primary key. + `cstr' specifies the string of the the tab separated column string where the name and the + value of each column are situated one after the other. + If successful, the return value is true, else, it is false. + If a record with the same key exists in the database, this function has no effect. */ +EJDB_EXPORT bool tctdbputkeep3(TCTDB *tdb, const char *pkstr, const char *cstr); + + +/* Concatenate columns of the existing record in a table database object. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. + If there is no corresponding record, a new record is created. */ +EJDB_EXPORT bool tctdbputcat(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); + + +/* Concatenate columns in a table database object with a zero separated column string. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cbuf' specifies the pointer to the region of the zero separated column string where the name + and the value of each column are situated one after the other. + `csiz' specifies the size of the region of the column string. + If successful, the return value is true, else, it is false. + If there is no corresponding record, a new record is created. */ +EJDB_EXPORT bool tctdbputcat2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz); + + +/* Concatenate columns in a table database object with with a tab separated column string. + `tdb' specifies the table database object connected as a writer. + `pkstr' specifies the string of the primary key. + `cstr' specifies the string of the the tab separated column string where the name and the + value of each column are situated one after the other. + If successful, the return value is true, else, it is false. + If there is no corresponding record, a new record is created. */ +EJDB_EXPORT bool tctdbputcat3(TCTDB *tdb, const char *pkstr, const char *cstr); + + +/* Remove a record of a table database object. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbout(TCTDB *tdb, const void *pkbuf, int pksiz); + + +/* Remove a string record of a table database object. + `tdb' specifies the table database object connected as a writer. + `pkstr' specifies the string of the primary key. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbout2(TCTDB *tdb, const char *pkstr); + + +/* Retrieve a record in a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If successful, the return value is a map object of the columns of the corresponding record. + `NULL' is returned if no record corresponds. + Because the object of the return value is created with the function `tcmapnew', it should be + deleted with the function `tcmapdel' when it is no longer in use. */ +EJDB_EXPORT TCMAP *tctdbget(TCTDB *tdb, const void *pkbuf, int pksiz); + + +/* Retrieve a record in a table database object as a zero separated column string. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `sp' specifies the pointer to the variable into which the size of the region of the return + value is assigned. + If successful, the return value is the pointer to the region of the column string of the + corresponding record. `NULL' is returned if no record corresponds. + Because an additional zero code is appended at the end of the region of the return value, + the return value can be treated as a character string. Because the region of the return + value is allocated with the `malloc' call, it should be released with the `free' call when + it is no longer in use. */ +EJDB_EXPORT char *tctdbget2(TCTDB *tdb, const void *pkbuf, int pksiz, int *sp); + + +/* Retrieve a string record in a table database object as a tab separated column string. + `tdb' specifies the table database object. + `pkstr' specifies the string of the primary key. + If successful, the return value is the tab separated column string of the corresponding + record. `NULL' is returned if no record corresponds. + Because the region of the return value is allocated with the `malloc' call, it should be + released with the `free' call when it is no longer in use. */ +EJDB_EXPORT char *tctdbget3(TCTDB *tdb, const char *pkstr); + + +/* Get the size of the value of a record in a table database object. + `tdb' specifies the table database object. + `kbuf' specifies the pointer to the region of the primary key. + `ksiz' specifies the size of the region of the primary key. + If successful, the return value is the size of the value of the corresponding record, else, + it is -1. */ +EJDB_EXPORT int tctdbvsiz(TCTDB *tdb, const void *pkbuf, int pksiz); + + +/* Get the size of the value of a string record in a table database object. + `tdb' specifies the table database object. + `kstr' specifies the string of the primary key. + If successful, the return value is the size of the value of the corresponding record, else, + it is -1. */ +EJDB_EXPORT int tctdbvsiz2(TCTDB *tdb, const char *pkstr); + + +/* Initialize the iterator of a table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. + The iterator is used in order to access the primary key of every record stored in a + database. */ +EJDB_EXPORT bool tctdbiterinit(TCTDB *tdb); + + +/* Get the next primary key of the iterator of a table database object. + `tdb' specifies the table database object. + `sp' specifies the pointer to the variable into which the size of the region of the return + value is assigned. + If successful, the return value is the pointer to the region of the next primary key, else, it + is `NULL'. `NULL' is returned when no record is to be get out of the iterator. + Because an additional zero code is appended at the end of the region of the return value, the + return value can be treated as a character string. Because the region of the return value is + allocated with the `malloc' call, it should be released with the `free' call when it is no + longer in use. It is possible to access every record by iteration of calling this function. + It is allowed to update or remove records whose keys are fetched while the iteration. + However, it is not assured if updating the database is occurred while the iteration. Besides, + the order of this traversal access method is arbitrary, so it is not assured that the order of + storing matches the one of the traversal access. */ +EJDB_EXPORT void *tctdbiternext(TCTDB *tdb, int *sp); + + +/* Get the next primary key string of the iterator of a table database object. + `tdb' specifies the table database object. + If successful, the return value is the string of the next primary key, else, it is `NULL'. + `NULL' is returned when no record is to be get out of the iterator. + Because the region of the return value is allocated with the `malloc' call, it should be + released with the `free' call when it is no longer in use. It is possible to access every + record by iteration of calling this function. However, it is not assured if updating the + database is occurred while the iteration. Besides, the order of this traversal access method + is arbitrary, so it is not assured that the order of storing matches the one of the traversal + access. */ +EJDB_EXPORT char *tctdbiternext2(TCTDB *tdb); + + +/* Get the columns of the next record of the iterator of a table database object. + `tdb' specifies the table database object. + If successful, the return value is a map object of the columns of the next record, else, it is + `NULL'. `NULL' is returned when no record is to be get out of the iterator. The primary key + is added into the map as a column of an empty string key. + Because the object of the return value is created with the function `tcmapnew', it should be + deleted with the function `tcmapdel' when it is no longer in use. It is possible to access + every record by iteration of calling this function. However, it is not assured if updating + the database is occurred while the iteration. Besides, the order of this traversal access + method is arbitrary, so it is not assured that the order of storing matches the one of the + traversal access. */ +EJDB_EXPORT TCMAP *tctdbiternext3(TCTDB *tdb); + + +/* Get forward matching primary keys in a table database object. + `tdb' specifies the table database object. + `pbuf' specifies the pointer to the region of the prefix. + `psiz' specifies the size of the region of the prefix. + `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is + specified. + The return value is a list object of the corresponding keys. This function does never fail. + It returns an empty list even if no key corresponds. + Because the object of the return value is created with the function `tclistnew', it should be + deleted with the function `tclistdel' when it is no longer in use. Note that this function + may be very slow because every key in the database is scanned. */ +EJDB_EXPORT TCLIST *tctdbfwmkeys(TCTDB *tdb, const void *pbuf, int psiz, int max); + + +/* Get forward matching string primary keys in a table database object. + `tdb' specifies the table database object. + `pstr' specifies the string of the prefix. + `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is + specified. + The return value is a list object of the corresponding keys. This function does never fail. + It returns an empty list even if no key corresponds. + Because the object of the return value is created with the function `tclistnew', it should be + deleted with the function `tclistdel' when it is no longer in use. Note that this function + may be very slow because every key in the database is scanned. */ +EJDB_EXPORT TCLIST *tctdbfwmkeys2(TCTDB *tdb, const char *pstr, int max); + + +/* Add an integer to a column of a record in a table database object. + `tdb' specifies the table database object connected as a writer. + `kbuf' specifies the pointer to the region of the primary key. + `ksiz' specifies the size of the region of the primary key. + `num' specifies the additional value. + If successful, the return value is the summation value, else, it is `INT_MIN'. + The additional value is stored as a decimal string value of a column whose name is "_num". + If no record corresponds, a new record with the additional value is stored. */ +EJDB_EXPORT int tctdbaddint(TCTDB *tdb, const void *pkbuf, int pksiz, int num); + + +/* Add a real number to a column of a record in a table database object. + `tdb' specifies the table database object connected as a writer. + `kbuf' specifies the pointer to the region of the primary key. + `ksiz' specifies the size of the region of the primary key. + `num' specifies the additional value. + If successful, the return value is the summation value, else, it is Not-a-Number. + The additional value is stored as a decimal string value of a column whose name is "_num". + If no record corresponds, a new record with the additional value is stored. */ +EJDB_EXPORT double tctdbadddouble(TCTDB *tdb, const void *pkbuf, int pksiz, double num); + + +/* Synchronize updated contents of a table database object with the file and the device. + `tdb' specifies the table database object connected as a writer. + If successful, the return value is true, else, it is false. + This function is useful when another process connects to the same database file. */ +EJDB_EXPORT bool tctdbsync(TCTDB *tdb); + + +/* Optimize the file of a table database object. + `tdb' specifies the table database object connected as a writer. + `bnum' specifies the number of elements of the bucket array. If it is not more than 0, the + default value is specified. The default value is two times of the number of records. + `apow' specifies the size of record alignment by power of 2. If it is negative, the current + setting is not changed. + `fpow' specifies the maximum number of elements of the free block pool by power of 2. If it + is negative, the current setting is not changed. + `opts' specifies options by bitwise-or: `BDBTLARGE' specifies that the size of the database + can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each record + is compressed with Deflate encoding, `BDBTBZIP' specifies that each record is compressed with + BZIP2 encoding, `BDBTTCBS' specifies that each record is compressed with TCBS encoding. If it + is `UINT8_MAX', the current setting is not changed. + If successful, the return value is true, else, it is false. + This function is useful to reduce the size of the database file with data fragmentation by + successive updating. */ +EJDB_EXPORT bool tctdboptimize(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); + + +/* Remove all records of a table database object. + `tdb' specifies the table database object connected as a writer. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbvanish(TCTDB *tdb); + + +/* Copy the database file of a table database object. + `tdb' specifies the table database object. + `path' specifies the path of the destination file. If it begins with `@', the trailing + substring is executed as a command line. + If successful, the return value is true, else, it is false. False is returned if the executed + command returns non-zero code. + The database file is assured to be kept synchronized and not modified while the copying or + executing operation is in progress. So, this function is useful to create a backup file of + the database file. */ +EJDB_EXPORT bool tctdbcopy(TCTDB *tdb, const char *path); + + +/* Begin the transaction of a table database object. + `tdb' specifies the table database object connected as a writer. + If successful, the return value is true, else, it is false. + The database is locked by the thread while the transaction so that only one transaction can be + activated with a database object at the same time. Thus, the serializable isolation level is + assumed if every database operation is performed in the transaction. Because all pages are + cached on memory while the transaction, the amount of referred records is limited by the + memory capacity. If the database is closed during transaction, the transaction is aborted + implicitly. */ +EJDB_EXPORT bool tctdbtranbegin(TCTDB *tdb); + + +/* Commit the transaction of a table database object. + `tdb' specifies the table database object connected as a writer. + If successful, the return value is true, else, it is false. + Update in the transaction is fixed when it is committed successfully. */ +EJDB_EXPORT bool tctdbtrancommit(TCTDB *tdb); + + +/* Abort the transaction of a table database object. + `tdb' specifies the table database object connected as a writer. + If successful, the return value is true, else, it is false. + Update in the transaction is discarded when it is aborted. The state of the database is + rollbacked to before transaction. */ +EJDB_EXPORT bool tctdbtranabort(TCTDB *tdb); + + +/* Get the file path of a table database object. + `tdb' specifies the table database object. + The return value is the path of the database file or `NULL' if the object does not connect to + any database file. */ +EJDB_EXPORT const char *tctdbpath(TCTDB *tdb); + + +/* Get the number of records ccccof a table database object. + `tdb' specifies the table database object. + The return value is the number of records or 0 if the object does not connect to any database + file. */ +EJDB_EXPORT uint64_t tctdbrnum(TCTDB *tdb); + + +/* Get the size of the database file of a table database object. + `tdb' specifies the table database object. + The return value is the size of the database file or 0 if the object does not connect to any + database file. */ +EJDB_EXPORT uint64_t tctdbfsiz(TCTDB *tdb); + + +/* Set a column index to a table database object. + `tdb' specifies the table database object connected as a writer. + `name' specifies the name of a column. If the name of an existing index is specified, the + index is rebuilt. An empty string means the primary key. + `type' specifies the index type: `TDBITLEXICAL' for lexical string, `TDBITDECIMAL' for decimal + string, `TDBITTOKEN' for token inverted index, `TDBITQGRAM' for q-gram inverted index. If it + is `TDBITOPT', the index is optimized. If it is `TDBITVOID', the index is removed. If + `TDBITKEEP' is added by bitwise-or and the index exists, this function merely returns failure. + If successful, the return value is true, else, it is false. + Note that the setting indices should be set after the database is opened. */ +EJDB_EXPORT bool tctdbsetindex(TCTDB *tdb, const char *name, int type); + +EJDB_EXPORT bool tctdbsetindexrldr(TCTDB *tdb, const char *name, int type, TDBRVALOADER rvldr, void* rvldrop); + + +/* Generate a unique ID number of a table database object. + `tdb' specifies the table database object connected as a writer. + The return value is the new unique ID number or -1 on failure. */ +EJDB_EXPORT int64_t tctdbgenuid(TCTDB *tdb); + + +/* Create a query object. + `tdb' specifies the table database object. + The return value is the new query object. */ +EJDB_EXPORT TDBQRY *tctdbqrynew(TCTDB *tdb); + + +/* Delete a query object. + `qry' specifies the query object. */ +EJDB_EXPORT void tctdbqrydel(TDBQRY *qry); + + +/* Add a narrowing condition to a query object. + `qry' specifies the query object. + `name' specifies the name of a column. An empty string means the primary key. + `op' specifies an operation type: `TDBQCSTREQ' for string which is equal to the expression, + `TDBQCSTRINC' for string which is included in the expression, `TDBQCSTRBW' for string which + begins with the expression, `TDBQCSTREW' for string which ends with the expression, + `TDBQCSTRAND' for string which includes all tokens in the expression, `TDBQCSTROR' for string + which includes at least one token in the expression, `TDBQCSTROREQ' for string which is equal + to at least one token in the expression, `TDBQCSTRRX' for string which matches regular + expressions of the expression, `TDBQCNUMEQ' for number which is equal to the expression, + `TDBQCNUMGT' for number which is greater than the expression, `TDBQCNUMGE' for number which is + greater than or equal to the expression, `TDBQCNUMLT' for number which is less than the + expression, `TDBQCNUMLE' for number which is less than or equal to the expression, `TDBQCNUMBT' + for number which is between two tokens of the expression, `TDBQCNUMOREQ' for number which is + equal to at least one token in the expression, `TDBQCFTSPH' for full-text search with the + phrase of the expression, `TDBQCFTSAND' for full-text search with all tokens in the expression, + `TDBQCFTSOR' for full-text search with at least one token in the expression, `TDBQCFTSEX' for + full-text search with the compound expression. All operations can be flagged by bitwise-or: + `TDBQCNEGATE' for negation, `TDBQCNOIDX' for using no index. + `expr' specifies an operand exression. */ +EJDB_EXPORT void tctdbqryaddcond(TDBQRY *qry, const char *name, int op, const char *expr); + + +/* Set the order of a query object. + `qry' specifies the query object. + `name' specifies the name of a column. An empty string means the primary key. + `type' specifies the order type: `TDBQOSTRASC' for string ascending, `TDBQOSTRDESC' for + string descending, `TDBQONUMASC' for number ascending, `TDBQONUMDESC' for number descending. */ +EJDB_EXPORT void tctdbqrysetorder(TDBQRY *qry, const char *name, int type); + + +/* Set the limit number of records of the result of a query object. + `qry' specifies the query object. + `max' specifies the maximum number of records of the result. If it is negative, no limit is + specified. + `skip' specifies the number of skipped records of the result. If it is not more than 0, no + record is skipped. */ +EJDB_EXPORT void tctdbqrysetlimit(TDBQRY *qry, int max, int skip); + + +/* Execute the search of a query object. + `qry' specifies the query object. + The return value is a list object of the primary keys of the corresponding records. This + function does never fail. It returns an empty list even if no record corresponds. + Because the object of the return value is created with the function `tclistnew', it should + be deleted with the function `tclistdel' when it is no longer in use. */ +EJDB_EXPORT TCLIST *tctdbqrysearch(TDBQRY *qry); + + +/* Remove each record corresponding to a query object. + `qry' specifies the query object of the database connected as a writer. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbqrysearchout(TDBQRY *qry); + + +/* Process each record corresponding to a query object. + `qry' specifies the query object of the database connected as a writer. + `proc' specifies the pointer to the iterator function called for each record. It receives + four parameters. The first parameter is the pointer to the region of the primary key. The + second parameter is the size of the region of the primary key. The third parameter is a map + object containing columns. The fourth parameter is the pointer to the optional opaque object. + It returns flags of the post treatment by bitwise-or: `TDBQPPUT' to modify the record, + `TDBQPOUT' to remove the record, `TDBQPSTOP' to stop the iteration. + `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If + it is not needed, `NULL' can be specified. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbqryproc(TDBQRY *qry, TDBQRYPROC proc, void *op); + + +/* Get the hint string of a query object. + `qry' specifies the query object. + The return value is the hint string. + This function should be called after the query execution by `tctdbqrysearch' and so on. The + region of the return value is overwritten when this function is called again. */ +EJDB_EXPORT const char *tctdbqryhint(TDBQRY *qry); + + +/* Retrieve records with multiple query objects and get the set of the result. + `qrys' specifies an array of the query objects. + `num' specifies the number of elements of the array. + `type' specifies a set operation type: `TDBMSUNION' for the union set, `TDBMSISECT' for the + intersection set, `TDBMSDIFF' for the difference set. + The return value is a list object of the primary keys of the corresponding records. This + function does never fail. It returns an empty list even if no record corresponds. + If the first query object has the order setting, the result array is sorted by the order. + Because the object of the return value is created with the function `tclistnew', it should be + deleted with the function `tclistdel' when it is no longer in use. */ +EJDB_EXPORT TCLIST *tctdbmetasearch(TDBQRY **qrys, int num, int type); + + + +/************************************************************************************************* + * features for experts + *************************************************************************************************/ + + +/* Set the error code of a table database object. + `tdb' specifies the table database object. + `ecode' specifies the error code. + `file' specifies the file name of the code. + `line' specifies the line number of the code. + `func' specifies the function name of the code. */ +EJDB_EXPORT void tctdbsetecode(TCTDB *tdb, int ecode, const char *filename, int line, const char *func); + +EJDB_EXPORT void tctdbsetecode2(TCTDB *tdb, int ecode, const char *filename, int line, const char *func, bool notfatal); + + +/* Set the file descriptor for debugging output. + `tdb' specifies the table database object. + `fd' specifies the file descriptor for debugging output. */ +EJDB_EXPORT void tctdbsetdbgfd(TCTDB *tdb, HANDLE fd); + + +/* Get the file descriptor for debugging output. + `tdb' specifies the table database object. + The return value is the file descriptor for debugging output. */ +EJDB_EXPORT HANDLE tctdbdbgfd(TCTDB *tdb); + + +/* Check whether mutual exclusion control is set to a table database object. + `tdb' specifies the table database object. + If mutual exclusion control is set, it is true, else it is false. */ +EJDB_EXPORT bool tctdbhasmutex(TCTDB *tdb); + + +/* Synchronize updating contents on memory of a table database object. + `tdb' specifies the table database object connected as a writer. + `phys' specifies whether to synchronize physically. + If successful, the return value is true, else, it is false. */ +bool tctdbmemsync(TCTDB *tdb, bool phys); + + +/* Get the number of elements of the bucket array of a table database object. + `tdb' specifies the table database object. + The return value is the number of elements of the bucket array or 0 if the object does not + connect to any database file. */ +EJDB_EXPORT uint64_t tctdbbnum(TCTDB *tdb); + + +/* Get the record alignment of a table database object. + `tdb' specifies the table database object. + The return value is the record alignment or 0 if the object does not connect to any database + file. */ +EJDB_EXPORT uint32_t tctdbalign(TCTDB *tdb); + + +/* Get the maximum number of the free block pool of a table database object. + `tdb' specifies the table database object. + The return value is the maximum number of the free block pool or 0 if the object does not + connect to any database file. */ +EJDB_EXPORT uint32_t tctdbfbpmax(TCTDB *tdb); + + +/* Get the inode number of the database file of a table database object. + `tdb' specifies the table database object. + The return value is the inode number of the database file or 0 if the object does not connect + to any database file. */ +EJDB_EXPORT uint64_t tctdbinode(TCTDB *tdb); + + +/* Get the modification time of the database file of a table database object. + `tdb' specifies the table database object. + The return value is the inode number of the database file or 0 if the object does not connect + to any database file. */ +EJDB_EXPORT time_t tctdbmtime(TCTDB *tdb); + + +/* Get the additional flags of a table database object. + `tdb' specifies the table database object. + The return value is the additional flags. */ +EJDB_EXPORT uint8_t tctdbflags(TCTDB *tdb); + + +/* Get the options of a table database object. + `tdb' specifies the table database object. + The return value is the options. */ +EJDB_EXPORT uint8_t tctdbopts(TCTDB *tdb); + + +/* Get the number of used elements of the bucket array of a table database object. + `tdb' specifies the table database object. + The return value is the number of used elements of the bucket array or 0 if the object does not + connect to any database file. */ +EJDB_EXPORT uint64_t tctdbbnumused(TCTDB *tdb); + + +/* Get the number of column indices of a table database object. + `tdb' specifies the table database object. + The return value is the number of column indices or 0 if the object does not connect to any + database file. */ +EJDB_EXPORT int tctdbinum(TCTDB *tdb); + + +/* Get the seed of unique ID unumbers of a table database object. + `tdb' specifies the table database object. + The return value is the seed of unique ID numbers or -1 on failure. */ +EJDB_EXPORT int64_t tctdbuidseed(TCTDB *tdb); + + +/* Set the seed of unique ID unumbers of a table database object. + `tdb' specifies the table database object connected as a writer. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbsetuidseed(TCTDB *tdb, int64_t seed); + + +/* Set the parameters of the inverted cache of a table database object. + `tdb' specifies the table database object. + `iccmax' specifies the maximum size. If it is not more than 0, the default value is + specified. The default value is 67108864. + `iccsync' specifies synchronization ratio. If it is not more than 0, the default value is + specified. The default value is 0.01. + If successful, the return value is true, else, it is false. + Note that the caching parameters should be set before the database is opened. */ +EJDB_EXPORT bool tctdbsetinvcache(TCTDB *tdb, int64_t iccmax, double iccsync); + + +/* Set the custom codec functions of a table database object. + `tdb' specifies the table database object. + `enc' specifies the pointer to the custom encoding function. It receives four parameters. + The first parameter is the pointer to the region. The second parameter is the size of the + region. The third parameter is the pointer to the variable into which the size of the region + of the return value is assigned. The fourth parameter is the pointer to the optional opaque + object. It returns the pointer to the result object allocated with `malloc' call if + successful, else, it returns `NULL'. + `encop' specifies an arbitrary pointer to be given as a parameter of the encoding function. + If it is not needed, `NULL' can be specified. + `dec' specifies the pointer to the custom decoding function. + `decop' specifies an arbitrary pointer to be given as a parameter of the decoding function. + If it is not needed, `NULL' can be specified. + If successful, the return value is true, else, it is false. + Note that the custom codec functions should be set before the database is opened and should be + set every time the database is being opened. */ +EJDB_EXPORT bool tctdbsetcodecfunc(TCTDB *tdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop); + + +/* Get the unit step number of auto defragmentation of a table database object. + `tdb' specifies the table database object. + The return value is the unit step number of auto defragmentation. */ +EJDB_EXPORT uint32_t tctdbdfunit(TCTDB *tdb); + + +/* Perform dynamic defragmentation of a table database object. + `tdb' specifies the table database object connected as a writer. + `step' specifie the number of steps. If it is not more than 0, the whole file is defragmented + gradually without keeping a continuous lock. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbdefrag(TCTDB *tdb, int64_t step); + + +/* Clear the cache of a table tree database object. + `tdb' specifies the table tree database object. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbcacheclear(TCTDB *tdb); + + +/* Store a record into a table database object with a duplication handler. + `tdb' specifies the table database object connected as a writer. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cbuf' specifies the pointer to the region of the zero separated column string where the name + and the value of each column are situated one after the other. `NULL' means that record + addition is ommited if there is no corresponding record. + `csiz' specifies the size of the region of the column string. + `proc' specifies the pointer to the callback function to process duplication. It receives + four parameters. The first parameter is the pointer to the region of the value. The second + parameter is the size of the region of the value. The third parameter is the pointer to the + variable into which the size of the region of the return value is assigned. The fourth + parameter is the pointer to the optional opaque object. It returns the pointer to the result + object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is + not modified. If it is `(void *)-1', the record is removed. + `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If + it is not needed, `NULL' can be specified. + If successful, the return value is true, else, it is false. + Note that the callback function can not perform any database operation because the function + is called in the critical section guarded by the same locks of database operations. */ +EJDB_EXPORT bool tctdbputproc(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz, + TCPDPROC proc, void *op); + + +/* Retrieve the value of a column of a record in a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `nbuf' specifies the pointer to the region of the column name. + `nsiz' specifies the size of the region of the column name. + `sp' specifies the pointer to the variable into which the size of the region of the return + value is assigned. + If successful, the return value is the pointer to the region of the value of the column of the + corresponding record. `NULL' is returned if no record corresponds or there is no column. + Because an additional zero code is appended at the end of the region of the return value, + the return value can be treated as a character string. Because the region of the return + value is allocated with the `malloc' call, it should be released with the `free' call when + it is no longer in use. */ +EJDB_EXPORT char *tctdbget4(TCTDB *tdb, const void *pkbuf, int pksiz, const void *nbuf, int nsiz, int *sp); + + +/* Move the iterator to the record corresponding a key of a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + If successful, the return value is true, else, it is false. False is returned if there is + no record corresponding the condition. */ +EJDB_EXPORT bool tctdbiterinit2(TCTDB *tdb, const void *pkbuf, int pksiz); + + +/* Move the iterator to the record corresponding a key string of a table database object. + `tdb' specifies the table database object. + `kstr' specifies the string of the primary key. + If successful, the return value is true, else, it is false. False is returned if there is + no record corresponding the condition. */ +EJDB_EXPORT bool tctdbiterinit3(TCTDB *tdb, const char *kstr); + + +/* Process each record atomically of a table database object. + `tdb' specifies the table database object. + `iter' specifies the pointer to the iterator function called for each record. It receives + five parameters. The first parameter is the pointer to the region of the key. The second + parameter is the size of the region of the key. The third parameter is the pointer to the + region of the value. The fourth parameter is the size of the region of the value. The fifth + parameter is the pointer to the optional opaque object. It returns true to continue iteration + or false to stop iteration. + `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If + it is not needed, `NULL' can be specified. + If successful, the return value is true, else, it is false. + Note that the callback function can not perform any database operation because the function + is called in the critical section guarded by the same locks of database operations. */ +EJDB_EXPORT bool tctdbforeach(TCTDB *tdb, TCITER iter, void *op); + + +/* Process each record corresponding to a query object with non-atomic fashion. + `qry' specifies the query object of the database connected as a writer. + `proc' specifies the pointer to the iterator function called for each record. It receives + four parameters. The first parameter is the pointer to the region of the primary key. The + second parameter is the size of the region of the primary key. The third parameter is a map + object containing columns. The fourth parameter is the pointer to the optional opaque object. + It returns flags of the post treatment by bitwise-or: `TDBQPPUT' to modify the record, + `TDBQPOUT' to remove the record, `TDBQPSTOP' to stop the iteration. + `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If + it is not needed, `NULL' can be specified. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbqryproc2(TDBQRY *qry, TDBQRYPROC proc, void *op); + + +/* Remove each record corresponding to a query object with non-atomic fashion. + `qry' specifies the query object of the database connected as a writer. + If successful, the return value is true, else, it is false. */ +EJDB_EXPORT bool tctdbqrysearchout2(TDBQRY *qry); + + +/* Convert a string into the index type number. + `str' specifies a string. + The return value is the index type number or -1 on failure. */ +EJDB_EXPORT int tctdbstrtoindextype(const char *str); + + +/* Convert a string into the meta search type number. + `str' specifies a string. + The return value is the meta search type number or -1 on failure. */ +EJDB_EXPORT int tctdbstrtometasearcytype(const char *str); + + +/* Get the count of corresponding records of a query object. + `qry' specifies the query object. + The return value is the count of corresponding records. */ +EJDB_EXPORT int tctdbqrycount(TDBQRY *qry); + + +/* Generate keyword-in-context strings from a query object. + `qry' specifies the query object. + `cols' specifies a map object containing columns. + `name' specifies the name of a column. If it is `NULL', the first column of the query is + specified. + `width' specifies the width of strings picked up around each keyword. + `opts' specifies options by bitwise-or: `TCKWMUTAB' specifies that each keyword is marked up + between two tab characters, `TCKWMUCTRL' specifies that each keyword is marked up by the STX + (0x02) code and the ETX (0x03) code, `TCKWMUBRCT' specifies that each keyword is marked up by + the two square brackets, `TCKWNOOVER' specifies that each context does not overlap, + `TCKWPULEAD' specifies that the lead string is picked up forcibly. + The return value is the list object whose elements are strings around keywords. + Because the object of the return value is created with the function `tclistnew', it should + be deleted with the function `tclistdel' when it is no longer in use. */ +EJDB_EXPORT TCLIST *tctdbqrykwic(TDBQRY *qry, TCMAP *cols, const char *name, int width, int opts); + + +/* Convert a string into the query operation number. + `str' specifies a string. + The return value is the query operation number or -1 on failure. */ +EJDB_EXPORT int tctdbqrystrtocondop(const char *str); + + +/* Convert a string into the query order type number. + `str' specifies a string. + The return value is the query order type or -1 on failure. */ +EJDB_EXPORT int tctdbqrystrtoordertype(const char *str); + + +/* Convert a string into the set operation type number. + `str' specifies a string. + The return value is the set operation type or -1 on failure. */ +EJDB_EXPORT int tctdbmetastrtosettype(const char *str); + + +/* Add a record into indices of a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. */ +bool tctdbidxput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); +bool tctdbidxput2(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); + +/* Remove a record from indices of a table database object. + `tdb' specifies the table database object. + `pkbuf' specifies the pointer to the region of the primary key. + `pksiz' specifies the size of the region of the primary key. + `cols' specifies a map object containing columns. + If successful, the return value is true, else, it is false. */ +bool tctdbidxout(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); +bool tctdbidxout2(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); + +/* Jump a cursor to the record of a key. + `cur' specifies the cursor object. + `expr' specifies the expression. + `esiz' specifies the size of the expression. + `first' specifies whether to jump the first candidate. + If successful, the return value is true, else, it is false. */ +bool tctdbqryidxcurjumpnum(BDBCUR *cur, const char *kbuf, int ksiz, bool first); + +/* Compare two primary keys by number ascending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +int tdbcmppkeynumasc(const TCLISTDATUM *a, const TCLISTDATUM *b); + + +/* Compare two primary keys by number descending. + `a' specifies a key. + `b' specifies of the other key. + The return value is positive if the former is big, negative if the latter is big, 0 if both + are equivalent. */ +int tdbcmppkeynumdesc(const TCLISTDATUM *a, const TCLISTDATUM *b); + + +/* Retrieve records by a token inverted index of a table database object. + `tdb' specifies the table database object. + `idx' specifies the index object. + `token' specifies the list object of tokens. + `op' specifies the operation type. + `hint' specifies the hint object. + The return value is a map object of the primary keys of the corresponding records. */ +TCMAP *tctdbidxgetbytokens(TCTDB *tdb, const TDBIDX *idx, const TCLIST *tokens, int op, TCXSTR *hint); + +bool tctdbtranbeginimpl(TCTDB *tdb); + +bool tctdbtrancommitimpl(TCTDB *tdb); + +bool tctdbtranabortimpl(TCTDB *tdb); + +#define TDBDEFBNUM 131071 // default bucket number + + +__TCTDB_CLINKAGEEND +#endif /* duplication check */ + + +/* END OF FILE */ diff --git a/tcejdb/src/tctdb/tests/tctmttest.c b/tcejdb/src/tctdb/tests/tctmttest.c new file mode 100644 index 0000000..cba512b --- /dev/null +++ b/tcejdb/src/tctdb/tests/tctmttest.c @@ -0,0 +1,1547 @@ +/************************************************************************************************* + * The test cases of the table database API + * Copyright (C) 2006-2012 FAL Labs + * This file is part of Tokyo Cabinet. + * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of + * the GNU Lesser General Public License as published by the Free Software Foundation; either + * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * You should have received a copy of the GNU Lesser General Public License along with Tokyo + * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA. + *************************************************************************************************/ + + +#include <tcutil.h> +#include <tctdb.h> +#include "myconf.h" + +#define RECBUFSIZ 48 // buffer for records + +typedef struct { // type of structure for write thread + TCTDB *tdb; + int rnum; + bool rnd; + int id; +} TARGWRITE; + +typedef struct { // type of structure for read thread + TCTDB *tdb; + int rnum; + bool rnd; + int id; +} TARGREAD; + +typedef struct { // type of structure for remove thread + TCTDB *tdb; + int rnum; + bool rnd; + int id; +} TARGREMOVE; + +typedef struct { // type of structure for wicked thread + TCTDB *tdb; + int rnum; + int id; +} TARGWICKED; + +typedef struct { // type of structure for typical thread + TCTDB *tdb; + int rnum; + int rratio; + int id; +} TARGTYPICAL; + + +/* global variables */ +const char *g_progname; // program name +unsigned int g_randseed; // random seed +HANDLE g_dbgfd = INVALID_HANDLE_VALUE; // debugging output + + +/* function prototypes */ +int main(int argc, char **argv); +static void usage(void); +static void iprintf(const char *format, ...); +static void iputchar(int c); +static void eprint(TCTDB *tdb, int line, const char *func); +static void sysprint(void); +static int myrand(int range); +static int myrandnd(int range); +static int runwrite(int argc, char **argv); +static int runread(int argc, char **argv); +static int runremove(int argc, char **argv); +static int runwicked(int argc, char **argv); +static int runtypical(int argc, char **argv); +static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, + int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int iflags, int omode, bool rnd); +static int procread(const char *path, int tnum, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd); +static int procremove(const char *path, int tnum, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd); +static int procwicked(const char *path, int tnum, int rnum, int opts, int omode); +static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, + int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int omode, int rratio); +static void *threadwrite(void *targ); +static void *threadread(void *targ); +static void *threadremove(void *targ); +static void *threadwicked(void *targ); +static void *threadtypical(void *targ); + +/* main routine */ +int main(int argc, char **argv) { + g_progname = argv[0]; + const char *ebuf = getenv("TCRNDSEED"); + g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; + srand(g_randseed); + ebuf = getenv("TCDBGFD"); + if (ebuf) { + int debugfd = tcatoix(ebuf); +#ifdef _WIN32 + g_dbgfd = (HANDLE) _get_osfhandle(debugfd); +#else + g_dbgfd = debugfd; +#endif + } + if (argc < 2) usage(); + int rv = 0; + if (!strcmp(argv[1], "write")) { + rv = runwrite(argc, argv); + } else if (!strcmp(argv[1], "read")) { + rv = runread(argc, argv); + } else if (!strcmp(argv[1], "remove")) { + rv = runremove(argc, argv); + } else if (!strcmp(argv[1], "wicked")) { + rv = runwicked(argc, argv); + } else if (!strcmp(argv[1], "typical")) { + rv = runtypical(argc, argv); + } else { + usage(); + } + if (rv != 0) { + printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int) getpid()); + for (int i = 0; i < argc; i++) { + printf(" %s", argv[i]); + } + printf("\n\n"); + } + return rv; +} + +/* print the usage and exit */ +static void usage(void) { + fprintf(stderr, "%s: test cases of the table database API of Tokyo Cabinet\n", g_progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num]" + " [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-rnd]" + " path tnum rnum [bnum [apow [fpow]]]\n", g_progname); + fprintf(stderr, " %s read [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd]" + " path tnum\n", g_progname); + fprintf(stderr, " %s remove [-rc num] [-lc num] [-nc num] [-xm num] [-df num]" + " [-nl|-nb] [-rnd] path tnum\n", g_progname); + fprintf(stderr, " %s wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path tnum rnum\n", g_progname); + fprintf(stderr, " %s typical [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num]" + " [-xm num] [-df num] [-nl|-nb] [-rr num] path tnum rnum [bnum [apow [fpow]]]\n", + g_progname); + fprintf(stderr, "\n"); + exit(1); +} + +/* print formatted information string and flush the buffer */ +static void iprintf(const char *format, ...) { + va_list ap; + va_start(ap, format); + vprintf(format, ap); + fflush(stdout); + va_end(ap); +} + +/* print a character and flush the buffer */ +static void iputchar(int c) { + putchar(c); + fflush(stdout); +} + +/* print error message of table database */ +static void eprint(TCTDB *tdb, int line, const char *func) { + const char *path = tctdbpath(tdb); + int ecode = tctdbecode(tdb); + fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", + g_progname, path ? path : "-", line, func, ecode, tctdberrmsg(ecode)); +} + +/* print system information */ +static void sysprint(void) { + TCMAP *info = tcsysinfo(); + if (info) { + tcmapiterinit(info); + const char *kbuf; + while ((kbuf = tcmapiternext2(info)) != NULL) { + iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); + } + tcmapdel(info); + } +} + +/* get a random number */ +static int myrand(int range) { + if (range < 2) return 0; + int high = (unsigned int) rand() >> 4; + int low = range * (rand() / (RAND_MAX + 1.0)); + low &= (unsigned int) INT_MAX >> 4; + return (high + low) % range; +} + +/* get a random number based on normal distribution */ +static int myrandnd(int range) { + int num = (int) tcdrandnd(range >> 1, range / 10); + return (num < 0 || num >= range) ? 0 : num; +} + +/* parse arguments of write command */ +static int runwrite(int argc, char **argv) { + char *path = NULL; + char *tstr = NULL; + char *rstr = NULL; + char *bstr = NULL; + char *astr = NULL; + char *fstr = NULL; + int opts = 0; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int iflags = 0; + int omode = 0; + bool rnd = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-tl")) { + opts |= TDBTLARGE; + } else if (!strcmp(argv[i], "-td")) { + opts |= TDBTDEFLATE; + } else if (!strcmp(argv[i], "-tb")) { + opts |= TDBTBZIP; + } else if (!strcmp(argv[i], "-tt")) { + opts |= TDBTTCBS; + } else if (!strcmp(argv[i], "-tx")) { + opts |= TDBTEXCODEC; + } else if (!strcmp(argv[i], "-rc")) { + if (++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-lc")) { + if (++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nc")) { + if (++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-xm")) { + if (++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-df")) { + if (++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-ip")) { + iflags |= 1 << 0; + } else if (!strcmp(argv[i], "-is")) { + iflags |= 1 << 1; + } else if (!strcmp(argv[i], "-in")) { + iflags |= 1 << 2; + } else if (!strcmp(argv[i], "-it")) { + iflags |= 1 << 3; + } else if (!strcmp(argv[i], "-if")) { + iflags |= 1 << 4; + } else if (!strcmp(argv[i], "-ix")) { + iflags |= 1 << 5; + } else if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-rnd")) { + rnd = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!tstr) { + tstr = argv[i]; + } else if (!rstr) { + rstr = argv[i]; + } else if (!bstr) { + bstr = argv[i]; + } else if (!astr) { + astr = argv[i]; + } else if (!fstr) { + fstr = argv[i]; + } else { + usage(); + } + } + if (!path || !tstr || !rstr) usage(); + int tnum = tcatoix(tstr); + int rnum = tcatoix(rstr); + if (tnum < 1 || rnum < 1) usage(); + int bnum = bstr ? tcatoix(bstr) : -1; + int apow = astr ? tcatoix(astr) : -1; + int fpow = fstr ? tcatoix(fstr) : -1; + int rv = procwrite(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, + xmsiz, dfunit, iflags, omode, rnd); + return rv; +} + +/* parse arguments of read command */ +static int runread(int argc, char **argv) { + char *path = NULL; + char *tstr = NULL; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int omode = 0; + bool rnd = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-rc")) { + if (++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-lc")) { + if (++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nc")) { + if (++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-xm")) { + if (++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-df")) { + if (++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-rnd")) { + rnd = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!tstr) { + tstr = argv[i]; + } else { + usage(); + } + } + if (!path || !tstr) usage(); + int tnum = tcatoix(tstr); + if (tnum < 1) usage(); + int rv = procread(path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + return rv; +} + +/* parse arguments of remove command */ +static int runremove(int argc, char **argv) { + char *path = NULL; + char *tstr = NULL; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int omode = 0; + bool rnd = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-rc")) { + if (++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-lc")) { + if (++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nc")) { + if (++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-xm")) { + if (++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-df")) { + if (++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-rnd")) { + rnd = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!tstr) { + tstr = argv[i]; + } else { + usage(); + } + } + if (!path || !tstr) usage(); + int tnum = tcatoix(tstr); + if (tnum < 1) usage(); + int rv = procremove(path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + return rv; +} + +/* parse arguments of wicked command */ +static int runwicked(int argc, char **argv) { + char *path = NULL; + char *tstr = NULL; + char *rstr = NULL; + int opts = 0; + int omode = 0; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-tl")) { + opts |= TDBTLARGE; + } else if (!strcmp(argv[i], "-td")) { + opts |= TDBTDEFLATE; + } else if (!strcmp(argv[i], "-tb")) { + opts |= TDBTBZIP; + } else if (!strcmp(argv[i], "-tt")) { + opts |= TDBTTCBS; + } else if (!strcmp(argv[i], "-tx")) { + opts |= TDBTEXCODEC; + } else if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!tstr) { + tstr = argv[i]; + } else if (!rstr) { + rstr = argv[i]; + } else { + usage(); + } + } + if (!path || !tstr || !rstr) usage(); + int tnum = tcatoix(tstr); + int rnum = tcatoix(rstr); + if (tnum < 1 || rnum < 1) usage(); + int rv = procwicked(path, tnum, rnum, opts, omode); + return rv; +} + +/* parse arguments of typical command */ +static int runtypical(int argc, char **argv) { + char *path = NULL; + char *tstr = NULL; + char *rstr = NULL; + char *bstr = NULL; + char *astr = NULL; + char *fstr = NULL; + int opts = 0; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int omode = 0; + int rratio = -1; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-tl")) { + opts |= TDBTLARGE; + } else if (!strcmp(argv[i], "-td")) { + opts |= TDBTDEFLATE; + } else if (!strcmp(argv[i], "-tb")) { + opts |= TDBTBZIP; + } else if (!strcmp(argv[i], "-tt")) { + opts |= TDBTTCBS; + } else if (!strcmp(argv[i], "-tx")) { + opts |= TDBTEXCODEC; + } else if (!strcmp(argv[i], "-rc")) { + if (++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-lc")) { + if (++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nc")) { + if (++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-xm")) { + if (++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-df")) { + if (++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-rr")) { + if (++i >= argc) usage(); + rratio = tcatoix(argv[i]); + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!tstr) { + tstr = argv[i]; + } else if (!rstr) { + rstr = argv[i]; + } else if (!bstr) { + bstr = argv[i]; + } else if (!astr) { + astr = argv[i]; + } else if (!fstr) { + fstr = argv[i]; + } else { + usage(); + } + } + if (!path || !tstr || !rstr) usage(); + int tnum = tcatoix(tstr); + int rnum = tcatoix(rstr); + if (tnum < 1 || rnum < 1) usage(); + int bnum = bstr ? tcatoix(bstr) : -1; + int apow = astr ? tcatoix(astr) : -1; + int fpow = fstr ? tcatoix(fstr) : -1; + int rv = proctypical(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, + xmsiz, dfunit, omode, rratio); + return rv; +} + +/* perform write command */ +static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, + int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int iflags, int omode, bool rnd) { + iprintf("<Writing Test>\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d fpow=%d" + " opts=%d rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d iflags=%d" + " omode=%d rnd=%d\n\n", + g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, + xmsiz, dfunit, iflags, omode, rnd); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetmutex(tdb)) { + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) { + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if (!tctdbtune(tdb, bnum, apow, fpow, opts)) { + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if (!tctdbsetcache(tdb, rcnum, lcnum, ncnum)) { + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if (xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)) { + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if (dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)) { + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if (!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)) { + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if ((iflags & (1 << 0)) && !tctdbsetindex(tdb, "", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if ((iflags & (1 << 1)) && !tctdbsetindex(tdb, "str", TDBITLEXICAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if ((iflags & (1 << 2)) && !tctdbsetindex(tdb, "num", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if ((iflags & (1 << 3)) && !tctdbsetindex(tdb, "type", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if ((iflags & (1 << 4)) && !tctdbsetindex(tdb, "flag", TDBITTOKEN)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if ((iflags & (1 << 5)) && !tctdbsetindex(tdb, "text", TDBITQGRAM)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + TARGWRITE targs[tnum]; + pthread_t threads[tnum]; + if (tnum == 1) { + targs[0].tdb = tdb; + targs[0].rnum = rnum; + targs[0].rnd = rnd; + targs[0].id = 0; + if (threadwrite(targs) != NULL) err = true; + } else { + for (int i = 0; i < tnum; i++) { + targs[i].tdb = tdb; + targs[i].rnum = rnum; + targs[i].rnd = rnd; + targs[i].id = i; + if (pthread_create(threads + i, NULL, threadwrite, targs + i) != 0) { + eprint(tdb, __LINE__, "pthread_create"); + targs[i].id = -1; + err = true; + } + } + for (int i = 0; i < tnum; i++) { + if (targs[i].id == -1) continue; + void *rv; + if (pthread_join(threads[i], &rv) != 0) { + eprint(tdb, __LINE__, "pthread_join"); + err = true; + } else if (rv) { + err = true; + } + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t) tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t) tctdbfsiz(tdb)); + sysprint(); + if (!tctdbclose(tdb)) { + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + +/* perform read command */ +static int procread(const char *path, int tnum, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd) { + iprintf("<Reading Test>\n seed=%u path=%s tnum=%d rcnum=%d lcnum=%d ncnum=%d" + " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", + g_randseed, path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetmutex(tdb)) { + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) { + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if (!tctdbsetcache(tdb, rcnum, lcnum, ncnum)) { + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if (xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)) { + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if (dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)) { + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if (!tctdbopen(tdb, path, TDBOREADER | omode)) { + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + int rnum = tctdbrnum(tdb) / tnum; + TARGREAD targs[tnum]; + pthread_t threads[tnum]; + if (tnum == 1) { + targs[0].tdb = tdb; + targs[0].rnum = rnum; + targs[0].rnd = rnd; + targs[0].id = 0; + if (threadread(targs) != NULL) err = true; + } else { + for (int i = 0; i < tnum; i++) { + targs[i].tdb = tdb; + targs[i].rnum = rnum; + targs[i].rnd = rnd; + targs[i].id = i; + if (pthread_create(threads + i, NULL, threadread, targs + i) != 0) { + eprint(tdb, __LINE__, "pthread_create"); + targs[i].id = -1; + err = true; + } + } + for (int i = 0; i < tnum; i++) { + if (targs[i].id == -1) continue; + void *rv; + if (pthread_join(threads[i], &rv) != 0) { + eprint(tdb, __LINE__, "pthread_join"); + err = true; + } else if (rv) { + err = true; + } + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t) tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t) tctdbfsiz(tdb)); + sysprint(); + if (!tctdbclose(tdb)) { + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + +/* perform remove command */ +static int procremove(const char *path, int tnum, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd) { + iprintf("<Removing Test>\n seed=%u path=%s tnum=%d rcnum=%d lcnum=%d ncnum=%d" + " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", + g_randseed, path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetmutex(tdb)) { + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) { + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if (!tctdbsetcache(tdb, rcnum, lcnum, ncnum)) { + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if (xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)) { + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if (dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)) { + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if (!tctdbopen(tdb, path, TDBOWRITER | omode)) { + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + int rnum = tctdbrnum(tdb) / tnum; + TARGREMOVE targs[tnum]; + pthread_t threads[tnum]; + if (tnum == 1) { + targs[0].tdb = tdb; + targs[0].rnum = rnum; + targs[0].rnd = rnd; + targs[0].id = 0; + if (threadremove(targs) != NULL) err = true; + } else { + for (int i = 0; i < tnum; i++) { + targs[i].tdb = tdb; + targs[i].rnum = rnum; + targs[i].rnd = rnd; + targs[i].id = i; + if (pthread_create(threads + i, NULL, threadremove, targs + i) != 0) { + eprint(tdb, __LINE__, "pthread_create"); + targs[i].id = -1; + err = true; + } + } + for (int i = 0; i < tnum; i++) { + if (targs[i].id == -1) continue; + void *rv; + if (pthread_join(threads[i], &rv) != 0) { + eprint(tdb, __LINE__, "pthread_join"); + err = true; + } else if (rv) { + err = true; + } + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t) tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t) tctdbfsiz(tdb)); + sysprint(); + if (!tctdbclose(tdb)) { + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + +/* perform wicked command */ +static int procwicked(const char *path, int tnum, int rnum, int opts, int omode) { + iprintf("<Writing Test>\n seed=%u path=%s tnum=%d rnum=%d opts=%d omode=%d\n\n", + g_randseed, path, tnum, rnum, opts, omode); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetmutex(tdb)) { + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) { + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if (!tctdbtune(tdb, rnum / 50, 2, -1, opts)) { + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if (!tctdbsetcache(tdb, rnum / 10, 128, 256)) { + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if (!tctdbsetxmsiz(tdb, rnum * sizeof (int))) { + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if (!tctdbsetdfunit(tdb, 8)) { + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if (!tctdbsetinvcache(tdb, -1, 0.5)) { + eprint(tdb, __LINE__, "tctdbsetinvcache"); + err = true; + } + if (!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)) { + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if (!tctdbsetindex(tdb, "", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "str", TDBITLEXICAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "num", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "type", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "flag", TDBITTOKEN)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "text", TDBITQGRAM)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbiterinit(tdb)) { + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + TARGWICKED targs[tnum]; + pthread_t threads[tnum]; + if (tnum == 1) { + targs[0].tdb = tdb; + targs[0].rnum = rnum; + targs[0].id = 0; + if (threadwicked(targs) != NULL) err = true; + } else { + for (int i = 0; i < tnum; i++) { + targs[i].tdb = tdb; + targs[i].rnum = rnum; + targs[i].id = i; + if (pthread_create(threads + i, NULL, threadwicked, targs + i) != 0) { + eprint(tdb, __LINE__, "pthread_create"); + targs[i].id = -1; + err = true; + } + } + for (int i = 0; i < tnum; i++) { + if (targs[i].id == -1) continue; + void *rv; + if (pthread_join(threads[i], &rv) != 0) { + eprint(tdb, __LINE__, "pthread_join"); + err = true; + } else if (rv) { + err = true; + } + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t) tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t) tctdbfsiz(tdb)); + sysprint(); + if (!tctdbclose(tdb)) { + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + +/* perform typical command */ +static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, + int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int omode, int rratio) { + iprintf("<Typical Access Test>\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d" + " fpow=%d opts=%d rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d" + " omode=%d rratio=%d\n\n", + g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, + xmsiz, dfunit, omode, rratio); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetmutex(tdb)) { + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) { + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if (!tctdbtune(tdb, bnum, apow, fpow, opts)) { + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if (!tctdbsetcache(tdb, rcnum, lcnum, ncnum)) { + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if (xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)) { + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if (dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)) { + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if (!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)) { + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if (!tctdbsetindex(tdb, "", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "str", TDBITLEXICAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "num", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "type", TDBITDECIMAL)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "flag", TDBITTOKEN)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if (!tctdbsetindex(tdb, "text", TDBITQGRAM)) { + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + TARGTYPICAL targs[tnum]; + pthread_t threads[tnum]; + if (tnum == 1) { + targs[0].tdb = tdb; + targs[0].rnum = rnum; + targs[0].rratio = rratio; + targs[0].id = 0; + if (threadtypical(targs) != NULL) err = true; + } else { + for (int i = 0; i < tnum; i++) { + targs[i].tdb = tdb; + targs[i].rnum = rnum; + targs[i].rratio = rratio; + targs[i].id = i; + if (pthread_create(threads + i, NULL, threadtypical, targs + i) != 0) { + eprint(tdb, __LINE__, "pthread_create"); + targs[i].id = -1; + err = true; + } + } + for (int i = 0; i < tnum; i++) { + if (targs[i].id == -1) continue; + void *rv; + if (pthread_join(threads[i], &rv) != 0) { + eprint(tdb, __LINE__, "pthread_join"); + err = true; + } else if (rv) { + err = true; + } + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t) tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t) tctdbfsiz(tdb)); + sysprint(); + if (!tctdbclose(tdb)) { + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + +/* thread the write function */ +static void *threadwrite(void *targ) { + TCTDB *tdb = ((TARGWRITE *) targ)->tdb; + int rnum = ((TARGWRITE *) targ)->rnum; + bool rnd = ((TARGWRITE *) targ)->rnd; + int id = ((TARGWRITE *) targ)->id; + bool err = false; + int base = id * rnum; + for (int i = 1; i <= rnum; i++) { + int uid = rnd ? (base + myrand(i) + 1) : tctdbgenuid(tdb); + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", uid); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ * 5]; + int vsiz = sprintf(vbuf, "%d", uid); + tcmapput(cols, "str", 3, vbuf, vsiz); + if (myrand(3) == 0) { + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for (int j = 0; j < num; j++) { + pt += myrand(5) + 1; + if (wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if (*vbuf != '\0') { + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + if (!tctdbput(tdb, pkbuf, pksiz, cols)) { + eprint(tdb, __LINE__, "tctdbput"); + err = true; + break; + } + tcmapdel(cols); + if (id == 0 && rnum > 250 && i % (rnum / 250) == 0) { + iputchar('.'); + if (i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + return err ? "error" : NULL; +} + +/* thread the read function */ +static void *threadread(void *targ) { + TCTDB *tdb = ((TARGREAD *) targ)->tdb; + int rnum = ((TARGREAD *) targ)->rnum; + bool rnd = ((TARGREAD *) targ)->rnd; + int id = ((TARGREAD *) targ)->id; + bool err = false; + int base = id * rnum; + for (int i = 1; i <= rnum && !err; i++) { + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", base + (rnd ? myrandnd(i) : i)); + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if (cols) { + tcmapdel(cols); + } else if (!rnd || tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbget"); + err = true; + } + if (id == 0 && rnum > 250 && i % (rnum / 250) == 0) { + iputchar('.'); + if (i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + return err ? "error" : NULL; +} + +/* thread the remove function */ +static void *threadremove(void *targ) { + TCTDB *tdb = ((TARGREMOVE *) targ)->tdb; + int rnum = ((TARGREMOVE *) targ)->rnum; + bool rnd = ((TARGREMOVE *) targ)->rnd; + int id = ((TARGREMOVE *) targ)->id; + bool err = false; + int base = id * rnum; + for (int i = 1; i <= rnum; i++) { + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", base + (rnd ? myrand(i + 1) : i)); + if (!tctdbout(tdb, pkbuf, pksiz) && (!rnd || tctdbecode(tdb) != TCENOREC)) { + eprint(tdb, __LINE__, "tctdbout"); + err = true; + break; + } + if (id == 0 && rnum > 250 && i % (rnum / 250) == 0) { + iputchar('.'); + if (i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + return err ? "error" : NULL; +} + +/* thread the wicked function */ +static void *threadwicked(void *targ) { + TCTDB *tdb = ((TARGWICKED *) targ)->tdb; + int rnum = ((TARGWICKED *) targ)->rnum; + int id = ((TARGWICKED *) targ)->id; + bool err = false; + const char *names[] = {"", "str", "num", "type", "flag", "c1"}; + int ops[] = {TDBQCSTREQ, TDBQCSTRINC, TDBQCSTRBW, TDBQCSTREW, TDBQCSTRAND, TDBQCSTROR, + TDBQCSTROREQ, TDBQCSTRRX, TDBQCNUMEQ, TDBQCNUMGT, TDBQCNUMGE, TDBQCNUMLT, + TDBQCNUMLE, TDBQCNUMBT, TDBQCNUMOREQ}; + int ftsops[] = {TDBQCFTSPH, TDBQCFTSAND, TDBQCFTSOR, TDBQCFTSEX}; + int types[] = {TDBQOSTRASC, TDBQOSTRDESC, TDBQONUMASC, TDBQONUMDESC}; + for (int i = 1; i <= rnum && !err; i++) { + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", myrand(rnum * (id + 1))); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ * 5]; + int vsiz = sprintf(vbuf, "%d", id); + tcmapput(cols, "str", 3, vbuf, vsiz); + if (myrand(3) == 0) { + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for (int j = 0; j < num; j++) { + pt += myrand(5) + 1; + if (wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if (*vbuf != '\0') { + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + char nbuf[RECBUFSIZ]; + int nsiz = sprintf(nbuf, "c%d", myrand(i) + 1); + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + tcmapput(cols, nbuf, nsiz, vbuf, vsiz); + char *cbuf; + int csiz; + TCMAP *ncols; + TDBQRY *qry; + switch (myrand(17)) { + case 0: + if (id == 0) iputchar('0'); + if (!tctdbput(tdb, pkbuf, pksiz, cols)) { + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + break; + case 1: + if (id == 0) iputchar('1'); + cbuf = tcstrjoin4(cols, &csiz); + if (!tctdbput2(tdb, pkbuf, pksiz, cbuf, csiz)) { + eprint(tdb, __LINE__, "tctdbput2"); + err = true; + } + tcfree(cbuf); + break; + case 2: + if (id == 0) iputchar('2'); + cbuf = tcstrjoin3(cols, '\t'); + if (!tctdbput3(tdb, pkbuf, cbuf)) { + eprint(tdb, __LINE__, "tctdbput3"); + err = true; + } + tcfree(cbuf); + break; + case 3: + if (id == 0) iputchar('3'); + if (!tctdbputkeep(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) != TCEKEEP) { + eprint(tdb, __LINE__, "tctdbputkeep"); + err = true; + } + break; + case 4: + if (id == 0) iputchar('4'); + cbuf = tcstrjoin4(cols, &csiz); + if (!tctdbputkeep2(tdb, pkbuf, pksiz, cbuf, csiz) && tctdbecode(tdb) != TCEKEEP) { + eprint(tdb, __LINE__, "tctdbputkeep2"); + err = true; + } + tcfree(cbuf); + break; + case 5: + if (id == 0) iputchar('5'); + cbuf = tcstrjoin3(cols, '\t'); + if (!tctdbputkeep3(tdb, pkbuf, cbuf) && tctdbecode(tdb) != TCEKEEP) { + eprint(tdb, __LINE__, "tctdbputkeep3"); + err = true; + } + tcfree(cbuf); + break; + case 6: + if (id == 0) iputchar('6'); + if (!tctdbputcat(tdb, pkbuf, pksiz, cols)) { + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + } + break; + case 7: + if (id == 0) iputchar('7'); + cbuf = tcstrjoin4(cols, &csiz); + if (!tctdbputcat2(tdb, pkbuf, pksiz, cbuf, csiz)) { + eprint(tdb, __LINE__, "tctdbputcat2"); + err = true; + } + tcfree(cbuf); + break; + case 8: + if (id == 0) iputchar('8'); + cbuf = tcstrjoin3(cols, '\t'); + if (!tctdbputcat3(tdb, pkbuf, cbuf)) { + eprint(tdb, __LINE__, "tctdbputcat3"); + err = true; + } + tcfree(cbuf); + break; + case 9: + if (id == 0) iputchar('9'); + if (myrand(2) == 0) { + if (!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + } + break; + case 10: + if (id == 0) iputchar('A'); + if (myrand(2) == 0) { + if (!tctdbout2(tdb, pkbuf) && tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbout2"); + err = true; + } + } + break; + case 11: + if (id == 0) iputchar('B'); + ncols = tctdbget(tdb, pkbuf, pksiz); + if (ncols) { + tcmapdel(ncols); + } else if (tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbget"); + err = true; + } + break; + case 12: + if (id == 0) iputchar('C'); + cbuf = tctdbget2(tdb, pkbuf, pksiz, &csiz); + if (cbuf) { + tcfree(cbuf); + } else if (tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbget2"); + err = true; + } + break; + case 13: + if (id == 0) iputchar('D'); + cbuf = tctdbget3(tdb, pkbuf); + if (cbuf) { + tcfree(cbuf); + } else if (tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbget3"); + err = true; + } + break; + case 14: + if (id == 0) iputchar('E'); + if (myrand(rnum / 50) == 0) { + if (!tctdbiterinit(tdb)) { + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + } + for (int j = myrand(rnum) / 1000 + 1; j >= 0; j--) { + int iksiz; + char *ikbuf = tctdbiternext(tdb, &iksiz); + if (ikbuf) { + tcfree(ikbuf); + } else { + int ecode = tctdbecode(tdb); + if (ecode != TCEINVALID && ecode != TCENOREC) { + eprint(tdb, __LINE__, "tctdbiternext"); + err = true; + } + } + } + break; + case 15: + if (id == 0) iputchar('F'); + qry = tctdbqrynew(tdb); + if (myrand(10) != 0) { + char expr[RECBUFSIZ]; + sprintf(expr, "%d", myrand(i) + 1); + switch (myrand(6)) { + default: + tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); + break; + case 1: + tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); + break; + case 2: + tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); + break; + case 3: + tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); + break; + case 4: + tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); + break; + case 5: + tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); + break; + } + switch (myrand(5)) { + case 0: + tctdbqrysetorder(qry, "str", TDBQOSTRASC); + break; + case 1: + tctdbqrysetorder(qry, "str", TDBQOSTRDESC); + break; + case 2: + tctdbqrysetorder(qry, "num", TDBQONUMASC); + break; + case 3: + tctdbqrysetorder(qry, "num", TDBQONUMDESC); + break; + } + tctdbqrysetlimit(qry, 10, myrand(10)); + } else { + int cnum = myrand(4); + if (cnum < 1 && myrand(5) != 0) cnum = 1; + for (int j = 0; j < cnum; j++) { + const char *name = names[myrand(sizeof (names) / sizeof (*names))]; + int op = ops[myrand(sizeof (ops) / sizeof (*ops))]; + if (myrand(10) == 0) op = ftsops[myrand(sizeof (ftsops) / sizeof (*ftsops))]; + if (myrand(20) == 0) op |= TDBQCNEGATE; + if (myrand(20) == 0) op |= TDBQCNOIDX; + char expr[RECBUFSIZ * 3]; + char *wp = expr; + if (myrand(3) == 0) { + wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); + } else { + wp += sprintf(expr, "%d", myrand(i)); + } + if (myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + if (myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + tctdbqryaddcond(qry, name, op, expr); + } + if (myrand(3) != 0) { + const char *name = names[myrand(sizeof (names) / sizeof (*names))]; + int type = types[myrand(sizeof (types) / sizeof (*types))]; + tctdbqrysetorder(qry, name, type); + } + if (myrand(3) != 0) tctdbqrysetlimit(qry, myrand(i), myrand(10)); + } + if (myrand(10) == 0) { + TCLIST *res = tctdbqrysearch(qry); + tclistdel(res); + } + tctdbqrydel(qry); + break; + default: + if (id == 0) iputchar('@'); + if (tctdbtranbegin(tdb)) { + if (myrand(2) == 0) { + if (!tctdbput(tdb, pkbuf, pksiz, cols)) { + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + } else { + if (!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + } + if (myrand(2) == 0) { + if (!tctdbtranabort(tdb)) { + eprint(tdb, __LINE__, "tctdbtranabort"); + err = true; + } + } else { + if (!tctdbtrancommit(tdb)) { + eprint(tdb, __LINE__, "tctdbtrancommit"); + err = true; + } + } + } else { + eprint(tdb, __LINE__, "tctdbtranbegin"); + err = true; + } + if (myrand(10000) == 0) srand((unsigned int) (tctime() * 1000) % UINT_MAX); + break; + } + tcmapdel(cols); + if (id == 0) { + if (i % 50 == 0) iprintf(" (%08d)\n", i); + if (id == 0 && i == rnum / 4) { + if (!tctdboptimize(tdb, rnum / 50, -1, -1, -1) && tctdbecode(tdb) != TCEINVALID) { + eprint(tdb, __LINE__, "tctdboptimize"); + err = true; + } + if (!tctdbiterinit(tdb)) { + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + } + } + } + return err ? "error" : NULL; +} + +/* thread the typical function */ +static void *threadtypical(void *targ) { + TCTDB *tdb = ((TARGTYPICAL *) targ)->tdb; + int rnum = ((TARGTYPICAL *) targ)->rnum; + int rratio = ((TARGTYPICAL *) targ)->rratio; + int id = ((TARGTYPICAL *) targ)->id; + bool err = false; + int base = id * rnum; + int mrange = tclmax(50 + rratio, 100); + for (int i = 1; !err && i <= rnum; i++) { + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%08d", base + myrandnd(i)); + int rnd = myrand(mrange); + if (rnd < 20) { + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ * 5]; + int vsiz = sprintf(vbuf, "%d", id); + tcmapput(cols, "str", 3, vbuf, vsiz); + if (myrand(3) == 0) { + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for (int j = 0; j < num; j++) { + pt += myrand(5) + 1; + if (wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if (*vbuf != '\0') { + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + char nbuf[RECBUFSIZ]; + int nsiz = sprintf(nbuf, "c%d", myrand(i) + 1); + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + tcmapput(cols, nbuf, nsiz, vbuf, vsiz); + if (myrand(2) == 0) { + if (!tctdbput(tdb, pkbuf, pksiz, cols)) { + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + } else { + if (!tctdbput(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) && tctdbecode(tdb) != TCEKEEP) { + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + } + tcmapdel(cols); + } else if (rnd < 30) { + if (!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) && tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + } else if (rnd < 31) { + if (myrand(10) == 0 && !tctdbiterinit(tdb) && tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + for (int j = 0; !err && j < 10; j++) { + int ksiz; + char *kbuf = tctdbiternext(tdb, &ksiz); + if (kbuf) { + tcfree(kbuf); + } else if (tctdbecode(tdb) != TCEINVALID && tctdbecode(tdb) != TCENOREC) { + eprint(tdb, __LINE__, "tctdbiternext"); + err = true; + } + } + } else { + TDBQRY *qry = tctdbqrynew(tdb); + char expr[RECBUFSIZ]; + sprintf(expr, "%d", myrand(i) + 1); + switch (myrand(6) * 1) { + default: + tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); + break; + case 1: + tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); + break; + case 2: + tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); + break; + case 3: + tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); + break; + case 4: + tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); + break; + case 5: + tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); + break; + } + tctdbqrysetlimit(qry, 10, myrand(5) * 10); + TCLIST *res = tctdbqrysearch(qry); + tclistdel(res); + tctdbqrydel(qry); + } + if (id == 0 && rnum > 250 && i % (rnum / 250) == 0) { + iputchar('.'); + if (i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + return err ? "error" : NULL; +} + + + +// END OF FILE diff --git a/tcejdb/src/tctdb/tests/tcttest.c b/tcejdb/src/tctdb/tests/tcttest.c new file mode 100644 index 0000000..6662e7a --- /dev/null +++ b/tcejdb/src/tctdb/tests/tcttest.c @@ -0,0 +1,2069 @@ +/************************************************************************************************* + * The test cases of the table database API + * Copyright (C) 2006-2012 FAL Labs + * This file is part of Tokyo Cabinet. + * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of + * the GNU Lesser General Public License as published by the Free Software Foundation; either + * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * You should have received a copy of the GNU Lesser General Public License along with Tokyo + * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA. + *************************************************************************************************/ + + +#include <tcutil.h> +#include <tctdb.h> +#include "myconf.h" + +#define RECBUFSIZ 48 // buffer for records + + +/* global variables */ +const char *g_progname; // program name +unsigned int g_randseed; // random seed +HANDLE g_dbgfd = INVALID_HANDLE_VALUE; // debugging output + + +/* function prototypes */ +int main(int argc, char **argv); +static void usage(void); +static void iprintf(const char *format, ...); +static void iputchar(int c); +static void eprint(TCTDB *tdb, int line, const char *func); +static void sysprint(void); +static int myrand(int range); +static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op); +static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); +static int runwrite(int argc, char **argv); +static int runread(int argc, char **argv); +static int runremove(int argc, char **argv); +static int runrcat(int argc, char **argv); +static int runmisc(int argc, char **argv); +static int runwicked(int argc, char **argv); +static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow, + bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int iflags, int omode, bool rnd); +static int procread(const char *path, bool mt, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd); +static int procremove(const char *path, bool mt, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd); +static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow, + bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int iflags, int omode, int pnum, bool dai, bool dad, bool rl, bool ru); +static int procmisc(const char *path, int rnum, bool mt, int opts, int omode); +static int procwicked(const char *path, int rnum, bool mt, int opts, int omode); + + +/* main routine */ +int main(int argc, char **argv){ + g_progname = argv[0]; + const char *ebuf = getenv("TCRNDSEED"); + g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; + srand(g_randseed); + ebuf = getenv("TCDBGFD"); + if (ebuf) { + int debugfd = tcatoix(ebuf); +#ifdef _WIN32 + g_dbgfd = (HANDLE) _get_osfhandle(debugfd); +#else + g_dbgfd = debugfd; +#endif + } + if(argc < 2) usage(); + int rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else if(!strcmp(argv[1], "remove")){ + rv = runremove(argc, argv); + } else if(!strcmp(argv[1], "rcat")){ + rv = runrcat(argc, argv); + } else if(!strcmp(argv[1], "misc")){ + rv = runmisc(argc, argv); + } else if(!strcmp(argv[1], "wicked")){ + rv = runwicked(argc, argv); + } else { + usage(); + } + if(rv != 0){ + printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); + for(int i = 0; i < argc; i++){ + printf(" %s", argv[i]); + } + printf("\n\n"); + } + return rv; +} + + +/* print the usage and exit */ +static void usage(void){ + fprintf(stderr, "%s: test cases of the table database API of Tokyo Cabinet\n", g_progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num]" + " [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-rnd]" + " path rnum [bnum [apow [fpow]]]\n", g_progname); + fprintf(stderr, " %s read [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num]" + " [-nl|-nb] [-rnd] path\n", g_progname); + fprintf(stderr, " %s remove [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num]" + " [-nl|-nb] [-rnd] path\n", g_progname); + fprintf(stderr, " %s rcat [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num]" + " [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-pn num]" + " [-dai|-dad|-rl|-ru] path rnum [bnum [apow [fpow]]]\n", g_progname); + fprintf(stderr, " %s misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum\n", g_progname); + fprintf(stderr, " %s wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum\n", g_progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* print formatted information string and flush the buffer */ +static void iprintf(const char *format, ...){ + va_list ap; + va_start(ap, format); + vprintf(format, ap); + fflush(stdout); + va_end(ap); +} + + +/* print a character and flush the buffer */ +static void iputchar(int c){ + putchar(c); + fflush(stdout); +} + + +/* print error message of table database */ +static void eprint(TCTDB *tdb, int line, const char *func){ + const char *path = tctdbpath(tdb); + int ecode = tctdbecode(tdb); + fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", + g_progname, path ? path : "-", line, func, ecode, tctdberrmsg(ecode)); +} + + +/* print system information */ +static void sysprint(void){ + TCMAP *info = tcsysinfo(); + if(info){ + tcmapiterinit(info); + const char *kbuf; + while((kbuf = tcmapiternext2(info)) != NULL){ + iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); + } + tcmapdel(info); + } +} + + +/* get a random number */ +static int myrand(int range){ + if(range < 2) return 0; + int high = (unsigned int)rand() >> 4; + int low = range * (rand() / (RAND_MAX + 1.0)); + low &= (unsigned int)INT_MAX >> 4; + return (high + low) % range; +} + + +/* duplication callback function */ +static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op){ + if(myrand(4) == 0) return (void *)-1; + if(myrand(2) == 0) return NULL; + int len = myrand(RECBUFSIZ - 5); + char buf[RECBUFSIZ]; + memcpy(buf, "proc", 5); + memset(buf + 5, '*', len); + *sp = len + 5; + return tcmemdup(buf, len + 5); +} + + +/* iterator function */ +static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ + unsigned int sum = 0; + while(--ksiz >= 0){ + sum += ((char *)kbuf)[ksiz]; + } + while(--vsiz >= 0){ + sum += ((char *)vbuf)[vsiz]; + } + return myrand(100 + (sum & 0xff)) > 0; +} + + +/* parse arguments of write command */ +static int runwrite(int argc, char **argv){ + char *path = NULL; + char *rstr = NULL; + char *bstr = NULL; + char *astr = NULL; + char *fstr = NULL; + bool mt = false; + int opts = 0; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int iflags = 0; + int omode = 0; + bool rnd = false; + for(int i = 2; i < argc; i++){ + if(!path && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-mt")){ + mt = true; + } else if(!strcmp(argv[i], "-tl")){ + opts |= TDBTLARGE; + } else if(!strcmp(argv[i], "-td")){ + opts |= TDBTDEFLATE; + } else if(!strcmp(argv[i], "-tb")){ + opts |= TDBTBZIP; + } else if(!strcmp(argv[i], "-tt")){ + opts |= TDBTTCBS; + } else if(!strcmp(argv[i], "-tx")){ + opts |= TDBTEXCODEC; + } else if(!strcmp(argv[i], "-rc")){ + if(++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-lc")){ + if(++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-nc")){ + if(++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-xm")){ + if(++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-df")){ + if(++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-ip")){ + iflags |= 1 << 0; + } else if(!strcmp(argv[i], "-is")){ + iflags |= 1 << 1; + } else if(!strcmp(argv[i], "-in")){ + iflags |= 1 << 2; + } else if(!strcmp(argv[i], "-it")){ + iflags |= 1 << 3; + } else if(!strcmp(argv[i], "-if")){ + iflags |= 1 << 4; + } else if(!strcmp(argv[i], "-ix")){ + iflags |= 1 << 5; + } else if(!strcmp(argv[i], "-nl")){ + omode |= TDBONOLCK; + } else if(!strcmp(argv[i], "-nb")){ + omode |= TDBOLCKNB; + } else if(!strcmp(argv[i], "-rnd")){ + rnd = true; + } else { + usage(); + } + } else if(!path){ + path = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else if(!bstr){ + bstr = argv[i]; + } else if(!astr){ + astr = argv[i]; + } else if(!fstr){ + fstr = argv[i]; + } else { + usage(); + } + } + if(!path || !rstr) usage(); + int rnum = tcatoix(rstr); + if(rnum < 1) usage(); + int bnum = bstr ? tcatoix(bstr) : -1; + int apow = astr ? tcatoix(astr) : -1; + int fpow = fstr ? tcatoix(fstr) : -1; + int rv = procwrite(path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, + iflags, omode, rnd); + return rv; +} + + +/* parse arguments of read command */ +static int runread(int argc, char **argv){ + char *path = NULL; + bool mt = false; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int omode = 0; + bool rnd = false; + for(int i = 2; i < argc; i++){ + if(!path && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-mt")){ + mt = true; + } else if(!strcmp(argv[i], "-rc")){ + if(++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-lc")){ + if(++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-nc")){ + if(++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-xm")){ + if(++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-df")){ + if(++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-nl")){ + omode |= TDBONOLCK; + } else if(!strcmp(argv[i], "-nb")){ + omode |= TDBOLCKNB; + } else if(!strcmp(argv[i], "-rnd")){ + rnd = true; + } else { + usage(); + } + } else if(!path){ + path = argv[i]; + } else { + usage(); + } + } + if(!path) usage(); + int rv = procread(path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + return rv; +} + + +/* parse arguments of remove command */ +static int runremove(int argc, char **argv){ + char *path = NULL; + bool mt = false; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int omode = 0; + bool rnd = false; + for(int i = 2; i < argc; i++){ + if(!path && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-mt")){ + mt = true; + } else if(!strcmp(argv[i], "-rc")){ + if(++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-lc")){ + if(++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-nc")){ + if(++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-xm")){ + if(++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-df")){ + if(++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-nl")){ + omode |= TDBONOLCK; + } else if(!strcmp(argv[i], "-nb")){ + omode |= TDBOLCKNB; + } else if(!strcmp(argv[i], "-rnd")){ + rnd = true; + } else { + usage(); + } + } else if(!path){ + path = argv[i]; + } else { + usage(); + } + } + if(!path) usage(); + int rv = procremove(path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + return rv; +} + + +/* parse arguments of rcat command */ +static int runrcat(int argc, char **argv){ + char *path = NULL; + char *rstr = NULL; + char *bstr = NULL; + char *astr = NULL; + char *fstr = NULL; + bool mt = false; + int opts = 0; + int rcnum = 0; + int lcnum = 0; + int ncnum = 0; + int xmsiz = -1; + int dfunit = 0; + int iflags = 0; + int omode = 0; + int pnum = 0; + bool dai = false; + bool dad = false; + bool rl = false; + bool ru = false; + for(int i = 2; i < argc; i++){ + if(!path && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-mt")){ + mt = true; + } else if(!strcmp(argv[i], "-tl")){ + opts |= TDBTLARGE; + } else if(!strcmp(argv[i], "-td")){ + opts |= TDBTDEFLATE; + } else if(!strcmp(argv[i], "-tb")){ + opts |= TDBTBZIP; + } else if(!strcmp(argv[i], "-tt")){ + opts |= TDBTTCBS; + } else if(!strcmp(argv[i], "-tx")){ + opts |= TDBTEXCODEC; + } else if(!strcmp(argv[i], "-xm")){ + if(++i >= argc) usage(); + xmsiz = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-df")){ + if(++i >= argc) usage(); + dfunit = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-rc")){ + if(++i >= argc) usage(); + rcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-lc")){ + if(++i >= argc) usage(); + lcnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-nc")){ + if(++i >= argc) usage(); + ncnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-ip")){ + iflags |= 1 << 0; + } else if(!strcmp(argv[i], "-is")){ + iflags |= 1 << 1; + } else if(!strcmp(argv[i], "-in")){ + iflags |= 1 << 2; + } else if(!strcmp(argv[i], "-it")){ + iflags |= 1 << 3; + } else if(!strcmp(argv[i], "-if")){ + iflags |= 1 << 4; + } else if(!strcmp(argv[i], "-ix")){ + iflags |= 1 << 5; + } else if(!strcmp(argv[i], "-nl")){ + omode |= TDBONOLCK; + } else if(!strcmp(argv[i], "-nb")){ + omode |= TDBOLCKNB; + } else if(!strcmp(argv[i], "-pn")){ + if(++i >= argc) usage(); + pnum = tcatoix(argv[i]); + } else if(!strcmp(argv[i], "-dai")){ + dai = true; + } else if(!strcmp(argv[i], "-dad")){ + dad = true; + } else if(!strcmp(argv[i], "-rl")){ + rl = true; + } else if(!strcmp(argv[i], "-ru")){ + ru = true; + } else { + usage(); + } + } else if(!path){ + path = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else if(!bstr){ + bstr = argv[i]; + } else if(!astr){ + astr = argv[i]; + } else if(!fstr){ + fstr = argv[i]; + } else { + usage(); + } + } + if(!path || !rstr) usage(); + int rnum = tcatoix(rstr); + if(rnum < 1) usage(); + int bnum = bstr ? tcatoix(bstr) : -1; + int apow = astr ? tcatoix(astr) : -1; + int fpow = fstr ? tcatoix(fstr) : -1; + int rv = procrcat(path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, + iflags, omode, pnum, dai, dad, rl, ru); + return rv; +} + + +/* parse arguments of misc command */ +static int runmisc(int argc, char **argv){ + char *path = NULL; + char *rstr = NULL; + bool mt = false; + int opts = 0; + int omode = 0; + for(int i = 2; i < argc; i++){ + if(!path && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-mt")){ + mt = true; + } else if(!strcmp(argv[i], "-tl")){ + opts |= TDBTLARGE; + } else if(!strcmp(argv[i], "-td")){ + opts |= TDBTDEFLATE; + } else if(!strcmp(argv[i], "-tb")){ + opts |= TDBTBZIP; + } else if(!strcmp(argv[i], "-tt")){ + opts |= TDBTTCBS; + } else if(!strcmp(argv[i], "-tx")){ + opts |= TDBTEXCODEC; + } else if(!strcmp(argv[i], "-nl")){ + omode |= TDBONOLCK; + } else if(!strcmp(argv[i], "-nb")){ + omode |= TDBOLCKNB; + } else { + usage(); + } + } else if(!path){ + path = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!path || !rstr) usage(); + int rnum = tcatoix(rstr); + if(rnum < 1) usage(); + int rv = procmisc(path, rnum, mt, opts, omode); + return rv; +} + + +/* parse arguments of wicked command */ +static int runwicked(int argc, char **argv){ + char *path = NULL; + char *rstr = NULL; + bool mt = false; + int opts = 0; + int omode = 0; + for(int i = 2; i < argc; i++){ + if(!path && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-mt")){ + mt = true; + } else if(!strcmp(argv[i], "-tl")){ + opts |= TDBTLARGE; + } else if(!strcmp(argv[i], "-td")){ + opts |= TDBTDEFLATE; + } else if(!strcmp(argv[i], "-tb")){ + opts |= TDBTBZIP; + } else if(!strcmp(argv[i], "-tt")){ + opts |= TDBTTCBS; + } else if(!strcmp(argv[i], "-tx")){ + opts |= TDBTEXCODEC; + } else if(!strcmp(argv[i], "-nl")){ + omode |= TDBONOLCK; + } else if(!strcmp(argv[i], "-nb")){ + omode |= TDBOLCKNB; + } else { + usage(); + } + } else if(!path){ + path = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!path || !rstr) usage(); + int rnum = tcatoix(rstr); + if(rnum < 1) usage(); + int rv = procwicked(path, rnum, mt, opts, omode); + return rv; +} + + +/* perform write command */ +static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow, + bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int iflags, int omode, bool rnd){ + iprintf("<Writing Test>\n seed=%u path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d" + " opts=%d rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d iflags=%d" + " omode=%d rnd=%d\n\n", + g_randseed, path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, + iflags, omode, rnd); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if(mt && !tctdbsetmutex(tdb)){ + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if(!rnd) omode |= TDBOTRUNC; + if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if((iflags & (1 << 0)) && !tctdbsetindex(tdb, "", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 1)) && !tctdbsetindex(tdb, "str", TDBITLEXICAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 2)) && !tctdbsetindex(tdb, "num", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 3)) && !tctdbsetindex(tdb, "type", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 4)) && !tctdbsetindex(tdb, "flag", TDBITTOKEN)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 5)) && !tctdbsetindex(tdb, "text", TDBITQGRAM)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + for(int i = 1; i <= rnum; i++){ + int id = rnd ? myrand(rnum) + 1 : (int)tctdbgenuid(tdb); + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", id); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ*5]; + int vsiz = sprintf(vbuf, "%d", id); + tcmapput(cols, "str", 3, vbuf, vsiz); + if(myrand(3) == 0){ + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for(int j = 0; j < num; j++){ + pt += myrand(5) + 1; + if(wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if(*vbuf != '\0'){ + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + if(!tctdbput(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbput"); + err = true; + break; + } + tcmapdel(cols); + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t)tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t)tctdbfsiz(tdb)); + sysprint(); + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + + +/* perform read command */ +static int procread(const char *path, bool mt, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd){ + iprintf("<Reading Test>\n seed=%u path=%s mt=%d rcnum=%d lcnum=%d ncnum=%d" + " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", + g_randseed, path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if(mt && !tctdbsetmutex(tdb)){ + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOREADER | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + int rnum = tctdbrnum(tdb); + for(int i = 1; i <= rnum; i++){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", rnd ? myrand(rnum) + 1 : i); + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if(cols){ + tcmapdel(cols); + } else if(!rnd || tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbget"); + err = true; + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t)tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t)tctdbfsiz(tdb)); + sysprint(); + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + + +/* perform remove command */ +static int procremove(const char *path, bool mt, int rcnum, int lcnum, int ncnum, + int xmsiz, int dfunit, int omode, bool rnd){ + iprintf("<Removing Test>\n seed=%u path=%s mt=%d rcnum=%d lcnum=%d ncnum=%d" + " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", + g_randseed, path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if(mt && !tctdbsetmutex(tdb)){ + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + int rnum = tctdbrnum(tdb); + for(int i = 1; i <= rnum; i++){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", rnd ? myrand(rnum) + 1 : i); + if(!tctdbout(tdb, pkbuf, pksiz) && !(rnd && tctdbecode(tdb) == TCENOREC)){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t)tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t)tctdbfsiz(tdb)); + sysprint(); + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + + +/* perform rcat command */ +static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow, + bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, + int iflags, int omode, int pnum, bool dai, bool dad, bool rl, bool ru){ + iprintf("<Random Concatenating Test>\n" + " seed=%u path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d opts=%d" + " rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d iflags=%d" + " omode=%d pnum=%d dai=%d dad=%d rl=%d ru=%d\n\n", + g_randseed, path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, rcnum, xmsiz, dfunit, + iflags, omode, pnum, dai, dad, rl, ru); + if(pnum < 1) pnum = rnum; + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if(mt && !tctdbsetmutex(tdb)){ + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if((iflags & (1 << 0)) && !tctdbsetindex(tdb, "", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 1)) && !tctdbsetindex(tdb, "str", TDBITLEXICAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 2)) && !tctdbsetindex(tdb, "num", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 3)) && !tctdbsetindex(tdb, "type", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 4)) && !tctdbsetindex(tdb, "flag", TDBITTOKEN)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if((iflags & (1 << 5)) && !tctdbsetindex(tdb, "text", TDBITQGRAM)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + for(int i = 1; i <= rnum; i++){ + int id = myrand(pnum) + 1; + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", id); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ*5]; + int vsiz = sprintf(vbuf, "%d", id); + tcmapput(cols, "str", 3, vbuf, vsiz); + if(myrand(3) == 0){ + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for(int j = 0; j < num; j++){ + pt += myrand(5) + 1; + if(wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if(*vbuf != '\0'){ + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + char nbuf[RECBUFSIZ]; + int nsiz = sprintf(nbuf, "c%d", myrand(pnum) + 1); + vsiz = sprintf(vbuf, "%d", myrand(rnum) + 1); + tcmapput(cols, nbuf, nsiz, vbuf, vsiz); + if(ru){ + switch(myrand(8)){ + case 0: + if(!tctdbput(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + break; + case 1: + if(!tctdbputkeep(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbputkeep"); + err = true; + } + break; + case 2: + if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + break; + case 3: + if(tctdbaddint(tdb, pkbuf, pksiz, 1) == INT_MIN && tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbaddint"); + err = true; + } + break; + case 4: + if(isnan(tctdbadddouble(tdb, pkbuf, pksiz, 1.0)) && tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbadddouble"); + err = true; + } + break; + case 5: + if(myrand(2) == 0){ + if(!tctdbputproc(tdb, pkbuf, pksiz, pkbuf, pksiz, pdprocfunc, NULL) && + tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbputproc"); + err = true; + } + } else { + if(!tctdbputproc(tdb, pkbuf, pksiz, NULL, 0, pdprocfunc, NULL) && + tctdbecode(tdb) != TCEKEEP && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbputproc"); + err = true; + } + } + break; + default: + if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + } + break; + } + if(err) break; + } else { + if(dai){ + if(tctdbaddint(tdb, pkbuf, pksiz, myrand(3)) == INT_MIN){ + eprint(tdb, __LINE__, "tctdbaddint"); + err = true; + break; + } + } else if(dad){ + if(isnan(tctdbadddouble(tdb, pkbuf, pksiz, myrand(30) / 10.0))){ + eprint(tdb, __LINE__, "tctdbadddouble"); + err = true; + break; + } + } else if(rl){ + char fbuf[PATH_MAX]; + int fsiz = myrand(PATH_MAX); + for(int j = 0; j < fsiz; j++){ + fbuf[j] = myrand(0x100); + } + tcmapput(cols, "bin", 3, fbuf, fsiz); + if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + break; + } + } else { + if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + break; + } + } + } + tcmapdel(cols); + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t)tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t)tctdbfsiz(tdb)); + sysprint(); + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + + +/* perform misc command */ +static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){ + iprintf("<Miscellaneous Test>\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", + g_randseed, path, rnum, mt, opts, omode); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if(mt && !tctdbsetmutex(tdb)){ + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbtune(tdb, rnum / 50, 2, -1, opts)){ + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if(!tctdbsetcache(tdb, rnum / 10, 128, 256)){ + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if(!tctdbsetxmsiz(tdb, rnum * sizeof(int))){ + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if(!tctdbsetdfunit(tdb, 8)){ + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if(!tctdbsetinvcache(tdb, -1, 0.5)){ + eprint(tdb, __LINE__, "tctdbsetinvcache"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if(!tctdbsetindex(tdb, "", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "str", TDBITLEXICAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "num", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "type", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "flag", TDBITTOKEN)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "text", TDBITQGRAM)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(1){ + TCTDB *tdbdup = tctdbnew(); + if(tctdbopen(tdbdup, path, TDBOREADER)){ + eprint(tdb, __LINE__, "(validation)"); + err = true; + } else if(tctdbecode(tdbdup) != TCETHREAD){ + eprint(tdb, __LINE__, "(validation)"); + err = true; + } + tctdbdel(tdbdup); + } + iprintf("writing:\n"); + for(int i = 1; i <= rnum; i++){ + int id = (int)tctdbgenuid(tdb); + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", id); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ*5]; + int vsiz = sprintf(vbuf, "%d", id); + tcmapput(cols, "str", 3, vbuf, vsiz); + if(myrand(3) == 0){ + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for(int j = 0; j < num; j++){ + pt += myrand(5) + 1; + if(wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if(*vbuf != '\0'){ + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + char nbuf[RECBUFSIZ]; + int nsiz = sprintf(nbuf, "c%d", myrand(32) + 1); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, nbuf, nsiz, vbuf, vsiz); + if(i % 3 == 0){ + if(!tctdbputkeep(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputkeep"); + err = true; + break; + } + } else { + if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + break; + } + } + tcmapdel(cols); + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("reading:\n"); + for(int i = 1; i <= rnum; i++){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", i); + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if(cols){ + const char *vbuf = tcmapget2(cols, "str"); + if(!vbuf || strcmp(vbuf, pkbuf)){ + eprint(tdb, __LINE__, "(validation)"); + tcmapdel(cols); + err = true; + break; + } + tcmapdel(cols); + } else { + eprint(tdb, __LINE__, "tctdbget"); + err = true; + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("erasing:\n"); + for(int i = 1; i <= rnum; i++){ + if(i % 2 == 1){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", i); + if(!tctdbout(tdb, pkbuf, pksiz)){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + break; + } + if(tctdbout(tdb, pkbuf, pksiz) || tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + break; + } + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(tctdbrnum(tdb) != rnum / 2){ + eprint(tdb, __LINE__, "tctdbrnum"); + err = true; + } + if(tctdbuidseed(tdb) != rnum){ + eprint(tdb, __LINE__, "tctdbuidseed"); + err = true; + } + iprintf("searching:\n"); + TDBQRY *qry = tctdbqrynew(tdb); + TCLIST *res = tctdbqrysearch(qry); + if(tclistnum(res) != rnum / 2){ + eprint(tdb, __LINE__, "tctdbqrysearch"); + err = true; + } + tclistdel(res); + tctdbqrydel(qry); + const char *names[] = { "", "str", "num", "type", "flag", "text", "c1" }; + int ops[] = { TDBQCSTREQ, TDBQCSTRINC, TDBQCSTRBW, TDBQCSTREW, TDBQCSTRAND, TDBQCSTROR, + TDBQCSTROREQ, TDBQCSTRRX, TDBQCNUMEQ, TDBQCNUMGT, TDBQCNUMGE, TDBQCNUMLT, + TDBQCNUMLE, TDBQCNUMBT, TDBQCNUMOREQ }; + int ftsops[] = { TDBQCFTSPH, TDBQCFTSAND, TDBQCFTSOR, TDBQCFTSEX }; + int types[] = { TDBQOSTRASC, TDBQOSTRDESC, TDBQONUMASC, TDBQONUMDESC }; + qry = tctdbqrynew(tdb); + for(int i = 1; i <= rnum; i++){ + if(myrand(100) != 0){ + tctdbqrydel(qry); + qry = tctdbqrynew(tdb); + if(myrand(10) != 0){ + char expr[RECBUFSIZ]; + sprintf(expr, "%d", myrand(i) + 1); + switch(myrand(6)){ + default: + tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); + break; + case 1: + tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); + break; + case 2: + tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); + break; + case 3: + tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); + break; + case 4: + tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); + break; + case 5: + tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); + break; + } + switch(myrand(5)){ + case 0: + tctdbqrysetorder(qry, "str", TDBQOSTRASC); + break; + case 1: + tctdbqrysetorder(qry, "str", TDBQOSTRDESC); + break; + case 2: + tctdbqrysetorder(qry, "num", TDBQONUMASC); + break; + case 3: + tctdbqrysetorder(qry, "num", TDBQONUMDESC); + break; + } + tctdbqrysetlimit(qry, 10, myrand(5) * 10); + } else { + int cnum = myrand(4); + if(cnum < 1 && myrand(5) != 0) cnum = 1; + for(int j = 0; j < cnum; j++){ + const char *name = names[myrand(sizeof(names) / sizeof(*names))]; + int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; + if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; + if(myrand(20) == 0) op |= TDBQCNEGATE; + if(myrand(20) == 0) op |= TDBQCNOIDX; + char expr[RECBUFSIZ*3]; + char *wp = expr; + if(myrand(3) == 0){ + wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); + } else { + wp += sprintf(expr, "%d", myrand(i)); + } + if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + tctdbqryaddcond(qry, name, op, expr); + } + if(myrand(3) != 0){ + const char *name = names[myrand(sizeof(names) / sizeof(*names))]; + int type = types[myrand(sizeof(types) / sizeof(*types))]; + tctdbqrysetorder(qry, name, type); + } + if(myrand(3) != 0) tctdbqrysetlimit(qry, myrand(i), myrand(10)); + } + } + if(myrand(10) == 0){ + TDBQRY *qrys[4]; + int num = myrand(5); + for(int j = 0; j < num; j++){ + qrys[j] = qry; + } + TCLIST *res = tctdbmetasearch(qrys, num, TDBMSUNION + myrand(3)); + if(num > 0){ + for(int j = 0; j < 3 && j < tclistnum(res); j++){ + int pksiz; + const char *pkbuf = tclistval(res, j, &pksiz); + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if(cols){ + TCLIST *texts = tctdbqrykwic(qrys[0], cols, NULL, myrand(10), TCKWNOOVER); + tclistdel(texts); + tcmapdel(cols); + } else { + eprint(tdb, __LINE__, "tctdbget"); + err = true; + } + } + } + tclistdel(res); + } else { + TCLIST *res = tctdbqrysearch(qry); + tclistdel(res); + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + tctdbqrydel(qry); + iprintf("random writing and reopening:\n"); + for(int i = 1; i <= rnum; i++){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", myrand(rnum) + 1); + switch(myrand(4)){ + default: + if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + break; + case 1: + if(!tctdbaddint(tdb, pkbuf, pksiz, 1)){ + eprint(tdb, __LINE__, "tctdbaddint"); + err = true; + } + break; + case 2: + if(!tctdbadddouble(tdb, pkbuf, pksiz, 1.0)){ + eprint(tdb, __LINE__, "tctdbadddouble"); + err = true; + } + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + iprintf("random updating:\n"); + for(int i = 1; i <= rnum; i++){ + if(myrand(2) == 0){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%X", myrand(rnum) + 1); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ*2]; + int vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + tcmapput(cols, "c1", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, " %d, %d ", myrand(i) + 1, rnum / (myrand(rnum) + 1)); + tcmapput(cols, "flag", 4, vbuf, vsiz); + tcmapput(cols, "text", 4, vbuf, vsiz); + if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + break; + } + tcmapdel(cols); + } else { + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%X", myrand(rnum) + 1); + if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + break; + } + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + iprintf("checking iterator:\n"); + int inum = 0; + if(!tctdbiterinit(tdb)){ + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + char *pkbuf; + int pksiz; + for(int i = 1; (pkbuf = tctdbiternext(tdb, &pksiz)) != NULL; i++, inum++){ + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if(!cols){ + eprint(tdb, __LINE__, "tctdbget"); + err = true; + tcfree(pkbuf); + break; + } + tcmapdel(cols); + tcfree(pkbuf); + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rnum > 250) iprintf(" (%08d)\n", inum); + if(tctdbecode(tdb) != TCENOREC || inum != tctdbrnum(tdb)){ + eprint(tdb, __LINE__, "(validation)"); + err = true; + } + iprintf("checking search consistency:\n"); + for(int i = 1; i <= rnum; i++){ + TDBQRY *myqry = tctdbqrynew(tdb); + qry = tctdbqrynew(tdb); + int cnum = myrand(4); + if(cnum < 1) cnum = 1; + for(int j = 0; j < cnum; j++){ + const char *name = names[myrand(sizeof(names) / sizeof(*names))]; + int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; + if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; + char expr[RECBUFSIZ*3]; + char *wp = expr; + if(myrand(3) == 0){ + wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); + } else { + wp += sprintf(expr, "%d", myrand(i)); + } + if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + tctdbqryaddcond(myqry, name, op | (myrand(2) == 0 ? TDBQCNOIDX : 0), expr); + tctdbqryaddcond(qry, name, op | (myrand(2) == 0 ? TDBQCNOIDX : 0), expr); + } + int max = (myrand(10) == 0) ? 0 : myrand(10) + 1; + if(max > 0){ + tctdbqrysetlimit(myqry, max, 0); + tctdbqrysetlimit(qry, max, 0); + TCLIST *myres = tctdbqrysearch(myqry); + res = tctdbqrysearch(qry); + if(tclistnum(myres) != tclistnum(res)){ + eprint(tdb, __LINE__, "(validation)"); + err = true; + } + tclistdel(res); + tclistdel(myres); + } else { + TCLIST *myres = tctdbqrysearch(myqry); + res = tctdbqrysearch(qry); + if(tclistnum(myres) == tclistnum(res)){ + tclistsort(myres); + tclistsort(res); + int rnum = tclistnum(myres); + for(int j = 0; j < rnum; j++){ + int myrsiz; + const char *myrbuf = tclistval(myres, j, &myrsiz); + int rsiz; + const char *rbuf = tclistval(res, j, &rsiz); + if(myrsiz != rsiz || memcmp(myrbuf, rbuf, myrsiz)){ + eprint(tdb, __LINE__, "(validation)"); + err = true; + break; + } + } + } else { + eprint(tdb, __LINE__, "(validation)"); + err = true; + } + tclistdel(res); + tclistdel(myres); + } + tctdbqrydel(qry); + tctdbqrydel(myqry); + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + qry = tctdbqrynew(tdb); + tctdbqryaddcond(qry, "", TDBQCSTRBW, "1"); + if(!tctdbqrysearchout(qry)){ + eprint(tdb, __LINE__, "tctdbqrysearchout"); + err = true; + } + tctdbqrydel(qry); + qry = tctdbqrynew(tdb); + tctdbqryaddcond(qry, "", TDBQCSTRBW, "2"); + if(!tctdbqrysearchout2(qry)){ + eprint(tdb, __LINE__, "tctdbqrysearchout2"); + err = true; + } + tctdbqrydel(qry); + if(myrand(4) == 0 && !tctdbdefrag(tdb, 0)){ + eprint(tdb, __LINE__, "tctdbdefrag"); + err = true; + } + if(myrand(4) == 0 && !tctdbcacheclear(tdb)){ + eprint(tdb, __LINE__, "tctdbcacheclear"); + err = true; + } + iprintf("checking transaction commit:\n"); + if(!tctdbtranbegin(tdb)){ + eprint(tdb, __LINE__, "tctdbtranbegin"); + err = true; + } + for(int i = 1; i <= rnum; i++){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", myrand(rnum)); + switch(myrand(4)){ + default: + if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + break; + case 1: + if(!tctdbaddint(tdb, pkbuf, pksiz, 1)){ + eprint(tdb, __LINE__, "tctdbaddint"); + err = true; + } + break; + case 2: + if(!tctdbadddouble(tdb, pkbuf, pksiz, 1.0)){ + eprint(tdb, __LINE__, "tctdbadddouble"); + err = true; + } + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(!tctdbtrancommit(tdb)){ + eprint(tdb, __LINE__, "tctdbtrancommit"); + err = true; + } + iprintf("checking transaction abort:\n"); + uint64_t ornum = tctdbrnum(tdb); + uint64_t ofsiz = tctdbfsiz(tdb); + if(!tctdbtranbegin(tdb)){ + eprint(tdb, __LINE__, "tctdbtranbegin"); + err = true; + } + for(int i = 1; i <= rnum; i++){ + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", myrand(rnum)); + switch(myrand(4)){ + default: + if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + break; + case 1: + if(!tctdbaddint(tdb, pkbuf, pksiz, 1)){ + eprint(tdb, __LINE__, "tctdbaddint"); + err = true; + } + break; + case 2: + if(!tctdbadddouble(tdb, pkbuf, pksiz, 1.0)){ + eprint(tdb, __LINE__, "tctdbadddouble"); + err = true; + } + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + iputchar('.'); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(!tctdbtranabort(tdb)){ + eprint(tdb, __LINE__, "tctdbtranabort"); + err = true; + } + if(tctdbrnum(tdb) != ornum || tctdbfsiz(tdb) != ofsiz){ + eprint(tdb, __LINE__, "(validation)"); + err = true; + } + if(!tctdbvanish(tdb)){ + eprint(tdb, __LINE__, "tctdbvanish"); + err = true; + } + if(!tctdbput3(tdb, "mikio", "str\tMIKIO\tbirth\t19780211")){ + eprint(tdb, __LINE__, "tctdbput3"); + err = true; + } + if(!tctdbtranbegin(tdb)){ + eprint(tdb, __LINE__, "tctdbtranbegin"); + err = true; + } + if(!tctdbput3(tdb, "mikio", "str\tMEKEO\tsex\tmale")){ + eprint(tdb, __LINE__, "tctdbput3"); + err = true; + } + for(int i = 0; i < 10; i++){ + char pkbuf[RECBUFSIZ]; + sprintf(pkbuf, "%d", myrand(10) + 1); + char vbuf[RECBUFSIZ*2]; + sprintf(vbuf, "%d\t%d", myrand(10) + 1, myrand(rnum) + 1); + if(!tctdbput3(tdb, pkbuf, vbuf)){ + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + } + if(!tctdbforeach(tdb, iterfunc, NULL)){ + eprint(tdb, __LINE__, "tctdbforeach"); + err = true; + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t)tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t)tctdbfsiz(tdb)); + sysprint(); + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + + +/* perform wicked command */ +static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){ + iprintf("<Wicked Writing Test>\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", + g_randseed, path, rnum, mt, opts, omode); + bool err = false; + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if(mt && !tctdbsetmutex(tdb)){ + eprint(tdb, __LINE__, "tctdbsetmutex"); + err = true; + } + if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(tdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbtune(tdb, rnum / 50, 2, -1, opts)){ + eprint(tdb, __LINE__, "tctdbtune"); + err = true; + } + if(!tctdbsetcache(tdb, rnum / 10, 128, 256)){ + eprint(tdb, __LINE__, "tctdbsetcache"); + err = true; + } + if(!tctdbsetxmsiz(tdb, rnum * sizeof(int))){ + eprint(tdb, __LINE__, "tctdbsetxmsiz"); + err = true; + } + if(!tctdbsetinvcache(tdb, -1, 0.5)){ + eprint(tdb, __LINE__, "tctdbsetinvcache"); + err = true; + } + if(!tctdbsetdfunit(tdb, 8)){ + eprint(tdb, __LINE__, "tctdbsetdfunit"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + if(!tctdbsetindex(tdb, "", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "str", TDBITLEXICAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "num", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "type", TDBITDECIMAL)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "flag", TDBITTOKEN)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + if(!tctdbsetindex(tdb, "text", TDBITQGRAM)){ + eprint(tdb, __LINE__, "tctdbsetindex"); + err = true; + } + const char *names[] = { "", "str", "num", "type", "flag", "text", "c1" }; + int ops[] = { TDBQCSTREQ, TDBQCSTRINC, TDBQCSTRBW, TDBQCSTREW, TDBQCSTRAND, TDBQCSTROR, + TDBQCSTROREQ, TDBQCSTRRX, TDBQCNUMEQ, TDBQCNUMGT, TDBQCNUMGE, TDBQCNUMLT, + TDBQCNUMLE, TDBQCNUMBT, TDBQCNUMOREQ }; + int ftsops[] = { TDBQCFTSPH, TDBQCFTSAND, TDBQCFTSOR, TDBQCFTSEX }; + int types[] = { TDBQOSTRASC, TDBQOSTRDESC, TDBQONUMASC, TDBQONUMDESC }; + for(int i = 1; i <= rnum; i++){ + int id = myrand(2) == 0 ? myrand(rnum) + 1 : (int)tctdbgenuid(tdb); + char pkbuf[RECBUFSIZ]; + int pksiz = sprintf(pkbuf, "%d", id); + TCMAP *cols = tcmapnew2(7); + char vbuf[RECBUFSIZ*5]; + int vsiz = sprintf(vbuf, "%d", id); + tcmapput(cols, "str", 3, vbuf, vsiz); + if(myrand(3) == 0){ + vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); + } else { + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + } + tcmapput(cols, "num", 3, vbuf, vsiz); + vsiz = sprintf(vbuf, "%d", myrand(32) + 1); + tcmapput(cols, "type", 4, vbuf, vsiz); + int num = myrand(5); + int pt = 0; + char *wp = vbuf; + for(int j = 0; j < num; j++){ + pt += myrand(5) + 1; + if(wp > vbuf) *(wp++) = ','; + wp += sprintf(wp, "%d", pt); + } + *wp = '\0'; + if(*vbuf != '\0'){ + tcmapput(cols, "flag", 4, vbuf, wp - vbuf); + tcmapput(cols, "text", 4, vbuf, wp - vbuf); + } + char nbuf[RECBUFSIZ]; + int nsiz = sprintf(nbuf, "c%d", myrand(i) + 1); + vsiz = sprintf(vbuf, "%d", myrand(i) + 1); + tcmapput(cols, nbuf, nsiz, vbuf, vsiz); + char *cbuf; + int csiz; + TCMAP *ncols; + TDBQRY *qry; + TCLIST *res; + switch(myrand(17)){ + case 0: + iputchar('0'); + if(!tctdbput(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbput"); + err = true; + } + break; + case 1: + iputchar('1'); + cbuf = tcstrjoin4(cols, &csiz); + if(!tctdbput2(tdb, pkbuf, pksiz, cbuf, csiz)){ + eprint(tdb, __LINE__, "tctdbput2"); + err = true; + } + tcfree(cbuf); + break; + case 2: + iputchar('2'); + cbuf = tcstrjoin3(cols, '\t'); + if(!tctdbput3(tdb, pkbuf, cbuf)){ + eprint(tdb, __LINE__, "tctdbput3"); + err = true; + } + tcfree(cbuf); + break; + case 3: + iputchar('3'); + if(!tctdbputkeep(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbputkeep"); + err = true; + } + break; + case 4: + iputchar('4'); + cbuf = tcstrjoin4(cols, &csiz); + if(!tctdbputkeep2(tdb, pkbuf, pksiz, cbuf, csiz) && tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbputkeep2"); + err = true; + } + tcfree(cbuf); + break; + case 5: + iputchar('5'); + cbuf = tcstrjoin3(cols, '\t'); + if(!tctdbputkeep3(tdb, pkbuf, cbuf) && tctdbecode(tdb) != TCEKEEP){ + eprint(tdb, __LINE__, "tctdbputkeep3"); + err = true; + } + tcfree(cbuf); + break; + case 6: + iputchar('6'); + if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ + eprint(tdb, __LINE__, "tctdbputcat"); + err = true; + } + break; + case 7: + iputchar('7'); + cbuf = tcstrjoin4(cols, &csiz); + if(!tctdbputcat2(tdb, pkbuf, pksiz, cbuf, csiz)){ + eprint(tdb, __LINE__, "tctdbputcat2"); + err = true; + } + tcfree(cbuf); + break; + case 8: + iputchar('8'); + cbuf = tcstrjoin3(cols, '\t'); + if(!tctdbputcat3(tdb, pkbuf, cbuf)){ + eprint(tdb, __LINE__, "tctdbputcat3"); + err = true; + } + tcfree(cbuf); + break; + case 9: + iputchar('9'); + if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout"); + err = true; + } + break; + case 10: + iputchar('A'); + if(!tctdbout2(tdb, pkbuf) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbout2"); + err = true; + } + break; + case 11: + iputchar('B'); + ncols = tctdbget(tdb, pkbuf, pksiz); + if(ncols){ + tcmapdel(ncols); + } else if(tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbget"); + err = true; + } + break; + case 12: + iputchar('C'); + cbuf = tctdbget2(tdb, pkbuf, pksiz, &csiz); + if(cbuf){ + tcfree(cbuf); + } else if(tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbget2"); + err = true; + } + break; + case 13: + iputchar('D'); + cbuf = tctdbget3(tdb, pkbuf); + if(cbuf){ + tcfree(cbuf); + } else if(tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbget3"); + err = true; + } + break; + case 14: + iputchar('E'); + if(myrand(rnum / 128) == 0){ + if(myrand(2) == 0){ + if(!tctdbiterinit(tdb)){ + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + } else { + if(!tctdbiterinit2(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ + eprint(tdb, __LINE__, "tctdbiterinit2"); + err = true; + } + } + } + for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ + if(j % 3 == 0){ + ncols = tctdbiternext3(tdb); + if(ncols){ + tcmapdel(ncols); + } else { + int ecode = tctdbecode(tdb); + if(ecode != TCEINVALID && ecode != TCENOREC){ + eprint(tdb, __LINE__, "tctdbiternext3"); + err = true; + } + } + } else { + int iksiz; + char *ikbuf = tctdbiternext(tdb, &iksiz); + if(ikbuf){ + tcfree(ikbuf); + } else { + int ecode = tctdbecode(tdb); + if(ecode != TCEINVALID && ecode != TCENOREC){ + eprint(tdb, __LINE__, "tctdbiternext"); + err = true; + } + } + } + } + break; + case 15: + iputchar('F'); + qry = tctdbqrynew(tdb); + if(myrand(10) != 0){ + char expr[RECBUFSIZ]; + sprintf(expr, "%d", myrand(i) + 1); + switch(myrand(6)){ + default: + tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); + break; + case 1: + tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); + break; + case 2: + tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); + break; + case 3: + tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); + break; + case 4: + tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); + break; + case 5: + tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); + break; + } + switch(myrand(5)){ + case 0: + tctdbqrysetorder(qry, "str", TDBQOSTRASC); + break; + case 1: + tctdbqrysetorder(qry, "str", TDBQOSTRDESC); + break; + case 2: + tctdbqrysetorder(qry, "num", TDBQONUMASC); + break; + case 3: + tctdbqrysetorder(qry, "num", TDBQONUMDESC); + break; + } + tctdbqrysetlimit(qry, 10, myrand(10)); + } else { + int cnum = myrand(4); + if(cnum < 1 && myrand(5) != 0) cnum = 1; + for(int j = 0; j < cnum; j++){ + const char *name = names[myrand(sizeof(names) / sizeof(*names))]; + int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; + if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; + if(myrand(20) == 0) op |= TDBQCNEGATE; + if(myrand(20) == 0) op |= TDBQCNOIDX; + char expr[RECBUFSIZ*3]; + char *wp = expr; + if(myrand(3) == 0){ + wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); + } else { + wp += sprintf(expr, "%d", myrand(i)); + } + if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); + tctdbqryaddcond(qry, name, op, expr); + } + if(myrand(3) != 0){ + const char *name = names[myrand(sizeof(names) / sizeof(*names))]; + int type = types[myrand(sizeof(types) / sizeof(*types))]; + tctdbqrysetorder(qry, name, type); + } + if(myrand(3) != 0) tctdbqrysetlimit(qry, myrand(i), myrand(10)); + } + res = tctdbqrysearch(qry); + tclistdel(res); + tctdbqrydel(qry); + break; + default: + iputchar('@'); + if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); + break; + } + tcmapdel(cols); + if(i % 50 == 0) iprintf(" (%08d)\n", i); + if(i == rnum / 2){ + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ + eprint(tdb, __LINE__, "tctdbopen"); + err = true; + } + } else if(i == rnum / 4){ + char *npath = tcsprintf("%s-tmp", path); + if(!tctdbcopy(tdb, npath)){ + eprint(tdb, __LINE__, "tctdbcopy"); + err = true; + } + TCTDB *ntdb = tctdbnew(); + if(!tctdbsetcodecfunc(ntdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ + eprint(ntdb, __LINE__, "tctdbsetcodecfunc"); + err = true; + } + if(!tctdbopen(ntdb, npath, TDBOREADER | omode)){ + eprint(ntdb, __LINE__, "tctdbopen"); + err = true; + } + tctdbdel(ntdb); + unlink(npath); + tcfree(npath); + if(!tctdboptimize(tdb, rnum / 50, -1, -1, -1)){ + eprint(tdb, __LINE__, "tctdboptimize"); + err = true; + } + if(!tctdbiterinit(tdb)){ + eprint(tdb, __LINE__, "tctdbiterinit"); + err = true; + } + } else if(i == rnum / 8){ + if(!tctdbtranbegin(tdb)){ + eprint(tdb, __LINE__, "tctdbtranbegin"); + err = true; + } + } else if(i == rnum / 8 + rnum / 16){ + if(!tctdbtrancommit(tdb)){ + eprint(tdb, __LINE__, "tctdbtrancommit"); + err = true; + } + } + } + if(!tctdbsync(tdb)){ + eprint(tdb, __LINE__, "tctdbsync"); + err = true; + } + iprintf("record number: %" PRIuMAX "\n", (uint64_t)tctdbrnum(tdb)); + iprintf("size: %" PRIuMAX "\n", (uint64_t)tctdbfsiz(tdb)); + sysprint(); + if(!tctdbclose(tdb)){ + eprint(tdb, __LINE__, "tctdbclose"); + err = true; + } + tctdbdel(tdb); + iprintf("time: %.3f\n", tctime() - stime); + iprintf("%s\n\n", err ? "error" : "ok"); + return err ? 1 : 0; +} + + + +// END OF FILE diff --git a/tcejdb/src/tctdb/tools/tctmgr.c b/tcejdb/src/tctdb/tools/tctmgr.c new file mode 100644 index 0000000..87dc379 --- /dev/null +++ b/tcejdb/src/tctdb/tools/tctmgr.c @@ -0,0 +1,1223 @@ +/************************************************************************************************* + * The command line utility of the table database API + * Copyright (C) 2006-2012 FAL Labs + * This file is part of Tokyo Cabinet. + * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of + * the GNU Lesser General Public License as published by the Free Software Foundation; either + * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * You should have received a copy of the GNU Lesser General Public License along with Tokyo + * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA. + *************************************************************************************************/ + + +#include <tcutil.h> +#include <tctdb.h> +#include "myconf.h" + + +/* global variables */ +const char *g_progname; // program name +HANDLE g_dbgfd = INVALID_HANDLE_VALUE; // debugging output + + +/* function prototypes */ +int main(int argc, char **argv); +static void usage(void); +static void printerr(TCTDB *tdb); +static int printdata(const char *ptr, int size, bool px); +static char *mygetline(FILE *ifp); +static int runcreate(int argc, char **argv); +static int runinform(int argc, char **argv); +static int runput(int argc, char **argv); +static int runout(int argc, char **argv); +static int runget(int argc, char **argv); +static int runlist(int argc, char **argv); +static int runsearch(int argc, char **argv); +static int runoptimize(int argc, char **argv); +static int runsetindex(int argc, char **argv); +static int runimporttsv(int argc, char **argv); +static int runversion(int argc, char **argv); +static int proccreate(const char *path, int bnum, int apow, int fpow, int opts); +static int procinform(const char *path, int omode); +static int procput(const char *path, const char *pkbuf, int pksiz, TCMAP *cols, + int omode, int dmode); +static int procout(const char *path, const char *pkbuf, int pksiz, int omode); +static int procget(const char *path, const char *pkbuf, int pksiz, int omode, bool px, bool pz); +static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr); +static int procsearch(const char *path, TCLIST *conds, const char *oname, const char *otype, + int omode, int max, int skip, bool pv, bool px, bool kw, bool ph, int bt, + bool rm, const char *mtype); +static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode, + bool df); +static int procsetindex(const char *path, const char *name, int omode, int type); +static int procimporttsv(const char *path, const char *file, int omode, bool sc); +static int procversion(void); + +/* main routine */ +int main(int argc, char **argv) { + g_progname = argv[0]; + g_dbgfd = INVALID_HANDLE_VALUE; + const char *ebuf = getenv("TCDBGFD"); + if (ebuf) { + int debugfd = tcatoix(ebuf); +#ifdef _WIN32 + g_dbgfd = (HANDLE) _get_osfhandle(debugfd); +#else + g_dbgfd = debugfd; +#endif + } + if (argc < 2) usage(); + int rv = 0; + if (!strcmp(argv[1], "create")) { + rv = runcreate(argc, argv); + } else if (!strcmp(argv[1], "inform")) { + rv = runinform(argc, argv); + } else if (!strcmp(argv[1], "put")) { + rv = runput(argc, argv); + } else if (!strcmp(argv[1], "out")) { + rv = runout(argc, argv); + } else if (!strcmp(argv[1], "get")) { + rv = runget(argc, argv); + } else if (!strcmp(argv[1], "list")) { + rv = runlist(argc, argv); + } else if (!strcmp(argv[1], "search")) { + rv = runsearch(argc, argv); + } else if (!strcmp(argv[1], "optimize")) { + rv = runoptimize(argc, argv); + } else if (!strcmp(argv[1], "setindex")) { + rv = runsetindex(argc, argv); + } else if (!strcmp(argv[1], "importtsv")) { + rv = runimporttsv(argc, argv); + } else if (!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")) { + rv = runversion(argc, argv); + } else { + usage(); + } + return rv; +} + +/* print the usage and exit */ +static void usage(void) { + fprintf(stderr, "%s: the command line utility of the table database API\n", g_progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]\n", g_progname); + fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname); + fprintf(stderr, " %s put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path pkey [cols...]\n", + g_progname); + fprintf(stderr, " %s out [-nl|-nb] [-sx] path pkey\n", g_progname); + fprintf(stderr, " %s get [-nl|-nb] [-sx] [-px] [-pz] path pkey\n", g_progname); + fprintf(stderr, " %s list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path\n", g_progname); + fprintf(stderr, " %s search [-nl|-nb] [-ord name type] [-m num] [-sk num] [-kw] [-pv] [-px]" + " [-ph] [-bt num] [-rm] [-ms type] path [name op expr ...]\n", g_progname); + fprintf(stderr, " %s optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df]" + " path [bnum [apow [fpow]]]\n", g_progname); + fprintf(stderr, " %s setindex [-nl|-nb] [-it type] path name\n", g_progname); + fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname); + fprintf(stderr, " %s version\n", g_progname); + fprintf(stderr, "\n"); + exit(1); +} + +/* print error information */ +static void printerr(TCTDB *tdb) { + const char *path = tctdbpath(tdb); + int ecode = tctdbecode(tdb); + fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tctdberrmsg(ecode)); +} + +/* print record data */ +static int printdata(const char *ptr, int size, bool px) { + int len = 0; + while (size-- > 0) { + if (px) { + if (len > 0) putchar(' '); + len += printf("%02X", *(unsigned char *) ptr); + } else { + if (strchr("\t\r\n", *ptr)) { + putchar(' '); + } else { + putchar(*ptr); + } + len++; + } + ptr++; + } + return len; +} + +/* read a line from a file descriptor */ +static char *mygetline(FILE *ifp) { + int len = 0; + int blen = 1024; + char *buf = tcmalloc(blen + 1); + bool end = true; + int c; + while ((c = fgetc(ifp)) != EOF) { + end = false; + if (c == '\0') continue; + if (blen <= len) { + blen *= 2; + buf = tcrealloc(buf, blen + 1); + } + if (c == '\n' || c == '\r') c = '\0'; + buf[len++] = c; + if (c == '\0') break; + } + if (end) { + tcfree(buf); + return NULL; + } + buf[len] = '\0'; + return buf; +} + +/* parse arguments of create command */ +static int runcreate(int argc, char **argv) { + char *path = NULL; + char *bstr = NULL; + char *astr = NULL; + char *fstr = NULL; + int opts = 0; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-tl")) { + opts |= TDBTLARGE; + } else if (!strcmp(argv[i], "-td")) { + opts |= TDBTDEFLATE; + } else if (!strcmp(argv[i], "-tb")) { + opts |= TDBTBZIP; + } else if (!strcmp(argv[i], "-tt")) { + opts |= TDBTTCBS; + } else if (!strcmp(argv[i], "-tx")) { + opts |= TDBTEXCODEC; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!bstr) { + bstr = argv[i]; + } else if (!astr) { + astr = argv[i]; + } else if (!fstr) { + fstr = argv[i]; + } else { + usage(); + } + } + if (!path) usage(); + int bnum = bstr ? tcatoix(bstr) : -1; + int apow = astr ? tcatoix(astr) : -1; + int fpow = fstr ? tcatoix(fstr) : -1; + int rv = proccreate(path, bnum, apow, fpow, opts); + return rv; +} + +/* parse arguments of inform command */ +static int runinform(int argc, char **argv) { + char *path = NULL; + int omode = 0; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else { + usage(); + } + } + if (!path) usage(); + int rv = procinform(path, omode); + return rv; +} + +/* parse arguments of put command */ +static int runput(int argc, char **argv) { + char *path = NULL; + char *pkey = NULL; + TCLIST *vals = tcmpoollistnew(tcmpoolglobal()); + int omode = 0; + int dmode = 0; + bool sx = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-dk")) { + dmode = -1; + } else if (!strcmp(argv[i], "-dc")) { + dmode = 1; + } else if (!strcmp(argv[i], "-dai")) { + dmode = 10; + } else if (!strcmp(argv[i], "-dad")) { + dmode = 11; + } else if (!strcmp(argv[i], "-sx")) { + sx = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!pkey) { + pkey = argv[i]; + } else { + tclistpush2(vals, argv[i]); + } + } + if (!path || !pkey) usage(); + TCMAP *cols = tcmapnew(); + char *pkbuf; + int pksiz; + if (sx) { + pkbuf = tchexdecode(pkey, &pksiz); + for (int i = 0; i < tclistnum(vals) - 1; i += 2) { + const char *name = tclistval2(vals, i); + const char *value = tclistval2(vals, i + 1); + int nsiz; + char *nbuf = tchexdecode(name, &nsiz); + int vsiz; + char *vbuf = tchexdecode(value, &vsiz); + tcmapput(cols, nbuf, nsiz, vbuf, vsiz); + tcfree(vbuf); + tcfree(nbuf); + } + } else { + pksiz = strlen(pkey); + pkbuf = tcmemdup(pkey, pksiz); + for (int i = 0; i < tclistnum(vals) - 1; i += 2) { + const char *name = tclistval2(vals, i); + const char *value = tclistval2(vals, i + 1); + tcmapput2(cols, name, value); + } + } + int rv = procput(path, pkbuf, pksiz, cols, omode, dmode); + tcmapdel(cols); + tcfree(pkbuf); + return rv; +} + +/* parse arguments of out command */ +static int runout(int argc, char **argv) { + char *path = NULL; + char *pkey = NULL; + int omode = 0; + bool sx = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-sx")) { + sx = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!pkey) { + pkey = argv[i]; + } else { + usage(); + } + } + if (!path || !pkey) usage(); + int pksiz; + char *pkbuf; + if (sx) { + pkbuf = tchexdecode(pkey, &pksiz); + } else { + pksiz = strlen(pkey); + pkbuf = tcmemdup(pkey, pksiz); + } + int rv = procout(path, pkbuf, pksiz, omode); + tcfree(pkbuf); + return rv; +} + +/* parse arguments of get command */ +static int runget(int argc, char **argv) { + char *path = NULL; + char *pkey = NULL; + int omode = 0; + bool sx = false; + bool px = false; + bool pz = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-sx")) { + sx = true; + } else if (!strcmp(argv[i], "-px")) { + px = true; + } else if (!strcmp(argv[i], "-pz")) { + pz = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!pkey) { + pkey = argv[i]; + } else { + usage(); + } + } + if (!path || !pkey) usage(); + int pksiz; + char *pkbuf; + if (sx) { + pkbuf = tchexdecode(pkey, &pksiz); + } else { + pksiz = strlen(pkey); + pkbuf = tcmemdup(pkey, pksiz); + } + int rv = procget(path, pkbuf, pksiz, omode, px, pz); + tcfree(pkbuf); + return rv; +} + +/* parse arguments of list command */ +static int runlist(int argc, char **argv) { + char *path = NULL; + int omode = 0; + int max = -1; + bool pv = false; + bool px = false; + char *fmstr = NULL; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-m")) { + if (++i >= argc) usage(); + max = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-pv")) { + pv = true; + } else if (!strcmp(argv[i], "-px")) { + px = true; + } else if (!strcmp(argv[i], "-fm")) { + if (++i >= argc) usage(); + fmstr = argv[i]; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else { + usage(); + } + } + if (!path) usage(); + int rv = proclist(path, omode, max, pv, px, fmstr); + return rv; +} + +/* parse arguments of search command */ +static int runsearch(int argc, char **argv) { + char *path = NULL; + TCLIST *conds = tcmpoollistnew(tcmpoolglobal()); + char *oname = NULL; + char *otype = NULL; + int omode = 0; + int max = -1; + int skip = -1; + bool pv = false; + bool px = false; + bool kw = false; + bool ph = false; + int bt = 0; + bool rm = false; + char *mtype = NULL; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-ord")) { + if (++i >= argc) usage(); + oname = argv[i]; + if (++i >= argc) usage(); + otype = argv[i]; + } else if (!strcmp(argv[i], "-m")) { + if (++i >= argc) usage(); + max = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-sk")) { + if (++i >= argc) usage(); + skip = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-kw")) { + kw = true; + } else if (!strcmp(argv[i], "-pv")) { + pv = true; + } else if (!strcmp(argv[i], "-px")) { + px = true; + } else if (!strcmp(argv[i], "-ph")) { + ph = true; + } else if (!strcmp(argv[i], "-bt")) { + if (++i >= argc) usage(); + bt = tcatoix(argv[i]); + } else if (!strcmp(argv[i], "-rm")) { + rm = true; + } else if (!strcmp(argv[i], "-ms")) { + if (++i >= argc) usage(); + mtype = argv[i]; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else { + tclistpush2(conds, argv[i]); + } + } + if (!path || tclistnum(conds) % 3 != 0) usage(); + int rv = procsearch(path, conds, oname, otype, omode, max, skip, + pv, px, kw, ph, bt, rm, mtype); + return rv; +} + +/* parse arguments of optimize command */ +static int runoptimize(int argc, char **argv) { + char *path = NULL; + char *bstr = NULL; + char *astr = NULL; + char *fstr = NULL; + int opts = UINT8_MAX; + int omode = 0; + bool df = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-tl")) { + if (opts == UINT8_MAX) opts = 0; + opts |= TDBTLARGE; + } else if (!strcmp(argv[i], "-td")) { + if (opts == UINT8_MAX) opts = 0; + opts |= TDBTDEFLATE; + } else if (!strcmp(argv[i], "-tb")) { + if (opts == UINT8_MAX) opts = 0; + opts |= TDBTBZIP; + } else if (!strcmp(argv[i], "-tt")) { + if (opts == UINT8_MAX) opts = 0; + opts |= TDBTTCBS; + } else if (!strcmp(argv[i], "-tx")) { + if (opts == UINT8_MAX) opts = 0; + opts |= TDBTEXCODEC; + } else if (!strcmp(argv[i], "-tz")) { + if (opts == UINT8_MAX) opts = 0; + } else if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-df")) { + df = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!bstr) { + bstr = argv[i]; + } else if (!astr) { + astr = argv[i]; + } else if (!fstr) { + fstr = argv[i]; + } else { + usage(); + } + } + if (!path) usage(); + int bnum = bstr ? tcatoix(bstr) : -1; + int apow = astr ? tcatoix(astr) : -1; + int fpow = fstr ? tcatoix(fstr) : -1; + int rv = procoptimize(path, bnum, apow, fpow, opts, omode, df); + return rv; +} + +/* parse arguments of setindex command */ +static int runsetindex(int argc, char **argv) { + char *path = NULL; + char *name = NULL; + int omode = 0; + int type = TDBITLEXICAL; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-it")) { + if (++i >= argc) usage(); + type = tctdbstrtoindextype(argv[i]); + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!name) { + name = argv[i]; + } else { + usage(); + } + } + if (!path || !name) usage(); + if (type < 0) usage(); + int rv = procsetindex(path, name, omode, type); + return rv; +} + +/* parse arguments of importtsv command */ +static int runimporttsv(int argc, char **argv) { + char *path = NULL; + char *file = NULL; + int omode = 0; + bool sc = false; + for (int i = 2; i < argc; i++) { + if (!path && argv[i][0] == '-') { + if (!strcmp(argv[i], "-nl")) { + omode |= TDBONOLCK; + } else if (!strcmp(argv[i], "-nb")) { + omode |= TDBOLCKNB; + } else if (!strcmp(argv[i], "-sc")) { + sc = true; + } else { + usage(); + } + } else if (!path) { + path = argv[i]; + } else if (!file) { + file = argv[i]; + } else { + usage(); + } + } + if (!path) usage(); + int rv = procimporttsv(path, file, omode, sc); + return rv; +} + +/* parse arguments of version command */ +static int runversion(int argc, char **argv) { + int rv = procversion(); + return rv; +} + +/* perform create command */ +static int proccreate(const char *path, int bnum, int apow, int fpow, int opts) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + if (!tctdbtune(tdb, bnum, apow, fpow, opts)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + if (!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + if (!tctdbclose(tdb)) { + printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform inform command */ +static int procinform(const char *path, int omode) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL); + if (!tctdbopen(tdb, path, TDBOREADER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + const char *npath = tctdbpath(tdb); + if (!npath) npath = "(unknown)"; + printf("path: %s\n", npath); + printf("database type: table\n"); + uint8_t flags = tctdbflags(tdb); + printf("additional flags:"); + if (flags & TDBFOPEN) printf(" open"); + if (flags & TDBFFATAL) printf(" fatal"); + printf("\n"); + printf("bucket number: %" PRIuMAX "\n", (uint64_t) tctdbbnum(tdb)); +#ifndef NDEBUG + if (tdb->hdb->cnt_writerec >= 0) + printf("used bucket number: %" PRIdMAX "\n", (int64_t) tctdbbnumused(tdb)); +#endif + printf("alignment: %u\n", tctdbalign(tdb)); + printf("free block pool: %u\n", tctdbfbpmax(tdb)); + printf("index number: %d\n", tctdbinum(tdb)); + TDBIDX *idxs = tdb->idxs; + int inum = tdb->inum; + for (int i = 0; i < inum; i++) { + TDBIDX *idxp = idxs + i; + switch (idxp->type) { + case TDBITLEXICAL: + printf(" name=%s, type=lexical, rnum=%" PRIdMAX ", fsiz=%" PRIdMAX "\n", + idxp->name, (int64_t) tcbdbrnum(idxp->db), (int64_t) tcbdbfsiz(idxp->db)); + break; + case TDBITDECIMAL: + printf(" name=%s, type=decimal, rnum=%" PRIdMAX ", fsiz=%" PRIdMAX "\n", + idxp->name, (int64_t) tcbdbrnum(idxp->db), (int64_t) tcbdbfsiz(idxp->db)); + break; + case TDBITTOKEN: + printf(" name=%s, type=token, rnum=%" PRIdMAX ", fsiz=%" PRIdMAX "\n", + idxp->name, (int64_t) tcbdbrnum(idxp->db), (int64_t) tcbdbfsiz(idxp->db)); + break; + case TDBITQGRAM: + printf(" name=%s, type=qgram, rnum=%" PRIdMAX ", fsiz=%" PRIdMAX "\n", + idxp->name, (int64_t) tcbdbrnum(idxp->db), (int64_t) tcbdbfsiz(idxp->db)); + break; + } + } + printf("unique ID seed: %" PRIdMAX "\n", (int64_t) tctdbuidseed(tdb)); + printf("inode number: %" PRIdMAX "\n", (int64_t) tctdbinode(tdb)); + char date[48]; + tcdatestrwww(tctdbmtime(tdb), INT_MAX, date); + printf("modified time: %s\n", date); + uint8_t opts = tctdbopts(tdb); + printf("options:"); + if (opts & TDBTLARGE) printf(" large"); + if (opts & TDBTDEFLATE) printf(" deflate"); + if (opts & TDBTBZIP) printf(" bzip"); + if (opts & TDBTTCBS) printf(" tcbs"); + if (opts & TDBTEXCODEC) printf(" excodec"); + printf("\n"); + printf("record number: %" PRIuMAX "\n", (uint64_t) tctdbrnum(tdb)); + printf("file size: %" PRIuMAX "\n", (uint64_t) tctdbfsiz(tdb)); + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform put command */ +static int procput(const char *path, const char *pkbuf, int pksiz, TCMAP *cols, + int omode, int dmode) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOWRITER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + char pknumbuf[TCNUMBUFSIZ]; + if (pksiz < 1) { + pksiz = sprintf(pknumbuf, "%" PRIdMAX "", (int64_t) tctdbgenuid(tdb)); + pkbuf = pknumbuf; + } + const char *vbuf; + switch (dmode) { + case -1: + if (!tctdbputkeep(tdb, pkbuf, pksiz, cols)) { + printerr(tdb); + err = true; + } + break; + case 1: + if (!tctdbputcat(tdb, pkbuf, pksiz, cols)) { + printerr(tdb); + err = true; + } + break; + case 10: + vbuf = tcmapget2(cols, "_num"); + if (!vbuf) vbuf = "1"; + if (tctdbaddint(tdb, pkbuf, pksiz, tcatoi(vbuf)) == INT_MIN) { + printerr(tdb); + err = true; + } + break; + case 11: + vbuf = tcmapget2(cols, "_num"); + if (!vbuf) vbuf = "1.0"; + if (isnan(tctdbadddouble(tdb, pkbuf, pksiz, tcatof(vbuf)))) { + printerr(tdb); + err = true; + } + break; + default: + if (!tctdbput(tdb, pkbuf, pksiz, cols)) { + printerr(tdb); + err = true; + } + break; + } + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform out command */ +static int procout(const char *path, const char *pkbuf, int pksiz, int omode) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOWRITER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + if (!tctdbout(tdb, pkbuf, pksiz)) { + printerr(tdb); + err = true; + } + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform get command */ +static int procget(const char *path, const char *pkbuf, int pksiz, int omode, bool px, bool pz) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOREADER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if (cols) { + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + printdata(kbuf, ksiz, px); + putchar('\t'); + printdata(vbuf, vsiz, px); + putchar(pz ? '\t' : '\n'); + } + tcmapdel(cols); + } else { + printerr(tdb); + err = true; + } + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform list command */ +static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOREADER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + if (fmstr) { + TCLIST *pkeys = tctdbfwmkeys2(tdb, fmstr, max); + for (int i = 0; i < tclistnum(pkeys); i++) { + int pksiz; + const char *pkbuf = tclistval(pkeys, i, &pksiz); + printdata(pkbuf, pksiz, px); + if (pv) { + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if (cols) { + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + putchar('\t'); + printdata(kbuf, ksiz, px); + putchar('\t'); + printdata(vbuf, vsiz, px); + } + tcmapdel(cols); + } + } + putchar('\n'); + } + tclistdel(pkeys); + } else { + if (!tctdbiterinit(tdb)) { + printerr(tdb); + err = true; + } + int cnt = 0; + TCMAP *cols; + while ((cols = tctdbiternext3(tdb)) != NULL) { + int pksiz; + const char *pkbuf = tcmapget(cols, "", 0, &pksiz); + if (pkbuf) { + printdata(pkbuf, pksiz, px); + if (pv) { + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + if (*kbuf == '\0') continue; + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + putchar('\t'); + printdata(kbuf, ksiz, px); + putchar('\t'); + printdata(vbuf, vsiz, px); + } + } + } + tcmapdel(cols); + putchar('\n'); + if (max >= 0 && ++cnt >= max) break; + } + } + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform search command */ +static int procsearch(const char *path, TCLIST *conds, const char *oname, const char *otype, + int omode, int max, int skip, bool pv, bool px, bool kw, bool ph, int bt, + bool rm, const char *mtype) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + if (!tctdbopen(tdb, path, (rm ? TDBOWRITER : TDBOREADER) | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + TDBQRY *qry = tctdbqrynew(tdb); + int cnum = tclistnum(conds); + for (int i = 0; i < cnum - 2; i += 3) { + const char *name = tclistval2(conds, i); + const char *opstr = tclistval2(conds, i + 1); + const char *expr = tclistval2(conds, i + 2); + int op = tctdbqrystrtocondop(opstr); + if (op >= 0) tctdbqryaddcond(qry, name, op, expr); + } + if (oname) { + int type = tctdbqrystrtoordertype(otype); + if (type >= 0) tctdbqrysetorder(qry, oname, type); + } + tctdbqrysetlimit(qry, max, skip); + if (rm) { + double stime = tctime(); + if (!tctdbqrysearchout(qry)) { + printerr(tdb); + err = true; + } + double etime = tctime(); + if (ph) { + TCLIST *hints = tcstrsplit(tctdbqryhint(qry), "\n"); + int hnum = tclistnum(hints); + for (int i = 0; i < hnum; i++) { + const char *hint = tclistval2(hints, i); + if (*hint == '\0') continue; + printf("\t:::: %s\n", hint); + } + tclistdel(hints); + printf("\t:::: number of records: %d\n", tctdbqrycount(qry)); + printf("\t:::: elapsed time: %.5f\n", etime - stime); + } + } else if (bt > 0) { + double sum = 0; + for (int i = 1; i <= bt; i++) { + double stime = tctime(); + TCLIST *res = tctdbqrysearch(qry); + double etime = tctime(); + tclistdel(res); + printf("%d: %.5f sec.\n", i, etime - stime); + sum += etime - stime; + } + printf("----\n"); + printf("total: %.5f sec. (%.5f s/q = %.5f q/s)\n", sum, sum / bt, bt / sum); + } else { + double stime = tctime(); + TCLIST *res; + TCLIST *hints; + int count; + int mtnum = mtype ? tctdbmetastrtosettype(mtype) : -1; + if (mtnum >= 0) { + TDBQRY * qrys[cnum / 3 + 1]; + int qnum = 0; + for (int i = 0; i < cnum - 2; i += 3) { + const char *name = tclistval2(conds, i); + const char *opstr = tclistval2(conds, i + 1); + const char *expr = tclistval2(conds, i + 2); + int op = tctdbqrystrtocondop(opstr); + if (op >= 0) { + qrys[qnum] = tctdbqrynew(tdb); + tctdbqryaddcond(qrys[qnum], name, op, expr); + if (oname) { + int type = tctdbqrystrtoordertype(otype); + if (type >= 0) tctdbqrysetorder(qrys[qnum], oname, type); + } + tctdbqrysetlimit(qrys[qnum], max, skip); + qnum++; + } + } + res = tctdbmetasearch(qrys, qnum, mtnum); + hints = qnum > 0 ? tcstrsplit(tctdbqryhint(qrys[0]), "\n") : tclistnew2(1); + count = qnum > 0 ? tctdbqrycount(qrys[0]) : 0; + for (int i = 0; i < qnum; i++) { + tctdbqrydel(qrys[i]); + } + } else { + res = tctdbqrysearch(qry); + hints = tcstrsplit(tctdbqryhint(qry), "\n"); + count = tctdbqrycount(qry); + } + double etime = tctime(); + if (max < 0) max = INT_MAX; + int rnum = tclistnum(res); + for (int i = 0; i < rnum && max > 0; i++) { + int pksiz; + const char *pkbuf = tclistval(res, i, &pksiz); + if (kw) { + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if (cols) { + TCLIST *texts = tctdbqrykwic(qry, cols, NULL, 16, TCKWMUTAB); + int tnum = tclistnum(texts); + for (int j = 0; j < tnum && max > 0; j++) { + int tsiz; + const char *text = tclistval(texts, j, &tsiz); + printdata(pkbuf, pksiz, px); + putchar('\t'); + printdata(text, tsiz, px); + putchar('\n'); + max--; + } + tclistdel(texts); + tcmapdel(cols); + } + } else { + printdata(pkbuf, pksiz, px); + if (pv) { + TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); + if (cols) { + tcmapiterinit(cols); + const char *kbuf; + int ksiz; + while ((kbuf = tcmapiternext(cols, &ksiz)) != NULL) { + int vsiz; + const char *vbuf = tcmapiterval(kbuf, &vsiz); + putchar('\t'); + printdata(kbuf, ksiz, px); + putchar('\t'); + printdata(vbuf, vsiz, px); + } + tcmapdel(cols); + } + } + putchar('\n'); + max--; + } + } + if (ph) { + int hnum = tclistnum(hints); + for (int i = 0; i < hnum; i++) { + const char *hint = tclistval2(hints, i); + if (*hint == '\0') continue; + printf("\t:::: %s\n", hint); + } + printf("\t:::: number of records: %d\n", count); + printf("\t:::: elapsed time: %.5f\n", etime - stime); + } + tclistdel(hints); + tclistdel(res); + } + tctdbqrydel(qry); + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform optimize command */ +static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode, + bool df) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + int64_t msiz = 0; + TCMAP *info = tcsysinfo(); + if (info) { + msiz = tcatoi(tcmapget4(info, "total", "0")); + tcmapdel(info); + } + if (!tctdbsetinvcache(tdb, msiz >= (1 << 30) ? msiz / 4 : 0, 1.0)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOWRITER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + if (df) { + if (!tctdbdefrag(tdb, INT64_MAX)) { + printerr(tdb); + err = true; + } + } else { + if (!tctdboptimize(tdb, bnum, apow, fpow, opts)) { + printerr(tdb); + err = true; + } + } + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform setindex command */ +static int procsetindex(const char *path, const char *name, int omode, int type) { + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + int64_t msiz = 0; + TCMAP *info = tcsysinfo(); + if (info) { + msiz = tcatoi(tcmapget4(info, "total", "0")); + tcmapdel(info); + } + if (!tctdbsetinvcache(tdb, msiz >= (1 << 30) ? msiz / 4 : 0, 1.0)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOWRITER | omode)) { + printerr(tdb); + tctdbdel(tdb); + return 1; + } + bool err = false; + if (!tctdbsetindex(tdb, name, type)) { + printerr(tdb); + err = true; + } + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + return err ? 1 : 0; +} + +/* perform importtsv command */ +static int procimporttsv(const char *path, const char *file, int omode, bool sc) { + FILE *ifp = file ? fopen(file, "rb") : stdin; + if (!ifp) { + fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)"); + return 1; + } + TCTDB *tdb = tctdbnew(); + if (!INVALIDHANDLE(g_dbgfd)) tctdbsetdbgfd(tdb, g_dbgfd); + if (!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); + int64_t msiz = 0; + TCMAP *info = tcsysinfo(); + if (info) { + msiz = tcatoi(tcmapget4(info, "total", "0")); + tcmapdel(info); + } + if (!tctdbsetinvcache(tdb, msiz >= (1 << 30) ? msiz / 4 : 0, 1.0)) printerr(tdb); + if (!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | omode)) { + printerr(tdb); + tctdbdel(tdb); + if (ifp != stdin) fclose(ifp); + return 1; + } + bool err = false; + char *line, numbuf[TCNUMBUFSIZ]; + int cnt = 0; + while (!err && (line = mygetline(ifp)) != NULL) { + char *pv = strchr(line, '\t'); + if (!pv) { + tcfree(line); + continue; + } + *pv = '\0'; + if (sc) tcstrutfnorm(line, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); + const char *pkey; + if (*line == '\0') { + sprintf(numbuf, "%" PRIdMAX "", (int64_t) tctdbgenuid(tdb)); + pkey = numbuf; + } else { + pkey = line; + } + if (!tctdbput3(tdb, pkey, pv + 1)) { + printerr(tdb); + err = true; + } + tcfree(line); + if (cnt > 0 && cnt % 100 == 0) { + putchar('.'); + fflush(stdout); + if (cnt % 5000 == 0) printf(" (%08d)\n", cnt); + } + cnt++; + } + printf(" (%08d)\n", cnt); + if (!tctdbclose(tdb)) { + if (!err) printerr(tdb); + err = true; + } + tctdbdel(tdb); + if (ifp != stdin) fclose(ifp); + return err ? 1 : 0; +} + +/* perform version command */ +static int procversion(void) { + printf("Tokyo Cabinet version %s (%d:%s) for %s\n", + tcversion, _TC_LIBVER, _TC_FORMATVER, TCSYSNAME); + printf("Copyright (C) 2006-2012 FAL Labs\n"); + return 0; +} + + + +// END OF FILE |