d568a57e601092d2bb4f51a9325fb85229957cc7
[platform/upstream/ejdb.git] / src / ejdb / ejdb.c
1 /**************************************************************************************************
2  *  EJDB database library http://ejdb.org
3  *  Copyright (C) 2012-2015 Softmotions Ltd <info@softmotions.com>
4  *
5  *  This file is part of EJDB.
6  *  EJDB is free software; you can redistribute it and/or modify it under the terms of
7  *  the GNU Lesser General Public License as published by the Free Software Foundation; either
8  *  version 2.1 of the License or any later version.  EJDB is distributed in the hope
9  *  that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
11  *  License for more details.
12  *  You should have received a copy of the GNU Lesser General Public License along with EJDB;
13  *  if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
14  *  Boston, MA 02111-1307 USA.
15  *************************************************************************************************/
16
17 #include "myconf.h"
18 #include "bson.h"
19 #include "ejdb_private.h"
20 #include "ejdbutl.h"
21
22 /* private macros */
23 #define JBLOCKMETHOD(JB_ejdb, JB_wr)                            \
24     ((JB_ejdb)->mmtx ? _ejdblockmethod((JB_ejdb), (JB_wr)) : true)
25 #define JBUNLOCKMETHOD(JB_ejdb)                         \
26     ((JB_ejdb)->mmtx ? _ejdbunlockmethod(JB_ejdb) : true)
27
28 #define JBCLOCKMETHOD(JB_col, JB_wr)                            \
29     ((JB_col)->mmtx ? _ejcollockmethod((JB_col), (JB_wr)) : true)
30 #define JBCUNLOCKMETHOD(JB_col)                         \
31     ((JB_col)->mmtx ? _ejcollunlockmethod(JB_col) : true)
32
33 #define JBISOPEN(JB_jb) ((JB_jb) && (JB_jb)->metadb && (JB_jb)->metadb->open) ? true : false
34
35 #define JBISVALCOLNAME(JB_cname) ((JB_cname) && \
36                                   strlen(JB_cname) < JBMAXCOLNAMELEN && \
37                                   !strchr((JB_cname), '.') && \
38                                   !strchr((JB_cname), '$'))
39
40 #define JBENSUREOPENLOCK(JB_jb, JB_lock, JB_ret)  \
41     assert(JB_jb); \
42     if (!JBLOCKMETHOD((JB_jb), (JB_lock))) return JB_ret; \
43     if (!JBISOPEN(JB_jb)) { \
44         _ejdbsetecode((JB_jb), TCEINVALID, __FILE__, __LINE__, __func__); \
45         JBUNLOCKMETHOD(JB_jb); \
46         return JB_ret; \
47     }
48
49 /* Default size of stack allocated buffer for string conversions eg. tcicaseformat() */
50 #define JBSTRINOPBUFFERSZ 512
51
52 /* Default size (16K) of tmp bson buffer on stack for field stripping in _pushstripbson() */
53 #define JBSBUFFERSZ 16384
54
55 #define JBFILEMODE 00644             // permission of created files
56
57 /* string processing/conversion flags */
58 typedef enum {
59     JBICASE = 1
60 } txtflags_t;
61
62 /* ejdb number */
63 typedef union {
64     int64_t inum;
65     double dnum;
66 } _EJDBNUM;
67
68 /* opaque data for `_bsonipathrowldr()` and `_bsonfpathrowldr()` functions */
69 typedef struct {
70     EJCOLL *coll; //current collection
71     bool icase; //ignore case normalization
72 } _BSONIPATHROWLDR;
73
74
75 /* Maximum number of objects keeped to update deffered indexes */
76 #define JBMAXDEFFEREDIDXNUM 512
77
78 /* context of deffered index updates. See `_updatebsonidx()` */
79 typedef struct {
80     bson_oid_t oid;
81     TCMAP *rmap;
82     TCMAP *imap;
83 } _DEFFEREDIDXCTX;
84
85 /* query execution context. See `_qryexecute()`*/
86 typedef struct {
87     bool imode;     //if true ifields are included otherwise excluded
88     int qflags;     //query flags (JBQRYCOUNT|JBQRYFINDONE) passed into `ejdbqryexecute()` method
89     EJCOLL *coll;   //collection
90     EJQ *q;         //query object
91     EJQF *mqf;      //main indexed query condition if exists
92     TCMAP *ifields; //include/exclude $fields
93     TCMAP *dfields; //$do fields
94     TCLIST *res;    //result set
95     TCXSTR *log;    //query debug log buffer
96     TCLIST *didxctx; //deffered indexes context
97 } _QRYCTX;
98
99
100 /* private function prototypes */
101 static void _ejdbsetecode(EJDB *jb, int ecode, const char *filename, int line, const char *func);
102 static void _ejdbsetecode2(EJDB *jb, int ecode, const char *filename, int line, const char *func, bool notfatal);
103 static bool _ejdbsetmutex(EJDB *ejdb);
104 EJDB_INLINE bool _ejdblockmethod(EJDB *ejdb, bool wr);
105 EJDB_INLINE bool _ejdbunlockmethod(EJDB *ejdb);
106 EJDB_INLINE bool _ejdbcolsetmutex(EJCOLL *coll);
107 EJDB_INLINE bool _ejcollockmethod(EJCOLL *coll, bool wr);
108 EJDB_INLINE bool _ejcollunlockmethod(EJCOLL *coll);
109 static bson_type _bsonoidkey(bson *bs, bson_oid_t *oid);
110 static char* _bsonitstrval(EJDB *jb, bson_iterator *it, int *vsz, TCLIST *tokens, txtflags_t flags);
111 static char* _bsonipathrowldr(TCLIST *tokens, const char *pkbuf, int pksz, const char *rowdata, int rowdatasz,
112                               const char *ipath, int ipathsz, void *op, int *vsz);
113 static char* _bsonfpathrowldr(TCLIST *tokens, const char *rowdata, int rowdatasz,
114                               const char *fpath, int fpathsz, void *op, int *vsz);
115 static bool _createcoldb(const char *colname, EJDB *jb, EJCOLLOPTS *opts, TCTDB** res);
116 static bool _addcoldb0(const char *colname, EJDB *jb, EJCOLLOPTS *opts, EJCOLL **res);
117 static void _delcoldb(EJCOLL *cdb);
118 static void _delqfdata(const EJQ *q, const EJQF *ejqf);
119 static bool _ejdbsavebsonimpl(EJCOLL *coll, bson *bs, bson_oid_t *oid, bool merge);
120 static bool _updatebsonidx(EJCOLL *coll, const bson_oid_t *oid, const bson *bs,
121                            const void *obsdata, int obsdatasz, TCLIST *dlist);
122 static bool _metasetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts);
123 static bool _metagetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts);
124 static bson* _metagetbson(EJDB *jb, const char *colname, int colnamesz, const char *mkey);
125 static bson* _metagetbson2(EJCOLL *coll, const char *mkey) __attribute__((unused));
126 static bool _metasetbson(EJDB *jb, const char *colname, int colnamesz,
127                          const char *mkey, bson *val, bool merge, bool mergeoverwrt);
128 static bool _metasetbson2(EJCOLL *coll, const char *mkey, bson *val, bool merge, bool mergeoverwrt);
129 static bson* _imetaidx(EJCOLL *coll, const char *ipath);
130 static bool _qrypreprocess(_QRYCTX *ctx);
131 static TCLIST* _parseqobj(EJDB *jb, EJQ *q, bson *qspec);
132 static TCLIST* _parseqobj2(EJDB *jb, EJQ *q, const void *qspecbsdata);
133 static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, TCLIST *pathStack, EJQF *pqf, int mgrp);
134 static int _ejdbsoncmp(const TCLISTDATUM *d1, const TCLISTDATUM *d2, void *opaque);
135 static bool _qrycondcheckstrand(const char *vbuf, const TCLIST *tokens);
136 static bool _qrycondcheckstror(const char *vbuf, const TCLIST *tokens);
137 static bool _qrybsvalmatch(const EJQF *qf, bson_iterator *it, bool expandarrays, int *arridx);
138 static bool _qrybsmatch(EJQF *qf, const void *bsbuf, int bsbufsz);
139 static bool _qry_and_or_match(EJCOLL *coll, EJQ *ejq, const void *pkbuf, int pkbufsz);
140 static bool _qryormatch2(EJCOLL *coll, EJQ *ejq, const void *bsbuf, int bsbufsz);
141 static bool _qryormatch3(EJCOLL *coll, EJQ *ejq, EJQ *oq, const void *bsbuf, int bsbufsz);
142 static bool _qryandmatch2(EJCOLL *coll, EJQ *ejq, const void *bsbuf, int bsbufsz);
143 static bool _qryallcondsmatch(EJQ *ejq, int anum, EJCOLL *coll, EJQF **qfs, int qfsz, const void *pkbuf, int pkbufsz);
144 static EJQ* _qryaddand(EJDB *jb, EJQ *q, const void *andbsdata);
145 static bool _qrydup(const EJQ *src, EJQ *target, uint32_t qflags);
146 static void _qrydel(EJQ *q, bool freequery);
147 static bool _pushprocessedbson(_QRYCTX *ctx, const void *bsbuf, int bsbufsz);
148 static bool _exec_do(_QRYCTX *ctx, const void *bsbuf, bson *bsout);
149 static void _qryctxclear(_QRYCTX *ctx);
150 static TCLIST* _qryexecute(EJCOLL *coll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log);
151 EJDB_INLINE void _nufetch(_EJDBNUM *nu, const char *sval, bson_type bt);
152 EJDB_INLINE int _nucmp(_EJDBNUM *nu, const char *sval, bson_type bt);
153 EJDB_INLINE int _nucmp2(_EJDBNUM *nu1, _EJDBNUM *nu2, bson_type bt);
154 static EJCOLL* _getcoll(EJDB *jb, const char *colname);
155 static bool _exportcoll(EJCOLL *coll, const char *dpath, int flags, TCXSTR *log);
156 static bool _importcoll(EJDB *jb, const char *bspath, TCLIST *cnames, int flags, TCXSTR *log);
157 static EJCOLL* _createcollimpl(EJDB *jb, const char *colname, EJCOLLOPTS *opts);
158 static bool _rmcollimpl(EJDB *jb, EJCOLL *coll, bool unlinkfile);
159 static bool _setindeximpl(EJCOLL *coll, const char *fpath, int flags, bool nolock);
160
161 extern const char *utf8proc_errmsg(ssize_t errcode);
162
163 static const bool yes = true;
164
165 const char *ejdbversion() {
166     return tcversion;
167 }
168
169 const char* ejdberrmsg(int ecode) {
170     if (ecode > -6 && ecode < 0) { //Hook for negative error codes of utf8proc library
171         return utf8proc_errmsg(ecode);
172     }
173     switch (ecode) {
174         case JBEINVALIDCOLNAME:
175             return "invalid collection name";
176         case JBEINVALIDBSON:
177             return "invalid bson object";
178         case JBEQINVALIDQCONTROL:
179             return "invalid query control field starting with '$'";
180         case JBEQINOPNOTARRAY:
181             return "$strand, $stror, $in, $nin, $bt keys require not empty array value";
182         case JBEMETANVALID:
183             return "inconsistent database metadata";
184         case JBEFPATHINVALID:
185             return "invalid JSEJDB_EXPORT const char *ejdbversion();ON field path value";
186         case JBEQINVALIDQRX:
187             return "invalid query regexp value";
188         case JBEQRSSORTING:
189             return "result set sorting error";
190         case JBEQERROR:
191             return "invalid query";
192         case JBEQUPDFAILED:
193             return "bson record update failed";
194         case JBEINVALIDBSONPK:
195             return "invalid bson _id field";
196         case JBEQONEEMATCH:
197             return "only one $elemMatch allowed in the fieldpath";
198         case JBEQINCEXCL:
199             return "$fields hint cannot mix include and exclude fields";
200         case JBEQACTKEY:
201             return "action key in $do block can be one of: $join, $slice";
202         case JBEMAXNUMCOLS:
203             return "exceeded the maximum number of collections per database: 1024";
204         case JBEEJSONPARSE:
205             return "JSON parsing failed";
206         case JBEEI:
207             return "data export/import failed";
208         case JBETOOBIGBSON:
209             return "bson size exceeds the maximum allowed size limit";
210         case JBEINVALIDCMD:
211             return "invalid ejdb command specified";
212         default:
213             return tcerrmsg(ecode);
214     }
215 }
216
217 bool ejdbisvalidoidstr(const char *oid) {
218     if (!oid) {
219         return false;
220     }
221     int i = 0;
222     for (; oid[i] != '\0' &&
223             ((oid[i] >= 0x30 && oid[i] <= 0x39) || /* 1 - 9 */
224              (oid[i] >= 0x61 && oid[i] <= 0x66)); ++i); /* a - f */
225     return (i == 24);
226 }
227
228 /* Get the last happened error code of a database object. */
229 int ejdbecode(EJDB *jb) {
230     assert(jb && jb->metadb);
231     return tctdbecode(jb->metadb);
232 }
233
234 EJDB* ejdbnew(void) {
235     EJDB *jb;
236     TCCALLOC(jb, 1, sizeof (*jb));
237     jb->metadb = tctdbnew();
238     tctdbsetmutex(jb->metadb);
239     tctdbsetcache(jb->metadb, 1024, 0, 0);
240     if (!_ejdbsetmutex(jb)) {
241         tctdbdel(jb->metadb);
242         TCFREE(jb);
243         return NULL;
244     }
245     return jb;
246 }
247
248 void ejdbdel(EJDB *jb) {
249     assert(jb && jb->metadb);
250     if (JBISOPEN(jb)) ejdbclose(jb);
251     for (int i = 0; i < jb->cdbsnum; ++i) {
252         assert(jb->cdbs[i]);
253         _delcoldb(jb->cdbs[i]);
254         TCFREE(jb->cdbs[i]);
255         jb->cdbs[i] = NULL;
256     }
257     jb->cdbsnum = 0;
258     if (jb->mmtx) {
259         pthread_rwlock_destroy(jb->mmtx);
260         TCFREE(jb->mmtx);
261     }
262     tctdbdel(jb->metadb);
263     TCFREE(jb);
264 }
265
266 bool ejdbclose(EJDB *jb) {
267     JBENSUREOPENLOCK(jb, true, false);
268     bool rv = true;
269     for (int i = 0; i < jb->cdbsnum; ++i) {
270         assert(jb->cdbs[i]);
271         JBCLOCKMETHOD(jb->cdbs[i], true);
272         if (!tctdbclose(jb->cdbs[i]->tdb)) {
273             rv = false;
274         }
275         JBCUNLOCKMETHOD(jb->cdbs[i]);
276     }
277     if (!tctdbclose(jb->metadb)) {
278         rv = false;
279     }
280     JBUNLOCKMETHOD(jb);
281     return rv;
282 }
283
284 bool ejdbisopen(EJDB *jb) {
285     return JBISOPEN(jb);
286 }
287
288 bool ejdbopen(EJDB *jb, const char *path, int mode) {
289     assert(jb && path && jb->metadb);
290     if (!JBLOCKMETHOD(jb, true)) return false;
291     if (JBISOPEN(jb)) {
292         _ejdbsetecode(jb, TCEINVALID, __FILE__, __LINE__, __func__);
293         JBUNLOCKMETHOD(jb);
294         return false;
295     }
296     bool rv = tctdbopen(jb->metadb, path, mode);
297     if (!rv) {
298         goto finish;
299     }
300     jb->cdbsnum = 0;
301     TCTDB *mdb = jb->metadb;
302     rv = tctdbiterinit(mdb);
303     if (!rv) {
304         goto finish;
305     }
306     char *colname = NULL;
307     for (int i = 0; i < mdb->hdb->rnum && (colname = tctdbiternext2(mdb)) != NULL; ++i) {
308         EJCOLL *cdb;
309         EJCOLLOPTS opts;
310         _metagetopts(jb, colname, &opts);
311         _addcoldb0(colname, jb, &opts, &cdb);
312         TCFREE(colname);
313     }
314 finish:
315     JBUNLOCKMETHOD(jb);
316     return rv;
317 }
318
319 EJCOLL* ejdbgetcoll(EJDB *jb, const char *colname) {
320     assert(colname);
321     EJCOLL *coll = NULL;
322     JBENSUREOPENLOCK(jb, false, NULL);
323     coll = _getcoll(jb, colname);
324     JBUNLOCKMETHOD(jb);
325     return coll;
326 }
327
328 TCLIST* ejdbgetcolls(EJDB *jb) {
329     assert(jb);
330     EJCOLL *coll = NULL;
331     JBENSUREOPENLOCK(jb, false, NULL);
332     TCLIST *ret = tclistnew2(jb->cdbsnum);
333     for (int i = 0; i < jb->cdbsnum; ++i) {
334         coll = jb->cdbs[i];
335         TCLISTPUSH(ret, coll, sizeof (*coll));
336     }
337     JBUNLOCKMETHOD(jb);
338     return ret;
339 }
340
341 EJCOLL* ejdbcreatecoll(EJDB *jb, const char *colname, EJCOLLOPTS *opts) {
342     assert(colname);
343     EJCOLL *coll = ejdbgetcoll(jb, colname);
344     if (coll) {
345         return coll;
346     }
347     JBENSUREOPENLOCK(jb, true, NULL);
348     coll = _createcollimpl(jb, colname, opts);
349     JBUNLOCKMETHOD(jb);
350     return coll;
351 }
352
353 bool ejdbrmcoll(EJDB *jb, const char *colname, bool unlinkfile) {
354     assert(colname);
355     JBENSUREOPENLOCK(jb, true, false);
356     bool rv = true;
357     EJCOLL *coll = _getcoll(jb, colname);
358     if (!coll) {
359         goto finish;
360     }
361     if (!JBCLOCKMETHOD(coll, true)) return false;
362     rv = _rmcollimpl(jb, coll, unlinkfile);
363     JBCUNLOCKMETHOD(coll);
364     _delcoldb(coll);
365     TCFREE(coll);
366 finish:
367     JBUNLOCKMETHOD(jb);
368     return rv;
369 }
370
371 /* Save/Update BSON  */
372 bool ejdbsavebson(EJCOLL *coll, bson *bs, bson_oid_t *oid) {
373     return ejdbsavebson2(coll, bs, oid, false);
374 }
375
376 bool ejdbsavebson2(EJCOLL *coll, bson *bs, bson_oid_t *oid, bool merge) {
377     assert(coll);
378     if (!bs || bs->err || !bs->finished) {
379         _ejdbsetecode(coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
380         return false;
381     }
382     if (!JBISOPEN(coll->jb)) {
383         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
384         return false;
385     }
386     if (!JBCLOCKMETHOD(coll, true)) return false;
387     bool rv = _ejdbsavebsonimpl(coll, bs, oid, merge);
388     JBCUNLOCKMETHOD(coll);
389     return rv;
390 }
391
392 bool ejdbsavebson3(EJCOLL *coll, const void *bsdata, bson_oid_t *oid, bool merge) {
393     bson bs;
394     bson_init_with_data(&bs, bsdata);
395     return ejdbsavebson2(coll, &bs, oid, merge);
396 }
397
398 bool ejdbrmbson(EJCOLL *coll, bson_oid_t *oid) {
399     assert(coll && oid);
400     if (!JBISOPEN(coll->jb)) {
401         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
402         return false;
403     }
404     JBCLOCKMETHOD(coll, true);
405     bool rv = true;
406     const void *olddata;
407     int olddatasz = 0;
408     TCMAP *rmap = tctdbget(coll->tdb, oid, sizeof (*oid));
409     if (!rmap) {
410         goto finish;
411     }
412     olddata = tcmapget3(rmap, JDBCOLBSON, JDBCOLBSONL, &olddatasz);
413     if (!_updatebsonidx(coll, oid, NULL, olddata, olddatasz, NULL) ||
414             !tctdbout(coll->tdb, oid, sizeof (*oid))) {
415         rv = false;
416     }
417 finish:
418     JBCUNLOCKMETHOD(coll);
419     if (rmap) {
420         tcmapdel(rmap);
421     }
422     return rv;
423 }
424
425 bson* ejdbloadbson(EJCOLL *coll, const bson_oid_t *oid) {
426     assert(coll && oid);
427     if (!JBISOPEN(coll->jb)) {
428         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
429         return NULL;
430     }
431     JBCLOCKMETHOD(coll, false);
432     bson *ret = NULL;
433     int datasz;
434     void *cdata = tchdbget(coll->tdb->hdb, oid, sizeof (*oid), &datasz);
435     if (!cdata) {
436         goto finish;
437     }
438     void *bsdata = tcmaploadone(cdata, datasz, JDBCOLBSON, JDBCOLBSONL, &datasz);
439     if (!bsdata) {
440         goto finish;
441     }
442     if (datasz <= 4) {
443         TCFREE(bsdata);
444         goto finish;
445     }
446     ret = bson_create();
447     bson_init_finished_data(ret, bsdata);
448 finish:
449     JBCUNLOCKMETHOD(coll);
450     if (cdata) {
451         TCFREE(cdata);
452     }
453     return ret;
454 }
455
456 EJQ* ejdbcreatequery(EJDB *jb, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints) {
457     assert(jb);
458     if (!qobj || qobj->err || !qobj->finished) {
459         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
460         return NULL;
461     }
462     EJQ *q;
463     TCCALLOC(q, 1, sizeof (*q));
464     if (qobj) {
465         q->qflist = _parseqobj(jb, q, qobj);
466         if (!q->qflist) {
467             goto error;
468         }
469     }
470     if (orqobjs && orqobjsnum > 0) {
471         for (int i = 0; i < orqobjsnum; ++i) {
472             bson *oqb = (orqobjs + i);
473             assert(oqb);
474             if (ejdbqueryaddor(jb, q, bson_data(oqb)) == NULL) {
475                 goto error;
476             }
477         }
478     }
479     if (hints) {
480         if (hints->err || !hints->finished) {
481             _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
482             return NULL;
483         }
484         q->hints = bson_create();
485         if (bson_copy(q->hints, hints)) {
486             goto error;
487         }
488     }
489     return q;
490 error:
491     ejdbquerydel(q);
492     return NULL;
493 }
494
495 EJQ* ejdbcreatequery2(EJDB *jb, const void *qbsdata) {
496     assert(jb);
497     if (!qbsdata) {
498         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
499         return NULL;
500     }
501     EJQ *q;
502     TCCALLOC(q, 1, sizeof (*q));
503     q->qflist = _parseqobj2(jb, q, qbsdata);
504     if (!q->qflist) {
505         goto error;
506     }
507     return q;
508 error:
509     ejdbquerydel(q);
510     return NULL;
511 }
512
513 EJQ* ejdbqueryaddor(EJDB *jb, EJQ *q, const void *orbsdata) {
514     assert(jb && q && orbsdata);
515     if (!orbsdata) {
516         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
517         return NULL;
518     }
519     EJQ *oq = ejdbcreatequery2(jb, orbsdata);
520     if (oq == NULL) {
521         return NULL;
522     }
523     if (q->orqlist == NULL) {
524         q->orqlist = tclistnew2(TCLISTINYNUM);
525     }
526     TCLISTPUSH(q->orqlist, &oq, sizeof(oq));
527     return q;
528 }
529
530 EJQ* ejdbqueryhints(EJDB *jb, EJQ *q, const void *hintsbsdata) {
531     assert(jb && q);
532     if (!hintsbsdata) {
533         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
534         return NULL;
535     }
536     bson_iterator it;
537     BSON_ITERATOR_FROM_BUFFER(&it, hintsbsdata);
538     bson *bs = bson_create_from_iterator(&it);
539     if (bs->err) {
540         bson_del(bs);
541         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
542         return NULL;
543     }
544     if (q->hints) {
545         bson_del(q->hints);
546         q->hints = NULL;
547     }
548     if (q->ifields) {
549         tcmapdel(q->ifields);
550         q->ifields = NULL;
551     }
552     q->hints = bs;
553     return q;
554 }
555
556 void ejdbquerydel(EJQ *q) {
557     _qrydel(q, true);
558 }
559
560 /** Set index */
561 bool ejdbsetindex(EJCOLL *coll, const char *fpath, int flags) {
562     return _setindeximpl(coll, fpath, flags, false);
563 }
564
565 uint32_t ejdbupdate(EJCOLL *coll, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints, TCXSTR *log) {
566     assert(coll);
567     uint32_t count = 0;
568     EJQ *q = ejdbcreatequery(coll->jb, qobj, orqobjs, orqobjsnum, hints);
569     if (q == NULL) {
570         return count;
571     }
572     ejdbqryexecute(coll, q, &count, JBQRYCOUNT, log);
573     ejdbquerydel(q);
574     return count;
575 }
576
577 EJQRESULT ejdbqryexecute(EJCOLL *coll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log) {
578     assert(coll && q && q->qflist);
579     if (!JBISOPEN(coll->jb)) {
580         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
581         return NULL;
582     }
583     JBCLOCKMETHOD(coll, (q->flags & EJQUPDATING) ? true : false);
584     _ejdbsetecode(coll->jb, TCESUCCESS, __FILE__, __LINE__, __func__);
585     if (ejdbecode(coll->jb) != TCESUCCESS) { //we are not in fatal state
586         JBCUNLOCKMETHOD(coll);
587         return NULL;
588     }
589     TCLIST *res = _qryexecute(coll, q, count, qflags, log);
590     JBCUNLOCKMETHOD(coll);
591     return res;
592 }
593
594 bson* ejdbqrydistinct(EJCOLL *coll, const char *fpath, bson *qobj, bson *orqobjs, int orqobjsnum, uint32_t *count, TCXSTR *log) {
595     assert(coll);
596     uint32_t icount = 0;
597     
598     bson *rqobj = qobj;
599     bson hints;
600     bson_init_as_query(&hints);
601     bson_append_start_object(&hints, "$fields");
602     bson_append_int(&hints, fpath, 1);
603     bson_append_finish_object(&hints);
604     bson_append_start_object(&hints, "$orderby");
605     bson_append_int(&hints, fpath, 1);
606     bson_append_finish_object(&hints);
607     bson_finish(&hints);
608
609     EJQ *q = NULL;
610     bson *rres = NULL;
611     *count = 0;
612
613     if (!rqobj) {
614         rqobj = bson_create();
615         bson_init(rqobj);
616         bson_finish(rqobj);
617     }
618     q = ejdbcreatequery(coll->jb, rqobj, orqobjs, orqobjsnum, &hints);
619     if (q == NULL) {
620         goto fail;
621     }
622     if (q->flags & EJQUPDATING) {
623         _ejdbsetecode(coll->jb, JBEQERROR, __FILE__, __LINE__, __func__);
624         goto fail;
625     }
626     TCLIST *res = ejdbqryexecute(coll, q, &icount, 0, log);
627     rres = bson_create();
628     bson_init(rres);
629
630     bson_iterator bsi[2];
631     bson_iterator *prev = NULL, *cur;
632     int biind = 0;
633     char biindstr[TCNUMBUFSIZ];
634     memset(biindstr, '\0', 32);
635     int fplen = strlen(fpath);
636     for(int i = 0; i < TCLISTNUM(res); ++i) {
637         cur = bsi + (biind & 1);
638         BSON_ITERATOR_FROM_BUFFER(cur, TCLISTVALPTR(res, i));
639         bson_type bt = bson_find_fieldpath_value2(fpath, fplen, cur);
640         if (bt == BSON_EOO) {
641             continue;
642         }
643         
644         if (prev == NULL || bson_compare_it_current(prev, cur)) {
645             bson_numstrn(biindstr, TCNUMBUFSIZ, biind);
646             bson_append_field_from_iterator2(biindstr, cur, rres);
647             prev = cur;
648             biind++;
649         }
650     }
651     bson_finish(rres);
652     tclistdel(res);
653     
654     *count = biind;
655     
656 fail:
657     if (q) {
658         ejdbquerydel(q);
659     }
660     if (rqobj != qobj) {
661         bson_del(rqobj);
662     }
663     bson_destroy(&hints);
664     return rres;
665 }
666
667 int ejdbqresultnum(EJQRESULT qr) {
668     return qr ? tclistnum(qr) : 0;
669 }
670
671 const void* ejdbqresultbsondata(EJQRESULT qr, int pos, int *size) {
672     if (!qr || pos < 0) {
673         *size = 0;
674         return NULL;
675     }
676     const void *bsdata = tclistval2(qr, pos);
677     *size = (bsdata != NULL) ? bson_size2(bsdata) : 0;
678     return bsdata;
679 }
680
681 void ejdbqresultdispose(EJQRESULT qr) {
682     if (qr) {
683         tclistdel(qr);
684     }
685 }
686
687 bool ejdbsyncoll(EJCOLL *coll) {
688     assert(coll);
689     if (!JBISOPEN(coll->jb)) {
690         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
691         return false;
692     }
693     bool rv = false;
694     if (!JBCLOCKMETHOD(coll, true)) return false;
695     rv = tctdbsync(coll->tdb);
696     JBCUNLOCKMETHOD(coll);
697     return rv;
698 }
699
700 bool ejdbsyncdb(EJDB *jb) {
701     assert(jb);
702     JBENSUREOPENLOCK(jb, true, false);
703     bool rv = true;
704     for (int i = 0; i < jb->cdbsnum; ++i) {
705         assert(jb->cdbs[i]);
706         rv = JBCLOCKMETHOD(jb->cdbs[i], true);
707         if (!rv) break;
708         rv = tctdbsync(jb->cdbs[i]->tdb);
709         JBCUNLOCKMETHOD(jb->cdbs[i]);
710         if (!rv) break;
711     }
712     JBUNLOCKMETHOD(jb);
713     return rv;
714 }
715
716 bool ejdbtranbegin(EJCOLL *coll) {
717     assert(coll);
718     if (!JBISOPEN(coll->jb)) {
719         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
720         return false;
721     }
722     for (double wsec = 1.0 / sysconf_SC_CLK_TCK; true; wsec *= 2) {
723         if (!JBCLOCKMETHOD(coll, true)) return false;
724         if (!coll->tdb->open || !coll->tdb->wmode) {
725             _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
726             JBCUNLOCKMETHOD(coll);
727             return false;
728         }
729         if (!coll->tdb->tran) break;
730         JBCUNLOCKMETHOD(coll);
731         if (wsec > 1.0) wsec = 1.0;
732         tcsleep(wsec);
733     }
734     if (!tctdbtranbeginimpl(coll->tdb)) {
735         JBCUNLOCKMETHOD(coll);
736         return false;
737     }
738     coll->tdb->tran = true;
739     JBCUNLOCKMETHOD(coll);
740     return true;
741 }
742
743 bool ejdbtrancommit(EJCOLL *coll) {
744     assert(coll);
745     if (!JBISOPEN(coll->jb)) {
746         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
747         return false;
748     }
749     if (!JBCLOCKMETHOD(coll, true)) return false;
750     if (!coll->tdb->open || !coll->tdb->wmode || !coll->tdb->tran) {
751         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
752         JBCUNLOCKMETHOD(coll);
753         return false;
754     }
755     coll->tdb->tran = false;
756     bool err = false;
757     if (!tctdbtrancommitimpl(coll->tdb)) err = true;
758     JBCUNLOCKMETHOD(coll);
759     return !err;
760 }
761
762 bool ejdbtranabort(EJCOLL *coll) {
763     assert(coll);
764     if (!JBISOPEN(coll->jb)) {
765         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
766         return false;
767     }
768     if (!JBCLOCKMETHOD(coll, true)) return false;
769     if (!coll->tdb->open || !coll->tdb->wmode || !coll->tdb->tran) {
770         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
771         JBCUNLOCKMETHOD(coll);
772         return false;
773     }
774     coll->tdb->tran = false;
775     bool err = false;
776     if (!tctdbtranabortimpl(coll->tdb)) err = true;
777     JBCUNLOCKMETHOD(coll);
778     return !err;
779 }
780
781 bool ejdbtranstatus(EJCOLL *coll, bool *txactive) {
782     assert(coll && txactive);
783     if (!JBISOPEN(coll->jb)) {
784         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
785         return false;
786     }
787     if (!JBCLOCKMETHOD(coll, true)) return false;
788     *txactive = coll->tdb->tran;
789     JBCUNLOCKMETHOD(coll);
790     return true;
791 }
792
793 static int _cmpcolls(const TCLISTDATUM *d1, const TCLISTDATUM *d2) {
794     EJCOLL *c1 = (EJCOLL*) d1->ptr;
795     EJCOLL *c2 = (EJCOLL*) d2->ptr;
796     return memcmp(c1->cname, c2->cname, MIN(c1->cnamesz, c2->cnamesz));
797 }
798
799 bson* ejdbmeta(EJDB *jb) {
800     JBENSUREOPENLOCK(jb, false, NULL);
801     char nbuff[TCNUMBUFSIZ];
802     bson *bs = bson_create();
803     bson_init(bs);
804     bson_append_string(bs, "file", jb->metadb->hdb->path);
805     bson_append_start_array(bs, "collections"); //collections
806
807     TCLIST *cols = ejdbgetcolls(jb);
808     tclistsortex(cols, _cmpcolls);
809
810     for (int i = 0; i < TCLISTNUM(cols); ++i) {
811         EJCOLL *coll = (EJCOLL*) TCLISTVALPTR(cols, i);
812         if (!JBCLOCKMETHOD(coll, false)) {
813             tclistdel(cols);
814             bson_del(bs);
815             JBUNLOCKMETHOD(jb);
816             return NULL;
817         }
818         bson_numstrn(nbuff, TCNUMBUFSIZ, i);
819         bson_append_start_object(bs, nbuff); //coll obj
820         bson_append_string_n(bs, "name", coll->cname, coll->cnamesz);
821         bson_append_string(bs, "file", coll->tdb->hdb->path);
822         bson_append_long(bs, "records", coll->tdb->hdb->rnum);
823
824         bson_append_start_object(bs, "options"); //coll.options
825         bson_append_long(bs, "buckets", coll->tdb->hdb->bnum);
826         bson_append_long(bs, "cachedrecords", coll->tdb->hdb->rcnum);
827         bson_append_bool(bs, "large", (coll->tdb->opts & TDBTLARGE));
828         bson_append_bool(bs, "compressed", (coll->tdb->opts & TDBTDEFLATE));
829         bson_append_finish_object(bs); //eof coll.options
830
831         bson_append_start_array(bs, "indexes"); //coll.indexes[]
832         for (int j = 0; j < coll->tdb->inum; ++j) {
833             TDBIDX *idx = (coll->tdb->idxs + j);
834             if (idx->type != TDBITLEXICAL &&
835                     idx->type != TDBITDECIMAL &&
836                     idx->type != TDBITTOKEN) {
837                 continue;
838             }
839             bson_numstrn(nbuff, TCNUMBUFSIZ, j);
840             bson_append_start_object(bs, nbuff); //coll.indexes.index
841             bson_append_string(bs, "field", idx->name + 1);
842             bson_append_string(bs, "iname", idx->name);
843             switch (idx->type) {
844                 case TDBITLEXICAL:
845                     bson_append_string(bs, "type", "lexical");
846                     break;
847                 case TDBITDECIMAL:
848                     bson_append_string(bs, "type", "decimal");
849                     break;
850                 case TDBITTOKEN:
851                     bson_append_string(bs, "type", "token");
852                     break;
853             }
854             TCBDB *idb = (TCBDB*) idx->db;
855             if (idb) {
856                 bson_append_long(bs, "records", idb->rnum);
857                 bson_append_string(bs, "file", idb->hdb->path);
858             }
859             bson_append_finish_object(bs); //eof coll.indexes.index
860         }
861         bson_append_finish_array(bs); //eof coll.indexes[]
862         bson_append_finish_object(bs); //eof coll
863         JBCUNLOCKMETHOD(coll);
864     }
865     bson_append_finish_array(bs); //eof collections
866     bson_finish(bs);
867     tclistdel(cols);
868     JBUNLOCKMETHOD(jb);
869     if (bs->err) {
870         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
871         bson_del(bs);
872         bs = NULL;
873     }
874     return bs;
875 }
876
877 bool ejdbexport(EJDB *jb, const char *path, TCLIST *cnames, int flags, TCXSTR *log) {
878     assert(jb && path);
879     bool err = false;
880     bool isdir = false;
881     tcstatfile(path, &isdir, NULL, NULL);
882     if (!isdir) {
883         if (mkdir(path, 00755)) {
884             _ejdbsetecode2(jb, TCEMKDIR, __FILE__, __LINE__, __func__, true);
885             return false;
886         }
887     }
888     JBENSUREOPENLOCK(jb, false, false);
889     TCLIST *_cnames = cnames;
890     if (_cnames == NULL) {
891         _cnames = tclistnew2(jb->cdbsnum);
892         for (int i = 0; i < jb->cdbsnum; ++i) {
893             EJCOLL *c = jb->cdbs[i];
894             TCLISTPUSH(_cnames, c->cname, c->cnamesz);
895         }
896     }
897     for (int i = 0; i < TCLISTNUM(_cnames); ++i) {
898         const char *cn = TCLISTVALPTR(_cnames, i);
899         assert(cn);
900         EJCOLL *coll = _getcoll(jb, cn);
901         if (!coll) continue;
902         if (!JBCLOCKMETHOD(coll, false)) {
903             err = true;
904             goto finish;
905         }
906         if (!_exportcoll(coll, path, flags, log)) {
907             err = true;
908         }
909         JBCUNLOCKMETHOD(coll);
910     }
911 finish:
912     JBUNLOCKMETHOD(jb);
913     if (_cnames != cnames) {
914         tclistdel(_cnames);
915     }
916     return !err;
917 }
918
919 bool ejdbimport(EJDB *jb, const char *path, TCLIST *cnames, int flags, TCXSTR *log) {
920     assert(jb && path);
921     bool err = false;
922     bool isdir = false;
923     if (flags == 0) {
924         flags |= JBIMPORTUPDATE;
925     }
926     if (!tcstatfile(path, &isdir, NULL, NULL) || !isdir) {
927         _ejdbsetecode2(jb, TCENOFILE, __FILE__, __LINE__, __func__, true);
928         return false;
929     }
930     bool tail = (path[0] != '\0' && path[strlen(path) - 1] == MYPATHCHR);
931     char *bsonpat = tail ? tcsprintf("%s*.bson") : tcsprintf("%s%c*.bson", path, MYPATHCHR);
932     TCLIST *bspaths = tcglobpat(bsonpat);
933     JBENSUREOPENLOCK(jb, true, false);
934     for (int i = 0; i < TCLISTNUM(bspaths); ++i) {
935         const char* bspath = TCLISTVALPTR(bspaths, i);
936         if (!_importcoll(jb, bspath, cnames, flags, log)) {
937             err = true;
938             goto finish;
939         }
940     }
941 finish:
942     JBUNLOCKMETHOD(jb);
943     if (bsonpat) {
944         TCFREE(bsonpat);
945     }
946     if (bspaths) {
947         tclistdel(bspaths);
948     }
949     return !err;
950 }
951
952 bson* ejdbcommand2(EJDB *jb, void *cmdbsondata) {
953     bson cmd;
954     bson_init_with_data(&cmd, cmdbsondata);
955     bson *bret = ejdbcommand(jb, &cmd);
956     return bret;
957 }
958
959 bson* ejdbcommand(EJDB *jb, bson *cmd) {
960     bson *ret = bson_create();
961     int ecode = 0;
962     const char *err = NULL;
963     TCXSTR *xlog = NULL;
964     TCLIST *cnames = NULL;
965     bool rv = true;
966
967     bson_init(ret);
968     bson_type bt;
969     bson_iterator it;
970     BSON_ITERATOR_INIT(&it, cmd);
971
972     while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
973         const char *key = BSON_ITERATOR_KEY(&it);
974         if (!strcmp("export", key) || !strcmp("import", key)) {
975             xlog = tcxstrnew();
976             char *path = NULL;
977             int flags = 0;
978             bson_iterator sit;
979             BSON_ITERATOR_SUBITERATOR(&it, &sit);
980             if (bson_find_fieldpath_value("path", &sit) == BSON_STRING) {
981                 path = strdup(bson_iterator_string(&sit));
982             }
983             BSON_ITERATOR_SUBITERATOR(&it, &sit);
984             if (bson_find_fieldpath_value("mode", &sit) == BSON_INT) {
985                 flags = bson_iterator_int(&sit);
986             }
987             BSON_ITERATOR_SUBITERATOR(&it, &sit);
988             if (bson_find_fieldpath_value("cnames", &sit) == BSON_ARRAY) {
989                 bson_iterator ait;
990                 BSON_ITERATOR_SUBITERATOR(&sit, &ait);
991                 while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
992                     if (bt == BSON_STRING) {
993                         if (cnames == NULL) {
994                             cnames = tclistnew();
995                         }
996                         const char *sv = bson_iterator_string(&ait);
997                         TCLISTPUSH(cnames, sv, strlen(sv));
998                     }
999                 }
1000             }
1001             if (path == NULL) {
1002                 err = "Missing required 'path' field";
1003                 ecode = JBEINVALIDCMD;
1004                 goto finish;
1005             }
1006             if (!strcmp("export", key)) {
1007                 rv = ejdbexport(jb, path, cnames, flags, xlog);
1008             } else { //import
1009                 rv = ejdbimport(jb, path, cnames, flags, xlog);
1010             }
1011             if (!rv) {
1012                 ecode = ejdbecode(jb);
1013                 err = ejdberrmsg(ecode);
1014             }
1015             TCFREE(path);
1016         } else if (!strcmp("ping", key)) {
1017             xlog = tcxstrnew();
1018             tcxstrprintf(xlog, "pong");
1019         } else {
1020             err = "Unknown command";
1021             ecode = JBEINVALIDCMD;
1022             goto finish;
1023         }
1024     }
1025 finish:
1026     if (err) {
1027         bson_append_string(ret, "error", err);
1028         bson_append_int(ret, "errorCode", ecode);
1029     }
1030     if (xlog) {
1031         bson_append_string(ret, "log", TCXSTRPTR(xlog));
1032         tcxstrdel(xlog);
1033     }
1034     if (cnames) {
1035         tclistdel(cnames);
1036     }
1037     bson_finish(ret);
1038     return ret;
1039 }
1040
1041 /*************************************************************************************************
1042  * private features
1043  *************************************************************************************************/
1044
1045 static bool _setindeximpl(EJCOLL *coll, const char *fpath, int flags, bool nolock) {
1046     assert(coll && fpath);
1047     bool rv = true;
1048     bson *imeta = NULL;
1049     bson_iterator it;
1050     int tcitype = 0; //TCDB index type
1051     int oldiflags = 0; //Old index flags stored in meta
1052     bool ibld = (flags & JBIDXREBLD);
1053     if (ibld) {
1054         flags &= ~JBIDXREBLD;
1055     }
1056     bool idrop = (flags & JBIDXDROP);
1057     if (idrop) {
1058         flags &= ~JBIDXDROP;
1059     }
1060     bool idropall = (flags & JBIDXDROPALL);
1061     if (idropall) {
1062         idrop = true;
1063         flags &= ~JBIDXDROPALL;
1064     }
1065     bool iop = (flags & JBIDXOP);
1066     if (iop) {
1067         flags &= ~JBIDXOP;
1068     }
1069     char ipath[BSON_MAX_FPATH_LEN + 2]; //add 2 bytes for one char prefix and '\0'term
1070     char ikey[BSON_MAX_FPATH_LEN + 2]; //add 2 bytes for one char prefix and '\0'term
1071     int fpathlen = strlen(fpath);
1072     if (fpathlen > BSON_MAX_FPATH_LEN) {
1073         _ejdbsetecode(coll->jb, JBEFPATHINVALID, __FILE__, __LINE__, __func__);
1074         rv = false;
1075         goto finish;
1076     }
1077     memmove(ikey + 1, fpath, fpathlen + 1);
1078     ikey[0] = 'i';
1079     memmove(ipath + 1, fpath, fpathlen + 1);
1080     ipath[0] = 's'; //defaulting to string index type
1081
1082     if (!nolock) {
1083         JBENSUREOPENLOCK(coll->jb, true, false);
1084     }
1085     imeta = _imetaidx(coll, fpath);
1086     if (!imeta) {
1087         if (idrop) { //Cannot drop/optimize not existent index;
1088             if (!nolock) {
1089                 JBUNLOCKMETHOD(coll->jb);
1090             }
1091             goto finish;
1092         }
1093         if (iop) {
1094             iop = false; //New index will be created
1095         }
1096         imeta = bson_create();
1097         bson_init(imeta);
1098         bson_append_string(imeta, "ipath", fpath);
1099         bson_append_int(imeta, "iflags", flags);
1100         bson_finish(imeta);
1101         rv = _metasetbson2(coll, ikey, imeta, false, false);
1102         if (!rv) {
1103             if (!nolock) {
1104                 JBUNLOCKMETHOD(coll->jb);
1105             }
1106             goto finish;
1107         }
1108     } else {
1109         if (bson_find(&it, imeta, "iflags") != BSON_EOO) {
1110             oldiflags = bson_iterator_int(&it);
1111         }
1112         if (!idrop && oldiflags != flags) { //Update index meta
1113             bson imetadelta;
1114             bson_init(&imetadelta);
1115             bson_append_int(&imetadelta, "iflags", (flags | oldiflags));
1116             bson_finish(&imetadelta);
1117             rv = _metasetbson2(coll, ikey, &imetadelta, true, true);
1118             bson_destroy(&imetadelta);
1119             if (!rv) {
1120                 if (!nolock) {
1121                     JBUNLOCKMETHOD(coll->jb);
1122                 }
1123                 goto finish;
1124             }
1125         }
1126     }
1127     if (!nolock) {
1128         JBUNLOCKMETHOD(coll->jb);
1129     }
1130     if (idrop) {
1131         tcitype = TDBITVOID;
1132         if (idropall && oldiflags) {
1133             flags = oldiflags; //Drop index only for existing types
1134         }
1135     } else if (iop) {
1136         tcitype = TDBITOPT;
1137         if (oldiflags) {
1138             flags = oldiflags; //Optimize index for all existing types
1139         }
1140     }
1141
1142     if (!nolock) {
1143         if (!JBCLOCKMETHOD(coll, true)) {
1144             rv = false;
1145             goto finish;
1146         }
1147     }
1148     _BSONIPATHROWLDR op;
1149     op.icase = false;
1150     op.coll = coll;
1151     if (tcitype) {
1152         if (flags & JBIDXSTR) {
1153             ipath[0] = 's';
1154             rv = tctdbsetindexrldr(coll->tdb, ipath, tcitype, _bsonipathrowldr, &op);
1155         }
1156         if (flags & JBIDXISTR) {
1157             ipath[0] = 'i';
1158             op.icase = true;
1159             rv = tctdbsetindexrldr(coll->tdb, ipath, tcitype, _bsonipathrowldr, &op);
1160         }
1161         if (rv && (flags & JBIDXNUM)) {
1162             ipath[0] = 'n';
1163             rv = tctdbsetindexrldr(coll->tdb, ipath, tcitype, _bsonipathrowldr, &op);
1164         }
1165         if (rv && (flags & JBIDXARR)) {
1166             ipath[0] = 'a';
1167             rv = tctdbsetindexrldr(coll->tdb, ipath, tcitype, _bsonipathrowldr, &op);
1168         }
1169         if (idrop) { //Update index meta on drop
1170             oldiflags &= ~flags;
1171             if (oldiflags) { //Index dropped only for some types
1172                 bson imetadelta;
1173                 bson_init(&imetadelta);
1174                 bson_append_int(&imetadelta, "iflags", oldiflags);
1175                 bson_finish(&imetadelta);
1176                 rv = _metasetbson2(coll, ikey, &imetadelta, true, true);
1177                 bson_destroy(&imetadelta);
1178             } else { //Index dropped completely
1179                 rv = _metasetbson2(coll, ikey, NULL, false, false);
1180             }
1181         }
1182     } else {
1183         if ((flags & JBIDXSTR) && (ibld || !(oldiflags & JBIDXSTR))) {
1184             ipath[0] = 's';
1185             rv = tctdbsetindexrldr(coll->tdb, ipath, TDBITLEXICAL, _bsonipathrowldr, &op);
1186         }
1187         if ((flags & JBIDXISTR) && (ibld || !(oldiflags & JBIDXISTR))) {
1188             ipath[0] = 'i';
1189             op.icase = true;
1190             rv = tctdbsetindexrldr(coll->tdb, ipath, TDBITLEXICAL, _bsonipathrowldr, &op);
1191         }
1192         if (rv && (flags & JBIDXNUM) && (ibld || !(oldiflags & JBIDXNUM))) {
1193             ipath[0] = 'n';
1194             rv = tctdbsetindexrldr(coll->tdb, ipath, TDBITDECIMAL, _bsonipathrowldr, &op);
1195         }
1196         if (rv && (flags & JBIDXARR) && (ibld || !(oldiflags & JBIDXARR))) {
1197             ipath[0] = 'a';
1198             rv = tctdbsetindexrldr(coll->tdb, ipath, TDBITTOKEN, _bsonipathrowldr, &op);
1199         }
1200     }
1201     if (!nolock) {
1202         JBCUNLOCKMETHOD(coll);
1203     }
1204 finish:
1205     if (imeta) {
1206         bson_del(imeta);
1207     }
1208     return rv;
1209 }
1210
1211 /**
1212  * In order to cleanup resources you need:
1213  *      _delcoldb(coll);
1214  *      TCFREE(coll);
1215  * after this method completion with any return status.
1216  */
1217 static bool _rmcollimpl(EJDB *jb, EJCOLL *coll, bool unlinkfile) {
1218     assert(jb && coll);
1219     bool rv = true;
1220     tctdbout(jb->metadb, coll->cname, coll->cnamesz);
1221     tctdbvanish(coll->tdb);
1222     TCLIST *paths = tclistnew2(10);
1223     tclistpush2(paths, coll->tdb->hdb->path);
1224     for (int j = 0; j < coll->tdb->inum; ++j) {
1225         TDBIDX *idx = coll->tdb->idxs + j;
1226         const char *ipath = tcbdbpath(idx->db);
1227         if (ipath) {
1228             tclistpush2(paths, ipath);
1229         }
1230     }
1231     tctdbclose(coll->tdb);
1232     if (unlinkfile) {
1233         for (int i = 0; i < TCLISTNUM(paths); ++i) {
1234             unlink(tclistval2(paths, i));
1235         }
1236     }
1237     tclistdel(paths);
1238     jb->cdbsnum--;
1239     for (int i = 0; i < EJDB_MAX_COLLECTIONS; ++i) {
1240         if (jb->cdbs[i] == coll) {
1241             jb->cdbs[i] = NULL;
1242             break;
1243         }
1244     }
1245     //collapse the NULL hole
1246     for (int i = 0; i < EJDB_MAX_COLLECTIONS - 1; ++i) {
1247         if (!jb->cdbs[i] && jb->cdbs[i + 1]) {
1248             jb->cdbs[i] = jb->cdbs[i + 1];
1249             jb->cdbs[i + 1] = NULL;
1250         }
1251     }
1252     return rv;
1253 }
1254
1255 static EJCOLL* _createcollimpl(EJDB *jb, const char *colname, EJCOLLOPTS *opts) {
1256     EJCOLL *coll = NULL;
1257     if (!JBISVALCOLNAME(colname)) {
1258         _ejdbsetecode(jb, JBEINVALIDCOLNAME, __FILE__, __LINE__, __func__);
1259         return NULL;
1260     }
1261     TCTDB *meta = jb->metadb;
1262     char *row = tcsprintf("name\t%s", colname);
1263     if (!tctdbput3(meta, colname, row)) {
1264         goto finish;
1265     }
1266     if (!_addcoldb0(colname, jb, opts, &coll)) {
1267         tctdbout2(meta, colname); //cleaning
1268         goto finish;
1269     }
1270     _metasetopts(jb, colname, opts);
1271 finish:
1272     if (row) {
1273         TCFREE(row);
1274     }
1275     return coll;
1276 }
1277
1278 static bool _importcoll(EJDB *jb, const char *bspath, TCLIST *cnames, int flags, TCXSTR *log) {
1279     if (log) {
1280         tcxstrprintf(log, "\n\nReading '%s'", bspath);
1281     }
1282     bool err = false;
1283     // /foo/bar.bson
1284     char *dp = strrchr(bspath, '.');
1285     char *pp = strrchr(bspath, MYPATHCHR);
1286     if (!dp || !pp || pp > dp) {
1287         if (log) {
1288             tcxstrprintf(log, "\nERROR: Invalid file path: '%s'", bspath);
1289         }
1290         _ejdbsetecode2(jb, JBEEI, __FILE__, __LINE__, __func__, true);
1291         return false;
1292     }
1293     char *lastsep = pp;
1294     int i = 0;
1295     char *cname, *mjson = NULL;
1296     bson_type bt;
1297     bson *mbson = NULL; //meta bson
1298     bson_iterator mbsonit;
1299     int sp;
1300     EJCOLL *coll;
1301
1302     TCMALLOC(cname, dp - pp + 1);
1303     TCXSTR *xmetapath = tcxstrnew();
1304
1305     while (++pp != dp) {
1306         cname[i++] = *pp;
1307     }
1308     cname[i] = '\0';
1309     if (cnames != NULL) {
1310         if (tclistlsearch(cnames, cname, i) == -1) {
1311             goto finish;
1312         }
1313     }
1314     tcxstrcat(xmetapath, bspath, lastsep - bspath + 1);
1315     tcxstrprintf(xmetapath, "%s-meta.json", cname);
1316     mjson = tcreadfile(tcxstrptr(xmetapath), 0, &sp);
1317     if (!mjson) {
1318         err = true;
1319         if (log) {
1320             tcxstrprintf(log, "\nERROR: Error reading the file: '%s'", tcxstrptr(xmetapath));
1321         }
1322         _ejdbsetecode2(jb, TCEREAD, __FILE__, __LINE__, __func__, true);
1323         goto finish;
1324     }
1325     mbson = json2bson(mjson);
1326     if (!mbson) {
1327         err = true;
1328         if (log) {
1329             tcxstrprintf(log, "\nERROR: Invalid JSON in the file: '%s'", tcxstrptr(xmetapath));
1330         }
1331         _ejdbsetecode2(jb, JBEEJSONPARSE, __FILE__, __LINE__, __func__, true);
1332     }
1333     coll = _getcoll(jb, cname);
1334     if (coll && (flags & JBIMPORTREPLACE)) {
1335         if (log) {
1336             tcxstrprintf(log, "\nReplacing all data in '%s'", cname);
1337         }
1338         err = !(_rmcollimpl(jb, coll, true));
1339         _delcoldb(coll);
1340         TCFREE(coll);
1341         coll = NULL;
1342         if (err) {
1343             if (log) {
1344                 tcxstrprintf(log, "\nERROR: Failed to remove collection: '%s'", cname);
1345             }
1346             goto finish;
1347         }
1348     }
1349     if (!coll) {
1350         //Build collection options
1351         BSON_ITERATOR_INIT(&mbsonit, mbson);
1352         EJCOLLOPTS cops = {0};
1353         if (bson_find_fieldpath_value("opts", &mbsonit) == BSON_OBJECT) {
1354             bson_iterator sit;
1355             BSON_ITERATOR_SUBITERATOR(&mbsonit, &sit);
1356             while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
1357                 const char *key = BSON_ITERATOR_KEY(&sit);
1358                 if (strcmp("compressed", key) == 0 && bt == BSON_BOOL) {
1359                     cops.compressed = bson_iterator_bool(&sit);
1360                 } else if (strcmp("large", key) == 0 && bt == BSON_BOOL) {
1361                     cops.large = bson_iterator_bool(&sit);
1362                 } else if (strcmp("cachedrecords", key) == 0 && BSON_IS_NUM_TYPE(bt)) {
1363                     cops.cachedrecords = bson_iterator_int(&sit);
1364                 } else if (strcmp("records", key) == 0 && BSON_IS_NUM_TYPE(bt)) {
1365                     cops.records = bson_iterator_long(&sit);
1366                 }
1367             }
1368         }
1369         coll = _createcollimpl(jb, cname, &cops);
1370         if (!coll) {
1371             err = true;
1372             if (log) {
1373                 tcxstrprintf(log, "\nERROR: Error creating collection: '%s'", cname);
1374             }
1375             goto finish;
1376         }
1377     }
1378
1379     //Register collection indexes
1380     BSON_ITERATOR_INIT(&mbsonit, mbson);
1381     while ((bt = bson_iterator_next(&mbsonit)) != BSON_EOO) {
1382         const char *key = BSON_ITERATOR_KEY(&mbsonit);
1383         if (bt != BSON_OBJECT || strlen(key) < 2 || key[0] != 'i') {
1384             continue;
1385         }
1386         char *ipath = NULL;
1387         int iflags = 0;
1388         bson_iterator sit;
1389
1390         BSON_ITERATOR_SUBITERATOR(&mbsonit, &sit);
1391         bt = bson_find_fieldpath_value("ipath", &sit);
1392         if (bt == BSON_STRING) {
1393             ipath = strdup(bson_iterator_string(&sit));
1394         }
1395
1396         BSON_ITERATOR_SUBITERATOR(&mbsonit, &sit);
1397         bt = bson_find_fieldpath_value("iflags", &sit);
1398         if (bt == BSON_INT || bt == BSON_LONG) {
1399             iflags = bson_iterator_int(&sit);
1400         }
1401         if (ipath) {
1402             if (!_setindeximpl(coll, ipath, iflags, true)) {
1403                 err = true;
1404                 if (log) {
1405                     tcxstrprintf(log, "\nERROR: Error creating collection index. Collection: '%s' Field: '%s'", cname, ipath);
1406                 }
1407                 TCFREE(ipath);
1408             }
1409             TCFREE(ipath);
1410         }
1411     }
1412
1413     int fd = open(bspath, O_RDONLY, TCFILEMODE);
1414     if (fd == -1) {
1415         err = true;
1416         if (log) {
1417             tcxstrprintf(log, "\nERROR: Error reading file: '%s'", bspath);
1418         }
1419         _ejdbsetecode2(jb, TCEREAD, __FILE__, __LINE__, __func__, true);
1420         goto finish;
1421     }
1422     if (!JBCLOCKMETHOD(coll, true)) {
1423         err = true;
1424         goto finish;
1425     }
1426     int32_t maxdocsiz = 0, docsiz = 0, numdocs = 0;
1427     char *docbuf;
1428     TCMALLOC(docbuf, 4);
1429     while (true) {
1430         sp = read(fd, docbuf, 4);
1431         if (sp < 4) {
1432             break;
1433         }
1434         memcpy(&docsiz, docbuf, 4);
1435         docsiz = TCHTOIL(docsiz);
1436         if (docsiz > EJDB_MAX_IMPORTED_BSON_SIZE) {
1437             err = true;
1438             if (log) {
1439                 tcxstrprintf(log, "\nERROR: BSON document size: %d exceeds the maximum allowed size limit: %d for import operation",
1440                              docsiz, EJDB_MAX_IMPORTED_BSON_SIZE);
1441             }
1442             _ejdbsetecode2(jb, JBETOOBIGBSON, __FILE__, __LINE__, __func__, true);
1443             break;
1444         }
1445         if (docsiz < 5) {
1446             break;
1447         }
1448         if (maxdocsiz < docsiz) {
1449             maxdocsiz = docsiz;
1450             TCREALLOC(docbuf, docbuf, maxdocsiz);
1451         }
1452         sp = read(fd, docbuf + 4, docsiz - 4);
1453         if (sp < docsiz - 4) {
1454             break;
1455         }
1456         bson_oid_t oid;
1457         bson savebs;
1458         bson_init_with_data(&savebs, docbuf);
1459         if (docbuf[docsiz - 1] != '\0' || savebs.err || !savebs.finished) {
1460             err = true;
1461             _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
1462             break;
1463         }
1464         if (!_ejdbsavebsonimpl(coll, &savebs, &oid, false)) {
1465             err = true;
1466             break;
1467         }
1468         ++numdocs;
1469     }
1470     if (docbuf) {
1471         TCFREE(docbuf);
1472     }
1473     if (!tctdbsync(coll->tdb)) {
1474         err = true;
1475     }
1476     if (!err && log) {
1477         tcxstrprintf(log, "\n%d objects imported into '%s'", numdocs, cname);
1478     }
1479     JBCUNLOCKMETHOD(coll);
1480 finish:
1481     if (mbson) {
1482         bson_del(mbson);
1483     }
1484     if (mjson) {
1485         TCFREE(mjson);
1486     }
1487     tcxstrdel(xmetapath);
1488     if (err && log) {
1489         tcxstrprintf(log, "\nERROR: Importing data into: '%s' failed with error: '%s'", cname, (ejdbecode(jb) != 0) ? ejdberrmsg(ejdbecode(jb)) : "Unknown");
1490     }
1491     TCFREE(cname);
1492     return !err;
1493 }
1494
1495 static bool _exportcoll(EJCOLL *coll, const char *dpath, int flags, TCXSTR *log) {
1496     bool err = false;
1497     char *fpath = tcsprintf("%s%c%s%s", dpath, MYPATHCHR, coll->cname, (flags & JBJSONEXPORT) ? ".json" : ".bson");
1498     char *fpathm = tcsprintf("%s%c%s%s", dpath, MYPATHCHR, coll->cname, "-meta.json");
1499     TCHDB *hdb = coll->tdb->hdb;
1500     TCXSTR *skbuf = tcxstrnew3(sizeof (bson_oid_t) + 1);
1501     TCXSTR *colbuf = tcxstrnew3(1024);
1502     TCXSTR *bsbuf = tcxstrnew3(1024);
1503     int sz = 0;
1504 #ifndef _WIN32
1505     HANDLE fd = open(fpath, O_RDWR | O_CREAT | O_TRUNC, JBFILEMODE);
1506     HANDLE fdm = open(fpathm, O_RDWR | O_CREAT | O_TRUNC, JBFILEMODE);
1507 #else
1508     HANDLE fd = CreateFile(fpath, GENERIC_READ | GENERIC_WRITE,
1509                            FILE_SHARE_READ | FILE_SHARE_WRITE,
1510                            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1511
1512     HANDLE fdm = CreateFile(fpathm, GENERIC_READ | GENERIC_WRITE,
1513                             FILE_SHARE_READ | FILE_SHARE_WRITE,
1514                             NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1515 #endif
1516     if (INVALIDHANDLE(fd) || INVALIDHANDLE(fdm)) {
1517         _ejdbsetecode2(coll->jb, JBEEI, __FILE__, __LINE__, __func__, true);
1518         err = true;
1519         goto finish;
1520     }
1521     TCHDBITER *it = tchdbiter2init(hdb);
1522     if (!it) {
1523         goto finish;
1524     }
1525     while (!err && tchdbiter2next(hdb, it, skbuf, colbuf)) {
1526         sz = tcmaploadoneintoxstr(TCXSTRPTR(colbuf), TCXSTRSIZE(colbuf), JDBCOLBSON, JDBCOLBSONL, bsbuf);
1527         if (sz > 0) {
1528             char *wbuf = NULL;
1529             int wsiz;
1530             if (flags & JBJSONEXPORT) {
1531                 if (bson2json(TCXSTRPTR(bsbuf), &wbuf, &wsiz) != BSON_OK) {
1532                     _ejdbsetecode2(coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__, true);
1533                     goto wfinish;
1534                 }
1535             } else {
1536                 wbuf = TCXSTRPTR(bsbuf);
1537                 wsiz = TCXSTRSIZE(bsbuf);
1538             }
1539             if (!tcwrite(fd, wbuf, wsiz)) {
1540                 _ejdbsetecode2(coll->jb, JBEEI, __FILE__, __LINE__, __func__, true);
1541                 goto wfinish;
1542             }
1543 wfinish:
1544             if (wbuf && wbuf != TCXSTRPTR(bsbuf)) {
1545                 TCFREE(wbuf);
1546             }
1547         }
1548         tcxstrclear(skbuf);
1549         tcxstrclear(colbuf);
1550         tcxstrclear(bsbuf);
1551     }
1552
1553     if (!err) { //export collection meta
1554         TCMAP *cmeta = tctdbget(coll->jb->metadb, coll->cname, coll->cnamesz);
1555         if (!cmeta) {
1556             goto finish;
1557         }
1558         bson mbs;
1559         bson_init(&mbs);
1560         tcmapiterinit(cmeta);
1561         const char *mkey = NULL;
1562         while ((mkey = tcmapiternext2(cmeta)) != NULL) {
1563             if (!mkey || (*mkey != 'i' && strcmp(mkey, "opts"))) {
1564                 continue; //allowing only index & opts meta bsons
1565             }
1566             bson *bs = _metagetbson(coll->jb, coll->cname, coll->cnamesz, mkey);
1567             if (bs) {
1568                 bson_append_bson(&mbs, mkey, bs);
1569                 bson_del(bs);
1570             }
1571         }
1572         tcmapdel(cmeta);
1573
1574         bson_finish(&mbs);
1575         char *wbuf = NULL;
1576         int wsiz;
1577         if (bson2json(bson_data(&mbs), &wbuf, &wsiz) != BSON_OK) {
1578             err = true;
1579             _ejdbsetecode2(coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__, true);
1580             bson_destroy(&mbs);
1581             goto finish;
1582         }
1583         bson_destroy(&mbs);
1584         if (!tcwrite(fdm, wbuf, wsiz)) {
1585             err = true;
1586             _ejdbsetecode2(coll->jb, JBEEI, __FILE__, __LINE__, __func__, true);
1587         }
1588         TCFREE(wbuf);
1589     }
1590
1591 finish:
1592     if (!INVALIDHANDLE(fd) && !CLOSEFH(fd)) {
1593         _ejdbsetecode2(coll->jb, JBEEI, __FILE__, __LINE__, __func__, true);
1594         err = true;
1595     }
1596     if (!INVALIDHANDLE(fdm) && !CLOSEFH(fdm)) {
1597         _ejdbsetecode2(coll->jb, JBEEI, __FILE__, __LINE__, __func__, true);
1598         err = true;
1599     }
1600     tcxstrdel(skbuf);
1601     tcxstrdel(colbuf);
1602     tcxstrdel(bsbuf);
1603     TCFREE(fpath);
1604     TCFREE(fpathm);
1605     return !err;
1606 }
1607
1608 /* Set the error code of a table database object. */
1609 static void _ejdbsetecode(EJDB *jb, int ecode, const char *filename, int line, const char *func) {
1610     _ejdbsetecode2(jb, ecode, filename, line, func, false);
1611 }
1612
1613 static void _ejdbsetecode2(EJDB *jb, int ecode, const char *filename, int line, const char *func, bool notfatal) {
1614     assert(jb && filename && line >= 1 && func);
1615     tctdbsetecode2(jb->metadb, ecode, filename, line, func, notfatal);
1616 }
1617
1618 static EJCOLL* _getcoll(EJDB *jb, const char *colname) {
1619     assert(colname);
1620     for (int i = 0; i < jb->cdbsnum; ++i) {
1621         assert(jb->cdbs[i]);
1622         if (!strcmp(colname, jb->cdbs[i]->cname)) {
1623             return jb->cdbs[i];
1624         }
1625     }
1626     return NULL;
1627 }
1628
1629 /* Set mutual exclusion control of a table database object for threading. */
1630 static bool _ejdbsetmutex(EJDB *ejdb) {
1631     assert(ejdb);
1632     if (ejdb->mmtx || JBISOPEN(ejdb)) {
1633         _ejdbsetecode(ejdb, TCEINVALID, __FILE__, __LINE__, __func__);
1634         return false;
1635     }
1636     TCMALLOC(ejdb->mmtx, sizeof (pthread_rwlock_t));
1637     bool err = false;
1638     if (pthread_rwlock_init(ejdb->mmtx, NULL) != 0) err = true;
1639     if (err) {
1640         TCFREE(ejdb->mmtx);
1641         ejdb->mmtx = NULL;
1642         return false;
1643     }
1644     return true;
1645 }
1646
1647 /* Lock a method of the table database object.
1648    `tdb' specifies the table database object.
1649    `wr' specifies whether the lock is writer or not.
1650    If successful, the return value is true, else, it is false. */
1651 EJDB_INLINE bool _ejdblockmethod(EJDB *ejdb, bool wr) {
1652     assert(ejdb);
1653     if (wr ? pthread_rwlock_wrlock(ejdb->mmtx) != 0 : pthread_rwlock_rdlock(ejdb->mmtx) != 0) {
1654         _ejdbsetecode(ejdb, TCETHREAD, __FILE__, __LINE__, __func__);
1655         return false;
1656     }
1657     TCTESTYIELD();
1658     return true;
1659 }
1660
1661 /* Unlock a method of the table database object.
1662    `tdb' specifies the table database object.
1663    If successful, the return value is true, else, it is false. */
1664 EJDB_INLINE bool _ejdbunlockmethod(EJDB *ejdb) {
1665     assert(ejdb);
1666     if (pthread_rwlock_unlock(ejdb->mmtx) != 0) {
1667         _ejdbsetecode(ejdb, TCETHREAD, __FILE__, __LINE__, __func__);
1668         return false;
1669     }
1670     TCTESTYIELD();
1671     return true;
1672 }
1673
1674 EJDB_INLINE bool _ejdbcolsetmutex(EJCOLL *coll) {
1675     assert(coll && coll->jb);
1676     if (coll->mmtx) {
1677         _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__);
1678         return false;
1679     }
1680     TCMALLOC(coll->mmtx, sizeof (pthread_rwlock_t));
1681     bool err = false;
1682     if (pthread_rwlock_init(coll->mmtx, NULL) != 0) err = true;
1683     if (err) {
1684         TCFREE(coll->mmtx);
1685         coll->mmtx = NULL;
1686         return false;
1687     }
1688     return true;
1689 }
1690
1691 EJDB_INLINE bool _ejcollockmethod(EJCOLL *coll, bool wr) {
1692     assert(coll && coll->jb);
1693     if (wr ? pthread_rwlock_wrlock(coll->mmtx) != 0 : pthread_rwlock_rdlock(coll->mmtx) != 0) {
1694         _ejdbsetecode(coll->jb, TCETHREAD, __FILE__, __LINE__, __func__);
1695         return false;
1696     }
1697     TCTESTYIELD();
1698     return (coll->tdb && coll->tdb->open);
1699 }
1700
1701 EJDB_INLINE bool _ejcollunlockmethod(EJCOLL *coll) {
1702     assert(coll && coll->jb);
1703     if (pthread_rwlock_unlock(coll->mmtx) != 0) {
1704         _ejdbsetecode(coll->jb, TCETHREAD, __FILE__, __LINE__, __func__);
1705         return false;
1706     }
1707     TCTESTYIELD();
1708     return true;
1709 }
1710
1711 bool ejcollockmethod(EJCOLL *coll, bool wr) {
1712     return _ejcollockmethod(coll, wr);
1713 }
1714
1715 bool ejcollunlockmethod(EJCOLL *coll) {
1716     return _ejcollunlockmethod(coll);
1717 }
1718
1719 static void _qrydel(EJQ *q, bool freequery) {
1720     if (!q) {
1721         return;
1722     }
1723     const EJQF *qf = NULL;
1724     if (q->qflist) {
1725         for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
1726             qf = TCLISTVALPTR(q->qflist, i);
1727             assert(qf);
1728             _delqfdata(q, qf);
1729         }
1730         tclistdel(q->qflist);
1731         q->qflist = NULL;
1732     }
1733
1734     if (q->orqlist) {
1735         for (int i = 0; i < TCLISTNUM(q->orqlist); ++i) {
1736             EJQ *oq = *((EJQ**) TCLISTVALPTR(q->orqlist, i));
1737             _qrydel(oq, true);
1738         }
1739         tclistdel(q->orqlist);
1740         q->orqlist = NULL;
1741     }
1742
1743     if (q->andqlist) {
1744         for (int i = 0; i < TCLISTNUM(q->andqlist); ++i) {
1745             EJQ *aq = *((EJQ**) TCLISTVALPTR(q->andqlist, i));
1746             _qrydel(aq, true);
1747         }
1748         tclistdel(q->andqlist);
1749         q->andqlist = NULL;
1750     }
1751
1752     if (q->hints) {
1753         bson_del(q->hints);
1754         q->hints = NULL;
1755     }
1756     if (q->ifields) {
1757         tcmapdel(q->ifields);
1758         q->ifields = NULL;
1759     }
1760     if (q->colbuf) {
1761         tcxstrdel(q->colbuf);
1762         q->colbuf = NULL;
1763     }
1764     if (q->bsbuf) {
1765         tcxstrdel(q->bsbuf);
1766         q->bsbuf = NULL;
1767     }
1768     if (q->tmpbuf) {
1769         tcxstrdel(q->tmpbuf);
1770         q->tmpbuf = NULL;
1771     }
1772     if (q->allqfields) {
1773         TCFREE(q->allqfields);
1774         q->allqfields = NULL;
1775     }
1776     if (freequery) {
1777         TCFREE(q);
1778     }
1779 }
1780
1781 static bool _qrybsvalmatch(const EJQF *qf, bson_iterator *it, bool expandarrays, int *arridx) {
1782     if (qf->tcop == TDBQTRUE) {
1783         return (true == !qf->negate);
1784     }
1785     bool rv = false;
1786     bson_type bt = BSON_ITERATOR_TYPE(it);
1787     const char *expr = qf->expr;
1788     int exprsz = qf->exprsz;
1789     char sbuf[JBSTRINOPBUFFERSZ]; //buffer for icase comparisons
1790     char oidbuf[25]; //OID buffer
1791     char *cbuf = NULL;
1792     int cbufstrlen = 0;
1793     int fvalsz;
1794     int sp;
1795     const char *fval;
1796
1797         // Feature #129: Handle BSON_SYMBOL like BSON_STRING
1798 #define _FETCHSTRFVAL() \
1799     do { \
1800         fvalsz = (BSON_IS_STRING_TYPE(bt)) ? bson_iterator_string_len(it) : 1; \
1801         fval = (BSON_IS_STRING_TYPE(bt)) ? bson_iterator_string(it) : ""; \
1802         if (bt == BSON_OID) { \
1803             bson_oid_to_string(bson_iterator_oid(it), oidbuf); \
1804             fvalsz = 25; \
1805             fval = oidbuf; \
1806         } \
1807     } while(false)
1808
1809
1810     if (bt == BSON_ARRAY && expandarrays) { //Iterate over array
1811         bson_iterator sit;
1812         BSON_ITERATOR_SUBITERATOR(it, &sit);
1813         int c = 0;
1814         while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
1815             if (_qrybsvalmatch(qf, &sit, false, arridx)) {
1816                 if (arridx) {
1817                     *arridx = c;
1818                 }
1819                 return true;
1820             }
1821             ++c;
1822         }
1823         return (false == !qf->negate);
1824     }
1825     switch (qf->tcop) {
1826         case TDBQCSTREQ: {
1827             _FETCHSTRFVAL();
1828             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1829                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1830                 if (cbufstrlen < 0) {
1831                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1832                     rv = false;
1833                 } else {
1834                     rv = (exprsz == cbufstrlen) && (exprsz == 0 || !memcmp(expr, cbuf, exprsz));
1835                 }
1836                 if (cbuf && cbuf != sbuf) {
1837                     TCFREE(cbuf);
1838                 }
1839             } else {
1840                 rv = (exprsz == fvalsz - 1) && (exprsz == 0 || !memcmp(expr, fval, exprsz));
1841             }
1842             break;
1843         }
1844         case TDBQCSTRINC: {
1845             _FETCHSTRFVAL();
1846             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1847                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1848                 if (cbufstrlen < 0) {
1849                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1850                     rv = false;
1851                 } else {
1852                     rv = (exprsz <= cbufstrlen) && strstr(cbuf, expr);
1853                 }
1854                 if (cbuf && cbuf != sbuf) {
1855                     TCFREE(cbuf);
1856                 }
1857             } else {
1858                 rv = (exprsz <= fvalsz) && strstr(fval, expr);
1859             }
1860             break;
1861         }
1862         case TDBQCSTRBW: {
1863             _FETCHSTRFVAL();
1864             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1865                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1866                 if (cbufstrlen < 0) {
1867                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1868                     rv = false;
1869                 } else {
1870                     rv = tcstrfwm(cbuf, expr);
1871                 }
1872                 if (cbuf && cbuf != sbuf) {
1873                     TCFREE(cbuf);
1874                 }
1875             } else {
1876                 rv = tcstrfwm(fval, expr);
1877             }
1878             break;
1879         }
1880         case TDBQCSTREW: {
1881             _FETCHSTRFVAL();
1882             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1883                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1884                 if (cbufstrlen < 0) {
1885                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1886                     rv = false;
1887                 } else {
1888                     rv = tcstrbwm(cbuf, expr);
1889                 }
1890                 if (cbuf && cbuf != sbuf) {
1891                     TCFREE(cbuf);
1892                 }
1893             } else {
1894                 rv = tcstrbwm(fval, expr);
1895             }
1896             break;
1897         }
1898         case TDBQCSTRAND: {
1899             TCLIST *tokens = qf->exprlist;
1900             assert(tokens);
1901             _FETCHSTRFVAL();
1902             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1903                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1904                 if (cbufstrlen < 0) {
1905                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1906                     rv = false;
1907                 } else {
1908                     rv = _qrycondcheckstrand(cbuf, tokens);
1909                 }
1910                 if (cbuf && cbuf != sbuf) {
1911                     TCFREE(cbuf);
1912                 }
1913             } else {
1914                 rv = _qrycondcheckstrand(fval, tokens);
1915             }
1916             break;
1917         }
1918         case TDBQCSTROR: {
1919             TCLIST *tokens = qf->exprlist;
1920             assert(tokens);
1921             _FETCHSTRFVAL();
1922             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1923                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1924                 if (cbufstrlen < 0) {
1925                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1926                     rv = false;
1927                 } else {
1928                     rv = _qrycondcheckstror(cbuf, tokens);
1929                 }
1930                 if (cbuf && cbuf != sbuf) {
1931                     TCFREE(cbuf);
1932                 }
1933             } else {
1934                 rv = _qrycondcheckstror(fval, tokens);
1935             }
1936             break;
1937         }
1938         case TDBQCSTROREQ: {
1939             TCLIST *tokens = qf->exprlist;
1940             assert(tokens);
1941             _FETCHSTRFVAL();
1942             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1943                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1944                 if (cbufstrlen < 0) {
1945                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1946                     rv = false;
1947                 } else {
1948                     if (qf->exprmap) {
1949                         if (tcmapget(qf->exprmap, cbuf, cbufstrlen, &sp) != NULL) {
1950                             rv = true;
1951                             break;
1952                         }
1953                     } else {
1954                         for (int i = 0; i < TCLISTNUM(tokens); ++i) {
1955                             const char *token = TCLISTVALPTR(tokens, i);
1956                             int tokensz = TCLISTVALSIZ(tokens, i);
1957                             if (tokensz == cbufstrlen && !strncmp(token, cbuf, tokensz)) {
1958                                 rv = true;
1959                                 break;
1960                             }
1961                         }
1962                     }
1963                 }
1964                 if (cbuf && cbuf != sbuf) {
1965                     TCFREE(cbuf);
1966                 }
1967             } else {
1968                 if (qf->exprmap) {
1969                     if (tcmapget3(qf->exprmap, fval, (fvalsz - 1), &sp) != NULL) {
1970                         rv = true;
1971                         break;
1972                     }
1973                 } else {
1974                     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
1975                         const char *token = TCLISTVALPTR(tokens, i);
1976                         int tokensz = TCLISTVALSIZ(tokens, i);
1977                         if (tokensz == (fvalsz - 1) && !strncmp(token, fval, tokensz)) {
1978                             rv = true;
1979                             break;
1980                         }
1981                     }
1982                 }
1983             }
1984             break;
1985         }
1986         case TDBQCSTRORBW: {
1987             TCLIST *tokens = qf->exprlist;
1988             assert(tokens);
1989             _FETCHSTRFVAL();
1990             if ((qf->flags & EJCONDICASE) && (bt == BSON_STRING)) {
1991                 cbufstrlen = tcicaseformat(fval, fvalsz - 1, sbuf, JBSTRINOPBUFFERSZ, &cbuf);
1992                 if (cbufstrlen < 0) {
1993                     _ejdbsetecode(qf->jb, cbufstrlen, __FILE__, __LINE__, __func__);
1994                     rv = false;
1995                 } else {
1996                     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
1997                         const char *token = TCLISTVALPTR(tokens, i);
1998                         int tokensz = TCLISTVALSIZ(tokens, i);
1999                         if (tokensz <= cbufstrlen && !strncmp(token, cbuf, tokensz)) {
2000                             rv = true;
2001                             break;
2002                         }
2003                     }
2004                 }
2005                 if (cbuf && cbuf != sbuf) {
2006                     TCFREE(cbuf);
2007                 }
2008             } else {
2009                 for (int i = 0; i < TCLISTNUM(tokens); ++i) {
2010                     const char *token = TCLISTVALPTR(tokens, i);
2011                     int tokensz = TCLISTVALSIZ(tokens, i);
2012                     if (tokensz <= (fvalsz - 1) && !strncmp(token, fval, tokensz)) {
2013                         rv = true;
2014                         break;
2015                     }
2016                 }
2017             }
2018             break;
2019         }
2020         case TDBQCSTRRX: {
2021             _FETCHSTRFVAL();
2022             rv = qf->regex && (regexec((regex_t *) qf->regex, fval, 0, NULL, 0) == 0);
2023             break;
2024         }
2025         case TDBQCNUMEQ: {
2026             if (bt == BSON_DOUBLE) {
2027                 rv = (qf->exprdblval == bson_iterator_double_raw(it));
2028             } else if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2029                 rv = (qf->exprlongval == bson_iterator_long(it));
2030             } else {
2031                 rv = false;
2032             }
2033             break;
2034         }
2035         case TDBQCNUMGT: {
2036             if (bt == BSON_DOUBLE) {
2037                 rv = (qf->exprdblval < bson_iterator_double_raw(it));
2038             } else if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2039                 rv = (qf->exprlongval < bson_iterator_long(it));
2040             } else {
2041                 rv = false;
2042             }
2043             break;
2044         }
2045         case TDBQCNUMGE: {
2046             if (bt == BSON_DOUBLE) {
2047                 rv = (qf->exprdblval <= bson_iterator_double_raw(it));
2048             } else if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2049                 rv = (qf->exprlongval <= bson_iterator_long(it));
2050             } else {
2051                 rv = false;
2052             }
2053             break;
2054         }
2055         case TDBQCNUMLT: {
2056             if (bt == BSON_DOUBLE) {
2057                 rv = (qf->exprdblval > bson_iterator_double_raw(it));
2058             } else if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2059                 rv = (qf->exprlongval > bson_iterator_long(it));
2060             } else {
2061                 rv = false;
2062             }
2063             break;
2064         }
2065         case TDBQCNUMLE: {
2066             if (bt == BSON_DOUBLE) {
2067                 rv = (qf->exprdblval >= bson_iterator_double_raw(it));
2068             } else if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2069                 rv = (qf->exprlongval >= bson_iterator_long(it));
2070             } else {
2071                 rv = false;
2072             }
2073             break;
2074         }
2075         case TDBQCNUMBT: {
2076             assert(qf->ftype == BSON_ARRAY);
2077             TCLIST *tokens = qf->exprlist;
2078             assert(tokens);
2079             assert(TCLISTNUM(tokens) == 2);
2080             if (BSON_ITERATOR_TYPE(it) == BSON_DOUBLE) {
2081                 double v1 = tcatof(tclistval2(tokens, 0));
2082                 double v2 = tcatof(tclistval2(tokens, 1));
2083                 double val = bson_iterator_double(it);
2084                 rv = (v2 > v1) ? (v2 >= val && v1 <= val) : (v2 <= val && v1 >= val);
2085             } else {
2086                 int64_t v1 = tcatoi(tclistval2(tokens, 0));
2087                 int64_t v2 = tcatoi(tclistval2(tokens, 1));
2088                 int64_t val = bson_iterator_long(it);
2089                 rv = (v2 > v1) ? (v2 >= val && v1 <= val) : (v2 <= val && v1 >= val);
2090             }
2091             break;
2092         }
2093         case TDBQCNUMOREQ: {
2094             TCLIST *tokens = qf->exprlist;
2095             assert(tokens);
2096             if (bt == BSON_DOUBLE) {
2097                 double nval = bson_iterator_double_raw(it);
2098                 for (int i = 0; i < TCLISTNUM(tokens); ++i) {
2099                     if (tcatof(TCLISTVALPTR(tokens, i)) == nval) {
2100                         rv = true;
2101                         break;
2102                     }
2103                 }
2104             } else if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2105                 int64_t nval = bson_iterator_long(it);
2106                 for (int i = 0; i < TCLISTNUM(tokens); ++i) {
2107                     if (tcatoi(TCLISTVALPTR(tokens, i)) == nval) {
2108                         rv = true;
2109                         break;
2110                     }
2111                 }
2112             }
2113             break;
2114         }
2115     }
2116     return (rv == !qf->negate);
2117
2118 #undef _FETCHSTRFVAL
2119 }
2120
2121
2122 //Fills `ffpctx` and `qf->uslots`
2123 static void _qrysetarrayidx(FFPCTX *ffpctx, EJQF *qf, int dpos, int mpos) {
2124     if (ffpctx->dpos == dpos && ffpctx->mpos == -1) { //single ctx matching
2125         ffpctx->mpos = mpos;
2126     }
2127     if (qf->uslots) {
2128         for (int i = 0; i < TCLISTNUM(qf->uslots); ++i) {
2129             USLOT *us = TCLISTVALPTR(qf->uslots, i);
2130             assert(us);
2131             if (us->dpos == dpos && us->mpos == -1) {
2132                 us->mpos = mpos;
2133             }
2134         }
2135     }
2136 }
2137
2138 static bool _qrybsrecurrmatch(EJQF *qf, FFPCTX *ffpctx, int currpos) {
2139     assert(qf && ffpctx && ffpctx->stopnestedarr);
2140     bson_type bt = bson_find_fieldpath_value3(ffpctx);
2141     if (bt == BSON_ARRAY && ffpctx->stopos < ffpctx->fplen) { //a bit of complicated code  in this case =)
2142         //we just stepped in some array in middle of our fieldpath, so have to perform recursive nested iterations
2143         //$elemMatch active in this context
2144         while (ffpctx->fpath[ffpctx->stopos] == '.' && ffpctx->stopos < ffpctx->fplen) ffpctx->stopos++;
2145         ffpctx->fplen = ffpctx->fplen - ffpctx->stopos;
2146         assert(ffpctx->fplen > 0);
2147         ffpctx->fpath = ffpctx->fpath + ffpctx->stopos;
2148         currpos += ffpctx->stopos; //adjust cumulative field position
2149
2150         bson_iterator sit;
2151         BSON_ITERATOR_SUBITERATOR(ffpctx->input, &sit);
2152         for (int arr_idx = 0;(bt = bson_iterator_next(&sit)) != BSON_EOO; ++arr_idx) {
2153             if (bt != BSON_OBJECT && bt != BSON_ARRAY)
2154                 continue;
2155
2156             bson_iterator sit2;
2157             BSON_ITERATOR_SUBITERATOR(&sit, &sit2);
2158
2159             ffpctx->input = &sit2;
2160
2161             // Match using context initialised above.
2162             if (_qrybsrecurrmatch(qf, ffpctx, currpos) == qf->negate) {
2163                 continue;
2164             }
2165
2166             bool ret = true;
2167             if (qf->elmatchgrp > 0 && qf->elmatchpos == currpos) { //$elemMatch matching group exists at right place
2168                 // Match all sub-queries on current field pos. Early exit (break) on failure.
2169                 for (int i = TCLISTNUM(qf->q->qflist) - 1; i >= 0; --i) {
2170                     EJQF *eqf = TCLISTVALPTR(qf->q->qflist, i);
2171                     if (eqf == qf || (eqf->mflags & EJFEXCLUDED) || eqf->elmatchgrp != qf->elmatchgrp) {
2172                         continue;
2173                     }
2174                     eqf->mflags |= EJFEXCLUDED;
2175                     BSON_ITERATOR_SUBITERATOR(&sit, &sit2);
2176                     FFPCTX nffpctx = *ffpctx;
2177                     nffpctx.fplen = eqf->fpathsz - eqf->elmatchpos;
2178                     if (nffpctx.fplen <= 0) { //should never happen if query construction is correct
2179                         assert(false);
2180                         ret = false;
2181                         break;
2182                     }
2183                     nffpctx.fpath = eqf->fpath + eqf->elmatchpos;
2184                     nffpctx.input = &sit2;
2185                     nffpctx.stopos = 0;
2186
2187                     // Match sub-query at current field pos.
2188                     // Ignores outer negate (qf) on inner query (eqf).
2189                     if (_qrybsrecurrmatch(eqf, &nffpctx, eqf->elmatchpos) == qf->negate) {
2190                         // Skip all remaining sub-queries on this field. Go to next element, if any.
2191                         ret = false;
2192                         break;
2193                     }
2194                 }
2195             }
2196             if (ret) {
2197                 _qrysetarrayidx(ffpctx, qf, (currpos - 1), arr_idx);
2198                 // Only return success at this point.
2199                 // An failure here may precede a later success so proceed to next element, if any.
2200                 return ret != qf->negate;
2201             }
2202         }
2203         return qf->negate;
2204     } else {
2205         if (bt == BSON_EOO || bt == BSON_UNDEFINED || bt == BSON_NULL) {
2206             return qf->negate; //Field missing
2207         } else if (qf->tcop == TDBQCEXIST) {
2208             return !qf->negate;
2209         }
2210         int mpos = -1;
2211         bool ret = _qrybsvalmatch(qf, ffpctx->input, true, &mpos);
2212         if (ret && currpos == 0 && bt == BSON_ARRAY) { //save $(projection)
2213             _qrysetarrayidx(ffpctx, qf, ffpctx->fplen, mpos);
2214         }
2215         return ret;
2216     }
2217 }
2218
2219 static bool _qrybsmatch(EJQF *qf, const void *bsbuf, int bsbufsz) {
2220     if (qf->tcop == TDBQTRUE) {
2221         return !qf->negate;
2222     }
2223     bson_iterator it;
2224     BSON_ITERATOR_FROM_BUFFER(&it, bsbuf);
2225     FFPCTX ffpctx = {
2226         .fpath = qf->fpath,
2227         .fplen = qf->fpathsz,
2228         .input = &it,
2229         .stopnestedarr = true,
2230         .stopos = 0,
2231         .dpos = -1,
2232         .mpos = -1
2233     };
2234     if (qf->uslots) {
2235         for (int i = 0; i < TCLISTNUM(qf->uslots); ++i) {
2236             ((USLOT*) (TCLISTVALPTR(qf->uslots, i)))->mpos = -1;
2237         }
2238     }
2239     return _qrybsrecurrmatch(qf, &ffpctx, 0);
2240 }
2241
2242 static bool _qry_and_or_match(EJCOLL *coll, EJQ *ejq, const void *pkbuf, int pkbufsz) {
2243     bool isor = (ejq->orqlist && TCLISTNUM(ejq->orqlist) > 0);
2244     bool isand = (ejq->andqlist && TCLISTNUM(ejq->andqlist) > 0);
2245     if (!isor && !isand) {
2246         return true;
2247     }
2248     void *bsbuf = TCXSTRPTR(ejq->bsbuf);
2249     int bsbufsz = TCXSTRSIZE(ejq->bsbuf);
2250
2251     if (!bsbuf || bsbufsz <= 0) {
2252         tcxstrclear(ejq->colbuf);
2253         tcxstrclear(ejq->bsbuf);
2254         if (tchdbgetintoxstr(coll->tdb->hdb, pkbuf, pkbufsz, ejq->colbuf) <= 0) {
2255             return false;
2256         }
2257         if (tcmaploadoneintoxstr(TCXSTRPTR(ejq->colbuf), TCXSTRSIZE(ejq->colbuf), JDBCOLBSON, JDBCOLBSONL, ejq->bsbuf) <= 0) {
2258             return false;
2259         }
2260         bsbufsz = TCXSTRSIZE(ejq->bsbuf);
2261         bsbuf = TCXSTRPTR(ejq->bsbuf);
2262     }
2263     if (isand && !_qryandmatch2(coll, ejq, bsbuf, bsbufsz)) {
2264         return false;
2265     } else if (isor) {
2266         return _qryormatch2(coll, ejq, bsbuf, bsbufsz);
2267     } else {
2268         return true;
2269     }
2270 }
2271
2272 static bool _qryormatch3(EJCOLL *coll, EJQ *ejq, EJQ *oq, const void *bsbuf, int bsbufsz) {
2273     int j = 0;
2274     int jm = TCLISTNUM(oq->qflist);
2275     for (; j < jm; ++j) {
2276         EJQF *qf = TCLISTVALPTR(oq->qflist, j);
2277         assert(qf);
2278         qf->mflags = qf->flags;
2279         if (qf->mflags & EJFEXCLUDED) {
2280             continue;
2281         }
2282         if (!_qrybsmatch(qf, bsbuf, bsbufsz)) {
2283             break;
2284         }
2285     }
2286     if (j == jm) { //all fields in oq are matched
2287         if (oq->andqlist && TCLISTNUM(oq->andqlist) > 0 &&
2288                 !_qryandmatch2(coll, oq, bsbuf, bsbufsz)) { //we have nested $and fields
2289             return false;
2290         }
2291         if (oq->orqlist && TCLISTNUM(oq->orqlist) &&
2292                 !_qryormatch2(coll, oq, bsbuf, bsbufsz)) { //we have nested $or fields
2293             return false;
2294         }
2295         return true;
2296     }
2297     return false;
2298 }
2299
2300 static bool _qryormatch2(EJCOLL *coll, EJQ *ejq, const void *bsbuf, int bsbufsz) {
2301     if (!ejq->orqlist || TCLISTNUM(ejq->orqlist) < 1) {
2302         return true;
2303     }
2304     if (ejq->lastmatchedorq && _qryormatch3(coll, ejq, ejq->lastmatchedorq, bsbuf, bsbufsz)) {
2305         return true;
2306     }
2307     for (int i = 0; i < TCLISTNUM(ejq->orqlist); ++i) {
2308         EJQ *oq = *((EJQ**) TCLISTVALPTR(ejq->orqlist, i));
2309         assert(oq && oq->qflist);
2310         if (ejq->lastmatchedorq == oq) {
2311             continue;
2312         }
2313         if (_qryormatch3(coll, ejq, oq, bsbuf, bsbufsz)) {
2314             ejq->lastmatchedorq = oq;
2315             return true;
2316         }
2317     }
2318     return false;
2319 }
2320
2321 static bool _qryandmatch2(EJCOLL *coll, EJQ *ejq, const void *bsbuf, int bsbufsz) {
2322     if (!ejq->andqlist || TCLISTNUM(ejq->andqlist) < 1) {
2323         return true;
2324     }
2325     for (int i = 0; i < TCLISTNUM(ejq->andqlist); ++i) {
2326         EJQ *aq = *((EJQ**) TCLISTVALPTR(ejq->andqlist, i));
2327         assert(aq && aq->qflist);
2328         for (int j = 0; j < TCLISTNUM(aq->qflist); ++j) {
2329             EJQF *qf = TCLISTVALPTR(aq->qflist, j);
2330             assert(qf);
2331             qf->mflags = qf->flags;
2332             if (qf->mflags & EJFEXCLUDED) {
2333                 continue;
2334             }
2335             if (!_qrybsmatch(qf, bsbuf, bsbufsz)) {
2336                 return false;
2337             }
2338         }
2339         if (aq->andqlist && TCLISTNUM(aq->andqlist) > 0 &&
2340                 !_qryandmatch2(coll, aq, bsbuf, bsbufsz)) { //we have nested $and fields
2341             return false;
2342         }
2343         if (aq->orqlist && TCLISTNUM(aq->orqlist) > 0 &&
2344                 !_qryormatch2(coll, aq, bsbuf, bsbufsz)) { //we have nested $or fields
2345             return false;
2346         }
2347     }
2348     return true;
2349 }
2350
2351 /** Return true if all main query conditions matched */
2352 static bool _qryallcondsmatch(
2353     EJQ *ejq, int anum,
2354     EJCOLL *coll, EJQF **qfs, int qfsz,
2355     const void *pkbuf, int pkbufsz) {
2356     assert(ejq->colbuf && ejq->bsbuf);
2357     if (!(ejq->flags & EJQUPDATING) && (ejq->flags & EJQONLYCOUNT) && anum < 1) {
2358         return true;
2359     }
2360     tcxstrclear(ejq->colbuf);
2361     tcxstrclear(ejq->bsbuf);
2362     if (tchdbgetintoxstr(coll->tdb->hdb, pkbuf, pkbufsz, ejq->colbuf) <= 0) {
2363         return false;
2364     }
2365     if (tcmaploadoneintoxstr(TCXSTRPTR(ejq->colbuf), TCXSTRSIZE(ejq->colbuf), JDBCOLBSON, JDBCOLBSONL, ejq->bsbuf) <= 0) {
2366         return false;
2367     }
2368     if (anum < 1) {
2369         return true;
2370     }
2371     for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags; //reset matching flags
2372     for (int i = 0; i < qfsz; ++i) {
2373         EJQF *qf = qfs[i];
2374         if (qf->mflags & EJFEXCLUDED) continue;
2375         if (!_qrybsmatch(qf, TCXSTRPTR(ejq->bsbuf), TCXSTRSIZE(ejq->bsbuf))) {
2376             return false;
2377         }
2378     }
2379     return true;
2380 }
2381
2382 static EJQ* _qryaddand(EJDB *jb, EJQ *q, const void *andbsdata) {
2383     assert(jb && q && andbsdata);
2384     if (!andbsdata) {
2385         _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
2386         return NULL;
2387     }
2388     EJQ *oq = ejdbcreatequery2(jb, andbsdata);
2389     if (oq == NULL) {
2390         return NULL;
2391     }
2392     if (q->andqlist == NULL) {
2393         q->andqlist = tclistnew2(TCLISTINYNUM);
2394     }
2395     tclistpush(q->andqlist, &oq, sizeof(oq));
2396     return q;
2397 }
2398
2399 typedef struct {
2400     EJQF **ofs;
2401     int ofsz;
2402 } _EJBSORTCTX;
2403
2404 /* RS sorting comparison func */
2405 static int _ejdbsoncmp(const TCLISTDATUM *d1, const TCLISTDATUM *d2, void *opaque) {
2406     _EJBSORTCTX *ctx = opaque;
2407     assert(ctx);
2408     int res = 0;
2409     for (int i = 0; !res && i < ctx->ofsz; ++i) {
2410         const EJQF *qf = ctx->ofs[i];
2411         if (qf->flags & EJFORDERUSED) {
2412             continue;
2413         }
2414         res = bson_compare(d1->ptr, d2->ptr, qf->fpath, qf->fpathsz) * (qf->order >= 0 ? 1 : -1);
2415     }
2416     return res;
2417 }
2418
2419 EJDB_INLINE void _nufetch(_EJDBNUM *nu, const char *sval, bson_type bt) {
2420     if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2421         nu->inum = tcatoi(sval);
2422     } else if (bt == BSON_DOUBLE) {
2423         nu->dnum = tcatof(sval);
2424     } else {
2425         nu->inum = 0;
2426         assert(0);
2427     }
2428 }
2429
2430 EJDB_INLINE int _nucmp(_EJDBNUM *nu, const char *sval, bson_type bt) {
2431     if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2432         int64_t v = tcatoi(sval);
2433         return (nu->inum > v) ? 1 : (nu->inum < v ? -1 : 0);
2434     } else if (bt == BSON_DOUBLE) {
2435         double v = tcatof(sval);
2436         return (nu->dnum > v) ? 1 : (nu->dnum < v ? -1 : 0);
2437     } else {
2438         assert(0);
2439     }
2440     return 0;
2441 }
2442
2443 EJDB_INLINE int _nucmp2(_EJDBNUM *nu1, _EJDBNUM *nu2, bson_type bt) {
2444     if (bt == BSON_INT || bt == BSON_LONG || bt == BSON_BOOL || bt == BSON_DATE) {
2445         return (nu1->inum > nu2->inum) ? 1 : (nu1->inum < nu2->inum ? -1 : 0);
2446     } else if (bt == BSON_DOUBLE) {
2447         return (nu1->dnum > nu2->dnum) ? 1 : (nu1->dnum < nu2->dnum ? -1 : 0);
2448     } else {
2449         assert(0);
2450     }
2451     return 0;
2452 }
2453
2454 static void _qryfieldup(const EJQF *src, EJQF *target, uint32_t qflags) {
2455     assert(src && target);
2456     memset(target, 0, sizeof (*target));
2457     target->exprdblval = src->exprdblval;
2458     target->exprlongval = src->exprlongval;
2459     target->flags = src->flags;
2460     target->ftype = src->ftype;
2461     target->negate = src->negate;
2462     target->order = src->order;
2463     target->orderseq = src->orderseq;
2464     target->tcop = src->tcop;
2465     target->elmatchgrp = src->elmatchgrp;
2466     target->elmatchpos = src->elmatchpos;
2467
2468     if (src->expr) {
2469         TCMEMDUP(target->expr, src->expr, src->exprsz);
2470         target->exprsz = src->exprsz;
2471     }
2472     if (src->fpath) {
2473         TCMEMDUP(target->fpath, src->fpath, src->fpathsz);
2474         target->fpathsz = src->fpathsz;
2475     }
2476     if (src->regex && (EJQINTERNAL & qflags)) {
2477         //We cannot do deep copy of regex_t so do shallow copy only for internal query objects
2478         target->regex = src->regex;
2479     }
2480     if (src->exprlist) {
2481         target->exprlist = tclistdup(src->exprlist);
2482     }
2483     if (src->exprmap) {
2484         target->exprmap = tcmapdup(src->exprmap);
2485     }
2486     if (src->updateobj) {
2487         target->updateobj = bson_dup(src->updateobj);
2488     }
2489     if (src->ufields) {
2490         target->ufields = tclistdup(src->ufields);
2491     }
2492     if (src->uslots) {
2493         target->uslots = tclistdup(src->uslots);
2494     }
2495 }
2496
2497 /* Clone query object */
2498 static bool _qrydup(const EJQ *src, EJQ *target, uint32_t qflags) {
2499     assert(src && target);
2500     memset(target, 0, sizeof (*target));
2501     target->flags = src->flags | qflags;
2502     target->max = src->max;
2503     target->skip = src->skip;
2504     if (src->qflist) {
2505         target->qflist = tclistnew2(TCLISTNUM(src->qflist));
2506         for (int i = 0; i < TCLISTNUM(src->qflist); ++i) {
2507             EJQF qf;
2508             _qryfieldup(TCLISTVALPTR(src->qflist, i), &qf, qflags);
2509             qf.q = target;
2510             TCLISTPUSH(target->qflist, &qf, sizeof (qf));
2511         }
2512     }
2513     if (src->hints) {
2514         target->hints = bson_dup(src->hints);
2515     }
2516     if (src->ifields) {
2517         target->ifields = tcmapdup(src->ifields);
2518     }
2519     if (src->orqlist) {
2520         target->orqlist = tclistnew2(TCLISTNUM(src->orqlist));
2521         for (int i = 0; i < TCLISTNUM(src->orqlist); ++i) {
2522             EJQ *q;
2523             TCMALLOC(q, sizeof(*q));
2524             if (_qrydup(*((EJQ**) TCLISTVALPTR(src->orqlist, i)), q, qflags)) {
2525                 TCLISTPUSH(target->orqlist, &q, sizeof (q));
2526             } else {
2527                 _qrydel(q, true);
2528             }
2529         }
2530     }
2531     if (src->andqlist) {
2532         target->andqlist = tclistnew2(TCLISTNUM(src->andqlist));
2533         for (int i = 0; i < TCLISTNUM(src->andqlist); ++i) {
2534             EJQ *q;
2535             TCMALLOC(q, sizeof(*q));
2536             if (_qrydup(*((EJQ**) TCLISTVALPTR(src->andqlist, i)), q, qflags)) {
2537                 tclistpush(target->andqlist, &q, sizeof (q));
2538             } else {
2539                 _qrydel(q, true);
2540             }
2541         }
2542     }
2543     return true;
2544 }
2545
2546 typedef struct { /**> $do action visitor context */
2547     EJQ *q;
2548     EJDB *jb;
2549     TCMAP *dfields;
2550     bson *sbson;
2551 } _BSONDOVISITORCTX;
2552
2553 static bson_visitor_cmd_t _bsondovisitor(const char *ipath, int ipathlen, const char *key, int keylen,
2554         const bson_iterator *it, bool after, void *op) {
2555
2556     assert(op);
2557     _BSONDOVISITORCTX *ictx = op;
2558     EJCOLL *coll;
2559     TCMAP *dfields = ictx->dfields;
2560     bson_type lbt = BSON_ITERATOR_TYPE(it), bt;
2561     bson_iterator doit, bufit, sit;
2562     int sp;
2563     const char *sval;
2564     const EJQF *dofield;
2565     bson_visitor_cmd_t rv = BSON_VCMD_SKIP_AFTER;
2566
2567     switch (lbt) {
2568         case BSON_OBJECT: {
2569             bson_append_field_from_iterator(it, ictx->sbson);
2570             rv = (BSON_VCMD_SKIP_AFTER | BSON_VCMD_SKIP_NESTED);
2571             break;
2572         }
2573         case BSON_STRING:
2574         case BSON_OID:
2575         case BSON_ARRAY: {
2576             dofield = tcmapget(dfields, ipath, ipathlen, &sp);
2577             if (!dofield) {
2578                 if (lbt == BSON_ARRAY) {
2579                     bson_append_field_from_iterator(it, ictx->sbson);
2580                     rv = (BSON_VCMD_SKIP_AFTER | BSON_VCMD_SKIP_NESTED);
2581                 } else {
2582                     bson_append_field_from_iterator(it, ictx->sbson);
2583                 }
2584                 break;
2585             }
2586             assert(dofield->updateobj && (dofield->flags & EJCONDOIT));
2587             BSON_ITERATOR_INIT(&doit, dofield->updateobj);
2588             while ((bt = bson_iterator_next(&doit)) != BSON_EOO) {
2589                                 const char *dofname = BSON_ITERATOR_KEY(&doit);
2590                                 
2591                                 if (bt == BSON_STRING && !strcmp("$join", dofname)) {
2592                                         coll = _getcoll(ictx->jb, bson_iterator_string(&doit));
2593                                         if (!coll) break;
2594                                         bson_oid_t loid;
2595                                         if (lbt == BSON_STRING) {
2596                                                 sval = bson_iterator_string(it);
2597                                                 if (!ejdbisvalidoidstr(sval)) break;
2598                                                 bson_oid_from_string(&loid, sval);
2599                                         } else if (lbt == BSON_OID) {
2600                                                 loid = *(bson_iterator_oid(it));
2601                                         }
2602                                         if (lbt == BSON_STRING || lbt == BSON_OID) {
2603                                                 tcxstrclear(ictx->q->colbuf);
2604                                                 tcxstrclear(ictx->q->tmpbuf);
2605                                                 if (!tchdbgetintoxstr(coll->tdb->hdb, &loid, sizeof (loid), ictx->q->colbuf) ||
2606                                                                 !tcmaploadoneintoxstr(TCXSTRPTR(ictx->q->colbuf), TCXSTRSIZE(ictx->q->colbuf),
2607                                                                                                           JDBCOLBSON, JDBCOLBSONL, ictx->q->tmpbuf)) {
2608                                                         break;
2609                                                 }
2610                                                 BSON_ITERATOR_FROM_BUFFER(&bufit, TCXSTRPTR(ictx->q->tmpbuf));
2611                                                 bson_append_object_from_iterator(BSON_ITERATOR_KEY(it), &bufit, ictx->sbson);
2612                                                 break;
2613                                         }
2614                                         if (lbt == BSON_ARRAY) {
2615                                                 BSON_ITERATOR_SUBITERATOR(it, &sit);
2616                                                 bson_append_start_array(ictx->sbson, BSON_ITERATOR_KEY(it));
2617                                                 while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
2618                                                         if (bt != BSON_STRING && bt != BSON_OID) {
2619                                                                 bson_append_field_from_iterator(&sit, ictx->sbson);
2620                                                                 continue;
2621                                                         }
2622                                                         if (bt == BSON_STRING) {
2623                                                                 sval = bson_iterator_string(&sit);
2624                                                                 if (!ejdbisvalidoidstr(sval)) break;
2625                                                                 bson_oid_from_string(&loid, sval);
2626                                                         } else if (bt == BSON_OID) {
2627                                                                 loid = *(bson_iterator_oid(&sit));
2628                                                         }
2629                                                         tcxstrclear(ictx->q->colbuf);
2630                                                         tcxstrclear(ictx->q->tmpbuf);
2631                                                         if (!tchdbgetintoxstr(coll->tdb->hdb, &loid, sizeof (loid), ictx->q->colbuf) ||
2632                                                                         !tcmaploadoneintoxstr(TCXSTRPTR(ictx->q->colbuf), TCXSTRSIZE(ictx->q->colbuf),
2633                                                                                                                   JDBCOLBSON, JDBCOLBSONL, ictx->q->tmpbuf)) {
2634                                                                 bson_append_field_from_iterator(&sit, ictx->sbson);
2635                                                                 continue;
2636                                                         }
2637                                                         BSON_ITERATOR_FROM_BUFFER(&bufit, TCXSTRPTR(ictx->q->tmpbuf));
2638                                                         bson_append_object_from_iterator(BSON_ITERATOR_KEY(&sit), &bufit, ictx->sbson);
2639                                                 }
2640                                                 bson_append_finish_array(ictx->sbson);
2641                                                 rv = (BSON_VCMD_SKIP_AFTER | BSON_VCMD_SKIP_NESTED);
2642                                         }
2643                                         
2644                                 } else if (lbt == BSON_ARRAY && 
2645                                                         (bt == BSON_ARRAY || BSON_IS_NUM_TYPE(bt)) && 
2646                                                         !strcmp("$slice", dofname)) {
2647                                                                 
2648                                         bson_append_start_array(ictx->sbson, BSON_ITERATOR_KEY(it));
2649                                         int skip = 0, limit, idx = 0, i;
2650                                         char nbuff[TCNUMBUFSIZ];
2651                                         
2652                                         if (bt == BSON_ARRAY) { // $slice : [skip, limit]
2653                                                 bson_type bt2;
2654                                                 bson_iterator sit2;
2655                                                 BSON_ITERATOR_SUBITERATOR(&doit, &sit2);
2656                                                 
2657                                                 bt2 = bson_find_fieldpath_value2("0", 1, &sit2);
2658                                                 if (!BSON_IS_NUM_TYPE(bt2)) {
2659                                                         bson_append_field_from_iterator(it, ictx->sbson);
2660                                                         break;
2661                                                 }
2662                                                 skip = bson_iterator_int(&sit2);
2663                                                 
2664                                                 bt2 = bson_find_fieldpath_value2("1", 1, &sit2);
2665                                                 if (!BSON_IS_NUM_TYPE(bt2)) {
2666                                                         bson_append_field_from_iterator(it, ictx->sbson);
2667                                                         break;
2668                                                 }
2669                                                 limit = abs(bson_iterator_int(&sit2));
2670                                         } else { // $slice : limit
2671                                                 limit = abs(bson_iterator_int(&doit));
2672                                         }
2673                                         
2674                                         if (skip < 0) {
2675                                                 int cnt = 0;
2676                                                 BSON_ITERATOR_SUBITERATOR(it, &sit);
2677                                                 while (bson_iterator_next(&sit) != BSON_EOO) ++cnt;
2678                                                 skip = cnt + skip % cnt;
2679                                                 if (skip == cnt) {
2680                                                         skip = 0;
2681                                                 }
2682                                         }
2683                                         
2684                                         limit = (limit <= INT_MAX - skip) ? limit + skip : INT_MAX;
2685                                         BSON_ITERATOR_SUBITERATOR(it, &sit);
2686                                         for (i = 0; idx < limit && (bt = bson_iterator_next(&sit)) != BSON_EOO; ++idx) {
2687                                                 if (idx >= skip) {
2688                                                         bson_numstrn(nbuff, TCNUMBUFSIZ, i++);
2689                                                         bson_append_field_from_iterator2(nbuff, &sit, ictx->sbson);
2690                                                 }
2691                                         }
2692                                         bson_append_finish_array(ictx->sbson);
2693                                         rv = (BSON_VCMD_SKIP_AFTER | BSON_VCMD_SKIP_NESTED);
2694                                 }
2695                         }
2696             break;
2697         }
2698         default:
2699             bson_append_field_from_iterator(it, ictx->sbson);
2700
2701     }
2702     return rv;
2703 }
2704
2705 static bool _pushprocessedbson(_QRYCTX *ctx, const void *bsbuf, int bsbufsz) {
2706     assert(bsbuf && bsbufsz);
2707     if (!ctx->dfields && !ctx->ifields && !ctx->q->ifields) { //Trivial case: no $do operations or $fields
2708         tclistpush(ctx->res, bsbuf, bsbufsz);
2709         return true;
2710     }
2711     bool rv = true;
2712     EJDB *jb = ctx->coll->jb;
2713     EJQ *q = ctx->q;
2714     TCMAP *ifields = ctx->ifields;
2715     int sp;
2716     char bstack[JBSBUFFERSZ];
2717     bson bsout;
2718     bson_init_on_stack(&bsout, bstack, bsbufsz, JBSBUFFERSZ);
2719
2720     if (ctx->dfields) { //$do fields exists
2721         rv = _exec_do(ctx, bsbuf, &bsout);
2722     }
2723
2724     if (rv && (ifields || q->ifields)) { //$fields hints
2725         TCMAP *_ifields = ifields;
2726         TCMAP *_fkfields = NULL; //Fields with overriden keys
2727         char* inbuf = (bsout.finished) ? bsout.data : (char*) bsbuf;
2728         if (bsout.finished) {
2729             bson_init_size(&bsout, bson_size(&bsout));
2730         }
2731         if (q->ifields) { //we have positional $(projection)
2732             assert(ctx->imode == true); //ensure we are in include mode
2733             if (!_ifields) {
2734                 _ifields = tcmapnew2(q->ifields->bnum);
2735             } else {
2736                 _ifields = tcmapdup(ifields);
2737             }
2738             _fkfields = tcmapnew2(TCMAPTINYBNUM);
2739             for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
2740                 EJQF *qf = TCLISTVALPTR(q->qflist, i);
2741                 const char *dfpath = tcmapget(q->ifields, qf->fpath, qf->fpathsz, &sp);
2742                 if (dfpath) {
2743                     TCXSTR *ifield = tcxstrnew3(sp + 10);
2744                     bson_iterator it;
2745                     BSON_ITERATOR_FROM_BUFFER(&it, inbuf);
2746                     FFPCTX ctx = {
2747                         .fpath = qf->fpath,
2748                         .fplen = qf->fpathsz,
2749                         .input = &it,
2750                         .stopnestedarr = true,
2751                         .stopos = 0,
2752                         .mpos = -1,
2753                         .dpos = -1
2754                     };
2755                     const char *dpos = strchr(dfpath, '$');
2756                     assert(dpos);
2757                     ctx.dpos = (dpos - dfpath) - 1;
2758                     qf->mflags = (qf->flags & ~EJFEXCLUDED);
2759                     if (!_qrybsrecurrmatch(qf, &ctx, 0)) {
2760                         assert(false); //something wrong, it should never be happen
2761                     } else if (ctx.mpos >= 0) {
2762                         tcxstrcat(ifield, dfpath, (dpos - dfpath));
2763                         tcxstrprintf(ifield, "%d", ctx.mpos);
2764                         tcmapput(_fkfields, TCXSTRPTR(ifield), TCXSTRSIZE(ifield), "0", strlen("0"));
2765                         tcxstrcat(ifield, dpos + 1, sp - (dpos - dfpath) - 1);
2766                         tcmapput(_ifields, TCXSTRPTR(ifield), TCXSTRSIZE(ifield), &yes, sizeof (yes));
2767                     } else {
2768                         assert(false); //something wrong, it should never be happen
2769                     }
2770                     tcxstrdel(ifield);
2771                 }
2772             }
2773         }
2774         BSONSTRIPCTX sctx = {
2775             .ifields = _ifields,
2776             .fkfields = _fkfields,
2777             .imode = ctx->imode,
2778             .bsbuf = inbuf,
2779             .bsout = &bsout
2780         };
2781         if (bson_strip2(&sctx) != BSON_OK) {
2782             _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
2783         }
2784         if (inbuf != bsbuf && inbuf != bstack) {
2785             TCFREE(inbuf);
2786         }
2787         if (_ifields != ifields) {
2788             tcmapdel(_ifields);
2789         }
2790         if (_fkfields) {
2791             tcmapdel(_fkfields);
2792         }
2793     }
2794
2795     if (rv) {
2796         assert(bsout.finished);
2797         if (bsout.flags & BSON_FLAG_STACK_ALLOCATED) {
2798             TCLISTPUSH(ctx->res, bsout.data, bson_size(&bsout));
2799         } else {
2800             tclistpushmalloc(ctx->res, bsout.data, bson_size(&bsout));
2801         }
2802     } else {
2803         bson_destroy(&bsout);
2804     }
2805     return rv;
2806 }
2807
2808 static bool _exec_do(_QRYCTX *ctx, const void *bsbuf, bson *bsout) {
2809     assert(ctx && ctx->dfields);
2810     _BSONDOVISITORCTX ictx = {
2811         .q = ctx->q,
2812         .jb = ctx->coll->jb,
2813         .dfields = ctx->dfields,
2814         .sbson = bsout
2815     };
2816     bson_iterator it;
2817     BSON_ITERATOR_FROM_BUFFER(&it, bsbuf);
2818     bson_visit_fields(&it, 0, _bsondovisitor, &ictx);
2819     if (bson_finish(bsout) != BSON_OK) {
2820         _ejdbsetecode(ctx->coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
2821         return false;
2822     }
2823     return true;
2824 }
2825
2826 //Create update BSON object for $set/$unset/$inc operations
2827 static bson* _qfgetupdateobj(const EJQF *qf) {
2828     assert(qf->updateobj);
2829     if (!qf->ufields || TCLISTNUM(qf->ufields) < 1) { //we do not ref $(query) fields.
2830         return qf->updateobj;
2831     }
2832     const EJQ *q = qf->q;
2833     char pbuf[BSON_MAX_FPATH_LEN + 1];
2834     int ppos = 0;
2835     bson_iterator it;
2836     bson_type bt;
2837     bson *ret =  bson_create();
2838     bson_init(ret);
2839     for (int i = 0; i < TCLISTNUM(qf->ufields); ++i) {
2840         const char *uf = TCLISTVALPTR(qf->ufields, i);
2841         for (int j = 0; *(q->allqfields + j) != '\0'; ++j) {
2842             const EJQF *kqf = *(q->allqfields + j);
2843             if (kqf == qf || kqf->uslots == NULL || TCLISTNUM(kqf->uslots) < 1) {
2844                 continue;
2845             }
2846             for (int k = 0; k < TCLISTNUM(kqf->uslots); ++k) {
2847                 USLOT *uslot = TCLISTVALPTR(kqf->uslots, k);
2848                 if (uslot->op == uf && uslot->mpos >= 0) {
2849                     char *dp = strchr(uf, '$');
2850                     assert(dp);
2851                     ppos = (dp - uf);
2852                     assert(ppos == uslot->dpos + 1);
2853                     if (ppos < 1 || ppos >= BSON_MAX_FPATH_LEN - 1) {
2854                         break;
2855                     }
2856                     memcpy(pbuf, uf, ppos);
2857                     int wl = bson_numstrn(pbuf + ppos, (BSON_MAX_FPATH_LEN - ppos), uslot->mpos);
2858                     if (wl >= BSON_MAX_FPATH_LEN - ppos) { //output is truncated
2859                         break;
2860                     }
2861                     ppos += wl;
2862                     //copy suffix
2863                     for (int fpos = (dp - uf) + 1; ppos < BSON_MAX_FPATH_LEN && *(uf + fpos) != '\0';) {
2864                         pbuf[ppos++] = *(uf + fpos++);
2865                     }
2866                     assert(ppos <= BSON_MAX_FPATH_LEN);
2867                     pbuf[ppos] = '\0';
2868
2869                     bt = bson_find(&it, qf->updateobj, uf);
2870                     if (bt == BSON_EOO) {
2871                         assert(false);
2872                         break;
2873                     }
2874                     bson_append_field_from_iterator2(pbuf, &it, ret);
2875                     break;
2876                 }
2877             }
2878         }
2879     }
2880     BSON_ITERATOR_INIT(&it, qf->updateobj);
2881     while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
2882         const char *key = bson_iterator_key(&it);
2883         if (strchr(key, '$') == NULL) {
2884             bson_append_field_from_iterator2(key, &it, ret);
2885         }
2886     }
2887     bson_finish(ret);
2888     return ret;
2889 }
2890
2891 static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
2892     assert(ctx && ctx->q && (ctx->q->flags & EJQUPDATING) && bsbuf && ctx->didxctx);
2893
2894     bool rv = true;
2895     bool update = false;
2896     EJCOLL *coll = ctx->coll;
2897     EJQ *q = ctx->q;
2898     bson_oid_t *oid;
2899     bson_type bt, bt2;
2900     bson_iterator it, it2;
2901     TCMAP *rowm = NULL;
2902
2903     if (q->flags & EJQDROPALL) { //Record will be dropped
2904         bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME);
2905         if (bt != BSON_OID) {
2906             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
2907             return false;
2908         }
2909         oid = bson_iterator_oid(&it);
2910         assert(oid);
2911         if (ctx->log) {
2912             char xoid[25];
2913             bson_oid_to_string(oid, xoid);
2914             tcxstrprintf(ctx->log, "$DROPALL ON: %s\n", xoid);
2915         }
2916         const void *olddata;
2917         int olddatasz = 0;
2918         TCMAP *rmap = tctdbget(coll->tdb, oid, sizeof (*oid));
2919         if (rmap) {
2920             olddata = tcmapget3(rmap, JDBCOLBSON, JDBCOLBSONL, &olddatasz);
2921             if (!_updatebsonidx(coll, oid, NULL, olddata, olddatasz, ctx->didxctx) ||
2922                     !tctdbout(coll->tdb, oid, sizeof (*oid))) {
2923                 rv = false;
2924             }
2925             tcmapdel(rmap);
2926         }
2927         return rv;
2928     }
2929     //Apply update operation
2930     bson bsout;
2931     bsout.data = NULL;
2932     bsout.dataSize = 0;
2933     bson_reset(&bsout);
2934
2935     const EJQF *setqf = NULL; /*$set*/
2936     const EJQF *unsetqf = NULL; /*$unset*/
2937     const EJQF *incqf = NULL; /*$inc*/
2938         const EJQF *renameqf = NULL; /*$rename*/
2939     const EJQF *addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/
2940     const EJQF *pullqf[2] = {NULL}; /*$pull, $pullAll*/
2941
2942     //$set, $inc, $addToSet, $addToSetAll, $pull, $pullAll operations
2943     for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
2944         const EJQF *qf = TCLISTVALPTR(q->qflist, i);
2945         if (qf->updateobj == NULL) {
2946             continue;
2947         }
2948         if (qf->flags & EJCONDSET) { //$set
2949             setqf = qf;
2950             continue;
2951         }
2952         if (qf->flags & EJCONDUNSET) { //$unset
2953             unsetqf = qf;
2954             continue;
2955         }
2956         if (qf->flags & EJCONDINC) { //$inc
2957             incqf = qf;
2958             continue;
2959         }
2960                 if (qf->flags & EJCONDRENAME) { //$rename
2961                         renameqf = qf;
2962                         continue;
2963                 }
2964         if (qf->flags & EJCONDADDSET) { //$addToSet, $addToSetAll
2965             if (qf->flags & EJCONDALL) {
2966                 addsetqf[1] = qf;
2967             } else {
2968                 addsetqf[0] = qf;
2969             }
2970         }
2971         if (qf->flags & EJCONDPULL) { //$pull, $pullAll
2972             if (qf->flags & EJCONDALL) {
2973                 pullqf[1] = qf;
2974             } else {
2975                 pullqf[0] = qf;
2976             }
2977         }
2978     }
2979
2980     if (setqf) { //$set
2981         bson *updobj = _qfgetupdateobj(setqf);
2982         update = true;
2983         bson_init_size(&bsout, bsbufsz);
2984         int err = bson_merge3(bsbuf, bson_data(updobj), &bsout);
2985         if (err) {
2986             rv = false;
2987             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
2988         }
2989         bson_finish(&bsout);
2990         if (updobj != setqf->updateobj) {
2991             bson_del(updobj);
2992         }
2993     }
2994     if (!rv) {
2995         goto finish;
2996     }
2997
2998     if (incqf) { //$inc
2999         bson *updobj = _qfgetupdateobj(incqf);
3000         if (!bsout.data) {
3001             bson_create_from_buffer2(&bsout, bsbuf, bsbufsz);
3002         }
3003         BSON_ITERATOR_INIT(&it, updobj);
3004         while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
3005             if (!BSON_IS_NUM_TYPE(bt)) {
3006                 continue;
3007             }
3008             BSON_ITERATOR_INIT(&it2, &bsout);
3009             bt2 = bson_find_fieldpath_value(BSON_ITERATOR_KEY(&it), &it2);
3010             if (!BSON_IS_NUM_TYPE(bt2)) {
3011                 continue;
3012             }
3013             if (bt2 == BSON_DOUBLE) {
3014                 double v = bson_iterator_double(&it2);
3015                 if (bt == BSON_DOUBLE) {
3016                     v += bson_iterator_double(&it);
3017                 } else {
3018                     v += bson_iterator_long(&it);
3019                 }
3020                 if (bson_inplace_set_double(&it2, v)) {
3021                     rv = false;
3022                     _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3023                     break;
3024                 }
3025                 update = true;
3026             } else {
3027                 int64_t v = bson_iterator_long(&it2);
3028                 v += bson_iterator_long(&it);
3029                 if (bson_inplace_set_long(&it2, v)) {
3030                     rv = false;
3031                     _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3032                     break;
3033                 }
3034                 update = true;
3035             }
3036         }
3037         if (updobj != incqf->updateobj) {
3038             bson_del(updobj);
3039         }
3040         if (!rv) {
3041             goto finish;
3042         }
3043     }
3044
3045     for (int i = 0; i < 2; ++i) { //$pull $pullAll
3046         const EJQF *qf = pullqf[i];
3047         if (!qf) continue;
3048         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3049         if (bson_find_merged_array_sets(bson_data(qf->updateobj), inbuf, (qf->flags & EJCONDALL))) {
3050             if (bsout.finished) {
3051                 //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
3052                 bson_init_size(&bsout, bson_size(&bsout));
3053             } else {
3054                 assert(bsout.data == NULL);
3055                 bson_init_size(&bsout, bsbufsz);
3056             }
3057             //$pull $pullAll merge
3058             if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, true, (qf->flags & EJCONDALL), &bsout)) {
3059                 rv = false;
3060                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3061             }
3062             if (inbuf != bsbuf) {
3063                 TCFREE(inbuf);
3064             }
3065             bson_finish(&bsout);
3066             update = true;
3067         }
3068         if (!rv) {
3069             goto finish;
3070         }
3071     }
3072
3073     for (int i = 0; i < 2; ++i) { //$addToSet $addToSetAll
3074         const EJQF *qf = addsetqf[i];
3075         if (!qf) continue;
3076         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3077         if ((qf->flags & EJCONDALL) || bson_find_unmerged_array_sets(bson_data(qf->updateobj), inbuf)) {
3078             //Missing $addToSet element in some array field found
3079             if (bsout.finished) {
3080                 //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
3081                 bson_init_size(&bsout, bson_size(&bsout));
3082             } else {
3083                 assert(bsout.data == NULL);
3084                 bson_init_size(&bsout, bsbufsz);
3085             }
3086             //$addToSet $addToSetAll merge
3087             if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, false, (qf->flags & EJCONDALL), &bsout)) {
3088                 rv = false;
3089                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3090             }
3091             if (inbuf != bsbuf) {
3092                 TCFREE(inbuf);
3093             }
3094             bson_finish(&bsout);
3095             update = true;
3096         }
3097     }
3098
3099     if (unsetqf) { //$unset
3100         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3101         if (bsout.finished) {
3102             //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
3103             bson_init_size(&bsout, bson_size(&bsout));
3104         } else {
3105             assert(bsout.data == NULL);
3106             bson_init_size(&bsout, bsbufsz);
3107         }
3108         bson *updobj = _qfgetupdateobj(unsetqf);
3109         TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM);
3110         BSON_ITERATOR_INIT(&it, updobj);
3111         while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
3112             const char *fpath = BSON_ITERATOR_KEY(&it);
3113             tcmapput(ifields, fpath, strlen(fpath), &yes, sizeof(yes));
3114         }
3115         if (bson_strip(ifields, false, inbuf, &bsout) != BSON_OK) {
3116             rv = false;
3117             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3118         }
3119         tcmapdel(ifields);
3120         if (inbuf != bsbuf) {
3121             TCFREE(inbuf);
3122         }
3123         bson_finish(&bsout);
3124         update = true;
3125     }
3126         if (!rv) {
3127                 goto finish;
3128         }
3129
3130         if (renameqf) {
3131         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3132         if (bsout.finished) {
3133             //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
3134             bson_init_size(&bsout, bson_size(&bsout));
3135         } else {
3136             assert(bsout.data == NULL);
3137             bson_init_size(&bsout, bsbufsz);
3138         }
3139
3140         TCMAP *efields = tcmapnew2(TCMAPTINYBNUM);
3141                 bson *updobj = _qfgetupdateobj(renameqf);
3142                 BSON_ITERATOR_INIT(&it, updobj);
3143                 while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
3144                         if (bt != BSON_STRING) {
3145                                 continue;
3146                         }
3147             const char *ofpath = BSON_ITERATOR_KEY(&it);
3148                         const char *nfpath = bson_iterator_string(&it);
3149                         
3150                         BSON_ITERATOR_FROM_BUFFER(&it2, inbuf);
3151             bt2 = bson_find_from_buffer(&it2, inbuf, ofpath);
3152                         if (bt2 == BSON_EOO) { 
3153                                 continue;
3154                         }
3155
3156                         if (bson_append_field_from_iterator2(nfpath, &it2, &bsout) != BSON_OK) {
3157                                 rv = false;
3158                                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3159                                 break;
3160                         }
3161                         tcmapputkeep(efields, ofpath, strlen(ofpath), "", 0);
3162                         tcmapputkeep(efields, nfpath, strlen(nfpath), "", 0);
3163                 }
3164                 
3165         BSON_ITERATOR_FROM_BUFFER(&it, inbuf);
3166         while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
3167             const char *fpath = BSON_ITERATOR_KEY(&it);
3168                         if (tcmapget2(efields, fpath)) { 
3169                                 continue;
3170                         }
3171                         
3172                         if (bson_append_field_from_iterator(&it, &bsout) != BSON_OK) {
3173                                 rv = false;
3174                                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3175                                 break;
3176                         }
3177                 }
3178         tcmapdel(efields);
3179         if (inbuf != bsbuf) {
3180             TCFREE(inbuf);
3181         }
3182         bson_finish(&bsout);
3183         update = true;
3184         }
3185
3186     if (!update || !rv) {
3187         goto finish;
3188     }
3189     if (bsout.err) { //Resulting BSON is OK?
3190         rv = false;
3191         _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3192         goto finish;
3193     }
3194     if (bson_size(&bsout) == 0) { //Record was not updated
3195         goto finish;
3196     }
3197     //Perform updating
3198     bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME);
3199     if (bt != BSON_OID) {
3200         rv = false;
3201         _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3202         goto finish;
3203     }
3204     oid = bson_iterator_oid(&it);
3205     rowm = tcmapnew2(TCMAPTINYBNUM);
3206     tcmapput(rowm, JDBCOLBSON, JDBCOLBSONL, bson_data(&bsout), bson_size(&bsout));
3207     rv = tctdbput(coll->tdb, oid, sizeof (*oid), rowm);
3208     if (rv) {
3209         rv = _updatebsonidx(coll, oid, &bsout, bsbuf, bsbufsz, ctx->didxctx);
3210     }
3211
3212 finish:
3213     bson_destroy(&bsout);
3214     if (rowm) {
3215         tcmapdel(rowm);
3216     }
3217     return rv;
3218 }
3219
3220 /** Query */
3221 static TCLIST* _qryexecute(EJCOLL *coll, const EJQ *_q, uint32_t *outcount, int qflags, TCXSTR *log) {
3222     assert(coll && coll->tdb && coll->tdb->hdb);
3223     *outcount = 0;
3224
3225     _QRYCTX ctx = {NULL};
3226     EJQ *q; //Clone the query object
3227     TCMALLOC(q, sizeof (*q));
3228     if (!_qrydup(_q, q, EJQINTERNAL)) {
3229         TCFREE(q);
3230         return NULL;
3231     }
3232     ctx.log = log;
3233     ctx.q = q;
3234     ctx.qflags = qflags;
3235     ctx.coll = coll;
3236     if (!_qrypreprocess(&ctx)) {
3237         _qryctxclear(&ctx);
3238         return NULL;
3239     }
3240     bool all = false; //if True we need all records to fetch (sorting)
3241     TCHDB *hdb = coll->tdb->hdb;
3242     TCLIST *res = ctx.res;
3243     EJQF *mqf = ctx.mqf;
3244
3245     int sz = 0; //generic size var
3246     int anum = 0; //number of active conditions
3247     int ofsz = 0; //order fields count
3248     int aofsz = 0; //active order fields count
3249     const int qfsz = TCLISTNUM(q->qflist); //number of all condition fields
3250     EJQF **ofs = NULL; //order fields
3251     EJQF **qfs = NULL; //condition fields array
3252     if (qfsz > 0) {
3253         TCMALLOC(qfs, qfsz * sizeof (EJQF*));
3254     }
3255
3256     const void *kbuf;
3257     int kbufsz;
3258     const void *vbuf;
3259     int vbufsz;
3260
3261     uint32_t count = 0; //current count
3262     uint32_t max = (q->max > 0) ? q->max : UINT_MAX;
3263     uint32_t skip = q->skip;
3264     const TDBIDX *midx = mqf ? mqf->idx : NULL;
3265
3266     if (midx) { //Main index used for ordering
3267         if (mqf->orderseq == 1 &&
3268                 !(mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR)) {
3269             mqf->flags |= EJFORDERUSED;
3270         }
3271     }
3272     for (int i = 0; i < qfsz; ++i) {
3273         EJQF *qf = TCLISTVALPTR(q->qflist, i);
3274         assert(qf);
3275         if (log) {
3276             if (qf->exprmap) {
3277                 tcxstrprintf(log, "USING HASH TOKENS IN: %s\n", qf->fpath);
3278             }
3279             if (qf->flags & EJCONDOIT) {
3280                 tcxstrprintf(log, "FIELD: %s HAS $do OPERATION\n", qf->fpath);
3281             }
3282         }
3283         qf->jb = coll->jb;
3284         qfs[i] = qf;
3285         if (qf->fpathsz > 0 && !(qf->flags & EJFEXCLUDED)) {
3286             anum++;
3287         }
3288         if (qf->orderseq) {
3289             ofsz++;
3290             if (q->flags & EJQONLYCOUNT) {
3291                 qf->flags |= EJFORDERUSED;
3292             }
3293         }
3294     }
3295     if (ofsz > 0) { //Collect order fields array
3296         TCMALLOC(ofs, ofsz * sizeof (EJQF*));
3297         for (int i = 0; i < ofsz; ++i) {
3298             for (int j = 0; j < qfsz; ++j) {
3299                 if (qfs[j]->orderseq == i + 1) { //orderseq starts with 1
3300                     ofs[i] = qfs[j];
3301                     if (!(ofs[i]->flags & EJFORDERUSED)) {
3302                         aofsz++;
3303                         if (ctx.ifields) { //Force order field to be included in result set
3304                             if (ctx.imode) { //add field to the included set
3305                                 tcmapputkeep(ctx.ifields, ofs[i]->fpath, ofs[i]->fpathsz, &yes, sizeof (yes));
3306                             } else { //remove field from excluded
3307                                 tcmapout(ctx.ifields, ofs[i]->fpath, ofs[i]->fpathsz);
3308                             }
3309                         }
3310                     }
3311                     break;
3312                 }
3313             }
3314         }
3315         for (int i = 0; i < ofsz; ++i) assert(ofs[i] != NULL);
3316     }
3317
3318     if ((q->flags & EJQONLYCOUNT) && qfsz == 0 &&
3319             (q->orqlist == NULL || TCLISTNUM(q->orqlist) < 1) &&
3320             (q->andqlist == NULL || TCLISTNUM(q->andqlist) < 1)) { //primitive count(*) query
3321         count = coll->tdb->hdb->rnum;
3322         if (log) {
3323             tcxstrprintf(log, "SIMPLE COUNT(*): %u\n", count);
3324         }
3325         goto finish;
3326     }
3327
3328     if (!(q->flags & EJQONLYCOUNT) && aofsz > 0 && (!midx || mqf->orderseq != 1)) { //Main index is not the main order field
3329         all = true; //Need all records for ordering for some other fields
3330     }
3331
3332     if (log) {
3333         tcxstrprintf(log, "UPDATING MODE: %s\n", (q->flags & EJQUPDATING) ? "YES" : "NO");
3334         tcxstrprintf(log, "MAX: %u\n", max);
3335         tcxstrprintf(log, "SKIP: %u\n", skip);
3336         tcxstrprintf(log, "COUNT ONLY: %s\n", (q->flags & EJQONLYCOUNT) ? "YES" : "NO");
3337         tcxstrprintf(log, "MAIN IDX: '%s'\n", midx ? midx->name : "NONE");
3338         tcxstrprintf(log, "ORDER FIELDS: %d\n", ofsz);
3339         tcxstrprintf(log, "ACTIVE CONDITIONS: %d\n", anum);
3340         tcxstrprintf(log, "ROOT $OR QUERIES: %d\n", ((q->orqlist) ? TCLISTNUM(q->orqlist) : 0));
3341         tcxstrprintf(log, "ROOT $AND QUERIES: %d\n", ((q->andqlist) ? TCLISTNUM(q->andqlist) : 0));
3342         tcxstrprintf(log, "FETCH ALL: %s\n", all ? "YES" : "NO");
3343     }
3344     if (max < UINT_MAX - skip) {
3345         max += skip;
3346     }
3347     if (max == 0) {
3348         goto finish;
3349     }
3350     if (!midx && (!mqf || !(mqf->flags & EJFPKMATCHING))) { //Missing main index & no PK matching
3351         goto fullscan;
3352     }
3353     if (log) {
3354         tcxstrprintf(log, "MAIN IDX TCOP: %d\n", mqf->tcop);
3355     }
3356
3357 #define JBQREGREC(_pkbuf, _pkbufsz, _bsbuf, _bsbufsz)   \
3358     ++count; \
3359     if (q->flags & EJQUPDATING) { \
3360         _qryupdate(&ctx, (_bsbuf), (_bsbufsz)); \
3361     } \
3362     if (!(q->flags & EJQONLYCOUNT) && (all || count > skip)) { \
3363         _pushprocessedbson(&ctx, (_bsbuf), (_bsbufsz)); \
3364     }
3365     //EOF #define JBQREGREC
3366
3367     bool trim = (midx && *midx->name != '\0');
3368     if (anum > 0 && !(mqf->flags & EJFEXCLUDED) && !(mqf->uslots && TCLISTNUM(mqf->uslots) > 0)) {
3369         anum--;
3370         mqf->flags |= EJFEXCLUDED;
3371     }
3372
3373     if (mqf->flags & EJFPKMATCHING) { //PK matching
3374         if (log) {
3375             tcxstrprintf(log, "PRIMARY KEY MATCHING: TRUE\n");
3376         }
3377         assert(mqf->expr);
3378         if (mqf->tcop == TDBQCSTREQ) {
3379             do {
3380                 bson_oid_t oid;
3381                 bson_oid_from_string(&oid, mqf->expr);
3382                 tcxstrclear(q->colbuf);
3383                 tcxstrclear(q->bsbuf);
3384                 sz = tchdbgetintoxstr(coll->tdb->hdb, &oid, sizeof (oid), q->colbuf);
3385                 if (sz <= 0) {
3386                     break;
3387                 }
3388                 sz = tcmaploadoneintoxstr(TCXSTRPTR(q->colbuf), TCXSTRSIZE(q->colbuf), JDBCOLBSON, JDBCOLBSONL, q->bsbuf);
3389                 if (sz <= 0) {
3390                     break;
3391                 }
3392                 bool matched = true;
3393                 for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags;
3394                 for (int i = 0; i < qfsz; ++i) {
3395                     EJQF *qf = qfs[i];
3396                     if (qf->mflags & EJFEXCLUDED) continue;
3397                     if (!_qrybsmatch(qf, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf))) {
3398                         matched = false;
3399                         break;
3400                     }
3401                 }
3402                 if (matched && _qry_and_or_match(coll, q, &oid, sizeof (oid))) {
3403                     JBQREGREC(&oid, sizeof (oid), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3404                 }
3405             } while (false);
3406         } else if (mqf->tcop == TDBQCSTROREQ) {
3407             TCLIST *tokens = mqf->exprlist;
3408             assert(tokens);
3409             tclistsort(tokens);
3410             for (int i = 1; i < TCLISTNUM(tokens); i++) {
3411                 if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3412                     TCFREE(tclistremove2(tokens, i));
3413                     i--;
3414                 }
3415             }
3416             int tnum = TCLISTNUM(tokens);
3417             for (int i = 0; (all || count < max) && i < tnum; i++) {
3418                 bool matched = true;
3419                 bson_oid_t oid;
3420                 const char *token;
3421                 int tsiz;
3422                 TCLISTVAL(token, tokens, i, tsiz);
3423                 if (tsiz < 1) {
3424                     continue;
3425                 }
3426                 bson_oid_from_string(&oid, token);
3427                 tcxstrclear(q->bsbuf);
3428                 tcxstrclear(q->colbuf);
3429                 sz = tchdbgetintoxstr(coll->tdb->hdb, &oid, sizeof (oid), q->colbuf);
3430                 if (sz <= 0) {
3431                     continue;
3432                 }
3433                 sz = tcmaploadoneintoxstr(TCXSTRPTR(q->colbuf), TCXSTRSIZE(q->colbuf), JDBCOLBSON, JDBCOLBSONL, q->bsbuf);
3434                 if (sz <= 0) {
3435                     continue;
3436                 }
3437                 for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags;
3438                 for (int i = 0; i < qfsz; ++i) {
3439                     EJQF *qf = qfs[i];
3440                     if (qf->mflags & EJFEXCLUDED) continue;
3441                     if (!_qrybsmatch(qf, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf))) {
3442                         matched = false;
3443                         break;
3444                     }
3445                 }
3446                 if (matched && _qry_and_or_match(coll, q, &oid, sizeof (oid))) {
3447                     JBQREGREC(&oid, sizeof (oid), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3448                 }
3449             }
3450         } else {
3451             assert(0);
3452         }
3453     } else if (mqf->tcop == TDBQTRUE) {
3454         BDBCUR *cur = tcbdbcurnew(midx->db);
3455         if (mqf->order >= 0) {
3456             tcbdbcurfirst(cur);
3457         } else {
3458             tcbdbcurlast(cur);
3459         }
3460         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3461             if (trim) kbufsz -= 3;
3462             vbuf = tcbdbcurval3(cur, &vbufsz);
3463             if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3464                 JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3465             }
3466             if (mqf->order >= 0) {
3467                 tcbdbcurnext(cur);
3468             } else {
3469                 tcbdbcurprev(cur);
3470             }
3471         }
3472         tcbdbcurdel(cur);
3473     } else if (mqf->tcop == TDBQCSTREQ) { /* string is equal to */
3474         assert(midx->type == TDBITLEXICAL);
3475         char *expr = mqf->expr;
3476         int exprsz = mqf->exprsz;
3477         BDBCUR *cur = tcbdbcurnew(midx->db);
3478         tcbdbcurjump(cur, expr, exprsz + trim);
3479         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3480             if (trim) kbufsz -= 3;
3481             if (kbufsz == exprsz && !memcmp(kbuf, expr, exprsz)) {
3482                 vbuf = tcbdbcurval3(cur, &vbufsz);
3483                 if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3484                     JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3485                 }
3486             } else {
3487                 break;
3488             }
3489             tcbdbcurnext(cur);
3490         }
3491         tcbdbcurdel(cur);
3492     } else if (mqf->tcop == TDBQCSTRBW) { /* string begins with */
3493         assert(midx->type == TDBITLEXICAL);
3494         char *expr = mqf->expr;
3495         int exprsz = mqf->exprsz;
3496         BDBCUR *cur = tcbdbcurnew(midx->db);
3497         tcbdbcurjump(cur, expr, exprsz + trim);
3498         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3499             if (trim) kbufsz -= 3;
3500             if (kbufsz >= exprsz && !memcmp(kbuf, expr, exprsz)) {
3501                 vbuf = tcbdbcurval3(cur, &vbufsz);
3502                 if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3503                     JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3504                 }
3505             } else {
3506                 break;
3507             }
3508             tcbdbcurnext(cur);
3509         }
3510         tcbdbcurdel(cur);
3511     } else if (mqf->tcop == TDBQCSTRORBW) { /* string begins with one token in */
3512         assert(mqf->ftype == BSON_ARRAY);
3513         assert(midx->type == TDBITLEXICAL);
3514         BDBCUR *cur = tcbdbcurnew(midx->db);
3515         TCLIST *tokens = mqf->exprlist;
3516         assert(tokens);
3517         tclistsort(tokens);
3518         for (int i = 1; i < TCLISTNUM(tokens); i++) {
3519             if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3520                 TCFREE(tclistremove2(tokens, i));
3521                 i--;
3522             }
3523         }
3524         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) {
3525             tclistinvert(tokens);
3526         }
3527         int tnum = TCLISTNUM(tokens);
3528         for (int i = 0; (all || count < max) && i < tnum; i++) {
3529             const char *token;
3530             int tsiz;
3531             TCLISTVAL(token, tokens, i, tsiz);
3532             if (tsiz < 1) continue;
3533             tcbdbcurjump(cur, token, tsiz + trim);
3534             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3535                 if (trim) kbufsz -= 3;
3536                 if (kbufsz >= tsiz && !memcmp(kbuf, token, tsiz)) {
3537                     vbuf = tcbdbcurval3(cur, &vbufsz);
3538                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3539                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3540                     }
3541                 } else {
3542                     break;
3543                 }
3544                 tcbdbcurnext(cur);
3545             }
3546         }
3547         tcbdbcurdel(cur);
3548     } else if (mqf->tcop == TDBQCSTROREQ) { /* string is equal to at least one token in */
3549         assert(mqf->ftype == BSON_ARRAY);
3550         assert(midx->type == TDBITLEXICAL);
3551         BDBCUR *cur = tcbdbcurnew(midx->db);
3552         TCLIST *tokens = mqf->exprlist;
3553         assert(tokens);
3554         tclistsort(tokens);
3555         for (int i = 1; i < TCLISTNUM(tokens); i++) {
3556             if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3557                 TCFREE(tclistremove2(tokens, i));
3558                 i--;
3559             }
3560         }
3561         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) {
3562             tclistinvert(tokens);
3563         }
3564         int tnum = TCLISTNUM(tokens);
3565         for (int i = 0; (all || count < max) && i < tnum; i++) {
3566             const char *token;
3567             int tsiz;
3568             TCLISTVAL(token, tokens, i, tsiz);
3569             if (tsiz < 1) continue;
3570             tcbdbcurjump(cur, token, tsiz + trim);
3571             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3572                 if (trim) kbufsz -= 3;
3573                 if (kbufsz == tsiz && !memcmp(kbuf, token, tsiz)) {
3574                     vbuf = tcbdbcurval3(cur, &vbufsz);
3575                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3576                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3577                     }
3578                 } else {
3579                     break;
3580                 }
3581                 tcbdbcurnext(cur);
3582             }
3583         }
3584         tcbdbcurdel(cur);
3585     } else if (mqf->tcop == TDBQCNUMEQ) { /* number is equal to */
3586         assert(midx->type == TDBITDECIMAL);
3587         char *expr = mqf->expr;
3588         int exprsz = mqf->exprsz;
3589         BDBCUR *cur = tcbdbcurnew(midx->db);
3590         _EJDBNUM num;
3591         _nufetch(&num, expr, mqf->ftype);
3592         tctdbqryidxcurjumpnum(cur, expr, exprsz, true);
3593         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3594             if (_nucmp(&num, kbuf, mqf->ftype) == 0) {
3595                 vbuf = tcbdbcurval3(cur, &vbufsz);
3596                 if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3597                     JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3598                 }
3599             } else {
3600                 break;
3601             }
3602             tcbdbcurnext(cur);
3603         }
3604         tcbdbcurdel(cur);
3605     } else if (mqf->tcop == TDBQCNUMGT || mqf->tcop == TDBQCNUMGE) {
3606         /* number is greater than | number is greater than or equal to */
3607         assert(midx->type == TDBITDECIMAL);
3608         char *expr = mqf->expr;
3609         int exprsz = mqf->exprsz;
3610         BDBCUR *cur = tcbdbcurnew(midx->db);
3611         _EJDBNUM xnum;
3612         _nufetch(&xnum, expr, mqf->ftype);
3613         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { //DESC
3614             tcbdbcurlast(cur);
3615             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3616                 _EJDBNUM knum;
3617                 _nufetch(&knum, kbuf, mqf->ftype);
3618                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3619                 if (cmp < 0) break;
3620                 if (cmp > 0 || (mqf->tcop == TDBQCNUMGE && cmp >= 0)) {
3621                     vbuf = tcbdbcurval3(cur, &vbufsz);
3622                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3623                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3624                     }
3625                 }
3626                 tcbdbcurprev(cur);
3627             }
3628         } else { //ASC
3629             tctdbqryidxcurjumpnum(cur, expr, exprsz, true);
3630             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3631                 _EJDBNUM knum;
3632                 _nufetch(&knum, kbuf, mqf->ftype);
3633                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3634                 if (cmp > 0 || (mqf->tcop == TDBQCNUMGE && cmp >= 0)) {
3635                     vbuf = tcbdbcurval3(cur, &vbufsz);
3636                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3637                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3638                     }
3639                 }
3640                 tcbdbcurnext(cur);
3641             }
3642         }
3643         tcbdbcurdel(cur);
3644     } else if (mqf->tcop == TDBQCNUMLT || mqf->tcop == TDBQCNUMLE) {
3645         /* number is less than | number is less than or equal to */
3646         assert(midx->type == TDBITDECIMAL);
3647         char *expr = mqf->expr;
3648         int exprsz = mqf->exprsz;
3649         BDBCUR *cur = tcbdbcurnew(midx->db);
3650         _EJDBNUM xnum;
3651         _nufetch(&xnum, expr, mqf->ftype);
3652         if (mqf->order >= 0) { //ASC
3653             tcbdbcurfirst(cur);
3654             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3655                 _EJDBNUM knum;
3656                 _nufetch(&knum, kbuf, mqf->ftype);
3657                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3658                 if (cmp > 0) break;
3659                 if (cmp < 0 || (cmp <= 0 && mqf->tcop == TDBQCNUMLE)) {
3660                     vbuf = tcbdbcurval3(cur, &vbufsz);
3661                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3662                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3663                     }
3664                 }
3665                 tcbdbcurnext(cur);
3666             }
3667         } else {
3668             tctdbqryidxcurjumpnum(cur, expr, exprsz, false);
3669             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3670                 _EJDBNUM knum;
3671                 _nufetch(&knum, kbuf, mqf->ftype);
3672                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3673                 if (cmp < 0 || (cmp <= 0 && mqf->tcop == TDBQCNUMLE)) {
3674                     vbuf = tcbdbcurval3(cur, &vbufsz);
3675                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3676                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3677                     }
3678                 }
3679                 tcbdbcurprev(cur);
3680             }
3681         }
3682         tcbdbcurdel(cur);
3683     } else if (mqf->tcop == TDBQCNUMBT) { /* number is between two tokens of */
3684         assert(mqf->ftype == BSON_ARRAY);
3685         assert(midx->type == TDBITDECIMAL);
3686         assert(mqf->exprlist);
3687         TCLIST *tokens = mqf->exprlist;
3688         assert(TCLISTNUM(tokens) == 2);
3689         const char *expr;
3690         int exprsz;
3691         long double lower = tcatof2(tclistval2(tokens, 0));
3692         long double upper = tcatof2(tclistval2(tokens, 1));
3693         expr = tclistval2(tokens, (lower > upper) ? 1 : 0);
3694         exprsz = strlen(expr);
3695         if (lower > upper) {
3696             long double swap = lower;
3697             lower = upper;
3698             upper = swap;
3699         }
3700         BDBCUR *cur = tcbdbcurnew(midx->db);
3701         tctdbqryidxcurjumpnum(cur, expr, exprsz, true);
3702         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3703             if (tcatof2(kbuf) > upper) break;
3704             vbuf = tcbdbcurval3(cur, &vbufsz);
3705             if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3706                 JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3707             }
3708             tcbdbcurnext(cur);
3709         }
3710         tcbdbcurdel(cur);
3711         if (!all && !(q->flags & EJQONLYCOUNT) && mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { //DESC
3712             tclistinvert(res);
3713         }
3714     } else if (mqf->tcop == TDBQCNUMOREQ) { /* number is equal to at least one token in */
3715         assert(mqf->ftype == BSON_ARRAY);
3716         assert(midx->type == TDBITDECIMAL);
3717         BDBCUR *cur = tcbdbcurnew(midx->db);
3718         TCLIST *tokens = mqf->exprlist;
3719         assert(tokens);
3720         tclistsortex(tokens, tdbcmppkeynumasc);
3721         for (int i = 1; i < TCLISTNUM(tokens); i++) {
3722             if (tcatof2(TCLISTVALPTR(tokens, i)) == tcatof2(TCLISTVALPTR(tokens, i - 1))) {
3723                 TCFREE(tclistremove2(tokens, i));
3724                 i--;
3725             }
3726         }
3727         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) {
3728             tclistinvert(tokens);
3729         }
3730         int tnum = TCLISTNUM(tokens);
3731         for (int i = 0; (all || count < max) && i < tnum; i++) {
3732             const char *token;
3733             int tsiz;
3734             TCLISTVAL(token, tokens, i, tsiz);
3735             if (tsiz < 1) continue;
3736             long double xnum = tcatof2(token);
3737             tctdbqryidxcurjumpnum(cur, token, tsiz, true);
3738             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3739                 if (tcatof2(kbuf) == xnum) {
3740                     vbuf = tcbdbcurval3(cur, &vbufsz);
3741                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3742                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3743                     }
3744                 } else {
3745                     break;
3746                 }
3747                 tcbdbcurnext(cur);
3748             }
3749         }
3750         tcbdbcurdel(cur);
3751     } else if (mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR) {
3752         /* string includes all tokens in | string includes at least one token in */
3753         assert(midx->type == TDBITTOKEN);
3754         assert(mqf->ftype == BSON_ARRAY);
3755         TCLIST *tokens = mqf->exprlist;
3756         assert(tokens);
3757         if (mqf->tcop == TDBQCSTRNUMOR) {
3758             tclistsortex(tokens, tdbcmppkeynumasc);
3759             for (int i = 1; i < TCLISTNUM(tokens); i++) {
3760                 if (tcatof2(TCLISTVALPTR(tokens, i)) == tcatof2(TCLISTVALPTR(tokens, i - 1))) {
3761                     TCFREE(tclistremove2(tokens, i));
3762                     i--;
3763                 }
3764             }
3765         } else {
3766             tclistsort(tokens);
3767             for (int i = 1; i < TCLISTNUM(tokens); i++) {
3768                 if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3769                     TCFREE(tclistremove2(tokens, i));
3770                     i--;
3771                 }
3772             }
3773         }
3774         TCMAP *tres = tctdbidxgetbytokens(coll->tdb, midx, tokens, mqf->tcop, log);
3775         tcmapiterinit(tres);
3776         while ((all || count < max) && (kbuf = tcmapiternext(tres, &kbufsz)) != NULL) {
3777             if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, kbuf, kbufsz) && _qry_and_or_match(coll, q, kbuf, kbufsz)) {
3778                 JBQREGREC(kbuf, kbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3779             }
3780         }
3781         tcmapdel(tres);
3782     }
3783
3784     if (q->flags & EJQONLYCOUNT) {
3785         goto finish;
3786     } else {
3787         goto sorting;
3788     }
3789
3790 fullscan: /* Full scan */
3791     assert(count == 0);
3792     assert(!res || TCLISTNUM(res) == 0);
3793
3794     if ((q->flags & EJQDROPALL) && (q->flags & EJQONLYCOUNT)) {
3795         //if we are in primitive $dropall case. Query: {$dropall:true}
3796         if (qfsz == 1 && qfs[0]->tcop == TDBQTRUE) { //single $dropall field
3797             if (log) {
3798                 tcxstrprintf(log, "VANISH WHOLE COLLECTION ON $dropall\n");
3799             }
3800             //write lock already acquired so use impl
3801             count = coll->tdb->hdb->rnum;
3802             if (!tctdbvanish(coll->tdb)) {
3803                 count = 0;
3804             }
3805             goto finish;
3806         }
3807     }
3808
3809     if (log) {
3810         tcxstrprintf(log, "RUN FULLSCAN\n");
3811     }
3812     TCMAP *updkeys = (q->flags & EJQUPDATING) ? tcmapnew2(100 * 1024) : NULL;
3813     TCHDBITER *hdbiter = tchdbiter2init(hdb);
3814     if (!hdbiter) {
3815         goto finish;
3816     }
3817     TCXSTR *skbuf = tcxstrnew3(sizeof (bson_oid_t) + 1);
3818     tcxstrclear(q->colbuf);
3819     tcxstrclear(q->bsbuf);
3820     int rows = 0;
3821     while ((all || count < max) && tchdbiter2next(hdb, hdbiter, skbuf, q->colbuf)) {
3822         ++rows;
3823         sz = tcmaploadoneintoxstr(TCXSTRPTR(q->colbuf), TCXSTRSIZE(q->colbuf), JDBCOLBSON, JDBCOLBSONL, q->bsbuf);
3824         if (sz <= 0) {
3825             goto wfinish;
3826         }
3827         bool matched = true;
3828         for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags;
3829         for (int i = 0; i < qfsz; ++i) {
3830             EJQF *qf = qfs[i];
3831             if (qf->mflags & EJFEXCLUDED) {
3832                 continue;
3833             }
3834             if (!_qrybsmatch(qf, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf))) {
3835                 matched = false;
3836                 break;
3837             }
3838         }
3839         if (matched && _qry_and_or_match(coll, q, TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf))) {
3840             if (updkeys) { //we are in updating mode
3841                 if (tcmapputkeep(updkeys, TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf), &yes, sizeof (yes))) {
3842                     JBQREGREC(TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3843                 }
3844             } else {
3845                 JBQREGREC(TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3846             }
3847         }
3848 wfinish:
3849         tcxstrclear(skbuf);
3850         tcxstrclear(q->colbuf);
3851         tcxstrclear(q->bsbuf);
3852     }
3853     tchdbiter2dispose(hdb, hdbiter);
3854     tcxstrdel(skbuf);
3855     if (updkeys) {
3856         tcmapdel(updkeys);
3857     }
3858
3859 sorting: /* Sorting resultset */
3860     if (!res || aofsz <= 0) { //No sorting needed
3861         goto finish;
3862     }
3863     _EJBSORTCTX sctx; //sorting context
3864     sctx.ofs = ofs;
3865     sctx.ofsz = ofsz;
3866     ejdbqsortlist(res, _ejdbsoncmp, &sctx);
3867
3868 finish:
3869     //check $upsert operation
3870     if (count == 0 && (q->flags & EJQUPDATING)) { //finding $upsert qf if no updates maden
3871         for (int i = 0; i < qfsz; ++i) {
3872             if (qfs[i]->flags & EJCONDUPSERT) {
3873                 bson *updateobj = qfs[i]->updateobj;
3874                 assert(updateobj);
3875                 bson_oid_t oid;
3876                 if (_ejdbsavebsonimpl(coll, updateobj, &oid, false)) {
3877                     bson *nbs = bson_create();
3878                     bson_init_size(nbs, bson_size(updateobj) + (strlen(JDBIDKEYNAME) + 1/*key*/ + 1/*type*/ + sizeof (oid)));
3879                     bson_append_oid(nbs, JDBIDKEYNAME, &oid);
3880                     bson_ensure_space(nbs, bson_size(updateobj) - 4);
3881                     bson_append(nbs, bson_data(updateobj) + 4, bson_size(updateobj) - (4 + 1/*BSON_EOO*/));
3882                     bson_finish(nbs);
3883                     if (nbs->err) {
3884                         _ejdbsetecode(coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
3885                         break;
3886                     }
3887                     ++count;
3888                     if (!(q->flags & EJQONLYCOUNT) && (all || count > skip)) {
3889                         _pushprocessedbson(&ctx, bson_data(nbs), bson_size(nbs));
3890                     }
3891                     bson_del(nbs);
3892                 }
3893                 break;
3894             }
3895         }
3896     } //EOF $upsert
3897
3898     //revert max
3899     if (max < UINT_MAX && max > skip) {
3900         max = max - skip;
3901     }
3902     if (res) {
3903         if (all) { //skipping results after full sorting with skip > 0
3904             for (int i = 0; i < skip && res->num > 0; ++i) {
3905                 TCFREE(res->array[res->start].ptr);
3906                 ++(res->start);
3907                 --(res->num);
3908             }
3909         }
3910         if ((res->start & 0xff) == 0 && res->start > (res->num >> 1)) {
3911             memmove(res->array, res->array + res->start, res->num * sizeof (res->array[0]));
3912             res->start = 0;
3913         }
3914         if (TCLISTNUM(res) > max) { //truncate results if max specified
3915             int end = res->start + res->num;
3916             TCLISTDATUM *array = res->array;
3917             for (int i = (res->start + max); i < end; i++) {
3918                 TCFREE(array[i].ptr);
3919                 --(res->num);
3920             }
3921         }
3922     }
3923     count = (skip < count) ? count - skip : 0;
3924     if (count > max) {
3925         count = max;
3926     }
3927     *outcount = count;
3928     if (log) {
3929         tcxstrprintf(log, "RS COUNT: %u\n", count);
3930         tcxstrprintf(log, "RS SIZE: %d\n", (res ? TCLISTNUM(res) : 0));
3931         tcxstrprintf(log, "FINAL SORTING: %s\n", ((q->flags & EJQONLYCOUNT) || aofsz <= 0) ? "NO" : "YES");
3932     }
3933
3934     //Apply deffered index changes
3935     if (ctx.didxctx) {
3936         for (int i = TCLISTNUM(ctx.didxctx) - 1; i >= 0; --i) {
3937             _DEFFEREDIDXCTX *di = TCLISTVALPTR(ctx.didxctx, i);
3938             assert(di);
3939             if (di->rmap) {
3940                 tctdbidxout2(coll->tdb, &(di->oid), sizeof (di->oid), di->rmap);
3941                 tcmapdel(di->rmap);
3942             }
3943             if (di->imap) {
3944                 tctdbidxput2(coll->tdb, &(di->oid), sizeof (di->oid), di->imap);
3945                 tcmapdel(di->imap);
3946             }
3947         }
3948     }
3949     //Cleanup
3950     if (qfs) {
3951         TCFREE(qfs);
3952     }
3953     if (ofs) {
3954         TCFREE(ofs);
3955     }
3956     ctx.res = NULL; //save res from deleting in `_qryctxclear()`
3957     _qryctxclear(&ctx);
3958 #undef JBQREGREC
3959     return res;
3960 }
3961
3962 static void _qryctxclear(_QRYCTX *ctx) {
3963     if (ctx->dfields) {
3964         tcmapdel(ctx->dfields);
3965     }
3966     if (ctx->ifields) {
3967         tcmapdel(ctx->ifields);
3968     }
3969     if (ctx->q) {
3970         ejdbquerydel(ctx->q);
3971     }
3972     if (ctx->res) {
3973         tclistdel(ctx->res);
3974     }
3975     if (ctx->didxctx) {
3976         tclistdel(ctx->didxctx);
3977     }
3978     memset(ctx, 0, sizeof(*ctx));
3979 }
3980
3981 static TDBIDX* _qryfindidx(EJCOLL *coll, EJQF *qf, bson *idxmeta) {
3982     TCTDB *tdb = coll->tdb;
3983     char p = '\0';
3984     switch (qf->tcop) {
3985         case TDBQCSTREQ:
3986         case TDBQCSTRBW:
3987         case TDBQCSTROREQ:
3988         case TDBQCSTRORBW:
3989             p = (qf->flags & EJCONDICASE) ? 'i' : 's'; //lexical string index
3990             break;
3991         case TDBQCNUMEQ:
3992         case TDBQCNUMGT:
3993         case TDBQCNUMGE:
3994         case TDBQCNUMLT:
3995         case TDBQCNUMLE:
3996         case TDBQCNUMBT:
3997         case TDBQCNUMOREQ:
3998             p = 'n'; //number index
3999             break;
4000         case TDBQCSTRAND:
4001         case TDBQCSTROR:
4002             p = 'a'; //token index
4003             break;
4004         case TDBQTRUE:
4005             p = 'o'; //take first appropriate index
4006             break;
4007     }
4008     if (p == '\0' || !qf->fpath || !qf->fpathsz) {
4009         return NULL;
4010     }
4011     for (int i = 0; i < tdb->inum; ++i) {
4012         TDBIDX *idx = tdb->idxs + i;
4013         assert(idx);
4014         if (p == 'o') {
4015             if (*idx->name == 'a' || *idx->name == 'i') { //token index or icase index not the best solution here
4016                 continue;
4017             }
4018         } else if (*idx->name != p) {
4019             continue;
4020         }
4021         if (!strcmp(qf->fpath, idx->name + 1)) {
4022             return idx;
4023         }
4024     }
4025     //No direct operation index. Any alternatives?
4026     if (idxmeta &&
4027             !(qf->flags & EJCONDICASE) && //if not case insensitive query
4028             (
4029                 qf->tcop == TDBQCSTREQ ||
4030                 qf->tcop == TDBQCSTROREQ ||
4031                 qf->tcop == TDBQCNUMOREQ ||
4032                 qf->tcop == TDBQCNUMEQ)
4033        ) {
4034         bson_iterator it;
4035         bson_type bt = bson_find(&it, idxmeta, "iflags");
4036         if (bt != BSON_INT) {
4037             return NULL;
4038         }
4039         int iflags = bson_iterator_int(&it);
4040         if (iflags & JBIDXARR) { //array token index exists so convert qf into TDBQCSTROR
4041             for (int i = 0; i < tdb->inum; ++i) {
4042                 TDBIDX *idx = tdb->idxs + i;
4043                 if (!strcmp(qf->fpath, idx->name + 1)) {
4044                     if (qf->tcop == TDBQCSTREQ) {
4045                         qf->tcop = TDBQCSTROR;
4046                         qf->exprlist = tclistnew2(1);
4047                         TCLISTPUSH(qf->exprlist, qf->expr, qf->exprsz);
4048                         if (qf->expr) TCFREE(qf->expr);
4049                         qf->expr = tclistdump(qf->exprlist, &qf->exprsz);
4050                         qf->ftype = BSON_ARRAY;
4051                         return idx;
4052                     } else if (qf->tcop == TDBQCNUMEQ) {
4053                         qf->tcop = TDBQCSTRNUMOR;
4054                         qf->exprlist = tclistnew2(1);
4055                         TCLISTPUSH(qf->exprlist, qf->expr, qf->exprsz);
4056                         if (qf->expr) TCFREE(qf->expr);
4057                         qf->expr = tclistdump(qf->exprlist, &qf->exprsz);
4058                         qf->ftype = BSON_ARRAY;
4059                         return idx;
4060                     } else if (qf->tcop == TDBQCSTROREQ) {
4061                         assert(qf->ftype == BSON_ARRAY);
4062                         qf->tcop = TDBQCSTROR;
4063                         return idx;
4064                     } else if (qf->tcop == TDBQCNUMOREQ) {
4065                         assert(qf->ftype == BSON_ARRAY);
4066                         qf->tcop = TDBQCSTRNUMOR;
4067                         return idx;
4068                     }
4069                 }
4070             }
4071         }
4072     }
4073     return NULL;
4074 }
4075
4076 static void _registerallqfields(TCLIST *reg, EJQ *q) {
4077     for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
4078         EJQF *qf = TCLISTVALPTR(q->qflist, i);
4079         assert(qf);
4080         tclistpush(reg, &qf, sizeof(qf));
4081     }
4082     for (int i = 0; q->andqlist && i < TCLISTNUM(q->andqlist); ++i) {
4083         _registerallqfields(reg, *((EJQ**) TCLISTVALPTR(q->andqlist, i)));
4084     }
4085     for (int i = 0; q->orqlist && i < TCLISTNUM(q->orqlist); ++i) {
4086         _registerallqfields(reg, *((EJQ**) TCLISTVALPTR(q->orqlist, i)));
4087     }
4088 }
4089
4090 static bool _qrypreprocess(_QRYCTX *ctx) {
4091     assert(ctx->coll && ctx->q && ctx->q->qflist);
4092     EJQ *q = ctx->q;
4093
4094     //Fill the NULL terminated registry of all *EQF fields including all $and $or QF
4095     assert(!q->allqfields);
4096     TCLIST *alist = tclistnew2(TCLISTINYNUM);
4097     _registerallqfields(alist, q);
4098     TCMALLOC(q->allqfields, sizeof(EJQF*) * (TCLISTNUM(alist) + 1));
4099     for (int i = 0; i < TCLISTNUM(alist); ++i) {
4100         EJQF **qfp = TCLISTVALPTR(alist, i);
4101         q->allqfields[i] = *qfp; //*EJQF
4102     }
4103     q->allqfields[TCLISTNUM(alist)] = NULL;
4104     tclistdel(alist);
4105     alist = NULL;
4106
4107     if (ctx->qflags & JBQRYCOUNT) { //sync the user JBQRYCOUNT flag with internal
4108         q->flags |= EJQONLYCOUNT;
4109     }
4110     EJQF *oqf = NULL; //Order condition
4111     TCLIST *qflist = q->qflist;
4112     bson_iterator it;
4113     bson_type bt;
4114
4115     if (q->hints) {
4116         bson_type bt;
4117         bson_iterator it, sit;
4118         //Process $orderby
4119         bt = bson_find(&it, q->hints, "$orderby");
4120         if (bt == BSON_OBJECT) {
4121             int orderseq = 1;
4122             BSON_ITERATOR_SUBITERATOR(&it, &sit);
4123             while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
4124                 if (!BSON_IS_NUM_TYPE(bt)) {
4125                     continue;
4126                 }
4127                 const char *ofield = BSON_ITERATOR_KEY(&sit);
4128                 int odir = bson_iterator_int(&sit);
4129                 odir = (odir > 0) ? 1 : (odir < 0 ? -1 : 0);
4130                 if (!odir) {
4131                     continue;
4132                 }
4133                 EJQF nqf;
4134                 EJQF *qf = NULL;
4135                 for (int i = 0; i < TCLISTNUM(qflist); ++i) {
4136                     if (!strcmp(ofield, ((EJQF*) TCLISTVALPTR(qflist, i))->fpath)) {
4137                         qf = TCLISTVALPTR(qflist, i);
4138                         assert(qf);
4139                         break;
4140                     }
4141                 }
4142                 if (qf == NULL) { //Create syntetic query field for orderby ops
4143                     memset(&nqf, 0, sizeof (EJQF));
4144                     nqf.fpath = tcstrdup(ofield);
4145                     nqf.fpathsz = strlen(nqf.fpath);
4146                     nqf.expr = tcstrdup("");
4147                     nqf.exprsz = 0;
4148                     nqf.tcop = TDBQTRUE; //disable any TC matching operation
4149                     nqf.ftype = BSON_OBJECT;
4150                     nqf.orderseq = orderseq++;
4151                     nqf.order = odir;
4152                     nqf.flags |= EJFEXCLUDED; //field excluded  from matching
4153                     qf = &nqf;
4154                     TCLISTPUSH(qflist, qf, sizeof (*qf));
4155                 } else {
4156                     qf->orderseq = orderseq++;
4157                     qf->order = odir;
4158                 }
4159             }
4160         }
4161         bt = bson_find(&it, q->hints, "$skip");
4162         if (BSON_IS_NUM_TYPE(bt)) {
4163             int64_t v = bson_iterator_long(&it);
4164             q->skip = (uint32_t) ((v < 0) ? 0 : v);
4165         }
4166         bt = bson_find(&it, q->hints, "$max");
4167         if (ctx->qflags & JBQRYFINDONE) {
4168             q->max = (uint32_t) 1;
4169         } else if (BSON_IS_NUM_TYPE(bt)) {
4170             int64_t v = bson_iterator_long(&it);
4171             q->max = (uint32_t) ((v < 0) ? 0 : v);
4172         }
4173         if (!(ctx->qflags & JBQRYCOUNT)) {
4174             bt = bson_find(&it, q->hints, "$fields"); //Collect required fields
4175             if (bt == BSON_OBJECT) {
4176                 TCMAP *fmap = tcmapnew2(TCMAPTINYBNUM);
4177                 BSON_ITERATOR_SUBITERATOR(&it, &sit);
4178                 for (int i = 0; (bt = bson_iterator_next(&sit)) != BSON_EOO; ++i) {
4179                     if (!BSON_IS_NUM_TYPE(bt)) {
4180                         continue;
4181                     }
4182                     bool inc = (bson_iterator_int(&sit) > 0 ? true : false);
4183                     if (i > 0 && inc != ctx->imode) { //$fields hint cannot mix include and exclude fields
4184                         tcmapdel(fmap);
4185                         _ejdbsetecode(ctx->coll->jb, JBEQINCEXCL, __FILE__, __LINE__, __func__);
4186                         return false;
4187                     }
4188                     ctx->imode = inc;
4189                     const char *key = BSON_ITERATOR_KEY(&sit);
4190                     char *pptr;
4191                     //Checking the $(projection) operator.
4192                     if (inc && (pptr = strstr(key, ".$")) && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.'
4193                         int dc = 0;
4194                         for (int i = 0; *(key + i) != '\0'; ++i) {
4195                             if (*(key + i) == '$' && (dc++ > 0)) break;
4196                         }
4197                         if (dc != 1) { //More than one '$' chars in projection, it is invalid
4198                             continue;
4199                         }
4200                         for (int i = 0; i < TCLISTNUM(qflist); ++i) {
4201                             EJQF *qf = TCLISTVALPTR(qflist, i);
4202                             int j;
4203                             for (j = 0; *(key + j) != '\0' && *(key + j) == *(qf->fpath + j); ++j);
4204                             if (key + j == pptr || key + j == pptr + 1) { //existing QF matched the $(projection) prefix
4205                                 if (!q->ifields) {
4206                                     q->ifields = tcmapnew2(TCMAPTINYBNUM);
4207                                 }
4208                                 tcmapput(q->ifields, qf->fpath, qf->fpathsz, key, strlen(key));
4209                                 break;
4210                             }
4211                         }
4212                         continue; //skip registering this fields in the fmap
4213                     }
4214                     tcmapputkeep(fmap, key, strlen(key), &yes, sizeof (yes));
4215                 }
4216                 if (TCMAPRNUM(fmap) == 0) { //if {$fields : {}} we will force {$fields : {_id:1}}
4217                     ctx->imode = true;
4218                     tcmapputkeep(fmap, JDBIDKEYNAME, JDBIDKEYNAMEL, &yes, sizeof (yes));
4219                 }
4220                 ctx->ifields = fmap;
4221             }
4222         }
4223     } //eof hints
4224
4225     const int scoreexact = 100;
4226     const int scoregtlt = 50;
4227     int maxiscore = 0; //Maximum index score
4228     int maxselectivity = 0;
4229
4230     uint32_t skipflags = (//skip field flags
4231                              EJFNOINDEX |
4232                              EJCONDSET |
4233                              EJCONDINC |
4234                              EJCONDADDSET |
4235                              EJCONDPULL |
4236                              EJCONDUPSERT |
4237                              EJCONDOIT);
4238
4239     for (int i = 0; i < TCLISTNUM(qflist); ++i) {
4240         int iscore = 0;
4241         EJQF *qf = (EJQF*) TCLISTVALPTR(qflist, i);
4242         assert(qf && qf->fpath);
4243
4244         if (qf->flags & EJCONDOIT) { //$do field
4245             TCMAP *dmap = ctx->dfields;
4246             if (!dmap) {
4247                 dmap = tcmapnew2(TCMAPTINYBNUM);
4248                 ctx->dfields = dmap;
4249             }
4250             tcmapputkeep(dmap, qf->fpath, qf->fpathsz, qf, sizeof (*qf));
4251         }
4252
4253         if (qf->flags & skipflags) {
4254             continue;
4255         }
4256         //OID PK matching
4257         if (!qf->negate && (qf->tcop == TDBQCSTREQ || qf->tcop == TDBQCSTROREQ) && !strcmp(JDBIDKEYNAME, qf->fpath)) {
4258             qf->flags |= EJFPKMATCHING;
4259             ctx->mqf = qf;
4260             break;
4261         }
4262
4263         bool firstorderqf = false;
4264         qf->idxmeta = _imetaidx(ctx->coll, qf->fpath);
4265         qf->idx = _qryfindidx(ctx->coll, qf, qf->idxmeta);
4266         if (qf->order && qf->orderseq == 1) { //Index for first 'orderby' exists
4267             oqf = qf;
4268             firstorderqf = true;
4269         }
4270         if (!qf->idx || !qf->idxmeta) {
4271             if (qf->idxmeta) {
4272                 bson_del(qf->idxmeta);
4273             }
4274             qf->idx = NULL;
4275             qf->idxmeta = NULL;
4276             continue;
4277         }
4278         if (qf->tcop == TDBQTRUE || qf->negate) {
4279             continue;
4280         }
4281         int avgreclen = -1;
4282         int selectivity = -1;
4283         bt = bson_find(&it, qf->idxmeta, "selectivity");
4284         if (bt == BSON_DOUBLE) {
4285             selectivity = (int) ((double) bson_iterator_double(&it) * 100); //Selectivity percent
4286         }
4287         bt = bson_find(&it, qf->idxmeta, "avgreclen");
4288         if (bt == BSON_DOUBLE) {
4289             avgreclen = (int) bson_iterator_double(&it);
4290         }
4291         if (selectivity > 0) {
4292             if (selectivity <= 20) { //Not using index at all if selectivity lesser than 20%
4293                 continue;
4294             }
4295             iscore += selectivity;
4296         }
4297         if (firstorderqf) {
4298             iscore += (maxselectivity - selectivity) / 2;
4299         }
4300         if (selectivity > maxselectivity) {
4301             maxselectivity = selectivity;
4302         }
4303         switch (qf->tcop) {
4304             case TDBQCSTREQ:
4305             case TDBQCSTROR:
4306             case TDBQCNUMEQ:
4307             case TDBQCNUMBT:
4308                 iscore += scoreexact;
4309                 break;
4310             case TDBQCSTRBW:
4311             case TDBQCSTREW:
4312                 if (avgreclen > 0 && qf->exprsz > avgreclen) {
4313                     iscore += scoreexact;
4314                 }
4315                 break;
4316             case TDBQCNUMGT:
4317             case TDBQCNUMGE:
4318             case TDBQCNUMLT:
4319             case TDBQCNUMLE:
4320                 if (firstorderqf) {
4321                     iscore += scoreexact;
4322                 } else {
4323                     iscore += scoregtlt;
4324                 }
4325                 break;
4326         }
4327         if (iscore >= maxiscore) {
4328             ctx->mqf = qf;
4329             maxiscore = iscore;
4330         }
4331     }
4332     if (ctx->mqf == NULL && (oqf && oqf->idx && !oqf->negate)) {
4333         ctx->mqf = oqf;
4334     }
4335
4336     if (q->flags & EJQHASUQUERY) { //check update $(query) projection then sync inter-qf refs #91
4337         for (int i = 0; *(q->allqfields + i) != '\0'; ++i) {
4338             EJQF *qf = q->allqfields[i];
4339             if (!qf->ufields) continue;
4340             TCLIST *uflist = qf->ufields;
4341             for (int j = 0; j < TCLISTNUM(uflist); ++j) {
4342                 const char *ukey = TCLISTVALPTR(uflist, j);
4343                 char *pptr = strstr(ukey, ".$");
4344                 assert(pptr);
4345                 for (int k = 0; *(q->allqfields + k) != '\0'; ++k) {
4346                     int l;
4347                     EJQF *kqf = q->allqfields[k];
4348                     if (kqf == qf || !kqf->fpath) { //do not process itself
4349                         continue;
4350                     }
4351                     for (l = 0; *(ukey + l) != '\0' && *(ukey + l) == *(kqf->fpath + l); ++l);
4352                     if (ukey + l == pptr || ukey + l == pptr + 1) { //existing QF matched the $(query) prefix
4353                         if (!kqf->uslots) {
4354                             kqf->uslots = tclistnew2(TCLISTINYNUM);
4355                         }
4356                         USLOT uslot = {
4357                             .mpos = -1,
4358                             .dpos = (pptr - ukey),
4359                             .op = ukey
4360                         };
4361                         tclistpush(kqf->uslots, &uslot, sizeof(uslot));
4362                     }
4363                 }
4364             }
4365         }
4366     }
4367
4368     //Init query processing buffers
4369     assert(!q->colbuf && !q->bsbuf);
4370     q->colbuf = tcxstrnew3(1024);
4371     q->bsbuf = tcxstrnew3(1024);
4372     q->tmpbuf = tcxstrnew();
4373     ctx->didxctx = (q->flags & EJQUPDATING) ? tclistnew() : NULL;
4374     ctx->res = (q->flags & EJQONLYCOUNT) ? NULL : tclistnew2(4096);
4375     return true;
4376 }
4377
4378 static bool _metasetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts) {
4379     bool rv = true;
4380     if (!opts) {
4381         return _metasetbson(jb, colname, strlen(colname), "opts", NULL, false, false);
4382     }
4383     bson *bsopts = bson_create();
4384     bson_init(bsopts);
4385     bson_append_bool(bsopts, "compressed", opts->compressed);
4386     bson_append_bool(bsopts, "large", opts->large);
4387     bson_append_int(bsopts, "cachedrecords", opts->cachedrecords);
4388     bson_append_int(bsopts, "records", opts->records);
4389     bson_finish(bsopts);
4390     rv = _metasetbson(jb, colname, strlen(colname), "opts", bsopts, false, false);
4391     bson_del(bsopts);
4392     return rv;
4393 }
4394
4395 static bool _metagetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts) {
4396     assert(opts);
4397     bool rv = true;
4398     memset(opts, 0, sizeof (*opts));
4399     bson *bsopts = _metagetbson(jb, colname, strlen(colname), "opts");
4400     if (!bsopts) {
4401         return true;
4402     }
4403     bson_iterator it;
4404     bson_type bt = bson_find(&it, bsopts, "compressed");
4405     if (bt == BSON_BOOL) {
4406         opts->compressed = bson_iterator_bool(&it);
4407     }
4408     bt = bson_find(&it, bsopts, "large");
4409     if (bt == BSON_BOOL) {
4410         opts->large = bson_iterator_bool(&it);
4411     }
4412     bt = bson_find(&it, bsopts, "cachedrecords");
4413     if (BSON_IS_NUM_TYPE(bt)) {
4414         opts->cachedrecords = bson_iterator_long(&it);
4415     }
4416     bt = bson_find(&it, bsopts, "records");
4417     if (BSON_IS_NUM_TYPE(bt)) {
4418         opts->records = bson_iterator_long(&it);
4419     }
4420     bson_del(bsopts);
4421     return rv;
4422 }
4423
4424 static bool _metasetbson(EJDB *jb, const char *colname, int colnamesz,
4425                          const char *mkey, bson *val, bool merge, bool mergeoverwrt) {
4426     assert(jb && colname && mkey);
4427     bool rv = true;
4428     bson *bsave = NULL;
4429     bson *oldval = NULL;
4430     bson mresult;
4431
4432     TCMAP *cmeta = tctdbget(jb->metadb, colname, colnamesz);
4433     if (!cmeta) {
4434         _ejdbsetecode(jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
4435         rv = false;
4436         goto finish;
4437     }
4438     if (!val) {
4439         if (tcmapout2(cmeta, mkey)) {
4440             rv = tctdbput(jb->metadb, colname, colnamesz, cmeta);
4441         }
4442         goto finish;
4443     }
4444     assert(val);
4445     if (merge) { //Merged
4446         oldval = _metagetbson(jb, colname, colnamesz, mkey);
4447         if (oldval) {
4448             bson_init(&mresult);
4449             bson_merge(oldval, val, mergeoverwrt, &mresult);
4450             bson_finish(&mresult);
4451             bsave = &mresult;
4452         } else {
4453             bsave = val;
4454         }
4455     } else { //Rewrited
4456         bsave = val;
4457     }
4458
4459     assert(bsave);
4460     tcmapput(cmeta, mkey, strlen(mkey), bson_data(bsave), bson_size(bsave));
4461     rv = tctdbput(jb->metadb, colname, colnamesz, cmeta);
4462 finish:
4463     if (oldval) {
4464         if (merge) {
4465             bson_destroy(bsave);
4466         }
4467         bson_del(oldval);
4468     }
4469     if (cmeta) {
4470         tcmapdel(cmeta);
4471     }
4472     tctdbsync(jb->metadb);
4473     return rv;
4474 }
4475
4476 static bool _metasetbson2(EJCOLL *coll, const char *mkey, bson *val, bool merge, bool mergeoverwrt) {
4477     assert(coll);
4478     return _metasetbson(coll->jb, coll->cname, coll->cnamesz, mkey, val, merge, mergeoverwrt);
4479 }
4480
4481 /**Returned meta BSON data must be freed by 'bson_del' */
4482 static bson* _metagetbson(EJDB *jb, const char *colname, int colnamesz, const char *mkey) {
4483     assert(jb && colname && mkey);
4484     bson *rv = NULL;
4485     TCMAP *cmeta = tctdbget(jb->metadb, colname, colnamesz);
4486     if (!cmeta) {
4487         _ejdbsetecode(jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
4488         return NULL;
4489     }
4490     int bsz;
4491     const void *raw = tcmapget(cmeta, mkey, strlen(mkey), &bsz);
4492     if (!raw || bsz == 0) {
4493         goto finish;
4494     }
4495     rv = bson_create();
4496     bson_init_size(rv, bsz);
4497     bson_ensure_space(rv, bsz - 4);
4498     bson_append(rv, ((char*) raw) + 4, bsz - (4 + 1/*BSON_EOO*/));
4499     bson_finish(rv);
4500 finish:
4501     tcmapdel(cmeta);
4502     return rv;
4503 }
4504
4505 static bson* _metagetbson2(EJCOLL *coll, const char *mkey) {
4506     assert(coll);
4507     return _metagetbson(coll->jb, coll->cname, coll->cnamesz, mkey);
4508 }
4509
4510 /**Returned index meta if not NULL it must be freed by 'bson_del' */
4511 static bson* _imetaidx(EJCOLL *coll, const char *ipath) {
4512     assert(coll && ipath);
4513     if (*ipath == '\0') {
4514         return NULL;
4515     }
4516     bson *rv = NULL;
4517     char fpathkey[BSON_MAX_FPATH_LEN + 1];
4518     TCMAP *cmeta = tctdbget(coll->jb->metadb, coll->cname, coll->cnamesz);
4519     if (!cmeta) {
4520         _ejdbsetecode(coll->jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
4521         goto finish;
4522     }
4523     int klen = snprintf(fpathkey, BSON_MAX_FPATH_LEN + 1, "i%s", ipath); //'i' prefix for all columns with index meta
4524     if (klen > BSON_MAX_FPATH_LEN) {
4525         _ejdbsetecode(coll->jb, JBEFPATHINVALID, __FILE__, __LINE__, __func__);
4526         goto finish;
4527     }
4528     int bsz;
4529     const void *bsdata = tcmapget(cmeta, fpathkey, klen, &bsz);
4530     if (bsdata) {
4531         rv = bson_create();
4532         bson_init_size(rv, bsz);
4533         bson_ensure_space(rv, bsz - 4);
4534         bson_append(rv, ((char*) bsdata) + 4, bsz - (4 + 1));
4535         bson_finish(rv);
4536     }
4537 finish:
4538     if (cmeta) {
4539         tcmapdel(cmeta);
4540     }
4541     return rv;
4542 }
4543
4544 /** Free EJQF field **/
4545 static void _delqfdata(const EJQ *q, const EJQF *qf) {
4546     assert(q && qf);
4547     if (qf->expr) {
4548         TCFREE(qf->expr);
4549     }
4550     if (qf->fpath) {
4551         TCFREE(qf->fpath);
4552     }
4553     if (qf->idxmeta) {
4554         bson_del(qf->idxmeta);
4555     }
4556     if (qf->updateobj) {
4557         bson_del(qf->updateobj);
4558     }
4559     if (qf->ufields) {
4560         tclistdel(qf->ufields);
4561     }
4562     if (qf->uslots) {
4563         tclistdel(qf->uslots);
4564     }
4565     if (qf->regex && !(EJQINTERNAL & q->flags)) {
4566         //We do not clear regex_t data because it not deep copy in internal queries
4567         regfree((regex_t *) qf->regex);
4568         TCFREE(qf->regex);
4569     }
4570     if (qf->exprlist) {
4571         tclistdel(qf->exprlist);
4572     }
4573     if (qf->exprmap) {
4574         tcmapdel(qf->exprmap);
4575     }
4576 }
4577
4578 static bool _ejdbsavebsonimpl(EJCOLL *coll, bson *bs, bson_oid_t *oid, bool merge) {
4579     bool rv = false;
4580     bson *nbs = NULL;
4581     bson_type oidt = _bsonoidkey(bs, oid);
4582     if (oidt == BSON_EOO) { //missing _id so generate a new _id
4583         bson_oid_gen(oid);
4584         nbs = bson_create();
4585         bson_init_size(nbs, bson_size(bs) + (strlen(JDBIDKEYNAME) + 1/*key*/ + 1/*type*/ + sizeof (*oid)));
4586         bson_append_oid(nbs, JDBIDKEYNAME, oid);
4587         bson_ensure_space(nbs, bson_size(bs) - 4);
4588         bson_append(nbs, bson_data(bs) + 4, bson_size(bs) - (4 + 1/*BSON_EOO*/));
4589         bson_finish(nbs);
4590         assert(!nbs->err);
4591         bs = nbs;
4592     } else if (oidt != BSON_OID) { //_oid presented by it is not BSON_OID
4593         _ejdbsetecode(coll->jb, JBEINVALIDBSONPK, __FILE__, __LINE__, __func__);
4594         return false;
4595     }
4596     TCTDB *tdb = coll->tdb;
4597     TCMAP *rowm = (tdb->hdb->rnum > 0) ? tctdbget(tdb, oid, sizeof (*oid)) : NULL;
4598     char *obsdata = NULL; //Old bson
4599     int obsdatasz = 0;
4600     if (rowm) { //Save the copy of old bson data
4601         const void *obs = tcmapget(rowm, JDBCOLBSON, JDBCOLBSONL, &obsdatasz);
4602         if (obs && obsdatasz > 0) {
4603             TCMALLOC(obsdata, obsdatasz);
4604             memmove(obsdata, obs, obsdatasz);
4605         }
4606     } else {
4607         rowm = tcmapnew2(TCMAPTINYBNUM);
4608     }
4609     if (merge && !nbs && obsdata) {
4610         nbs = bson_create();
4611         bson_init_size(nbs, MAX(obsdatasz, bson_size(bs)));
4612         bson_merge2(obsdata, bson_data(bs), true, nbs);
4613         bson_finish(nbs);
4614         assert(!nbs->err);
4615         bs = nbs;
4616     }
4617     tcmapput(rowm, JDBCOLBSON, JDBCOLBSONL, bson_data(bs), bson_size(bs));
4618     if (!tctdbput(tdb, oid, sizeof (*oid), rowm)) {
4619         goto finish;
4620     }
4621     //Update indexes
4622     rv = _updatebsonidx(coll, oid, bs, obsdata, obsdatasz, NULL);
4623 finish:
4624     if (rowm) {
4625         tcmapdel(rowm);
4626     }
4627     if (obsdata) {
4628         TCFREE(obsdata);
4629     }
4630     if (nbs) {
4631         bson_del(nbs);
4632     }
4633     return rv;
4634 }
4635
4636 /**
4637  * Copy BSON array into new TCLIST. TCLIST must be freed by 'tclistdel'.
4638  * @param it BSON iterator
4639  * @param type[out] Detected BSON type of last element
4640  */
4641 static TCLIST* _fetch_bson_str_array(EJDB *jb, bson_iterator *it, bson_type *type, txtflags_t tflags) {
4642     TCLIST *res = tclistnew();
4643     *type = BSON_EOO;
4644     bson_type ftype;
4645     for (int i = 0; (ftype = bson_iterator_next(it)) != BSON_EOO; ++i) {
4646         switch (ftype) {
4647             case BSON_STRING:
4648                 *type = ftype;
4649                 if (tflags & JBICASE) { //ignore case
4650                     char *buf = NULL;
4651                     char sbuf[JBSTRINOPBUFFERSZ];
4652                     int len = tcicaseformat(bson_iterator_string(it), bson_iterator_string_len(it) - 1, sbuf, JBSTRINOPBUFFERSZ, &buf);
4653                     if (len < 0) {
4654                         _ejdbsetecode(jb, len, __FILE__, __LINE__, __func__);
4655                         break;
4656                     }
4657                     tclistpush2(res, buf);
4658                     if (buf && buf != sbuf) {
4659                         TCFREE(buf);
4660                     }
4661                 } else {
4662                     tclistpush2(res, bson_iterator_string(it));
4663                 }
4664                 break;
4665             case BSON_INT:
4666             case BSON_LONG:
4667             case BSON_BOOL:
4668             case BSON_DATE:
4669                 *type = ftype;
4670                 tclistprintf(res, "%" PRId64, bson_iterator_long(it));
4671                 break;
4672             case BSON_DOUBLE:
4673                 *type = ftype;
4674                 tclistprintf(res, "%f", bson_iterator_double(it));
4675                 break;
4676             case BSON_OID:
4677                 *type = ftype;
4678                 char xoid[25];
4679                 bson_oid_to_string(bson_iterator_oid(it), xoid);
4680                 tclistprintf(res, "%s", xoid);
4681                 break;
4682             default:
4683                 break;
4684         }
4685     }
4686     return res;
4687 }
4688
4689 /** result must be freed by TCFREE */
4690 static char* _fetch_bson_str_array2(EJDB *jb, bson_iterator *it, bson_type *type, txtflags_t tflags) {
4691     TCLIST *res = _fetch_bson_str_array(jb, it, type, tflags);
4692     char *tokens = tcstrjoin(res, ',');
4693     tclistdel(res);
4694     return tokens;
4695 }
4696
4697 static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, TCLIST *pathStack, EJQF *pqf, int elmatchgrp) {
4698     assert(it && qlist && pathStack);
4699     int ret = 0;
4700     bson_type ftype, bt;
4701
4702     while ((ftype = bson_iterator_next(it)) != BSON_EOO) {
4703         const char *fkey = BSON_ITERATOR_KEY(it);
4704         bool isckey = (*fkey == '$'); //Key is a control key: $in, $nin, $not, $all, ...
4705         EJQF qf;
4706         memset(&qf, 0, sizeof (qf));
4707         qf.q = q;
4708         if (pqf) {
4709             qf.elmatchgrp = pqf->elmatchgrp;
4710             qf.elmatchpos = pqf->elmatchpos;
4711             qf.flags = pqf->flags;
4712             if(elmatchgrp > 0)
4713                 qf.negate = pqf->negate;
4714         }
4715
4716         if (!isckey) {
4717             //Push key on top of path stack
4718             tclistpush2(pathStack, fkey);
4719             qf.ftype = ftype;
4720         } else {
4721             if (!strcmp("$or", fkey) || //Both levels operators.
4722                     !strcmp("$and", fkey)) {
4723                 //nop
4724             } else if (!strcmp("$set", fkey) ||
4725                        !strcmp("$inc", fkey) ||
4726                        !strcmp("$dropall", fkey) ||
4727                        !strcmp("$addToSet", fkey) ||
4728                        !strcmp("$addToSetAll", fkey) ||
4729                        !strcmp("$upsert", fkey) ||
4730                        !strcmp("$pull", fkey) ||
4731                        !strcmp("$pullAll", fkey) ||
4732                        !strcmp("$do", fkey) ||
4733                        !strcmp("$unset", fkey) ||
4734                        !strcmp("$rename", fkey)
4735                       ) {
4736                 if (pqf) { //Top level ops
4737                     ret = JBEQERROR;
4738                     _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4739                     break;
4740                 }
4741             } else {
4742                 if (!pqf) { //Require parent query object
4743                     ret = JBEQERROR;
4744                     _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4745                     break;
4746                 }
4747                 qf = *pqf;
4748                 if (!strcmp("$not", fkey)) {
4749                     qf.negate = !qf.negate;
4750                 } else if (!strcmp("$gt", fkey)) {
4751                     qf.flags |= EJCOMPGT;
4752                 } else if (!strcmp("$gte", fkey)) {
4753                     qf.flags |= EJCOMPGTE;
4754                 } else if (!strcmp("$lt", fkey)) {
4755                     qf.flags |= EJCOMPLT;
4756                 } else if (!strcmp("$lte", fkey)) {
4757                     qf.flags |= EJCOMPLTE;
4758                 } else if (!strcmp("$begin", fkey)) {
4759                     qf.flags |= EJCONDSTARTWITH;
4760                 } else if (!strcmp("$icase", fkey)) {
4761                     qf.flags |= EJCONDICASE;
4762                 }
4763             }
4764         }
4765
4766         switch (ftype) {
4767             case BSON_ARRAY: {
4768                 if (isckey) {
4769                     if (!strcmp("$and", fkey)) {
4770                         bson_iterator sit;
4771                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4772                         while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
4773                             if (bt != BSON_OBJECT) {
4774                                 continue;
4775                             }
4776                             if (_qryaddand(jb, q, bson_iterator_value(&sit)) == NULL) {
4777                                 break;
4778                             }
4779                         }
4780                         break;
4781                     } else if (!strcmp("$or", fkey)) {
4782                         bson_iterator sit;
4783                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4784                         while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
4785                             if (bt != BSON_OBJECT) {
4786                                 continue;
4787                             }
4788                             if (ejdbqueryaddor(jb, q, bson_iterator_value(&sit)) == NULL) {
4789                                 break;
4790                             }
4791                         }
4792                         break;
4793                     } else {
4794                         bson_iterator sit;
4795                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4796                         bson_type atype = 0;
4797                         TCLIST *tokens = _fetch_bson_str_array(jb, &sit, &atype, (qf.flags & EJCONDICASE) ? JBICASE : 0);
4798                         if (atype == 0) {
4799                             ret = JBEQINOPNOTARRAY;
4800                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4801                             tclistdel(tokens);
4802                             break;
4803                         }
4804                         assert(!qf.expr && !qf.fpath);
4805                         qf.ftype = BSON_ARRAY;
4806                         qf.expr = tclistdump(tokens, &qf.exprsz);
4807                         qf.exprlist = tokens;
4808                         if (!strcmp("$in", fkey) || !strcmp("$nin", fkey)) {
4809                             if (!strcmp("$nin", fkey)) {
4810                                 qf.negate = true;
4811                             }
4812                             if (BSON_IS_NUM_TYPE(atype) || atype == BSON_BOOL || atype == BSON_DATE) {
4813                                 qf.tcop = TDBQCNUMOREQ;
4814                             } else {
4815                                 qf.tcop = TDBQCSTROREQ;
4816                                 if (TCLISTNUM(tokens) >= JBINOPTMAPTHRESHOLD) {
4817                                     assert(!qf.exprmap);
4818                                     qf.exprmap = tcmapnew2(TCLISTNUM(tokens));
4819                                     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
4820                                         tcmapputkeep(qf.exprmap, TCLISTVALPTR(tokens, i), TCLISTVALSIZ(tokens, i), &yes, sizeof (yes));
4821                                     }
4822                                 }
4823                             }
4824                         } else if (!strcmp("$bt", fkey)) { //between
4825                             qf.tcop = TDBQCNUMBT;
4826                             if (TCLISTNUM(tokens) != 2) {
4827                                 ret = JBEQINOPNOTARRAY;
4828                                 _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4829                                 TCFREE(qf.expr);
4830                                 tclistdel(qf.exprlist);
4831                                 break;
4832                             }
4833                         } else if (!strcmp("$strand", fkey)) { //$strand
4834                             qf.tcop = TDBQCSTRAND;
4835                         } else if (!strcmp("$stror", fkey)) { //$stror
4836                             qf.tcop = TDBQCSTROR;
4837                         } else if (qf.flags & EJCONDSTARTWITH) { //$begin with some token
4838                             qf.tcop = TDBQCSTRORBW;
4839                         } else {
4840                             ret = JBEQINVALIDQCONTROL;
4841                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4842                             TCFREE(qf.expr);
4843                             tclistdel(qf.exprlist);
4844                             break;
4845                         }
4846                         qf.fpath = tcstrjoin(pathStack, '.');
4847                         qf.fpathsz = strlen(qf.fpath);
4848                         TCLISTPUSH(qlist, &qf, sizeof (qf));
4849                         break;
4850                     }
4851                 } else {
4852                     bson_iterator sit;
4853                     BSON_ITERATOR_SUBITERATOR(it, &sit);
4854                     ret = _parse_qobj_impl(jb, q, &sit, qlist, pathStack, &qf, elmatchgrp);
4855                     break;
4856                 }
4857             }
4858
4859             case BSON_OBJECT: {
4860                 if (isckey) {
4861                     if (!strcmp("$inc", fkey)) {
4862                         qf.flags |= EJCONDINC;
4863                     } else if (!pqf) { //top level op
4864                         if (!strcmp("$set", fkey)) { //top level set OP
4865                             qf.flags |= EJCONDSET;
4866                         } else if (!strcmp("$addToSet", fkey)) {
4867                             qf.flags |= EJCONDADDSET;
4868                         } else if (!strcmp("$pull", fkey)) {
4869                             qf.flags |= EJCONDPULL;
4870                         } else if (!strcmp("$upsert", fkey)) {
4871                             qf.flags |= EJCONDSET;
4872                             qf.flags |= EJCONDUPSERT;
4873                         } else if (!strcmp("$addToSetAll", fkey)) {
4874                             qf.flags |= EJCONDADDSET;
4875                             qf.flags |= EJCONDALL;
4876                         } else if (!strcmp("$pullAll", fkey)) {
4877                             qf.flags |= EJCONDPULL;
4878                             qf.flags |= EJCONDALL;
4879                         } else if (!strcmp("$do", fkey)) {
4880                             qf.flags |= EJCONDOIT;
4881                         } else if (!strcmp("$unset", fkey)) {
4882                             qf.flags |= EJCONDUNSET;
4883                         } else if (!strcmp("$rename", fkey)) {
4884                             qf.flags |= EJCONDRENAME;
4885                         }
4886                     }
4887
4888                     if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDUNSET | EJCONDRENAME))) {
4889                         assert(qf.updateobj == NULL);
4890                         qf.q->flags |= EJQUPDATING;
4891                         qf.updateobj = bson_create();
4892                         bson_init_as_query(qf.updateobj);
4893                         bson_type sbt;
4894                         bson_iterator sit;
4895                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4896                         while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
4897                             if ((qf.flags & EJCONDALL) && sbt != BSON_ARRAY) {
4898                                 //$addToSetAll & $pullAll accepts only a"$set"rrays
4899                                 continue;
4900                             }
4901                             if (!(qf.flags & EJCONDALL) && (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET | EJCONDRENAME))) { //Checking the $(query) positional operator.
4902                                 const char* ukey = BSON_ITERATOR_KEY(&sit);
4903                                 char *pptr;
4904                                 if ((pptr = strstr(ukey, ".$")) && pptr && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.'
4905                                     int dc = 0;
4906                                     for (int i = 0; *(ukey + i) != '\0'; ++i) {
4907                                         if (*(ukey + i) == '$' && (dc++ > 0)) break;
4908                                     }
4909                                     if (dc != 1) { //More than one '$' chars in projection, it is invalid
4910                                         continue;
4911                                     }
4912                                     // Now just store only [$(query) key] into the qf->ufields
4913                                     if (!qf.ufields) {
4914                                         qf.ufields = tclistnew2(TCLISTINYNUM);
4915                                     }
4916                                     q->flags |= EJQHASUQUERY;
4917                                     tclistpush(qf.ufields, ukey, strlen(ukey));
4918                                 }
4919                             }
4920                             bson_append_field_from_iterator(&sit, qf.updateobj);
4921                         }
4922                         bson_finish(qf.updateobj);
4923                         if (qf.updateobj->err) {
4924                             ret = JBEQERROR;
4925                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4926                             break;
4927                         }
4928                         qf.fpath = strdup(fkey);
4929                         qf.fpathsz = strlen(qf.fpath);
4930                         qf.tcop = TDBQTRUE;
4931                         qf.flags |= EJFEXCLUDED;
4932                         TCLISTPUSH(qlist, &qf, sizeof (qf));
4933                         break;
4934                     }
4935
4936                     if (!strcmp("$elemMatch", fkey)) {
4937                         if (qf.elmatchgrp) { //only one $elemMatch allowed in query field
4938                             ret = JBEQERROR;
4939                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4940                             break;
4941                         }
4942                         qf.elmatchgrp = ++elmatchgrp;
4943                         char *fpath = tcstrjoin(pathStack, '.');
4944                         qf.elmatchpos = strlen(fpath) + 1; //+ 1 to skip next dot '.'
4945                         free(fpath);
4946                     }
4947                 } else {
4948                     if (qf.flags & EJCONDOIT) {
4949                         qf.updateobj = bson_create();
4950                         bson_init_as_query(qf.updateobj);
4951                         bson_type sbt;
4952                         bson_iterator sit;
4953                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4954                         int ac = 0;
4955                         while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
4956                             const char *akey = BSON_ITERATOR_KEY(&sit);
4957                             if (!strcmp("$join", akey) || !strcmp("$slice", akey)) {
4958                                 bson_append_field_from_iterator(&sit, qf.updateobj);
4959                                 ++ac;
4960                             }
4961                         }
4962                         bson_finish(qf.updateobj);
4963                         if (qf.updateobj->err) {
4964                             ret = JBEQERROR;
4965                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4966                             break;
4967                         }
4968                         if (ac == 0) {
4969                             ret = JBEQACTKEY;
4970                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4971                             break;
4972                         }
4973                         qf.fpath = strdup(fkey);
4974                         qf.fpathsz = strlen(qf.fpath);
4975                         qf.tcop = TDBQTRUE;
4976                         qf.flags |= EJFEXCLUDED;
4977                         TCLISTPUSH(qlist, &qf, sizeof (qf));
4978                         break;
4979                     }
4980                 }
4981                 bson_iterator sit;
4982                 BSON_ITERATOR_SUBITERATOR(it, &sit);
4983                 ret = _parse_qobj_impl(jb, q, &sit, qlist, pathStack, &qf, elmatchgrp);
4984                 break;
4985             }
4986             case BSON_OID: {
4987
4988                 assert(!qf.fpath && !qf.expr);
4989                 qf.ftype = ftype;
4990                 TCMALLOC(qf.expr, 25 * sizeof (char));
4991                 bson_oid_to_string(bson_iterator_oid(it), qf.expr);
4992                 qf.exprsz = 24;
4993                 qf.fpath = tcstrjoin(pathStack, '.');
4994                 qf.fpathsz = strlen(qf.fpath);
4995                 qf.tcop = TDBQCSTREQ;
4996                 TCLISTPUSH(qlist, &qf, sizeof (qf));
4997                 break;
4998             }
4999             case BSON_STRING: {
5000                 assert(!qf.fpath && !qf.expr);
5001                 qf.ftype = ftype;
5002                 if (qf.flags & EJCONDICASE) {
5003                     qf.exprsz = tcicaseformat(bson_iterator_string(it), bson_iterator_string_len(it) - 1, NULL, 0, &qf.expr);
5004                     if (qf.exprsz < 0) {
5005                         ret = qf.exprsz;
5006                         _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5007                         qf.exprsz = 0;
5008                         break;
5009                     }
5010                 } else {
5011                     qf.expr = tcstrdup(bson_iterator_string(it));
5012                     qf.exprsz = strlen(qf.expr);
5013                 }
5014
5015                 qf.fpath = tcstrjoin(pathStack, '.');
5016                 qf.fpathsz = strlen(qf.fpath);
5017                 if (qf.flags & EJCONDSTARTWITH) {
5018                     qf.tcop = TDBQCSTRBW;
5019                 } else {
5020                     qf.tcop = TDBQCSTREQ;
5021                     if (!strcmp(JDBIDKEYNAME, fkey)) { //_id
5022                         qf.ftype = BSON_OID;
5023                     }
5024                 }
5025                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5026                 break;
5027             }
5028             case BSON_LONG:
5029             case BSON_DOUBLE:
5030             case BSON_INT:
5031             case BSON_DATE: {
5032                 assert(!qf.fpath && !qf.expr);
5033                 qf.ftype = ftype;
5034                 qf.fpath = tcstrjoin(pathStack, '.');
5035                 qf.fpathsz = strlen(qf.fpath);
5036                 if (ftype == BSON_LONG || ftype == BSON_INT || ftype == BSON_DATE) {
5037                     qf.exprlongval = bson_iterator_long(it);
5038                     qf.exprdblval = qf.exprlongval;
5039                     // 2015-04-14: Change to use standard format string for int64_t
5040                     qf.expr = tcsprintf("%" PRId64, qf.exprlongval);
5041                 } else {
5042                     qf.exprdblval = bson_iterator_double(it);
5043                     qf.exprlongval = (int64_t) qf.exprdblval;
5044                     qf.expr = tcsprintf("%f", qf.exprdblval);
5045                 }
5046                 qf.exprsz = strlen(qf.expr);
5047                 if (qf.flags & EJCOMPGT) {
5048                     qf.tcop = TDBQCNUMGT;
5049                 } else if (qf.flags & EJCOMPGTE) {
5050                     qf.tcop = TDBQCNUMGE;
5051                 } else if (qf.flags & EJCOMPLT) {
5052                     qf.tcop = TDBQCNUMLT;
5053                 } else if (qf.flags & EJCOMPLTE) {
5054                     qf.tcop = TDBQCNUMLE;
5055                 } else {
5056                     qf.tcop = TDBQCNUMEQ;
5057                 }
5058                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5059                 break;
5060             }
5061             case BSON_REGEX: {
5062                 assert(!qf.fpath && !qf.expr);
5063                 qf.ftype = ftype;
5064                 qf.tcop = TDBQCSTRRX;
5065                 char *re = tcstrdup(bson_iterator_regex(it));
5066                 const char *opts = bson_iterator_regex_opts(it);
5067                 qf.fpath = tcstrjoin(pathStack, '.');
5068                 qf.fpathsz = strlen(qf.fpath);
5069                 qf.expr = re;
5070                 qf.exprsz = strlen(qf.expr);
5071                 const char *rxstr = qf.expr;
5072                 regex_t rxbuf;
5073                 int rxopt = REG_EXTENDED | REG_NOSUB;
5074                 if (strchr(opts, 'i')) {
5075                     rxopt |= REG_ICASE;
5076                 }
5077                 if (regcomp(&rxbuf, rxstr, rxopt) == 0) {
5078                     TCMALLOC(qf.regex, sizeof (rxbuf));
5079                     memcpy(qf.regex, &rxbuf, sizeof (rxbuf));
5080                 } else {
5081                     ret = JBEQINVALIDQRX;
5082                     _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5083                     TCFREE(qf.fpath);
5084                     TCFREE(qf.expr);
5085                     break;
5086                 }
5087                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5088                 break;
5089             }
5090             case BSON_NULL:
5091             case BSON_UNDEFINED:
5092                 qf.ftype = ftype;
5093                 qf.tcop = TDBQCEXIST;
5094                 qf.negate = !qf.negate;
5095                 qf.expr = tcstrdup(""); //Empty string as expr
5096                 qf.exprsz = 0;
5097                 qf.fpath = tcstrjoin(pathStack, '.');
5098                 qf.fpathsz = strlen(qf.fpath);
5099                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5100                 break;
5101
5102             case BSON_BOOL: { //boolean converted into number
5103                 bool bv = bson_iterator_bool_raw(it);
5104                 if (isckey) {
5105                     if (!strcmp("$dropall", fkey) && bv) {
5106                         qf.flags |= EJFEXCLUDED;
5107                         qf.fpath = tcstrjoin(pathStack, '.');
5108                         qf.fpathsz = strlen(qf.fpath);
5109                         qf.tcop = TDBQTRUE;
5110                         qf.q->flags |= EJQUPDATING;
5111                         qf.q->flags |= EJQDROPALL;
5112                         qf.expr = tcstrdup(""); //Empty string as expr
5113                         qf.exprsz = 0;
5114                         TCLISTPUSH(qlist, &qf, sizeof (qf));
5115                         break;
5116                     }
5117                     if (!strcmp("$exists", fkey)) {
5118                         qf.tcop = TDBQCEXIST;
5119                         qf.fpath = tcstrjoin(pathStack, '.');
5120                         qf.fpathsz = strlen(qf.fpath);
5121                         qf.expr = tcstrdup(""); //Empty string as expr
5122                         qf.exprsz = 0;
5123                         if (!bv) {
5124                             qf.negate = !qf.negate;
5125                         }
5126                         TCLISTPUSH(qlist, &qf, sizeof (qf));
5127                         break;
5128                     }
5129                 }
5130                 qf.tcop = TDBQCNUMEQ;
5131                 qf.fpath = tcstrjoin(pathStack, '.');
5132                 qf.fpathsz = strlen(qf.fpath);
5133                 qf.exprlongval = (bv ? 1 : 0);
5134                 qf.exprdblval = qf.exprlongval;
5135                 qf.expr = strdup(bv ? "1" : "0");
5136                 qf.exprsz = 1;
5137                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5138                 break;
5139             }
5140             default:
5141                 break;
5142         };
5143
5144         if (!isckey) {
5145             assert(pathStack->num > 0);
5146             TCFREE(tclistpop2(pathStack));
5147         }
5148         if (ret) { //cleanup on error condition
5149             break;
5150         }
5151     }
5152     return ret;
5153 }
5154
5155 /**
5156  * Convert bson query spec into field path -> EJQF instance.
5157  *  Created map instance must be freed by `tcmapdel`.
5158  *  Each element of map must be freed by `ejdbquerydel`.
5159  */
5160 static TCLIST* _parseqobj(EJDB *jb, EJQ *q, bson *qspec) {
5161     assert(qspec);
5162     return _parseqobj2(jb, q, bson_data(qspec));
5163 }
5164
5165 static TCLIST* _parseqobj2(EJDB *jb, EJQ *q, const void *qspecbsdata) {
5166     assert(qspecbsdata);
5167     int rv = 0;
5168     TCLIST *res = tclistnew2(TCLISTINYNUM);
5169     TCLIST *pathStack = tclistnew2(TCLISTINYNUM);
5170     bson_iterator it;
5171     BSON_ITERATOR_FROM_BUFFER(&it, qspecbsdata);
5172     rv = _parse_qobj_impl(jb, q, &it, res, pathStack, NULL, 0);
5173     if (rv) {
5174         tclistdel(res);
5175         res = NULL;
5176     }
5177     assert(!pathStack->num);
5178     tclistdel(pathStack);
5179     return res;
5180 }
5181
5182 /**
5183  * Get OID value from the '_id' field of specified bson object.
5184  * @param bson[in] BSON object
5185  * @param oid[out] Pointer to OID type
5186  * @return True if OID value is found int _id field of bson object otherwise False.
5187  */
5188 static bson_type _bsonoidkey(bson *bs, bson_oid_t *oid) {
5189     bson_iterator it;
5190     bson_type bt = bson_find(&it, bs, JDBIDKEYNAME);
5191     if (bt == BSON_OID) {
5192         *oid = *bson_iterator_oid(&it);
5193     }
5194     return bt;
5195 }
5196
5197 /**
5198  * Return string value representation of value pointed by 'it'.
5199  * Resulting value size stored into 'vsz'.
5200  * If returned value is not NULL it must be freed by TCFREE.
5201  */
5202 static char* _bsonitstrval(EJDB *jb, bson_iterator *it, int *vsz, TCLIST *tokens, txtflags_t tflags) {
5203     int retlen = 0;
5204     char *ret = NULL;
5205     bson_type btype = BSON_ITERATOR_TYPE(it);
5206     if (btype == BSON_STRING) {
5207         if (tokens) { //split string into tokens and push it into 'tokens' list
5208             const unsigned char *sp = (unsigned char *) bson_iterator_string(it);
5209             while (*sp != '\0') {
5210                 while ((*sp != '\0' && *sp <= ' ') || *sp == ',') {
5211                     sp++;
5212                 }
5213                 const unsigned char *ep = sp;
5214                 while (*ep > ' ' && *ep != ',') {
5215                     ep++;
5216                 }
5217                 if (ep > sp) {
5218                     if (tflags & JBICASE) { //ignore case mode
5219                         char *buf = NULL;
5220                         char sbuf[JBSTRINOPBUFFERSZ];
5221                         int len = tcicaseformat((const char*) sp, ep - sp, sbuf, JBSTRINOPBUFFERSZ, &buf);
5222                         if (len >= 0) { //success
5223                             TCLISTPUSH(tokens, buf, len);
5224                         } else {
5225                             _ejdbsetecode(jb, len, __FILE__, __LINE__, __func__);
5226                         }
5227                         if (buf && buf != sbuf) {
5228                             TCFREE(buf);
5229                         }
5230                     } else {
5231                         TCLISTPUSH(tokens, sp, ep - sp);
5232                     }
5233                 }
5234                 sp = ep;
5235             }
5236         } else {
5237             retlen = bson_iterator_string_len(it) - 1;
5238             if (tflags & JBICASE) {
5239                 retlen = tcicaseformat(bson_iterator_string(it), retlen, NULL, 0, &ret);
5240             } else {
5241                 ret = tcmemdup(bson_iterator_string(it), retlen);
5242             }
5243         }
5244     } else if (BSON_IS_NUM_TYPE(btype) || btype == BSON_BOOL || btype == BSON_DATE) {
5245         char nbuff[TCNUMBUFSIZ];
5246         if (btype == BSON_INT || btype == BSON_LONG || btype == BSON_BOOL || btype == BSON_DATE) {
5247             retlen = bson_numstrn(nbuff, TCNUMBUFSIZ, bson_iterator_long(it));
5248             if (retlen >= TCNUMBUFSIZ) {
5249                 retlen = TCNUMBUFSIZ - 1;
5250             }
5251         } else if (btype == BSON_DOUBLE) {
5252             retlen = tcftoa(bson_iterator_double(it), nbuff, TCNUMBUFSIZ, 6);
5253             if (retlen >= TCNUMBUFSIZ) {
5254                 retlen = TCNUMBUFSIZ - 1;
5255             }
5256         }
5257         if (tflags & JBICASE) {
5258             retlen = tcicaseformat(nbuff, retlen, NULL, 0, &ret);
5259         } else {
5260             ret = tcmemdup(nbuff, retlen);
5261         }
5262     } else if (btype == BSON_ARRAY) {
5263         bson_type eltype; //last element bson type
5264         bson_iterator sit;
5265         BSON_ITERATOR_SUBITERATOR(it, &sit);
5266         if (tokens) {
5267             while ((eltype = bson_iterator_next(&sit)) != BSON_EOO) {
5268                 int vz = 0;
5269                 char *v = _bsonitstrval(jb, &sit, &vz, NULL, tflags);
5270                 if (v) {
5271                     TCLISTPUSH(tokens, v, vz);
5272                     TCFREE(v);
5273                 }
5274             }
5275         } else {
5276             //Array elements are joined with ',' delimeter.
5277             ret = _fetch_bson_str_array2(jb, &sit, &eltype, tflags);
5278             retlen = strlen(ret);
5279         }
5280     }
5281     if (retlen < 0) {
5282         _ejdbsetecode(jb, retlen, __FILE__, __LINE__, __func__);
5283         ret = NULL;
5284         retlen = 0;
5285     }
5286     *vsz = retlen;
5287     return ret;
5288 }
5289
5290 static char* _bsonipathrowldr(
5291     TCLIST *tokens,
5292     const char *pkbuf, int pksz,
5293     const char *rowdata, int rowdatasz,
5294     const char *ipath, int ipathsz, void *op, int *vsz) {
5295     assert(op);
5296     char *res = NULL;
5297     if (ipath && *ipath == '\0') { //PK TODO review
5298         if (tokens) {
5299             const unsigned char *sp = (unsigned char *) pkbuf;
5300             while (*sp != '\0') {
5301                 while ((*sp != '\0' && *sp <= ' ') || *sp == ',') {
5302                     sp++;
5303                 }
5304                 const unsigned char *ep = sp;
5305                 while (*ep > ' ' && *ep != ',') {
5306                     ep++;
5307                 }
5308                 if (ep > sp) TCLISTPUSH(tokens, sp, ep - sp);
5309                 sp = ep;
5310             }
5311             *vsz = 0;
5312             return NULL;
5313         } else {
5314             TCMEMDUP(res, pkbuf, pksz);
5315             *vsz = pksz;
5316             return res;
5317         }
5318     }
5319     if (!ipath || ipathsz < 2 || *(ipath + 1) == '\0' || strchr("snai", *ipath) == NULL) {
5320         return NULL;
5321     }
5322     //skip index type prefix char with (fpath + 1)
5323     res = _bsonfpathrowldr(tokens, rowdata, rowdatasz, ipath + 1, ipathsz - 1, op, vsz);
5324     if (*vsz == 0) { //Do not allow empty strings for index opration
5325         if (res) TCFREE(res);
5326         res = NULL;
5327     }
5328     return res;
5329 }
5330
5331 static char* _bsonfpathrowldr(TCLIST *tokens, const char *rowdata, int rowdatasz,
5332                               const char *fpath, int fpathsz, void *op, int *vsz) {
5333     _BSONIPATHROWLDR *odata = (_BSONIPATHROWLDR*) op;
5334     assert(odata && odata->coll);
5335     char *ret = NULL;
5336     int bsize;
5337     bson_iterator it;
5338     char *bsdata = tcmaploadone(rowdata, rowdatasz, JDBCOLBSON, JDBCOLBSONL, &bsize);
5339     if (!bsdata) {
5340         *vsz = 0;
5341         return NULL;
5342     }
5343     BSON_ITERATOR_FROM_BUFFER(&it, bsdata);
5344     bson_find_fieldpath_value2(fpath, fpathsz, &it);
5345     ret = _bsonitstrval(odata->coll->jb, &it, vsz, tokens, (odata->icase ? JBICASE : 0));
5346     TCFREE(bsdata);
5347     return ret;
5348 }
5349
5350 static bool _updatebsonidx(EJCOLL *coll, const bson_oid_t *oid, const bson *bs,
5351                            const void *obsdata, int obsdatasz, TCLIST *dlist) {
5352     bool rv = true;
5353     TCMAP *cmeta = tctdbget(coll->jb->metadb, coll->cname, coll->cnamesz);
5354     if (!cmeta) {
5355         _ejdbsetecode(coll->jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
5356         return false;
5357     }
5358     TCMAP *imap = NULL; //New index map
5359     TCMAP *rimap = NULL; //Remove index map
5360     bson_type mt = BSON_EOO;
5361     bson_type ft = BSON_EOO;
5362     bson_type oft = BSON_EOO;
5363     bson_iterator fit, oit, mit;
5364     int bsz;
5365     char ikey[BSON_MAX_FPATH_LEN + 2];
5366     const char *mkey;
5367     int mkeysz;
5368
5369     tcmapiterinit(cmeta);
5370     while ((mkey = tcmapiternext(cmeta, &mkeysz)) != NULL && mkeysz > 0) {
5371         if (*mkey != 'i' || mkeysz > BSON_MAX_FPATH_LEN + 1) {
5372             continue;
5373         }
5374         const void *mraw = tcmapget(cmeta, mkey, mkeysz, &bsz);
5375         if (!mraw || !bsz || (mt = bson_find_from_buffer(&mit, mraw, "iflags")) != BSON_INT) {
5376             continue;
5377         }
5378         int iflags = bson_iterator_int(&mit);
5379         //OK then process index keys
5380         memcpy(ikey + 1, mkey + 1, mkeysz - 1);
5381         ikey[mkeysz] = '\0';
5382
5383         int fvaluesz = 0;
5384         char *fvalue = NULL;
5385         int ofvaluesz = 0;
5386         char *ofvalue = NULL;
5387         txtflags_t textflags = (iflags & JBIDXISTR) ? JBICASE : 0;
5388
5389         if (obsdata && obsdatasz > 0) {
5390             BSON_ITERATOR_FROM_BUFFER(&oit, obsdata);
5391             oft = bson_find_fieldpath_value2(mkey + 1, mkeysz - 1, &oit);
5392             TCLIST *tokens = (oft == BSON_ARRAY || (oft == BSON_STRING && (iflags & JBIDXARR))) ? tclistnew() : NULL;
5393             ofvalue = BSON_IS_IDXSUPPORTED_TYPE(oft) ? _bsonitstrval(coll->jb, &oit, &ofvaluesz, tokens, textflags) : NULL;
5394             if (tokens) {
5395                 oft = BSON_ARRAY;
5396                 ofvalue = tclistdump(tokens, &ofvaluesz);
5397                 tclistdel(tokens);
5398             }
5399         }
5400         if (bs) {
5401             BSON_ITERATOR_INIT(&fit, bs);
5402             ft = bson_find_fieldpath_value2(mkey + 1, mkeysz - 1, &fit);
5403             TCLIST *tokens = (ft == BSON_ARRAY || (ft == BSON_STRING && (iflags & JBIDXARR))) ? tclistnew() : NULL;
5404             fvalue = BSON_IS_IDXSUPPORTED_TYPE(ft) ? _bsonitstrval(coll->jb, &fit, &fvaluesz, tokens, textflags) : NULL;
5405             if (tokens) {
5406                 ft = BSON_ARRAY;
5407                 fvalue = tclistdump(tokens, &fvaluesz);
5408                 tclistdel(tokens);
5409             }
5410         }
5411         if (!fvalue && !ofvalue) {
5412             continue;
5413         }
5414         if (imap == NULL) {
5415             imap = tcmapnew2(TCMAPTINYBNUM);
5416             rimap = tcmapnew2(TCMAPTINYBNUM);
5417         }
5418         for (int i = 4; i <= 7; ++i) { /* JBIDXNUM, JBIDXSTR, JBIDXARR, JBIDXISTR */
5419             bool rm = false;
5420             int itype = (1 << i);
5421             if (itype == JBIDXNUM && (JBIDXNUM & iflags)) {
5422                 ikey[0] = 'n';
5423             } else if (itype == JBIDXSTR && (JBIDXSTR & iflags)) {
5424                 ikey[0] = 's';
5425             } else if (itype == JBIDXISTR && (JBIDXISTR & iflags)) {
5426                 ikey[0] = 'i';
5427             } else if (itype == JBIDXARR && (JBIDXARR & iflags)) {
5428                 ikey[0] = 'a';
5429                 if (ofvalue && oft == BSON_ARRAY &&
5430                         (!fvalue || ft != oft || fvaluesz != ofvaluesz || memcmp(fvalue, ofvalue, fvaluesz))) {
5431                     tcmapput(rimap, ikey, mkeysz, ofvalue, ofvaluesz);
5432                     rm = true;
5433                 }
5434                 if (fvalue && fvaluesz > 0 && ft == BSON_ARRAY && (!ofvalue || rm)) {
5435                     tcmapput(imap, ikey, mkeysz, fvalue, fvaluesz);
5436                 }
5437                 continue;
5438             } else {
5439                 continue;
5440             }
5441             if (ofvalue && oft != BSON_ARRAY &&
5442                     (!fvalue || ft != oft || fvaluesz != ofvaluesz || memcmp(fvalue, ofvalue, fvaluesz))) {
5443                 tcmapput(rimap, ikey, mkeysz, ofvalue, ofvaluesz);
5444                 rm = true;
5445             }
5446             if (fvalue && fvaluesz > 0 && ft != BSON_ARRAY && (!ofvalue || rm)) {
5447                 tcmapput(imap, ikey, mkeysz, fvalue, fvaluesz);
5448             }
5449         }
5450         if (fvalue) TCFREE(fvalue);
5451         if (ofvalue) TCFREE(ofvalue);
5452     }
5453     tcmapdel(cmeta);
5454
5455     if (dlist) { //storage for deffered index ops provided, save changes into
5456         _DEFFEREDIDXCTX dctx;
5457         dctx.oid = *oid;
5458         dctx.rmap = (rimap && TCMAPRNUM(rimap) > 0) ? tcmapdup(rimap) : NULL;
5459         dctx.imap = (imap && TCMAPRNUM(imap) > 0) ? tcmapdup(imap) : NULL;
5460         if (dctx.imap || dctx.rmap) {
5461             TCLISTPUSH(dlist, &dctx, sizeof (dctx));
5462         }
5463         //flush deffered indexes if number pending objects greater JBMAXDEFFEREDIDXNUM
5464         if (TCLISTNUM(dlist) >= JBMAXDEFFEREDIDXNUM) {
5465             for (int i = 0; i < TCLISTNUM(dlist); ++i) {
5466                 _DEFFEREDIDXCTX *di = TCLISTVALPTR(dlist, i);
5467                 assert(di);
5468                 if (di->rmap) {
5469                     tctdbidxout2(coll->tdb, &(di->oid), sizeof (di->oid), di->rmap);
5470                     tcmapdel(di->rmap);
5471                 }
5472                 if (di->imap) {
5473                     tctdbidxput2(coll->tdb, &(di->oid), sizeof (di->oid), di->imap);
5474                     tcmapdel(di->imap);
5475                 }
5476             }
5477             TCLISTTRUNC(dlist, 0);
5478         }
5479     } else { //apply index changes immediately
5480         if (rimap && !tctdbidxout2(coll->tdb, oid, sizeof (*oid), rimap)) rv = false;
5481         if (imap && !tctdbidxput2(coll->tdb, oid, sizeof (*oid), imap)) rv = false;
5482     }
5483     if (imap) tcmapdel(imap);
5484     if (rimap) tcmapdel(rimap);
5485     return rv;
5486 }
5487
5488 static void _delcoldb(EJCOLL *coll) {
5489     assert(coll);
5490     tctdbdel(coll->tdb);
5491     coll->tdb = NULL;
5492     coll->jb = NULL;
5493     coll->cnamesz = 0;
5494     TCFREE(coll->cname);
5495     if (coll->mmtx) {
5496         pthread_rwlock_destroy(coll->mmtx);
5497         TCFREE(coll->mmtx);
5498     }
5499 }
5500
5501 static bool _addcoldb0(const char *cname, EJDB *jb, EJCOLLOPTS *opts, EJCOLL **res) {
5502     int i;
5503     bool rv = true;
5504     TCTDB *cdb;
5505
5506     for (i = 0; i < EJDB_MAX_COLLECTIONS && jb->cdbs[i]; ++i);
5507     if (i == EJDB_MAX_COLLECTIONS) {
5508         _ejdbsetecode(jb, JBEMAXNUMCOLS, __FILE__, __LINE__, __func__);
5509         return false;
5510     }
5511     rv = _createcoldb(cname, jb, opts, &cdb);
5512     if (!rv) {
5513         *res = NULL;
5514         return rv;
5515     }
5516     EJCOLL *coll;
5517     TCCALLOC(coll, 1, sizeof (*coll));
5518     jb->cdbs[i] = coll;
5519     ++jb->cdbsnum;
5520     coll->cname = tcstrdup(cname);
5521     coll->cnamesz = strlen(cname);
5522     coll->tdb = cdb;
5523     coll->jb = jb;
5524     coll->mmtx = NULL;
5525     _ejdbcolsetmutex(coll);
5526     *res = coll;
5527     return rv;
5528 }
5529
5530 static bool _createcoldb(const char *colname, EJDB *jb, EJCOLLOPTS *opts, TCTDB **res) {
5531     assert(jb && jb->metadb);
5532     if (!JBISVALCOLNAME(colname)) {
5533         _ejdbsetecode(jb, JBEINVALIDCOLNAME, __FILE__, __LINE__, __func__);
5534         *res = NULL;
5535         return false;
5536     }
5537     bool rv = true;
5538     TCTDB *cdb = tctdbnew();
5539     tctdbsetmutex(cdb);
5540     if (opts) {
5541         if (opts->cachedrecords > 0) {
5542             tctdbsetcache(cdb, opts->cachedrecords, 0, 0);
5543         }
5544         int bnum = 0;
5545         uint8_t tflags = 0;
5546         if (opts->records > 0) {
5547             bnum = tclmax(opts->records * 2 + 1, TDBDEFBNUM);
5548         }
5549         if (opts->large) {
5550             tflags |= TDBTLARGE;
5551         }
5552         if (opts->compressed) {
5553             tflags |= TDBTDEFLATE;
5554         }
5555         tctdbtune(cdb, bnum, 0, 0, tflags);
5556     }
5557     const char *mdbpath = jb->metadb->hdb->path;
5558     assert(mdbpath);
5559     TCXSTR *cxpath = tcxstrnew2(mdbpath);
5560     tcxstrcat2(cxpath, "_");
5561     tcxstrcat2(cxpath, colname);
5562     uint32_t mode = jb->metadb->hdb->omode;
5563     if (mode & (JBOWRITER | JBOCREAT)) {
5564         mode |= JBOCREAT;
5565     }
5566     rv = tctdbopen(cdb, tcxstrptr(cxpath), mode);
5567     *res = rv ? cdb : NULL;
5568     tcxstrdel(cxpath);
5569     return rv;
5570 }
5571
5572 /* Check whether a string includes all tokens in another string.*/
5573 static bool _qrycondcheckstrand(const char *vbuf, const TCLIST *tokens) {
5574     assert(vbuf && tokens);
5575     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
5576         const char *token = TCLISTVALPTR(tokens, i);
5577         int tokensz = TCLISTVALSIZ(tokens, i);
5578         bool found = false;
5579         const char *str = vbuf;
5580         while (true) {
5581             const char *sp = str;
5582             while (*str != '\0' && !strchr(", ", *str)) {
5583                 str++;
5584             }
5585             if (tokensz == (str - sp) && !strncmp(token, sp, tokensz)) { //Token matched
5586                 found = true;
5587                 break;
5588             }
5589             if (*str == '\0') break;
5590             str++;
5591         }
5592         if (!found) {
5593             return false;
5594         }
5595     }
5596     return true;
5597 }
5598
5599 /* Check whether a string includes at least one tokens in another string.*/
5600 static bool _qrycondcheckstror(const char *vbuf, const TCLIST *tokens) {
5601     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
5602         const char *token = TCLISTVALPTR(tokens, i);
5603         int tokensz = TCLISTVALSIZ(tokens, i);
5604         bool found = false;
5605         const char *str = vbuf;
5606         while (true) {
5607             const char *sp = str;
5608             while (*str != '\0' && !strchr(", ", *str)) {
5609                 str++;
5610             }
5611             if (tokensz == (str - sp) && !strncmp(token, sp, tokensz)) { //Token matched
5612                 found = true;
5613                 break;
5614             }
5615             if (*str == '\0') break;
5616             str++;
5617         }
5618         if (found) {
5619             return true;
5620         }
5621     }
5622     return false;
5623 }