• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "ejdb2_internal.h"
2 
3 #define JB_SOLID_EXPRNUM 127
4 
_jbi_print_index(struct _JBIDX * idx,IWXSTR * xstr)5 static void _jbi_print_index(struct _JBIDX *idx, IWXSTR *xstr) {
6   int cnt = 0;
7   ejdb_idx_mode_t m = idx->mode;
8   if (m & EJDB_IDX_UNIQUE) {
9     cnt++;
10     iwxstr_cat2(xstr, "UNIQUE");
11   }
12   if (m & EJDB_IDX_STR) {
13     if (cnt++) {
14       iwxstr_cat2(xstr, "|");
15     }
16     iwxstr_cat2(xstr, "STR");
17   }
18   if (m & EJDB_IDX_I64) {
19     if (cnt++) {
20       iwxstr_cat2(xstr, "|");
21     }
22     iwxstr_cat2(xstr, "I64");
23   }
24   if (m & EJDB_IDX_F64) {
25     if (cnt++) {
26       iwxstr_cat2(xstr, "|");
27     }
28     iwxstr_cat2(xstr, "F64");
29   }
30   if (cnt++) {
31     iwxstr_cat2(xstr, "|");
32   }
33   iwxstr_printf(xstr, "%" PRId64 " ", idx->rnum);
34   jbl_ptr_serialize(idx->ptr, xstr);
35 }
36 
_jbi_log_cursor_op(IWXSTR * xstr,IWKV_cursor_op op)37 static void _jbi_log_cursor_op(IWXSTR *xstr, IWKV_cursor_op op) {
38   switch (op) {
39     case IWKV_CURSOR_EQ:
40       iwxstr_cat2(xstr, "IWKV_CURSOR_EQ");
41       break;
42     case IWKV_CURSOR_GE:
43       iwxstr_cat2(xstr, "IWKV_CURSOR_GE");
44       break;
45     case IWKV_CURSOR_NEXT:
46       iwxstr_cat2(xstr, "IWKV_CURSOR_NEXT");
47       break;
48     case IWKV_CURSOR_PREV:
49       iwxstr_cat2(xstr, "IWKV_CURSOR_PREV");
50       break;
51     case IWKV_CURSOR_BEFORE_FIRST:
52       iwxstr_cat2(xstr, "IWKV_CURSOR_BEFORE_FIRST");
53       break;
54     case IWKV_CURSOR_AFTER_LAST:
55       iwxstr_cat2(xstr, "IWKV_CURSOR_AFTER_LAST");
56       break;
57   }
58 }
59 
_jbi_log_index_rules(IWXSTR * xstr,struct _JBMIDX * mctx)60 static void _jbi_log_index_rules(IWXSTR *xstr, struct _JBMIDX *mctx) {
61   _jbi_print_index(mctx->idx, xstr);
62   if (mctx->expr1) {
63     iwxstr_cat2(xstr, " EXPR1: \'");
64     jqp_print_filter_node_expr(mctx->expr1, jbl_xstr_json_printer, xstr);
65     iwxstr_cat2(xstr, "\'");
66   }
67   if (mctx->expr2) {
68     iwxstr_cat2(xstr, " EXPR2: \'");
69     jqp_print_filter_node_expr(mctx->expr2, jbl_xstr_json_printer, xstr);
70     iwxstr_cat2(xstr, "\'");
71   }
72   if (mctx->cursor_init) {
73     iwxstr_cat2(xstr, " INIT: ");
74     _jbi_log_cursor_op(xstr, mctx->cursor_init);
75   }
76   if (mctx->cursor_step) {
77     iwxstr_cat2(xstr, " STEP: ");
78     _jbi_log_cursor_op(xstr, mctx->cursor_step);
79   }
80   if (mctx->orderby_support) {
81     iwxstr_cat2(xstr, " ORDERBY");
82   }
83   iwxstr_cat2(xstr, "\n");
84 }
85 
_jbi_idx_expr_op_weight(struct _JBMIDX * midx)86 IW_INLINE int _jbi_idx_expr_op_weight(struct _JBMIDX *midx) {
87   jqp_op_t op = midx->expr1->op->value;
88   switch (op) {
89     case JQP_OP_EQ:
90       return 10;
91     case JQP_OP_IN:
92       //case JQP_OP_NI: todo
93       return 9;
94     default:
95       break;
96   }
97   if (midx->orderby_support) {
98     return 8;
99   }
100   switch (op) {
101     case JQP_OP_GT:
102     case JQP_OP_GTE:
103       return 7;
104     case JQP_OP_PREFIX:
105       return 6;
106     case JQP_OP_LT:
107     case JQP_OP_LTE:
108       return 5;
109     default:
110       return 0;
111   }
112 }
113 
_jbi_is_solid_node_expression(const JQP_NODE * n)114 static bool _jbi_is_solid_node_expression(const JQP_NODE *n) {
115   JQPUNIT *unit = n->value;
116   for (const JQP_EXPR *expr = &unit->expr; expr; expr = expr->next) {
117     if (  expr->op->negate
118        || (expr->join && (expr->join->negate || (expr->join->value == JQP_JOIN_OR)))
119        || (expr->op->value == JQP_OP_RE)) {
120       // No negate conditions, No OR, No regexp
121       return false;
122     }
123     JQPUNIT *left = expr->left;
124     if (  (left->type == JQP_EXPR_TYPE)
125        || ((left->type == JQP_STRING_TYPE) && (left->string.flavour & JQP_STR_STAR))) {
126       return false;
127     }
128   }
129   return true;
130 }
131 
_jbi_compute_index_rules(JBEXEC * ctx,struct _JBMIDX * mctx)132 static iwrc _jbi_compute_index_rules(JBEXEC *ctx, struct _JBMIDX *mctx) {
133   JQP_EXPR *expr = mctx->nexpr; // Node expression
134   if (!expr) {
135     return 0;
136   }
137   JQP_AUX *aux = ctx->ux->q->aux;
138 
139   for ( ; expr; expr = expr->next) {
140     iwrc rc = 0;
141     jqp_op_t op = expr->op->value;
142     JQVAL *rv = jql_unit_to_jqval(aux, expr->right, &rc);
143     RCRET(rc);
144     if (expr->left->type != JQP_STRING_TYPE) {
145       continue;
146     }
147     switch (rv->type) {
148       case JQVAL_NULL:
149       case JQVAL_RE:
150       case JQVAL_BINN:
151         continue;
152       case JQVAL_JBLNODE: {
153         if ((op != JQP_OP_IN) || (rv->vnode->type != JBV_ARRAY)) {
154           continue;
155         }
156         int vcnt = 0;
157         for (JBL_NODE n = rv->vnode->child; n; n = n->next, ++vcnt);
158         if (  (vcnt > JB_IDX_EMPIRIC_MIN_INOP_ARRAY_SIZE)
159            && (  (vcnt > JB_IDX_EMPIRIC_MAX_INOP_ARRAY_SIZE)
160               || (mctx->idx->rnum < rv->vbinn->count * JB_IDX_EMPIRIC_MAX_INOP_ARRAY_RATIO))) {
161           // No index for large IN array | small collection size
162           continue;
163         }
164         break;
165       }
166       default:
167         break;
168     }
169     switch (op) {
170       case JQP_OP_EQ:
171         mctx->cursor_init = IWKV_CURSOR_EQ;
172         mctx->expr1 = expr;
173         mctx->expr2 = 0;
174         return 0;
175       case JQP_OP_PREFIX:
176         if (!(mctx->idx->mode & EJDB_IDX_STR)) {
177           mctx->expr1 = 0;
178           return 0;
179         }
180         if ((rv->type == JQVAL_STR) && (*rv->vstr == '\0')) {
181           continue;
182         }
183       case JQP_OP_GT:
184       case JQP_OP_GTE:
185         if (mctx->cursor_init != IWKV_CURSOR_EQ) {
186           if (mctx->expr1 && (mctx->cursor_init == IWKV_CURSOR_GE) && (op != JQP_OP_PREFIX)) {
187             JQVAL *pval = jql_unit_to_jqval(aux, mctx->expr1->right, &rc);
188             RCRET(rc);
189             int cv = jql_cmp_jqval_pair(pval, rv, &rc);
190             RCRET(rc);
191             if (cv > 0) {
192               break;
193             }
194           }
195           mctx->expr1 = expr;
196           mctx->cursor_init = IWKV_CURSOR_GE;
197           mctx->cursor_step = IWKV_CURSOR_PREV;
198         }
199         break;
200       case JQP_OP_LT:
201       case JQP_OP_LTE:
202         if (mctx->expr2) {
203           JQVAL *pval = jql_unit_to_jqval(aux, mctx->expr2->right, &rc);
204           RCRET(rc);
205           int cv = jql_cmp_jqval_pair(pval, rv, &rc);
206           RCRET(rc);
207           if (cv < 0) {
208             break;
209           }
210         }
211         mctx->expr2 = expr;
212         break;
213       case JQP_OP_IN:
214         if ((mctx->cursor_init != IWKV_CURSOR_EQ) && (rv->type >= JQVAL_JBLNODE)) {
215           mctx->expr1 = expr;
216           mctx->expr2 = 0;
217           mctx->cursor_init = IWKV_CURSOR_EQ;
218         }
219         break;
220       default:
221         continue;
222     }
223   }
224 
225   if (mctx->expr2) {
226     if (!mctx->expr1) {
227       mctx->expr1 = mctx->expr2;
228       mctx->expr2 = 0;
229       mctx->cursor_init = IWKV_CURSOR_GE;
230       mctx->cursor_step = IWKV_CURSOR_NEXT;
231     }
232   }
233 
234   // Orderby compatibility
235   if (mctx->orderby_support) {
236     if ((aux->orderby_num == 1) && (mctx->cursor_init != IWKV_CURSOR_EQ)) {
237       bool desc = (aux->orderby_ptrs[0]->op & 1) != 0; // Desc sort
238       if (desc) {
239         if (mctx->cursor_step != IWKV_CURSOR_NEXT) {
240           mctx->orderby_support = false;
241         }
242       } else {
243         if (mctx->cursor_step != IWKV_CURSOR_PREV) {
244           mctx->orderby_support = false;
245         }
246       }
247       if (!mctx->orderby_support && mctx->expr2) {
248         JQP_EXPR *tmp = mctx->expr1;
249         mctx->expr1 = mctx->expr2;
250         mctx->expr2 = tmp;
251         mctx->orderby_support = true;
252         if (desc) {
253           mctx->cursor_step = IWKV_CURSOR_NEXT;
254         } else {
255           mctx->cursor_step = IWKV_CURSOR_PREV;
256         }
257       }
258     } else {
259       mctx->orderby_support = false;
260     }
261   }
262 
263   return 0;
264 }
265 
_jbi_collect_indexes(JBEXEC * ctx,const struct JQP_EXPR_NODE * en,struct _JBMIDX marr[static JB_SOLID_EXPRNUM],size_t * snp)266 static iwrc _jbi_collect_indexes(
267   JBEXEC                     *ctx,
268   const struct JQP_EXPR_NODE *en,
269   struct _JBMIDX              marr[static JB_SOLID_EXPRNUM],
270   size_t                     *snp
271   ) {
272   iwrc rc = 0;
273   if (*snp >= JB_SOLID_EXPRNUM - 1) {
274     return 0;
275   }
276   if (en->type == JQP_EXPR_NODE_TYPE) {
277     struct JQP_EXPR_NODE *cn = en->chain;
278     for ( ; cn; cn = cn->next) {
279       if (cn->join && (cn->join->value == JQP_JOIN_OR)) {
280         return 0;
281       }
282     }
283     for (cn = en->chain; cn; cn = cn->next) {
284       if (!cn->join || !cn->join->negate) {
285         rc = _jbi_collect_indexes(ctx, cn, marr, snp);
286         RCRET(rc);
287       }
288     }
289   } else if (en->type == JQP_FILTER_TYPE) {
290     int fnc = 0;
291     JQP_FILTER *f = (JQP_FILTER*) en;  // -V1027
292     for (JQP_NODE *n = f->node; n; n = n->next, ++fnc) {
293       switch (n->ntype) {
294         case JQP_NODE_ANY:
295         case JQP_NODE_ANYS:
296           return 0;
297         case JQP_NODE_FIELD:
298           break;
299         case JQP_NODE_EXPR:
300           if (!_jbi_is_solid_node_expression(n)) {
301             return 0;
302           }
303           break;
304       }
305     }
306 
307     struct JQP_AUX *aux = ctx->ux->q->aux;
308     struct _JBL_PTR *obp = aux->orderby_num ? aux->orderby_ptrs[0] : 0;
309 
310     for (struct _JBIDX *idx = ctx->jbc->idx; idx && *snp < JB_SOLID_EXPRNUM; idx = idx->next) {
311       struct _JBMIDX mctx = { .filter = f };
312       struct _JBL_PTR *ptr = idx->ptr;
313       if (ptr->cnt > fnc) {
314         continue;
315       }
316 
317       JQP_EXPR *nexpr = 0;
318       int i = 0, j = 0;
319       for (JQP_NODE *n = f->node; n && i < ptr->cnt; n = n->next, ++i) {
320         nexpr = 0;
321         const char *field = 0;
322         if (n->ntype == JQP_NODE_FIELD) {
323           field = n->value->string.value;
324         } else if (n->ntype == JQP_NODE_EXPR) {
325           nexpr = &n->value->expr;
326           JQPUNIT *left = nexpr->left;
327           if (left->type == JQP_STRING_TYPE) {
328             field = left->string.value;
329           }
330         }
331         if (!field || (strcmp(field, ptr->n[i]) != 0)) {
332           break;
333         }
334         if (obp && (i == j) && (i < obp->cnt) && !strcmp(ptr->n[i], obp->n[i])) {
335           j++;
336         }
337         // Check for the last iteration and the special `**` case
338         if (  (i == ptr->cnt - 1)
339            && (idx->idbf & IWDB_COMPOUND_KEYS)
340            && n->next && !n->next->next && (n->next->ntype == JQP_NODE_EXPR)) {
341           JQPUNIT *left = n->next->value->expr.left;
342           if ((left->type == JQP_STRING_TYPE) && (left->string.flavour & JQP_STR_DBL_STAR)) {
343             i++;
344             j++;
345             nexpr = &n->next->value->expr;
346             break;
347           }
348         }
349       }
350       if ((i == ptr->cnt) && nexpr) {
351         mctx.idx = idx;
352         mctx.nexpr = nexpr;
353         mctx.orderby_support = (i == j);
354         rc = _jbi_compute_index_rules(ctx, &mctx);
355         RCRET(rc);
356         if (!mctx.expr1) { // Cannot find matching expressions
357           continue;
358         }
359         if (ctx->ux->log) {
360           iwxstr_cat2(ctx->ux->log, "[INDEX] MATCHED  ");
361           _jbi_log_index_rules(ctx->ux->log, &mctx);
362         }
363         marr[*snp] = mctx;
364         *snp = *snp + 1;
365       }
366     }
367   }
368   return rc;
369 }
370 
_jbi_idx_cmp(const void * o1,const void * o2)371 static int _jbi_idx_cmp(const void *o1, const void *o2) {
372   struct _JBMIDX *d1 = (struct _JBMIDX*) o1;
373   struct _JBMIDX *d2 = (struct _JBMIDX*) o2;
374   assert(d1 && d2);
375   int w1 = _jbi_idx_expr_op_weight(d1);
376   int w2 = _jbi_idx_expr_op_weight(d2);
377   if (w2 != w1) {
378     return w2 - w1;
379   }
380   w1 = d1->expr2 != 0;
381   w2 = d2->expr2 != 0;
382   if (w2 != w1) {
383     return w2 - w1;
384   }
385   if (d1->idx->rnum != d2->idx->rnum) {
386     return (d1->idx->rnum - d2->idx->rnum) > 0 ? 1 : -1;
387   }
388   return (d1->idx->ptr->cnt - d2->idx->ptr->cnt);
389 }
390 
_jbi_select_index_for_orderby(JBEXEC * ctx)391 static struct _JBIDX* _jbi_select_index_for_orderby(JBEXEC *ctx) {
392   struct JQP_AUX *aux = ctx->ux->q->aux;
393   struct _JBL_PTR *obp = aux->orderby_ptrs[0];
394   assert(obp);
395   for (struct _JBIDX *idx = ctx->jbc->idx; idx; idx = idx->next) {
396     struct _JBL_PTR *ptr = idx->ptr;
397     if (obp->cnt != ptr->cnt) {
398       continue;
399     }
400     int i = 0;
401     for ( ; i < obp->cnt && !strcmp(ptr->n[i], obp->n[i]); ++i);
402     if (i == obp->cnt) {
403       memset(&ctx->midx, 0, sizeof(ctx->midx));
404       if (!(obp->op & 1)) { // Asc sort
405         ctx->cursor_init = IWKV_CURSOR_AFTER_LAST;
406         ctx->cursor_step = IWKV_CURSOR_PREV;
407       }
408       ctx->midx.idx = idx;
409       ctx->midx.orderby_support = true;
410       ctx->midx.cursor_init = ctx->cursor_init;
411       ctx->midx.cursor_step = ctx->cursor_step;
412       ctx->sorting = false;
413       return idx;
414     }
415   }
416   return 0;
417 }
418 
jbi_selection(JBEXEC * ctx)419 iwrc jbi_selection(JBEXEC *ctx) {
420   iwrc rc = 0;
421   size_t snp = 0;
422   struct JQP_AUX *aux = ctx->ux->q->aux;
423   struct _JBMIDX fctx[JB_SOLID_EXPRNUM] = { 0 };
424 
425   ctx->cursor_init = IWKV_CURSOR_BEFORE_FIRST;
426   ctx->cursor_step = IWKV_CURSOR_NEXT;
427 
428   // Index not found:
429   if (aux->orderby_num) {
430     ctx->sorting = true;
431   } else if (aux->qmode & JQP_QRY_INVERSE) {
432     ctx->cursor_init = IWKV_CURSOR_AFTER_LAST;
433     ctx->cursor_step = IWKV_CURSOR_PREV;
434   }
435 
436   if (!(aux->qmode & JQP_QRY_NOIDX) && ctx->jbc->idx) { // we have indexes associated with collection
437     rc = _jbi_collect_indexes(ctx, aux->expr, fctx, &snp);
438     RCRET(rc);
439     if (snp) { // Index selected
440       qsort(fctx, snp, sizeof(fctx[0]), _jbi_idx_cmp);
441       memcpy(&ctx->midx, &fctx[0], sizeof(ctx->midx));
442       struct _JBMIDX *midx = &ctx->midx;
443       jqp_op_t op = midx->expr1->op->value;
444       if ((op == JQP_OP_EQ) || (op == JQP_OP_IN) || ((op == JQP_OP_GTE) && (ctx->cursor_init == IWKV_CURSOR_GE))) {
445         midx->expr1->prematched = true;
446       }
447       if (ctx->ux->log) {
448         iwxstr_cat2(ctx->ux->log, "[INDEX] SELECTED ");
449         _jbi_log_index_rules(ctx->ux->log, &ctx->midx);
450       }
451       if (midx->orderby_support && (aux->orderby_num == 1)) {
452         // Turn off final sorting since it supported by natural index scan order
453         ctx->sorting = false;
454       } else if (aux->orderby_num) {
455         ctx->sorting = true;
456       }
457     } else if (ctx->sorting) { // Last chance to use index and avoid sorting
458       if (_jbi_select_index_for_orderby(ctx) && ctx->ux->log) {
459         iwxstr_cat2(ctx->ux->log, "[INDEX] SELECTED ");
460         _jbi_log_index_rules(ctx->ux->log, &ctx->midx);
461       }
462     }
463   }
464   return rc;
465 }
466