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