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, "%lld ", 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