summaryrefslogtreecommitdiff
path: root/tcejdb/src/tctdb
diff options
context:
space:
mode:
authorAnton Adamansky <adamansky@gmail.com>2015-02-18 01:11:04 +0600
committerAnton Adamansky <adamansky@gmail.com>2015-02-18 01:11:04 +0600
commit533d760e05e469db1b1fe0c84ca6090034c9d0ee (patch)
treec02bacc7d1a60ad76d9195da08ba610b26f9d5db /tcejdb/src/tctdb
parent240e72e8203dfd4eda2e75b8d974df87b1c05d3f (diff)
downloadejdb-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.c6349
-rw-r--r--tcejdb/src/tctdb/tctdb.h1162
-rw-r--r--tcejdb/src/tctdb/tests/tctmttest.c1547
-rw-r--r--tcejdb/src/tctdb/tests/tcttest.c2069
-rw-r--r--tcejdb/src/tctdb/tools/tctmgr.c1223
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