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_db.h"
23 #include "../grn_str.h"
24 
25 #include <groonga/plugin.h>
26 
27 grn_column_flags
grn_proc_column_parse_flags(grn_ctx * ctx,const char * error_message_tag,const char * text,const char * end)28 grn_proc_column_parse_flags(grn_ctx *ctx,
29                             const char *error_message_tag,
30                             const char *text,
31                             const char *end)
32 {
33   grn_column_flags flags = 0;
34   while (text < end) {
35     size_t name_size;
36 
37     if (*text == '|' || *text == ' ') {
38       text += 1;
39       continue;
40     }
41 
42 #define CHECK_FLAG(name)                                                \
43     name_size = strlen(#name);                                          \
44     if ((end - text) >= name_size &&                                    \
45         memcmp(text, #name, name_size) == 0) {                          \
46       flags |= GRN_OBJ_ ## name;                                        \
47       text += name_size;                                                \
48       continue;                                                         \
49     }
50 
51     CHECK_FLAG(COLUMN_SCALAR);
52     CHECK_FLAG(COLUMN_VECTOR);
53     CHECK_FLAG(COLUMN_INDEX);
54     CHECK_FLAG(COMPRESS_ZLIB);
55     CHECK_FLAG(COMPRESS_LZ4);
56     CHECK_FLAG(COMPRESS_ZSTD);
57     CHECK_FLAG(WITH_SECTION);
58     CHECK_FLAG(WITH_WEIGHT);
59     CHECK_FLAG(WITH_POSITION);
60     CHECK_FLAG(RING_BUFFER);
61     CHECK_FLAG(INDEX_SMALL);
62     CHECK_FLAG(INDEX_MEDIUM);
63 
64 #undef CHECK_FLAG
65 
66     ERR(GRN_INVALID_ARGUMENT,
67         "%s unknown flag: <%.*s>",
68         error_message_tag,
69         (int)(end - text), text);
70     return 0;
71   }
72   return flags;
73 }
74 
75 static grn_rc
command_column_create_resolve_source_name(grn_ctx * ctx,grn_obj * table,const char * source_name,int source_name_length,grn_obj * source_ids)76 command_column_create_resolve_source_name(grn_ctx *ctx,
77                                           grn_obj *table,
78                                           const char *source_name,
79                                           int source_name_length,
80                                           grn_obj *source_ids)
81 {
82   grn_obj *column;
83 
84   column = grn_obj_column(ctx, table, source_name, source_name_length);
85   if (!column) {
86     ERR(GRN_INVALID_ARGUMENT,
87         "[column][create] nonexistent source: <%.*s>",
88         source_name_length, source_name);
89     return ctx->rc;
90   }
91 
92   if (column->header.type == GRN_ACCESSOR) {
93     if (strncmp(source_name, "_key", source_name_length) == 0) {
94       grn_id source_id = grn_obj_id(ctx, table);
95       GRN_UINT32_PUT(ctx, source_ids, source_id);
96     } else {
97       ERR(GRN_INVALID_ARGUMENT,
98           "[column][create] pseudo column except <_key> is invalid: <%.*s>",
99           source_name_length, source_name);
100     }
101   } else {
102     grn_id source_id = grn_obj_id(ctx, column);
103     GRN_UINT32_PUT(ctx, source_ids, source_id);
104   }
105   grn_obj_unlink(ctx, column);
106 
107   return ctx->rc;
108 }
109 
110 static grn_rc
command_column_create_resolve_source_names(grn_ctx * ctx,grn_obj * table,grn_obj * source_names,grn_obj * source_ids)111 command_column_create_resolve_source_names(grn_ctx *ctx,
112                                            grn_obj *table,
113                                            grn_obj *source_names,
114                                            grn_obj *source_ids)
115 {
116   int i, names_length;
117   int start, source_name_length;
118   const char *names;
119 
120   names = GRN_TEXT_VALUE(source_names);
121   start = 0;
122   source_name_length = 0;
123   names_length = GRN_TEXT_LEN(source_names);
124   for (i = 0; i < names_length; i++) {
125     switch (names[i]) {
126     case ' ' :
127       if (source_name_length == 0) {
128         start++;
129       }
130       break;
131     case ',' :
132       {
133         grn_rc rc;
134         const char *source_name = names + start;
135         rc = command_column_create_resolve_source_name(ctx,
136                                                        table,
137                                                        source_name,
138                                                        source_name_length,
139                                                        source_ids);
140         if (rc) {
141           return rc;
142         }
143         start = i + 1;
144         source_name_length = 0;
145       }
146       break;
147     default :
148       source_name_length++;
149       break;
150     }
151   }
152 
153   if (source_name_length > 0) {
154     grn_rc rc;
155     const char *source_name = names + start;
156     rc = command_column_create_resolve_source_name(ctx,
157                                                    table,
158                                                    source_name,
159                                                    source_name_length,
160                                                    source_ids);
161     if (rc) {
162       return rc;
163     }
164   }
165 
166   return GRN_SUCCESS;
167 }
168 
169 static grn_obj *
command_column_create(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)170 command_column_create(grn_ctx *ctx, int nargs, grn_obj **args,
171                       grn_user_data *user_data)
172 {
173   grn_bool succeeded = GRN_TRUE;
174   grn_obj *table;
175   grn_obj *column;
176   grn_obj *table_raw;
177   grn_obj *name;
178   grn_obj *flags_raw;
179   grn_obj *type_raw;
180   grn_obj *source_raw;
181   grn_column_flags flags;
182   grn_obj *type = NULL;
183 
184   table_raw  = grn_plugin_proc_get_var(ctx, user_data, "table", -1);
185   name       = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
186   flags_raw  = grn_plugin_proc_get_var(ctx, user_data, "flags", -1);
187   type_raw   = grn_plugin_proc_get_var(ctx, user_data, "type", -1);
188   source_raw = grn_plugin_proc_get_var(ctx, user_data, "source", -1);
189 
190   table = grn_ctx_get(ctx, GRN_TEXT_VALUE(table_raw), GRN_TEXT_LEN(table_raw));
191   if (!table) {
192     GRN_PLUGIN_ERROR(ctx,
193                      GRN_INVALID_ARGUMENT,
194                      "[column][create] table doesn't exist: <%.*s>",
195                      (int)GRN_TEXT_LEN(table_raw),
196                      GRN_TEXT_VALUE(table_raw));
197     succeeded = GRN_FALSE;
198     goto exit;
199   }
200 
201   {
202     const char *rest;
203     flags = grn_atoi(GRN_TEXT_VALUE(flags_raw),
204                      GRN_BULK_CURR(flags_raw),
205                      &rest);
206     if (GRN_TEXT_VALUE(flags_raw) == rest) {
207       flags = grn_proc_column_parse_flags(ctx,
208                                           "[column][create][flags]",
209                                           GRN_TEXT_VALUE(flags_raw),
210                                           GRN_BULK_CURR(flags_raw));
211       if (ctx->rc) {
212         succeeded = GRN_FALSE;
213         goto exit;
214       }
215     }
216   }
217 
218   type = grn_ctx_get(ctx,
219                      GRN_TEXT_VALUE(type_raw),
220                      GRN_TEXT_LEN(type_raw));
221   if (!type) {
222     GRN_PLUGIN_ERROR(ctx,
223                      GRN_INVALID_ARGUMENT,
224                      "[column][create] type doesn't exist: <%.*s>",
225                      (int)GRN_TEXT_LEN(type_raw),
226                      GRN_TEXT_VALUE(type_raw));
227     succeeded = GRN_FALSE;
228     goto exit;
229   }
230 
231   if (GRN_TEXT_LEN(name) == 0) {
232     GRN_PLUGIN_ERROR(ctx,
233                      GRN_INVALID_ARGUMENT,
234                      "[column][create] name is missing");
235     succeeded = GRN_FALSE;
236     goto exit;
237   }
238   flags |= GRN_OBJ_PERSISTENT;
239 
240   column = grn_column_create(ctx, table,
241                              GRN_TEXT_VALUE(name),
242                              GRN_TEXT_LEN(name),
243                              NULL, flags, type);
244   if (!column) {
245     succeeded = GRN_FALSE;
246     goto exit;
247   }
248 
249   if (GRN_TEXT_LEN(source_raw) > 0) {
250     grn_rc rc;
251     grn_obj source_ids;
252     GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
253     rc = command_column_create_resolve_source_names(ctx,
254                                                     type,
255                                                     source_raw,
256                                                     &source_ids);
257     if (rc == GRN_SUCCESS && GRN_BULK_VSIZE(&source_ids) > 0) {
258       grn_obj_set_info(ctx, column, GRN_INFO_SOURCE, &source_ids);
259       rc = ctx->rc;
260     }
261     GRN_OBJ_FIN(ctx, &source_ids);
262     if (rc != GRN_SUCCESS) {
263       grn_obj_remove(ctx, column);
264       succeeded = GRN_FALSE;
265       goto exit;
266     }
267   }
268 
269   grn_obj_unlink(ctx, column);
270 
271 exit :
272   grn_ctx_output_bool(ctx, succeeded);
273   if (table) { grn_obj_unlink(ctx, table); }
274   if (type) { grn_obj_unlink(ctx, type); }
275 
276   return NULL;
277 }
278 
279 void
grn_proc_init_column_create(grn_ctx * ctx)280 grn_proc_init_column_create(grn_ctx *ctx)
281 {
282   grn_expr_var vars[5];
283 
284   grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1);
285   grn_plugin_expr_var_init(ctx, &(vars[1]), "name", -1);
286   grn_plugin_expr_var_init(ctx, &(vars[2]), "flags", -1);
287   grn_plugin_expr_var_init(ctx, &(vars[3]), "type", -1);
288   grn_plugin_expr_var_init(ctx, &(vars[4]), "source", -1);
289   grn_plugin_command_create(ctx,
290                             "column_create", -1,
291                             command_column_create,
292                             5,
293                             vars);
294 }
295 
296 static grn_obj *
command_column_remove(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)297 command_column_remove(grn_ctx *ctx, int nargs, grn_obj **args,
298                       grn_user_data *user_data)
299 {
300   grn_obj *table_raw;
301   grn_obj *name;
302   grn_obj *table;
303   grn_obj *column;
304   char fullname[GRN_TABLE_MAX_KEY_SIZE];
305   unsigned int fullname_len;
306 
307   table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1);
308   name      = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
309 
310   table = grn_ctx_get(ctx,
311                       GRN_TEXT_VALUE(table_raw),
312                       GRN_TEXT_LEN(table_raw));
313 
314   fullname_len = grn_obj_name(ctx, table, fullname, GRN_TABLE_MAX_KEY_SIZE);
315   if (fullname_len == 0) {
316     GRN_PLUGIN_ERROR(ctx,
317                      GRN_INVALID_ARGUMENT,
318                      "[column][remove] table isn't found: <%.*s>",
319                      (int)GRN_TEXT_LEN(table_raw),
320                      GRN_TEXT_VALUE(table_raw));
321     grn_ctx_output_bool(ctx, GRN_FALSE);
322     return NULL;
323   }
324 
325   fullname[fullname_len] = GRN_DB_DELIMITER;
326   fullname_len++;
327   if (fullname_len + GRN_TEXT_LEN(name) > GRN_TABLE_MAX_KEY_SIZE) {
328     GRN_PLUGIN_ERROR(ctx,
329                      GRN_INVALID_ARGUMENT,
330                      "[column][remove] column name is too long: <%d> > <%u>: "
331                      "<%.*s>",
332                      (int)GRN_TEXT_LEN(name),
333                      GRN_TABLE_MAX_KEY_SIZE - fullname_len,
334                      (int)GRN_TEXT_LEN(name),
335                      GRN_TEXT_VALUE(name));
336     grn_ctx_output_bool(ctx, GRN_FALSE);
337     return NULL;
338   }
339   grn_memcpy(fullname + fullname_len,
340              GRN_TEXT_VALUE(name),
341              GRN_TEXT_LEN(name));
342   fullname_len += GRN_TEXT_LEN(name);
343   column = grn_ctx_get(ctx, fullname, fullname_len);
344   if (!column) {
345     GRN_PLUGIN_ERROR(ctx,
346                      GRN_INVALID_ARGUMENT,
347                      "[column][remove] column isn't found: <%.*s%c%.*s>",
348                      (int)GRN_TEXT_LEN(table_raw),
349                      GRN_TEXT_VALUE(table_raw),
350                      GRN_DB_DELIMITER,
351                      (int)GRN_TEXT_LEN(name),
352                      GRN_TEXT_VALUE(name));
353     grn_ctx_output_bool(ctx, GRN_FALSE);
354     return NULL;
355   }
356 
357   grn_obj_remove(ctx, column);
358   grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS);
359   return NULL;
360 }
361 
362 void
grn_proc_init_column_remove(grn_ctx * ctx)363 grn_proc_init_column_remove(grn_ctx *ctx)
364 {
365   grn_expr_var vars[2];
366 
367   grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1);
368   grn_plugin_expr_var_init(ctx, &(vars[1]), "name", -1);
369   grn_plugin_command_create(ctx,
370                             "column_remove", -1,
371                             command_column_remove,
372                             2,
373                             vars);
374 }
375 
376 static grn_obj *
command_column_rename(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)377 command_column_rename(grn_ctx *ctx, int nargs, grn_obj **args,
378                       grn_user_data *user_data)
379 {
380   grn_rc rc = GRN_SUCCESS;
381   grn_obj *table_raw;
382   grn_obj *name;
383   grn_obj *new_name;
384   grn_obj *table = NULL;
385   grn_obj *column = NULL;
386 
387   table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1);
388   name      = grn_plugin_proc_get_var(ctx, user_data, "name", -1);
389   new_name  = grn_plugin_proc_get_var(ctx, user_data, "new_name", -1);
390 
391   if (GRN_TEXT_LEN(table_raw) == 0) {
392     rc = GRN_INVALID_ARGUMENT;
393     GRN_PLUGIN_ERROR(ctx,
394                      rc,
395                      "[column][rename] table name isn't specified");
396     goto exit;
397   }
398 
399   table = grn_ctx_get(ctx, GRN_TEXT_VALUE(table_raw), GRN_TEXT_LEN(table_raw));
400   if (!table) {
401     rc = GRN_INVALID_ARGUMENT;
402     GRN_PLUGIN_ERROR(ctx,
403                      rc,
404                      "[column][rename] table isn't found: <%.*s>",
405                      (int)GRN_TEXT_LEN(table_raw),
406                      GRN_TEXT_VALUE(table_raw));
407     goto exit;
408   }
409 
410   if (GRN_TEXT_LEN(name) == 0) {
411     rc = GRN_INVALID_ARGUMENT;
412     GRN_PLUGIN_ERROR(ctx,
413                      rc,
414                      "[column][rename] column name isn't specified: <%.*s>",
415                      (int)GRN_TEXT_LEN(table_raw),
416                      GRN_TEXT_VALUE(table_raw));
417     goto exit;
418   }
419 
420   column = grn_obj_column(ctx, table,
421                           GRN_TEXT_VALUE(name),
422                           GRN_TEXT_LEN(name));
423   if (!column) {
424     rc = GRN_INVALID_ARGUMENT;
425     GRN_PLUGIN_ERROR(ctx,
426                      rc,
427                      "[column][rename] column isn't found: <%.*s%c%.*s>",
428                      (int)GRN_TEXT_LEN(table_raw),
429                      GRN_TEXT_VALUE(table_raw),
430                      GRN_DB_DELIMITER,
431                      (int)GRN_TEXT_LEN(name),
432                      GRN_TEXT_VALUE(name));
433     goto exit;
434   }
435 
436   if (GRN_TEXT_LEN(new_name) == 0) {
437     rc = GRN_INVALID_ARGUMENT;
438     GRN_PLUGIN_ERROR(ctx,
439                      rc,
440                      "[column][rename] new column name isn't specified: "
441                      "<%.*s%c%.*s>",
442                      (int)GRN_TEXT_LEN(table_raw),
443                      GRN_TEXT_VALUE(table_raw),
444                      GRN_DB_DELIMITER,
445                      (int)GRN_TEXT_LEN(name),
446                      GRN_TEXT_VALUE(name));
447     goto exit;
448   }
449 
450   rc = grn_column_rename(ctx, column,
451                          GRN_TEXT_VALUE(new_name),
452                          GRN_TEXT_LEN(new_name));
453   if (rc != GRN_SUCCESS && ctx->rc == GRN_SUCCESS) {
454     GRN_PLUGIN_ERROR(ctx,
455                      rc,
456                      "[column][rename] failed to rename: "
457                      "<%.*s%c%.*s> -> <%.*s%c%.*s>",
458                      (int)GRN_TEXT_LEN(table_raw),
459                      GRN_TEXT_VALUE(table_raw),
460                      GRN_DB_DELIMITER,
461                      (int)GRN_TEXT_LEN(name),
462                      GRN_TEXT_VALUE(name),
463                      (int)GRN_TEXT_LEN(table_raw),
464                      GRN_TEXT_VALUE(table_raw),
465                      GRN_DB_DELIMITER,
466                      (int)GRN_TEXT_LEN(new_name),
467                      GRN_TEXT_VALUE(new_name));
468     goto exit;
469   }
470 
471 exit :
472   grn_ctx_output_bool(ctx, rc == GRN_SUCCESS);
473   if (column) { grn_obj_unlink(ctx, column); }
474   if (table) { grn_obj_unlink(ctx, table); }
475   return NULL;
476 }
477 
478 void
grn_proc_init_column_rename(grn_ctx * ctx)479 grn_proc_init_column_rename(grn_ctx *ctx)
480 {
481   grn_expr_var vars[3];
482 
483   grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1);
484   grn_plugin_expr_var_init(ctx, &(vars[1]), "name", -1);
485   grn_plugin_expr_var_init(ctx, &(vars[2]), "new_name", -1);
486   grn_plugin_command_create(ctx,
487                             "column_rename", -1,
488                             command_column_rename,
489                             3,
490                             vars);
491 }
492 
493 static void
output_column_name(grn_ctx * ctx,grn_obj * column)494 output_column_name(grn_ctx *ctx, grn_obj *column)
495 {
496   grn_obj bulk;
497   int name_len;
498   char name[GRN_TABLE_MAX_KEY_SIZE];
499 
500   GRN_TEXT_INIT(&bulk, GRN_OBJ_DO_SHALLOW_COPY);
501   name_len = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE);
502   GRN_TEXT_SET(ctx, &bulk, name, name_len);
503 
504   grn_ctx_output_obj(ctx, &bulk, NULL);
505   GRN_OBJ_FIN(ctx, &bulk);
506 }
507 
508 static int
output_column_info(grn_ctx * ctx,grn_obj * column)509 output_column_info(grn_ctx *ctx, grn_obj *column)
510 {
511   grn_obj o;
512   grn_id id;
513   const char *type;
514   const char *path;
515 
516   switch (column->header.type) {
517   case GRN_COLUMN_FIX_SIZE:
518     type = "fix";
519     break;
520   case GRN_COLUMN_VAR_SIZE:
521     type = "var";
522     break;
523   case GRN_COLUMN_INDEX:
524     type = "index";
525     break;
526   default:
527     GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid header type %d\n", column->header.type);
528     return 0;
529   }
530   id = grn_obj_id(ctx, column);
531   path = grn_obj_path(ctx, column);
532   GRN_TEXT_INIT(&o, 0);
533   grn_ctx_output_array_open(ctx, "COLUMN", 8);
534   grn_ctx_output_int64(ctx, id);
535   output_column_name(ctx, column);
536   grn_ctx_output_cstr(ctx, path);
537   grn_ctx_output_cstr(ctx, type);
538   grn_dump_column_create_flags(ctx, grn_column_get_flags(ctx, column), &o);
539   grn_ctx_output_obj(ctx, &o, NULL);
540   grn_proc_output_object_id_name(ctx, column->header.domain);
541   grn_proc_output_object_id_name(ctx, grn_obj_get_range(ctx, column));
542   {
543     grn_db_obj *obj = (grn_db_obj *)column;
544     grn_id *s = obj->source;
545     int i = 0, n = obj->source_size / sizeof(grn_id);
546     grn_ctx_output_array_open(ctx, "SOURCES", n);
547     for (i = 0; i < n; i++, s++) {
548       grn_proc_output_object_id_name(ctx, *s);
549     }
550     grn_ctx_output_array_close(ctx);
551 
552   }
553   /* output_obj_source(ctx, (grn_db_obj *)column); */
554   grn_ctx_output_array_close(ctx);
555   GRN_OBJ_FIN(ctx, &o);
556   return 1;
557 }
558 
559 static grn_obj *
command_column_list(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)560 command_column_list(grn_ctx *ctx, int nargs, grn_obj **args,
561                     grn_user_data *user_data)
562 {
563   grn_obj *table_raw;
564   grn_obj *table;
565   grn_hash *cols;
566   grn_obj *col;
567   int column_list_size = -1;
568 
569   table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1);
570 
571   table = grn_ctx_get(ctx,
572                       GRN_TEXT_VALUE(table_raw),
573                       GRN_TEXT_LEN(table_raw));
574   if (!table) {
575     GRN_PLUGIN_ERROR(ctx,
576                      GRN_INVALID_ARGUMENT,
577                      "[column][list] table doesn't exist: <%.*s>",
578                      (int)GRN_TEXT_LEN(table_raw),
579                      GRN_TEXT_VALUE(table_raw));
580     return NULL;
581   }
582 
583   if (!grn_obj_is_table(ctx, table)) {
584     const char *type_name;
585     type_name = grn_obj_type_to_string(table->header.type);
586     grn_obj_unlink(ctx, table);
587     GRN_PLUGIN_ERROR(ctx,
588                      GRN_INVALID_ARGUMENT,
589                      "[column][list] not table: <%.*s>: <%s>",
590                      (int)GRN_TEXT_LEN(table_raw),
591                      GRN_TEXT_VALUE(table_raw),
592                      type_name);
593     return NULL;
594   }
595 
596   column_list_size = 1; /* [header, (key), (COLUMNS)] */
597   if (table->header.type != GRN_TABLE_NO_KEY) {
598     column_list_size++;
599   }
600   cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
601                          GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
602   if (!cols) {
603     grn_obj_unlink(ctx, table);
604     GRN_PLUGIN_ERROR(ctx,
605                      GRN_INVALID_ARGUMENT,
606                      "[column][list] "
607                      "failed to create temporary table to list columns: <%.*s>",
608                      (int)GRN_TEXT_LEN(table_raw),
609                      GRN_TEXT_VALUE(table_raw));
610     return NULL;
611   }
612 
613   column_list_size += grn_table_columns(ctx, table, NULL, 0, (grn_obj *)cols);
614 
615   grn_ctx_output_array_open(ctx, "COLUMN_LIST", column_list_size);
616   grn_ctx_output_array_open(ctx, "HEADER", 8);
617   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
618   grn_ctx_output_cstr(ctx, "id");
619   grn_ctx_output_cstr(ctx, "UInt32");
620   grn_ctx_output_array_close(ctx);
621   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
622   grn_ctx_output_cstr(ctx, "name");
623   grn_ctx_output_cstr(ctx, "ShortText");
624   grn_ctx_output_array_close(ctx);
625   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
626   grn_ctx_output_cstr(ctx, "path");
627   grn_ctx_output_cstr(ctx, "ShortText");
628   grn_ctx_output_array_close(ctx);
629   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
630   grn_ctx_output_cstr(ctx, "type");
631   grn_ctx_output_cstr(ctx, "ShortText");
632   grn_ctx_output_array_close(ctx);
633   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
634   grn_ctx_output_cstr(ctx, "flags");
635   grn_ctx_output_cstr(ctx, "ShortText");
636   grn_ctx_output_array_close(ctx);
637   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
638   grn_ctx_output_cstr(ctx, "domain");
639   grn_ctx_output_cstr(ctx, "ShortText");
640   grn_ctx_output_array_close(ctx);
641   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
642   grn_ctx_output_cstr(ctx, "range");
643   grn_ctx_output_cstr(ctx, "ShortText");
644   grn_ctx_output_array_close(ctx);
645   grn_ctx_output_array_open(ctx, "PROPERTY", 2);
646   grn_ctx_output_cstr(ctx, "source");
647   grn_ctx_output_cstr(ctx, "ShortText");
648   grn_ctx_output_array_close(ctx);
649   grn_ctx_output_array_close(ctx);
650 
651   if ((col = grn_obj_column(ctx, table,
652                             GRN_COLUMN_NAME_KEY,
653                             GRN_COLUMN_NAME_KEY_LEN))) {
654     int name_len;
655     char name_buf[GRN_TABLE_MAX_KEY_SIZE];
656     grn_id id;
657     grn_obj buf;
658     GRN_TEXT_INIT(&buf, 0);
659     grn_ctx_output_array_open(ctx, "COLUMN", 8);
660     id = grn_obj_id(ctx, table);
661     grn_ctx_output_int64(ctx, id);
662     grn_ctx_output_cstr(ctx, GRN_COLUMN_NAME_KEY);
663     grn_ctx_output_cstr(ctx, "");
664     grn_ctx_output_cstr(ctx, "");
665     grn_dump_column_create_flags(ctx, 0, &buf);
666     grn_ctx_output_obj(ctx, &buf, NULL);
667     name_len = grn_obj_name(ctx, table, name_buf, GRN_TABLE_MAX_KEY_SIZE);
668     grn_ctx_output_str(ctx, name_buf, name_len);
669     grn_proc_output_object_id_name(ctx, table->header.domain);
670     grn_ctx_output_array_open(ctx, "SOURCES", 0);
671     grn_ctx_output_array_close(ctx);
672     grn_ctx_output_array_close(ctx);
673     GRN_OBJ_FIN(ctx, &buf);
674     grn_obj_unlink(ctx, col);
675   }
676   {
677     grn_id *key;
678     GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
679       if ((col = grn_ctx_at(ctx, *key))) {
680         output_column_info(ctx, col);
681         grn_obj_unlink(ctx, col);
682       }
683     });
684   }
685   grn_ctx_output_array_close(ctx);
686   grn_hash_close(ctx, cols);
687   grn_obj_unlink(ctx, table);
688 
689   return NULL;
690 }
691 
692 void
grn_proc_init_column_list(grn_ctx * ctx)693 grn_proc_init_column_list(grn_ctx *ctx)
694 {
695   grn_expr_var vars[1];
696 
697   grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1);
698   grn_plugin_command_create(ctx,
699                             "column_list", -1,
700                             command_column_list,
701                             1,
702                             vars);
703 }
704 
705 static grn_rc
command_column_copy_resolve_target(grn_ctx * ctx,const char * label,grn_obj * table_name,grn_obj * column_name,grn_obj ** table,grn_obj ** column)706 command_column_copy_resolve_target(grn_ctx *ctx,
707                                    const char *label,
708                                    grn_obj *table_name,
709                                    grn_obj *column_name,
710                                    grn_obj **table,
711                                    grn_obj **column)
712 {
713   if (GRN_TEXT_LEN(table_name) == 0) {
714     ERR(GRN_INVALID_ARGUMENT,
715         "[column][copy] %s table name isn't specified",
716         label);
717     return ctx->rc;
718   }
719   *table = grn_ctx_get(ctx,
720                        GRN_TEXT_VALUE(table_name),
721                        GRN_TEXT_LEN(table_name));
722   if (!*table) {
723     ERR(GRN_INVALID_ARGUMENT,
724         "[column][copy] %s table isn't found: <%.*s>",
725         label,
726         (int)GRN_TEXT_LEN(table_name),
727         GRN_TEXT_VALUE(table_name));
728     return ctx->rc;
729   }
730 
731   if (GRN_TEXT_LEN(column_name) == 0) {
732     ERR(GRN_INVALID_ARGUMENT,
733         "[column][copy] %s column name isn't specified: <%.*s>",
734         label,
735         (int)GRN_TEXT_LEN(table_name),
736         GRN_TEXT_VALUE(table_name));
737     return ctx->rc;
738   }
739   *column = grn_obj_column(ctx, *table,
740                            GRN_TEXT_VALUE(column_name),
741                            GRN_TEXT_LEN(column_name));
742   if (!*column) {
743     ERR(GRN_INVALID_ARGUMENT,
744         "[column][copy] %s column isn't found: <%.*s.%.*s>",
745         label,
746         (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
747         (int)GRN_TEXT_LEN(column_name), GRN_TEXT_VALUE(column_name));
748     return ctx->rc;
749   }
750 
751   return ctx->rc;
752 }
753 
754 static void
command_column_copy_same_table(grn_ctx * ctx,grn_obj * table,grn_obj * from_column,grn_obj * to_column)755 command_column_copy_same_table(grn_ctx *ctx, grn_obj *table,
756                                grn_obj *from_column, grn_obj *to_column)
757 {
758   grn_table_cursor *cursor;
759   grn_id id;
760   grn_obj value;
761 
762   cursor = grn_table_cursor_open(ctx, table,
763                                  NULL, 0,
764                                  NULL, 0,
765                                  0, -1, 0);
766   if (!cursor) {
767     return;
768   }
769 
770   GRN_VOID_INIT(&value);
771   while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
772     GRN_BULK_REWIND(&value);
773     grn_obj_get_value(ctx, from_column, id, &value);
774     grn_obj_set_value(ctx, to_column, id, &value, GRN_OBJ_SET);
775   }
776   GRN_OBJ_FIN(ctx, &value);
777   grn_table_cursor_close(ctx, cursor);
778 }
779 
780 static void
command_column_copy_same_key_type(grn_ctx * ctx,grn_obj * from_table,grn_obj * from_column,grn_obj * to_table,grn_obj * to_column)781 command_column_copy_same_key_type(grn_ctx *ctx,
782                                   grn_obj *from_table,
783                                   grn_obj *from_column,
784                                   grn_obj *to_table,
785                                   grn_obj *to_column)
786 {
787   grn_table_cursor *cursor;
788   grn_id from_id;
789   grn_obj value;
790 
791   cursor = grn_table_cursor_open(ctx, from_table,
792                                  NULL, 0,
793                                  NULL, 0,
794                                  0, -1, 0);
795   if (!cursor) {
796     return;
797   }
798 
799   GRN_VOID_INIT(&value);
800   while ((from_id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
801     void *key;
802     int key_size;
803     grn_id to_id;
804 
805     key_size = grn_table_cursor_get_key(ctx, cursor, &key);
806     to_id = grn_table_add(ctx, to_table, key, key_size, NULL);
807     if (to_id == GRN_ID_NIL) {
808       continue;
809     }
810 
811     GRN_BULK_REWIND(&value);
812     grn_obj_get_value(ctx, from_column, from_id, &value);
813     grn_obj_set_value(ctx, to_column, to_id, &value, GRN_OBJ_SET);
814   }
815   GRN_OBJ_FIN(ctx, &value);
816   grn_table_cursor_close(ctx, cursor);
817 }
818 
819 static void
command_column_copy_different(grn_ctx * ctx,grn_obj * from_table,grn_obj * from_column,grn_obj * to_table,grn_obj * to_column,grn_obj * from_table_name,grn_obj * from_column_name,grn_obj * to_table_name,grn_obj * to_column_name)820 command_column_copy_different(grn_ctx *ctx,
821                               grn_obj *from_table,
822                               grn_obj *from_column,
823                               grn_obj *to_table,
824                               grn_obj *to_column,
825                               grn_obj *from_table_name,
826                               grn_obj *from_column_name,
827                               grn_obj *to_table_name,
828                               grn_obj *to_column_name)
829 {
830   grn_table_cursor *cursor;
831   grn_id from_id;
832   grn_obj from_key_buffer;
833   grn_obj to_key_buffer;
834   grn_obj value;
835 
836   cursor = grn_table_cursor_open(ctx, from_table,
837                                  NULL, 0,
838                                  NULL, 0,
839                                  0, -1, 0);
840   if (!cursor) {
841     return;
842   }
843 
844   if (from_table->header.domain == GRN_DB_SHORT_TEXT) {
845     GRN_SHORT_TEXT_INIT(&from_key_buffer, 0);
846   } else {
847     GRN_VALUE_FIX_SIZE_INIT(&from_key_buffer, 0, from_table->header.domain);
848   }
849   if (to_table->header.domain == GRN_DB_SHORT_TEXT) {
850     GRN_SHORT_TEXT_INIT(&to_key_buffer, 0);
851   } else {
852     GRN_VALUE_FIX_SIZE_INIT(&to_key_buffer, 0, to_table->header.domain);
853   }
854   GRN_VOID_INIT(&value);
855   while ((from_id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
856     void *key;
857     int key_size;
858     grn_rc cast_rc;
859     grn_id to_id;
860 
861     GRN_BULK_REWIND(&from_key_buffer);
862     GRN_BULK_REWIND(&to_key_buffer);
863 
864     key_size = grn_table_cursor_get_key(ctx, cursor, &key);
865     grn_bulk_write(ctx, &from_key_buffer, key, key_size);
866     cast_rc = grn_obj_cast(ctx, &from_key_buffer, &to_key_buffer, GRN_FALSE);
867     if (cast_rc != GRN_SUCCESS) {
868       grn_obj *to_key_type;
869       grn_obj inspected_key;
870       grn_obj inspected_to_key_type;
871 
872       to_key_type = grn_ctx_at(ctx, to_table->header.domain);
873       GRN_TEXT_INIT(&inspected_key, 0);
874       GRN_TEXT_INIT(&inspected_to_key_type, 0);
875       grn_inspect(ctx, &inspected_key, &from_key_buffer);
876       grn_inspect(ctx, &inspected_to_key_type, to_key_type);
877       ERR(cast_rc,
878           "[column][copy] failed to cast key: <%.*s> -> %.*s: "
879           "<%.*s.%.*s> -> <%.*s.%.*s>",
880           (int)GRN_TEXT_LEN(&inspected_key),
881           GRN_TEXT_VALUE(&inspected_key),
882           (int)GRN_TEXT_LEN(&inspected_to_key_type),
883           GRN_TEXT_VALUE(&inspected_to_key_type),
884           (int)GRN_TEXT_LEN(from_table_name),
885           GRN_TEXT_VALUE(from_table_name),
886           (int)GRN_TEXT_LEN(from_column_name),
887           GRN_TEXT_VALUE(from_column_name),
888           (int)GRN_TEXT_LEN(to_table_name),
889           GRN_TEXT_VALUE(to_table_name),
890           (int)GRN_TEXT_LEN(to_column_name),
891           GRN_TEXT_VALUE(to_column_name));
892       GRN_OBJ_FIN(ctx, &inspected_key);
893       GRN_OBJ_FIN(ctx, &inspected_to_key_type);
894       break;
895     }
896     to_id = grn_table_add(ctx, to_table,
897                           GRN_BULK_HEAD(&to_key_buffer),
898                           GRN_BULK_VSIZE(&to_key_buffer),
899                           NULL);
900     if (to_id == GRN_ID_NIL) {
901       continue;
902     }
903 
904     GRN_BULK_REWIND(&value);
905     grn_obj_get_value(ctx, from_column, from_id, &value);
906     grn_obj_set_value(ctx, to_column, to_id, &value, GRN_OBJ_SET);
907   }
908   GRN_OBJ_FIN(ctx, &from_key_buffer);
909   GRN_OBJ_FIN(ctx, &to_key_buffer);
910   GRN_OBJ_FIN(ctx, &value);
911 
912   grn_table_cursor_close(ctx, cursor);
913 }
914 
915 static grn_obj *
command_column_copy(grn_ctx * ctx,int nargs,grn_obj ** args,grn_user_data * user_data)916 command_column_copy(grn_ctx *ctx, int nargs, grn_obj **args,
917                     grn_user_data *user_data)
918 {
919   grn_rc rc = GRN_SUCCESS;
920   grn_obj *from_table = NULL;
921   grn_obj *from_column = NULL;
922   grn_obj *to_table = NULL;
923   grn_obj *to_column = NULL;
924   grn_obj *from_table_name;
925   grn_obj *from_column_name;
926   grn_obj *to_table_name;
927   grn_obj *to_column_name;
928 
929   from_table_name  = grn_plugin_proc_get_var(ctx, user_data, "from_table", -1);
930   from_column_name = grn_plugin_proc_get_var(ctx, user_data, "from_name", -1);
931   to_table_name    = grn_plugin_proc_get_var(ctx, user_data, "to_table", -1);
932   to_column_name   = grn_plugin_proc_get_var(ctx, user_data, "to_name", -1);
933 
934   rc = command_column_copy_resolve_target(ctx, "from",
935                                           from_table_name, from_column_name,
936                                           &from_table, &from_column);
937   if (rc != GRN_SUCCESS) {
938     goto exit;
939   }
940   rc = command_column_copy_resolve_target(ctx, "to",
941                                           to_table_name, to_column_name,
942                                           &to_table, &to_column);
943   if (rc != GRN_SUCCESS) {
944     goto exit;
945   }
946 
947   if ((from_table->header.type == GRN_TABLE_NO_KEY ||
948        to_table->header.type == GRN_TABLE_NO_KEY) &&
949       from_table != to_table) {
950     rc = GRN_OPERATION_NOT_SUPPORTED;
951     GRN_PLUGIN_ERROR(ctx,
952                      rc,
953                      "[column][copy] copy from/to TABLE_NO_KEY isn't supported: "
954                      "<%.*s%c%.*s> -> <%.*s%c%.*s>",
955                      (int)GRN_TEXT_LEN(from_table_name),
956                      GRN_TEXT_VALUE(from_table_name),
957                      GRN_DB_DELIMITER,
958                      (int)GRN_TEXT_LEN(from_column_name),
959                      GRN_TEXT_VALUE(from_column_name),
960                      (int)GRN_TEXT_LEN(to_table_name),
961                      GRN_TEXT_VALUE(to_table_name),
962                      GRN_DB_DELIMITER,
963                      (int)GRN_TEXT_LEN(to_column_name),
964                      GRN_TEXT_VALUE(to_column_name));
965     goto exit;
966   }
967 
968   if (from_table == to_table) {
969     command_column_copy_same_table(ctx, from_table, from_column, to_column);
970   } else if (from_table->header.domain == to_table->header.domain) {
971     command_column_copy_same_key_type(ctx,
972                                       from_table, from_column,
973                                       to_table, to_column);
974   } else {
975     command_column_copy_different(ctx,
976                                   from_table,
977                                   from_column,
978                                   to_table,
979                                   to_column,
980                                   from_table_name,
981                                   from_column_name,
982                                   to_table_name,
983                                   to_column_name);
984   }
985 
986 exit :
987   grn_ctx_output_bool(ctx, rc == GRN_SUCCESS);
988 
989   if (to_column) {
990     grn_obj_unlink(ctx, to_column);
991   }
992   if (to_table) {
993     grn_obj_unlink(ctx, to_table);
994   }
995   if (from_column) {
996     grn_obj_unlink(ctx, from_column);
997   }
998   if (from_table) {
999     grn_obj_unlink(ctx, from_table);
1000   }
1001 
1002   return NULL;
1003 }
1004 
1005 void
grn_proc_init_column_copy(grn_ctx * ctx)1006 grn_proc_init_column_copy(grn_ctx *ctx)
1007 {
1008   grn_expr_var vars[4];
1009 
1010   grn_plugin_expr_var_init(ctx, &(vars[0]), "from_table", -1);
1011   grn_plugin_expr_var_init(ctx, &(vars[1]), "from_name", -1);
1012   grn_plugin_expr_var_init(ctx, &(vars[2]), "to_table", -1);
1013   grn_plugin_expr_var_init(ctx, &(vars[3]), "to_name", -1);
1014   grn_plugin_command_create(ctx,
1015                             "column_copy", -1,
1016                             command_column_copy,
1017                             4,
1018                             vars);
1019 }
1020