1 /* -*- c-basic-offset: 2 -*- */
2 /*
3   Copyright(C) 2009-2017 Brazil
4 
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License version 2.1 as published by the Free Software Foundation.
8 
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
17 */
18 
19 #include "../grn_proc.h"
20 #include "../grn_raw_string.h"
21 #include "../grn_expr.h"
22 #include "../grn_str.h"
23 #include "../grn_output.h"
24 #include "../grn_util.h"
25 #include "../grn_cache.h"
26 #include "../grn_ii.h"
27 
28 #include "../grn_ts.h"
29 
30 #include <groonga/plugin.h>
31 
32 #define GRN_SELECT_INTERNAL_VAR_MATCH_COLUMNS "$match_columns"
33 
34 #define DEFAULT_DRILLDOWN_LIMIT           10
35 #define DEFAULT_DRILLDOWN_OUTPUT_COLUMNS  "_key, _nsubrecs"
36 
37 typedef enum {
38   GRN_COLUMN_STAGE_INITIAL,
39   GRN_COLUMN_STAGE_FILTERED,
40   GRN_COLUMN_STAGE_OUTPUT
41 } grn_column_stage;
42 
43 typedef struct {
44   grn_raw_string label;
45   grn_column_stage stage;
46   grn_obj *type;
47   grn_obj_flags flags;
48   grn_raw_string value;
49   struct {
50     grn_raw_string sort_keys;
51     grn_raw_string group_keys;
52   } window;
53 } grn_column_data;
54 
55 typedef struct {
56   grn_hash *initial;
57   grn_hash *filtered;
58   grn_hash *output;
59 } grn_columns;
60 
61 typedef struct {
62   grn_raw_string match_columns;
63   grn_raw_string query;
64   grn_raw_string query_expander;
65   grn_raw_string query_flags;
66   grn_raw_string filter;
67   struct {
68     grn_obj *match_columns;
69     grn_obj *expression;
70   } condition;
71   grn_obj *filtered;
72 } grn_filter_data;
73 
74 typedef struct {
75   grn_raw_string label;
76   grn_filter_data filter;
77   grn_raw_string sort_keys;
78   grn_raw_string output_columns;
79   int offset;
80   int limit;
81   grn_obj *table;
82 } grn_slice_data;
83 
84 typedef struct {
85   grn_raw_string label;
86   grn_raw_string keys;
87   grn_table_sort_key *parsed_keys;
88   int n_parsed_keys;
89   grn_raw_string sort_keys;
90   grn_raw_string output_columns;
91   int offset;
92   int limit;
93   grn_table_group_flags calc_types;
94   grn_raw_string calc_target_name;
95   grn_raw_string filter;
96   grn_raw_string table_name;
97   grn_columns columns;
98   grn_table_group_result result;
99   grn_obj *filtered_result;
100 } grn_drilldown_data;
101 
102 typedef struct _grn_select_output_formatter grn_select_output_formatter;
103 
104 typedef struct {
105   /* inputs */
106   grn_raw_string table;
107   grn_filter_data filter;
108   grn_raw_string scorer;
109   grn_raw_string sort_keys;
110   grn_raw_string output_columns;
111   int offset;
112   int limit;
113   grn_hash *slices;
114   grn_drilldown_data drilldown;
115   grn_hash *drilldowns;
116   grn_raw_string cache;
117   grn_raw_string match_escalation_threshold;
118   grn_raw_string adjuster;
119   grn_columns columns;
120 
121   /* for processing */
122   struct {
123     grn_obj *target;
124     grn_obj *initial;
125     grn_obj *result;
126     grn_obj *sorted;
127     grn_obj *output;
128   } tables;
129   uint16_t cacheable;
130   uint16_t taintable;
131   struct {
132     int n_elements;
133     grn_select_output_formatter *formatter;
134   } output;
135 } grn_select_data;
136 
137 typedef void grn_select_output_slices_label_func(grn_ctx *ctx,
138                                                  grn_select_data *data);
139 typedef void grn_select_output_slices_open_func(grn_ctx *ctx,
140                                                 grn_select_data *data,
141                                                 unsigned int n_result_sets);
142 typedef void grn_select_output_slices_close_func(grn_ctx *ctx,
143                                                  grn_select_data *data);
144 typedef void grn_select_output_slice_label_func(grn_ctx *ctx,
145                                                 grn_select_data *data,
146                                                 grn_slice_data *slice);
147 typedef void grn_select_output_drilldowns_label_func(grn_ctx *ctx,
148                                                      grn_select_data *data);
149 typedef void grn_select_output_drilldowns_open_func(grn_ctx *ctx,
150                                                     grn_select_data *data,
151                                                     unsigned int n_result_sets);
152 typedef void grn_select_output_drilldowns_close_func(grn_ctx *ctx,
153                                                      grn_select_data *data);
154 typedef void grn_select_output_drilldown_label_func(grn_ctx *ctx,
155                                                     grn_select_data *data,
156                                                     grn_drilldown_data *drilldown);
157 
158 struct _grn_select_output_formatter {
159   grn_select_output_slices_label_func      *slices_label;
160   grn_select_output_slices_open_func       *slices_open;
161   grn_select_output_slices_close_func      *slices_close;
162   grn_select_output_slice_label_func       *slice_label;
163   grn_select_output_drilldowns_label_func  *drilldowns_label;
164   grn_select_output_drilldowns_open_func   *drilldowns_open;
165   grn_select_output_drilldowns_close_func  *drilldowns_close;
166   grn_select_output_drilldown_label_func   *drilldown_label;
167 };
168 
169 grn_rc
grn_proc_syntax_expand_query(grn_ctx * ctx,const char * query,unsigned int query_len,grn_expr_flags flags,const char * query_expander_name,unsigned int query_expander_name_len,const char * term_column_name,unsigned int term_column_name_len,const char * expanded_term_column_name,unsigned int expanded_term_column_name_len,grn_obj * expanded_query,const char * error_message_tag)170 grn_proc_syntax_expand_query(grn_ctx *ctx,
171                              const char *query,
172                              unsigned int query_len,
173                              grn_expr_flags flags,
174                              const char *query_expander_name,
175                              unsigned int query_expander_name_len,
176                              const char *term_column_name,
177                              unsigned int term_column_name_len,
178                              const char *expanded_term_column_name,
179                              unsigned int expanded_term_column_name_len,
180                              grn_obj *expanded_query,
181                              const char *error_message_tag)
182 {
183   grn_obj *query_expander;
184 
185   query_expander = grn_ctx_get(ctx,
186                                query_expander_name,
187                                query_expander_name_len);
188   if (!query_expander) {
189     GRN_PLUGIN_ERROR(ctx,
190                      GRN_INVALID_ARGUMENT,
191                      "%s nonexistent query expander: <%.*s>",
192                      error_message_tag,
193                      (int)query_expander_name_len,
194                      query_expander_name);
195     return ctx->rc;
196   }
197 
198   if (expanded_term_column_name_len == 0) {
199     return grn_expr_syntax_expand_query(ctx, query, query_len, flags,
200                                         query_expander, expanded_query);
201   }
202 
203   if (!grn_obj_is_table(ctx, query_expander)) {
204     grn_obj inspected;
205     GRN_TEXT_INIT(&inspected, 0);
206     grn_inspect(ctx, &inspected, query_expander);
207     GRN_PLUGIN_ERROR(ctx,
208                      GRN_INVALID_ARGUMENT,
209                      "%s query expander with expanded term column "
210                      "must be table: <%.*s>",
211                      error_message_tag,
212                      (int)GRN_TEXT_LEN(&inspected),
213                      GRN_TEXT_VALUE(&inspected));
214     GRN_OBJ_FIN(ctx, &inspected);
215     return ctx->rc;
216   }
217 
218   {
219     grn_obj *term_column = NULL;
220     grn_obj *expanded_term_column;
221 
222     expanded_term_column = grn_obj_column(ctx,
223                                           query_expander,
224                                           expanded_term_column_name,
225                                           expanded_term_column_name_len);
226     if (!expanded_term_column) {
227       grn_obj inspected;
228       GRN_TEXT_INIT(&inspected, 0);
229       grn_inspect(ctx, &inspected, query_expander);
230       GRN_PLUGIN_ERROR(ctx,
231                        GRN_INVALID_ARGUMENT,
232                        "%s nonexistent expanded term column: <%.*s>: "
233                        "query expander: <%.*s>",
234                        error_message_tag,
235                        (int)expanded_term_column_name_len,
236                        expanded_term_column_name,
237                        (int)GRN_TEXT_LEN(&inspected),
238                        GRN_TEXT_VALUE(&inspected));
239       GRN_OBJ_FIN(ctx, &inspected);
240       return ctx->rc;
241     }
242 
243     if (term_column_name_len > 0) {
244       term_column = grn_obj_column(ctx,
245                                    query_expander,
246                                    term_column_name,
247                                    term_column_name_len);
248       if (!term_column) {
249         grn_obj inspected;
250         GRN_TEXT_INIT(&inspected, 0);
251         grn_inspect(ctx, &inspected, query_expander);
252         GRN_PLUGIN_ERROR(ctx,
253                          GRN_INVALID_ARGUMENT,
254                          "%s nonexistent term column: <%.*s>: "
255                          "query expander: <%.*s>",
256                          error_message_tag,
257                          (int)term_column_name_len,
258                          term_column_name,
259                          (int)GRN_TEXT_LEN(&inspected),
260                          GRN_TEXT_VALUE(&inspected));
261         GRN_OBJ_FIN(ctx, &inspected);
262         if (grn_obj_is_accessor(ctx, expanded_term_column)) {
263           grn_obj_unlink(ctx, expanded_term_column);
264         }
265         return ctx->rc;
266       }
267     }
268 
269     grn_expr_syntax_expand_query_by_table(ctx,
270                                           query, query_len,
271                                           flags,
272                                           term_column,
273                                           expanded_term_column,
274                                           expanded_query);
275     if (grn_obj_is_accessor(ctx, term_column)) {
276       grn_obj_unlink(ctx, term_column);
277     }
278     if (grn_obj_is_accessor(ctx, expanded_term_column)) {
279       grn_obj_unlink(ctx, expanded_term_column);
280     }
281     return ctx->rc;
282   }
283 }
284 
285 static grn_table_group_flags
grn_parse_table_group_calc_types(grn_ctx * ctx,const char * calc_types,unsigned int calc_types_len)286 grn_parse_table_group_calc_types(grn_ctx *ctx,
287                                  const char *calc_types,
288                                  unsigned int calc_types_len)
289 {
290   grn_table_group_flags flags = 0;
291   const char *calc_types_end = calc_types + calc_types_len;
292 
293   while (calc_types < calc_types_end) {
294     if (*calc_types == ',' || *calc_types == ' ') {
295       calc_types += 1;
296       continue;
297     }
298 
299 #define CHECK_TABLE_GROUP_CALC_TYPE(name)\
300   if (((calc_types_end - calc_types) >= (sizeof(#name) - 1)) &&\
301       (!memcmp(calc_types, #name, sizeof(#name) - 1))) {\
302     flags |= GRN_TABLE_GROUP_CALC_ ## name;\
303     calc_types += sizeof(#name) - 1;\
304     continue;\
305   }
306 
307     CHECK_TABLE_GROUP_CALC_TYPE(COUNT);
308     CHECK_TABLE_GROUP_CALC_TYPE(MAX);
309     CHECK_TABLE_GROUP_CALC_TYPE(MIN);
310     CHECK_TABLE_GROUP_CALC_TYPE(SUM);
311     CHECK_TABLE_GROUP_CALC_TYPE(AVG);
312 
313 #define GRN_TABLE_GROUP_CALC_NONE 0
314     CHECK_TABLE_GROUP_CALC_TYPE(NONE);
315 #undef GRN_TABLE_GROUP_CALC_NONE
316 
317     GRN_PLUGIN_ERROR(ctx,
318                      GRN_INVALID_ARGUMENT,
319                      "invalid table group calc type: <%.*s>",
320                      (int)(calc_types_end - calc_types),
321                      calc_types);
322     return 0;
323 #undef CHECK_TABLE_GROUP_CALC_TYPE
324   }
325 
326   return flags;
327 }
328 
329 static const char *
grn_column_stage_name(grn_column_stage stage)330 grn_column_stage_name(grn_column_stage stage)
331 {
332   switch (stage) {
333   case GRN_COLUMN_STAGE_INITIAL :
334     return "initial";
335   case GRN_COLUMN_STAGE_FILTERED :
336     return "filtered";
337   case GRN_COLUMN_STAGE_OUTPUT :
338     return "output";
339   default :
340     return "unknown";
341   }
342 }
343 
344 static grn_bool
grn_column_data_init(grn_ctx * ctx,const char * label,size_t label_len,grn_column_stage stage,grn_hash ** columns)345 grn_column_data_init(grn_ctx *ctx,
346                      const char *label,
347                      size_t label_len,
348                      grn_column_stage stage,
349                      grn_hash **columns)
350 {
351   void *column_raw;
352   grn_column_data *column;
353   int added;
354 
355   if (!*columns) {
356     *columns = grn_hash_create(ctx,
357                                NULL,
358                                GRN_TABLE_MAX_KEY_SIZE,
359                                sizeof(grn_column_data),
360                                GRN_OBJ_TABLE_HASH_KEY |
361                                GRN_OBJ_KEY_VAR_SIZE |
362                                GRN_HASH_TINY);
363   }
364   if (!*columns) {
365     return GRN_FALSE;
366   }
367 
368   grn_hash_add(ctx,
369                *columns,
370                label,
371                label_len,
372                &column_raw,
373                &added);
374   if (!added) {
375     return GRN_TRUE;
376   }
377 
378   column = column_raw;
379   column->label.value = label;
380   column->label.length = label_len;
381   column->stage = stage;
382   column->type = grn_ctx_at(ctx, GRN_DB_TEXT);
383   column->flags = GRN_OBJ_COLUMN_SCALAR;
384   GRN_RAW_STRING_INIT(column->value);
385   GRN_RAW_STRING_INIT(column->window.sort_keys);
386   GRN_RAW_STRING_INIT(column->window.group_keys);
387 
388   return GRN_TRUE;
389 }
390 
391 static grn_bool
grn_column_data_fill(grn_ctx * ctx,grn_column_data * column,grn_obj * type_raw,grn_obj * flags,grn_obj * value,grn_obj * window_sort_keys,grn_obj * window_group_keys)392 grn_column_data_fill(grn_ctx *ctx,
393                      grn_column_data *column,
394                      grn_obj *type_raw,
395                      grn_obj *flags,
396                      grn_obj *value,
397                      grn_obj *window_sort_keys,
398                      grn_obj *window_group_keys)
399 {
400   if (type_raw && GRN_TEXT_LEN(type_raw) > 0) {
401     grn_obj *type;
402 
403     type = grn_ctx_get(ctx, GRN_TEXT_VALUE(type_raw), GRN_TEXT_LEN(type_raw));
404     if (!type) {
405       GRN_PLUGIN_ERROR(ctx,
406                        GRN_INVALID_ARGUMENT,
407                        "[select][columns][%s][%.*s] unknown type: <%.*s>",
408                        grn_column_stage_name(column->stage),
409                        (int)(column->label.length),
410                        column->label.value,
411                        (int)(GRN_TEXT_LEN(type_raw)),
412                        GRN_TEXT_VALUE(type_raw));
413       return GRN_FALSE;
414     }
415     if (!(grn_obj_is_type(ctx, type) || grn_obj_is_table(ctx, type))) {
416       grn_obj inspected;
417       GRN_TEXT_INIT(&inspected, 0);
418       grn_inspect(ctx, &inspected, type);
419       GRN_PLUGIN_ERROR(ctx,
420                        GRN_INVALID_ARGUMENT,
421                        "[select][columns][%s][%.*s] invalid type: %.*s",
422                        grn_column_stage_name(column->stage),
423                        (int)(column->label.length),
424                        column->label.value,
425                        (int)(GRN_TEXT_LEN(&inspected)),
426                        GRN_TEXT_VALUE(&inspected));
427       GRN_OBJ_FIN(ctx, &inspected);
428       grn_obj_unlink(ctx, type);
429       return GRN_FALSE;
430     }
431     column->type = type;
432   }
433 
434   if (flags && GRN_TEXT_LEN(flags) > 0) {
435     char error_message_tag[GRN_TABLE_MAX_KEY_SIZE];
436 
437     grn_snprintf(error_message_tag,
438                  GRN_TABLE_MAX_KEY_SIZE,
439                  GRN_TABLE_MAX_KEY_SIZE,
440                  "[select][columns][%s][%.*s]",
441                  grn_column_stage_name(column->stage),
442                  (int)(column->label.length),
443                  column->label.value);
444     column->flags =
445       grn_proc_column_parse_flags(ctx,
446                                   error_message_tag,
447                                   GRN_TEXT_VALUE(flags),
448                                   GRN_TEXT_VALUE(flags) + GRN_TEXT_LEN(flags));
449     if (ctx->rc != GRN_SUCCESS) {
450       return GRN_FALSE;
451     }
452   }
453 
454   GRN_RAW_STRING_FILL(column->value, value);
455   GRN_RAW_STRING_FILL(column->window.sort_keys, window_sort_keys);
456   GRN_RAW_STRING_FILL(column->window.group_keys, window_group_keys);
457 
458   return GRN_TRUE;
459 }
460 
461 static grn_bool
grn_column_data_collect(grn_ctx * ctx,grn_user_data * user_data,grn_hash * columns,const char * prefix_label,size_t prefix_label_len)462 grn_column_data_collect(grn_ctx *ctx,
463                         grn_user_data *user_data,
464                         grn_hash *columns,
465                         const char *prefix_label,
466                         size_t prefix_label_len)
467 {
468   grn_hash_cursor *cursor = NULL;
469 
470   cursor = grn_hash_cursor_open(ctx, columns,
471                                 NULL, 0, NULL, 0, 0, -1, 0);
472   if (!cursor) {
473     return GRN_FALSE;
474   }
475 
476   while (grn_hash_cursor_next(ctx, cursor)) {
477     grn_column_data *column;
478     char key_name[GRN_TABLE_MAX_KEY_SIZE];
479     grn_obj *type = NULL;
480     grn_obj *flags = NULL;
481     grn_obj *value = NULL;
482     struct {
483       grn_obj *sort_keys;
484       grn_obj *group_keys;
485     } window;
486 
487     window.sort_keys = NULL;
488     window.group_keys = NULL;
489 
490     grn_hash_cursor_get_value(ctx, cursor, (void **)&column);
491 
492 #define GET_VAR_RAW(parameter_key, name)                                \
493     if (!name) {                                                        \
494       grn_snprintf(key_name,                                            \
495                    GRN_TABLE_MAX_KEY_SIZE,                              \
496                    GRN_TABLE_MAX_KEY_SIZE,                              \
497                    "%.*s%s[%.*s]." # name,                              \
498                    (int)prefix_label_len,                               \
499                    prefix_label,                                        \
500                    parameter_key,                                       \
501                    (int)(column->label.length),                         \
502                    column->label.value);                                \
503       name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1);     \
504     }
505 
506 #define GET_VAR(name) do {                      \
507       GET_VAR_RAW("columns", name);             \
508       /* For backward compatibility */          \
509       GET_VAR_RAW("column", name);              \
510     } while (GRN_FALSE)
511 
512     GET_VAR(type);
513     GET_VAR(flags);
514     GET_VAR(value);
515     GET_VAR(window.sort_keys);
516     GET_VAR(window.group_keys);
517 
518 #undef GET_VAR
519 
520 #undef GET_VAR_RAW
521 
522     grn_column_data_fill(ctx, column,
523                          type, flags, value,
524                          window.sort_keys,
525                          window.group_keys);
526   }
527   grn_hash_cursor_close(ctx, cursor);
528   return GRN_TRUE;
529 }
530 
531 static void
grn_columns_init(grn_ctx * ctx,grn_columns * columns)532 grn_columns_init(grn_ctx *ctx, grn_columns *columns)
533 {
534   columns->initial = NULL;
535   columns->filtered = NULL;
536   columns->output = NULL;
537 }
538 
539 static void
grn_columns_fin(grn_ctx * ctx,grn_columns * columns)540 grn_columns_fin(grn_ctx *ctx, grn_columns *columns)
541 {
542   if (columns->initial) {
543     grn_hash_close(ctx, columns->initial);
544   }
545 
546   if (columns->filtered) {
547     grn_hash_close(ctx, columns->filtered);
548   }
549 
550   if (columns->output) {
551     grn_hash_close(ctx, columns->output);
552   }
553 }
554 
555 static grn_bool
grn_columns_collect(grn_ctx * ctx,grn_user_data * user_data,grn_columns * columns,const char * prefix,const char * base_prefix,size_t base_prefix_len)556 grn_columns_collect(grn_ctx *ctx,
557                     grn_user_data *user_data,
558                     grn_columns *columns,
559                     const char *prefix,
560                     const char *base_prefix,
561                     size_t base_prefix_len)
562 {
563   grn_obj *vars;
564   grn_table_cursor *cursor;
565   size_t prefix_len;
566   const char *suffix = "].stage";
567   size_t suffix_len;
568 
569   vars = grn_plugin_proc_get_vars(ctx, user_data);
570   cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0);
571   if (!cursor) {
572     return GRN_FALSE;
573   }
574 
575   prefix_len = strlen(prefix);
576   suffix_len = strlen(suffix);
577   while (grn_table_cursor_next(ctx, cursor)) {
578     void *key;
579     char *variable_name;
580     int variable_name_len;
581     char *column_name;
582     size_t column_name_len;
583     void *value_raw;
584     grn_obj *value;
585     grn_column_stage stage;
586     grn_hash **target_columns;
587 
588     variable_name_len = grn_table_cursor_get_key(ctx, cursor, &key);
589     variable_name = key;
590     if (variable_name_len < base_prefix_len + prefix_len + suffix_len + 1) {
591       continue;
592     }
593 
594     if (base_prefix_len > 0) {
595       if (memcmp(base_prefix, variable_name, base_prefix_len) != 0) {
596         continue;
597       }
598     }
599 
600     if (memcmp(prefix, variable_name + base_prefix_len, prefix_len) != 0) {
601       continue;
602     }
603 
604     if (memcmp(suffix,
605                variable_name + (variable_name_len - suffix_len),
606                suffix_len) != 0) {
607       continue;
608     }
609 
610     grn_table_cursor_get_value(ctx, cursor, &value_raw);
611     value = value_raw;
612     if (GRN_TEXT_EQUAL_CSTRING(value, "initial")) {
613       stage = GRN_COLUMN_STAGE_INITIAL;
614       target_columns = &(columns->initial);
615     } else if (GRN_TEXT_EQUAL_CSTRING(value, "filtered")) {
616       stage = GRN_COLUMN_STAGE_FILTERED;
617       target_columns = &(columns->filtered);
618     } else if (GRN_TEXT_EQUAL_CSTRING(value, "output")) {
619       stage = GRN_COLUMN_STAGE_OUTPUT;
620       target_columns = &(columns->output);
621     } else {
622       continue;
623     }
624 
625     column_name = variable_name + base_prefix_len + prefix_len;
626     column_name_len =
627       variable_name_len - base_prefix_len - prefix_len - suffix_len;
628     if (!grn_column_data_init(ctx,
629                               column_name,
630                               column_name_len,
631                               stage,
632                               target_columns)) {
633       grn_table_cursor_close(ctx, cursor);
634       return GRN_FALSE;
635     }
636   }
637   grn_table_cursor_close(ctx, cursor);
638 
639   return GRN_TRUE;
640 }
641 
642 static grn_bool
grn_columns_fill(grn_ctx * ctx,grn_user_data * user_data,grn_columns * columns,const char * prefix,size_t prefix_length)643 grn_columns_fill(grn_ctx *ctx,
644                  grn_user_data *user_data,
645                  grn_columns *columns,
646                  const char *prefix,
647                  size_t prefix_length)
648 {
649   if (!grn_columns_collect(ctx, user_data, columns,
650                            "columns[", prefix, prefix_length)) {
651     return GRN_FALSE;
652   }
653 
654   /* For backward compatibility */
655   if (!grn_columns_collect(ctx, user_data, columns,
656                            "column[", prefix, prefix_length)) {
657     return GRN_FALSE;
658   }
659 
660   if (columns->initial) {
661     if (!grn_column_data_collect(ctx,
662                                  user_data,
663                                  columns->initial,
664                                  prefix,
665                                  prefix_length)) {
666       return GRN_FALSE;
667     }
668   }
669 
670   if (columns->filtered) {
671     if (!grn_column_data_collect(ctx,
672                                  user_data,
673                                  columns->filtered,
674                                  prefix,
675                                  prefix_length)) {
676       return GRN_FALSE;
677     }
678   }
679 
680   if (columns->output) {
681     if (!grn_column_data_collect(ctx,
682                                  user_data,
683                                  columns->output,
684                                  prefix,
685                                  prefix_length)) {
686       return GRN_FALSE;
687     }
688   }
689 
690   return GRN_TRUE;
691 }
692 
693 static void
grn_filter_data_init(grn_ctx * ctx,grn_filter_data * data)694 grn_filter_data_init(grn_ctx *ctx, grn_filter_data *data)
695 {
696   GRN_RAW_STRING_INIT(data->match_columns);
697   GRN_RAW_STRING_INIT(data->query);
698   GRN_RAW_STRING_INIT(data->query_expander);
699   GRN_RAW_STRING_INIT(data->query_flags);
700   GRN_RAW_STRING_INIT(data->filter);
701   data->condition.match_columns = NULL;
702   data->condition.expression = NULL;
703   data->filtered = NULL;
704 }
705 
706 static void
grn_filter_data_fin(grn_ctx * ctx,grn_filter_data * data)707 grn_filter_data_fin(grn_ctx *ctx, grn_filter_data *data)
708 {
709   if (data->filtered) {
710     grn_obj_unlink(ctx, data->filtered);
711   }
712   if (data->condition.expression) {
713     grn_obj_close(ctx, data->condition.expression);
714   }
715   if (data->condition.match_columns) {
716     grn_obj_close(ctx, data->condition.match_columns);
717   }
718 }
719 
720 static void
grn_filter_data_fill(grn_ctx * ctx,grn_filter_data * data,grn_obj * match_columns,grn_obj * query,grn_obj * query_expander,grn_obj * query_flags,grn_obj * filter)721 grn_filter_data_fill(grn_ctx *ctx,
722                      grn_filter_data *data,
723                      grn_obj *match_columns,
724                      grn_obj *query,
725                      grn_obj *query_expander,
726                      grn_obj *query_flags,
727                      grn_obj *filter)
728 {
729   GRN_RAW_STRING_FILL(data->match_columns, match_columns);
730   GRN_RAW_STRING_FILL(data->query, query);
731   GRN_RAW_STRING_FILL(data->query_expander, query_expander);
732   GRN_RAW_STRING_FILL(data->query_flags, query_flags);
733   GRN_RAW_STRING_FILL(data->filter, filter);
734 }
735 
736 static grn_bool
grn_filter_data_execute(grn_ctx * ctx,grn_filter_data * data,grn_obj * table,const char * tag)737 grn_filter_data_execute(grn_ctx *ctx,
738                         grn_filter_data *data,
739                         grn_obj *table,
740                         const char *tag)
741 {
742   grn_obj *variable;
743 
744   if (data->query.length == 0 && data->filter.length == 0) {
745     return GRN_TRUE;
746   }
747 
748   GRN_EXPR_CREATE_FOR_QUERY(ctx,
749                             table,
750                             data->condition.expression,
751                             variable);
752   if (!data->condition.expression) {
753     grn_rc rc = ctx->rc;
754     if (rc == GRN_SUCCESS) {
755       rc = GRN_NO_MEMORY_AVAILABLE;
756     }
757     GRN_PLUGIN_ERROR(ctx,
758                      rc,
759                      "%s[condition] "
760                      "failed to create expression for condition: %s",
761                      tag,
762                      ctx->errbuf);
763     return GRN_FALSE;
764   }
765 
766   if (data->query.length > 0) {
767     if (data->match_columns.length > 0) {
768       GRN_EXPR_CREATE_FOR_QUERY(ctx,
769                                 table,
770                                 data->condition.match_columns,
771                                 variable);
772       if (!data->condition.match_columns) {
773         grn_rc rc = ctx->rc;
774         if (rc == GRN_SUCCESS) {
775           rc = GRN_NO_MEMORY_AVAILABLE;
776         }
777         GRN_PLUGIN_ERROR(ctx,
778                          rc,
779                          "%s[match_columns] "
780                          "failed to create expression for match columns: "
781                          "<%.*s>: %s",
782                          tag,
783                          (int)(data->match_columns.length),
784                          data->match_columns.value,
785                          ctx->errbuf);
786         return GRN_FALSE;
787       }
788 
789       grn_expr_parse(ctx,
790                      data->condition.match_columns,
791                      data->match_columns.value,
792                      data->match_columns.length,
793                      NULL, GRN_OP_MATCH, GRN_OP_AND,
794                      GRN_EXPR_SYNTAX_SCRIPT);
795       if (ctx->rc != GRN_SUCCESS) {
796         return GRN_FALSE;
797       }
798     }
799 
800     {
801       grn_expr_flags flags;
802       grn_obj query_expander_buf;
803       const char *query = data->query.value;
804       unsigned int query_len = data->query.length;
805 
806       flags = GRN_EXPR_SYNTAX_QUERY;
807       if (data->query_flags.length) {
808         flags |= grn_proc_expr_query_flags_parse(ctx,
809                                                  data->query_flags.value,
810                                                  data->query_flags.length,
811                                                  tag);
812         if (ctx->rc != GRN_SUCCESS) {
813           return GRN_FALSE;
814         }
815       } else {
816         flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN;
817       }
818 
819       GRN_TEXT_INIT(&query_expander_buf, 0);
820       if (data->query_expander.length > 0) {
821         grn_rc rc;
822         rc = grn_proc_syntax_expand_query(ctx,
823                                           data->query.value,
824                                           data->query.length,
825                                           flags,
826                                           data->query_expander.value,
827                                           data->query_expander.length,
828                                           NULL, 0,
829                                           NULL, 0,
830                                           &query_expander_buf,
831                                           tag);
832         if (rc == GRN_SUCCESS) {
833           query = GRN_TEXT_VALUE(&query_expander_buf);
834           query_len = GRN_TEXT_LEN(&query_expander_buf);
835         } else {
836           GRN_OBJ_FIN(ctx, &query_expander_buf);
837           return GRN_FALSE;
838         }
839       }
840 
841       grn_expr_parse(ctx,
842                      data->condition.expression,
843                      query,
844                      query_len,
845                      data->condition.match_columns,
846                      GRN_OP_MATCH,
847                      GRN_OP_AND,
848                      flags);
849       GRN_OBJ_FIN(ctx, &query_expander_buf);
850 
851       if (ctx->rc != GRN_SUCCESS) {
852         return GRN_FALSE;
853       }
854     }
855   }
856 
857   if (data->filter.length > 0) {
858     grn_expr_parse(ctx,
859                    data->condition.expression,
860                    data->filter.value,
861                    data->filter.length,
862                    data->condition.match_columns,
863                    GRN_OP_MATCH,
864                    GRN_OP_AND,
865                    GRN_EXPR_SYNTAX_SCRIPT);
866     if (ctx->rc != GRN_SUCCESS) {
867       return GRN_FALSE;
868     }
869 
870     if (data->query.length > 0) {
871       grn_expr_append_op(ctx, data->condition.expression, GRN_OP_AND, 2);
872     }
873 
874     if (ctx->rc != GRN_SUCCESS) {
875       return GRN_FALSE;
876     }
877   }
878 
879   data->filtered = grn_table_select(ctx,
880                                     table,
881                                     data->condition.expression,
882                                     NULL,
883                                     GRN_OP_OR);
884 
885   return ctx->rc == GRN_SUCCESS;
886 }
887 
888 static void
grn_slice_data_init(grn_ctx * ctx,grn_slice_data * slice,const char * label,size_t label_len)889 grn_slice_data_init(grn_ctx *ctx,
890                     grn_slice_data *slice,
891                     const char *label,
892                     size_t label_len)
893 {
894   slice->label.value = label;
895   slice->label.length = label_len;
896   grn_filter_data_init(ctx, &(slice->filter));
897   GRN_RAW_STRING_INIT(slice->sort_keys);
898   GRN_RAW_STRING_INIT(slice->output_columns);
899   slice->offset = 0;
900   slice->limit = GRN_SELECT_DEFAULT_LIMIT;
901   slice->table = NULL;
902 }
903 
904 static void
grn_slice_data_fin(grn_ctx * ctx,grn_slice_data * slice)905 grn_slice_data_fin(grn_ctx *ctx, grn_slice_data *slice)
906 {
907   grn_filter_data_fin(ctx, &(slice->filter));
908 }
909 
910 static void
grn_slice_data_fill(grn_ctx * ctx,grn_slice_data * slice,grn_obj * match_columns,grn_obj * query,grn_obj * query_expander,grn_obj * query_flags,grn_obj * filter,grn_obj * sort_keys,grn_obj * output_columns,grn_obj * offset,grn_obj * limit)911 grn_slice_data_fill(grn_ctx *ctx,
912                     grn_slice_data *slice,
913                     grn_obj *match_columns,
914                     grn_obj *query,
915                     grn_obj *query_expander,
916                     grn_obj *query_flags,
917                     grn_obj *filter,
918                     grn_obj *sort_keys,
919                     grn_obj *output_columns,
920                     grn_obj *offset,
921                     grn_obj *limit)
922 {
923   grn_filter_data_fill(ctx,
924                        &(slice->filter),
925                        match_columns,
926                        query,
927                        query_expander,
928                        query_flags,
929                        filter);
930 
931   GRN_RAW_STRING_FILL(slice->sort_keys, sort_keys);
932 
933   GRN_RAW_STRING_FILL(slice->output_columns, output_columns);
934   if (slice->output_columns.length == 0) {
935     slice->output_columns.value = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS;
936     slice->output_columns.length = strlen(GRN_SELECT_DEFAULT_OUTPUT_COLUMNS);
937   }
938 
939   slice->offset = grn_proc_option_value_int32(ctx, offset, 0);
940   slice->limit = grn_proc_option_value_int32(ctx,
941                                              limit,
942                                              GRN_SELECT_DEFAULT_LIMIT);
943 }
944 
945 static void
grn_drilldown_data_init(grn_ctx * ctx,grn_drilldown_data * drilldown,const char * label,size_t label_len)946 grn_drilldown_data_init(grn_ctx *ctx,
947                         grn_drilldown_data *drilldown,
948                         const char *label,
949                         size_t label_len)
950 {
951   drilldown->label.value = label;
952   drilldown->label.length = label_len;
953   GRN_RAW_STRING_INIT(drilldown->keys);
954   drilldown->parsed_keys = NULL;
955   drilldown->n_parsed_keys = 0;
956   GRN_RAW_STRING_INIT(drilldown->sort_keys);
957   GRN_RAW_STRING_INIT(drilldown->output_columns);
958   drilldown->offset = 0;
959   drilldown->limit = DEFAULT_DRILLDOWN_LIMIT;
960   drilldown->calc_types = 0;
961   GRN_RAW_STRING_INIT(drilldown->calc_target_name);
962   GRN_RAW_STRING_INIT(drilldown->filter);
963   GRN_RAW_STRING_INIT(drilldown->table_name);
964   grn_columns_init(ctx, &(drilldown->columns));
965   drilldown->result.table = NULL;
966   drilldown->filtered_result = NULL;
967 }
968 
969 static void
grn_drilldown_data_fin(grn_ctx * ctx,grn_drilldown_data * drilldown)970 grn_drilldown_data_fin(grn_ctx *ctx, grn_drilldown_data *drilldown)
971 {
972   grn_table_group_result *result;
973 
974   grn_columns_fin(ctx, &(drilldown->columns));
975 
976   if (drilldown->filtered_result) {
977     grn_obj_close(ctx, drilldown->filtered_result);
978   }
979 
980   result = &(drilldown->result);
981   if (result->table) {
982     if (result->calc_target) {
983       grn_obj_unlink(ctx, result->calc_target);
984     }
985     if (result->table) {
986       grn_obj_close(ctx, result->table);
987     }
988   }
989 }
990 
991 static void
grn_drilldown_data_fill(grn_ctx * ctx,grn_drilldown_data * drilldown,grn_obj * keys,grn_obj * sort_keys,grn_obj * output_columns,grn_obj * offset,grn_obj * limit,grn_obj * calc_types,grn_obj * calc_target,grn_obj * filter,grn_obj * table)992 grn_drilldown_data_fill(grn_ctx *ctx,
993                         grn_drilldown_data *drilldown,
994                         grn_obj *keys,
995                         grn_obj *sort_keys,
996                         grn_obj *output_columns,
997                         grn_obj *offset,
998                         grn_obj *limit,
999                         grn_obj *calc_types,
1000                         grn_obj *calc_target,
1001                         grn_obj *filter,
1002                         grn_obj *table)
1003 {
1004   GRN_RAW_STRING_FILL(drilldown->keys, keys);
1005 
1006   GRN_RAW_STRING_FILL(drilldown->sort_keys, sort_keys);
1007 
1008   GRN_RAW_STRING_FILL(drilldown->output_columns, output_columns);
1009   if (drilldown->output_columns.length == 0) {
1010     drilldown->output_columns.value = DEFAULT_DRILLDOWN_OUTPUT_COLUMNS;
1011     drilldown->output_columns.length = strlen(DEFAULT_DRILLDOWN_OUTPUT_COLUMNS);
1012   }
1013 
1014   if (offset && GRN_TEXT_LEN(offset)) {
1015     drilldown->offset =
1016       grn_atoi(GRN_TEXT_VALUE(offset), GRN_BULK_CURR(offset), NULL);
1017   } else {
1018     drilldown->offset = 0;
1019   }
1020 
1021   if (limit && GRN_TEXT_LEN(limit)) {
1022     drilldown->limit =
1023       grn_atoi(GRN_TEXT_VALUE(limit), GRN_BULK_CURR(limit), NULL);
1024   } else {
1025     drilldown->limit = DEFAULT_DRILLDOWN_LIMIT;
1026   }
1027 
1028   if (calc_types && GRN_TEXT_LEN(calc_types)) {
1029     drilldown->calc_types =
1030       grn_parse_table_group_calc_types(ctx,
1031                                        GRN_TEXT_VALUE(calc_types),
1032                                        GRN_TEXT_LEN(calc_types));
1033   } else {
1034     drilldown->calc_types = 0;
1035   }
1036 
1037   GRN_RAW_STRING_FILL(drilldown->calc_target_name, calc_target);
1038 
1039   GRN_RAW_STRING_FILL(drilldown->filter, filter);
1040 
1041   GRN_RAW_STRING_FILL(drilldown->table_name, table);
1042 }
1043 
1044 grn_expr_flags
grn_proc_expr_query_flags_parse(grn_ctx * ctx,const char * query_flags,size_t query_flags_size,const char * error_message_tag)1045 grn_proc_expr_query_flags_parse(grn_ctx *ctx,
1046                                 const char *query_flags,
1047                                 size_t query_flags_size,
1048                                 const char *error_message_tag)
1049 {
1050   grn_expr_flags flags = 0;
1051   const char *query_flags_end = query_flags + query_flags_size;
1052 
1053   while (query_flags < query_flags_end) {
1054     if (*query_flags == '|' || *query_flags == ' ') {
1055       query_flags += 1;
1056       continue;
1057     }
1058 
1059 #define CHECK_EXPR_FLAG(name)                                           \
1060     if (((query_flags_end - query_flags) >= (sizeof(#name) - 1)) &&     \
1061         (memcmp(query_flags, #name, sizeof(#name) - 1) == 0) &&         \
1062         (((query_flags_end - query_flags) == (sizeof(#name) - 1)) ||    \
1063          (query_flags[sizeof(#name) - 1] == '|') ||                     \
1064          (query_flags[sizeof(#name) - 1] == ' '))) {                    \
1065       flags |= GRN_EXPR_ ## name;                                       \
1066       query_flags += sizeof(#name) - 1;                                 \
1067       continue;                                                         \
1068     }
1069 
1070     CHECK_EXPR_FLAG(ALLOW_PRAGMA);
1071     CHECK_EXPR_FLAG(ALLOW_COLUMN);
1072     CHECK_EXPR_FLAG(ALLOW_UPDATE);
1073     CHECK_EXPR_FLAG(ALLOW_LEADING_NOT);
1074     CHECK_EXPR_FLAG(QUERY_NO_SYNTAX_ERROR);
1075 
1076 #define GRN_EXPR_NONE 0
1077     CHECK_EXPR_FLAG(NONE);
1078 #undef GNR_EXPR_NONE
1079 
1080     GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
1081                      "%s invalid query flag: <%.*s>",
1082                      error_message_tag,
1083                      (int)(query_flags_end - query_flags),
1084                      query_flags);
1085     return 0;
1086 #undef CHECK_EXPR_FLAG
1087   }
1088 
1089   return flags;
1090 }
1091 
1092 static void
grn_select_expression_set_condition(grn_ctx * ctx,grn_obj * expression,grn_obj * condition)1093 grn_select_expression_set_condition(grn_ctx *ctx,
1094                                     grn_obj *expression,
1095                                     grn_obj *condition)
1096 {
1097   grn_obj *condition_ptr;
1098 
1099   if (!expression) {
1100     return;
1101   }
1102 
1103   condition_ptr =
1104     grn_expr_get_or_add_var(ctx, expression,
1105                             GRN_SELECT_INTERNAL_VAR_CONDITION,
1106                             GRN_SELECT_INTERNAL_VAR_CONDITION_LEN);
1107   GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT);
1108   GRN_PTR_SET(ctx, condition_ptr, condition);
1109 }
1110 
1111 grn_bool
grn_proc_select_format_init(grn_ctx * ctx,grn_obj_format * format,grn_obj * result_set,int n_hits,int offset,int limit,const char * columns,int columns_len,grn_obj * condition)1112 grn_proc_select_format_init(grn_ctx *ctx,
1113                             grn_obj_format *format,
1114                             grn_obj *result_set,
1115                             int n_hits,
1116                             int offset,
1117                             int limit,
1118                             const char *columns,
1119                             int columns_len,
1120                             grn_obj *condition)
1121 {
1122   grn_rc rc;
1123 
1124   GRN_OBJ_FORMAT_INIT(format, n_hits, offset, limit, offset);
1125   format->flags =
1126     GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
1127     GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET;
1128   rc = grn_output_format_set_columns(ctx,
1129                                      format,
1130                                      result_set,
1131                                      columns,
1132                                      columns_len);
1133   if (rc != GRN_SUCCESS) {
1134     GRN_OBJ_FORMAT_FIN(ctx, format);
1135     return GRN_FALSE;
1136   }
1137 
1138   grn_select_expression_set_condition(ctx, format->expression, condition);
1139 
1140   return ctx->rc == GRN_SUCCESS;
1141 }
1142 
1143 grn_bool
grn_proc_select_format_fin(grn_ctx * ctx,grn_obj_format * format)1144 grn_proc_select_format_fin(grn_ctx *ctx, grn_obj_format *format)
1145 {
1146   GRN_OBJ_FORMAT_FIN(ctx, format);
1147 
1148   return ctx->rc == GRN_SUCCESS;
1149 }
1150 
1151 grn_bool
grn_proc_select_output_columns_open(grn_ctx * ctx,grn_obj_format * format,grn_obj * res,int n_hits,int offset,int limit,const char * columns,int columns_len,grn_obj * condition,uint32_t n_additional_elements)1152 grn_proc_select_output_columns_open(grn_ctx *ctx,
1153                                     grn_obj_format *format,
1154                                     grn_obj *res,
1155                                     int n_hits,
1156                                     int offset,
1157                                     int limit,
1158                                     const char *columns,
1159                                     int columns_len,
1160                                     grn_obj *condition,
1161                                     uint32_t n_additional_elements)
1162 {
1163   grn_bool succeeded;
1164 
1165   if (!grn_proc_select_format_init(ctx,
1166                                    format,
1167                                    res,
1168                                    n_hits,
1169                                    offset,
1170                                    limit,
1171                                    columns,
1172                                    columns_len,
1173                                    condition)) {
1174     return GRN_FALSE;
1175   }
1176 
1177   GRN_OUTPUT_RESULT_SET_OPEN(res, format, n_additional_elements);
1178   succeeded = (ctx->rc == GRN_SUCCESS);
1179   if (!succeeded) {
1180     GRN_OUTPUT_RESULT_SET_CLOSE(res, format);
1181   }
1182 
1183   return succeeded;
1184 }
1185 
1186 grn_bool
grn_proc_select_output_columns_close(grn_ctx * ctx,grn_obj_format * format,grn_obj * result_set)1187 grn_proc_select_output_columns_close(grn_ctx *ctx,
1188                                      grn_obj_format *format,
1189                                      grn_obj *result_set)
1190 {
1191   GRN_OUTPUT_RESULT_SET_CLOSE(result_set, format);
1192 
1193   return grn_proc_select_format_fin(ctx, format);
1194 }
1195 
1196 grn_bool
grn_proc_select_output_columns(grn_ctx * ctx,grn_obj * res,int n_hits,int offset,int limit,const char * columns,int columns_len,grn_obj * condition)1197 grn_proc_select_output_columns(grn_ctx *ctx,
1198                                grn_obj *res,
1199                                int n_hits,
1200                                int offset,
1201                                int limit,
1202                                const char *columns,
1203                                int columns_len,
1204                                grn_obj *condition)
1205 {
1206   grn_obj_format format;
1207   uint32_t n_additional_elements = 0;
1208 
1209   if (!grn_proc_select_output_columns_open(ctx,
1210                                            &format,
1211                                            res,
1212                                            n_hits,
1213                                            offset,
1214                                            limit,
1215                                            columns,
1216                                            columns_len,
1217                                            condition,
1218                                            n_additional_elements)) {
1219     return GRN_FALSE;
1220   }
1221 
1222   return grn_proc_select_output_columns_close(ctx, &format, res);
1223 }
1224 
1225 static grn_obj *
grn_select_create_all_selected_result_table(grn_ctx * ctx,grn_obj * table)1226 grn_select_create_all_selected_result_table(grn_ctx *ctx,
1227                                             grn_obj *table)
1228 {
1229   grn_obj *result;
1230   grn_posting posting;
1231 
1232   result = grn_table_create(ctx, NULL, 0, NULL,
1233                             GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
1234                             table, NULL);
1235   if (!result) {
1236     return NULL;
1237   }
1238 
1239   memset(&posting, 0, sizeof(grn_posting));
1240   GRN_TABLE_EACH_BEGIN(ctx, table, cursor, id) {
1241     posting.rid = id;
1242     grn_ii_posting_add(ctx,
1243                        &posting,
1244                        (grn_hash *)(result),
1245                        GRN_OP_OR);
1246   } GRN_TABLE_EACH_END(ctx, cursor);
1247 
1248   return result;
1249 }
1250 
1251 static grn_obj *
grn_select_create_no_sort_keys_sorted_table(grn_ctx * ctx,grn_select_data * data,grn_obj * table)1252 grn_select_create_no_sort_keys_sorted_table(grn_ctx *ctx,
1253                                             grn_select_data *data,
1254                                             grn_obj *table)
1255 {
1256   grn_obj *sorted;
1257   grn_table_cursor *cursor;
1258 
1259   sorted = grn_table_create(ctx, NULL, 0, NULL,
1260                             GRN_OBJ_TABLE_NO_KEY,
1261                             NULL,
1262                             table);
1263 
1264   if (!sorted) {
1265     return NULL;
1266   }
1267 
1268   cursor = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
1269                                  data->offset,
1270                                  data->limit,
1271                                  GRN_CURSOR_ASCENDING);
1272   if (cursor) {
1273     grn_id id;
1274     while ((id = grn_table_cursor_next(ctx, cursor))) {
1275       grn_id *value;
1276       if (grn_array_add(ctx, (grn_array *)sorted, (void **)&value)) {
1277         *value = id;
1278       }
1279     }
1280     grn_table_cursor_close(ctx, cursor);
1281   }
1282 
1283   return sorted;
1284 }
1285 
1286 
1287 static void
grn_select_apply_columns(grn_ctx * ctx,grn_select_data * data,grn_obj * table,grn_hash * columns)1288 grn_select_apply_columns(grn_ctx *ctx,
1289                          grn_select_data *data,
1290                          grn_obj *table,
1291                          grn_hash *columns)
1292 {
1293   grn_hash_cursor *columns_cursor;
1294 
1295   columns_cursor = grn_hash_cursor_open(ctx, columns,
1296                                         NULL, 0, NULL, 0, 0, -1, 0);
1297   if (!columns_cursor) {
1298     return;
1299   }
1300 
1301   while (grn_hash_cursor_next(ctx, columns_cursor) != GRN_ID_NIL) {
1302     grn_column_data *column_data;
1303     grn_obj *column;
1304     grn_obj *expression;
1305     grn_obj *record;
1306 
1307     grn_hash_cursor_get_value(ctx, columns_cursor, (void **)&column_data);
1308 
1309     column = grn_column_create(ctx,
1310                                table,
1311                                column_data->label.value,
1312                                column_data->label.length,
1313                                NULL,
1314                                column_data->flags,
1315                                column_data->type);
1316     if (!column) {
1317       GRN_PLUGIN_ERROR(ctx,
1318                        GRN_INVALID_ARGUMENT,
1319                        "[select][column][%s][%.*s] failed to create column: %s",
1320                        grn_column_stage_name(column_data->stage),
1321                        (int)(column_data->label.length),
1322                        column_data->label.value,
1323                        ctx->errbuf);
1324       break;
1325     }
1326 
1327     GRN_EXPR_CREATE_FOR_QUERY(ctx, table, expression, record);
1328     if (!expression) {
1329       grn_obj_close(ctx, column);
1330       GRN_PLUGIN_ERROR(ctx,
1331                        GRN_INVALID_ARGUMENT,
1332                        "[select][column][%s][%.*s] "
1333                        "failed to create expression to compute value: %s",
1334                        grn_column_stage_name(column_data->stage),
1335                        (int)(column_data->label.length),
1336                        column_data->label.value,
1337                        ctx->errbuf);
1338       break;
1339     }
1340     grn_expr_parse(ctx,
1341                    expression,
1342                    column_data->value.value,
1343                    column_data->value.length,
1344                    NULL,
1345                    GRN_OP_MATCH,
1346                    GRN_OP_AND,
1347                    GRN_EXPR_SYNTAX_SCRIPT);
1348     if (ctx->rc != GRN_SUCCESS) {
1349       grn_obj_close(ctx, expression);
1350       grn_obj_close(ctx, column);
1351       GRN_PLUGIN_ERROR(ctx,
1352                        GRN_INVALID_ARGUMENT,
1353                        "[select][column][%s][%.*s] "
1354                        "failed to parse value: <%.*s>: %s",
1355                        grn_column_stage_name(column_data->stage),
1356                        (int)(column_data->label.length),
1357                        column_data->label.value,
1358                        (int)(column_data->value.length),
1359                        column_data->value.value,
1360                        ctx->errbuf);
1361       break;
1362     }
1363     grn_select_expression_set_condition(ctx,
1364                                         expression,
1365                                         data->filter.condition.expression);
1366 
1367     if (column_data->window.sort_keys.length > 0 ||
1368         column_data->window.group_keys.length > 0) {
1369       grn_window_definition definition;
1370       grn_rc rc;
1371 
1372       if (column_data->window.sort_keys.length > 0) {
1373         int n_sort_keys;
1374         definition.sort_keys =
1375           grn_table_sort_key_from_str(ctx,
1376                                       column_data->window.sort_keys.value,
1377                                       column_data->window.sort_keys.length,
1378                                       table, &n_sort_keys);
1379         definition.n_sort_keys = n_sort_keys;
1380         if (!definition.sort_keys) {
1381           grn_obj_close(ctx, expression);
1382           grn_obj_close(ctx, column);
1383           GRN_PLUGIN_ERROR(ctx,
1384                            GRN_INVALID_ARGUMENT,
1385                            "[select][column][%s][%.*s] "
1386                            "failed to parse sort keys: %s",
1387                            grn_column_stage_name(column_data->stage),
1388                            (int)(column_data->label.length),
1389                            column_data->label.value,
1390                            ctx->errbuf);
1391           break;
1392         }
1393       } else {
1394         definition.sort_keys = NULL;
1395         definition.n_sort_keys = 0;
1396       }
1397 
1398       if (column_data->window.group_keys.length > 0) {
1399         int n_group_keys;
1400         definition.group_keys =
1401           grn_table_sort_key_from_str(ctx,
1402                                       column_data->window.group_keys.value,
1403                                       column_data->window.group_keys.length,
1404                                       table, &n_group_keys);
1405         definition.n_group_keys = n_group_keys;
1406         if (!definition.group_keys) {
1407           grn_obj_close(ctx, expression);
1408           grn_obj_close(ctx, column);
1409           if (definition.sort_keys) {
1410             grn_table_sort_key_close(ctx,
1411                                      definition.sort_keys,
1412                                      definition.n_sort_keys);
1413           }
1414           GRN_PLUGIN_ERROR(ctx,
1415                            GRN_INVALID_ARGUMENT,
1416                            "[select][column][%s][%.*s] "
1417                            "failed to parse group keys: %s",
1418                            grn_column_stage_name(column_data->stage),
1419                            (int)(column_data->label.length),
1420                            column_data->label.value,
1421                            ctx->errbuf);
1422           break;
1423         }
1424       } else {
1425         definition.group_keys = NULL;
1426         definition.n_group_keys = 0;
1427       }
1428 
1429       rc = grn_table_apply_window_function(ctx,
1430                                            table,
1431                                            column,
1432                                            &definition,
1433                                            expression);
1434       if (definition.sort_keys) {
1435         grn_table_sort_key_close(ctx,
1436                                  definition.sort_keys,
1437                                  definition.n_sort_keys);
1438       }
1439       if (definition.group_keys) {
1440         grn_table_sort_key_close(ctx,
1441                                  definition.group_keys,
1442                                  definition.n_group_keys);
1443       }
1444       if (rc != GRN_SUCCESS) {
1445         grn_obj_close(ctx, expression);
1446         grn_obj_close(ctx, column);
1447         break;
1448       }
1449     } else {
1450       grn_rc rc;
1451       rc = grn_table_apply_expr(ctx, table, column, expression);
1452       if (rc != GRN_SUCCESS) {
1453         grn_obj_close(ctx, expression);
1454         grn_obj_close(ctx, column);
1455         GRN_PLUGIN_ERROR(ctx,
1456                          GRN_INVALID_ARGUMENT,
1457                          "[select][column][%s][%.*s] "
1458                          "failed to apply expression to generate column values: "
1459                          "%s",
1460                          grn_column_stage_name(column_data->stage),
1461                          (int)(column_data->label.length),
1462                          column_data->label.value,
1463                          ctx->errbuf);
1464         break;
1465       }
1466     }
1467 
1468     grn_obj_close(ctx, expression);
1469 
1470     GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
1471                   ":", "columns[%.*s](%d)",
1472                   (int)(column_data->label.length),
1473                   column_data->label.value,
1474                   grn_table_size(ctx, table));
1475   }
1476 
1477   grn_hash_cursor_close(ctx, columns_cursor);
1478 }
1479 
1480 static grn_bool
grn_select_apply_initial_columns(grn_ctx * ctx,grn_select_data * data)1481 grn_select_apply_initial_columns(grn_ctx *ctx,
1482                                  grn_select_data *data)
1483 {
1484   if (!data->columns.initial) {
1485     return GRN_TRUE;
1486   }
1487 
1488   data->tables.initial =
1489     grn_select_create_all_selected_result_table(ctx, data->tables.target);
1490   if (!data->tables.initial) {
1491     return GRN_FALSE;
1492   }
1493 
1494   grn_select_apply_columns(ctx,
1495                            data,
1496                            data->tables.initial,
1497                            data->columns.initial);
1498 
1499   return ctx->rc == GRN_SUCCESS;
1500 }
1501 
1502 static grn_bool
grn_select_filter(grn_ctx * ctx,grn_select_data * data)1503 grn_select_filter(grn_ctx *ctx,
1504                   grn_select_data *data)
1505 {
1506   if (!grn_filter_data_execute(ctx,
1507                                &(data->filter),
1508                                data->tables.initial,
1509                                "[select]")) {
1510     return GRN_FALSE;
1511   }
1512 
1513   data->tables.result = data->filter.filtered;
1514   if (!data->tables.result) {
1515     data->tables.result = data->tables.initial;
1516   }
1517 
1518   {
1519     grn_expr *expression;
1520     expression = (grn_expr *)(data->filter.condition.expression);
1521     if (expression) {
1522       data->cacheable *= expression->cacheable;
1523       data->taintable += expression->taintable;
1524     }
1525   }
1526 
1527   return GRN_TRUE;
1528 }
1529 
1530 static grn_bool
grn_select_apply_filtered_columns(grn_ctx * ctx,grn_select_data * data)1531 grn_select_apply_filtered_columns(grn_ctx *ctx,
1532                                   grn_select_data *data)
1533 {
1534   if (!data->columns.filtered) {
1535     return GRN_TRUE;
1536   }
1537 
1538   if (data->tables.result == data->tables.initial) {
1539     data->tables.result =
1540       grn_select_create_all_selected_result_table(ctx, data->tables.initial);
1541     if (!data->tables.result) {
1542       return GRN_FALSE;
1543     }
1544   }
1545 
1546   grn_select_apply_columns(ctx,
1547                            data,
1548                            data->tables.result,
1549                            data->columns.filtered);
1550 
1551   return ctx->rc == GRN_SUCCESS;
1552 }
1553 
1554 static int
grn_select_apply_adjuster_execute_ensure_factor(grn_ctx * ctx,grn_obj * factor_object)1555 grn_select_apply_adjuster_execute_ensure_factor(grn_ctx *ctx,
1556                                                 grn_obj *factor_object)
1557 {
1558   if (!factor_object) {
1559     return 1;
1560   } else if (factor_object->header.domain == GRN_DB_INT32) {
1561     return GRN_INT32_VALUE(factor_object);
1562   } else {
1563     grn_rc rc;
1564     grn_obj int32_object;
1565     int factor;
1566     GRN_INT32_INIT(&int32_object, 0);
1567     rc = grn_obj_cast(ctx, factor_object, &int32_object, GRN_FALSE);
1568     if (rc == GRN_SUCCESS) {
1569       factor = GRN_INT32_VALUE(&int32_object);
1570     } else {
1571       /* TODO: Log or return error? */
1572       factor = 1;
1573     }
1574     GRN_OBJ_FIN(ctx, &int32_object);
1575     return factor;
1576   }
1577 }
1578 
1579 static void
grn_select_apply_adjuster_execute_adjust(grn_ctx * ctx,grn_obj * table,grn_obj * column,grn_obj * value,grn_obj * factor)1580 grn_select_apply_adjuster_execute_adjust(grn_ctx *ctx,
1581                                          grn_obj *table,
1582                                          grn_obj *column,
1583                                          grn_obj *value,
1584                                          grn_obj *factor)
1585 {
1586   grn_obj *index;
1587   unsigned int n_indexes;
1588   int factor_value;
1589 
1590   n_indexes = grn_column_index(ctx, column, GRN_OP_MATCH, &index, 1, NULL);
1591   if (n_indexes == 0) {
1592     char column_name[GRN_TABLE_MAX_KEY_SIZE];
1593     int column_name_size;
1594     column_name_size = grn_obj_name(ctx, column,
1595                                     column_name, GRN_TABLE_MAX_KEY_SIZE);
1596     GRN_PLUGIN_ERROR(ctx,
1597                      GRN_INVALID_ARGUMENT,
1598                      "adjuster requires index column for the target column: "
1599                      "<%.*s>",
1600                      column_name_size,
1601                      column_name);
1602     return;
1603   }
1604 
1605   factor_value = grn_select_apply_adjuster_execute_ensure_factor(ctx, factor);
1606 
1607   {
1608     grn_search_optarg options;
1609     memset(&options, 0, sizeof(grn_search_optarg));
1610 
1611     options.mode = GRN_OP_EXACT;
1612     options.similarity_threshold = 0;
1613     options.max_interval = 0;
1614     options.weight_vector = NULL;
1615     options.vector_size = factor_value;
1616     options.proc = NULL;
1617     options.max_size = 0;
1618     options.scorer = NULL;
1619 
1620     grn_obj_search(ctx, index, value, table, GRN_OP_ADJUST, &options);
1621   }
1622 }
1623 
1624 static void
grn_select_apply_adjuster_execute(grn_ctx * ctx,grn_obj * table,grn_obj * adjuster)1625 grn_select_apply_adjuster_execute(grn_ctx *ctx,
1626                                   grn_obj *table,
1627                                   grn_obj *adjuster)
1628 {
1629   grn_expr *expr = (grn_expr *)adjuster;
1630   grn_expr_code *code, *code_end;
1631 
1632   code = expr->codes;
1633   code_end = expr->codes + expr->codes_curr;
1634   while (code < code_end) {
1635     grn_obj *column, *value, *factor;
1636 
1637     if (code->op == GRN_OP_PLUS) {
1638       code++;
1639       continue;
1640     }
1641 
1642     column = code->value;
1643     code++;
1644     value = code->value;
1645     code++;
1646     code++; /* op == GRN_OP_MATCH */
1647     if ((code_end - code) >= 2 && code[1].op == GRN_OP_STAR) {
1648       factor = code->value;
1649       code++;
1650       code++; /* op == GRN_OP_STAR */
1651     } else {
1652       factor = NULL;
1653     }
1654     grn_select_apply_adjuster_execute_adjust(ctx, table, column, value, factor);
1655   }
1656 }
1657 
1658 static grn_bool
grn_select_apply_adjuster(grn_ctx * ctx,grn_select_data * data)1659 grn_select_apply_adjuster(grn_ctx *ctx,
1660                           grn_select_data *data)
1661 {
1662   grn_obj *adjuster;
1663   grn_obj *record;
1664   grn_rc rc;
1665 
1666   if (data->adjuster.length == 0) {
1667     return GRN_TRUE;
1668   }
1669 
1670   GRN_EXPR_CREATE_FOR_QUERY(ctx, data->tables.target, adjuster, record);
1671   if (!adjuster) {
1672     GRN_PLUGIN_ERROR(ctx,
1673                      GRN_INVALID_ARGUMENT,
1674                      "[select][adjuster] "
1675                      "failed to create expression: %s",
1676                      ctx->errbuf);
1677     return GRN_FALSE;
1678   }
1679 
1680   rc = grn_expr_parse(ctx, adjuster,
1681                       data->adjuster.value,
1682                       data->adjuster.length,
1683                       NULL,
1684                       GRN_OP_MATCH, GRN_OP_ADJUST,
1685                       GRN_EXPR_SYNTAX_ADJUSTER);
1686   if (rc != GRN_SUCCESS) {
1687     grn_obj_unlink(ctx, adjuster);
1688     GRN_PLUGIN_ERROR(ctx,
1689                      rc,
1690                      "[select][adjuster] "
1691                      "failed to parse: %s",
1692                      ctx->errbuf);
1693     return GRN_FALSE;
1694   }
1695 
1696   data->cacheable *= ((grn_expr *)adjuster)->cacheable;
1697   data->taintable += ((grn_expr *)adjuster)->taintable;
1698   grn_select_apply_adjuster_execute(ctx, data->tables.result, adjuster);
1699   grn_obj_unlink(ctx, adjuster);
1700 
1701   GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
1702                 ":", "adjust(%d)", grn_table_size(ctx, data->tables.result));
1703 
1704   return GRN_TRUE;
1705 }
1706 
1707 static grn_bool
grn_select_apply_scorer(grn_ctx * ctx,grn_select_data * data)1708 grn_select_apply_scorer(grn_ctx *ctx,
1709                         grn_select_data *data)
1710 {
1711   grn_obj *scorer;
1712   grn_obj *record;
1713   grn_rc rc = GRN_SUCCESS;
1714 
1715   if (data->scorer.length == 0) {
1716     return GRN_TRUE;
1717   }
1718 
1719   GRN_EXPR_CREATE_FOR_QUERY(ctx, data->tables.result, scorer, record);
1720   if (!scorer) {
1721     GRN_PLUGIN_ERROR(ctx,
1722                      GRN_INVALID_ARGUMENT,
1723                      "[select][scorer] "
1724                      "failed to create expression: %s",
1725                      ctx->errbuf);
1726     return GRN_FALSE;
1727   }
1728 
1729   rc = grn_expr_parse(ctx,
1730                       scorer,
1731                       data->scorer.value,
1732                       data->scorer.length,
1733                       NULL,
1734                       GRN_OP_MATCH,
1735                       GRN_OP_AND,
1736                       GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
1737   if (rc != GRN_SUCCESS) {
1738     grn_obj_unlink(ctx, scorer);
1739     GRN_PLUGIN_ERROR(ctx,
1740                      GRN_INVALID_ARGUMENT,
1741                      "[select][scorer] "
1742                      "failed to parse: %s",
1743                      ctx->errbuf);
1744     return GRN_FALSE;
1745   }
1746 
1747   data->cacheable *= ((grn_expr *)scorer)->cacheable;
1748   data->taintable += ((grn_expr *)scorer)->taintable;
1749   GRN_TABLE_EACH_BEGIN(ctx, data->tables.result, cursor, id) {
1750     GRN_RECORD_SET(ctx, record, id);
1751     grn_expr_exec(ctx, scorer, 0);
1752     if (ctx->rc) {
1753       rc = ctx->rc;
1754       GRN_PLUGIN_ERROR(ctx,
1755                        rc,
1756                        "[select][scorer] "
1757                        "failed to execute: <%.*s>: %s",
1758                        (int)(data->scorer.length),
1759                        data->scorer.value,
1760                        ctx->errbuf);
1761       break;
1762     }
1763   } GRN_TABLE_EACH_END(ctx, cursor);
1764   grn_obj_unlink(ctx, scorer);
1765 
1766   GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
1767                 ":", "score(%d)", grn_table_size(ctx, data->tables.result));
1768 
1769   return rc == GRN_SUCCESS;
1770 }
1771 
1772 static grn_bool
grn_select_sort(grn_ctx * ctx,grn_select_data * data)1773 grn_select_sort(grn_ctx *ctx,
1774                 grn_select_data *data)
1775 {
1776   grn_table_sort_key *keys;
1777   uint32_t n_keys;
1778 
1779   if (data->sort_keys.length == 0) {
1780     return GRN_TRUE;
1781   }
1782 
1783   keys = grn_table_sort_key_from_str(ctx,
1784                                      data->sort_keys.value,
1785                                      data->sort_keys.length,
1786                                      data->tables.result,
1787                                      &n_keys);
1788   if (!keys) {
1789     if (ctx->rc == GRN_SUCCESS) {
1790       return GRN_TRUE;
1791     } else {
1792       GRN_PLUGIN_ERROR(ctx,
1793                        ctx->rc,
1794                        "[select][sort] "
1795                        "failed to parse: <%.*s>: %s",
1796                        (int)(data->sort_keys.length),
1797                        data->sort_keys.value,
1798                        ctx->errbuf);
1799       return GRN_FALSE;
1800     }
1801   }
1802 
1803   data->tables.sorted = grn_table_create(ctx, NULL, 0, NULL,
1804                                          GRN_OBJ_TABLE_NO_KEY,
1805                                          NULL,
1806                                          data->tables.result);
1807   if (!data->tables.sorted) {
1808     GRN_PLUGIN_ERROR(ctx,
1809                      ctx->rc,
1810                      "[select][sort] "
1811                      "failed to create table to store sorted record: "
1812                      "<%.*s>: %s",
1813                      (int)(data->sort_keys.length),
1814                      data->sort_keys.value,
1815                      ctx->errbuf);
1816     return GRN_FALSE;
1817   }
1818 
1819   grn_table_sort(ctx,
1820                  data->tables.result,
1821                  data->offset,
1822                  data->limit,
1823                  data->tables.sorted,
1824                  keys,
1825                  n_keys);
1826 
1827   grn_table_sort_key_close(ctx, keys, n_keys);
1828 
1829   GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
1830                 ":", "sort(%d)", data->limit);
1831 
1832   return ctx->rc == GRN_SUCCESS;
1833 }
1834 
1835 static grn_bool
grn_select_apply_output_columns(grn_ctx * ctx,grn_select_data * data)1836 grn_select_apply_output_columns(grn_ctx *ctx,
1837                                 grn_select_data *data)
1838 {
1839   if (!data->columns.output) {
1840     return GRN_TRUE;
1841   }
1842 
1843   if (!data->tables.sorted) {
1844     data->tables.sorted =
1845       grn_select_create_no_sort_keys_sorted_table(ctx,
1846                                                   data,
1847                                                   data->tables.result);
1848     if (!data->tables.sorted) {
1849       return GRN_FALSE;
1850     }
1851   }
1852 
1853   grn_select_apply_columns(ctx,
1854                            data,
1855                            data->tables.sorted,
1856                            data->columns.output);
1857 
1858   return ctx->rc == GRN_SUCCESS;
1859 }
1860 
1861 static grn_bool
grn_select_output_match_open(grn_ctx * ctx,grn_select_data * data,grn_obj_format * format,uint32_t n_additional_elements)1862 grn_select_output_match_open(grn_ctx *ctx,
1863                              grn_select_data *data,
1864                              grn_obj_format *format,
1865                              uint32_t n_additional_elements)
1866 {
1867   grn_bool succeeded = GRN_TRUE;
1868   int offset;
1869   grn_obj *output_table;
1870 
1871   if (data->tables.sorted) {
1872     offset = 0;
1873     output_table = data->tables.sorted;
1874   } else {
1875     offset = data->offset;
1876     output_table = data->tables.result;
1877   }
1878   succeeded =
1879     grn_proc_select_output_columns_open(ctx,
1880                                         format,
1881                                         output_table,
1882                                         grn_table_size(ctx, data->tables.result),
1883                                         offset,
1884                                         data->limit,
1885                                         data->output_columns.value,
1886                                         data->output_columns.length,
1887                                         data->filter.condition.expression,
1888                                         n_additional_elements);
1889   GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
1890                 ":", "output(%d)", data->limit);
1891 
1892   return succeeded;
1893 }
1894 
1895 static grn_bool
grn_select_output_match_close(grn_ctx * ctx,grn_select_data * data,grn_obj_format * format)1896 grn_select_output_match_close(grn_ctx *ctx,
1897                               grn_select_data *data,
1898                               grn_obj_format *format)
1899 {
1900   grn_obj *output_table;
1901 
1902   if (data->tables.sorted) {
1903     output_table = data->tables.sorted;
1904   } else {
1905     output_table = data->tables.result;
1906   }
1907 
1908   return grn_proc_select_output_columns_close(ctx, format, output_table);
1909 }
1910 
1911 static grn_bool
grn_select_output_match(grn_ctx * ctx,grn_select_data * data)1912 grn_select_output_match(grn_ctx *ctx, grn_select_data *data)
1913 {
1914   grn_obj_format format;
1915   uint32_t n_additional_elements = 0;
1916 
1917   if (!grn_select_output_match_open(ctx, data, &format, n_additional_elements)) {
1918     return GRN_FALSE;
1919   }
1920 
1921   return grn_select_output_match_close(ctx, data, &format);
1922 }
1923 
1924 static grn_bool
grn_select_slice_execute(grn_ctx * ctx,grn_select_data * data,grn_obj * table,grn_slice_data * slice)1925 grn_select_slice_execute(grn_ctx *ctx,
1926                          grn_select_data *data,
1927                          grn_obj *table,
1928                          grn_slice_data *slice)
1929 {
1930   char tag[GRN_TABLE_MAX_KEY_SIZE];
1931   grn_filter_data *filter;
1932 
1933   grn_snprintf(tag, GRN_TABLE_MAX_KEY_SIZE, GRN_TABLE_MAX_KEY_SIZE,
1934                "[select][slices][%.*s]",
1935                (int)(slice->label.length),
1936                slice->label.value);
1937   filter = &(slice->filter);
1938   if (filter->query.length == 0 && filter->filter.length == 0) {
1939     GRN_PLUGIN_ERROR(ctx,
1940                      GRN_INVALID_ARGUMENT,
1941                      "%s slice requires query or filter",
1942                      tag);
1943     return GRN_FALSE;
1944   }
1945 
1946   if (!grn_filter_data_execute(ctx, filter, table, tag)) {
1947     return GRN_FALSE;
1948   }
1949 
1950   slice->table = filter->filtered;
1951 
1952   return GRN_TRUE;
1953 }
1954 
1955 static grn_bool
grn_select_slices_execute(grn_ctx * ctx,grn_select_data * data,grn_obj * table,grn_hash * slices)1956 grn_select_slices_execute(grn_ctx *ctx,
1957                           grn_select_data *data,
1958                           grn_obj *table,
1959                           grn_hash *slices)
1960 {
1961   grn_bool succeeded = GRN_TRUE;
1962 
1963   GRN_HASH_EACH_BEGIN(ctx, slices, cursor, id) {
1964     grn_slice_data *slice;
1965 
1966     grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
1967     if (!grn_select_slice_execute(ctx, data, table, slice)) {
1968       succeeded = GRN_FALSE;
1969       break;
1970     }
1971   } GRN_HASH_EACH_END(ctx, cursor);
1972 
1973   return succeeded;
1974 }
1975 
1976 static grn_bool
grn_select_prepare_slices(grn_ctx * ctx,grn_select_data * data)1977 grn_select_prepare_slices(grn_ctx *ctx,
1978                           grn_select_data *data)
1979 {
1980   if (!data->slices) {
1981     return GRN_TRUE;
1982   }
1983 
1984   if (!grn_select_slices_execute(ctx, data, data->tables.result, data->slices)) {
1985     return GRN_FALSE;
1986   }
1987 
1988   data->output.n_elements += 1;
1989 
1990   return GRN_TRUE;
1991 }
1992 
1993 static grn_bool
grn_select_output_slices(grn_ctx * ctx,grn_select_data * data)1994 grn_select_output_slices(grn_ctx *ctx,
1995                          grn_select_data *data)
1996 {
1997   grn_bool succeeded = GRN_TRUE;
1998   unsigned int n_available_results = 0;
1999 
2000   if (!data->slices) {
2001     return GRN_TRUE;
2002   }
2003 
2004   data->output.formatter->slices_label(ctx, data);
2005 
2006   GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
2007     grn_slice_data *slice;
2008 
2009     grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
2010     if (slice->table) {
2011       n_available_results++;
2012     }
2013   } GRN_HASH_EACH_END(ctx, cursor);
2014 
2015   data->output.formatter->slices_open(ctx, data, n_available_results);
2016 
2017   GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
2018     grn_slice_data *slice;
2019     uint32_t n_hits;
2020     int offset;
2021     int limit;
2022 
2023     grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
2024     if (!slice->table) {
2025       continue;
2026     }
2027 
2028     n_hits = grn_table_size(ctx, slice->table);
2029 
2030     offset = slice->offset;
2031     limit = slice->limit;
2032     grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit);
2033 
2034     if (slice->sort_keys.length > 0) {
2035       grn_table_sort_key *sort_keys;
2036       uint32_t n_sort_keys;
2037       sort_keys = grn_table_sort_key_from_str(ctx,
2038                                               slice->sort_keys.value,
2039                                               slice->sort_keys.length,
2040                                               slice->table, &n_sort_keys);
2041       if (sort_keys) {
2042         grn_obj *sorted;
2043         sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY,
2044                                   NULL, slice->table);
2045         if (sorted) {
2046           grn_table_sort(ctx, slice->table, offset, limit,
2047                          sorted, sort_keys, n_sort_keys);
2048           data->output.formatter->slice_label(ctx, data, slice);
2049           if (!grn_proc_select_output_columns(ctx,
2050                                               sorted,
2051                                               n_hits,
2052                                               0,
2053                                               limit,
2054                                               slice->output_columns.value,
2055                                               slice->output_columns.length,
2056                                               slice->filter.condition.expression)) {
2057             succeeded = GRN_FALSE;
2058           }
2059           grn_obj_unlink(ctx, sorted);
2060         }
2061         grn_table_sort_key_close(ctx, sort_keys, n_sort_keys);
2062       } else {
2063         succeeded = GRN_FALSE;
2064       }
2065     } else {
2066       data->output.formatter->slice_label(ctx, data, slice);
2067       if (!grn_proc_select_output_columns(ctx,
2068                                           slice->table,
2069                                           n_hits,
2070                                           offset,
2071                                           limit,
2072                                           slice->output_columns.value,
2073                                           slice->output_columns.length,
2074                                           slice->filter.condition.expression)) {
2075         succeeded = GRN_FALSE;
2076       }
2077     }
2078 
2079     if (!succeeded) {
2080       break;
2081     }
2082 
2083     GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
2084                   ":", "slice(%d)[%.*s]",
2085                   n_hits,
2086                   (int)(slice->label.length),
2087                   slice->label.value);
2088   } GRN_HASH_EACH_END(ctx, cursor);
2089 
2090   data->output.formatter->slices_close(ctx, data);
2091 
2092   return succeeded;
2093 }
2094 
2095 static grn_bool
grn_select_drilldown_execute(grn_ctx * ctx,grn_select_data * data,grn_obj * table,grn_hash * drilldowns,grn_id id)2096 grn_select_drilldown_execute(grn_ctx *ctx,
2097                              grn_select_data *data,
2098                              grn_obj *table,
2099                              grn_hash *drilldowns,
2100                              grn_id id)
2101 {
2102   grn_table_sort_key *keys = NULL;
2103   unsigned int n_keys = 0;
2104   grn_obj *target_table = table;
2105   grn_drilldown_data *drilldown;
2106   grn_table_group_result *result;
2107 
2108   drilldown =
2109     (grn_drilldown_data *)grn_hash_get_value_(ctx, drilldowns, id, NULL);
2110   result = &(drilldown->result);
2111 
2112   result->limit = 1;
2113   result->flags = GRN_TABLE_GROUP_CALC_COUNT;
2114   result->op = 0;
2115   result->max_n_subrecs = 0;
2116   result->key_begin = 0;
2117   result->key_end = 0;
2118   if (result->calc_target) {
2119     grn_obj_unlink(ctx, result->calc_target);
2120   }
2121   result->calc_target = NULL;
2122 
2123   if (drilldown->table_name.length > 0) {
2124     grn_id dependent_id;
2125     dependent_id = grn_hash_get(ctx,
2126                                 drilldowns,
2127                                 drilldown->table_name.value,
2128                                 drilldown->table_name.length,
2129                                 NULL);
2130     if (dependent_id == GRN_ID_NIL) {
2131       if (data->slices) {
2132         grn_slice_data *slice;
2133         dependent_id = grn_hash_get(ctx,
2134                                     data->slices,
2135                                     drilldown->table_name.value,
2136                                     drilldown->table_name.length,
2137                                     NULL);
2138         if (dependent_id) {
2139           slice =
2140             (grn_slice_data *)grn_hash_get_value_(ctx, data->slices,
2141                                                   dependent_id, NULL);
2142           target_table = slice->table;
2143         }
2144       }
2145       if (dependent_id == GRN_ID_NIL) {
2146         GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
2147                          "[select][drilldowns][%.*s][table] "
2148                          "nonexistent label: <%.*s>",
2149                          (int)(drilldown->label.length),
2150                          drilldown->label.value,
2151                          (int)(drilldown->table_name.length),
2152                          drilldown->table_name.value);
2153         return GRN_FALSE;
2154       }
2155     } else {
2156       grn_drilldown_data *dependent_drilldown;
2157       grn_table_group_result *dependent_result;
2158 
2159       dependent_drilldown =
2160         (grn_drilldown_data *)grn_hash_get_value_(ctx,
2161                                                   drilldowns,
2162                                                   dependent_id,
2163                                                   NULL);
2164       dependent_result = &(dependent_drilldown->result);
2165       target_table = dependent_result->table;
2166     }
2167   }
2168 
2169   if (drilldown->parsed_keys) {
2170     result->key_end = drilldown->n_parsed_keys;
2171   } else if (drilldown->keys.length > 0) {
2172     keys = grn_table_sort_key_from_str(ctx,
2173                                        drilldown->keys.value,
2174                                        drilldown->keys.length,
2175                                        target_table, &n_keys);
2176     if (!keys) {
2177       GRN_PLUGIN_CLEAR_ERROR(ctx);
2178       return GRN_FALSE;
2179     }
2180 
2181     result->key_end = n_keys - 1;
2182     if (n_keys > 1) {
2183       result->max_n_subrecs = 1;
2184     }
2185   }
2186 
2187   if (drilldown->calc_target_name.length > 0) {
2188     result->calc_target = grn_obj_column(ctx, target_table,
2189                                          drilldown->calc_target_name.value,
2190                                          drilldown->calc_target_name.length);
2191   }
2192   if (result->calc_target) {
2193     result->flags |= drilldown->calc_types;
2194   }
2195 
2196   if (drilldown->parsed_keys) {
2197     grn_table_group(ctx,
2198                     target_table,
2199                     drilldown->parsed_keys,
2200                     drilldown->n_parsed_keys,
2201                     result,
2202                     1);
2203   } else {
2204     grn_table_group(ctx, target_table, keys, n_keys, result, 1);
2205   }
2206 
2207   if (keys) {
2208     grn_table_sort_key_close(ctx, keys, n_keys);
2209   }
2210 
2211   if (!result->table) {
2212     return GRN_FALSE;
2213   }
2214 
2215   if (drilldown->columns.initial) {
2216     grn_select_apply_columns(ctx,
2217                              data,
2218                              result->table,
2219                              drilldown->columns.initial);
2220   }
2221 
2222   if (drilldown->filter.length > 0) {
2223     grn_obj *expression;
2224     grn_obj *record;
2225     GRN_EXPR_CREATE_FOR_QUERY(ctx, result->table, expression, record);
2226     if (!expression) {
2227       GRN_PLUGIN_ERROR(ctx,
2228                        GRN_INVALID_ARGUMENT,
2229                        "[select][drilldowns]%s%.*s%s[filter] "
2230                        "failed to create expression for filter: %s",
2231                        drilldown->label.length > 0 ? "[" : "",
2232                        (int)(drilldown->label.length),
2233                        drilldown->label.value,
2234                        drilldown->label.length > 0 ? "]" : "",
2235                        ctx->errbuf);
2236       return GRN_FALSE;
2237     }
2238     grn_expr_parse(ctx,
2239                    expression,
2240                    drilldown->filter.value,
2241                    drilldown->filter.length,
2242                    NULL,
2243                    GRN_OP_MATCH,
2244                    GRN_OP_AND,
2245                    GRN_EXPR_SYNTAX_SCRIPT);
2246     if (ctx->rc != GRN_SUCCESS) {
2247       grn_obj_close(ctx, expression);
2248       GRN_PLUGIN_ERROR(ctx,
2249                        GRN_INVALID_ARGUMENT,
2250                        "[select][drilldowns]%s%.*s%s[filter] "
2251                        "failed to parse filter: <%.*s>: %s",
2252                        drilldown->label.length > 0 ? "[" : "",
2253                        (int)(drilldown->label.length),
2254                        drilldown->label.value,
2255                        drilldown->label.length > 0 ? "]" : "",
2256                        (int)(drilldown->filter.length),
2257                        drilldown->filter.value,
2258                        ctx->errbuf);
2259       return GRN_FALSE;
2260     }
2261     drilldown->filtered_result = grn_table_select(ctx,
2262                                                   result->table,
2263                                                   expression,
2264                                                   NULL,
2265                                                   GRN_OP_OR);
2266     if (ctx->rc != GRN_SUCCESS) {
2267       grn_obj_close(ctx, expression);
2268       if (drilldown->filtered_result) {
2269         grn_obj_close(ctx, drilldown->filtered_result);
2270         drilldown->filtered_result = NULL;
2271       }
2272       GRN_PLUGIN_ERROR(ctx,
2273                        GRN_INVALID_ARGUMENT,
2274                        "[select][drilldowns]%s%.*s%s[filter] "
2275                        "failed to execute filter: <%.*s>: %s",
2276                        drilldown->label.length > 0 ? "[" : "",
2277                        (int)(drilldown->label.length),
2278                        drilldown->label.value,
2279                        drilldown->label.length > 0 ? "]" : "",
2280                        (int)(drilldown->filter.length),
2281                        drilldown->filter.value,
2282                        ctx->errbuf);
2283       return GRN_FALSE;
2284     }
2285     grn_obj_close(ctx, expression);
2286   }
2287 
2288   {
2289     unsigned int n_hits;
2290 
2291     if (drilldown->filtered_result) {
2292       n_hits = grn_table_size(ctx, drilldown->filtered_result);
2293     } else {
2294       n_hits = grn_table_size(ctx, result->table);
2295     }
2296     if (data->drilldown.keys.length == 0) {
2297       GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
2298                     ":", "drilldowns[%.*s](%u)",
2299                     (int)(drilldown->label.length),
2300                     drilldown->label.value,
2301                     n_hits);
2302     } else {
2303       GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
2304                     ":", "drilldown(%u)",
2305                     n_hits);
2306     }
2307   }
2308 
2309   return GRN_TRUE;
2310 }
2311 
2312 typedef enum {
2313   TSORT_STATUS_NOT_VISITED,
2314   TSORT_STATUS_VISITING,
2315   TSORT_STATUS_VISITED
2316 } tsort_status;
2317 
2318 static grn_bool
drilldown_tsort_visit(grn_ctx * ctx,grn_hash * drilldowns,tsort_status * statuses,grn_obj * ids,grn_id id)2319 drilldown_tsort_visit(grn_ctx *ctx,
2320                       grn_hash *drilldowns,
2321                       tsort_status *statuses,
2322                       grn_obj *ids,
2323                       grn_id id)
2324 {
2325   grn_bool cycled = GRN_TRUE;
2326   uint32_t index = id - 1;
2327 
2328   switch (statuses[index]) {
2329   case TSORT_STATUS_VISITING :
2330     cycled = GRN_TRUE;
2331     break;
2332   case TSORT_STATUS_VISITED :
2333     cycled = GRN_FALSE;
2334     break;
2335   case TSORT_STATUS_NOT_VISITED :
2336     cycled = GRN_FALSE;
2337     statuses[index] = TSORT_STATUS_VISITING;
2338     {
2339       grn_drilldown_data *drilldown;
2340       drilldown =
2341         (grn_drilldown_data *)grn_hash_get_value_(ctx, drilldowns, id, NULL);
2342       if (drilldown->table_name.length > 0) {
2343         grn_id dependent_id;
2344         dependent_id = grn_hash_get(ctx, drilldowns,
2345                                     drilldown->table_name.value,
2346                                     drilldown->table_name.length,
2347                                     NULL);
2348         if (dependent_id != GRN_ID_NIL) {
2349           cycled = drilldown_tsort_visit(ctx,
2350                                          drilldowns,
2351                                          statuses,
2352                                          ids,
2353                                          dependent_id);
2354           if (cycled) {
2355             GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
2356                              "[select][drilldowns][%.*s][table] "
2357                              "cycled dependency: <%.*s>",
2358                              (int)(drilldown->label.length),
2359                              drilldown->label.value,
2360                              (int)(drilldown->table_name.length),
2361                              drilldown->table_name.value);
2362           }
2363         }
2364       }
2365     }
2366     if (!cycled) {
2367       statuses[index] = TSORT_STATUS_VISITED;
2368       GRN_RECORD_PUT(ctx, ids, id);
2369     }
2370     break;
2371   }
2372 
2373   return cycled;
2374 }
2375 
2376 static grn_bool
drilldown_tsort_body(grn_ctx * ctx,grn_hash * drilldowns,tsort_status * statuses,grn_obj * ids)2377 drilldown_tsort_body(grn_ctx *ctx,
2378                      grn_hash *drilldowns,
2379                      tsort_status *statuses,
2380                      grn_obj *ids)
2381 {
2382   grn_bool succeeded = GRN_TRUE;
2383 
2384   GRN_HASH_EACH_BEGIN(ctx, drilldowns, cursor, id) {
2385     if (drilldown_tsort_visit(ctx, drilldowns, statuses, ids, id)) {
2386       succeeded = GRN_FALSE;
2387       break;
2388     }
2389   } GRN_HASH_EACH_END(ctx, cursor);
2390 
2391   return succeeded;
2392 }
2393 
2394 static void
drilldown_tsort_init(grn_ctx * ctx,tsort_status * statuses,size_t n_statuses)2395 drilldown_tsort_init(grn_ctx *ctx,
2396                      tsort_status *statuses,
2397                      size_t n_statuses)
2398 {
2399   size_t i;
2400   for (i = 0; i < n_statuses; i++) {
2401     statuses[i] = TSORT_STATUS_NOT_VISITED;
2402   }
2403 }
2404 
2405 static grn_bool
drilldown_tsort(grn_ctx * ctx,grn_hash * drilldowns,grn_obj * ids)2406 drilldown_tsort(grn_ctx *ctx,
2407                 grn_hash *drilldowns,
2408                 grn_obj *ids)
2409 {
2410   tsort_status *statuses;
2411   size_t n_statuses;
2412   grn_bool succeeded;
2413 
2414   n_statuses = grn_hash_size(ctx, drilldowns);
2415   statuses = GRN_PLUGIN_MALLOCN(ctx, tsort_status, n_statuses);
2416   if (!statuses) {
2417     return GRN_FALSE;
2418   }
2419 
2420   drilldown_tsort_init(ctx, statuses, n_statuses);
2421   succeeded = drilldown_tsort_body(ctx, drilldowns, statuses, ids);
2422   GRN_PLUGIN_FREE(ctx, statuses);
2423   return succeeded;
2424 }
2425 
2426 static grn_bool
grn_select_drilldowns_execute(grn_ctx * ctx,grn_select_data * data)2427 grn_select_drilldowns_execute(grn_ctx *ctx,
2428                               grn_select_data *data)
2429 {
2430   grn_bool succeeded = GRN_TRUE;
2431   grn_obj tsorted_ids;
2432   size_t i;
2433   size_t n_drilldowns;
2434 
2435   GRN_RECORD_INIT(&tsorted_ids, GRN_OBJ_VECTOR, GRN_ID_NIL);
2436   if (!drilldown_tsort(ctx, data->drilldowns, &tsorted_ids)) {
2437     succeeded = GRN_FALSE;
2438     goto exit;
2439   }
2440 
2441   n_drilldowns = GRN_BULK_VSIZE(&tsorted_ids) / sizeof(grn_id);
2442   for (i = 0; i < n_drilldowns; i++) {
2443     grn_id id;
2444 
2445     id = GRN_RECORD_VALUE_AT(&tsorted_ids, i);
2446     if (!grn_select_drilldown_execute(ctx,
2447                                       data,
2448                                       data->tables.result,
2449                                       data->drilldowns,
2450                                       id)) {
2451       if (ctx->rc != GRN_SUCCESS) {
2452         succeeded = GRN_FALSE;
2453         break;
2454       }
2455     }
2456   }
2457 
2458 exit :
2459   GRN_OBJ_FIN(ctx, &tsorted_ids);
2460 
2461   return succeeded;
2462 }
2463 
2464 static grn_drilldown_data *
grn_select_data_drilldowns_add(grn_ctx * ctx,grn_select_data * data,const char * label,size_t label_len)2465 grn_select_data_drilldowns_add(grn_ctx *ctx,
2466                                grn_select_data *data,
2467                                const char *label,
2468                                size_t label_len)
2469 {
2470   grn_drilldown_data *drilldown = NULL;
2471   int added;
2472 
2473   if (!data->drilldowns) {
2474     data->drilldowns = grn_hash_create(ctx,
2475                                        NULL,
2476                                        GRN_TABLE_MAX_KEY_SIZE,
2477                                        sizeof(grn_drilldown_data),
2478                                        GRN_OBJ_TABLE_HASH_KEY |
2479                                        GRN_OBJ_KEY_VAR_SIZE |
2480                                        GRN_HASH_TINY);
2481     if (!data->drilldowns) {
2482       GRN_PLUGIN_ERROR(ctx,
2483                        GRN_INVALID_ARGUMENT,
2484                        "[select][drilldowns] "
2485                        "failed to allocate drilldowns data: %s",
2486                        ctx->errbuf);
2487       return NULL;
2488     }
2489   }
2490 
2491   grn_hash_add(ctx,
2492                data->drilldowns,
2493                label,
2494                label_len,
2495                (void **)&drilldown,
2496                &added);
2497   if (added) {
2498     grn_drilldown_data_init(ctx, drilldown, label, label_len);
2499   }
2500 
2501   return drilldown;
2502 }
2503 
2504 static grn_bool
grn_select_prepare_drilldowns(grn_ctx * ctx,grn_select_data * data)2505 grn_select_prepare_drilldowns(grn_ctx *ctx,
2506                               grn_select_data *data)
2507 {
2508   if (data->drilldown.keys.length > 0) {
2509     data->drilldown.parsed_keys =
2510       grn_table_sort_key_from_str(ctx,
2511                                   data->drilldown.keys.value,
2512                                   data->drilldown.keys.length,
2513                                   data->tables.result,
2514                                   &(data->drilldown.n_parsed_keys));
2515     if (data->drilldown.parsed_keys) {
2516       int i;
2517       grn_obj buffer;
2518 
2519       GRN_TEXT_INIT(&buffer, 0);
2520       for (i = 0; i < data->drilldown.n_parsed_keys; i++) {
2521         grn_drilldown_data *drilldown;
2522 
2523         GRN_BULK_REWIND(&buffer);
2524         grn_text_printf(ctx, &buffer, "drilldown%d", i);
2525         drilldown = grn_select_data_drilldowns_add(ctx,
2526                                                    data,
2527                                                    GRN_TEXT_VALUE(&buffer),
2528                                                    GRN_TEXT_LEN(&buffer));
2529         if (!drilldown) {
2530           continue;
2531         }
2532 
2533         drilldown->parsed_keys = data->drilldown.parsed_keys + i;
2534         drilldown->n_parsed_keys = 1;
2535 
2536 #define COPY(field)                                     \
2537         drilldown->field = data->drilldown.field
2538 
2539         COPY(sort_keys);
2540         COPY(output_columns);
2541         COPY(offset);
2542         COPY(limit);
2543         COPY(calc_types);
2544         COPY(calc_target_name);
2545         COPY(filter);
2546 
2547 #undef COPY
2548       }
2549     }
2550   }
2551 
2552   if (!data->drilldowns) {
2553     return GRN_TRUE;
2554   }
2555 
2556   if (!grn_select_drilldowns_execute(ctx, data)) {
2557     return GRN_FALSE;
2558   }
2559 
2560   {
2561     unsigned int n_available_results = 0;
2562 
2563     GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) {
2564       grn_drilldown_data *drilldown;
2565       grn_table_group_result *result;
2566 
2567       grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
2568       result = &(drilldown->result);
2569       if (result->table) {
2570         n_available_results++;
2571       }
2572     } GRN_HASH_EACH_END(ctx, cursor);
2573 
2574     if (data->drilldown.keys.length > 0) {
2575       data->output.n_elements += n_available_results;
2576     } else {
2577       if (n_available_results > 0) {
2578         data->output.n_elements += 1;
2579       }
2580     }
2581   }
2582 
2583   return GRN_TRUE;
2584 }
2585 
2586 static grn_bool
grn_select_output_drilldowns(grn_ctx * ctx,grn_select_data * data)2587 grn_select_output_drilldowns(grn_ctx *ctx,
2588                              grn_select_data *data)
2589 {
2590   grn_bool succeeded = GRN_TRUE;
2591   unsigned int n_available_results = 0;
2592   grn_bool is_labeled;
2593 
2594   if (!data->drilldowns) {
2595     return GRN_TRUE;
2596   }
2597 
2598   data->output.formatter->drilldowns_label(ctx, data);
2599 
2600   GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) {
2601     grn_drilldown_data *drilldown;
2602     grn_table_group_result *result;
2603 
2604     grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
2605     result = &(drilldown->result);
2606     if (result->table) {
2607       n_available_results++;
2608     }
2609   } GRN_HASH_EACH_END(ctx, cursor);
2610 
2611   is_labeled = (data->drilldown.keys.length == 0);
2612 
2613   data->output.formatter->drilldowns_open(ctx, data, n_available_results);
2614 
2615   GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) {
2616     grn_drilldown_data *drilldown;
2617     grn_table_group_result *result;
2618     grn_obj *target_table;
2619     uint32_t n_hits;
2620     int offset;
2621     int limit;
2622 
2623     grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
2624     result = &(drilldown->result);
2625 
2626     if (!result->table) {
2627       continue;
2628     }
2629 
2630     if (drilldown->filtered_result) {
2631       target_table = drilldown->filtered_result;
2632     } else {
2633       target_table = result->table;
2634     }
2635 
2636     n_hits = grn_table_size(ctx, target_table);
2637 
2638     offset = drilldown->offset;
2639     limit = drilldown->limit;
2640     grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit);
2641 
2642     if (drilldown->sort_keys.length > 0) {
2643       grn_table_sort_key *sort_keys;
2644       uint32_t n_sort_keys;
2645       sort_keys = grn_table_sort_key_from_str(ctx,
2646                                               drilldown->sort_keys.value,
2647                                               drilldown->sort_keys.length,
2648                                               target_table, &n_sort_keys);
2649       if (sort_keys) {
2650         grn_obj *sorted;
2651         sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY,
2652                                   NULL, target_table);
2653         if (sorted) {
2654           grn_table_sort(ctx, target_table, offset, limit,
2655                          sorted, sort_keys, n_sort_keys);
2656           data->output.formatter->drilldown_label(ctx, data, drilldown);
2657           if (!grn_proc_select_output_columns(ctx,
2658                                               sorted,
2659                                               n_hits,
2660                                               0,
2661                                               limit,
2662                                               drilldown->output_columns.value,
2663                                               drilldown->output_columns.length,
2664                                               data->filter.condition.expression)) {
2665             succeeded = GRN_FALSE;
2666           }
2667           grn_obj_unlink(ctx, sorted);
2668         }
2669         grn_table_sort_key_close(ctx, sort_keys, n_sort_keys);
2670       } else {
2671         succeeded = GRN_FALSE;
2672       }
2673     } else {
2674       data->output.formatter->drilldown_label(ctx, data, drilldown);
2675       if (!grn_proc_select_output_columns(ctx,
2676                                           target_table,
2677                                           n_hits,
2678                                           offset,
2679                                           limit,
2680                                           drilldown->output_columns.value,
2681                                           drilldown->output_columns.length,
2682                                           data->filter.condition.expression)) {
2683         succeeded = GRN_FALSE;
2684       }
2685     }
2686 
2687     if (!succeeded) {
2688       break;
2689     }
2690 
2691     if (is_labeled) {
2692       GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
2693                     ":", "output.drilldowns[%.*s](%d)",
2694                     (int)(drilldown->label.length),
2695                     drilldown->label.value,
2696                     n_hits);
2697     } else {
2698       GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
2699                     ":", "output.drilldown(%d)", n_hits);
2700     }
2701   } GRN_HASH_EACH_END(ctx, cursor);
2702 
2703   data->output.formatter->drilldowns_close(ctx, data);
2704 
2705   return succeeded;
2706 }
2707 
2708 static grn_bool
grn_select_output(grn_ctx * ctx,grn_select_data * data)2709 grn_select_output(grn_ctx *ctx, grn_select_data *data)
2710 {
2711   grn_bool succeeded = GRN_TRUE;
2712 
2713   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
2714     GRN_OUTPUT_ARRAY_OPEN("RESULT", data->output.n_elements);
2715     succeeded = grn_select_output_match(ctx, data);
2716     if (succeeded) {
2717       succeeded = grn_select_output_slices(ctx, data);
2718     }
2719     if (succeeded) {
2720       succeeded = grn_select_output_drilldowns(ctx, data);
2721     }
2722     GRN_OUTPUT_ARRAY_CLOSE();
2723   } else {
2724     grn_obj_format format;
2725     uint32_t n_additional_elements = 0;
2726 
2727     if (data->slices) {
2728       n_additional_elements++;
2729     }
2730     if (data->drilldowns) {
2731       n_additional_elements++;
2732     }
2733 
2734     succeeded = grn_select_output_match_open(ctx,
2735                                              data,
2736                                              &format,
2737                                              n_additional_elements);
2738     if (succeeded) {
2739       succeeded = grn_select_output_slices(ctx, data);
2740       if (succeeded) {
2741         succeeded = grn_select_output_drilldowns(ctx, data);
2742       }
2743       if (!grn_select_output_match_close(ctx, data, &format)) {
2744         succeeded = GRN_FALSE;
2745       }
2746     }
2747   }
2748 
2749   return succeeded;
2750 }
2751 
2752 static void
grn_select_output_slices_label_v1(grn_ctx * ctx,grn_select_data * data)2753 grn_select_output_slices_label_v1(grn_ctx *ctx, grn_select_data *data)
2754 {
2755 }
2756 
2757 static void
grn_select_output_slices_open_v1(grn_ctx * ctx,grn_select_data * data,unsigned int n_result_sets)2758 grn_select_output_slices_open_v1(grn_ctx *ctx,
2759                                  grn_select_data *data,
2760                                  unsigned int n_result_sets)
2761 {
2762   GRN_OUTPUT_MAP_OPEN("SLICES", n_result_sets);
2763 }
2764 
2765 static void
grn_select_output_slices_close_v1(grn_ctx * ctx,grn_select_data * data)2766 grn_select_output_slices_close_v1(grn_ctx *ctx, grn_select_data *data)
2767 {
2768   GRN_OUTPUT_MAP_CLOSE();
2769 }
2770 
2771 static void
grn_select_output_slice_label_v1(grn_ctx * ctx,grn_select_data * data,grn_slice_data * slice)2772 grn_select_output_slice_label_v1(grn_ctx *ctx,
2773                                  grn_select_data *data,
2774                                  grn_slice_data *slice)
2775 {
2776   GRN_OUTPUT_STR(slice->label.value, slice->label.length);
2777 }
2778 
2779 static void
grn_select_output_drilldowns_label_v1(grn_ctx * ctx,grn_select_data * data)2780 grn_select_output_drilldowns_label_v1(grn_ctx *ctx, grn_select_data *data)
2781 {
2782 }
2783 
2784 static void
grn_select_output_drilldowns_open_v1(grn_ctx * ctx,grn_select_data * data,unsigned int n_result_sets)2785 grn_select_output_drilldowns_open_v1(grn_ctx *ctx,
2786                                      grn_select_data *data,
2787                                      unsigned int n_result_sets)
2788 {
2789   if (data->drilldown.keys.length == 0) {
2790     GRN_OUTPUT_MAP_OPEN("DRILLDOWNS", n_result_sets);
2791   }
2792 }
2793 
2794 static void
grn_select_output_drilldowns_close_v1(grn_ctx * ctx,grn_select_data * data)2795 grn_select_output_drilldowns_close_v1(grn_ctx *ctx, grn_select_data *data)
2796 {
2797   if (data->drilldown.keys.length == 0) {
2798     GRN_OUTPUT_MAP_CLOSE();
2799   }
2800 }
2801 
2802 static void
grn_select_output_drilldown_label_v1(grn_ctx * ctx,grn_select_data * data,grn_drilldown_data * drilldown)2803 grn_select_output_drilldown_label_v1(grn_ctx *ctx,
2804                                      grn_select_data *data,
2805                                      grn_drilldown_data *drilldown)
2806 {
2807   if (data->drilldown.keys.length == 0) {
2808     GRN_OUTPUT_STR(drilldown->label.value, drilldown->label.length);
2809   }
2810 }
2811 
2812 static grn_select_output_formatter grn_select_output_formatter_v1 = {
2813   grn_select_output_slices_label_v1,
2814   grn_select_output_slices_open_v1,
2815   grn_select_output_slices_close_v1,
2816   grn_select_output_slice_label_v1,
2817   grn_select_output_drilldowns_label_v1,
2818   grn_select_output_drilldowns_open_v1,
2819   grn_select_output_drilldowns_close_v1,
2820   grn_select_output_drilldown_label_v1
2821 };
2822 
2823 static void
grn_select_output_slices_label_v3(grn_ctx * ctx,grn_select_data * data)2824 grn_select_output_slices_label_v3(grn_ctx *ctx, grn_select_data *data)
2825 {
2826   GRN_OUTPUT_CSTR("slices");
2827 }
2828 
2829 static void
grn_select_output_slices_open_v3(grn_ctx * ctx,grn_select_data * data,unsigned int n_result_sets)2830 grn_select_output_slices_open_v3(grn_ctx *ctx,
2831                                  grn_select_data *data,
2832                                  unsigned int n_result_sets)
2833 {
2834   GRN_OUTPUT_MAP_OPEN("slices", n_result_sets);
2835 }
2836 
2837 static void
grn_select_output_slices_close_v3(grn_ctx * ctx,grn_select_data * data)2838 grn_select_output_slices_close_v3(grn_ctx *ctx, grn_select_data *data)
2839 {
2840   GRN_OUTPUT_MAP_CLOSE();
2841 }
2842 
2843 static void
grn_select_output_slice_label_v3(grn_ctx * ctx,grn_select_data * data,grn_slice_data * slice)2844 grn_select_output_slice_label_v3(grn_ctx *ctx,
2845                                  grn_select_data *data,
2846                                  grn_slice_data *slice)
2847 {
2848   GRN_OUTPUT_STR(slice->label.value, slice->label.length);
2849 }
2850 
2851 static void
grn_select_output_drilldowns_label_v3(grn_ctx * ctx,grn_select_data * data)2852 grn_select_output_drilldowns_label_v3(grn_ctx *ctx, grn_select_data *data)
2853 {
2854   GRN_OUTPUT_CSTR("drilldowns");
2855 }
2856 
2857 static void
grn_select_output_drilldowns_open_v3(grn_ctx * ctx,grn_select_data * data,unsigned int n_result_sets)2858 grn_select_output_drilldowns_open_v3(grn_ctx *ctx,
2859                                      grn_select_data *data,
2860                                      unsigned int n_result_sets)
2861 {
2862   GRN_OUTPUT_MAP_OPEN("drilldowns", n_result_sets);
2863 }
2864 
2865 static void
grn_select_output_drilldowns_close_v3(grn_ctx * ctx,grn_select_data * data)2866 grn_select_output_drilldowns_close_v3(grn_ctx *ctx, grn_select_data *data)
2867 {
2868   GRN_OUTPUT_MAP_CLOSE();
2869 }
2870 
2871 static void
grn_select_output_drilldown_label_v3(grn_ctx * ctx,grn_select_data * data,grn_drilldown_data * drilldown)2872 grn_select_output_drilldown_label_v3(grn_ctx *ctx,
2873                                      grn_select_data *data,
2874                                      grn_drilldown_data *drilldown)
2875 {
2876   if (data->drilldown.keys.length == 0) {
2877     GRN_OUTPUT_STR(drilldown->label.value, drilldown->label.length);
2878   } else {
2879     grn_obj *key;
2880     char name[GRN_TABLE_MAX_KEY_SIZE];
2881     int name_len;
2882 
2883     key = drilldown->parsed_keys[0].key;
2884     switch (key->header.type) {
2885     case GRN_COLUMN_FIX_SIZE :
2886     case GRN_COLUMN_VAR_SIZE :
2887     case GRN_COLUMN_INDEX :
2888       name_len = grn_column_name(ctx, key, name, GRN_TABLE_MAX_KEY_SIZE);
2889       break;
2890     default :
2891       name_len = grn_obj_name(ctx, key, name, GRN_TABLE_MAX_KEY_SIZE);
2892       break;
2893     }
2894     GRN_OUTPUT_STR(name, name_len);
2895   }
2896 }
2897 
2898 static grn_select_output_formatter grn_select_output_formatter_v3 = {
2899   grn_select_output_slices_label_v3,
2900   grn_select_output_slices_open_v3,
2901   grn_select_output_slices_close_v3,
2902   grn_select_output_slice_label_v3,
2903   grn_select_output_drilldowns_label_v3,
2904   grn_select_output_drilldowns_open_v3,
2905   grn_select_output_drilldowns_close_v3,
2906   grn_select_output_drilldown_label_v3
2907 };
2908 
2909 static grn_rc
grn_select(grn_ctx * ctx,grn_select_data * data)2910 grn_select(grn_ctx *ctx, grn_select_data *data)
2911 {
2912   uint32_t nhits;
2913   grn_obj *outbuf = ctx->impl->output.buf;
2914   grn_content_type output_type = ctx->impl->output.type;
2915   char cache_key[GRN_CACHE_MAX_KEY_SIZE];
2916   uint32_t cache_key_size;
2917   long long int threshold, original_threshold = 0;
2918   grn_cache *cache_obj = grn_cache_current_get(ctx);
2919 
2920   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
2921     data->output.formatter = &grn_select_output_formatter_v1;
2922   } else {
2923     data->output.formatter = &grn_select_output_formatter_v3;
2924   }
2925 
2926   data->cacheable = 1;
2927   data->taintable = 0;
2928 
2929   data->output.n_elements = 0;
2930 
2931   grn_raw_string_lstrip(ctx, &(data->filter.query));
2932 
2933   cache_key_size =
2934     data->table.length + 1 +
2935     data->filter.match_columns.length + 1 +
2936     data->filter.query.length + 1 +
2937     data->filter.filter.length + 1 +
2938     data->scorer.length + 1 +
2939     data->sort_keys.length + 1 +
2940     data->output_columns.length + 1 +
2941     data->match_escalation_threshold.length + 1 +
2942     data->filter.query_expander.length + 1 +
2943     data->filter.query_flags.length + 1 +
2944     data->adjuster.length + 1 +
2945     sizeof(grn_content_type) +
2946     sizeof(int) * 2 +
2947     sizeof(grn_command_version) +
2948     sizeof(grn_bool);
2949   if (data->slices) {
2950     GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
2951       grn_slice_data *slice;
2952       grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
2953       grn_raw_string_lstrip(ctx, &(slice->filter.query));
2954       cache_key_size +=
2955         slice->filter.match_columns.length + 1 +
2956         slice->filter.query.length + 1 +
2957         slice->filter.query_expander.length + 1 +
2958         slice->filter.query_flags.length + 1 +
2959         slice->filter.filter.length + 1 +
2960         slice->sort_keys.length + 1 +
2961         slice->output_columns.length + 1 +
2962         slice->label.length + 1 +
2963         sizeof(int) * 2;
2964     } GRN_HASH_EACH_END(ctx, cursor);
2965   }
2966 #define DRILLDOWN_CACHE_SIZE(drilldown)         \
2967   drilldown->keys.length + 1 +                  \
2968   drilldown->sort_keys.length + 1 +             \
2969     drilldown->output_columns.length + 1 +      \
2970     drilldown->label.length + 1 +               \
2971     drilldown->calc_target_name.length + 1 +    \
2972     drilldown->filter.length + 1 +              \
2973     drilldown->table_name.length + 1 +          \
2974     sizeof(int) * 2 +                           \
2975     sizeof(grn_table_group_flags)
2976   if (data->drilldown.keys.length > 0) {
2977     grn_drilldown_data *drilldown = &(data->drilldown);
2978     cache_key_size += DRILLDOWN_CACHE_SIZE(drilldown);
2979   }
2980   if (data->drilldowns) {
2981     GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) {
2982       grn_drilldown_data *drilldown;
2983       grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
2984       cache_key_size += DRILLDOWN_CACHE_SIZE(drilldown);
2985     } GRN_HASH_EACH_END(ctx, cursor);
2986   }
2987 #undef DRILLDOWN_CACHE_SIZE
2988   if (cache_key_size <= GRN_CACHE_MAX_KEY_SIZE) {
2989     char *cp = cache_key;
2990 
2991 #define PUT_CACHE_KEY(string)                                   \
2992     if ((string).value)                                         \
2993       grn_memcpy(cp, (string).value, (string).length);          \
2994     cp += (string).length;                                      \
2995     *cp++ = '\0'
2996 
2997     PUT_CACHE_KEY(data->table);
2998     PUT_CACHE_KEY(data->filter.match_columns);
2999     PUT_CACHE_KEY(data->filter.query);
3000     PUT_CACHE_KEY(data->filter.filter);
3001     PUT_CACHE_KEY(data->scorer);
3002     PUT_CACHE_KEY(data->sort_keys);
3003     PUT_CACHE_KEY(data->output_columns);
3004     if (data->slices) {
3005       GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
3006         grn_slice_data *slice;
3007         grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
3008         PUT_CACHE_KEY(slice->filter.match_columns);
3009         PUT_CACHE_KEY(slice->filter.query);
3010         PUT_CACHE_KEY(slice->filter.query_expander);
3011         PUT_CACHE_KEY(slice->filter.query_flags);
3012         PUT_CACHE_KEY(slice->filter.filter);
3013         PUT_CACHE_KEY(slice->sort_keys);
3014         PUT_CACHE_KEY(slice->output_columns);
3015         PUT_CACHE_KEY(slice->label);
3016         grn_memcpy(cp, &(slice->offset), sizeof(int));
3017         cp += sizeof(int);
3018         grn_memcpy(cp, &(slice->limit), sizeof(int));
3019         cp += sizeof(int);
3020       } GRN_HASH_EACH_END(ctx, cursor);
3021     }
3022 #define PUT_CACHE_KEY_DRILLDOWN(drilldown) do {                 \
3023       PUT_CACHE_KEY(drilldown->keys);                           \
3024       PUT_CACHE_KEY(drilldown->sort_keys);                      \
3025       PUT_CACHE_KEY(drilldown->output_columns);                 \
3026       PUT_CACHE_KEY(drilldown->label);                          \
3027       PUT_CACHE_KEY(drilldown->calc_target_name);               \
3028       PUT_CACHE_KEY(drilldown->filter);                         \
3029       PUT_CACHE_KEY(drilldown->table_name);                     \
3030       grn_memcpy(cp, &(drilldown->offset), sizeof(int));        \
3031       cp += sizeof(int);                                        \
3032       grn_memcpy(cp, &(drilldown->limit), sizeof(int));         \
3033       cp += sizeof(int);                                        \
3034       grn_memcpy(cp,                                            \
3035                  &(drilldown->calc_types),                      \
3036                  sizeof(grn_table_group_flags));                \
3037       cp += sizeof(grn_table_group_flags);                      \
3038     } while (GRN_FALSE)
3039     if (data->drilldown.keys.length > 0) {
3040       grn_drilldown_data *drilldown = &(data->drilldown);
3041       PUT_CACHE_KEY_DRILLDOWN(drilldown);
3042     }
3043     if (data->drilldowns) {
3044       GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) {
3045         grn_drilldown_data *drilldown;
3046         grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
3047         PUT_CACHE_KEY_DRILLDOWN(drilldown);
3048       } GRN_HASH_EACH_END(ctx, cursor);
3049     }
3050 #undef PUT_CACHE_KEY_DRILLDOWN
3051     PUT_CACHE_KEY(data->match_escalation_threshold);
3052     PUT_CACHE_KEY(data->filter.query_expander);
3053     PUT_CACHE_KEY(data->filter.query_flags);
3054     PUT_CACHE_KEY(data->adjuster);
3055     grn_memcpy(cp, &output_type, sizeof(grn_content_type));
3056     cp += sizeof(grn_content_type);
3057     grn_memcpy(cp, &(data->offset), sizeof(int));
3058     cp += sizeof(int);
3059     grn_memcpy(cp, &(data->limit), sizeof(int));
3060     cp += sizeof(int);
3061     grn_memcpy(cp, &(ctx->impl->command.version), sizeof(grn_command_version));
3062     cp += sizeof(grn_command_version);
3063     grn_memcpy(cp, &(ctx->impl->output.is_pretty), sizeof(grn_bool));
3064     cp += sizeof(grn_bool);
3065 #undef PUT_CACHE_KEY
3066 
3067     {
3068       grn_rc rc;
3069       rc = grn_cache_fetch(ctx, cache_obj, cache_key, cache_key_size, outbuf);
3070       if (rc == GRN_SUCCESS) {
3071         GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_CACHE,
3072                       ":", "cache(%" GRN_FMT_LLD ")",
3073                       (long long int)GRN_TEXT_LEN(outbuf));
3074         return ctx->rc;
3075       }
3076     }
3077   }
3078   if (data->match_escalation_threshold.length) {
3079     const char *end, *rest;
3080     original_threshold = grn_ctx_get_match_escalation_threshold(ctx);
3081     end =
3082       data->match_escalation_threshold.value +
3083       data->match_escalation_threshold.length;
3084     threshold = grn_atoll(data->match_escalation_threshold.value, end, &rest);
3085     if (end == rest) {
3086       grn_ctx_set_match_escalation_threshold(ctx, threshold);
3087     }
3088   }
3089 
3090   data->tables.target = grn_ctx_get(ctx, data->table.value, data->table.length);
3091   if (!data->tables.target) {
3092     GRN_PLUGIN_ERROR(ctx,
3093                      GRN_INVALID_ARGUMENT,
3094                      "[select][table] invalid name: <%.*s>",
3095                      (int)(data->table.length),
3096                      data->table.value);
3097     goto exit;
3098   }
3099 
3100   {
3101     if (data->filter.filter.length > 0 &&
3102         (data->filter.filter.value[0] == '?') &&
3103         (ctx->impl->output.type == GRN_CONTENT_JSON)) {
3104       ctx->rc = grn_ts_select(ctx, data->tables.target,
3105                               data->filter.filter.value + 1,
3106                               data->filter.filter.length - 1,
3107                               data->scorer.value,
3108                               data->scorer.length,
3109                               data->sort_keys.value,
3110                               data->sort_keys.length,
3111                               data->output_columns.value,
3112                               data->output_columns.length,
3113                               data->offset,
3114                               data->limit);
3115       if (!ctx->rc &&
3116           data->cacheable > 0 &&
3117           cache_key_size <= GRN_CACHE_MAX_KEY_SIZE &&
3118           (!data->cache.value ||
3119            data->cache.length != 2 ||
3120            data->cache.value[0] != 'n' ||
3121            data->cache.value[1] != 'o')) {
3122         grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf);
3123       }
3124       goto exit;
3125     }
3126 
3127     data->tables.initial = data->tables.target;
3128     if (!grn_select_apply_initial_columns(ctx, data)) {
3129       goto exit;
3130     }
3131 
3132     if (!grn_select_filter(ctx, data)) {
3133       goto exit;
3134     }
3135 
3136     nhits = grn_table_size(ctx, data->tables.result);
3137     GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
3138                   ":", "select(%d)", nhits);
3139 
3140     if (!grn_select_apply_filtered_columns(ctx, data)) {
3141       goto exit;
3142     }
3143 
3144     {
3145       grn_bool succeeded;
3146 
3147       /* For select results */
3148       data->output.n_elements = 1;
3149 
3150       if (!grn_select_apply_adjuster(ctx, data)) {
3151         goto exit;
3152       }
3153 
3154       if (!grn_select_apply_scorer(ctx, data)) {
3155         goto exit;
3156       }
3157 
3158       grn_normalize_offset_and_limit(ctx, nhits,
3159                                      &(data->offset), &(data->limit));
3160 
3161       if (!grn_select_sort(ctx, data)) {
3162         goto exit;
3163       }
3164 
3165       if (!grn_select_apply_output_columns(ctx, data)) {
3166         goto exit;
3167       }
3168 
3169       if (!grn_select_prepare_slices(ctx, data)) {
3170         goto exit;
3171       }
3172 
3173       if (!grn_select_prepare_drilldowns(ctx, data)) {
3174         goto exit;
3175       }
3176 
3177       succeeded = grn_select_output(ctx, data);
3178       if (!succeeded) {
3179         goto exit;
3180       }
3181     }
3182     if (!ctx->rc &&
3183         data->cacheable &&
3184         cache_key_size <= GRN_CACHE_MAX_KEY_SIZE &&
3185         (!data->cache.value ||
3186          data->cache.length != 2 ||
3187          data->cache.value[0] != 'n' ||
3188          data->cache.value[1] != 'o')) {
3189       grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf);
3190     }
3191     if (data->taintable > 0) {
3192       grn_db_touch(ctx, DB_OBJ(data->tables.target)->db);
3193     }
3194   }
3195 
3196 exit :
3197   if (data->match_escalation_threshold.length > 0) {
3198     grn_ctx_set_match_escalation_threshold(ctx, original_threshold);
3199   }
3200 
3201   /* GRN_LOG(ctx, GRN_LOG_NONE, "%d", ctx->seqno); */
3202 
3203   return ctx->rc;
3204 }
3205 
3206 static grn_slice_data *
grn_select_data_slices_add(grn_ctx * ctx,grn_select_data * data,const char * label,size_t label_len)3207 grn_select_data_slices_add(grn_ctx *ctx,
3208                            grn_select_data *data,
3209                            const char *label,
3210                            size_t label_len)
3211 {
3212   grn_slice_data *slice = NULL;
3213   int added;
3214 
3215   if (!data->slices) {
3216     data->slices = grn_hash_create(ctx,
3217                                    NULL,
3218                                    GRN_TABLE_MAX_KEY_SIZE,
3219                                    sizeof(grn_slice_data),
3220                                    GRN_OBJ_TABLE_HASH_KEY |
3221                                    GRN_OBJ_KEY_VAR_SIZE |
3222                                    GRN_HASH_TINY);
3223     if (!data->slices) {
3224       GRN_PLUGIN_ERROR(ctx,
3225                        GRN_INVALID_ARGUMENT,
3226                        "[select][slices] "
3227                        "failed to allocate slices data: %s",
3228                        ctx->errbuf);
3229       return NULL;
3230     }
3231   }
3232 
3233   grn_hash_add(ctx,
3234                data->slices,
3235                label,
3236                label_len,
3237                (void **)&slice,
3238                &added);
3239   if (added) {
3240     grn_slice_data_init(ctx, slice, label, label_len);
3241   }
3242 
3243   return slice;
3244 }
3245 
3246 static grn_bool
grn_select_data_fill_slice_labels(grn_ctx * ctx,grn_user_data * user_data,grn_select_data * data)3247 grn_select_data_fill_slice_labels(grn_ctx *ctx,
3248                                   grn_user_data *user_data,
3249                                   grn_select_data *data)
3250 {
3251   grn_obj *vars;
3252   grn_table_cursor *cursor;
3253   const char *prefix = "slices[";
3254   int prefix_len;
3255 
3256   vars = grn_plugin_proc_get_vars(ctx, user_data);
3257 
3258   cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0);
3259   if (!cursor) {
3260     return GRN_FALSE;
3261   }
3262 
3263   prefix_len = strlen(prefix);
3264   while (grn_table_cursor_next(ctx, cursor)) {
3265     void *key;
3266     char *name;
3267     int name_len;
3268     name_len = grn_table_cursor_get_key(ctx, cursor, &key);
3269     name = key;
3270     if (name_len > prefix_len + 1 &&
3271         strncmp(prefix, name, prefix_len) == 0) {
3272       const char *label_end;
3273       size_t label_len;
3274       label_end = memchr(name + prefix_len + 1,
3275                          ']',
3276                          name_len - prefix_len - 1);
3277       if (!label_end) {
3278         continue;
3279       }
3280       label_len = (label_end - name) - prefix_len;
3281       grn_select_data_slices_add(ctx,
3282                                  data,
3283                                  name + prefix_len,
3284                                  label_len);
3285     }
3286   }
3287   grn_table_cursor_close(ctx, cursor);
3288 
3289   return GRN_TRUE;
3290 }
3291 
3292 static grn_bool
grn_select_data_fill_slices(grn_ctx * ctx,grn_user_data * user_data,grn_select_data * data)3293 grn_select_data_fill_slices(grn_ctx *ctx,
3294                             grn_user_data *user_data,
3295                             grn_select_data *data)
3296 {
3297   if (!grn_select_data_fill_slice_labels(ctx, user_data, data)) {
3298     return GRN_FALSE;
3299   }
3300 
3301   GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) {
3302     grn_slice_data *slice;
3303     char slice_label[GRN_TABLE_MAX_KEY_SIZE];
3304     char key_name[GRN_TABLE_MAX_KEY_SIZE];
3305     grn_obj *match_columns;
3306     grn_obj *query;
3307     grn_obj *query_expander;
3308     grn_obj *query_flags;
3309     grn_obj *filter;
3310     grn_obj *sort_keys;
3311     grn_obj *output_columns;
3312     grn_obj *offset;
3313     grn_obj *limit;
3314 
3315     grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
3316 
3317     grn_snprintf(slice_label,
3318                  GRN_TABLE_MAX_KEY_SIZE,
3319                  GRN_TABLE_MAX_KEY_SIZE,
3320                  "slices[%.*s].",
3321                  (int)(slice->label.length),
3322                  slice->label.value);
3323 
3324 #define GET_VAR(name)                                                   \
3325       grn_snprintf(key_name,                                            \
3326                    GRN_TABLE_MAX_KEY_SIZE,                              \
3327                    GRN_TABLE_MAX_KEY_SIZE,                              \
3328                    "%s%s", slice_label, #name);                         \
3329       name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1);
3330 
3331       GET_VAR(match_columns);
3332       GET_VAR(query);
3333       GET_VAR(query_expander);
3334       GET_VAR(query_flags);
3335       GET_VAR(filter);
3336       GET_VAR(sort_keys);
3337       GET_VAR(output_columns);
3338       GET_VAR(offset);
3339       GET_VAR(limit);
3340 
3341 #undef GET_VAR
3342 
3343       grn_slice_data_fill(ctx,
3344                           slice,
3345                           match_columns,
3346                           query,
3347                           query_expander,
3348                           query_flags,
3349                           filter,
3350                           sort_keys,
3351                           output_columns,
3352                           offset,
3353                           limit);
3354   } GRN_HASH_EACH_END(ctx, cursor);
3355 
3356   return GRN_TRUE;
3357 }
3358 
3359 static grn_bool
grn_select_data_fill_drilldown_labels(grn_ctx * ctx,grn_user_data * user_data,grn_select_data * data,const char * prefix)3360 grn_select_data_fill_drilldown_labels(grn_ctx *ctx,
3361                                       grn_user_data *user_data,
3362                                       grn_select_data *data,
3363                                       const char *prefix)
3364 {
3365   grn_obj *vars;
3366   grn_table_cursor *cursor;
3367   int prefix_len;
3368 
3369   vars = grn_plugin_proc_get_vars(ctx, user_data);
3370 
3371   cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0);
3372   if (!cursor) {
3373     return GRN_FALSE;
3374   }
3375 
3376   prefix_len = strlen(prefix);
3377   while (grn_table_cursor_next(ctx, cursor)) {
3378     void *key;
3379     char *name;
3380     int name_len;
3381     name_len = grn_table_cursor_get_key(ctx, cursor, &key);
3382     name = key;
3383     if (name_len > prefix_len + 1 &&
3384         strncmp(prefix, name, prefix_len) == 0) {
3385       const char *label_end;
3386       size_t label_len;
3387       label_end = memchr(name + prefix_len + 1,
3388                          ']',
3389                          name_len - prefix_len - 1);
3390       if (!label_end) {
3391         continue;
3392       }
3393       label_len = (label_end - name) - prefix_len;
3394       grn_select_data_drilldowns_add(ctx,
3395                                      data,
3396                                      name + prefix_len,
3397                                      label_len);
3398     }
3399   }
3400   grn_table_cursor_close(ctx, cursor);
3401 
3402   return GRN_TRUE;
3403 }
3404 
3405 static grn_bool
grn_select_data_fill_drilldown_columns(grn_ctx * ctx,grn_user_data * user_data,grn_drilldown_data * drilldown,const char * parameter_key)3406 grn_select_data_fill_drilldown_columns(grn_ctx *ctx,
3407                                        grn_user_data *user_data,
3408                                        grn_drilldown_data *drilldown,
3409                                        const char *parameter_key)
3410 {
3411   char prefix[GRN_TABLE_MAX_KEY_SIZE];
3412 
3413   grn_snprintf(prefix,
3414                GRN_TABLE_MAX_KEY_SIZE,
3415                GRN_TABLE_MAX_KEY_SIZE,
3416                "%s[%.*s].",
3417                parameter_key,
3418                (int)(drilldown->label.length),
3419                drilldown->label.value);
3420   return grn_columns_fill(ctx,
3421                           user_data,
3422                           &(drilldown->columns),
3423                           prefix,
3424                           strlen(prefix));
3425 }
3426 
3427 static grn_bool
grn_select_data_fill_drilldowns(grn_ctx * ctx,grn_user_data * user_data,grn_select_data * data)3428 grn_select_data_fill_drilldowns(grn_ctx *ctx,
3429                                 grn_user_data *user_data,
3430                                 grn_select_data *data)
3431 {
3432   grn_obj *drilldown;
3433 
3434   drilldown = grn_plugin_proc_get_var(ctx, user_data, "drilldown", -1);
3435   if (GRN_TEXT_LEN(drilldown) > 0) {
3436     grn_obj *sort_keys;
3437 
3438     sort_keys = grn_plugin_proc_get_var(ctx, user_data,
3439                                         "drilldown_sort_keys", -1);
3440     if (GRN_TEXT_LEN(sort_keys) == 0) {
3441       /* For backward compatibility */
3442       sort_keys = grn_plugin_proc_get_var(ctx, user_data,
3443                                           "drilldown_sortby", -1);
3444     }
3445     grn_drilldown_data_fill(ctx,
3446                             &(data->drilldown),
3447                             drilldown,
3448                             sort_keys,
3449                             grn_plugin_proc_get_var(ctx, user_data,
3450                                                     "drilldown_output_columns",
3451                                                     -1),
3452                             grn_plugin_proc_get_var(ctx, user_data,
3453                                                     "drilldown_offset", -1),
3454                             grn_plugin_proc_get_var(ctx, user_data,
3455                                                     "drilldown_limit", -1),
3456                             grn_plugin_proc_get_var(ctx, user_data,
3457                                                     "drilldown_calc_types", -1),
3458                             grn_plugin_proc_get_var(ctx, user_data,
3459                                                     "drilldown_calc_target", -1),
3460                             grn_plugin_proc_get_var(ctx, user_data,
3461                                                     "drilldown_filter", -1),
3462                             NULL);
3463     return GRN_TRUE;
3464   } else {
3465     grn_bool succeeded = GRN_TRUE;
3466 
3467     if (!grn_select_data_fill_drilldown_labels(ctx, user_data, data,
3468                                                "drilldowns[")) {
3469       return GRN_FALSE;
3470     }
3471 
3472     /* For backward compatibility */
3473     if (!grn_select_data_fill_drilldown_labels(ctx, user_data, data,
3474                                                "drilldown[")) {
3475       return GRN_FALSE;
3476     }
3477 
3478     GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) {
3479       grn_drilldown_data *drilldown;
3480       grn_obj *keys = NULL;
3481       grn_obj *sort_keys = NULL;
3482       grn_obj *output_columns = NULL;
3483       grn_obj *offset = NULL;
3484       grn_obj *limit = NULL;
3485       grn_obj *calc_types = NULL;
3486       grn_obj *calc_target = NULL;
3487       grn_obj *filter = NULL;
3488       grn_obj *table = NULL;
3489 
3490       grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
3491 
3492       succeeded = grn_select_data_fill_drilldown_columns(ctx,
3493                                                          user_data,
3494                                                          drilldown,
3495                                                          "drilldowns");
3496       if (!succeeded) {
3497         break;
3498       }
3499 
3500       /* For backward compatibility */
3501       succeeded = grn_select_data_fill_drilldown_columns(ctx,
3502                                                          user_data,
3503                                                          drilldown,
3504                                                          "drilldown");
3505       if (!succeeded) {
3506         break;
3507       }
3508 
3509 #define GET_VAR_RAW(parameter_key, name) do {                           \
3510         if (!name) {                                                    \
3511           char key_name[GRN_TABLE_MAX_KEY_SIZE];                        \
3512           grn_snprintf(key_name,                                        \
3513                        GRN_TABLE_MAX_KEY_SIZE,                          \
3514                        GRN_TABLE_MAX_KEY_SIZE,                          \
3515                        "%s[%.*s].%s",                                   \
3516                        (parameter_key),                                 \
3517                        (int)(drilldown->label.length),                  \
3518                        drilldown->label.value,                          \
3519                        #name);                                          \
3520           name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1); \
3521         }                                                               \
3522       } while (GRN_FALSE)
3523 
3524 #define GET_VAR(name) do {                                              \
3525         GET_VAR_RAW("drilldowns", name);                                \
3526         /* For backward compatibility */                                \
3527         GET_VAR_RAW("drilldown", name);                                 \
3528       } while (GRN_FALSE)
3529 
3530       GET_VAR(keys);
3531       GET_VAR(sort_keys);
3532       if (!sort_keys) {
3533         grn_obj *sortby = NULL;
3534         GET_VAR(sortby);
3535         sort_keys = sortby;
3536       }
3537       GET_VAR(output_columns);
3538       GET_VAR(offset);
3539       GET_VAR(limit);
3540       GET_VAR(calc_types);
3541       GET_VAR(calc_target);
3542       GET_VAR(filter);
3543       GET_VAR(table);
3544 
3545 #undef GET_VAR
3546 
3547 #undef GET_VAR_RAW
3548 
3549       grn_drilldown_data_fill(ctx,
3550                               drilldown,
3551                               keys,
3552                               sort_keys,
3553                               output_columns,
3554                               offset,
3555                               limit,
3556                               calc_types,
3557                               calc_target,
3558                               filter,
3559                               table);
3560     } GRN_HASH_EACH_END(ctx, cursor);
3561 
3562     return succeeded;
3563   }
3564 }
3565 
3566 static grn_obj *
command_select(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)3567 command_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
3568 {
3569   grn_select_data data;
3570 
3571   grn_columns_init(ctx, &(data.columns));
3572   grn_filter_data_init(ctx, &(data.filter));
3573 
3574   data.tables.target = NULL;
3575   data.tables.initial = NULL;
3576   data.tables.result = NULL;
3577   data.tables.sorted = NULL;
3578 
3579   data.slices = NULL;
3580   grn_drilldown_data_init(ctx, &(data.drilldown), NULL, 0);
3581   data.drilldowns = NULL;
3582 
3583   data.table.value = grn_plugin_proc_get_var_string(ctx, user_data,
3584                                                     "table", -1,
3585                                                     &(data.table.length));
3586 #define GET_VAR(name)                                           \
3587   grn_plugin_proc_get_var(ctx, user_data, name, strlen(name))
3588 
3589   {
3590     grn_obj *query_expander;
3591 
3592     query_expander = GET_VAR("query_expander");
3593     if (GRN_TEXT_LEN(query_expander) == 0) {
3594       query_expander = GET_VAR("query_expansion");
3595     }
3596 
3597     grn_filter_data_fill(ctx,
3598                          &(data.filter),
3599                          GET_VAR("match_columns"),
3600                          GET_VAR("query"),
3601                          query_expander,
3602                          GET_VAR("query_flags"),
3603                          GET_VAR("filter"));
3604   }
3605 #undef GET_VAR
3606 
3607   data.scorer.value =
3608     grn_plugin_proc_get_var_string(ctx, user_data,
3609                                    "scorer", -1,
3610                                    &(data.scorer.length));
3611   data.sort_keys.value =
3612     grn_plugin_proc_get_var_string(ctx, user_data,
3613                                    "sort_keys", -1,
3614                                    &(data.sort_keys.length));
3615   if (data.sort_keys.length == 0) {
3616     /* For backward compatibility */
3617     data.sort_keys.value =
3618       grn_plugin_proc_get_var_string(ctx, user_data,
3619                                      "sortby", -1,
3620                                      &(data.sort_keys.length));
3621   }
3622   data.output_columns.value =
3623     grn_plugin_proc_get_var_string(ctx, user_data,
3624                                    "output_columns", -1,
3625                                    &(data.output_columns.length));
3626   if (!data.output_columns.value) {
3627     data.output_columns.value = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS;
3628     data.output_columns.length = strlen(GRN_SELECT_DEFAULT_OUTPUT_COLUMNS);
3629   }
3630   data.offset = grn_plugin_proc_get_var_int32(ctx, user_data,
3631                                               "offset", -1,
3632                                               0);
3633   data.limit = grn_plugin_proc_get_var_int32(ctx, user_data,
3634                                              "limit", -1,
3635                                              GRN_SELECT_DEFAULT_LIMIT);
3636 
3637   data.cache.value = grn_plugin_proc_get_var_string(ctx, user_data,
3638                                                     "cache", -1,
3639                                                     &(data.cache.length));
3640   data.match_escalation_threshold.value =
3641     grn_plugin_proc_get_var_string(ctx, user_data,
3642                                    "match_escalation_threshold", -1,
3643                                    &(data.match_escalation_threshold.length));
3644 
3645   data.adjuster.value =
3646     grn_plugin_proc_get_var_string(ctx, user_data,
3647                                    "adjuster", -1,
3648                                    &(data.adjuster.length));
3649 
3650   if (!grn_select_data_fill_slices(ctx, user_data, &data)) {
3651     goto exit;
3652   }
3653 
3654   if (!grn_select_data_fill_drilldowns(ctx, user_data, &data)) {
3655     goto exit;
3656   }
3657 
3658   if (!grn_columns_fill(ctx, user_data, &(data.columns), NULL, 0)) {
3659     goto exit;
3660   }
3661 
3662   grn_select(ctx, &data);
3663 
3664 exit :
3665   if (data.drilldowns) {
3666     GRN_HASH_EACH_BEGIN(ctx, data.drilldowns, cursor, id) {
3667       grn_drilldown_data *drilldown;
3668       grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown);
3669       grn_drilldown_data_fin(ctx, drilldown);
3670     } GRN_HASH_EACH_END(ctx, cursor);
3671     grn_hash_close(ctx, data.drilldowns);
3672   }
3673 
3674   if (data.drilldown.parsed_keys) {
3675     grn_table_sort_key_close(ctx,
3676                              data.drilldown.parsed_keys,
3677                              data.drilldown.n_parsed_keys);
3678   }
3679   grn_drilldown_data_fin(ctx, &(data.drilldown));
3680 
3681   if (data.slices) {
3682     GRN_HASH_EACH_BEGIN(ctx, data.slices, cursor, id) {
3683       grn_slice_data *slice;
3684       grn_hash_cursor_get_value(ctx, cursor, (void **)&slice);
3685       grn_slice_data_fin(ctx, slice);
3686     } GRN_HASH_EACH_END(ctx, cursor);
3687     grn_hash_close(ctx, data.slices);
3688   }
3689 
3690   if (data.tables.sorted) {
3691     grn_obj_unlink(ctx, data.tables.sorted);
3692   }
3693 
3694   if (data.tables.result == data.filter.filtered) {
3695     data.tables.result = NULL;
3696   }
3697   grn_filter_data_fin(ctx, &(data.filter));
3698 
3699   if (data.tables.result &&
3700       data.tables.result != data.tables.initial &&
3701       data.tables.result != data.tables.target) {
3702     grn_obj_unlink(ctx, data.tables.result);
3703   }
3704 
3705   if (data.tables.initial && data.tables.initial != data.tables.target) {
3706     grn_obj_unlink(ctx, data.tables.initial);
3707   }
3708 
3709   if (data.tables.target) {
3710     grn_obj_unlink(ctx, data.tables.target);
3711   }
3712 
3713   grn_columns_fin(ctx, &(data.columns));
3714 
3715   return NULL;
3716 }
3717 
3718 #define N_VARS 26
3719 #define DEFINE_VARS grn_expr_var vars[N_VARS]
3720 
3721 static void
init_vars(grn_ctx * ctx,grn_expr_var * vars)3722 init_vars(grn_ctx *ctx, grn_expr_var *vars)
3723 {
3724   grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1);
3725   grn_plugin_expr_var_init(ctx, &(vars[1]), "table", -1);
3726   grn_plugin_expr_var_init(ctx, &(vars[2]), "match_columns", -1);
3727   grn_plugin_expr_var_init(ctx, &(vars[3]), "query", -1);
3728   grn_plugin_expr_var_init(ctx, &(vars[4]), "filter", -1);
3729   grn_plugin_expr_var_init(ctx, &(vars[5]), "scorer", -1);
3730   /* Deprecated since 6.0.3. Use sort_keys instead. */
3731   grn_plugin_expr_var_init(ctx, &(vars[6]), "sortby", -1);
3732   grn_plugin_expr_var_init(ctx, &(vars[7]), "output_columns", -1);
3733   grn_plugin_expr_var_init(ctx, &(vars[8]), "offset", -1);
3734   grn_plugin_expr_var_init(ctx, &(vars[9]), "limit", -1);
3735   grn_plugin_expr_var_init(ctx, &(vars[10]), "drilldown", -1);
3736   /* Deprecated since 6.0.3. Use drilldown_sort_keys instead. */
3737   grn_plugin_expr_var_init(ctx, &(vars[11]), "drilldown_sortby", -1);
3738   grn_plugin_expr_var_init(ctx, &(vars[12]), "drilldown_output_columns", -1);
3739   grn_plugin_expr_var_init(ctx, &(vars[13]), "drilldown_offset", -1);
3740   grn_plugin_expr_var_init(ctx, &(vars[14]), "drilldown_limit", -1);
3741   grn_plugin_expr_var_init(ctx, &(vars[15]), "cache", -1);
3742   grn_plugin_expr_var_init(ctx, &(vars[16]), "match_escalation_threshold", -1);
3743   /* Deprecated. Use query_expander instead. */
3744   grn_plugin_expr_var_init(ctx, &(vars[17]), "query_expansion", -1);
3745   grn_plugin_expr_var_init(ctx, &(vars[18]), "query_flags", -1);
3746   grn_plugin_expr_var_init(ctx, &(vars[19]), "query_expander", -1);
3747   grn_plugin_expr_var_init(ctx, &(vars[20]), "adjuster", -1);
3748   grn_plugin_expr_var_init(ctx, &(vars[21]), "drilldown_calc_types", -1);
3749   grn_plugin_expr_var_init(ctx, &(vars[22]), "drilldown_calc_target", -1);
3750   grn_plugin_expr_var_init(ctx, &(vars[23]), "drilldown_filter", -1);
3751   grn_plugin_expr_var_init(ctx, &(vars[24]), "sort_keys", -1);
3752   grn_plugin_expr_var_init(ctx, &(vars[25]), "drilldown_sort_keys", -1);
3753 }
3754 
3755 void
grn_proc_init_select(grn_ctx * ctx)3756 grn_proc_init_select(grn_ctx *ctx)
3757 {
3758   DEFINE_VARS;
3759 
3760   init_vars(ctx, vars);
3761   grn_plugin_command_create(ctx,
3762                             "select", -1,
3763                             command_select,
3764                             N_VARS - 1,
3765                             vars + 1);
3766 }
3767 
3768 static grn_obj *
command_define_selector(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)3769 command_define_selector(grn_ctx *ctx, int nargs, grn_obj **args,
3770                         grn_user_data *user_data)
3771 {
3772   uint32_t i, nvars;
3773   grn_expr_var *vars;
3774 
3775   grn_proc_get_info(ctx, user_data, &vars, &nvars, NULL);
3776   for (i = 1; i < nvars; i++) {
3777     grn_obj *var;
3778     var = grn_plugin_proc_get_var_by_offset(ctx, user_data, i);
3779     GRN_TEXT_SET(ctx, &((vars + i)->value),
3780                  GRN_TEXT_VALUE(var),
3781                  GRN_TEXT_LEN(var));
3782   }
3783   {
3784     grn_obj *name;
3785     name = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
3786     grn_plugin_command_create(ctx,
3787                               GRN_TEXT_VALUE(name),
3788                               GRN_TEXT_LEN(name),
3789                               command_select,
3790                               nvars - 1,
3791                               vars + 1);
3792   }
3793   GRN_OUTPUT_BOOL(!ctx->rc);
3794 
3795   return NULL;
3796 }
3797 
3798 void
grn_proc_init_define_selector(grn_ctx * ctx)3799 grn_proc_init_define_selector(grn_ctx *ctx)
3800 {
3801   DEFINE_VARS;
3802 
3803   init_vars(ctx, vars);
3804   grn_plugin_command_create(ctx,
3805                             "define_selector", -1,
3806                             command_define_selector,
3807                             N_VARS,
3808                             vars);
3809 }
3810