$push and $pushAll implemented. Fixed #130
[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             .matched = 0
2781         };
2782         if (bson_strip2(&sctx) != BSON_OK) {
2783             _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
2784         }
2785         if (inbuf != bsbuf && inbuf != bstack) {
2786             TCFREE(inbuf);
2787         }
2788         if (_ifields != ifields) {
2789             tcmapdel(_ifields);
2790         }
2791         if (_fkfields) {
2792             tcmapdel(_fkfields);
2793         }
2794     }
2795
2796     if (rv) {
2797         assert(bsout.finished);
2798         if (bsout.flags & BSON_FLAG_STACK_ALLOCATED) {
2799             TCLISTPUSH(ctx->res, bsout.data, bson_size(&bsout));
2800         } else {
2801             tclistpushmalloc(ctx->res, bsout.data, bson_size(&bsout));
2802         }
2803     } else {
2804         bson_destroy(&bsout);
2805     }
2806     return rv;
2807 }
2808
2809 static bool _exec_do(_QRYCTX *ctx, const void *bsbuf, bson *bsout) {
2810     assert(ctx && ctx->dfields);
2811     _BSONDOVISITORCTX ictx = {
2812         .q = ctx->q,
2813         .jb = ctx->coll->jb,
2814         .dfields = ctx->dfields,
2815         .sbson = bsout
2816     };
2817     bson_iterator it;
2818     BSON_ITERATOR_FROM_BUFFER(&it, bsbuf);
2819     bson_visit_fields(&it, 0, _bsondovisitor, &ictx);
2820     if (bson_finish(bsout) != BSON_OK) {
2821         _ejdbsetecode(ctx->coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
2822         return false;
2823     }
2824     return true;
2825 }
2826
2827 //Create update BSON object for $set/$unset/$inc operations
2828 static bson* _qfgetupdateobj(const EJQF *qf) {
2829     assert(qf->updateobj);
2830     if (!qf->ufields || TCLISTNUM(qf->ufields) < 1) { //we do not ref $(query) fields.
2831         return qf->updateobj;
2832     }
2833     const EJQ *q = qf->q;
2834     char pbuf[BSON_MAX_FPATH_LEN + 1];
2835     int ppos = 0;
2836     bson_iterator it;
2837     bson_type bt;
2838     bson *ret =  bson_create();
2839     bson_init(ret);
2840     for (int i = 0; i < TCLISTNUM(qf->ufields); ++i) {
2841         const char *uf = TCLISTVALPTR(qf->ufields, i);
2842         for (int j = 0; *(q->allqfields + j) != '\0'; ++j) {
2843             const EJQF *kqf = *(q->allqfields + j);
2844             if (kqf == qf || kqf->uslots == NULL || TCLISTNUM(kqf->uslots) < 1) {
2845                 continue;
2846             }
2847             for (int k = 0; k < TCLISTNUM(kqf->uslots); ++k) {
2848                 USLOT *uslot = TCLISTVALPTR(kqf->uslots, k);
2849                 if (uslot->op == uf && uslot->mpos >= 0) {
2850                     char *dp = strchr(uf, '$');
2851                     assert(dp);
2852                     ppos = (dp - uf);
2853                     assert(ppos == uslot->dpos + 1);
2854                     if (ppos < 1 || ppos >= BSON_MAX_FPATH_LEN - 1) {
2855                         break;
2856                     }
2857                     memcpy(pbuf, uf, ppos);
2858                     int wl = bson_numstrn(pbuf + ppos, (BSON_MAX_FPATH_LEN - ppos), uslot->mpos);
2859                     if (wl >= BSON_MAX_FPATH_LEN - ppos) { //output is truncated
2860                         break;
2861                     }
2862                     ppos += wl;
2863                     //copy suffix
2864                     for (int fpos = (dp - uf) + 1; ppos < BSON_MAX_FPATH_LEN && *(uf + fpos) != '\0';) {
2865                         pbuf[ppos++] = *(uf + fpos++);
2866                     }
2867                     assert(ppos <= BSON_MAX_FPATH_LEN);
2868                     pbuf[ppos] = '\0';
2869
2870                     bt = bson_find(&it, qf->updateobj, uf);
2871                     if (bt == BSON_EOO) {
2872                         assert(false);
2873                         break;
2874                     }
2875                     bson_append_field_from_iterator2(pbuf, &it, ret);
2876                     break;
2877                 }
2878             }
2879         }
2880     }
2881     BSON_ITERATOR_INIT(&it, qf->updateobj);
2882     while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
2883         const char *key = bson_iterator_key(&it);
2884         if (strchr(key, '$') == NULL) {
2885             bson_append_field_from_iterator2(key, &it, ret);
2886         }
2887     }
2888     bson_finish(ret);
2889     return ret;
2890 }
2891
2892 static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
2893     assert(ctx && ctx->q && (ctx->q->flags & EJQUPDATING) && bsbuf && ctx->didxctx);
2894
2895     bool rv = true;
2896     int update = 0;
2897     EJCOLL *coll = ctx->coll;
2898     EJQ *q = ctx->q;
2899     bson_oid_t *oid;
2900     bson_type bt, bt2;
2901     bson_iterator it, it2;
2902     TCMAP *rowm = NULL;
2903
2904     if (q->flags & EJQDROPALL) { //Records will be dropped
2905         bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME);
2906         if (bt != BSON_OID) {
2907             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
2908             return false;
2909         }
2910         oid = bson_iterator_oid(&it);
2911         assert(oid);
2912         if (ctx->log) {
2913             char xoid[25];
2914             bson_oid_to_string(oid, xoid);
2915             tcxstrprintf(ctx->log, "$DROPALL ON: %s\n", xoid);
2916         }
2917         const void *olddata;
2918         int olddatasz = 0;
2919         TCMAP *rmap = tctdbget(coll->tdb, oid, sizeof (*oid));
2920         if (rmap) {
2921             olddata = tcmapget3(rmap, JDBCOLBSON, JDBCOLBSONL, &olddatasz);
2922             if (!_updatebsonidx(coll, oid, NULL, olddata, olddatasz, ctx->didxctx) ||
2923                     !tctdbout(coll->tdb, oid, sizeof (*oid))) {
2924                 rv = false;
2925             }
2926             tcmapdel(rmap);
2927         }
2928         return rv;
2929     }
2930     
2931     //Apply update operation
2932     bson bsout;
2933     bsout.data = NULL;
2934     bsout.dataSize = 0;
2935     bson_reset(&bsout);
2936
2937     const EJQF *setqf = NULL; /*$set*/
2938     const EJQF *unsetqf = NULL; /*$unset*/
2939     const EJQF *incqf = NULL; /*$inc*/
2940         const EJQF *renameqf = NULL; /*$rename*/
2941     const EJQF *addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/
2942     const EJQF *pushqf[2] = {NULL}; /*$push, $pushAll */
2943     const EJQF *pullqf[2] = {NULL}; /*$pull, $pullAll*/
2944
2945     for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
2946         
2947         const EJQF *qf = TCLISTVALPTR(q->qflist, i);
2948         const uint32_t flags = qf->flags;
2949         
2950         if (qf->updateobj == NULL) {
2951             continue;
2952         } 
2953         if (flags & EJCONDSET) { //$set
2954             setqf = qf;
2955         } else if (flags & EJCONDUNSET) { //$unset
2956             unsetqf = qf;
2957         } else if (flags & EJCONDINC) { //$inc
2958             incqf = qf;
2959         } else if (flags & EJCONDRENAME) { //$rename
2960                         renameqf = qf;
2961                 } else if (flags & EJCONDADDSET) { //$addToSet, $addToSetAll
2962             if (flags & EJCONDALL) {
2963                 addsetqf[1] = qf;
2964             } else {
2965                 addsetqf[0] = qf;
2966             }
2967         } else if (flags & EJCONDPUSH) { //$push, $pushAll
2968             if (flags & EJCONDALL) {
2969                 pushqf[1] = qf;
2970             } else {
2971                 pushqf[0] = qf;
2972             }
2973         } else if (flags & EJCONDPULL) { //$pull, $pullAll
2974             if (flags & EJCONDALL) {
2975                 pullqf[1] = qf;
2976             } else {
2977                 pullqf[0] = qf;
2978             }
2979         }
2980     }
2981     
2982         if (renameqf) { //todo rename nested fields!
2983         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
2984         if (bsout.finished) {
2985             //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
2986             bson_init_size(&bsout, bson_size(&bsout));
2987         } else {
2988             assert(bsout.data == NULL);
2989             bson_init_size(&bsout, bsbufsz);
2990         }
2991         TCMAP *efields = NULL;
2992                 bson *updobj = _qfgetupdateobj(renameqf);
2993                 BSON_ITERATOR_INIT(&it, updobj);
2994                 while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
2995                         if (bt != BSON_STRING) {
2996                                 continue;
2997                         }
2998             const char *ofpath = BSON_ITERATOR_KEY(&it);
2999                         const char *nfpath = bson_iterator_string(&it);
3000             bt2 = bson_find_from_buffer(&it2, inbuf, ofpath);
3001                         if (bt2 == BSON_EOO) { 
3002                                 continue;
3003                         }
3004                         if (bson_append_field_from_iterator2(nfpath, &it2, &bsout) != BSON_OK) {
3005                                 rv = false;
3006                                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3007                                 break;
3008                         }
3009             update++;
3010             if (!efields) {
3011                 efields = tcmapnew2(TCMAPTINYBNUM);
3012             }
3013                         tcmapputkeep(efields, ofpath, strlen(ofpath), "", 0);
3014                         tcmapputkeep(efields, nfpath, strlen(nfpath), "", 0);
3015                 }
3016
3017         BSON_ITERATOR_FROM_BUFFER(&it, inbuf);
3018         while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
3019             const char *fpath = BSON_ITERATOR_KEY(&it);
3020                         if (efields && tcmapget2(efields, fpath)) { 
3021                                 continue;
3022                         }
3023                         if (bson_append_field_from_iterator(&it, &bsout) != BSON_OK) {
3024                                 rv = false;
3025                                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3026                                 break;
3027                         }
3028                 }
3029         if (efields) {
3030             tcmapdel(efields);
3031         }
3032         bson_finish(&bsout);
3033         if (inbuf != bsbuf) {
3034             TCFREE(inbuf);
3035         }
3036         if (updobj != renameqf->updateobj) {
3037             bson_del(updobj);
3038         }
3039         if (!rv) {
3040             goto finish;
3041         }
3042         }
3043     
3044     if (unsetqf) { //$unset
3045         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3046         if (bsout.finished) {
3047             bson_init_size(&bsout, bson_size(&bsout));
3048         } else {
3049             assert(bsout.data == NULL);
3050             bson_init_size(&bsout, bsbufsz);
3051         }
3052         int matched = 0;
3053         bson *updobj = _qfgetupdateobj(unsetqf);
3054         TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM);
3055         BSON_ITERATOR_INIT(&it, updobj);
3056         while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
3057             const char *fpath = BSON_ITERATOR_KEY(&it);
3058             tcmapput(ifields, fpath, strlen(fpath), &yes, sizeof(yes));
3059         }
3060         if (bson_strip(ifields, false, inbuf, &bsout, &matched) != BSON_OK) {
3061             rv = false;
3062             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3063         }
3064         if (matched > 0) {
3065             update++;
3066         }
3067         tcmapdel(ifields);
3068         bson_finish(&bsout);
3069         if (inbuf != bsbuf) {
3070             TCFREE(inbuf);
3071         }
3072         if (updobj != unsetqf->updateobj) {
3073             bson_del(updobj);
3074         }
3075         if (!rv) {
3076             goto finish;
3077         }
3078     }
3079
3080     if (setqf) { //$set
3081         update++;
3082         bson *updobj = _qfgetupdateobj(setqf);
3083         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3084         if (bsout.finished) {
3085             bson_init_size(&bsout, bson_size(&bsout));
3086         } else {
3087             assert(bsout.data == NULL);
3088             bson_init_size(&bsout, bsbufsz);
3089         }
3090         int err = bson_merge3(bsbuf, bson_data(updobj), &bsout);
3091         if (err) {
3092             rv = false;
3093             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3094         }
3095         bson_finish(&bsout);
3096         if (inbuf != bsbuf) {
3097             TCFREE(inbuf);
3098         }
3099         if (updobj != setqf->updateobj) {
3100             bson_del(updobj);
3101         }
3102         if (!rv) {
3103             goto finish;
3104         }
3105     }
3106
3107     if (incqf) { //$inc
3108         bson *updobj = _qfgetupdateobj(incqf);
3109         if (!bsout.data) {
3110             bson_create_from_buffer2(&bsout, bsbuf, bsbufsz);
3111         }
3112         BSON_ITERATOR_INIT(&it, updobj);
3113         while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
3114             if (!BSON_IS_NUM_TYPE(bt)) {
3115                 continue;
3116             }
3117             BSON_ITERATOR_INIT(&it2, &bsout);
3118             bt2 = bson_find_fieldpath_value(BSON_ITERATOR_KEY(&it), &it2);
3119             if (!BSON_IS_NUM_TYPE(bt2)) {
3120                 continue;
3121             }
3122             if (bt2 == BSON_DOUBLE) {
3123                 double v = bson_iterator_double(&it2);
3124                 if (bt == BSON_DOUBLE) {
3125                     v += bson_iterator_double(&it);
3126                 } else {
3127                     v += bson_iterator_long(&it);
3128                 }
3129                 if (bson_inplace_set_double(&it2, v)) {
3130                     rv = false;
3131                     _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3132                     break;
3133                 }
3134                 update++;
3135             } else {
3136                 int64_t v = bson_iterator_long(&it2);
3137                 v += bson_iterator_long(&it);
3138                 if (bson_inplace_set_long(&it2, v)) {
3139                     rv = false;
3140                     _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3141                     break;
3142                 }
3143                 update++;
3144             }
3145         }
3146         if (updobj != incqf->updateobj) {
3147             bson_del(updobj);
3148         }
3149         if (!rv) {
3150             goto finish;
3151         }
3152     }
3153
3154     for (int i = 0; i < 2; ++i) { //$pull $pullAll
3155         const EJQF *qf = pullqf[i];
3156         if (!qf) continue;
3157         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3158         if (bson_find_merged_arrays(bson_data(qf->updateobj), inbuf, (qf->flags & EJCONDALL))) {
3159             if (bsout.finished) {
3160                 bson_init_size(&bsout, bson_size(&bsout));
3161             } else {
3162                 assert(bsout.data == NULL);
3163                 bson_init_size(&bsout, bsbufsz);
3164             }
3165             //$pull $pullAll merge
3166             if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, 
3167                                   BSON_MERGE_ARRAY_PULL, (qf->flags & EJCONDALL), &bsout)) {
3168                 rv = false;
3169                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3170             }
3171             if (inbuf != bsbuf) {
3172                 TCFREE(inbuf);
3173             }
3174             bson_finish(&bsout);
3175             update++;
3176         }
3177         if (!rv) {
3178             goto finish;
3179         }
3180     }
3181     
3182     for (int i = 0; i < 2; ++i) { //$push $pushAll
3183         const EJQF *qf = pushqf[i];
3184         if (!qf) continue;
3185         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3186         if (bsout.finished) {
3187             bson_init_size(&bsout, bson_size(&bsout));
3188         } else {
3189             assert(bsout.data == NULL);
3190             bson_init_size(&bsout, bsbufsz);
3191         }
3192         //$push $pushAll merge
3193         if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, 
3194                               BSON_MERGE_ARRAY_PUSH, (qf->flags & EJCONDALL), &bsout)) {
3195             rv = false;
3196             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3197         }
3198         if (inbuf != bsbuf) {
3199             TCFREE(inbuf);
3200         }
3201         bson_finish(&bsout);
3202         update++;
3203     }
3204
3205     for (int i = 0; i < 2; ++i) { //$addToSet $addToSetAll
3206         const EJQF *qf = addsetqf[i];
3207         if (!qf) continue;
3208         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
3209         if ((qf->flags & EJCONDALL) || bson_find_unmerged_arrays(bson_data(qf->updateobj), inbuf)) {
3210             //Missing $addToSet element in some array field found
3211             if (bsout.finished) {
3212                 bson_init_size(&bsout, bson_size(&bsout));
3213             } else {
3214                 assert(bsout.data == NULL);
3215                 bson_init_size(&bsout, bsbufsz);
3216             }
3217             //$addToSet $addToSetAll merge
3218             if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, 
3219                                   BSON_MERGE_ARRAY_ADDSET, (qf->flags & EJCONDALL), &bsout)) {
3220                 rv = false;
3221                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3222             }
3223             if (inbuf != bsbuf) {
3224                 TCFREE(inbuf);
3225             }
3226             bson_finish(&bsout);
3227             update++;
3228         }
3229         if (!rv) {
3230             goto finish;
3231         }
3232     }
3233     
3234     //Finishing document update
3235     if (!update || !rv) {
3236         goto finish;
3237     }
3238     if (bsout.err) { //Resulting BSON is OK?
3239         rv = false;
3240         _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3241         goto finish;
3242     }
3243     if (bson_size(&bsout) == 0) { //Record was not updated
3244         goto finish;
3245     }
3246     //Perform updating
3247     bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME);
3248     if (bt != BSON_OID) {
3249         rv = false;
3250         _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
3251         goto finish;
3252     }
3253     oid = bson_iterator_oid(&it);
3254     rowm = tcmapnew2(TCMAPTINYBNUM);
3255     tcmapput(rowm, JDBCOLBSON, JDBCOLBSONL, bson_data(&bsout), bson_size(&bsout));
3256     rv = tctdbput(coll->tdb, oid, sizeof (*oid), rowm);
3257     if (rv) {
3258         rv = _updatebsonidx(coll, oid, &bsout, bsbuf, bsbufsz, ctx->didxctx);
3259     }
3260
3261 finish:
3262     bson_destroy(&bsout);
3263     if (rowm) {
3264         tcmapdel(rowm);
3265     }
3266     return rv;
3267 }
3268
3269 /** Query */
3270 static TCLIST* _qryexecute(EJCOLL *coll, const EJQ *_q, uint32_t *outcount, int qflags, TCXSTR *log) {
3271     assert(coll && coll->tdb && coll->tdb->hdb);
3272     *outcount = 0;
3273
3274     _QRYCTX ctx = {NULL};
3275     EJQ *q; //Clone the query object
3276     TCMALLOC(q, sizeof (*q));
3277     if (!_qrydup(_q, q, EJQINTERNAL)) {
3278         TCFREE(q);
3279         return NULL;
3280     }
3281     ctx.log = log;
3282     ctx.q = q;
3283     ctx.qflags = qflags;
3284     ctx.coll = coll;
3285     if (!_qrypreprocess(&ctx)) {
3286         _qryctxclear(&ctx);
3287         return NULL;
3288     }
3289     bool all = false; //if True we need all records to fetch (sorting)
3290     TCHDB *hdb = coll->tdb->hdb;
3291     TCLIST *res = ctx.res;
3292     EJQF *mqf = ctx.mqf;
3293
3294     int sz = 0; //generic size var
3295     int anum = 0; //number of active conditions
3296     int ofsz = 0; //order fields count
3297     int aofsz = 0; //active order fields count
3298     const int qfsz = TCLISTNUM(q->qflist); //number of all condition fields
3299     EJQF **ofs = NULL; //order fields
3300     EJQF **qfs = NULL; //condition fields array
3301     if (qfsz > 0) {
3302         TCMALLOC(qfs, qfsz * sizeof (EJQF*));
3303     }
3304
3305     const void *kbuf;
3306     int kbufsz;
3307     const void *vbuf;
3308     int vbufsz;
3309
3310     uint32_t count = 0; //current count
3311     uint32_t max = (q->max > 0) ? q->max : UINT_MAX;
3312     uint32_t skip = q->skip;
3313     const TDBIDX *midx = mqf ? mqf->idx : NULL;
3314
3315     if (midx) { //Main index used for ordering
3316         if (mqf->orderseq == 1 &&
3317                 !(mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR)) {
3318             mqf->flags |= EJFORDERUSED;
3319         }
3320     }
3321     for (int i = 0; i < qfsz; ++i) {
3322         EJQF *qf = TCLISTVALPTR(q->qflist, i);
3323         assert(qf);
3324         if (log) {
3325             if (qf->exprmap) {
3326                 tcxstrprintf(log, "USING HASH TOKENS IN: %s\n", qf->fpath);
3327             }
3328             if (qf->flags & EJCONDOIT) {
3329                 tcxstrprintf(log, "FIELD: %s HAS $do OPERATION\n", qf->fpath);
3330             }
3331         }
3332         qf->jb = coll->jb;
3333         qfs[i] = qf;
3334         if (qf->fpathsz > 0 && !(qf->flags & EJFEXCLUDED)) {
3335             anum++;
3336         }
3337         if (qf->orderseq) {
3338             ofsz++;
3339             if (q->flags & EJQONLYCOUNT) {
3340                 qf->flags |= EJFORDERUSED;
3341             }
3342         }
3343     }
3344     if (ofsz > 0) { //Collect order fields array
3345         TCMALLOC(ofs, ofsz * sizeof (EJQF*));
3346         for (int i = 0; i < ofsz; ++i) {
3347             for (int j = 0; j < qfsz; ++j) {
3348                 if (qfs[j]->orderseq == i + 1) { //orderseq starts with 1
3349                     ofs[i] = qfs[j];
3350                     if (!(ofs[i]->flags & EJFORDERUSED)) {
3351                         aofsz++;
3352                         if (ctx.ifields) { //Force order field to be included in result set
3353                             if (ctx.imode) { //add field to the included set
3354                                 tcmapputkeep(ctx.ifields, ofs[i]->fpath, ofs[i]->fpathsz, &yes, sizeof (yes));
3355                             } else { //remove field from excluded
3356                                 tcmapout(ctx.ifields, ofs[i]->fpath, ofs[i]->fpathsz);
3357                             }
3358                         }
3359                     }
3360                     break;
3361                 }
3362             }
3363         }
3364         for (int i = 0; i < ofsz; ++i) assert(ofs[i] != NULL);
3365     }
3366
3367     if ((q->flags & EJQONLYCOUNT) && qfsz == 0 &&
3368             (q->orqlist == NULL || TCLISTNUM(q->orqlist) < 1) &&
3369             (q->andqlist == NULL || TCLISTNUM(q->andqlist) < 1)) { //primitive count(*) query
3370         count = coll->tdb->hdb->rnum;
3371         if (log) {
3372             tcxstrprintf(log, "SIMPLE COUNT(*): %u\n", count);
3373         }
3374         goto finish;
3375     }
3376
3377     if (!(q->flags & EJQONLYCOUNT) && aofsz > 0 && (!midx || mqf->orderseq != 1)) { //Main index is not the main order field
3378         all = true; //Need all records for ordering for some other fields
3379     }
3380
3381     if (log) {
3382         tcxstrprintf(log, "UPDATING MODE: %s\n", (q->flags & EJQUPDATING) ? "YES" : "NO");
3383         tcxstrprintf(log, "MAX: %u\n", max);
3384         tcxstrprintf(log, "SKIP: %u\n", skip);
3385         tcxstrprintf(log, "COUNT ONLY: %s\n", (q->flags & EJQONLYCOUNT) ? "YES" : "NO");
3386         tcxstrprintf(log, "MAIN IDX: '%s'\n", midx ? midx->name : "NONE");
3387         tcxstrprintf(log, "ORDER FIELDS: %d\n", ofsz);
3388         tcxstrprintf(log, "ACTIVE CONDITIONS: %d\n", anum);
3389         tcxstrprintf(log, "ROOT $OR QUERIES: %d\n", ((q->orqlist) ? TCLISTNUM(q->orqlist) : 0));
3390         tcxstrprintf(log, "ROOT $AND QUERIES: %d\n", ((q->andqlist) ? TCLISTNUM(q->andqlist) : 0));
3391         tcxstrprintf(log, "FETCH ALL: %s\n", all ? "YES" : "NO");
3392     }
3393     if (max < UINT_MAX - skip) {
3394         max += skip;
3395     }
3396     if (max == 0) {
3397         goto finish;
3398     }
3399     if (!midx && (!mqf || !(mqf->flags & EJFPKMATCHING))) { //Missing main index & no PK matching
3400         goto fullscan;
3401     }
3402     if (log) {
3403         tcxstrprintf(log, "MAIN IDX TCOP: %d\n", mqf->tcop);
3404     }
3405
3406 #define JBQREGREC(_pkbuf, _pkbufsz, _bsbuf, _bsbufsz)   \
3407     ++count; \
3408     if (q->flags & EJQUPDATING) { \
3409         _qryupdate(&ctx, (_bsbuf), (_bsbufsz)); \
3410     } \
3411     if (!(q->flags & EJQONLYCOUNT) && (all || count > skip)) { \
3412         _pushprocessedbson(&ctx, (_bsbuf), (_bsbufsz)); \
3413     }
3414     //EOF #define JBQREGREC
3415
3416     bool trim = (midx && *midx->name != '\0');
3417     if (anum > 0 && !(mqf->flags & EJFEXCLUDED) && !(mqf->uslots && TCLISTNUM(mqf->uslots) > 0)) {
3418         anum--;
3419         mqf->flags |= EJFEXCLUDED;
3420     }
3421
3422     if (mqf->flags & EJFPKMATCHING) { //PK matching
3423         if (log) {
3424             tcxstrprintf(log, "PRIMARY KEY MATCHING: TRUE\n");
3425         }
3426         assert(mqf->expr);
3427         if (mqf->tcop == TDBQCSTREQ) {
3428             do {
3429                 bson_oid_t oid;
3430                 bson_oid_from_string(&oid, mqf->expr);
3431                 tcxstrclear(q->colbuf);
3432                 tcxstrclear(q->bsbuf);
3433                 sz = tchdbgetintoxstr(coll->tdb->hdb, &oid, sizeof (oid), q->colbuf);
3434                 if (sz <= 0) {
3435                     break;
3436                 }
3437                 sz = tcmaploadoneintoxstr(TCXSTRPTR(q->colbuf), TCXSTRSIZE(q->colbuf), JDBCOLBSON, JDBCOLBSONL, q->bsbuf);
3438                 if (sz <= 0) {
3439                     break;
3440                 }
3441                 bool matched = true;
3442                 for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags;
3443                 for (int i = 0; i < qfsz; ++i) {
3444                     EJQF *qf = qfs[i];
3445                     if (qf->mflags & EJFEXCLUDED) continue;
3446                     if (!_qrybsmatch(qf, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf))) {
3447                         matched = false;
3448                         break;
3449                     }
3450                 }
3451                 if (matched && _qry_and_or_match(coll, q, &oid, sizeof (oid))) {
3452                     JBQREGREC(&oid, sizeof (oid), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3453                 }
3454             } while (false);
3455         } else if (mqf->tcop == TDBQCSTROREQ) {
3456             TCLIST *tokens = mqf->exprlist;
3457             assert(tokens);
3458             tclistsort(tokens);
3459             for (int i = 1; i < TCLISTNUM(tokens); i++) {
3460                 if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3461                     TCFREE(tclistremove2(tokens, i));
3462                     i--;
3463                 }
3464             }
3465             int tnum = TCLISTNUM(tokens);
3466             for (int i = 0; (all || count < max) && i < tnum; i++) {
3467                 bool matched = true;
3468                 bson_oid_t oid;
3469                 const char *token;
3470                 int tsiz;
3471                 TCLISTVAL(token, tokens, i, tsiz);
3472                 if (tsiz < 1) {
3473                     continue;
3474                 }
3475                 bson_oid_from_string(&oid, token);
3476                 tcxstrclear(q->bsbuf);
3477                 tcxstrclear(q->colbuf);
3478                 sz = tchdbgetintoxstr(coll->tdb->hdb, &oid, sizeof (oid), q->colbuf);
3479                 if (sz <= 0) {
3480                     continue;
3481                 }
3482                 sz = tcmaploadoneintoxstr(TCXSTRPTR(q->colbuf), TCXSTRSIZE(q->colbuf), JDBCOLBSON, JDBCOLBSONL, q->bsbuf);
3483                 if (sz <= 0) {
3484                     continue;
3485                 }
3486                 for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags;
3487                 for (int i = 0; i < qfsz; ++i) {
3488                     EJQF *qf = qfs[i];
3489                     if (qf->mflags & EJFEXCLUDED) continue;
3490                     if (!_qrybsmatch(qf, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf))) {
3491                         matched = false;
3492                         break;
3493                     }
3494                 }
3495                 if (matched && _qry_and_or_match(coll, q, &oid, sizeof (oid))) {
3496                     JBQREGREC(&oid, sizeof (oid), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3497                 }
3498             }
3499         } else {
3500             assert(0);
3501         }
3502     } else if (mqf->tcop == TDBQTRUE) {
3503         BDBCUR *cur = tcbdbcurnew(midx->db);
3504         if (mqf->order >= 0) {
3505             tcbdbcurfirst(cur);
3506         } else {
3507             tcbdbcurlast(cur);
3508         }
3509         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3510             if (trim) kbufsz -= 3;
3511             vbuf = tcbdbcurval3(cur, &vbufsz);
3512             if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3513                 JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3514             }
3515             if (mqf->order >= 0) {
3516                 tcbdbcurnext(cur);
3517             } else {
3518                 tcbdbcurprev(cur);
3519             }
3520         }
3521         tcbdbcurdel(cur);
3522     } else if (mqf->tcop == TDBQCSTREQ) { /* string is equal to */
3523         assert(midx->type == TDBITLEXICAL);
3524         char *expr = mqf->expr;
3525         int exprsz = mqf->exprsz;
3526         BDBCUR *cur = tcbdbcurnew(midx->db);
3527         tcbdbcurjump(cur, expr, exprsz + trim);
3528         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3529             if (trim) kbufsz -= 3;
3530             if (kbufsz == exprsz && !memcmp(kbuf, expr, exprsz)) {
3531                 vbuf = tcbdbcurval3(cur, &vbufsz);
3532                 if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3533                     JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3534                 }
3535             } else {
3536                 break;
3537             }
3538             tcbdbcurnext(cur);
3539         }
3540         tcbdbcurdel(cur);
3541     } else if (mqf->tcop == TDBQCSTRBW) { /* string begins with */
3542         assert(midx->type == TDBITLEXICAL);
3543         char *expr = mqf->expr;
3544         int exprsz = mqf->exprsz;
3545         BDBCUR *cur = tcbdbcurnew(midx->db);
3546         tcbdbcurjump(cur, expr, exprsz + trim);
3547         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3548             if (trim) kbufsz -= 3;
3549             if (kbufsz >= exprsz && !memcmp(kbuf, expr, exprsz)) {
3550                 vbuf = tcbdbcurval3(cur, &vbufsz);
3551                 if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3552                     JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3553                 }
3554             } else {
3555                 break;
3556             }
3557             tcbdbcurnext(cur);
3558         }
3559         tcbdbcurdel(cur);
3560     } else if (mqf->tcop == TDBQCSTRORBW) { /* string begins with one token in */
3561         assert(mqf->ftype == BSON_ARRAY);
3562         assert(midx->type == TDBITLEXICAL);
3563         BDBCUR *cur = tcbdbcurnew(midx->db);
3564         TCLIST *tokens = mqf->exprlist;
3565         assert(tokens);
3566         tclistsort(tokens);
3567         for (int i = 1; i < TCLISTNUM(tokens); i++) {
3568             if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3569                 TCFREE(tclistremove2(tokens, i));
3570                 i--;
3571             }
3572         }
3573         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) {
3574             tclistinvert(tokens);
3575         }
3576         int tnum = TCLISTNUM(tokens);
3577         for (int i = 0; (all || count < max) && i < tnum; i++) {
3578             const char *token;
3579             int tsiz;
3580             TCLISTVAL(token, tokens, i, tsiz);
3581             if (tsiz < 1) continue;
3582             tcbdbcurjump(cur, token, tsiz + trim);
3583             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3584                 if (trim) kbufsz -= 3;
3585                 if (kbufsz >= tsiz && !memcmp(kbuf, token, tsiz)) {
3586                     vbuf = tcbdbcurval3(cur, &vbufsz);
3587                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3588                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3589                     }
3590                 } else {
3591                     break;
3592                 }
3593                 tcbdbcurnext(cur);
3594             }
3595         }
3596         tcbdbcurdel(cur);
3597     } else if (mqf->tcop == TDBQCSTROREQ) { /* string is equal to at least one token in */
3598         assert(mqf->ftype == BSON_ARRAY);
3599         assert(midx->type == TDBITLEXICAL);
3600         BDBCUR *cur = tcbdbcurnew(midx->db);
3601         TCLIST *tokens = mqf->exprlist;
3602         assert(tokens);
3603         tclistsort(tokens);
3604         for (int i = 1; i < TCLISTNUM(tokens); i++) {
3605             if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3606                 TCFREE(tclistremove2(tokens, i));
3607                 i--;
3608             }
3609         }
3610         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) {
3611             tclistinvert(tokens);
3612         }
3613         int tnum = TCLISTNUM(tokens);
3614         for (int i = 0; (all || count < max) && i < tnum; i++) {
3615             const char *token;
3616             int tsiz;
3617             TCLISTVAL(token, tokens, i, tsiz);
3618             if (tsiz < 1) continue;
3619             tcbdbcurjump(cur, token, tsiz + trim);
3620             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3621                 if (trim) kbufsz -= 3;
3622                 if (kbufsz == tsiz && !memcmp(kbuf, token, tsiz)) {
3623                     vbuf = tcbdbcurval3(cur, &vbufsz);
3624                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3625                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3626                     }
3627                 } else {
3628                     break;
3629                 }
3630                 tcbdbcurnext(cur);
3631             }
3632         }
3633         tcbdbcurdel(cur);
3634     } else if (mqf->tcop == TDBQCNUMEQ) { /* number is equal to */
3635         assert(midx->type == TDBITDECIMAL);
3636         char *expr = mqf->expr;
3637         int exprsz = mqf->exprsz;
3638         BDBCUR *cur = tcbdbcurnew(midx->db);
3639         _EJDBNUM num;
3640         _nufetch(&num, expr, mqf->ftype);
3641         tctdbqryidxcurjumpnum(cur, expr, exprsz, true);
3642         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3643             if (_nucmp(&num, kbuf, mqf->ftype) == 0) {
3644                 vbuf = tcbdbcurval3(cur, &vbufsz);
3645                 if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3646                     JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3647                 }
3648             } else {
3649                 break;
3650             }
3651             tcbdbcurnext(cur);
3652         }
3653         tcbdbcurdel(cur);
3654     } else if (mqf->tcop == TDBQCNUMGT || mqf->tcop == TDBQCNUMGE) {
3655         /* number is greater than | number is greater than or equal to */
3656         assert(midx->type == TDBITDECIMAL);
3657         char *expr = mqf->expr;
3658         int exprsz = mqf->exprsz;
3659         BDBCUR *cur = tcbdbcurnew(midx->db);
3660         _EJDBNUM xnum;
3661         _nufetch(&xnum, expr, mqf->ftype);
3662         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { //DESC
3663             tcbdbcurlast(cur);
3664             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3665                 _EJDBNUM knum;
3666                 _nufetch(&knum, kbuf, mqf->ftype);
3667                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3668                 if (cmp < 0) break;
3669                 if (cmp > 0 || (mqf->tcop == TDBQCNUMGE && cmp >= 0)) {
3670                     vbuf = tcbdbcurval3(cur, &vbufsz);
3671                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3672                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3673                     }
3674                 }
3675                 tcbdbcurprev(cur);
3676             }
3677         } else { //ASC
3678             tctdbqryidxcurjumpnum(cur, expr, exprsz, true);
3679             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3680                 _EJDBNUM knum;
3681                 _nufetch(&knum, kbuf, mqf->ftype);
3682                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3683                 if (cmp > 0 || (mqf->tcop == TDBQCNUMGE && cmp >= 0)) {
3684                     vbuf = tcbdbcurval3(cur, &vbufsz);
3685                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3686                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3687                     }
3688                 }
3689                 tcbdbcurnext(cur);
3690             }
3691         }
3692         tcbdbcurdel(cur);
3693     } else if (mqf->tcop == TDBQCNUMLT || mqf->tcop == TDBQCNUMLE) {
3694         /* number is less than | number is less than or equal to */
3695         assert(midx->type == TDBITDECIMAL);
3696         char *expr = mqf->expr;
3697         int exprsz = mqf->exprsz;
3698         BDBCUR *cur = tcbdbcurnew(midx->db);
3699         _EJDBNUM xnum;
3700         _nufetch(&xnum, expr, mqf->ftype);
3701         if (mqf->order >= 0) { //ASC
3702             tcbdbcurfirst(cur);
3703             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3704                 _EJDBNUM knum;
3705                 _nufetch(&knum, kbuf, mqf->ftype);
3706                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3707                 if (cmp > 0) break;
3708                 if (cmp < 0 || (cmp <= 0 && mqf->tcop == TDBQCNUMLE)) {
3709                     vbuf = tcbdbcurval3(cur, &vbufsz);
3710                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3711                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3712                     }
3713                 }
3714                 tcbdbcurnext(cur);
3715             }
3716         } else {
3717             tctdbqryidxcurjumpnum(cur, expr, exprsz, false);
3718             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3719                 _EJDBNUM knum;
3720                 _nufetch(&knum, kbuf, mqf->ftype);
3721                 int cmp = _nucmp2(&knum, &xnum, mqf->ftype);
3722                 if (cmp < 0 || (cmp <= 0 && mqf->tcop == TDBQCNUMLE)) {
3723                     vbuf = tcbdbcurval3(cur, &vbufsz);
3724                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3725                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3726                     }
3727                 }
3728                 tcbdbcurprev(cur);
3729             }
3730         }
3731         tcbdbcurdel(cur);
3732     } else if (mqf->tcop == TDBQCNUMBT) { /* number is between two tokens of */
3733         assert(mqf->ftype == BSON_ARRAY);
3734         assert(midx->type == TDBITDECIMAL);
3735         assert(mqf->exprlist);
3736         TCLIST *tokens = mqf->exprlist;
3737         assert(TCLISTNUM(tokens) == 2);
3738         const char *expr;
3739         int exprsz;
3740         long double lower = tcatof2(tclistval2(tokens, 0));
3741         long double upper = tcatof2(tclistval2(tokens, 1));
3742         expr = tclistval2(tokens, (lower > upper) ? 1 : 0);
3743         exprsz = strlen(expr);
3744         if (lower > upper) {
3745             long double swap = lower;
3746             lower = upper;
3747             upper = swap;
3748         }
3749         BDBCUR *cur = tcbdbcurnew(midx->db);
3750         tctdbqryidxcurjumpnum(cur, expr, exprsz, true);
3751         while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3752             if (tcatof2(kbuf) > upper) break;
3753             vbuf = tcbdbcurval3(cur, &vbufsz);
3754             if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3755                 JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3756             }
3757             tcbdbcurnext(cur);
3758         }
3759         tcbdbcurdel(cur);
3760         if (!all && !(q->flags & EJQONLYCOUNT) && mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { //DESC
3761             tclistinvert(res);
3762         }
3763     } else if (mqf->tcop == TDBQCNUMOREQ) { /* number is equal to at least one token in */
3764         assert(mqf->ftype == BSON_ARRAY);
3765         assert(midx->type == TDBITDECIMAL);
3766         BDBCUR *cur = tcbdbcurnew(midx->db);
3767         TCLIST *tokens = mqf->exprlist;
3768         assert(tokens);
3769         tclistsortex(tokens, tdbcmppkeynumasc);
3770         for (int i = 1; i < TCLISTNUM(tokens); i++) {
3771             if (tcatof2(TCLISTVALPTR(tokens, i)) == tcatof2(TCLISTVALPTR(tokens, i - 1))) {
3772                 TCFREE(tclistremove2(tokens, i));
3773                 i--;
3774             }
3775         }
3776         if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) {
3777             tclistinvert(tokens);
3778         }
3779         int tnum = TCLISTNUM(tokens);
3780         for (int i = 0; (all || count < max) && i < tnum; i++) {
3781             const char *token;
3782             int tsiz;
3783             TCLISTVAL(token, tokens, i, tsiz);
3784             if (tsiz < 1) continue;
3785             long double xnum = tcatof2(token);
3786             tctdbqryidxcurjumpnum(cur, token, tsiz, true);
3787             while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) {
3788                 if (tcatof2(kbuf) == xnum) {
3789                     vbuf = tcbdbcurval3(cur, &vbufsz);
3790                     if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, vbuf, vbufsz) && _qry_and_or_match(coll, q, vbuf, vbufsz)) {
3791                         JBQREGREC(vbuf, vbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3792                     }
3793                 } else {
3794                     break;
3795                 }
3796                 tcbdbcurnext(cur);
3797             }
3798         }
3799         tcbdbcurdel(cur);
3800     } else if (mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR) {
3801         /* string includes all tokens in | string includes at least one token in */
3802         assert(midx->type == TDBITTOKEN);
3803         assert(mqf->ftype == BSON_ARRAY);
3804         TCLIST *tokens = mqf->exprlist;
3805         assert(tokens);
3806         if (mqf->tcop == TDBQCSTRNUMOR) {
3807             tclistsortex(tokens, tdbcmppkeynumasc);
3808             for (int i = 1; i < TCLISTNUM(tokens); i++) {
3809                 if (tcatof2(TCLISTVALPTR(tokens, i)) == tcatof2(TCLISTVALPTR(tokens, i - 1))) {
3810                     TCFREE(tclistremove2(tokens, i));
3811                     i--;
3812                 }
3813             }
3814         } else {
3815             tclistsort(tokens);
3816             for (int i = 1; i < TCLISTNUM(tokens); i++) {
3817                 if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) {
3818                     TCFREE(tclistremove2(tokens, i));
3819                     i--;
3820                 }
3821             }
3822         }
3823         TCMAP *tres = tctdbidxgetbytokens(coll->tdb, midx, tokens, mqf->tcop, log);
3824         tcmapiterinit(tres);
3825         while ((all || count < max) && (kbuf = tcmapiternext(tres, &kbufsz)) != NULL) {
3826             if (_qryallcondsmatch(q, anum, coll, qfs, qfsz, kbuf, kbufsz) && _qry_and_or_match(coll, q, kbuf, kbufsz)) {
3827                 JBQREGREC(kbuf, kbufsz, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3828             }
3829         }
3830         tcmapdel(tres);
3831     }
3832
3833     if (q->flags & EJQONLYCOUNT) {
3834         goto finish;
3835     } else {
3836         goto sorting;
3837     }
3838
3839 fullscan: /* Full scan */
3840     assert(count == 0);
3841     assert(!res || TCLISTNUM(res) == 0);
3842
3843     if ((q->flags & EJQDROPALL) && (q->flags & EJQONLYCOUNT)) {
3844         //if we are in primitive $dropall case. Query: {$dropall:true}
3845         if (qfsz == 1 && qfs[0]->tcop == TDBQTRUE) { //single $dropall field
3846             if (log) {
3847                 tcxstrprintf(log, "VANISH WHOLE COLLECTION ON $dropall\n");
3848             }
3849             //write lock already acquired so use impl
3850             count = coll->tdb->hdb->rnum;
3851             if (!tctdbvanish(coll->tdb)) {
3852                 count = 0;
3853             }
3854             goto finish;
3855         }
3856     }
3857
3858     if (log) {
3859         tcxstrprintf(log, "RUN FULLSCAN\n");
3860     }
3861     TCMAP *updkeys = (q->flags & EJQUPDATING) ? tcmapnew2(100 * 1024) : NULL;
3862     TCHDBITER *hdbiter = tchdbiter2init(hdb);
3863     if (!hdbiter) {
3864         goto finish;
3865     }
3866     TCXSTR *skbuf = tcxstrnew3(sizeof (bson_oid_t) + 1);
3867     tcxstrclear(q->colbuf);
3868     tcxstrclear(q->bsbuf);
3869     int rows = 0;
3870     while ((all || count < max) && tchdbiter2next(hdb, hdbiter, skbuf, q->colbuf)) {
3871         ++rows;
3872         sz = tcmaploadoneintoxstr(TCXSTRPTR(q->colbuf), TCXSTRSIZE(q->colbuf), JDBCOLBSON, JDBCOLBSONL, q->bsbuf);
3873         if (sz <= 0) {
3874             goto wfinish;
3875         }
3876         bool matched = true;
3877         for (int i = 0; i < qfsz; ++i) qfs[i]->mflags = qfs[i]->flags;
3878         for (int i = 0; i < qfsz; ++i) {
3879             EJQF *qf = qfs[i];
3880             if (qf->mflags & EJFEXCLUDED) {
3881                 continue;
3882             }
3883             if (!_qrybsmatch(qf, TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf))) {
3884                 matched = false;
3885                 break;
3886             }
3887         }
3888         if (matched && _qry_and_or_match(coll, q, TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf))) {
3889             if (updkeys) { //we are in updating mode
3890                 if (tcmapputkeep(updkeys, TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf), &yes, sizeof (yes))) {
3891                     JBQREGREC(TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3892                 }
3893             } else {
3894                 JBQREGREC(TCXSTRPTR(skbuf), TCXSTRSIZE(skbuf), TCXSTRPTR(q->bsbuf), TCXSTRSIZE(q->bsbuf));
3895             }
3896         }
3897 wfinish:
3898         tcxstrclear(skbuf);
3899         tcxstrclear(q->colbuf);
3900         tcxstrclear(q->bsbuf);
3901     }
3902     tchdbiter2dispose(hdb, hdbiter);
3903     tcxstrdel(skbuf);
3904     if (updkeys) {
3905         tcmapdel(updkeys);
3906     }
3907
3908 sorting: /* Sorting resultset */
3909     if (!res || aofsz <= 0) { //No sorting needed
3910         goto finish;
3911     }
3912     _EJBSORTCTX sctx; //sorting context
3913     sctx.ofs = ofs;
3914     sctx.ofsz = ofsz;
3915     ejdbqsortlist(res, _ejdbsoncmp, &sctx);
3916
3917 finish:
3918     //check $upsert operation
3919     if (count == 0 && (q->flags & EJQUPDATING)) { //finding $upsert qf if no updates maden
3920         for (int i = 0; i < qfsz; ++i) {
3921             if (qfs[i]->flags & EJCONDUPSERT) {
3922                 bson *updateobj = qfs[i]->updateobj;
3923                 assert(updateobj);
3924                 bson_oid_t oid;
3925                 if (_ejdbsavebsonimpl(coll, updateobj, &oid, false)) {
3926                     bson *nbs = bson_create();
3927                     bson_init_size(nbs, bson_size(updateobj) + (strlen(JDBIDKEYNAME) + 1/*key*/ + 1/*type*/ + sizeof (oid)));
3928                     bson_append_oid(nbs, JDBIDKEYNAME, &oid);
3929                     bson_ensure_space(nbs, bson_size(updateobj) - 4);
3930                     bson_append(nbs, bson_data(updateobj) + 4, bson_size(updateobj) - (4 + 1/*BSON_EOO*/));
3931                     bson_finish(nbs);
3932                     if (nbs->err) {
3933                         _ejdbsetecode(coll->jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
3934                         break;
3935                     }
3936                     ++count;
3937                     if (!(q->flags & EJQONLYCOUNT) && (all || count > skip)) {
3938                         _pushprocessedbson(&ctx, bson_data(nbs), bson_size(nbs));
3939                     }
3940                     bson_del(nbs);
3941                 }
3942                 break;
3943             }
3944         }
3945     } //EOF $upsert
3946
3947     //revert max
3948     if (max < UINT_MAX && max > skip) {
3949         max = max - skip;
3950     }
3951     if (res) {
3952         if (all) { //skipping results after full sorting with skip > 0
3953             for (int i = 0; i < skip && res->num > 0; ++i) {
3954                 TCFREE(res->array[res->start].ptr);
3955                 ++(res->start);
3956                 --(res->num);
3957             }
3958         }
3959         if ((res->start & 0xff) == 0 && res->start > (res->num >> 1)) {
3960             memmove(res->array, res->array + res->start, res->num * sizeof (res->array[0]));
3961             res->start = 0;
3962         }
3963         if (TCLISTNUM(res) > max) { //truncate results if max specified
3964             int end = res->start + res->num;
3965             TCLISTDATUM *array = res->array;
3966             for (int i = (res->start + max); i < end; i++) {
3967                 TCFREE(array[i].ptr);
3968                 --(res->num);
3969             }
3970         }
3971     }
3972     count = (skip < count) ? count - skip : 0;
3973     if (count > max) {
3974         count = max;
3975     }
3976     *outcount = count;
3977     if (log) {
3978         tcxstrprintf(log, "RS COUNT: %u\n", count);
3979         tcxstrprintf(log, "RS SIZE: %d\n", (res ? TCLISTNUM(res) : 0));
3980         tcxstrprintf(log, "FINAL SORTING: %s\n", ((q->flags & EJQONLYCOUNT) || aofsz <= 0) ? "NO" : "YES");
3981     }
3982
3983     //Apply deffered index changes
3984     if (ctx.didxctx) {
3985         for (int i = TCLISTNUM(ctx.didxctx) - 1; i >= 0; --i) {
3986             _DEFFEREDIDXCTX *di = TCLISTVALPTR(ctx.didxctx, i);
3987             assert(di);
3988             if (di->rmap) {
3989                 tctdbidxout2(coll->tdb, &(di->oid), sizeof (di->oid), di->rmap);
3990                 tcmapdel(di->rmap);
3991             }
3992             if (di->imap) {
3993                 tctdbidxput2(coll->tdb, &(di->oid), sizeof (di->oid), di->imap);
3994                 tcmapdel(di->imap);
3995             }
3996         }
3997     }
3998     //Cleanup
3999     if (qfs) {
4000         TCFREE(qfs);
4001     }
4002     if (ofs) {
4003         TCFREE(ofs);
4004     }
4005     ctx.res = NULL; //save res from deleting in `_qryctxclear()`
4006     _qryctxclear(&ctx);
4007 #undef JBQREGREC
4008     return res;
4009 }
4010
4011 static void _qryctxclear(_QRYCTX *ctx) {
4012     if (ctx->dfields) {
4013         tcmapdel(ctx->dfields);
4014     }
4015     if (ctx->ifields) {
4016         tcmapdel(ctx->ifields);
4017     }
4018     if (ctx->q) {
4019         ejdbquerydel(ctx->q);
4020     }
4021     if (ctx->res) {
4022         tclistdel(ctx->res);
4023     }
4024     if (ctx->didxctx) {
4025         tclistdel(ctx->didxctx);
4026     }
4027     memset(ctx, 0, sizeof(*ctx));
4028 }
4029
4030 static TDBIDX* _qryfindidx(EJCOLL *coll, EJQF *qf, bson *idxmeta) {
4031     TCTDB *tdb = coll->tdb;
4032     char p = '\0';
4033     switch (qf->tcop) {
4034         case TDBQCSTREQ:
4035         case TDBQCSTRBW:
4036         case TDBQCSTROREQ:
4037         case TDBQCSTRORBW:
4038             p = (qf->flags & EJCONDICASE) ? 'i' : 's'; //lexical string index
4039             break;
4040         case TDBQCNUMEQ:
4041         case TDBQCNUMGT:
4042         case TDBQCNUMGE:
4043         case TDBQCNUMLT:
4044         case TDBQCNUMLE:
4045         case TDBQCNUMBT:
4046         case TDBQCNUMOREQ:
4047             p = 'n'; //number index
4048             break;
4049         case TDBQCSTRAND:
4050         case TDBQCSTROR:
4051             p = 'a'; //token index
4052             break;
4053         case TDBQTRUE:
4054             p = 'o'; //take first appropriate index
4055             break;
4056     }
4057     if (p == '\0' || !qf->fpath || !qf->fpathsz) {
4058         return NULL;
4059     }
4060     for (int i = 0; i < tdb->inum; ++i) {
4061         TDBIDX *idx = tdb->idxs + i;
4062         assert(idx);
4063         if (p == 'o') {
4064             if (*idx->name == 'a' || *idx->name == 'i') { //token index or icase index not the best solution here
4065                 continue;
4066             }
4067         } else if (*idx->name != p) {
4068             continue;
4069         }
4070         if (!strcmp(qf->fpath, idx->name + 1)) {
4071             return idx;
4072         }
4073     }
4074     //No direct operation index. Any alternatives?
4075     if (idxmeta &&
4076             !(qf->flags & EJCONDICASE) && //if not case insensitive query
4077             (
4078                 qf->tcop == TDBQCSTREQ ||
4079                 qf->tcop == TDBQCSTROREQ ||
4080                 qf->tcop == TDBQCNUMOREQ ||
4081                 qf->tcop == TDBQCNUMEQ)
4082        ) {
4083         bson_iterator it;
4084         bson_type bt = bson_find(&it, idxmeta, "iflags");
4085         if (bt != BSON_INT) {
4086             return NULL;
4087         }
4088         int iflags = bson_iterator_int(&it);
4089         if (iflags & JBIDXARR) { //array token index exists so convert qf into TDBQCSTROR
4090             for (int i = 0; i < tdb->inum; ++i) {
4091                 TDBIDX *idx = tdb->idxs + i;
4092                 if (!strcmp(qf->fpath, idx->name + 1)) {
4093                     if (qf->tcop == TDBQCSTREQ) {
4094                         qf->tcop = TDBQCSTROR;
4095                         qf->exprlist = tclistnew2(1);
4096                         TCLISTPUSH(qf->exprlist, qf->expr, qf->exprsz);
4097                         if (qf->expr) TCFREE(qf->expr);
4098                         qf->expr = tclistdump(qf->exprlist, &qf->exprsz);
4099                         qf->ftype = BSON_ARRAY;
4100                         return idx;
4101                     } else if (qf->tcop == TDBQCNUMEQ) {
4102                         qf->tcop = TDBQCSTRNUMOR;
4103                         qf->exprlist = tclistnew2(1);
4104                         TCLISTPUSH(qf->exprlist, qf->expr, qf->exprsz);
4105                         if (qf->expr) TCFREE(qf->expr);
4106                         qf->expr = tclistdump(qf->exprlist, &qf->exprsz);
4107                         qf->ftype = BSON_ARRAY;
4108                         return idx;
4109                     } else if (qf->tcop == TDBQCSTROREQ) {
4110                         assert(qf->ftype == BSON_ARRAY);
4111                         qf->tcop = TDBQCSTROR;
4112                         return idx;
4113                     } else if (qf->tcop == TDBQCNUMOREQ) {
4114                         assert(qf->ftype == BSON_ARRAY);
4115                         qf->tcop = TDBQCSTRNUMOR;
4116                         return idx;
4117                     }
4118                 }
4119             }
4120         }
4121     }
4122     return NULL;
4123 }
4124
4125 static void _registerallqfields(TCLIST *reg, EJQ *q) {
4126     for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
4127         EJQF *qf = TCLISTVALPTR(q->qflist, i);
4128         assert(qf);
4129         tclistpush(reg, &qf, sizeof(qf));
4130     }
4131     for (int i = 0; q->andqlist && i < TCLISTNUM(q->andqlist); ++i) {
4132         _registerallqfields(reg, *((EJQ**) TCLISTVALPTR(q->andqlist, i)));
4133     }
4134     for (int i = 0; q->orqlist && i < TCLISTNUM(q->orqlist); ++i) {
4135         _registerallqfields(reg, *((EJQ**) TCLISTVALPTR(q->orqlist, i)));
4136     }
4137 }
4138
4139 static bool _qrypreprocess(_QRYCTX *ctx) {
4140     assert(ctx->coll && ctx->q && ctx->q->qflist);
4141     EJQ *q = ctx->q;
4142
4143     //Fill the NULL terminated registry of all *EQF fields including all $and $or QF
4144     assert(!q->allqfields);
4145     TCLIST *alist = tclistnew2(TCLISTINYNUM);
4146     _registerallqfields(alist, q);
4147     TCMALLOC(q->allqfields, sizeof(EJQF*) * (TCLISTNUM(alist) + 1));
4148     for (int i = 0; i < TCLISTNUM(alist); ++i) {
4149         EJQF **qfp = TCLISTVALPTR(alist, i);
4150         q->allqfields[i] = *qfp; //*EJQF
4151     }
4152     q->allqfields[TCLISTNUM(alist)] = NULL;
4153     tclistdel(alist);
4154     alist = NULL;
4155
4156     if (ctx->qflags & JBQRYCOUNT) { //sync the user JBQRYCOUNT flag with internal
4157         q->flags |= EJQONLYCOUNT;
4158     }
4159     EJQF *oqf = NULL; //Order condition
4160     TCLIST *qflist = q->qflist;
4161     bson_iterator it;
4162     bson_type bt;
4163
4164     if (q->hints) {
4165         bson_type bt;
4166         bson_iterator it, sit;
4167         //Process $orderby
4168         bt = bson_find(&it, q->hints, "$orderby");
4169         if (bt == BSON_OBJECT) {
4170             int orderseq = 1;
4171             BSON_ITERATOR_SUBITERATOR(&it, &sit);
4172             while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
4173                 if (!BSON_IS_NUM_TYPE(bt)) {
4174                     continue;
4175                 }
4176                 const char *ofield = BSON_ITERATOR_KEY(&sit);
4177                 int odir = bson_iterator_int(&sit);
4178                 odir = (odir > 0) ? 1 : (odir < 0 ? -1 : 0);
4179                 if (!odir) {
4180                     continue;
4181                 }
4182                 EJQF nqf;
4183                 EJQF *qf = NULL;
4184                 for (int i = 0; i < TCLISTNUM(qflist); ++i) {
4185                     if (!strcmp(ofield, ((EJQF*) TCLISTVALPTR(qflist, i))->fpath)) {
4186                         qf = TCLISTVALPTR(qflist, i);
4187                         assert(qf);
4188                         break;
4189                     }
4190                 }
4191                 if (qf == NULL) { //Create syntetic query field for orderby ops
4192                     memset(&nqf, 0, sizeof (EJQF));
4193                     nqf.fpath = tcstrdup(ofield);
4194                     nqf.fpathsz = strlen(nqf.fpath);
4195                     nqf.expr = tcstrdup("");
4196                     nqf.exprsz = 0;
4197                     nqf.tcop = TDBQTRUE; //disable any TC matching operation
4198                     nqf.ftype = BSON_OBJECT;
4199                     nqf.orderseq = orderseq++;
4200                     nqf.order = odir;
4201                     nqf.flags |= EJFEXCLUDED; //field excluded  from matching
4202                     qf = &nqf;
4203                     TCLISTPUSH(qflist, qf, sizeof (*qf));
4204                 } else {
4205                     qf->orderseq = orderseq++;
4206                     qf->order = odir;
4207                 }
4208             }
4209         }
4210         bt = bson_find(&it, q->hints, "$skip");
4211         if (BSON_IS_NUM_TYPE(bt)) {
4212             int64_t v = bson_iterator_long(&it);
4213             q->skip = (uint32_t) ((v < 0) ? 0 : v);
4214         }
4215         bt = bson_find(&it, q->hints, "$max");
4216         if (ctx->qflags & JBQRYFINDONE) {
4217             q->max = (uint32_t) 1;
4218         } else if (BSON_IS_NUM_TYPE(bt)) {
4219             int64_t v = bson_iterator_long(&it);
4220             q->max = (uint32_t) ((v < 0) ? 0 : v);
4221         }
4222         if (!(ctx->qflags & JBQRYCOUNT)) {
4223             bt = bson_find(&it, q->hints, "$fields"); //Collect required fields
4224             if (bt == BSON_OBJECT) {
4225                 TCMAP *fmap = tcmapnew2(TCMAPTINYBNUM);
4226                 BSON_ITERATOR_SUBITERATOR(&it, &sit);
4227                 for (int i = 0; (bt = bson_iterator_next(&sit)) != BSON_EOO; ++i) {
4228                     if (!BSON_IS_NUM_TYPE(bt)) {
4229                         continue;
4230                     }
4231                     bool inc = (bson_iterator_int(&sit) > 0 ? true : false);
4232                     if (i > 0 && inc != ctx->imode) { //$fields hint cannot mix include and exclude fields
4233                         tcmapdel(fmap);
4234                         _ejdbsetecode(ctx->coll->jb, JBEQINCEXCL, __FILE__, __LINE__, __func__);
4235                         return false;
4236                     }
4237                     ctx->imode = inc;
4238                     const char *key = BSON_ITERATOR_KEY(&sit);
4239                     char *pptr;
4240                     //Checking the $(projection) operator.
4241                     if (inc && (pptr = strstr(key, ".$")) && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.'
4242                         int dc = 0;
4243                         for (int i = 0; *(key + i) != '\0'; ++i) {
4244                             if (*(key + i) == '$' && (dc++ > 0)) break;
4245                         }
4246                         if (dc != 1) { //More than one '$' chars in projection, it is invalid
4247                             continue;
4248                         }
4249                         for (int i = 0; i < TCLISTNUM(qflist); ++i) {
4250                             EJQF *qf = TCLISTVALPTR(qflist, i);
4251                             int j;
4252                             for (j = 0; *(key + j) != '\0' && *(key + j) == *(qf->fpath + j); ++j);
4253                             if (key + j == pptr || key + j == pptr + 1) { //existing QF matched the $(projection) prefix
4254                                 if (!q->ifields) {
4255                                     q->ifields = tcmapnew2(TCMAPTINYBNUM);
4256                                 }
4257                                 tcmapput(q->ifields, qf->fpath, qf->fpathsz, key, strlen(key));
4258                                 break;
4259                             }
4260                         }
4261                         continue; //skip registering this fields in the fmap
4262                     }
4263                     tcmapputkeep(fmap, key, strlen(key), &yes, sizeof (yes));
4264                 }
4265                 if (TCMAPRNUM(fmap) == 0) { //if {$fields : {}} we will force {$fields : {_id:1}}
4266                     ctx->imode = true;
4267                     tcmapputkeep(fmap, JDBIDKEYNAME, JDBIDKEYNAMEL, &yes, sizeof (yes));
4268                 }
4269                 ctx->ifields = fmap;
4270             }
4271         }
4272     } //eof hints
4273
4274     const int scoreexact = 100;
4275     const int scoregtlt = 50;
4276     int maxiscore = 0; //Maximum index score
4277     int maxselectivity = 0;
4278
4279     uint32_t skipflags = (//skip field flags
4280                              EJFNOINDEX |
4281                              EJCONDSET |
4282                              EJCONDINC |
4283                              EJCONDADDSET |
4284                              EJCONDPULL |
4285                              EJCONDUPSERT |
4286                              EJCONDOIT);
4287
4288     for (int i = 0; i < TCLISTNUM(qflist); ++i) {
4289         int iscore = 0;
4290         EJQF *qf = (EJQF*) TCLISTVALPTR(qflist, i);
4291         assert(qf && qf->fpath);
4292
4293         if (qf->flags & EJCONDOIT) { //$do field
4294             TCMAP *dmap = ctx->dfields;
4295             if (!dmap) {
4296                 dmap = tcmapnew2(TCMAPTINYBNUM);
4297                 ctx->dfields = dmap;
4298             }
4299             tcmapputkeep(dmap, qf->fpath, qf->fpathsz, qf, sizeof (*qf));
4300         }
4301
4302         if (qf->flags & skipflags) {
4303             continue;
4304         }
4305         //OID PK matching
4306         if (!qf->negate && (qf->tcop == TDBQCSTREQ || qf->tcop == TDBQCSTROREQ) && !strcmp(JDBIDKEYNAME, qf->fpath)) {
4307             qf->flags |= EJFPKMATCHING;
4308             ctx->mqf = qf;
4309             break;
4310         }
4311
4312         bool firstorderqf = false;
4313         qf->idxmeta = _imetaidx(ctx->coll, qf->fpath);
4314         qf->idx = _qryfindidx(ctx->coll, qf, qf->idxmeta);
4315         if (qf->order && qf->orderseq == 1) { //Index for first 'orderby' exists
4316             oqf = qf;
4317             firstorderqf = true;
4318         }
4319         if (!qf->idx || !qf->idxmeta) {
4320             if (qf->idxmeta) {
4321                 bson_del(qf->idxmeta);
4322             }
4323             qf->idx = NULL;
4324             qf->idxmeta = NULL;
4325             continue;
4326         }
4327         if (qf->tcop == TDBQTRUE || qf->negate) {
4328             continue;
4329         }
4330         int avgreclen = -1;
4331         int selectivity = -1;
4332         bt = bson_find(&it, qf->idxmeta, "selectivity");
4333         if (bt == BSON_DOUBLE) {
4334             selectivity = (int) ((double) bson_iterator_double(&it) * 100); //Selectivity percent
4335         }
4336         bt = bson_find(&it, qf->idxmeta, "avgreclen");
4337         if (bt == BSON_DOUBLE) {
4338             avgreclen = (int) bson_iterator_double(&it);
4339         }
4340         if (selectivity > 0) {
4341             if (selectivity <= 20) { //Not using index at all if selectivity lesser than 20%
4342                 continue;
4343             }
4344             iscore += selectivity;
4345         }
4346         if (firstorderqf) {
4347             iscore += (maxselectivity - selectivity) / 2;
4348         }
4349         if (selectivity > maxselectivity) {
4350             maxselectivity = selectivity;
4351         }
4352         switch (qf->tcop) {
4353             case TDBQCSTREQ:
4354             case TDBQCSTROR:
4355             case TDBQCNUMEQ:
4356             case TDBQCNUMBT:
4357                 iscore += scoreexact;
4358                 break;
4359             case TDBQCSTRBW:
4360             case TDBQCSTREW:
4361                 if (avgreclen > 0 && qf->exprsz > avgreclen) {
4362                     iscore += scoreexact;
4363                 }
4364                 break;
4365             case TDBQCNUMGT:
4366             case TDBQCNUMGE:
4367             case TDBQCNUMLT:
4368             case TDBQCNUMLE:
4369                 if (firstorderqf) {
4370                     iscore += scoreexact;
4371                 } else {
4372                     iscore += scoregtlt;
4373                 }
4374                 break;
4375         }
4376         if (iscore >= maxiscore) {
4377             ctx->mqf = qf;
4378             maxiscore = iscore;
4379         }
4380     }
4381     if (ctx->mqf == NULL && (oqf && oqf->idx && !oqf->negate)) {
4382         ctx->mqf = oqf;
4383     }
4384
4385     if (q->flags & EJQHASUQUERY) { //check update $(query) projection then sync inter-qf refs #91
4386         for (int i = 0; *(q->allqfields + i) != '\0'; ++i) {
4387             EJQF *qf = q->allqfields[i];
4388             if (!qf->ufields) continue;
4389             TCLIST *uflist = qf->ufields;
4390             for (int j = 0; j < TCLISTNUM(uflist); ++j) {
4391                 const char *ukey = TCLISTVALPTR(uflist, j);
4392                 char *pptr = strstr(ukey, ".$");
4393                 assert(pptr);
4394                 for (int k = 0; *(q->allqfields + k) != '\0'; ++k) {
4395                     int l;
4396                     EJQF *kqf = q->allqfields[k];
4397                     if (kqf == qf || !kqf->fpath) { //do not process itself
4398                         continue;
4399                     }
4400                     for (l = 0; *(ukey + l) != '\0' && *(ukey + l) == *(kqf->fpath + l); ++l);
4401                     if (ukey + l == pptr || ukey + l == pptr + 1) { //existing QF matched the $(query) prefix
4402                         if (!kqf->uslots) {
4403                             kqf->uslots = tclistnew2(TCLISTINYNUM);
4404                         }
4405                         USLOT uslot = {
4406                             .mpos = -1,
4407                             .dpos = (pptr - ukey),
4408                             .op = ukey
4409                         };
4410                         tclistpush(kqf->uslots, &uslot, sizeof(uslot));
4411                     }
4412                 }
4413             }
4414         }
4415     }
4416
4417     //Init query processing buffers
4418     assert(!q->colbuf && !q->bsbuf);
4419     q->colbuf = tcxstrnew3(1024);
4420     q->bsbuf = tcxstrnew3(1024);
4421     q->tmpbuf = tcxstrnew();
4422     ctx->didxctx = (q->flags & EJQUPDATING) ? tclistnew() : NULL;
4423     ctx->res = (q->flags & EJQONLYCOUNT) ? NULL : tclistnew2(4096);
4424     return true;
4425 }
4426
4427 static bool _metasetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts) {
4428     bool rv = true;
4429     if (!opts) {
4430         return _metasetbson(jb, colname, strlen(colname), "opts", NULL, false, false);
4431     }
4432     bson *bsopts = bson_create();
4433     bson_init(bsopts);
4434     bson_append_bool(bsopts, "compressed", opts->compressed);
4435     bson_append_bool(bsopts, "large", opts->large);
4436     bson_append_int(bsopts, "cachedrecords", opts->cachedrecords);
4437     bson_append_int(bsopts, "records", opts->records);
4438     bson_finish(bsopts);
4439     rv = _metasetbson(jb, colname, strlen(colname), "opts", bsopts, false, false);
4440     bson_del(bsopts);
4441     return rv;
4442 }
4443
4444 static bool _metagetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts) {
4445     assert(opts);
4446     bool rv = true;
4447     memset(opts, 0, sizeof (*opts));
4448     bson *bsopts = _metagetbson(jb, colname, strlen(colname), "opts");
4449     if (!bsopts) {
4450         return true;
4451     }
4452     bson_iterator it;
4453     bson_type bt = bson_find(&it, bsopts, "compressed");
4454     if (bt == BSON_BOOL) {
4455         opts->compressed = bson_iterator_bool(&it);
4456     }
4457     bt = bson_find(&it, bsopts, "large");
4458     if (bt == BSON_BOOL) {
4459         opts->large = bson_iterator_bool(&it);
4460     }
4461     bt = bson_find(&it, bsopts, "cachedrecords");
4462     if (BSON_IS_NUM_TYPE(bt)) {
4463         opts->cachedrecords = bson_iterator_long(&it);
4464     }
4465     bt = bson_find(&it, bsopts, "records");
4466     if (BSON_IS_NUM_TYPE(bt)) {
4467         opts->records = bson_iterator_long(&it);
4468     }
4469     bson_del(bsopts);
4470     return rv;
4471 }
4472
4473 static bool _metasetbson(EJDB *jb, const char *colname, int colnamesz,
4474                          const char *mkey, bson *val, bool merge, bool mergeoverwrt) {
4475     assert(jb && colname && mkey);
4476     bool rv = true;
4477     bson *bsave = NULL;
4478     bson *oldval = NULL;
4479     bson mresult;
4480
4481     TCMAP *cmeta = tctdbget(jb->metadb, colname, colnamesz);
4482     if (!cmeta) {
4483         _ejdbsetecode(jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
4484         rv = false;
4485         goto finish;
4486     }
4487     if (!val) {
4488         if (tcmapout2(cmeta, mkey)) {
4489             rv = tctdbput(jb->metadb, colname, colnamesz, cmeta);
4490         }
4491         goto finish;
4492     }
4493     assert(val);
4494     if (merge) { //Merged
4495         oldval = _metagetbson(jb, colname, colnamesz, mkey);
4496         if (oldval) {
4497             bson_init(&mresult);
4498             bson_merge(oldval, val, mergeoverwrt, &mresult);
4499             bson_finish(&mresult);
4500             bsave = &mresult;
4501         } else {
4502             bsave = val;
4503         }
4504     } else { //Rewrited
4505         bsave = val;
4506     }
4507
4508     assert(bsave);
4509     tcmapput(cmeta, mkey, strlen(mkey), bson_data(bsave), bson_size(bsave));
4510     rv = tctdbput(jb->metadb, colname, colnamesz, cmeta);
4511 finish:
4512     if (oldval) {
4513         if (merge) {
4514             bson_destroy(bsave);
4515         }
4516         bson_del(oldval);
4517     }
4518     if (cmeta) {
4519         tcmapdel(cmeta);
4520     }
4521     tctdbsync(jb->metadb);
4522     return rv;
4523 }
4524
4525 static bool _metasetbson2(EJCOLL *coll, const char *mkey, bson *val, bool merge, bool mergeoverwrt) {
4526     assert(coll);
4527     return _metasetbson(coll->jb, coll->cname, coll->cnamesz, mkey, val, merge, mergeoverwrt);
4528 }
4529
4530 /**Returned meta BSON data must be freed by 'bson_del' */
4531 static bson* _metagetbson(EJDB *jb, const char *colname, int colnamesz, const char *mkey) {
4532     assert(jb && colname && mkey);
4533     bson *rv = NULL;
4534     TCMAP *cmeta = tctdbget(jb->metadb, colname, colnamesz);
4535     if (!cmeta) {
4536         _ejdbsetecode(jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
4537         return NULL;
4538     }
4539     int bsz;
4540     const void *raw = tcmapget(cmeta, mkey, strlen(mkey), &bsz);
4541     if (!raw || bsz == 0) {
4542         goto finish;
4543     }
4544     rv = bson_create();
4545     bson_init_size(rv, bsz);
4546     bson_ensure_space(rv, bsz - 4);
4547     bson_append(rv, ((char*) raw) + 4, bsz - (4 + 1/*BSON_EOO*/));
4548     bson_finish(rv);
4549 finish:
4550     tcmapdel(cmeta);
4551     return rv;
4552 }
4553
4554 static bson* _metagetbson2(EJCOLL *coll, const char *mkey) {
4555     assert(coll);
4556     return _metagetbson(coll->jb, coll->cname, coll->cnamesz, mkey);
4557 }
4558
4559 /**Returned index meta if not NULL it must be freed by 'bson_del' */
4560 static bson* _imetaidx(EJCOLL *coll, const char *ipath) {
4561     assert(coll && ipath);
4562     if (*ipath == '\0') {
4563         return NULL;
4564     }
4565     bson *rv = NULL;
4566     char fpathkey[BSON_MAX_FPATH_LEN + 1];
4567     TCMAP *cmeta = tctdbget(coll->jb->metadb, coll->cname, coll->cnamesz);
4568     if (!cmeta) {
4569         _ejdbsetecode(coll->jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
4570         goto finish;
4571     }
4572     int klen = snprintf(fpathkey, BSON_MAX_FPATH_LEN + 1, "i%s", ipath); //'i' prefix for all columns with index meta
4573     if (klen > BSON_MAX_FPATH_LEN) {
4574         _ejdbsetecode(coll->jb, JBEFPATHINVALID, __FILE__, __LINE__, __func__);
4575         goto finish;
4576     }
4577     int bsz;
4578     const void *bsdata = tcmapget(cmeta, fpathkey, klen, &bsz);
4579     if (bsdata) {
4580         rv = bson_create();
4581         bson_init_size(rv, bsz);
4582         bson_ensure_space(rv, bsz - 4);
4583         bson_append(rv, ((char*) bsdata) + 4, bsz - (4 + 1));
4584         bson_finish(rv);
4585     }
4586 finish:
4587     if (cmeta) {
4588         tcmapdel(cmeta);
4589     }
4590     return rv;
4591 }
4592
4593 /** Free EJQF field **/
4594 static void _delqfdata(const EJQ *q, const EJQF *qf) {
4595     assert(q && qf);
4596     if (qf->expr) {
4597         TCFREE(qf->expr);
4598     }
4599     if (qf->fpath) {
4600         TCFREE(qf->fpath);
4601     }
4602     if (qf->idxmeta) {
4603         bson_del(qf->idxmeta);
4604     }
4605     if (qf->updateobj) {
4606         bson_del(qf->updateobj);
4607     }
4608     if (qf->ufields) {
4609         tclistdel(qf->ufields);
4610     }
4611     if (qf->uslots) {
4612         tclistdel(qf->uslots);
4613     }
4614     if (qf->regex && !(EJQINTERNAL & q->flags)) {
4615         //We do not clear regex_t data because it not deep copy in internal queries
4616         regfree((regex_t *) qf->regex);
4617         TCFREE(qf->regex);
4618     }
4619     if (qf->exprlist) {
4620         tclistdel(qf->exprlist);
4621     }
4622     if (qf->exprmap) {
4623         tcmapdel(qf->exprmap);
4624     }
4625 }
4626
4627 static bool _ejdbsavebsonimpl(EJCOLL *coll, bson *bs, bson_oid_t *oid, bool merge) {
4628     bool rv = false;
4629     bson *nbs = NULL;
4630     bson_type oidt = _bsonoidkey(bs, oid);
4631     if (oidt == BSON_EOO) { //missing _id so generate a new _id
4632         bson_oid_gen(oid);
4633         nbs = bson_create();
4634         bson_init_size(nbs, bson_size(bs) + (strlen(JDBIDKEYNAME) + 1/*key*/ + 1/*type*/ + sizeof (*oid)));
4635         bson_append_oid(nbs, JDBIDKEYNAME, oid);
4636         bson_ensure_space(nbs, bson_size(bs) - 4);
4637         bson_append(nbs, bson_data(bs) + 4, bson_size(bs) - (4 + 1/*BSON_EOO*/));
4638         bson_finish(nbs);
4639         assert(!nbs->err);
4640         bs = nbs;
4641     } else if (oidt != BSON_OID) { //_oid presented by it is not BSON_OID
4642         _ejdbsetecode(coll->jb, JBEINVALIDBSONPK, __FILE__, __LINE__, __func__);
4643         return false;
4644     }
4645     TCTDB *tdb = coll->tdb;
4646     TCMAP *rowm = (tdb->hdb->rnum > 0) ? tctdbget(tdb, oid, sizeof (*oid)) : NULL;
4647     char *obsdata = NULL; //Old bson
4648     int obsdatasz = 0;
4649     if (rowm) { //Save the copy of old bson data
4650         const void *obs = tcmapget(rowm, JDBCOLBSON, JDBCOLBSONL, &obsdatasz);
4651         if (obs && obsdatasz > 0) {
4652             TCMALLOC(obsdata, obsdatasz);
4653             memmove(obsdata, obs, obsdatasz);
4654         }
4655     } else {
4656         rowm = tcmapnew2(TCMAPTINYBNUM);
4657     }
4658     if (merge && !nbs && obsdata) {
4659         nbs = bson_create();
4660         bson_init_size(nbs, MAX(obsdatasz, bson_size(bs)));
4661         bson_merge2(obsdata, bson_data(bs), true, nbs);
4662         bson_finish(nbs);
4663         assert(!nbs->err);
4664         bs = nbs;
4665     }
4666     tcmapput(rowm, JDBCOLBSON, JDBCOLBSONL, bson_data(bs), bson_size(bs));
4667     if (!tctdbput(tdb, oid, sizeof (*oid), rowm)) {
4668         goto finish;
4669     }
4670     //Update indexes
4671     rv = _updatebsonidx(coll, oid, bs, obsdata, obsdatasz, NULL);
4672 finish:
4673     if (rowm) {
4674         tcmapdel(rowm);
4675     }
4676     if (obsdata) {
4677         TCFREE(obsdata);
4678     }
4679     if (nbs) {
4680         bson_del(nbs);
4681     }
4682     return rv;
4683 }
4684
4685 /**
4686  * Copy BSON array into new TCLIST. TCLIST must be freed by 'tclistdel'.
4687  * @param it BSON iterator
4688  * @param type[out] Detected BSON type of last element
4689  */
4690 static TCLIST* _fetch_bson_str_array(EJDB *jb, bson_iterator *it, bson_type *type, txtflags_t tflags) {
4691     TCLIST *res = tclistnew();
4692     *type = BSON_EOO;
4693     bson_type ftype;
4694     for (int i = 0; (ftype = bson_iterator_next(it)) != BSON_EOO; ++i) {
4695         switch (ftype) {
4696             case BSON_STRING:
4697                 *type = ftype;
4698                 if (tflags & JBICASE) { //ignore case
4699                     char *buf = NULL;
4700                     char sbuf[JBSTRINOPBUFFERSZ];
4701                     int len = tcicaseformat(bson_iterator_string(it), bson_iterator_string_len(it) - 1, sbuf, JBSTRINOPBUFFERSZ, &buf);
4702                     if (len < 0) {
4703                         _ejdbsetecode(jb, len, __FILE__, __LINE__, __func__);
4704                         break;
4705                     }
4706                     tclistpush2(res, buf);
4707                     if (buf && buf != sbuf) {
4708                         TCFREE(buf);
4709                     }
4710                 } else {
4711                     tclistpush2(res, bson_iterator_string(it));
4712                 }
4713                 break;
4714             case BSON_INT:
4715             case BSON_LONG:
4716             case BSON_BOOL:
4717             case BSON_DATE:
4718                 *type = ftype;
4719                 tclistprintf(res, "%" PRId64, bson_iterator_long(it));
4720                 break;
4721             case BSON_DOUBLE:
4722                 *type = ftype;
4723                 tclistprintf(res, "%f", bson_iterator_double(it));
4724                 break;
4725             case BSON_OID:
4726                 *type = ftype;
4727                 char xoid[25];
4728                 bson_oid_to_string(bson_iterator_oid(it), xoid);
4729                 tclistprintf(res, "%s", xoid);
4730                 break;
4731             default:
4732                 break;
4733         }
4734     }
4735     return res;
4736 }
4737
4738 /** result must be freed by TCFREE */
4739 static char* _fetch_bson_str_array2(EJDB *jb, bson_iterator *it, bson_type *type, txtflags_t tflags) {
4740     TCLIST *res = _fetch_bson_str_array(jb, it, type, tflags);
4741     char *tokens = tcstrjoin(res, ',');
4742     tclistdel(res);
4743     return tokens;
4744 }
4745
4746 static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist, TCLIST *pathStack, EJQF *pqf, int elmatchgrp) {
4747     assert(it && qlist && pathStack);
4748     int ret = 0;
4749     bson_type ftype, bt;
4750
4751     while ((ftype = bson_iterator_next(it)) != BSON_EOO) {
4752         const char *fkey = BSON_ITERATOR_KEY(it);
4753         bool isckey = (*fkey == '$'); //Key is a control key: $in, $nin, $not, $all, ...
4754         EJQF qf;
4755         memset(&qf, 0, sizeof (qf));
4756         qf.q = q;
4757         if (pqf) {
4758             qf.elmatchgrp = pqf->elmatchgrp;
4759             qf.elmatchpos = pqf->elmatchpos;
4760             qf.flags = pqf->flags;
4761             if(elmatchgrp > 0)
4762                 qf.negate = pqf->negate;
4763         }
4764
4765         if (!isckey) {
4766             //Push key on top of path stack
4767             tclistpush2(pathStack, fkey);
4768             qf.ftype = ftype;
4769         } else {
4770             if (!strcmp("$or", fkey) || //Both levels operators.
4771                     !strcmp("$and", fkey)) {
4772                 //nop
4773             } else if (!strcmp("$set", fkey) ||
4774                        !strcmp("$inc", fkey) ||
4775                        !strcmp("$dropall", fkey) ||
4776                        !strcmp("$addToSet", fkey) ||
4777                        !strcmp("$addToSetAll", fkey) ||
4778                        !strcmp("$push", fkey) ||
4779                        !strcmp("$pushAll", fkey) ||
4780                        !strcmp("$pull", fkey) ||
4781                        !strcmp("$pullAll", fkey) ||
4782                        !strcmp("$upsert", fkey) ||
4783                        !strcmp("$do", fkey) ||
4784                        !strcmp("$unset", fkey) ||
4785                        !strcmp("$rename", fkey)
4786                       ) {
4787                 if (pqf) { //Top level ops
4788                     ret = JBEQERROR;
4789                     _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4790                     break;
4791                 }
4792             } else {
4793                 if (!pqf) { //Require parent query object
4794                     ret = JBEQERROR;
4795                     _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4796                     break;
4797                 }
4798                 qf = *pqf;
4799                 if (!strcmp("$not", fkey)) {
4800                     qf.negate = !qf.negate;
4801                 } else if (!strcmp("$gt", fkey)) {
4802                     qf.flags |= EJCOMPGT;
4803                 } else if (!strcmp("$gte", fkey)) {
4804                     qf.flags |= EJCOMPGTE;
4805                 } else if (!strcmp("$lt", fkey)) {
4806                     qf.flags |= EJCOMPLT;
4807                 } else if (!strcmp("$lte", fkey)) {
4808                     qf.flags |= EJCOMPLTE;
4809                 } else if (!strcmp("$begin", fkey)) {
4810                     qf.flags |= EJCONDSTARTWITH;
4811                 } else if (!strcmp("$icase", fkey)) {
4812                     qf.flags |= EJCONDICASE;
4813                 }
4814             }
4815         }
4816
4817         switch (ftype) {
4818             case BSON_ARRAY: {
4819                 if (isckey) {
4820                     if (!strcmp("$and", fkey)) {
4821                         bson_iterator sit;
4822                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4823                         while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
4824                             if (bt != BSON_OBJECT) {
4825                                 continue;
4826                             }
4827                             if (_qryaddand(jb, q, bson_iterator_value(&sit)) == NULL) {
4828                                 break;
4829                             }
4830                         }
4831                         break;
4832                     } else if (!strcmp("$or", fkey)) {
4833                         bson_iterator sit;
4834                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4835                         while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
4836                             if (bt != BSON_OBJECT) {
4837                                 continue;
4838                             }
4839                             if (ejdbqueryaddor(jb, q, bson_iterator_value(&sit)) == NULL) {
4840                                 break;
4841                             }
4842                         }
4843                         break;
4844                     } else {
4845                         bson_iterator sit;
4846                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4847                         bson_type atype = 0;
4848                         TCLIST *tokens = _fetch_bson_str_array(jb, &sit, &atype, (qf.flags & EJCONDICASE) ? JBICASE : 0);
4849                         if (atype == 0) {
4850                             ret = JBEQINOPNOTARRAY;
4851                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4852                             tclistdel(tokens);
4853                             break;
4854                         }
4855                         assert(!qf.expr && !qf.fpath);
4856                         qf.ftype = BSON_ARRAY;
4857                         qf.expr = tclistdump(tokens, &qf.exprsz);
4858                         qf.exprlist = tokens;
4859                         if (!strcmp("$in", fkey) || !strcmp("$nin", fkey)) {
4860                             if (!strcmp("$nin", fkey)) {
4861                                 qf.negate = true;
4862                             }
4863                             if (BSON_IS_NUM_TYPE(atype) || atype == BSON_BOOL || atype == BSON_DATE) {
4864                                 qf.tcop = TDBQCNUMOREQ;
4865                             } else {
4866                                 qf.tcop = TDBQCSTROREQ;
4867                                 if (TCLISTNUM(tokens) >= JBINOPTMAPTHRESHOLD) {
4868                                     assert(!qf.exprmap);
4869                                     qf.exprmap = tcmapnew2(TCLISTNUM(tokens));
4870                                     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
4871                                         tcmapputkeep(qf.exprmap, TCLISTVALPTR(tokens, i), TCLISTVALSIZ(tokens, i), &yes, sizeof (yes));
4872                                     }
4873                                 }
4874                             }
4875                         } else if (!strcmp("$bt", fkey)) { //between
4876                             qf.tcop = TDBQCNUMBT;
4877                             if (TCLISTNUM(tokens) != 2) {
4878                                 ret = JBEQINOPNOTARRAY;
4879                                 _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4880                                 TCFREE(qf.expr);
4881                                 tclistdel(qf.exprlist);
4882                                 break;
4883                             }
4884                         } else if (!strcmp("$strand", fkey)) { //$strand
4885                             qf.tcop = TDBQCSTRAND;
4886                         } else if (!strcmp("$stror", fkey)) { //$stror
4887                             qf.tcop = TDBQCSTROR;
4888                         } else if (qf.flags & EJCONDSTARTWITH) { //$begin with some token
4889                             qf.tcop = TDBQCSTRORBW;
4890                         } else {
4891                             ret = JBEQINVALIDQCONTROL;
4892                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4893                             TCFREE(qf.expr);
4894                             tclistdel(qf.exprlist);
4895                             break;
4896                         }
4897                         qf.fpath = tcstrjoin(pathStack, '.');
4898                         qf.fpathsz = strlen(qf.fpath);
4899                         TCLISTPUSH(qlist, &qf, sizeof (qf));
4900                         break;
4901                     }
4902                 } else {
4903                     bson_iterator sit;
4904                     BSON_ITERATOR_SUBITERATOR(it, &sit);
4905                     ret = _parse_qobj_impl(jb, q, &sit, qlist, pathStack, &qf, elmatchgrp);
4906                     break;
4907                 }
4908             }
4909
4910             case BSON_OBJECT: {
4911                 if (isckey) {
4912                     if (!strcmp("$inc", fkey)) {
4913                         qf.flags |= EJCONDINC;
4914                     } else if (!pqf) { //top level op
4915                         if (!strcmp("$do", fkey)) {
4916                             qf.flags |= EJCONDOIT;
4917                         } else if (!strcmp("$set", fkey)) { //top level set OP
4918                             qf.flags |= EJCONDSET;
4919                         } else if (!strcmp("$addToSet", fkey)) {
4920                             qf.flags |= EJCONDADDSET;
4921                         } else if (!strcmp("$addToSetAll", fkey)) {
4922                             qf.flags |= EJCONDADDSET;
4923                             qf.flags |= EJCONDALL;
4924                         } else if (!strcmp("$push", fkey)) {
4925                             qf.flags |= EJCONDPUSH;
4926                         } else if (!strcmp("$pushAll", fkey)) {
4927                             qf.flags |= EJCONDPUSH;
4928                             qf.flags |= EJCONDALL;
4929                         } else if (!strcmp("$pull", fkey)) {
4930                             qf.flags |= EJCONDPULL;
4931                         } else if (!strcmp("$pullAll", fkey)) {
4932                             qf.flags |= EJCONDPULL;
4933                             qf.flags |= EJCONDALL;
4934                         } else if (!strcmp("$upsert", fkey)) {
4935                             qf.flags |= EJCONDSET;
4936                             qf.flags |= EJCONDUPSERT;
4937                         } else if (!strcmp("$unset", fkey)) {
4938                             qf.flags |= EJCONDUNSET;
4939                         } else if (!strcmp("$rename", fkey)) {
4940                             qf.flags |= EJCONDRENAME;
4941                         }
4942                     }
4943
4944                     if ((qf.flags & 
4945                           (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDPUSH | 
4946                            EJCONDUNSET | EJCONDRENAME))) {
4947                                
4948                         assert(qf.updateobj == NULL);
4949                         qf.q->flags |= EJQUPDATING;
4950                         qf.updateobj = bson_create();
4951                         bson_init_as_query(qf.updateobj);
4952                         bson_type sbt;
4953                         bson_iterator sit;
4954                         BSON_ITERATOR_SUBITERATOR(it, &sit);
4955                         while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
4956                             if ((qf.flags & EJCONDALL) && sbt != BSON_ARRAY) {
4957                                 //addToSet, push, pull accept only an arrays
4958                                 continue;
4959                             }
4960                             if (!(qf.flags & EJCONDALL) && 
4961                                  (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET | EJCONDRENAME))) { //Checking the $(query) positional operator.
4962                                 const char* ukey = BSON_ITERATOR_KEY(&sit);
4963                                 char *pptr;
4964                                 if ((pptr = strstr(ukey, ".$")) && pptr && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.'
4965                                     int dc = 0;
4966                                     for (int i = 0; *(ukey + i) != '\0'; ++i) {
4967                                         if (*(ukey + i) == '$' && (dc++ > 0)) break;
4968                                     }
4969                                     if (dc != 1) { //More than one '$' chars in projection, it is invalid
4970                                         continue;
4971                                     }
4972                                     // Now just store only [$(query) key] into the qf->ufields
4973                                     if (!qf.ufields) {
4974                                         qf.ufields = tclistnew2(TCLISTINYNUM);
4975                                     }
4976                                     q->flags |= EJQHASUQUERY;
4977                                     tclistpush(qf.ufields, ukey, strlen(ukey));
4978                                 }
4979                             }
4980                             bson_append_field_from_iterator(&sit, qf.updateobj);
4981                         }
4982                         bson_finish(qf.updateobj);
4983                         if (qf.updateobj->err) {
4984                             ret = JBEQERROR;
4985                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
4986                             break;
4987                         }
4988                         qf.fpath = strdup(fkey);
4989                         qf.fpathsz = strlen(qf.fpath);
4990                         qf.tcop = TDBQTRUE;
4991                         qf.flags |= EJFEXCLUDED;
4992                         TCLISTPUSH(qlist, &qf, sizeof (qf));
4993                         break;
4994                     }
4995
4996                     if (!strcmp("$elemMatch", fkey)) {
4997                         if (qf.elmatchgrp) { //only one $elemMatch allowed in query field
4998                             ret = JBEQERROR;
4999                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5000                             break;
5001                         }
5002                         qf.elmatchgrp = ++elmatchgrp;
5003                         char *fpath = tcstrjoin(pathStack, '.');
5004                         qf.elmatchpos = strlen(fpath) + 1; //+ 1 to skip next dot '.'
5005                         free(fpath);
5006                     }
5007                 } else {
5008                     if (qf.flags & EJCONDOIT) {
5009                         qf.updateobj = bson_create();
5010                         bson_init_as_query(qf.updateobj);
5011                         bson_type sbt;
5012                         bson_iterator sit;
5013                         BSON_ITERATOR_SUBITERATOR(it, &sit);
5014                         int ac = 0;
5015                         while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
5016                             const char *akey = BSON_ITERATOR_KEY(&sit);
5017                             if (!strcmp("$join", akey) || !strcmp("$slice", akey)) {
5018                                 bson_append_field_from_iterator(&sit, qf.updateobj);
5019                                 ++ac;
5020                             }
5021                         }
5022                         bson_finish(qf.updateobj);
5023                         if (qf.updateobj->err) {
5024                             ret = JBEQERROR;
5025                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5026                             break;
5027                         }
5028                         if (ac == 0) {
5029                             ret = JBEQACTKEY;
5030                             _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5031                             break;
5032                         }
5033                         qf.fpath = strdup(fkey);
5034                         qf.fpathsz = strlen(qf.fpath);
5035                         qf.tcop = TDBQTRUE;
5036                         qf.flags |= EJFEXCLUDED;
5037                         TCLISTPUSH(qlist, &qf, sizeof (qf));
5038                         break;
5039                     }
5040                 }
5041                 bson_iterator sit;
5042                 BSON_ITERATOR_SUBITERATOR(it, &sit);
5043                 ret = _parse_qobj_impl(jb, q, &sit, qlist, pathStack, &qf, elmatchgrp);
5044                 break;
5045             }
5046             case BSON_OID: {
5047
5048                 assert(!qf.fpath && !qf.expr);
5049                 qf.ftype = ftype;
5050                 TCMALLOC(qf.expr, 25 * sizeof (char));
5051                 bson_oid_to_string(bson_iterator_oid(it), qf.expr);
5052                 qf.exprsz = 24;
5053                 qf.fpath = tcstrjoin(pathStack, '.');
5054                 qf.fpathsz = strlen(qf.fpath);
5055                 qf.tcop = TDBQCSTREQ;
5056                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5057                 break;
5058             }
5059             case BSON_STRING: {
5060                 assert(!qf.fpath && !qf.expr);
5061                 qf.ftype = ftype;
5062                 if (qf.flags & EJCONDICASE) {
5063                     qf.exprsz = tcicaseformat(bson_iterator_string(it), bson_iterator_string_len(it) - 1, NULL, 0, &qf.expr);
5064                     if (qf.exprsz < 0) {
5065                         ret = qf.exprsz;
5066                         _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5067                         qf.exprsz = 0;
5068                         break;
5069                     }
5070                 } else {
5071                     qf.expr = tcstrdup(bson_iterator_string(it));
5072                     qf.exprsz = strlen(qf.expr);
5073                 }
5074
5075                 qf.fpath = tcstrjoin(pathStack, '.');
5076                 qf.fpathsz = strlen(qf.fpath);
5077                 if (qf.flags & EJCONDSTARTWITH) {
5078                     qf.tcop = TDBQCSTRBW;
5079                 } else {
5080                     qf.tcop = TDBQCSTREQ;
5081                     if (!strcmp(JDBIDKEYNAME, fkey)) { //_id
5082                         qf.ftype = BSON_OID;
5083                     }
5084                 }
5085                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5086                 break;
5087             }
5088             case BSON_LONG:
5089             case BSON_DOUBLE:
5090             case BSON_INT:
5091             case BSON_DATE: {
5092                 assert(!qf.fpath && !qf.expr);
5093                 qf.ftype = ftype;
5094                 qf.fpath = tcstrjoin(pathStack, '.');
5095                 qf.fpathsz = strlen(qf.fpath);
5096                 if (ftype == BSON_LONG || ftype == BSON_INT || ftype == BSON_DATE) {
5097                     qf.exprlongval = bson_iterator_long(it);
5098                     qf.exprdblval = qf.exprlongval;
5099                     // 2015-04-14: Change to use standard format string for int64_t
5100                     qf.expr = tcsprintf("%" PRId64, qf.exprlongval);
5101                 } else {
5102                     qf.exprdblval = bson_iterator_double(it);
5103                     qf.exprlongval = (int64_t) qf.exprdblval;
5104                     qf.expr = tcsprintf("%f", qf.exprdblval);
5105                 }
5106                 qf.exprsz = strlen(qf.expr);
5107                 if (qf.flags & EJCOMPGT) {
5108                     qf.tcop = TDBQCNUMGT;
5109                 } else if (qf.flags & EJCOMPGTE) {
5110                     qf.tcop = TDBQCNUMGE;
5111                 } else if (qf.flags & EJCOMPLT) {
5112                     qf.tcop = TDBQCNUMLT;
5113                 } else if (qf.flags & EJCOMPLTE) {
5114                     qf.tcop = TDBQCNUMLE;
5115                 } else {
5116                     qf.tcop = TDBQCNUMEQ;
5117                 }
5118                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5119                 break;
5120             }
5121             case BSON_REGEX: {
5122                 assert(!qf.fpath && !qf.expr);
5123                 qf.ftype = ftype;
5124                 qf.tcop = TDBQCSTRRX;
5125                 char *re = tcstrdup(bson_iterator_regex(it));
5126                 const char *opts = bson_iterator_regex_opts(it);
5127                 qf.fpath = tcstrjoin(pathStack, '.');
5128                 qf.fpathsz = strlen(qf.fpath);
5129                 qf.expr = re;
5130                 qf.exprsz = strlen(qf.expr);
5131                 const char *rxstr = qf.expr;
5132                 regex_t rxbuf;
5133                 int rxopt = REG_EXTENDED | REG_NOSUB;
5134                 if (strchr(opts, 'i')) {
5135                     rxopt |= REG_ICASE;
5136                 }
5137                 if (regcomp(&rxbuf, rxstr, rxopt) == 0) {
5138                     TCMALLOC(qf.regex, sizeof (rxbuf));
5139                     memcpy(qf.regex, &rxbuf, sizeof (rxbuf));
5140                 } else {
5141                     ret = JBEQINVALIDQRX;
5142                     _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__);
5143                     TCFREE(qf.fpath);
5144                     TCFREE(qf.expr);
5145                     break;
5146                 }
5147                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5148                 break;
5149             }
5150             case BSON_NULL:
5151             case BSON_UNDEFINED:
5152                 qf.ftype = ftype;
5153                 qf.tcop = TDBQCEXIST;
5154                 qf.negate = !qf.negate;
5155                 qf.expr = tcstrdup(""); //Empty string as expr
5156                 qf.exprsz = 0;
5157                 qf.fpath = tcstrjoin(pathStack, '.');
5158                 qf.fpathsz = strlen(qf.fpath);
5159                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5160                 break;
5161
5162             case BSON_BOOL: { //boolean converted into number
5163                 bool bv = bson_iterator_bool_raw(it);
5164                 if (isckey) {
5165                     if (!strcmp("$dropall", fkey) && bv) {
5166                         qf.flags |= EJFEXCLUDED;
5167                         qf.fpath = tcstrjoin(pathStack, '.');
5168                         qf.fpathsz = strlen(qf.fpath);
5169                         qf.tcop = TDBQTRUE;
5170                         qf.q->flags |= EJQUPDATING;
5171                         qf.q->flags |= EJQDROPALL;
5172                         qf.expr = tcstrdup(""); //Empty string as expr
5173                         qf.exprsz = 0;
5174                         TCLISTPUSH(qlist, &qf, sizeof (qf));
5175                         break;
5176                     }
5177                     if (!strcmp("$exists", fkey)) {
5178                         qf.tcop = TDBQCEXIST;
5179                         qf.fpath = tcstrjoin(pathStack, '.');
5180                         qf.fpathsz = strlen(qf.fpath);
5181                         qf.expr = tcstrdup(""); //Empty string as expr
5182                         qf.exprsz = 0;
5183                         if (!bv) {
5184                             qf.negate = !qf.negate;
5185                         }
5186                         TCLISTPUSH(qlist, &qf, sizeof (qf));
5187                         break;
5188                     }
5189                 }
5190                 qf.tcop = TDBQCNUMEQ;
5191                 qf.fpath = tcstrjoin(pathStack, '.');
5192                 qf.fpathsz = strlen(qf.fpath);
5193                 qf.exprlongval = (bv ? 1 : 0);
5194                 qf.exprdblval = qf.exprlongval;
5195                 qf.expr = strdup(bv ? "1" : "0");
5196                 qf.exprsz = 1;
5197                 TCLISTPUSH(qlist, &qf, sizeof (qf));
5198                 break;
5199             }
5200             default:
5201                 break;
5202         };
5203
5204         if (!isckey) {
5205             assert(pathStack->num > 0);
5206             TCFREE(tclistpop2(pathStack));
5207         }
5208         if (ret) { //cleanup on error condition
5209             break;
5210         }
5211     }
5212     return ret;
5213 }
5214
5215 /**
5216  * Convert bson query spec into field path -> EJQF instance.
5217  *  Created map instance must be freed by `tcmapdel`.
5218  *  Each element of map must be freed by `ejdbquerydel`.
5219  */
5220 static TCLIST* _parseqobj(EJDB *jb, EJQ *q, bson *qspec) {
5221     assert(qspec);
5222     return _parseqobj2(jb, q, bson_data(qspec));
5223 }
5224
5225 static TCLIST* _parseqobj2(EJDB *jb, EJQ *q, const void *qspecbsdata) {
5226     assert(qspecbsdata);
5227     int rv = 0;
5228     TCLIST *res = tclistnew2(TCLISTINYNUM);
5229     TCLIST *pathStack = tclistnew2(TCLISTINYNUM);
5230     bson_iterator it;
5231     BSON_ITERATOR_FROM_BUFFER(&it, qspecbsdata);
5232     rv = _parse_qobj_impl(jb, q, &it, res, pathStack, NULL, 0);
5233     if (rv) {
5234         tclistdel(res);
5235         res = NULL;
5236     }
5237     assert(!pathStack->num);
5238     tclistdel(pathStack);
5239     return res;
5240 }
5241
5242 /**
5243  * Get OID value from the '_id' field of specified bson object.
5244  * @param bson[in] BSON object
5245  * @param oid[out] Pointer to OID type
5246  * @return True if OID value is found int _id field of bson object otherwise False.
5247  */
5248 static bson_type _bsonoidkey(bson *bs, bson_oid_t *oid) {
5249     bson_iterator it;
5250     bson_type bt = bson_find(&it, bs, JDBIDKEYNAME);
5251     if (bt == BSON_OID) {
5252         *oid = *bson_iterator_oid(&it);
5253     }
5254     return bt;
5255 }
5256
5257 /**
5258  * Return string value representation of value pointed by 'it'.
5259  * Resulting value size stored into 'vsz'.
5260  * If returned value is not NULL it must be freed by TCFREE.
5261  */
5262 static char* _bsonitstrval(EJDB *jb, bson_iterator *it, int *vsz, TCLIST *tokens, txtflags_t tflags) {
5263     int retlen = 0;
5264     char *ret = NULL;
5265     bson_type btype = BSON_ITERATOR_TYPE(it);
5266     if (btype == BSON_STRING) {
5267         if (tokens) { //split string into tokens and push it into 'tokens' list
5268             const unsigned char *sp = (unsigned char *) bson_iterator_string(it);
5269             while (*sp != '\0') {
5270                 while ((*sp != '\0' && *sp <= ' ') || *sp == ',') {
5271                     sp++;
5272                 }
5273                 const unsigned char *ep = sp;
5274                 while (*ep > ' ' && *ep != ',') {
5275                     ep++;
5276                 }
5277                 if (ep > sp) {
5278                     if (tflags & JBICASE) { //ignore case mode
5279                         char *buf = NULL;
5280                         char sbuf[JBSTRINOPBUFFERSZ];
5281                         int len = tcicaseformat((const char*) sp, ep - sp, sbuf, JBSTRINOPBUFFERSZ, &buf);
5282                         if (len >= 0) { //success
5283                             TCLISTPUSH(tokens, buf, len);
5284                         } else {
5285                             _ejdbsetecode(jb, len, __FILE__, __LINE__, __func__);
5286                         }
5287                         if (buf && buf != sbuf) {
5288                             TCFREE(buf);
5289                         }
5290                     } else {
5291                         TCLISTPUSH(tokens, sp, ep - sp);
5292                     }
5293                 }
5294                 sp = ep;
5295             }
5296         } else {
5297             retlen = bson_iterator_string_len(it) - 1;
5298             if (tflags & JBICASE) {
5299                 retlen = tcicaseformat(bson_iterator_string(it), retlen, NULL, 0, &ret);
5300             } else {
5301                 ret = tcmemdup(bson_iterator_string(it), retlen);
5302             }
5303         }
5304     } else if (BSON_IS_NUM_TYPE(btype) || btype == BSON_BOOL || btype == BSON_DATE) {
5305         char nbuff[TCNUMBUFSIZ];
5306         if (btype == BSON_INT || btype == BSON_LONG || btype == BSON_BOOL || btype == BSON_DATE) {
5307             retlen = bson_numstrn(nbuff, TCNUMBUFSIZ, bson_iterator_long(it));
5308             if (retlen >= TCNUMBUFSIZ) {
5309                 retlen = TCNUMBUFSIZ - 1;
5310             }
5311         } else if (btype == BSON_DOUBLE) {
5312             retlen = tcftoa(bson_iterator_double(it), nbuff, TCNUMBUFSIZ, 6);
5313             if (retlen >= TCNUMBUFSIZ) {
5314                 retlen = TCNUMBUFSIZ - 1;
5315             }
5316         }
5317         if (tflags & JBICASE) {
5318             retlen = tcicaseformat(nbuff, retlen, NULL, 0, &ret);
5319         } else {
5320             ret = tcmemdup(nbuff, retlen);
5321         }
5322     } else if (btype == BSON_ARRAY) {
5323         bson_type eltype; //last element bson type
5324         bson_iterator sit;
5325         BSON_ITERATOR_SUBITERATOR(it, &sit);
5326         if (tokens) {
5327             while ((eltype = bson_iterator_next(&sit)) != BSON_EOO) {
5328                 int vz = 0;
5329                 char *v = _bsonitstrval(jb, &sit, &vz, NULL, tflags);
5330                 if (v) {
5331                     TCLISTPUSH(tokens, v, vz);
5332                     TCFREE(v);
5333                 }
5334             }
5335         } else {
5336             //Array elements are joined with ',' delimeter.
5337             ret = _fetch_bson_str_array2(jb, &sit, &eltype, tflags);
5338             retlen = strlen(ret);
5339         }
5340     }
5341     if (retlen < 0) {
5342         _ejdbsetecode(jb, retlen, __FILE__, __LINE__, __func__);
5343         ret = NULL;
5344         retlen = 0;
5345     }
5346     *vsz = retlen;
5347     return ret;
5348 }
5349
5350 static char* _bsonipathrowldr(
5351     TCLIST *tokens,
5352     const char *pkbuf, int pksz,
5353     const char *rowdata, int rowdatasz,
5354     const char *ipath, int ipathsz, void *op, int *vsz) {
5355     assert(op);
5356     char *res = NULL;
5357     if (ipath && *ipath == '\0') { //PK TODO review
5358         if (tokens) {
5359             const unsigned char *sp = (unsigned char *) pkbuf;
5360             while (*sp != '\0') {
5361                 while ((*sp != '\0' && *sp <= ' ') || *sp == ',') {
5362                     sp++;
5363                 }
5364                 const unsigned char *ep = sp;
5365                 while (*ep > ' ' && *ep != ',') {
5366                     ep++;
5367                 }
5368                 if (ep > sp) TCLISTPUSH(tokens, sp, ep - sp);
5369                 sp = ep;
5370             }
5371             *vsz = 0;
5372             return NULL;
5373         } else {
5374             TCMEMDUP(res, pkbuf, pksz);
5375             *vsz = pksz;
5376             return res;
5377         }
5378     }
5379     if (!ipath || ipathsz < 2 || *(ipath + 1) == '\0' || strchr("snai", *ipath) == NULL) {
5380         return NULL;
5381     }
5382     //skip index type prefix char with (fpath + 1)
5383     res = _bsonfpathrowldr(tokens, rowdata, rowdatasz, ipath + 1, ipathsz - 1, op, vsz);
5384     if (*vsz == 0) { //Do not allow empty strings for index opration
5385         if (res) TCFREE(res);
5386         res = NULL;
5387     }
5388     return res;
5389 }
5390
5391 static char* _bsonfpathrowldr(TCLIST *tokens, const char *rowdata, int rowdatasz,
5392                               const char *fpath, int fpathsz, void *op, int *vsz) {
5393     _BSONIPATHROWLDR *odata = (_BSONIPATHROWLDR*) op;
5394     assert(odata && odata->coll);
5395     char *ret = NULL;
5396     int bsize;
5397     bson_iterator it;
5398     char *bsdata = tcmaploadone(rowdata, rowdatasz, JDBCOLBSON, JDBCOLBSONL, &bsize);
5399     if (!bsdata) {
5400         *vsz = 0;
5401         return NULL;
5402     }
5403     BSON_ITERATOR_FROM_BUFFER(&it, bsdata);
5404     bson_find_fieldpath_value2(fpath, fpathsz, &it);
5405     ret = _bsonitstrval(odata->coll->jb, &it, vsz, tokens, (odata->icase ? JBICASE : 0));
5406     TCFREE(bsdata);
5407     return ret;
5408 }
5409
5410 static bool _updatebsonidx(EJCOLL *coll, const bson_oid_t *oid, const bson *bs,
5411                            const void *obsdata, int obsdatasz, TCLIST *dlist) {
5412     bool rv = true;
5413     TCMAP *cmeta = tctdbget(coll->jb->metadb, coll->cname, coll->cnamesz);
5414     if (!cmeta) {
5415         _ejdbsetecode(coll->jb, JBEMETANVALID, __FILE__, __LINE__, __func__);
5416         return false;
5417     }
5418     TCMAP *imap = NULL; //New index map
5419     TCMAP *rimap = NULL; //Remove index map
5420     bson_type mt = BSON_EOO;
5421     bson_type ft = BSON_EOO;
5422     bson_type oft = BSON_EOO;
5423     bson_iterator fit, oit, mit;
5424     int bsz;
5425     char ikey[BSON_MAX_FPATH_LEN + 2];
5426     const char *mkey;
5427     int mkeysz;
5428
5429     tcmapiterinit(cmeta);
5430     while ((mkey = tcmapiternext(cmeta, &mkeysz)) != NULL && mkeysz > 0) {
5431         if (*mkey != 'i' || mkeysz > BSON_MAX_FPATH_LEN + 1) {
5432             continue;
5433         }
5434         const void *mraw = tcmapget(cmeta, mkey, mkeysz, &bsz);
5435         if (!mraw || !bsz || (mt = bson_find_from_buffer(&mit, mraw, "iflags")) != BSON_INT) {
5436             continue;
5437         }
5438         int iflags = bson_iterator_int(&mit);
5439         //OK then process index keys
5440         memcpy(ikey + 1, mkey + 1, mkeysz - 1);
5441         ikey[mkeysz] = '\0';
5442
5443         int fvaluesz = 0;
5444         char *fvalue = NULL;
5445         int ofvaluesz = 0;
5446         char *ofvalue = NULL;
5447         txtflags_t textflags = (iflags & JBIDXISTR) ? JBICASE : 0;
5448
5449         if (obsdata && obsdatasz > 0) {
5450             BSON_ITERATOR_FROM_BUFFER(&oit, obsdata);
5451             oft = bson_find_fieldpath_value2(mkey + 1, mkeysz - 1, &oit);
5452             TCLIST *tokens = (oft == BSON_ARRAY || (oft == BSON_STRING && (iflags & JBIDXARR))) ? tclistnew() : NULL;
5453             ofvalue = BSON_IS_IDXSUPPORTED_TYPE(oft) ? _bsonitstrval(coll->jb, &oit, &ofvaluesz, tokens, textflags) : NULL;
5454             if (tokens) {
5455                 oft = BSON_ARRAY;
5456                 ofvalue = tclistdump(tokens, &ofvaluesz);
5457                 tclistdel(tokens);
5458             }
5459         }
5460         if (bs) {
5461             BSON_ITERATOR_INIT(&fit, bs);
5462             ft = bson_find_fieldpath_value2(mkey + 1, mkeysz - 1, &fit);
5463             TCLIST *tokens = (ft == BSON_ARRAY || (ft == BSON_STRING && (iflags & JBIDXARR))) ? tclistnew() : NULL;
5464             fvalue = BSON_IS_IDXSUPPORTED_TYPE(ft) ? _bsonitstrval(coll->jb, &fit, &fvaluesz, tokens, textflags) : NULL;
5465             if (tokens) {
5466                 ft = BSON_ARRAY;
5467                 fvalue = tclistdump(tokens, &fvaluesz);
5468                 tclistdel(tokens);
5469             }
5470         }
5471         if (!fvalue && !ofvalue) {
5472             continue;
5473         }
5474         if (imap == NULL) {
5475             imap = tcmapnew2(TCMAPTINYBNUM);
5476             rimap = tcmapnew2(TCMAPTINYBNUM);
5477         }
5478         for (int i = 4; i <= 7; ++i) { /* JBIDXNUM, JBIDXSTR, JBIDXARR, JBIDXISTR */
5479             bool rm = false;
5480             int itype = (1 << i);
5481             if (itype == JBIDXNUM && (JBIDXNUM & iflags)) {
5482                 ikey[0] = 'n';
5483             } else if (itype == JBIDXSTR && (JBIDXSTR & iflags)) {
5484                 ikey[0] = 's';
5485             } else if (itype == JBIDXISTR && (JBIDXISTR & iflags)) {
5486                 ikey[0] = 'i';
5487             } else if (itype == JBIDXARR && (JBIDXARR & iflags)) {
5488                 ikey[0] = 'a';
5489                 if (ofvalue && oft == BSON_ARRAY &&
5490                         (!fvalue || ft != oft || fvaluesz != ofvaluesz || memcmp(fvalue, ofvalue, fvaluesz))) {
5491                     tcmapput(rimap, ikey, mkeysz, ofvalue, ofvaluesz);
5492                     rm = true;
5493                 }
5494                 if (fvalue && fvaluesz > 0 && ft == BSON_ARRAY && (!ofvalue || rm)) {
5495                     tcmapput(imap, ikey, mkeysz, fvalue, fvaluesz);
5496                 }
5497                 continue;
5498             } else {
5499                 continue;
5500             }
5501             if (ofvalue && oft != BSON_ARRAY &&
5502                     (!fvalue || ft != oft || fvaluesz != ofvaluesz || memcmp(fvalue, ofvalue, fvaluesz))) {
5503                 tcmapput(rimap, ikey, mkeysz, ofvalue, ofvaluesz);
5504                 rm = true;
5505             }
5506             if (fvalue && fvaluesz > 0 && ft != BSON_ARRAY && (!ofvalue || rm)) {
5507                 tcmapput(imap, ikey, mkeysz, fvalue, fvaluesz);
5508             }
5509         }
5510         if (fvalue) TCFREE(fvalue);
5511         if (ofvalue) TCFREE(ofvalue);
5512     }
5513     tcmapdel(cmeta);
5514
5515     if (dlist) { //storage for deffered index ops provided, save changes into
5516         _DEFFEREDIDXCTX dctx;
5517         dctx.oid = *oid;
5518         dctx.rmap = (rimap && TCMAPRNUM(rimap) > 0) ? tcmapdup(rimap) : NULL;
5519         dctx.imap = (imap && TCMAPRNUM(imap) > 0) ? tcmapdup(imap) : NULL;
5520         if (dctx.imap || dctx.rmap) {
5521             TCLISTPUSH(dlist, &dctx, sizeof (dctx));
5522         }
5523         //flush deffered indexes if number pending objects greater JBMAXDEFFEREDIDXNUM
5524         if (TCLISTNUM(dlist) >= JBMAXDEFFEREDIDXNUM) {
5525             for (int i = 0; i < TCLISTNUM(dlist); ++i) {
5526                 _DEFFEREDIDXCTX *di = TCLISTVALPTR(dlist, i);
5527                 assert(di);
5528                 if (di->rmap) {
5529                     tctdbidxout2(coll->tdb, &(di->oid), sizeof (di->oid), di->rmap);
5530                     tcmapdel(di->rmap);
5531                 }
5532                 if (di->imap) {
5533                     tctdbidxput2(coll->tdb, &(di->oid), sizeof (di->oid), di->imap);
5534                     tcmapdel(di->imap);
5535                 }
5536             }
5537             TCLISTTRUNC(dlist, 0);
5538         }
5539     } else { //apply index changes immediately
5540         if (rimap && !tctdbidxout2(coll->tdb, oid, sizeof (*oid), rimap)) rv = false;
5541         if (imap && !tctdbidxput2(coll->tdb, oid, sizeof (*oid), imap)) rv = false;
5542     }
5543     if (imap) tcmapdel(imap);
5544     if (rimap) tcmapdel(rimap);
5545     return rv;
5546 }
5547
5548 static void _delcoldb(EJCOLL *coll) {
5549     assert(coll);
5550     tctdbdel(coll->tdb);
5551     coll->tdb = NULL;
5552     coll->jb = NULL;
5553     coll->cnamesz = 0;
5554     TCFREE(coll->cname);
5555     if (coll->mmtx) {
5556         pthread_rwlock_destroy(coll->mmtx);
5557         TCFREE(coll->mmtx);
5558     }
5559 }
5560
5561 static bool _addcoldb0(const char *cname, EJDB *jb, EJCOLLOPTS *opts, EJCOLL **res) {
5562     int i;
5563     bool rv = true;
5564     TCTDB *cdb;
5565
5566     for (i = 0; i < EJDB_MAX_COLLECTIONS && jb->cdbs[i]; ++i);
5567     if (i == EJDB_MAX_COLLECTIONS) {
5568         _ejdbsetecode(jb, JBEMAXNUMCOLS, __FILE__, __LINE__, __func__);
5569         return false;
5570     }
5571     rv = _createcoldb(cname, jb, opts, &cdb);
5572     if (!rv) {
5573         *res = NULL;
5574         return rv;
5575     }
5576     EJCOLL *coll;
5577     TCCALLOC(coll, 1, sizeof (*coll));
5578     jb->cdbs[i] = coll;
5579     ++jb->cdbsnum;
5580     coll->cname = tcstrdup(cname);
5581     coll->cnamesz = strlen(cname);
5582     coll->tdb = cdb;
5583     coll->jb = jb;
5584     coll->mmtx = NULL;
5585     _ejdbcolsetmutex(coll);
5586     *res = coll;
5587     return rv;
5588 }
5589
5590 static bool _createcoldb(const char *colname, EJDB *jb, EJCOLLOPTS *opts, TCTDB **res) {
5591     assert(jb && jb->metadb);
5592     if (!JBISVALCOLNAME(colname)) {
5593         _ejdbsetecode(jb, JBEINVALIDCOLNAME, __FILE__, __LINE__, __func__);
5594         *res = NULL;
5595         return false;
5596     }
5597     bool rv = true;
5598     TCTDB *cdb = tctdbnew();
5599     tctdbsetmutex(cdb);
5600     if (opts) {
5601         if (opts->cachedrecords > 0) {
5602             tctdbsetcache(cdb, opts->cachedrecords, 0, 0);
5603         }
5604         int bnum = 0;
5605         uint8_t tflags = 0;
5606         if (opts->records > 0) {
5607             bnum = tclmax(opts->records * 2 + 1, TDBDEFBNUM);
5608         }
5609         if (opts->large) {
5610             tflags |= TDBTLARGE;
5611         }
5612         if (opts->compressed) {
5613             tflags |= TDBTDEFLATE;
5614         }
5615         tctdbtune(cdb, bnum, 0, 0, tflags);
5616     }
5617     const char *mdbpath = jb->metadb->hdb->path;
5618     assert(mdbpath);
5619     TCXSTR *cxpath = tcxstrnew2(mdbpath);
5620     tcxstrcat2(cxpath, "_");
5621     tcxstrcat2(cxpath, colname);
5622     uint32_t mode = jb->metadb->hdb->omode;
5623     if (mode & (JBOWRITER | JBOCREAT)) {
5624         mode |= JBOCREAT;
5625     }
5626     rv = tctdbopen(cdb, tcxstrptr(cxpath), mode);
5627     *res = rv ? cdb : NULL;
5628     tcxstrdel(cxpath);
5629     return rv;
5630 }
5631
5632 /* Check whether a string includes all tokens in another string.*/
5633 static bool _qrycondcheckstrand(const char *vbuf, const TCLIST *tokens) {
5634     assert(vbuf && tokens);
5635     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
5636         const char *token = TCLISTVALPTR(tokens, i);
5637         int tokensz = TCLISTVALSIZ(tokens, i);
5638         bool found = false;
5639         const char *str = vbuf;
5640         while (true) {
5641             const char *sp = str;
5642             while (*str != '\0' && !strchr(", ", *str)) {
5643                 str++;
5644             }
5645             if (tokensz == (str - sp) && !strncmp(token, sp, tokensz)) { //Token matched
5646                 found = true;
5647                 break;
5648             }
5649             if (*str == '\0') break;
5650             str++;
5651         }
5652         if (!found) {
5653             return false;
5654         }
5655     }
5656     return true;
5657 }
5658
5659 /* Check whether a string includes at least one tokens in another string.*/
5660 static bool _qrycondcheckstror(const char *vbuf, const TCLIST *tokens) {
5661     for (int i = 0; i < TCLISTNUM(tokens); ++i) {
5662         const char *token = TCLISTVALPTR(tokens, i);
5663         int tokensz = TCLISTVALSIZ(tokens, i);
5664         bool found = false;
5665         const char *str = vbuf;
5666         while (true) {
5667             const char *sp = str;
5668             while (*str != '\0' && !strchr(", ", *str)) {
5669                 str++;
5670             }
5671             if (tokensz == (str - sp) && !strncmp(token, sp, tokensz)) { //Token matched
5672                 found = true;
5673                 break;
5674             }
5675             if (*str == '\0') break;
5676             str++;
5677         }
5678         if (found) {
5679             return true;
5680         }
5681     }
5682     return false;
5683 }