1 /* -*- c-basic-offset: 2 -*- */
2 /*
3   Copyright(C) 2009-2016 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 
21 #include "../grn_ctx.h"
22 #include "../grn_str.h"
23 #include "../grn_db.h"
24 
25 #include <groonga/plugin.h>
26 
27 static grn_table_flags
command_table_create_parse_flags(grn_ctx * ctx,const char * nptr,const char * end)28 command_table_create_parse_flags(grn_ctx *ctx,
29                                  const char *nptr,
30                                  const char *end)
31 {
32   grn_table_flags flags = 0;
33   while (nptr < end) {
34     size_t name_size;
35 
36     if (*nptr == '|' || *nptr == ' ') {
37       nptr += 1;
38       continue;
39     }
40 
41 #define CHECK_FLAG(name)                                                \
42     name_size = strlen(#name);                                          \
43     if ((end - nptr) >= name_size &&                                    \
44         memcmp(nptr, #name, name_size) == 0) {                          \
45       flags |= GRN_OBJ_ ## name;                                        \
46       nptr += name_size;                                                \
47       continue;                                                         \
48     }
49 
50     CHECK_FLAG(TABLE_HASH_KEY);
51     CHECK_FLAG(TABLE_PAT_KEY);
52     CHECK_FLAG(TABLE_DAT_KEY);
53     CHECK_FLAG(TABLE_NO_KEY);
54     CHECK_FLAG(KEY_NORMALIZE);
55     CHECK_FLAG(KEY_WITH_SIS);
56     CHECK_FLAG(KEY_LARGE);
57 
58 #undef CHECK_FLAG
59 
60     GRN_PLUGIN_ERROR(ctx,
61                      GRN_INVALID_ARGUMENT,
62                      "[table][create][flags] unknown flag: <%.*s>",
63                      (int)(end - nptr), nptr);
64     return 0;
65   }
66   return flags;
67 }
68 
69 static grn_bool
grn_proc_table_set_token_filters_put(grn_ctx * ctx,grn_obj * token_filters,const char * token_filter_name,int token_filter_name_length)70 grn_proc_table_set_token_filters_put(grn_ctx *ctx,
71                                      grn_obj *token_filters,
72                                      const char *token_filter_name,
73                                      int token_filter_name_length)
74 {
75   grn_obj *token_filter;
76 
77   token_filter = grn_ctx_get(ctx,
78                              token_filter_name,
79                              token_filter_name_length);
80   if (token_filter) {
81     GRN_PTR_PUT(ctx, token_filters, token_filter);
82     return GRN_TRUE;
83   } else {
84     GRN_PLUGIN_ERROR(ctx,
85                      GRN_INVALID_ARGUMENT,
86                      "[table][create][token-filter] "
87                      "nonexistent token filter: <%.*s>",
88                      token_filter_name_length, token_filter_name);
89     return GRN_FALSE;
90   }
91 }
92 
93 static grn_bool
grn_proc_table_set_token_filters_fill(grn_ctx * ctx,grn_obj * token_filters,grn_obj * token_filter_names)94 grn_proc_table_set_token_filters_fill(grn_ctx *ctx,
95                                       grn_obj *token_filters,
96                                       grn_obj *token_filter_names)
97 {
98   const char *start, *current, *end;
99   const char *name_start, *name_end;
100   const char *last_name_end;
101 
102   start = GRN_TEXT_VALUE(token_filter_names);
103   end = start + GRN_TEXT_LEN(token_filter_names);
104   current = start;
105   name_start = NULL;
106   name_end = NULL;
107   last_name_end = start;
108   while (current < end) {
109     switch (current[0]) {
110     case ' ' :
111       if (name_start && !name_end) {
112         name_end = current;
113       }
114       break;
115     case ',' :
116       if (!name_start) {
117         goto break_loop;
118       }
119       if (!name_end) {
120         name_end = current;
121       }
122       if (!grn_proc_table_set_token_filters_put(ctx,
123                                                 token_filters,
124                                                 name_start,
125                                                 name_end - name_start)) {
126         return GRN_FALSE;
127       }
128       last_name_end = name_end + 1;
129       name_start = NULL;
130       name_end = NULL;
131       break;
132     default :
133       if (!name_start) {
134         name_start = current;
135       }
136       break;
137     }
138     current++;
139   }
140 
141 break_loop:
142   if (!name_start) {
143     GRN_PLUGIN_ERROR(ctx,
144                      GRN_INVALID_ARGUMENT,
145                      "[table][create][token-filter] empty token filter name: "
146                      "<%.*s|%.*s|%.*s>",
147                      (int)(last_name_end - start), start,
148                      (int)(current - last_name_end), last_name_end,
149                      (int)(end - current), current);
150     return GRN_FALSE;
151   }
152 
153   if (!name_end) {
154     name_end = current;
155   }
156   grn_proc_table_set_token_filters_put(ctx,
157                                        token_filters,
158                                        name_start,
159                                        name_end - name_start);
160 
161   return GRN_TRUE;
162 }
163 
164 grn_bool
grn_proc_table_set_token_filters(grn_ctx * ctx,grn_obj * table,grn_obj * token_filter_names)165 grn_proc_table_set_token_filters(grn_ctx *ctx,
166                                  grn_obj *table,
167                                  grn_obj *token_filter_names)
168 {
169   grn_bool succeeded = GRN_FALSE;
170   grn_obj token_filters;
171 
172   if (GRN_TEXT_LEN(token_filter_names) == 0) {
173     return GRN_TRUE;
174   }
175 
176   GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, 0);
177   succeeded = grn_proc_table_set_token_filters_fill(ctx,
178                                                     &token_filters,
179                                                     token_filter_names);
180   if (succeeded) {
181     grn_obj_set_info(ctx, table, GRN_INFO_TOKEN_FILTERS, &token_filters);
182   }
183   grn_obj_unlink(ctx, &token_filters);
184 
185   return succeeded;
186 }
187 
188 static grn_obj *
command_table_create(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)189 command_table_create(grn_ctx *ctx,
190                      int nargs,
191                      grn_obj **args,
192                      grn_user_data *user_data)
193 {
194   grn_obj *name;
195   grn_obj *flags_raw;
196   grn_obj *key_type_name;
197   grn_obj *value_type_name;
198   grn_obj *default_tokenizer_name;
199   grn_obj *normalizer_name;
200   grn_obj *token_filters_name;
201   grn_obj *table;
202   const char *rest;
203   grn_table_flags flags;
204 
205   name = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
206   flags_raw = grn_plugin_proc_get_var(ctx, user_data, "flags", -1);
207   key_type_name = grn_plugin_proc_get_var(ctx, user_data, "key_type", -1);
208   value_type_name = grn_plugin_proc_get_var(ctx, user_data, "value_type", -1);
209   default_tokenizer_name =
210     grn_plugin_proc_get_var(ctx, user_data, "default_tokenizer", -1);
211   normalizer_name =
212     grn_plugin_proc_get_var(ctx, user_data, "normalizer", -1);
213   token_filters_name =
214     grn_plugin_proc_get_var(ctx, user_data, "token_filters", -1);
215 
216   flags = grn_atoi(GRN_TEXT_VALUE(flags_raw),
217                    GRN_BULK_CURR(flags_raw),
218                    &rest);
219 
220   if (GRN_TEXT_VALUE(flags_raw) == rest) {
221     flags = command_table_create_parse_flags(ctx,
222                                              GRN_TEXT_VALUE(flags_raw),
223                                              GRN_BULK_CURR(flags_raw));
224     if (ctx->rc) { goto exit; }
225   }
226 
227   if (GRN_TEXT_LEN(name) == 0) {
228     GRN_PLUGIN_ERROR(ctx,
229                      GRN_INVALID_ARGUMENT,
230                      "[table][create] should not create anonymous table");
231     goto exit;
232   }
233 
234   {
235     grn_obj *key_type = NULL;
236     grn_obj *value_type = NULL;
237 
238     if (GRN_TEXT_LEN(key_type_name) > 0) {
239       key_type = grn_ctx_get(ctx,
240                              GRN_TEXT_VALUE(key_type_name),
241                              GRN_TEXT_LEN(key_type_name));
242       if (!key_type) {
243         GRN_PLUGIN_ERROR(ctx,
244                          GRN_INVALID_ARGUMENT,
245                          "[table][create] "
246                          "key type doesn't exist: <%.*s> (%.*s)",
247                          (int)GRN_TEXT_LEN(name),
248                          GRN_TEXT_VALUE(name),
249                          (int)GRN_TEXT_LEN(key_type_name),
250                          GRN_TEXT_VALUE(key_type_name));
251         goto exit;
252       }
253     }
254 
255     if (GRN_TEXT_LEN(value_type_name) > 0) {
256       value_type = grn_ctx_get(ctx,
257                                GRN_TEXT_VALUE(value_type_name),
258                                GRN_TEXT_LEN(value_type_name));
259       if (!value_type) {
260         GRN_PLUGIN_ERROR(ctx,
261                          GRN_INVALID_ARGUMENT,
262                          "[table][create] "
263                          "value type doesn't exist: <%.*s> (%.*s)",
264                          (int)GRN_TEXT_LEN(name),
265                          GRN_TEXT_VALUE(name),
266                          (int)GRN_TEXT_LEN(value_type_name),
267                          GRN_TEXT_VALUE(value_type_name));
268         goto exit;
269       }
270     }
271 
272     flags |= GRN_OBJ_PERSISTENT;
273     table = grn_table_create(ctx,
274                              GRN_TEXT_VALUE(name),
275                              GRN_TEXT_LEN(name),
276                              NULL, flags,
277                              key_type,
278                              value_type);
279     if (!table) {
280       goto exit;
281     }
282 
283     if (GRN_TEXT_LEN(default_tokenizer_name) > 0) {
284       grn_obj *default_tokenizer;
285 
286       default_tokenizer =
287         grn_ctx_get(ctx,
288                     GRN_TEXT_VALUE(default_tokenizer_name),
289                     GRN_TEXT_LEN(default_tokenizer_name));
290       if (!default_tokenizer) {
291         GRN_PLUGIN_ERROR(ctx,
292                          GRN_INVALID_ARGUMENT,
293                          "[table][create][%.*s] unknown tokenizer: <%.*s>",
294                          (int)GRN_TEXT_LEN(name),
295                          GRN_TEXT_VALUE(name),
296                          (int)GRN_TEXT_LEN(default_tokenizer_name),
297                          GRN_TEXT_VALUE(default_tokenizer_name));
298         grn_obj_remove(ctx, table);
299         goto exit;
300       }
301       grn_obj_set_info(ctx, table,
302                        GRN_INFO_DEFAULT_TOKENIZER,
303                        default_tokenizer);
304     }
305 
306     if (GRN_TEXT_LEN(normalizer_name) > 0) {
307       grn_obj *normalizer;
308 
309       normalizer =
310         grn_ctx_get(ctx,
311                     GRN_TEXT_VALUE(normalizer_name),
312                     GRN_TEXT_LEN(normalizer_name));
313       if (!normalizer) {
314         GRN_PLUGIN_ERROR(ctx,
315                          GRN_INVALID_ARGUMENT,
316                          "[table][create][%.*s] unknown normalizer: <%.*s>",
317                          (int)GRN_TEXT_LEN(name),
318                          GRN_TEXT_VALUE(name),
319                          (int)GRN_TEXT_LEN(normalizer_name),
320                          GRN_TEXT_VALUE(normalizer_name));
321         grn_obj_remove(ctx, table);
322         goto exit;
323       }
324       grn_obj_set_info(ctx, table, GRN_INFO_NORMALIZER, normalizer);
325     }
326 
327     if (!grn_proc_table_set_token_filters(ctx, table, token_filters_name)) {
328       grn_obj_remove(ctx, table);
329       goto exit;
330     }
331 
332     grn_obj_unlink(ctx, table);
333   }
334 
335 exit :
336   grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS);
337   return NULL;
338 }
339 
340 void
grn_proc_init_table_create(grn_ctx * ctx)341 grn_proc_init_table_create(grn_ctx *ctx)
342 {
343   grn_expr_var vars[7];
344 
345   grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1);
346   grn_plugin_expr_var_init(ctx, &(vars[1]), "flags", -1);
347   grn_plugin_expr_var_init(ctx, &(vars[2]), "key_type", -1);
348   grn_plugin_expr_var_init(ctx, &(vars[3]), "value_type", -1);
349   grn_plugin_expr_var_init(ctx, &(vars[4]), "default_tokenizer", -1);
350   grn_plugin_expr_var_init(ctx, &(vars[5]), "normalizer", -1);
351   grn_plugin_expr_var_init(ctx, &(vars[6]), "token_filters", -1);
352   grn_plugin_command_create(ctx,
353                             "table_create", -1,
354                             command_table_create,
355                             7,
356                             vars);
357 }
358 
359 static int
output_table_info(grn_ctx * ctx,grn_obj * table)360 output_table_info(grn_ctx *ctx, grn_obj *table)
361 {
362   grn_id id;
363   grn_obj o;
364   const char *path;
365   grn_table_flags flags;
366   grn_obj *default_tokenizer;
367   grn_obj *normalizer;
368   grn_obj *token_filters;
369 
370   id = grn_obj_id(ctx, table);
371   path = grn_obj_path(ctx, table);
372   GRN_TEXT_INIT(&o, 0);
373   grn_ctx_output_array_open(ctx, "TABLE", 8);
374   grn_ctx_output_int64(ctx, id);
375   grn_proc_output_object_id_name(ctx, id);
376   grn_ctx_output_cstr(ctx, path);
377   GRN_BULK_REWIND(&o);
378 
379   grn_table_get_info(ctx, table,
380                      &flags,
381                      NULL,
382                      &default_tokenizer,
383                      &normalizer,
384                      &token_filters);
385   grn_dump_table_create_flags(ctx, flags, &o);
386   grn_ctx_output_obj(ctx, &o, NULL);
387   grn_proc_output_object_id_name(ctx, table->header.domain);
388   grn_proc_output_object_id_name(ctx, grn_obj_get_range(ctx, table));
389   grn_proc_output_object_name(ctx, default_tokenizer);
390   grn_proc_output_object_name(ctx, normalizer);
391   grn_ctx_output_array_close(ctx);
392   GRN_OBJ_FIN(ctx, &o);
393   return 1;
394 }
395 
396 static grn_obj *
command_table_list(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)397 command_table_list(grn_ctx *ctx, int nargs, grn_obj **args,
398                    grn_user_data *user_data)
399 {
400   grn_obj *db;
401   grn_obj tables;
402   int n_top_level_elements;
403   int n_elements_for_header = 1;
404   int n_tables;
405   int i;
406 
407   db = grn_ctx_db(ctx);
408 
409   {
410     grn_table_cursor *cursor;
411     grn_id id;
412     grn_obj *prefix;
413     const void *min = NULL;
414     unsigned int min_size = 0;
415     int flags = 0;
416 
417     prefix = grn_plugin_proc_get_var(ctx, user_data, "prefix", -1);
418     if (GRN_TEXT_LEN(prefix) > 0) {
419       min = GRN_TEXT_VALUE(prefix);
420       min_size = GRN_TEXT_LEN(prefix);
421       flags |= GRN_CURSOR_PREFIX;
422     }
423     cursor = grn_table_cursor_open(ctx, db,
424                                    min, min_size,
425                                    NULL, 0,
426                                    0, -1, flags);
427     if (!cursor) {
428       return NULL;
429     }
430 
431     GRN_PTR_INIT(&tables, GRN_OBJ_VECTOR, GRN_ID_NIL);
432     while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
433       grn_obj *object;
434       const char *name;
435       void *key;
436       int i, key_size;
437       grn_bool have_period = GRN_FALSE;
438 
439       key_size = grn_table_cursor_get_key(ctx, cursor, &key);
440       name = key;
441       for (i = 0; i < key_size; i++) {
442         if (name[i] == '.') {
443           have_period = GRN_TRUE;
444           break;
445         }
446       }
447       if (have_period) {
448         continue;
449       }
450 
451       object = grn_ctx_at(ctx, id);
452       if (object) {
453         if (grn_obj_is_table(ctx, object)) {
454           GRN_PTR_PUT(ctx, &tables, object);
455         } else {
456           grn_obj_unlink(ctx, object);
457         }
458       } else {
459         if (ctx->rc != GRN_SUCCESS) {
460           ERRCLR(ctx);
461         }
462       }
463     }
464     grn_table_cursor_close(ctx, cursor);
465   }
466   n_tables = GRN_BULK_VSIZE(&tables) / sizeof(grn_obj *);
467   n_top_level_elements = n_elements_for_header + n_tables;
468   grn_ctx_output_array_open(ctx, "TABLE_LIST", n_top_level_elements);
469 
470   grn_ctx_output_array_open(ctx, "HEADER", 8);
471   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
472   grn_ctx_output_cstr(ctx, "id");
473   grn_ctx_output_cstr(ctx, "UInt32");
474   grn_ctx_output_array_close(ctx);
475   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
476   grn_ctx_output_cstr(ctx, "name");
477   grn_ctx_output_cstr(ctx, "ShortText");
478   grn_ctx_output_array_close(ctx);
479   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
480   grn_ctx_output_cstr(ctx, "path");
481   grn_ctx_output_cstr(ctx, "ShortText");
482   grn_ctx_output_array_close(ctx);
483   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
484   grn_ctx_output_cstr(ctx, "flags");
485   grn_ctx_output_cstr(ctx, "ShortText");
486   grn_ctx_output_array_close(ctx);
487   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
488   grn_ctx_output_cstr(ctx, "domain");
489   grn_ctx_output_cstr(ctx, "ShortText");
490   grn_ctx_output_array_close(ctx);
491   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
492   grn_ctx_output_cstr(ctx, "range");
493   grn_ctx_output_cstr(ctx, "ShortText");
494   grn_ctx_output_array_close(ctx);
495   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
496   grn_ctx_output_cstr(ctx, "default_tokenizer");
497   grn_ctx_output_cstr(ctx, "ShortText");
498   grn_ctx_output_array_close(ctx);
499   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
500   grn_ctx_output_cstr(ctx, "normalizer");
501   grn_ctx_output_cstr(ctx, "ShortText");
502   grn_ctx_output_array_close(ctx);
503   grn_ctx_output_array_close(ctx);
504 
505   for (i = 0; i < n_tables; i++) {
506     grn_obj *table = GRN_PTR_VALUE_AT(&tables, i);
507     output_table_info(ctx, table);
508     grn_obj_unlink(ctx, table);
509   }
510   GRN_OBJ_FIN(ctx, &tables);
511 
512   grn_ctx_output_array_close(ctx);
513 
514   return NULL;
515 }
516 
517 void
grn_proc_init_table_list(grn_ctx * ctx)518 grn_proc_init_table_list(grn_ctx *ctx)
519 {
520   grn_expr_var vars[1];
521 
522   grn_plugin_expr_var_init(ctx, &(vars[0]), "prefix", -1);
523   grn_plugin_command_create(ctx,
524                             "table_list", -1,
525                             command_table_list,
526                             1,
527                             vars);
528 }
529 
530 static grn_obj *
command_table_remove(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)531 command_table_remove(grn_ctx *ctx,
532                      int nargs,
533                      grn_obj **args,
534                      grn_user_data *user_data)
535 {
536   grn_obj *name;
537   grn_obj *table;
538   grn_bool dependent;
539 
540   name = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
541   dependent = grn_plugin_proc_get_var_bool(ctx, user_data, "dependent", -1,
542                                            GRN_FALSE);
543   table = grn_ctx_get(ctx,
544                       GRN_TEXT_VALUE(name),
545                       GRN_TEXT_LEN(name));
546   if (!table) {
547     GRN_PLUGIN_ERROR(ctx,
548                      GRN_INVALID_ARGUMENT,
549                      "[table][remove] table isn't found: <%.*s>",
550                      (int)GRN_TEXT_LEN(name),
551                      GRN_TEXT_VALUE(name));
552     grn_ctx_output_bool(ctx, GRN_FALSE);
553     return NULL;
554   }
555 
556   if (!grn_obj_is_table(ctx, table)) {
557     const char *type_name;
558     type_name = grn_obj_type_to_string(table->header.type);
559     grn_obj_unlink(ctx, table);
560     GRN_PLUGIN_ERROR(ctx,
561                      GRN_INVALID_ARGUMENT,
562                      "[table][remove] not table: <%.*s>: <%s>",
563                      (int)GRN_TEXT_LEN(name),
564                      GRN_TEXT_VALUE(name),
565                      type_name);
566     grn_ctx_output_bool(ctx, GRN_FALSE);
567     return NULL;
568   }
569 
570   if (dependent) {
571     grn_obj_remove_dependent(ctx, table);
572   } else {
573     grn_obj_remove(ctx, table);
574   }
575   grn_ctx_output_bool(ctx, !ctx->rc);
576   return NULL;
577 }
578 
579 void
grn_proc_init_table_remove(grn_ctx * ctx)580 grn_proc_init_table_remove(grn_ctx *ctx)
581 {
582   grn_expr_var vars[2];
583 
584   grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1);
585   grn_plugin_expr_var_init(ctx, &(vars[1]), "dependent", -1);
586   grn_plugin_command_create(ctx,
587                             "table_remove", -1,
588                             command_table_remove,
589                             2,
590                             vars);
591 }
592 
593 static grn_obj *
command_table_rename(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)594 command_table_rename(grn_ctx *ctx,
595                      int nargs,
596                      grn_obj **args,
597                      grn_user_data *user_data)
598 {
599   grn_rc rc = GRN_SUCCESS;
600   grn_obj *name;
601   grn_obj *new_name;
602   grn_obj *table = NULL;
603 
604   name = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
605   new_name = grn_plugin_proc_get_var(ctx, user_data, "new_name", -1);
606   if (GRN_TEXT_LEN(name) == 0) {
607     rc = GRN_INVALID_ARGUMENT;
608     GRN_PLUGIN_ERROR(ctx, rc, "[table][rename] table name isn't specified");
609     goto exit;
610   }
611   table = grn_ctx_get(ctx, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name));
612   if (!table) {
613     rc = GRN_INVALID_ARGUMENT;
614     GRN_PLUGIN_ERROR(ctx,
615                      rc,
616                      "[table][rename] table isn't found: <%.*s>",
617                      (int)GRN_TEXT_LEN(name),
618                      GRN_TEXT_VALUE(name));
619     goto exit;
620   }
621   if (GRN_TEXT_LEN(new_name) == 0) {
622     rc = GRN_INVALID_ARGUMENT;
623     GRN_PLUGIN_ERROR(ctx,
624                      rc,
625                      "[table][rename] new table name isn't specified: <%.*s>",
626                      (int)GRN_TEXT_LEN(name),
627                      GRN_TEXT_VALUE(name));
628     goto exit;
629   }
630   rc = grn_table_rename(ctx, table,
631                         GRN_TEXT_VALUE(new_name),
632                         GRN_TEXT_LEN(new_name));
633   if (rc != GRN_SUCCESS && ctx->rc == GRN_SUCCESS) {
634     GRN_PLUGIN_ERROR(ctx,
635                      rc,
636                      "[table][rename] failed to rename: <%.*s> -> <%.*s>",
637                      (int)GRN_TEXT_LEN(name),
638                      GRN_TEXT_VALUE(name),
639                      (int)GRN_TEXT_LEN(new_name),
640                      GRN_TEXT_VALUE(new_name));
641   }
642 exit :
643   grn_ctx_output_bool(ctx, !rc);
644   if (table) { grn_obj_unlink(ctx, table); }
645   return NULL;
646 }
647 
648 void
grn_proc_init_table_rename(grn_ctx * ctx)649 grn_proc_init_table_rename(grn_ctx *ctx)
650 {
651   grn_expr_var vars[2];
652 
653   grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1);
654   grn_plugin_expr_var_init(ctx, &(vars[1]), "new_name", -1);
655   grn_plugin_command_create(ctx,
656                             "table_rename", -1,
657                             command_table_rename,
658                             2,
659                             vars);
660 }
661 
662 static grn_rc
command_table_copy_resolve_target(grn_ctx * ctx,const char * label,grn_obj * name,grn_obj ** table)663 command_table_copy_resolve_target(grn_ctx *ctx,
664                                   const char *label,
665                                   grn_obj *name,
666                                   grn_obj **table)
667 {
668   if (GRN_TEXT_LEN(name) == 0) {
669     GRN_PLUGIN_ERROR(ctx,
670                      GRN_INVALID_ARGUMENT,
671                      "[table][copy] %s name isn't specified",
672                      label);
673     return ctx->rc;
674   }
675   *table = grn_ctx_get(ctx,
676                        GRN_TEXT_VALUE(name),
677                        GRN_TEXT_LEN(name));
678   if (!*table) {
679     GRN_PLUGIN_ERROR(ctx,
680                      GRN_INVALID_ARGUMENT,
681                      "[table][copy] %s table isn't found: <%.*s>",
682                      label,
683                      (int)GRN_TEXT_LEN(name),
684                      GRN_TEXT_VALUE(name));
685     return ctx->rc;
686   }
687 
688   return ctx->rc;
689 }
690 
691 static void
command_table_copy_same_key_type(grn_ctx * ctx,grn_obj * from_table,grn_obj * to_table,grn_obj * from_name,grn_obj * to_name)692 command_table_copy_same_key_type(grn_ctx *ctx,
693                                  grn_obj *from_table,
694                                  grn_obj *to_table,
695                                  grn_obj *from_name,
696                                  grn_obj *to_name)
697 {
698   GRN_TABLE_EACH_BEGIN_FLAGS(ctx, from_table, cursor, from_id,
699                              GRN_CURSOR_BY_KEY | GRN_CURSOR_ASCENDING) {
700     void   *key;
701     int     key_size;
702     grn_id  to_id;
703 
704     key_size = grn_table_cursor_get_key(ctx, cursor, &key);
705     to_id    = grn_table_add(ctx, to_table, key, key_size, NULL);
706     if (to_id == GRN_ID_NIL) {
707       grn_obj key_buffer;
708       grn_obj inspected_key;
709       if (from_table->header.domain == GRN_DB_SHORT_TEXT) {
710         GRN_SHORT_TEXT_INIT(&key_buffer, 0);
711       } else {
712         GRN_VALUE_FIX_SIZE_INIT(&key_buffer, 0, from_table->header.domain);
713       }
714       grn_bulk_write(ctx, &key_buffer, key, key_size);
715       GRN_TEXT_INIT(&inspected_key, 0);
716       grn_inspect(ctx, &inspected_key, &key_buffer);
717       GRN_PLUGIN_ERROR(ctx,
718                        GRN_INVALID_ARGUMENT,
719                        "[table][copy] failed to copy key: <%.*s>: "
720                        "<%.*s> -> <%.*s>",
721                        (int)GRN_TEXT_LEN(&inspected_key),
722                        GRN_TEXT_VALUE(&inspected_key),
723                        (int)GRN_TEXT_LEN(from_name),
724                        GRN_TEXT_VALUE(from_name),
725                        (int)GRN_TEXT_LEN(to_name),
726                        GRN_TEXT_VALUE(to_name));
727       GRN_OBJ_FIN(ctx, &inspected_key);
728       GRN_OBJ_FIN(ctx, &key_buffer);
729       break;
730     }
731   } GRN_TABLE_EACH_END(ctx, cursor);
732 }
733 
734 static void
command_table_copy_different(grn_ctx * ctx,grn_obj * from_table,grn_obj * to_table,grn_obj * from_name,grn_obj * to_name)735 command_table_copy_different(grn_ctx *ctx,
736                               grn_obj *from_table,
737                               grn_obj *to_table,
738                               grn_obj *from_name,
739                               grn_obj *to_name)
740 {
741   grn_obj from_key_buffer;
742   grn_obj to_key_buffer;
743 
744   if (from_table->header.domain == GRN_DB_SHORT_TEXT) {
745     GRN_SHORT_TEXT_INIT(&from_key_buffer, 0);
746   } else {
747     GRN_VALUE_FIX_SIZE_INIT(&from_key_buffer, 0, from_table->header.domain);
748   }
749   if (to_table->header.domain == GRN_DB_SHORT_TEXT) {
750     GRN_SHORT_TEXT_INIT(&to_key_buffer, 0);
751   } else {
752     GRN_VALUE_FIX_SIZE_INIT(&to_key_buffer, 0, to_table->header.domain);
753   }
754 
755   GRN_TABLE_EACH_BEGIN_FLAGS(ctx, from_table, cursor, from_id,
756                              GRN_CURSOR_BY_KEY | GRN_CURSOR_ASCENDING) {
757     void *key;
758     int key_size;
759     grn_rc cast_rc;
760     grn_id to_id;
761 
762     GRN_BULK_REWIND(&from_key_buffer);
763     GRN_BULK_REWIND(&to_key_buffer);
764 
765     key_size = grn_table_cursor_get_key(ctx, cursor, &key);
766     grn_bulk_write(ctx, &from_key_buffer, key, key_size);
767     cast_rc = grn_obj_cast(ctx, &from_key_buffer, &to_key_buffer, GRN_FALSE);
768     if (cast_rc != GRN_SUCCESS) {
769       grn_obj *to_key_type;
770       grn_obj inspected_key;
771       grn_obj inspected_to_key_type;
772 
773       to_key_type = grn_ctx_at(ctx, to_table->header.domain);
774       GRN_TEXT_INIT(&inspected_key, 0);
775       GRN_TEXT_INIT(&inspected_to_key_type, 0);
776       grn_inspect(ctx, &inspected_key, &from_key_buffer);
777       grn_inspect(ctx, &inspected_to_key_type, to_key_type);
778       ERR(cast_rc,
779           "[table][copy] failed to cast key: <%.*s> -> %.*s: "
780           "<%.*s> -> <%.*s>",
781           (int)GRN_TEXT_LEN(&inspected_key),
782           GRN_TEXT_VALUE(&inspected_key),
783           (int)GRN_TEXT_LEN(&inspected_to_key_type),
784           GRN_TEXT_VALUE(&inspected_to_key_type),
785           (int)GRN_TEXT_LEN(from_name),
786           GRN_TEXT_VALUE(from_name),
787           (int)GRN_TEXT_LEN(to_name),
788           GRN_TEXT_VALUE(to_name));
789       GRN_OBJ_FIN(ctx, &inspected_key);
790       GRN_OBJ_FIN(ctx, &inspected_to_key_type);
791       break;
792     }
793 
794     to_id = grn_table_add(ctx, to_table,
795                           GRN_BULK_HEAD(&to_key_buffer),
796                           GRN_BULK_VSIZE(&to_key_buffer),
797                           NULL);
798     if (to_id == GRN_ID_NIL) {
799       grn_obj inspected_from_key;
800       grn_obj inspected_to_key;
801       GRN_TEXT_INIT(&inspected_from_key, 0);
802       GRN_TEXT_INIT(&inspected_to_key, 0);
803       grn_inspect(ctx, &inspected_from_key, &from_key_buffer);
804       grn_inspect(ctx, &inspected_to_key, &to_key_buffer);
805       GRN_PLUGIN_ERROR(ctx,
806                        GRN_INVALID_ARGUMENT,
807                        "[table][copy] failed to copy key: <%.*s> -> <%.*s>: "
808                        "<%.*s> -> <%.*s>",
809                        (int)GRN_TEXT_LEN(&inspected_from_key),
810                        GRN_TEXT_VALUE(&inspected_from_key),
811                        (int)GRN_TEXT_LEN(&inspected_to_key),
812                        GRN_TEXT_VALUE(&inspected_to_key),
813                        (int)GRN_TEXT_LEN(from_name),
814                        GRN_TEXT_VALUE(from_name),
815                        (int)GRN_TEXT_LEN(to_name),
816                        GRN_TEXT_VALUE(to_name));
817       GRN_OBJ_FIN(ctx, &inspected_from_key);
818       GRN_OBJ_FIN(ctx, &inspected_to_key);
819       break;
820     }
821   } GRN_TABLE_EACH_END(ctx, cursor);
822   GRN_OBJ_FIN(ctx, &from_key_buffer);
823   GRN_OBJ_FIN(ctx, &to_key_buffer);
824 }
825 
826 static grn_obj *
command_table_copy(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)827 command_table_copy(grn_ctx *ctx,
828                    int nargs,
829                    grn_obj **args,
830                    grn_user_data *user_data)
831 {
832   grn_rc rc = GRN_SUCCESS;
833   grn_obj *from_table = NULL;
834   grn_obj *to_table = NULL;
835   grn_obj *from_name;
836   grn_obj *to_name;
837 
838   from_name = grn_plugin_proc_get_var(ctx, user_data, "from_name", -1);
839   to_name   = grn_plugin_proc_get_var(ctx, user_data, "to_name", -1);
840 
841   rc = command_table_copy_resolve_target(ctx, "from", from_name, &from_table);
842   if (rc != GRN_SUCCESS) {
843     goto exit;
844   }
845   rc = command_table_copy_resolve_target(ctx, "to", to_name, &to_table);
846   if (rc != GRN_SUCCESS) {
847     goto exit;
848   }
849 
850   if (from_table->header.type == GRN_TABLE_NO_KEY ||
851       to_table->header.type == GRN_TABLE_NO_KEY) {
852     GRN_PLUGIN_ERROR(ctx,
853                      GRN_OPERATION_NOT_SUPPORTED,
854                      "[table][copy] copy from/to TABLE_NO_KEY isn't supported: "
855                      "<%.*s> -> <%.*s>",
856                      (int)GRN_TEXT_LEN(from_name),
857                      GRN_TEXT_VALUE(from_name),
858                      (int)GRN_TEXT_LEN(to_name),
859                      GRN_TEXT_VALUE(to_name));
860     rc = ctx->rc;
861     goto exit;
862   }
863 
864   if (from_table == to_table) {
865     GRN_PLUGIN_ERROR(ctx,
866                      GRN_OPERATION_NOT_SUPPORTED,
867                      "[table][copy] from table and to table is the same: "
868                      "<%.*s>",
869                      (int)GRN_TEXT_LEN(from_name),
870                      GRN_TEXT_VALUE(from_name));
871     rc = ctx->rc;
872     goto exit;
873   }
874 
875   if (from_table->header.domain == to_table->header.domain) {
876     command_table_copy_same_key_type(ctx,
877                                      from_table, to_table,
878                                      from_name, to_name);
879   } else {
880     command_table_copy_different(ctx,
881                                  from_table, to_table,
882                                  from_name, to_name);
883   }
884 
885 exit :
886   grn_ctx_output_bool(ctx, rc == GRN_SUCCESS);
887 
888   if (to_table) {
889     grn_obj_unlink(ctx, to_table);
890   }
891   if (from_table) {
892     grn_obj_unlink(ctx, from_table);
893   }
894 
895   return NULL;
896 }
897 
898 void
grn_proc_init_table_copy(grn_ctx * ctx)899 grn_proc_init_table_copy(grn_ctx *ctx)
900 {
901   grn_expr_var vars[2];
902 
903   grn_plugin_expr_var_init(ctx, &(vars[0]), "from_name", -1);
904   grn_plugin_expr_var_init(ctx, &(vars[1]), "to_name", -1);
905   grn_plugin_command_create(ctx,
906                             "table_copy", -1,
907                             command_table_copy,
908                             2,
909                             vars);
910 }
911