1 /*************************************************************************************************
2  * The core API of Tokyo Dystopia
3  *                                                               Copyright (C) 2007-2010 FAL Labs
4  * This file is part of Tokyo Dystopia.
5  * Tokyo Dystopia is free software; you can redistribute it and/or modify it under the terms of
6  * the GNU Lesser General Public License as published by the Free Software Foundation; either
7  * version 2.1 of the License or any later version.  Tokyo Dystopia is distributed in the hope
8  * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
10  * License for more details.
11  * You should have received a copy of the GNU Lesser General Public License along with Tokyo
12  * Dystopia; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13  * Boston, MA 02111-1307 USA.
14  *************************************************************************************************/
15 
16 
17 #include "dystopia.h"
18 #include "myconf.h"
19 
20 #define IDBDIRMODE     00755             // permission of created directories
21 #define IDBIOBUFSIZ    65536             // size of an I/O buffer
22 #define IDBDEFFWMMAX   2048              // default maximum number forward matching expansion
23 #define IDBTXDBNAME    "dystopia.tch"    // name of the text database
24 #define IDBTXDBMAGIC   0x49              // magic data for identification
25 
26 #define IDBDEFERNUM    1000000           // default expected record number
27 #define IDBDEFETNUM    1000000           // default expected token number
28 #define IDBDEFIUSIZ    (1024LL*1024*512) // default unit size of each index file
29 #define IDBTXBNUMCO    2                 // coefficient of the bucket number
30 #define IDBTXAPOW      3                 // alignment power of the text database
31 #define IDBTXFPOW      10                // free block pool power of the text database
32 
33 
34 /* private function prototypes */
35 static bool tcidblockmethod(TCIDB *idb, bool wr);
36 static bool tcidbunlockmethod(TCIDB *idb);
37 static bool tcidbsynccb(int total, int current, const char *msg, TCIDB *idb);
38 static bool tcidbopenimpl(TCIDB *idb, const char *path, int omode);
39 static bool tcidbcloseimpl(TCIDB *idb);
40 static bool tcidbputimpl(TCIDB *idb, int64_t id, const char *text);
41 static bool tcidboutimpl(TCIDB *idb, int64_t id);
42 static char *tcidbgetimpl(TCIDB *idb, int64_t id);
43 static uint64_t *tcidbsearchimpl(TCIDB *idb, const char *word, int smode, int *np);
44 static uint64_t *tcidbsearchtoken(TCIDB *idb, const char *token, int *np);
45 static bool tcidboptimizeimpl(TCIDB *idb);
46 static bool tcidbvanishimpl(TCIDB *idb);
47 static bool tcidbcopyimpl(TCIDB *idb, const char *path);
48 
49 
50 
51 /*************************************************************************************************
52  * API
53  *************************************************************************************************/
54 
55 
56 /* Get the message string corresponding to an error code. */
tcidberrmsg(int ecode)57 const char *tcidberrmsg(int ecode){
58   return tchdberrmsg(ecode);
59 }
60 
61 
62 /* Create an indexed database object. */
tcidbnew(void)63 TCIDB *tcidbnew(void){
64   TCIDB *idb = tcmalloc(sizeof(*idb));
65   idb->mmtx = tcmalloc(sizeof(pthread_rwlock_t));
66   if(pthread_rwlock_init(idb->mmtx, NULL) != 0) tcmyfatal("pthread_rwlock_init failed");
67   idb->txdb = tchdbnew();
68   if(!tchdbsetmutex(idb->txdb)) tcmyfatal("tchdbsetmutex failed");
69   TCQDB **idxs = idb->idxs;
70   for(int i = 0; i < IDBQDBMAX; i++){
71     idxs[i] = tcqdbnew();
72     tcqdbsetsynccb(idxs[i], (bool (*)(int, int, const char *, void *))tcidbsynccb, idb);
73   }
74   idb->inum = 0;
75   idb->cnum = 0;
76   idb->path = NULL;
77   idb->wmode = false;
78   idb->qopts = 0;
79   idb->qomode = 0;
80   idb->ernum = IDBDEFERNUM;
81   idb->etnum = IDBDEFETNUM;
82   idb->iusiz = IDBDEFIUSIZ;
83   idb->opts = 0;
84   idb->synccb = NULL;
85   idb->syncopq = NULL;
86   idb->exopts = 0;
87   return idb;
88 }
89 
90 
91 /* Delete an indexed database object. */
tcidbdel(TCIDB * idb)92 void tcidbdel(TCIDB *idb){
93   assert(idb);
94   if(idb->path) tcidbclose(idb);
95   TCQDB **idxs = idb->idxs;
96   for(int i = IDBQDBMAX - 1; i >= 0; i--){
97     tcqdbdel(idxs[i]);
98   }
99   tchdbdel(idb->txdb);
100   pthread_rwlock_destroy(idb->mmtx);
101   tcfree(idb->mmtx);
102   tcfree(idb);
103 }
104 
105 
106 /* Get the last happened error code of an indexed database object. */
tcidbecode(TCIDB * idb)107 int tcidbecode(TCIDB *idb){
108   assert(idb);
109   return tchdbecode(idb->txdb);
110 }
111 
112 
113 /* Set the tuning parameters of an indexed database object. */
tcidbtune(TCIDB * idb,int64_t ernum,int64_t etnum,int64_t iusiz,uint8_t opts)114 bool tcidbtune(TCIDB *idb, int64_t ernum, int64_t etnum, int64_t iusiz, uint8_t opts){
115   assert(idb);
116   if(!tcidblockmethod(idb, true)) return false;
117   if(idb->path){
118     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
119     tcidbunlockmethod(idb);
120     return false;
121   }
122   idb->ernum = (ernum > 0) ? ernum : IDBDEFERNUM;
123   idb->etnum = (etnum > 0) ? etnum : IDBDEFETNUM;
124   idb->iusiz = (iusiz > 0) ? iusiz : IDBDEFIUSIZ;
125   idb->opts = opts;
126   tcidbunlockmethod(idb);
127   return true;
128 }
129 
130 
131 /* Set the caching parameters of an indexed database object. */
tcidbsetcache(TCIDB * idb,int64_t icsiz,int32_t lcnum)132 bool tcidbsetcache(TCIDB *idb, int64_t icsiz, int32_t lcnum){
133   assert(idb);
134   if(!tcidblockmethod(idb, true)) return false;
135   if(idb->path){
136     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
137     tcidbunlockmethod(idb);
138     return false;
139   }
140   TCQDB **idxs = idb->idxs;
141   for(int i = 0; i < IDBQDBMAX; i++){
142     tcqdbsetcache(idxs[i], icsiz, lcnum);
143   }
144   tcidbunlockmethod(idb);
145   return true;
146 }
147 
148 
149 /* Set the maximum number of forward matching expansion of an indexed database object. */
tcidbsetfwmmax(TCIDB * idb,uint32_t fwmmax)150 bool tcidbsetfwmmax(TCIDB *idb, uint32_t fwmmax){
151   assert(idb);
152   if(!tcidblockmethod(idb, true)) return false;
153   if(idb->path){
154     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
155     tcidbunlockmethod(idb);
156     return false;
157   }
158   TCQDB **idxs = idb->idxs;
159   for(int i = 0; i < IDBQDBMAX; i++){
160     tcqdbsetfwmmax(idxs[i], fwmmax);
161   }
162   tcidbunlockmethod(idb);
163   return true;
164 }
165 
166 
167 /* Open an indexed database object. */
tcidbopen(TCIDB * idb,const char * path,int omode)168 bool tcidbopen(TCIDB *idb, const char *path, int omode){
169   assert(idb && path);
170   if(!tcidblockmethod(idb, true)) return false;
171   if(idb->path){
172     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
173     tcidbunlockmethod(idb);
174     return false;
175   }
176   bool rv = tcidbopenimpl(idb, path, omode);
177   tcidbunlockmethod(idb);
178   return rv;
179 }
180 
181 
182 /* Close an indexed database object. */
tcidbclose(TCIDB * idb)183 bool tcidbclose(TCIDB *idb){
184   assert(idb);
185   if(!tcidblockmethod(idb, true)) return false;
186   if(!idb->path){
187     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
188     tcidbunlockmethod(idb);
189     return false;
190   }
191   bool rv = tcidbcloseimpl(idb);
192   tcidbunlockmethod(idb);
193   return rv;
194 }
195 
196 
197 /* Store a record into an indexed database object. */
tcidbput(TCIDB * idb,int64_t id,const char * text)198 bool tcidbput(TCIDB *idb, int64_t id, const char *text){
199   assert(idb && id > 0 && text);
200   if(!tcidblockmethod(idb, true)) return false;
201   if(!idb->path || !idb->wmode){
202     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
203     tcidbunlockmethod(idb);
204     return false;
205   }
206   bool rv = tcidbputimpl(idb, id, text);
207   tcidbunlockmethod(idb);
208   return rv;
209 }
210 
211 
212 /* Remove a record of an indexed database object. */
tcidbout(TCIDB * idb,int64_t id)213 bool tcidbout(TCIDB *idb, int64_t id){
214   assert(idb && id > 0);
215   if(!tcidblockmethod(idb, true)) return false;
216   if(!idb->path || !idb->wmode){
217     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
218     tcidbunlockmethod(idb);
219     return false;
220   }
221   bool rv = tcidboutimpl(idb, id);
222   tcidbunlockmethod(idb);
223   return rv;
224 }
225 
226 
227 /* Retrieve a record of an indexed database object. */
tcidbget(TCIDB * idb,int64_t id)228 char *tcidbget(TCIDB *idb, int64_t id){
229   assert(idb && id > 0);
230   if(!tcidblockmethod(idb, false)) return false;
231   if(!idb->path){
232     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
233     tcidbunlockmethod(idb);
234     return false;
235   }
236   char *rv = tcidbgetimpl(idb, id);
237   tcidbunlockmethod(idb);
238   return rv;
239 }
240 
241 
242 /* Search an indexed database. */
tcidbsearch(TCIDB * idb,const char * word,int smode,int * np)243 uint64_t *tcidbsearch(TCIDB *idb, const char *word, int smode, int *np){
244   assert(idb && word && np);
245   if(!tcidblockmethod(idb, false)) return false;
246   if(!idb->path){
247     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
248     tcidbunlockmethod(idb);
249     return false;
250   }
251   char *nword = tcstrdup(word);
252   tctextnormalize(nword, TCTNLOWER | TCTNNOACC | TCTNSPACE);
253   uint64_t *rv;
254   if(smode == IDBSTOKEN){
255     QDBRSET rsets[4];
256     char *token = tcmalloc(strlen(nword) + 3);
257     sprintf(token, "%s", nword);
258     rsets[0].ids = tcidbsearchimpl(idb, token, IDBSFULL, &rsets[0].num);
259     sprintf(token, " %s ", nword);
260     rsets[1].ids = tcidbsearchimpl(idb, token, IDBSSUBSTR, &rsets[1].num);
261     sprintf(token, "%s ", nword);
262     rsets[2].ids = tcidbsearchimpl(idb, token, IDBSPREFIX, &rsets[2].num);
263     sprintf(token, " %s", nword);
264     rsets[3].ids = tcidbsearchimpl(idb, token, IDBSSUFFIX, &rsets[3].num);
265     rv = tcqdbresunion(rsets, 4, np);
266     tcfree(rsets[3].ids);
267     tcfree(rsets[2].ids);
268     tcfree(rsets[1].ids);
269     tcfree(rsets[0].ids);
270     tcfree(token);
271   } else if(smode == IDBSTOKPRE){
272     QDBRSET rsets[2];
273     char *token = tcmalloc(strlen(nword) + 3);
274     sprintf(token, "%s", nword);
275     rsets[0].ids = tcidbsearchimpl(idb, token, IDBSPREFIX, &rsets[0].num);
276     sprintf(token, " %s", nword);
277     rsets[1].ids = tcidbsearchimpl(idb, token, IDBSSUBSTR, &rsets[1].num);
278     rv = tcqdbresunion(rsets, 2, np);
279     tcfree(rsets[1].ids);
280     tcfree(rsets[0].ids);
281     tcfree(token);
282   } else if(smode == IDBSTOKSUF){
283     QDBRSET rsets[2];
284     char *token = tcmalloc(strlen(nword) + 3);
285     sprintf(token, "%s", nword);
286     rsets[0].ids = tcidbsearchimpl(idb, token, IDBSSUFFIX, &rsets[0].num);
287     sprintf(token, "%s ", nword);
288     rsets[1].ids = tcidbsearchimpl(idb, token, IDBSSUBSTR, &rsets[1].num);
289     rv = tcqdbresunion(rsets, 2, np);
290     tcfree(rsets[1].ids);
291     tcfree(rsets[0].ids);
292     tcfree(token);
293   } else {
294     rv = tcidbsearchimpl(idb, nword, smode, np);
295   }
296   tcfree(nword);
297   tcidbunlockmethod(idb);
298   return rv;
299 }
300 
301 
302 /* Search an indexed database with a compound expression. */
tcidbsearch2(TCIDB * idb,const char * expr,int * np)303 uint64_t *tcidbsearch2(TCIDB *idb, const char *expr, int *np){
304   assert(idb && expr && np);
305   TCLIST *terms = tclistnew();
306   char *nexpr = tcstrdup(expr);
307   tctextnormalize(nexpr, TCTNSPACE);
308   const char *rp = nexpr;
309   while(*rp != '\0'){
310     if(*rp == ' '){
311       rp++;
312       while(*rp == ' '){
313         rp++;
314       }
315     } else if(*rp == '"'){
316       const char *pv = rp;
317       rp++;
318       while(*rp != '\0' && !(*rp == '"' && *(++rp) != '"')){
319         rp++;
320       }
321       if(*rp == '"') rp++;
322       tclistpush(terms, pv, rp - pv);
323     } else if(rp[0] == '[' && rp[1] == '['){
324       const char *pv = rp;
325       rp += 2;
326       while(*rp != '\0' && !(rp[0] == ']' && rp[1] == ']')){
327         rp++;
328       }
329       if(rp[0] == ']' && rp[1] == ']') rp += 2;
330       tclistpush(terms, pv, rp - pv);
331     } else {
332       const char *pv = rp;
333       rp++;
334       while(*rp != '\0' && *rp != ' ' && *rp != '"'){
335         rp++;
336       }
337       tclistpush(terms, pv, rp - pv);
338     }
339   }
340   tcfree(nexpr);
341   int tnum = tclistnum(terms);
342   if(tnum < 1){
343     tclistdel(terms);
344     *np = 0;
345     return tcmalloc(1);
346   }
347   if(tnum == 1){
348     uint64_t *res = tcidbsearchtoken(idb, tclistval2(terms, 0), np);
349     tclistdel(terms);
350     return res;
351   }
352   QDBRSET *rsets = tcmalloc(tnum * sizeof(*rsets));
353   int rsnum = 0;
354   bool sign = true;
355   int ti = 0;
356   while(ti < tnum){
357     const char *term = tclistval2(terms, ti);
358     if(!strcmp(term, "&&") || !strcmp(term, "||")){
359       sign = true;
360     } else if(!strcmp(term, "!!")){
361       sign = false;
362     } else {
363       rsets[rsnum].ids = tcidbsearchtoken(idb, term, &rsets[rsnum].num);
364       int rsover = 0;
365       while(ti + 2 < tnum && !strcmp(tclistval2(terms, ti + 1), "||")){
366         rsover++;
367         int ri = rsnum + rsover;
368         rsets[ri].ids = tcidbsearchtoken(idb, tclistval2(terms, ti + 2), &rsets[ri].num);
369         ti += 2;
370       }
371       if(rsover > 0){
372         int rnum;
373         uint64_t *res = tcqdbresunion(rsets + rsnum, rsover + 1, &rnum);
374         for(int i = 0; i <= rsover; i++){
375           tcfree(rsets[rsnum+i].ids);
376         }
377         rsets[rsnum].ids = res;
378         rsets[rsnum].num = rnum;
379       }
380       if(!sign) rsets[rsnum].num *= -1;
381       rsnum++;
382       sign = true;
383     }
384     ti++;
385   }
386   uint64_t *res;
387   int rnum;
388   while(rsnum > 1){
389     if(rsets[0].num < 0) rsets[0].num = 0;
390     int unum = 0;
391     for(int i = 1; i < rsnum; i++){
392       if(rsets[i].num < 0) break;
393       unum++;
394     }
395     if(unum > 0){
396       res = tcqdbresisect(rsets, unum + 1, &rnum);
397       for(int i = 0; i <= unum; i++){
398         tcfree(rsets[i].ids);
399       }
400       rsets[0].ids = res;
401       rsets[0].num = rnum;
402       memmove(rsets + 1, rsets + unum + 1, (rsnum - unum - 1) * sizeof(*rsets));
403       rsnum -= unum;
404     }
405     if(rsnum > 1){
406       unum = 0;
407       for(int i = 1; i < rsnum; i++){
408         if(rsets[i].num >= 0) break;
409         rsets[i].num *= -1;
410         unum++;
411       }
412       if(unum > 0){
413         res = tcqdbresdiff(rsets, unum + 1, &rnum);
414         for(int i = 0; i <= unum; i++){
415           tcfree(rsets[i].ids);
416         }
417         rsets[0].ids = res;
418         rsets[0].num = rnum;
419         memmove(rsets + 1, rsets + unum + 1, (rsnum - unum - 1) * sizeof(*rsets));
420         rsnum -= unum;
421       }
422     }
423   }
424   if(rsnum < 1){
425     res = tcmalloc(1);
426     rnum = 0;
427   } else {
428     if(!rsets[0].ids || rsets[0].num < 0) rsets[0].num = 0;
429     res = rsets[0].ids;
430     rnum = rsets[0].num;
431     rsnum--;
432   }
433   for(int i = 0; i < rsnum; i++){
434     tcfree(rsets[i].ids);
435   }
436   tcfree(rsets);
437   tclistdel(terms);
438   *np = rnum;
439   return res;
440 }
441 
442 
443 /* Initialize the iterator of an indexed database object. */
tcidbiterinit(TCIDB * idb)444 bool tcidbiterinit(TCIDB *idb){
445   assert(idb);
446   if(!tcidblockmethod(idb, true)) return false;
447   if(!idb->path){
448     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
449     tcidbunlockmethod(idb);
450     return false;
451   }
452   bool rv = tchdbiterinit(idb->txdb);
453   tcidbunlockmethod(idb);
454   return rv;
455 }
456 
457 
458 /* Get the next ID number of the iterator of an indexed database object. */
tcidbiternext(TCIDB * idb)459 uint64_t tcidbiternext(TCIDB *idb){
460   assert(idb);
461   if(!tcidblockmethod(idb, true)) return false;
462   if(!idb->path){
463     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
464     tcidbunlockmethod(idb);
465     return false;
466   }
467   uint64_t rv = 0;
468   int vsiz;
469   char *vbuf = tchdbiternext(idb->txdb, &vsiz);
470   if(vbuf){
471     TDREADVNUMBUF64(vbuf, rv, vsiz);
472     tcfree(vbuf);
473   }
474   tcidbunlockmethod(idb);
475   return rv;
476 }
477 
478 
479 /* Synchronize updated contents of an indexed database object with the files and the device. */
tcidbsync(TCIDB * idb)480 bool tcidbsync(TCIDB *idb){
481   assert(idb);
482   if(!tcidblockmethod(idb, true)) return false;
483   if(!idb->path || !idb->wmode){
484     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
485     tcidbunlockmethod(idb);
486     return false;
487   }
488   bool rv = tcidbmemsync(idb, 2);
489   tcidbunlockmethod(idb);
490   return rv;
491 }
492 
493 
494 /* Optimize the files of an indexed database object. */
tcidboptimize(TCIDB * idb)495 bool tcidboptimize(TCIDB *idb){
496   assert(idb);
497   if(!tcidblockmethod(idb, true)) return false;
498   if(!idb->path || !idb->wmode){
499     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
500     tcidbunlockmethod(idb);
501     return false;
502   }
503   bool rv = tcidboptimizeimpl(idb);
504   tcidbunlockmethod(idb);
505   return rv;
506 }
507 
508 
509 /* Remove all records of an indexed database object. */
tcidbvanish(TCIDB * idb)510 bool tcidbvanish(TCIDB *idb){
511   assert(idb);
512   if(!tcidblockmethod(idb, true)) return false;
513   if(!idb->path || !idb->wmode){
514     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
515     tcidbunlockmethod(idb);
516     return false;
517   }
518   bool rv = tcidbvanishimpl(idb);
519   tcidbunlockmethod(idb);
520   return rv;
521 }
522 
523 
524 /* Copy the database directory of an indexed database object. */
tcidbcopy(TCIDB * idb,const char * path)525 bool tcidbcopy(TCIDB *idb, const char *path){
526   assert(idb);
527   if(!tcidblockmethod(idb, false)) return false;
528   if(!idb->path || !idb->wmode){
529     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
530     tcidbunlockmethod(idb);
531     return false;
532   }
533   bool rv = tcidbcopyimpl(idb, path);
534   tcidbunlockmethod(idb);
535   return rv;
536 }
537 
538 
539 /* Get the directory path of an indexed database object. */
tcidbpath(TCIDB * idb)540 const char *tcidbpath(TCIDB *idb){
541   assert(idb);
542   return idb->path;
543 }
544 
545 
546 /* Get the number of records of an indexed database object. */
tcidbrnum(TCIDB * idb)547 uint64_t tcidbrnum(TCIDB *idb){
548   assert(idb);
549   if(!tcidblockmethod(idb, false)) return false;
550   if(!idb->path){
551     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
552     tcidbunlockmethod(idb);
553     return 0;
554   }
555   uint64_t rv = tchdbrnum(idb->txdb);
556   tcidbunlockmethod(idb);
557   return rv;
558 }
559 
560 
561 /* Get the total size of the database files of an indexed database object. */
tcidbfsiz(TCIDB * idb)562 uint64_t tcidbfsiz(TCIDB *idb){
563   assert(idb);
564   if(!tcidblockmethod(idb, false)) return false;
565   if(!idb->path){
566     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
567     tcidbunlockmethod(idb);
568     return 0;
569   }
570   uint64_t rv = tchdbfsiz(idb->txdb);
571   TCQDB **idxs = idb->idxs;
572   uint8_t inum = idb->inum;
573   for(int i = 0; i < inum; i++){
574     rv += tcqdbfsiz(idxs[i]);
575   }
576   tcidbunlockmethod(idb);
577   return rv;
578 }
579 
580 
581 
582 /*************************************************************************************************
583  * features for experts
584  *************************************************************************************************/
585 
586 
587 /* Set the file descriptor for debugging output. */
tcidbsetdbgfd(TCIDB * idb,int fd)588 void tcidbsetdbgfd(TCIDB *idb, int fd){
589   assert(idb);
590   tchdbsetdbgfd(idb->txdb, fd);
591   TCQDB **idxs = idb->idxs;
592   for(int i = 0; i < IDBQDBMAX; i++){
593     tcqdbsetdbgfd(idxs[i], fd);
594   }
595 }
596 
597 
598 /* Get the file descriptor for debugging output. */
tcidbdbgfd(TCIDB * idb)599 int tcidbdbgfd(TCIDB *idb){
600   assert(idb);
601   return tchdbdbgfd(idb->txdb);
602 }
603 
604 
605 /* Synchronize updating contents on memory of an indexed database object. */
tcidbmemsync(TCIDB * idb,int level)606 bool tcidbmemsync(TCIDB *idb, int level){
607   assert(idb);
608   if(!idb->path || !idb->wmode){
609     tchdbsetecode(idb->txdb, TCEINVALID, __FILE__, __LINE__, __func__);
610     return false;
611   }
612   TCHDB *txdb = idb->txdb;
613   TCQDB **idxs = idb->idxs;
614   uint8_t inum = idb->inum;
615   char *txopq = tchdbopaque(txdb);
616   *(uint8_t *)(txopq + sizeof(uint8_t)) = inum;
617   bool err = false;
618   if(!tchdbmemsync(txdb, false)) err = true;
619   for(int i = 0; i < inum; i++){
620     if(!tcqdbmemsync(idxs[i], level)){
621       tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
622       err = true;
623     }
624   }
625   return !err;
626 }
627 
628 
629 /* Get the inode number of the database file of an indexed database object. */
tcidbinode(TCIDB * idb)630 uint64_t tcidbinode(TCIDB *idb){
631   assert(idb);
632   return tchdbinode(idb->txdb);
633 }
634 
635 
636 /* Get the modification time of the database file of an indexed database object. */
tcidbmtime(TCIDB * idb)637 time_t tcidbmtime(TCIDB *idb){
638   assert(idb);
639   return tchdbmtime(idb->txdb);
640 }
641 
642 
643 /* Get the options of an indexed database object. */
tcidbopts(TCIDB * idb)644 uint8_t tcidbopts(TCIDB *idb){
645   assert(idb);
646   return idb->opts;
647 }
648 
649 
650 /* Set the callback function for sync progression of an indexed database object. */
tcidbsetsynccb(TCIDB * idb,bool (* cb)(int,int,const char *,void *),void * opq)651 void tcidbsetsynccb(TCIDB *idb, bool (*cb)(int, int, const char *, void *), void *opq){
652   assert(idb);
653   idb->synccb = cb;
654   idb->syncopq = opq;
655 }
656 
657 
658 /* Set the expert options of an indexed database object. */
tcidbsetexopts(TCIDB * idb,uint32_t exopts)659 void tcidbsetexopts(TCIDB *idb, uint32_t exopts){
660   assert(idb);
661   idb->exopts = exopts;
662 }
663 
664 
665 
666 /*************************************************************************************************
667  * private features
668  *************************************************************************************************/
669 
670 
671 /* Lock a method of the indexed database object.
672    `idb' specifies the indexed database object.
673    `wr' specifies whether the lock is writer or not.
674    If successful, the return value is true, else, it is false. */
tcidblockmethod(TCIDB * idb,bool wr)675 static bool tcidblockmethod(TCIDB *idb, bool wr){
676   assert(idb);
677   if(wr ? pthread_rwlock_wrlock(idb->mmtx) != 0 : pthread_rwlock_rdlock(idb->mmtx) != 0){
678     tchdbsetecode(idb->txdb, TCETHREAD, __FILE__, __LINE__, __func__);
679     return false;
680   }
681   return true;
682 }
683 
684 
685 /* Unlock a method of the indexed database object.
686    `idb' specifies the indexed database object.
687    If successful, the return value is true, else, it is false. */
tcidbunlockmethod(TCIDB * idb)688 static bool tcidbunlockmethod(TCIDB *idb){
689   assert(idb);
690   if(pthread_rwlock_unlock(idb->mmtx) != 0){
691     tchdbsetecode(idb->txdb, TCETHREAD, __FILE__, __LINE__, __func__);
692     return false;
693   }
694   return true;
695 }
696 
697 
698 /* Call the callback for sync progression.
699    `total' specifies the number of tokens to be synchronized.
700    `current' specifies the number of processed tokens.
701    `msg' specifies the message string.
702    `idb' specifies the indexed database object.
703    The return value is true usually, or false if the sync operation should be terminated. */
tcidbsynccb(int total,int current,const char * msg,TCIDB * idb)704 static bool tcidbsynccb(int total, int current, const char *msg, TCIDB *idb){
705   bool rv = idb->synccb ? idb->synccb(total, current, msg, idb->syncopq) : true;
706   if((total|current) == 0 && !strcmp(msg, QDBSYNCMSGL) &&
707      tcqdbfsiz(idb->idxs[idb->cnum]) >= idb->iusiz && idb->inum > 0){
708     TCQDB **idxs = idb->idxs;
709     if(idb->synccb && !idb->synccb(total, current, "to be cycled", idb->syncopq)) rv = false;
710     if(!tcqdbcacheclear(idxs[idb->cnum])){
711       tchdbsetecode(idb->txdb, tcqdbecode(idxs[idb->cnum]), __FILE__, __LINE__, __func__);
712       rv = false;
713     }
714     int inum = idb->inum;
715     idb->cnum = 0;
716     uint64_t min = UINT64_MAX;
717     for(int i = 0; i < inum; i++){
718       uint64_t fsiz = tcqdbfsiz(idxs[i]);
719       if(fsiz < min){
720         idb->cnum = i;
721         min = fsiz;
722       }
723     }
724     if(min > idb->iusiz && inum < IDBQDBMAX) idb->cnum = inum;
725   }
726   return rv;
727 }
728 
729 
730 /* Open an indexed database object.
731    `idb' specifies the indexed database object.
732    `path' specifies the path of the database file.
733    `omode' specifies the connection mode.
734    If successful, the return value is true, else, it is false. */
tcidbopenimpl(TCIDB * idb,const char * path,int omode)735 static bool tcidbopenimpl(TCIDB *idb, const char *path, int omode){
736   assert(idb && path);
737   char pbuf[strlen(path)+TDNUMBUFSIZ];
738   if(omode & IDBOWRITER){
739     if(omode & IDBOCREAT){
740       if(mkdir(path, IDBDIRMODE) == -1 && errno != EEXIST){
741         int ecode = TCEMKDIR;
742         switch(errno){
743           case EACCES: ecode = TCENOPERM; break;
744           case ENOENT: ecode = TCENOFILE; break;
745         }
746         tchdbsetecode(idb->txdb, ecode, __FILE__, __LINE__, __func__);
747         return false;
748       }
749     }
750     if(omode & IDBOTRUNC){
751       sprintf(pbuf, "%s%c%s", path, MYPATHCHR, IDBTXDBNAME);
752       if(unlink(pbuf) == -1 && errno != ENOENT){
753         tchdbsetecode(idb->txdb, TCEUNLINK, __FILE__, __LINE__, __func__);
754         return false;
755       }
756       for(int i = 0; i < IDBQDBMAX; i++){
757         sprintf(pbuf, "%s%c%04d", path, MYPATHCHR, i + 1);
758         if(unlink(pbuf) == -1 && errno != ENOENT){
759           tchdbsetecode(idb->txdb, TCEUNLINK, __FILE__, __LINE__, __func__);
760           return false;
761         }
762       }
763     }
764   }
765   struct stat sbuf;
766   if(stat(path, &sbuf) == -1){
767     int ecode = TCEOPEN;
768     switch(errno){
769       case EACCES: ecode = TCENOPERM; break;
770       case ENOENT: ecode = TCENOFILE; break;
771     }
772     tchdbsetecode(idb->txdb, ecode, __FILE__, __LINE__, __func__);
773     return false;
774   }
775   if(!S_ISDIR(sbuf.st_mode)){
776     tchdbsetecode(idb->txdb, TCEMISC, __FILE__, __LINE__, __func__);
777     return false;
778   }
779   TCHDB *txdb = idb->txdb;
780   TCQDB **idxs = idb->idxs;
781   int homode = HDBOREADER;
782   uint8_t hopts = 0;
783   int qomode = QDBOREADER;
784   uint8_t qopts = 0;
785   int64_t etnum = idb->etnum;
786   int64_t iusiz = idb->iusiz;
787   if(omode & IDBOWRITER){
788     homode = HDBOWRITER;
789     qomode = QDBOWRITER;
790     if(omode & IDBOCREAT){
791       homode |= HDBOCREAT;
792       qomode |= QDBOCREAT;
793     }
794     if(omode & IDBOTRUNC){
795       homode |= HDBOTRUNC;
796       qomode |= QDBOTRUNC;
797     }
798     int64_t bnum = idb->ernum * IDBTXBNUMCO + 1;
799     if(idb->opts & IDBTLARGE){
800       hopts |= HDBTLARGE;
801       qopts |= QDBTLARGE;
802     }
803     if(idb->opts & IDBTDEFLATE) qopts |= QDBTDEFLATE;
804     if(idb->opts & IDBTBZIP) qopts |= QDBTBZIP;
805     if(idb->opts & IDBTTCBS){
806       hopts |= HDBTTCBS;
807       qopts |= QDBTTCBS;
808     }
809     if(idb->exopts & IDBXNOTXT){
810       if(!tchdbtune(txdb, 1, 0, 0, 0)) return false;
811     } else {
812       if(!tchdbtune(txdb, bnum, IDBTXAPOW, IDBTXFPOW, hopts)) return false;
813     }
814   }
815   if(omode & IDBONOLCK){
816     homode |= HDBONOLCK;
817     qomode |= QDBONOLCK;
818   }
819   if(omode & IDBOLCKNB){
820     homode |= HDBOLCKNB;
821     qomode |= QDBOLCKNB;
822   }
823   sprintf(pbuf, "%s%c%s", path, MYPATHCHR, IDBTXDBNAME);
824   if(!tchdbopen(txdb, pbuf, homode)) return false;
825   char *txopq = tchdbopaque(txdb);
826   uint8_t magic = *(uint8_t *)txopq;
827   if(magic == 0 && (omode & IDBOWRITER)){
828     *(uint8_t *)txopq = IDBTXDBMAGIC;
829     *(uint8_t *)(txopq + sizeof(magic) + sizeof(uint8_t)) = qopts;
830     uint64_t llnum = TDHTOILL(etnum);
831     memcpy(txopq + sizeof(magic) + sizeof(uint8_t) + sizeof(qopts), &llnum, sizeof(llnum));
832     llnum = TDHTOILL(iusiz);
833     memcpy(txopq + sizeof(magic) + sizeof(uint8_t) + sizeof(qopts) + sizeof(llnum),
834            &llnum, sizeof(llnum));
835   } else {
836     qopts = *(uint8_t *)(txopq + sizeof(magic) + sizeof(uint8_t));
837     memcpy(&etnum, txopq + sizeof(magic) + sizeof(uint8_t) + sizeof(qopts), sizeof(etnum));
838     etnum = TDITOHLL(etnum);
839     memcpy(&iusiz, txopq + sizeof(magic) + sizeof(uint8_t) + sizeof(qopts) + sizeof(etnum),
840            sizeof(iusiz));
841     iusiz = TDITOHLL(iusiz);
842   }
843   if(omode & IDBOWRITER){
844     for(int i = 0; i < IDBQDBMAX; i++){
845       if(!tcqdbtune(idxs[i], etnum, qopts)){
846         tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
847         return false;
848       }
849     }
850   }
851   idb->opts = 0;
852   if(qopts & QDBTLARGE) idb->opts |= QDBTLARGE;
853   if(qopts & QDBTDEFLATE) idb->opts |= QDBTDEFLATE;
854   if(qopts & QDBTBZIP) idb->opts |= QDBTBZIP;
855   if(qopts & QDBTTCBS) idb->opts |= IDBTTCBS;
856   uint8_t inum;
857   memcpy(&inum, txopq + sizeof(magic), sizeof(inum));
858   if(inum > IDBQDBMAX){
859     tchdbclose(txdb);
860     tchdbsetecode(txdb, TCEMETA, __FILE__, __LINE__, __func__);
861     return false;
862   }
863   idb->cnum = 0;
864   uint64_t min = UINT64_MAX;
865   for(int i = 0; i < inum; i++){
866     sprintf(pbuf, "%s%c%04d", path, MYPATHCHR, i + 1);
867     if(!tcqdbopen(idxs[i], pbuf, qomode)){
868       tchdbclose(txdb);
869       tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
870       for(int j = i - 1; j >= 0; j--){
871         tcqdbclose(idxs[i]);
872       }
873       return false;
874     }
875     uint64_t fsiz = tcqdbfsiz(idxs[i]);
876     if(fsiz < min){
877       idb->cnum = i;
878       min = fsiz;
879     }
880   }
881   idb->inum = inum;
882   idb->path = tcstrdup(path);
883   idb->wmode = omode & IDBOWRITER;
884   idb->qopts = qopts;
885   idb->qomode = qomode;
886   idb->iusiz = iusiz;
887   return true;
888 }
889 
890 
891 /* Close an indexed database object.
892    `idb' specifies the indexed database object.
893    If successful, the return value is true, else, it is false. */
tcidbcloseimpl(TCIDB * idb)894 static bool tcidbcloseimpl(TCIDB *idb){
895   assert(idb);
896   bool err = false;
897   TCHDB *txdb = idb->txdb;
898   TCQDB **idxs = idb->idxs;
899   uint8_t inum = idb->inum;
900   if(idb->wmode){
901     char *txopq = tchdbopaque(txdb);
902     *(uint8_t *)(txopq + sizeof(uint8_t)) = inum;
903   }
904   idb->inum = 0;
905   for(int i = 0; i < inum; i++){
906     if(!tcqdbclose(idxs[i])){
907       tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
908       err = true;
909     }
910   }
911   if(!tchdbclose(txdb)) err = true;
912   tcfree(idb->path);
913   idb->path = NULL;
914   return !err;
915 }
916 
917 
918 /* Store a record into an indexed database object.
919    `idb' specifies the indexed database object.
920    `id' specifies the ID number of the record.
921    `text' specifies the string of the record.
922    If successful, the return value is true, else, it is false. */
tcidbputimpl(TCIDB * idb,int64_t id,const char * text)923 static bool tcidbputimpl(TCIDB *idb, int64_t id, const char *text){
924   assert(idb && id > 0 && text);
925   TCHDB *txdb = idb->txdb;
926   TCQDB **idxs = idb->idxs;
927   uint8_t inum = idb->inum;
928   uint8_t cnum = idb->cnum;
929   if(cnum >= inum){
930     char pbuf[strlen(idb->path)+TDNUMBUFSIZ];
931     sprintf(pbuf, "%s%c%04d", idb->path, MYPATHCHR, inum + 1);
932     TCQDB *nidx = idxs[inum];
933     if(!tcqdbopen(nidx, pbuf, idb->qomode | IDBOCREAT)){
934       tchdbsetecode(txdb, tcqdbecode(nidx), __FILE__, __LINE__, __func__);
935       return false;
936     }
937     idb->cnum = idb->inum;
938     cnum = idb->cnum;
939     idb->inum++;
940   }
941   char kbuf[TDNUMBUFSIZ];
942   int ksiz;
943   TDSETVNUMBUF64(ksiz, kbuf, id);
944   char stack[IDBIOBUFSIZ];
945   int vsiz = tchdbget3(txdb, kbuf, ksiz, stack, IDBIOBUFSIZ);
946   uint8_t ocnum;
947   if(vsiz >= (int)sizeof(ocnum)){
948     ocnum = ((uint8_t *)stack)[vsiz-1];
949     if(ocnum >= IDBQDBMAX){
950       tchdbsetecode(txdb, TCEMISC, __FILE__, __LINE__, __func__);
951       return false;
952     }
953     TCQDB *oidx = idxs[ocnum];
954     if(vsiz >= IDBIOBUFSIZ){
955       char *vbuf = tchdbget(txdb, kbuf, ksiz, &vsiz);
956       if(vbuf){
957         if(vsiz < (int)sizeof(ocnum)){
958           tchdbsetecode(txdb, TCEMISC, __FILE__, __LINE__, __func__);
959           tcfree(vbuf);
960           return false;
961         }
962         vbuf[vsiz-1] = '\0';
963         tctextnormalize(vbuf, TCTNLOWER | TCTNNOACC | TCTNSPACE);
964         if(!tcqdbout(oidx, id, vbuf)){
965           tchdbsetecode(txdb, tcqdbecode(oidx), __FILE__, __LINE__, __func__);
966           tcfree(vbuf);
967           return false;
968         }
969         tcfree(vbuf);
970       } else {
971         tchdbsetecode(txdb, TCEMISC, __FILE__, __LINE__, __func__);
972         return false;
973       }
974     } else {
975       stack[vsiz-1] = '\0';
976       tctextnormalize(stack, TCTNLOWER | TCTNNOACC | TCTNSPACE);
977       if(!tcqdbout(oidx, id, stack)){
978         tchdbsetecode(txdb, tcqdbecode(oidx), __FILE__, __LINE__, __func__);
979         return false;
980       }
981     }
982     if(!tchdbout(txdb, kbuf, ksiz)) return false;
983   }
984   int tlen = strlen(text);
985   char *vbuf = (tlen < IDBIOBUFSIZ - sizeof(cnum)) ? stack : tcmalloc(tlen + sizeof(cnum));
986   memcpy(vbuf, text, tlen);
987   ((uint8_t *)vbuf)[tlen] = cnum;
988   if(!(idb->exopts & IDBXNOTXT) && !tchdbputkeep(txdb, kbuf, ksiz, vbuf, tlen + sizeof(cnum))){
989     if(vbuf != stack) tcfree(vbuf);
990     return false;
991   }
992   vbuf[tlen] = '\0';
993   tctextnormalize(vbuf, TCTNLOWER | TCTNNOACC | TCTNSPACE);
994   TCQDB *cidx = idxs[cnum];
995   if(!tcqdbput(cidx, id, vbuf)){
996     tchdbsetecode(txdb, tcqdbecode(cidx), __FILE__, __LINE__, __func__);
997     if(vbuf != stack) tcfree(vbuf);
998     return false;
999   }
1000   if(vbuf != stack) tcfree(vbuf);
1001   return true;
1002 }
1003 
1004 
1005 /* Remove a record of an indexed database object.
1006    `idb' specifies the indexed database object.
1007    `id' specifies the ID number of the record.
1008    If successful, the return value is true, else, it is false. */
tcidboutimpl(TCIDB * idb,int64_t id)1009 static bool tcidboutimpl(TCIDB *idb, int64_t id){
1010   TCHDB *txdb = idb->txdb;
1011   TCQDB **idxs = idb->idxs;
1012   char kbuf[TDNUMBUFSIZ];
1013   int ksiz;
1014   TDSETVNUMBUF64(ksiz, kbuf, id);
1015   char stack[IDBIOBUFSIZ];
1016   int vsiz = tchdbget3(txdb, kbuf, ksiz, stack, IDBIOBUFSIZ);
1017   uint8_t ocnum;
1018   if(vsiz >= (int)sizeof(ocnum)){
1019     ocnum = ((uint8_t *)stack)[vsiz-1];
1020     if(ocnum >= IDBQDBMAX){
1021       tchdbsetecode(txdb, TCEMISC, __FILE__, __LINE__, __func__);
1022       return false;
1023     }
1024     TCQDB *oidx = idxs[ocnum];
1025     if(vsiz >= IDBIOBUFSIZ){
1026       char *vbuf = tchdbget(txdb, kbuf, ksiz, &vsiz);
1027       if(vbuf){
1028         if(vsiz < (int)sizeof(ocnum)){
1029           tchdbsetecode(txdb, TCEMISC, __FILE__, __LINE__, __func__);
1030           tcfree(vbuf);
1031           return false;
1032         }
1033         vbuf[vsiz-1] = '\0';
1034         tctextnormalize(vbuf, TCTNLOWER | TCTNNOACC | TCTNSPACE);
1035         if(!tcqdbout(oidx, id, vbuf)){
1036           tchdbsetecode(txdb, tcqdbecode(oidx), __FILE__, __LINE__, __func__);
1037           tcfree(vbuf);
1038           return false;
1039         }
1040         tcfree(vbuf);
1041       } else {
1042         tchdbsetecode(txdb, TCEMISC, __FILE__, __LINE__, __func__);
1043         return false;
1044       }
1045     } else {
1046       stack[vsiz-1] = '\0';
1047       tctextnormalize(stack, TCTNLOWER | TCTNNOACC | TCTNSPACE);
1048       if(!tcqdbout(oidx, id, stack)){
1049         tchdbsetecode(txdb, tcqdbecode(oidx), __FILE__, __LINE__, __func__);
1050         return false;
1051       }
1052     }
1053     if(!tchdbout(txdb, kbuf, ksiz)) return false;
1054     return true;
1055   }
1056   tchdbsetecode(txdb, TCENOREC, __FILE__, __LINE__, __func__);
1057   return false;
1058 }
1059 
1060 
1061 /* Retrieve a record of an indexed database object.
1062    `idb' specifies the indexed database object connected as a writer.
1063    `id' specifies the ID number of the record.  It should be positive.
1064    If successful, the return value is the string of the corresponding record, else, it is
1065    `NULL'. */
tcidbgetimpl(TCIDB * idb,int64_t id)1066 static char *tcidbgetimpl(TCIDB *idb, int64_t id){
1067   assert(idb && id > 0);
1068   char kbuf[TDNUMBUFSIZ];
1069   int ksiz;
1070   TDSETVNUMBUF64(ksiz, kbuf, id);
1071   int vsiz;
1072   char *vbuf = tchdbget(idb->txdb, kbuf, ksiz, &vsiz);
1073   if(!vbuf) return NULL;
1074   if(vsiz < (int)sizeof(uint8_t)){
1075     tcfree(vbuf);
1076     tchdbsetecode(idb->txdb, TCEMISC, __FILE__, __LINE__, __func__);
1077     return false;
1078   }
1079   vbuf[vsiz-1] = '\0';
1080   return vbuf;
1081 }
1082 
1083 
1084 /* Search an indexed database.
1085    `idb' specifies the indexed database object.
1086    `word' specifies the string of the word to be matched to.
1087    `smode' specifies the matching mode.
1088    `np' specifies the pointer to the variable into which the number of elements of the return
1089    value is assigned.
1090    If successful, the return value is the pointer to an array of ID numbers of the corresponding
1091    records.  `NULL' is returned on failure. */
tcidbsearchimpl(TCIDB * idb,const char * word,int smode,int * np)1092 static uint64_t *tcidbsearchimpl(TCIDB *idb, const char *word, int smode, int *np){
1093   assert(idb && word && np);
1094   TCQDB **idxs = idb->idxs;
1095   uint8_t inum = idb->inum;
1096   if(inum < 1){
1097     *np = 0;
1098     return tcmalloc(1);
1099   }
1100   if(inum == 1){
1101     uint64_t *res = tcqdbsearch(idxs[0], word, smode, np);
1102     if(!res) tchdbsetecode(idb->txdb, tcqdbecode(idxs[0]), __FILE__, __LINE__, __func__);
1103     return res;
1104   }
1105   QDBRSET rsets[inum];
1106   for(int i = 0; i < inum; i++){
1107     rsets[i].ids = tcqdbsearch(idxs[i], word, smode, &rsets[i].num);
1108   }
1109   uint64_t *res = tcqdbresunion(rsets, inum, np);
1110   for(int i = 0; i < inum; i++){
1111     tcfree(rsets[i].ids);
1112   }
1113   return res;
1114 }
1115 
1116 
1117 /* Search an indexed database with a token expression.
1118    `idb' specifies the indexed database object.
1119    `token' specifies the string of the token expression.
1120    `np' specifies the pointer to the variable into which the number of elements of the return
1121    value is assigned.
1122    If successful, the return value is the pointer to an array of ID numbers of the corresponding
1123    records.  `NULL' is returned on failure. */
tcidbsearchtoken(TCIDB * idb,const char * token,int * np)1124 static uint64_t *tcidbsearchtoken(TCIDB *idb, const char *token, int *np){
1125   assert(idb && token && np);
1126   int len = strlen(token);
1127   if(*token == '"'){
1128     char *bare = tcmalloc(len + 1);
1129     char *wp = bare;
1130     const char *rp = token + 1;
1131     while(*rp != '\0'){
1132       if(rp[0] == '"'){
1133         if(rp[1] == '"'){
1134           *(wp++) = '"';
1135         }
1136       } else {
1137         *(wp++) = *rp;
1138       }
1139       rp++;
1140     }
1141     *wp = '\0';
1142     uint64_t *res = tcidbsearch(idb, bare, IDBSSUBSTR, np);
1143     tcfree(bare);
1144     return res;
1145   }
1146   if(len < 4) return tcidbsearch(idb, token, IDBSSUBSTR, np);
1147   if(token[0] == '[' && token[1] == '[' && token[2] == '[' && token[3] == '['){
1148     char *bare = tcmemdup(token + 4, len - 4);
1149     uint64_t *res = tcidbsearch(idb, bare, IDBSPREFIX, np);
1150     tcfree(bare);
1151     return res;
1152   }
1153   if(token[len-1] == ']' && token[len-2] == ']' && token[len-3] == ']' && token[len-4] == ']'){
1154     char *bare = tcmemdup(token, len - 4);
1155     uint64_t *res = tcidbsearch(idb, bare, IDBSSUFFIX, np);
1156     tcfree(bare);
1157     return res;
1158   }
1159   if(token[0] != '[' || token[1] != '[' || token[len-1] != ']' || token[len-2] != ']')
1160     return tcidbsearch(idb, token, IDBSSUBSTR, np);
1161   len -= 4;
1162   char *bare = tcmemdup(token + 2, len);
1163   bool prefix = false;
1164   bool suffix = false;
1165   if(len > 0 && bare[0] == '*'){
1166     memmove(bare, bare + 1, len);
1167     len--;
1168     suffix = true;
1169   }
1170   if(len > 0 && bare[len-1] == '*'){
1171     bare[len-1] = '\0';
1172     len--;
1173     prefix = true;
1174   }
1175   if(len < 1){
1176     tcfree(bare);
1177     *np = 0;
1178     return tcmalloc(1);
1179   }
1180   int smode = IDBSTOKEN;
1181   if(prefix && suffix){
1182     smode = IDBSSUBSTR;
1183   } else if(prefix){
1184     smode = IDBSTOKPRE;
1185   } else if(suffix){
1186     smode = IDBSTOKSUF;
1187   }
1188   uint64_t *res = tcidbsearch(idb, bare, smode, np);
1189   tcfree(bare);
1190   return res;
1191 }
1192 
1193 
1194 /* Optimize the file of an indexed database object.
1195    `idb' specifies the indexed database object connected as a writer.
1196    If successful, the return value is true, else, it is false. */
tcidboptimizeimpl(TCIDB * idb)1197 static bool tcidboptimizeimpl(TCIDB *idb){
1198   assert(idb);
1199   TCHDB *txdb = idb->txdb;
1200   TCQDB **idxs = idb->idxs;
1201   uint8_t inum = idb->inum;
1202   bool err = false;
1203   if(!tchdboptimize(txdb, -1, -1, -1, UINT8_MAX)) err = true;
1204   for(int i = 0; i < inum; i++){
1205     if(!tcqdboptimize(idxs[i])){
1206       tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
1207       err = true;
1208     }
1209   }
1210   return !err;
1211 }
1212 
1213 
1214 /* Remove all records of an indexed database object.
1215    `idb' specifies the indexed database object connected as a writer.
1216    If successful, the return value is true, else, it is false. */
tcidbvanishimpl(TCIDB * idb)1217 static bool tcidbvanishimpl(TCIDB *idb){
1218   assert(idb);
1219   TCHDB *txdb = idb->txdb;
1220   TCQDB **idxs = idb->idxs;
1221   uint8_t inum = idb->inum;
1222   bool err = false;
1223   if(!tchdbvanish(txdb)) err = true;
1224   char *txopq = tchdbopaque(txdb);
1225   *(uint8_t *)(txopq + sizeof(uint8_t) + sizeof(uint8_t)) = idb->qopts;
1226   for(int i = 0; i < inum; i++){
1227     if(!tcqdbvanish(idxs[i])){
1228       tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
1229       err = true;
1230     }
1231   }
1232   return !err;
1233 }
1234 
1235 
1236 /* Copy the database directory of an indexed database object.
1237    `idb' specifies the indexed database object.
1238    `path' specifies the path of the destination directory.
1239    If successful, the return value is true, else, it is false. */
tcidbcopyimpl(TCIDB * idb,const char * path)1240 static bool tcidbcopyimpl(TCIDB *idb, const char *path){
1241   assert(idb && path);
1242   TCHDB *txdb = idb->txdb;
1243   TCQDB **idxs = idb->idxs;
1244   uint8_t inum = idb->inum;
1245   bool err = false;
1246   if(mkdir(path, IDBDIRMODE) == -1 && errno != EEXIST){
1247     int ecode = TCEMKDIR;
1248     switch(errno){
1249       case EACCES: ecode = TCENOPERM; break;
1250       case ENOENT: ecode = TCENOFILE; break;
1251     }
1252     tchdbsetecode(txdb, ecode, __FILE__, __LINE__, __func__);
1253     return false;
1254   }
1255   char pbuf[strlen(path)+TDNUMBUFSIZ];
1256   sprintf(pbuf, "%s%c%s", path, MYPATHCHR, IDBTXDBNAME);
1257   if(!tchdbcopy(txdb, pbuf)) err = true;
1258   for(int i = 0; i < inum; i++){
1259     sprintf(pbuf, "%s%c%04d", path, MYPATHCHR, i + 1);
1260     if(!tcqdbcopy(idxs[i], pbuf)){
1261       tchdbsetecode(txdb, tcqdbecode(idxs[i]), __FILE__, __LINE__, __func__);
1262       err = true;
1263     }
1264   }
1265   return !err;
1266 }
1267 
1268 
1269 
1270 // END OF FILE
1271