1 /* -*- c-basic-offset: 2 -*- */
2 /* Copyright(C) 2009-2015 Brazil
3 
4   This library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Lesser General Public
6   License version 2.1 as published by the Free Software Foundation.
7 
8   This library is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   Lesser General Public License for more details.
12 
13   You should have received a copy of the GNU Lesser General Public
14   License along with this library; if not, write to the Free Software
15   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
16 */
17 
18 #include "grn.h"
19 
20 #include <string.h>
21 #include "grn_str.h"
22 #include "grn_db.h"
23 #include "grn_expr_code.h"
24 #include "grn_util.h"
25 #include "grn_output.h"
26 
27 #define LEVELS (&ctx->impl->output.levels)
28 #define DEPTH (GRN_BULK_VSIZE(LEVELS)>>2)
29 #define CURR_LEVEL (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1))) : 0)
30 #define INCR_DEPTH(i) GRN_UINT32_PUT(ctx, LEVELS, i)
31 #define DECR_DEPTH (DEPTH ? grn_bulk_truncate(ctx, LEVELS, GRN_BULK_VSIZE(LEVELS) - sizeof(uint32_t)) : 0)
32 #define INCR_LENGTH (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1)) += 2) : 0)
33 
34 static void
indent(grn_ctx * ctx,grn_obj * outbuf,size_t level)35 indent(grn_ctx *ctx, grn_obj *outbuf, size_t level)
36 {
37   size_t i;
38   for (i = 0; i < level; i++) {
39     GRN_TEXT_PUTS(ctx, outbuf, "  ");
40   }
41 }
42 
43 static void
json_array_open(grn_ctx * ctx,grn_obj * outbuf,size_t * indent_level)44 json_array_open(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level)
45 {
46   GRN_TEXT_PUTC(ctx, outbuf, '[');
47   if (ctx->impl->output.is_pretty) {
48     GRN_TEXT_PUTC(ctx, outbuf, '\n');
49     (*indent_level)++;
50     indent(ctx, outbuf, *indent_level);
51   }
52 }
53 
54 static void
json_array_close(grn_ctx * ctx,grn_obj * outbuf,size_t * indent_level)55 json_array_close(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level)
56 {
57   if (ctx->impl->output.is_pretty) {
58     GRN_TEXT_PUTC(ctx, outbuf, '\n');
59     (*indent_level)--;
60     indent(ctx, outbuf, *indent_level);
61   }
62   GRN_TEXT_PUTC(ctx, outbuf, ']');
63 }
64 
65 static void
json_element_end(grn_ctx * ctx,grn_obj * outbuf,size_t indent_level)66 json_element_end(grn_ctx *ctx, grn_obj *outbuf, size_t indent_level)
67 {
68   GRN_TEXT_PUTC(ctx, outbuf, ',');
69   if (ctx->impl->output.is_pretty) {
70     GRN_TEXT_PUTC(ctx, outbuf, '\n');
71     indent(ctx, outbuf, indent_level);
72   }
73 }
74 
75 static void
json_map_open(grn_ctx * ctx,grn_obj * outbuf,size_t * indent_level)76 json_map_open(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level)
77 {
78   GRN_TEXT_PUTC(ctx, outbuf, '{');
79   if (ctx->impl->output.is_pretty) {
80     GRN_TEXT_PUTC(ctx, outbuf, '\n');
81     (*indent_level)++;
82     indent(ctx, outbuf, *indent_level);
83   }
84 }
85 
86 static void
json_map_close(grn_ctx * ctx,grn_obj * outbuf,size_t * indent_level)87 json_map_close(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level)
88 {
89   if (ctx->impl->output.is_pretty) {
90     GRN_TEXT_PUTC(ctx, outbuf, '\n');
91     (*indent_level)--;
92     indent(ctx, outbuf, *indent_level);
93   }
94   GRN_TEXT_PUTC(ctx, outbuf, '}');
95 }
96 
97 static void
json_key_end(grn_ctx * ctx,grn_obj * outbuf)98 json_key_end(grn_ctx *ctx, grn_obj *outbuf)
99 {
100   GRN_TEXT_PUTC(ctx, outbuf, ':');
101   if (ctx->impl->output.is_pretty) {
102     GRN_TEXT_PUTC(ctx, outbuf, ' ');
103   }
104 }
105 
106 static void
json_key(grn_ctx * ctx,grn_obj * outbuf,const char * key)107 json_key(grn_ctx *ctx, grn_obj *outbuf, const char *key)
108 {
109   grn_text_esc(ctx, outbuf, key, strlen(key));
110   json_key_end(ctx, outbuf);
111 }
112 
113 static void
json_value_end(grn_ctx * ctx,grn_obj * outbuf,size_t indent_level)114 json_value_end(grn_ctx *ctx, grn_obj *outbuf, size_t indent_level)
115 {
116   GRN_TEXT_PUTC(ctx, outbuf, ',');
117   if (ctx->impl->output.is_pretty) {
118     GRN_TEXT_PUTC(ctx, outbuf, '\n');
119     indent(ctx, outbuf, indent_level);
120   }
121 }
122 
123 static void
put_delimiter(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)124 put_delimiter(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
125 {
126   uint32_t level = CURR_LEVEL;
127   switch (output_type) {
128   case GRN_CONTENT_JSON:
129     if (level < 2) {
130       if (DEPTH > 0 && ctx->impl->output.is_pretty) {
131         GRN_TEXT_PUTC(ctx, outbuf, '\n');
132         indent(ctx, outbuf, DEPTH + 1);
133       }
134       return;
135     }
136     if ((level & 3) == 3) {
137       GRN_TEXT_PUTC(ctx, outbuf, ':');
138       if (ctx->impl->output.is_pretty) {
139         GRN_TEXT_PUTC(ctx, outbuf, ' ');
140       }
141     } else {
142       json_element_end(ctx, outbuf, DEPTH + 1);
143     }
144     // if (DEPTH == 1 && ((level & 3) != 3)) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); }
145     break;
146   case GRN_CONTENT_XML:
147     if (!DEPTH) { return; }
148     GRN_TEXT_PUTC(ctx, outbuf, '\n');
149     break;
150   case GRN_CONTENT_TSV:
151     if (level < 2) { return; }
152     if (DEPTH <= 2) {
153       GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? '\t' : '\n');
154     } else {
155       GRN_TEXT_PUTC(ctx, outbuf, '\t');
156     }
157   case GRN_CONTENT_MSGPACK :
158     // do nothing
159     break;
160   case GRN_CONTENT_GROONGA_COMMAND_LIST :
161     break;
162   case GRN_CONTENT_NONE:
163     break;
164   }
165 }
166 
167 void
grn_output_array_open(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,const char * name,int nelements)168 grn_output_array_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
169                       const char *name, int nelements)
170 {
171   put_delimiter(ctx, outbuf, output_type);
172   switch (output_type) {
173   case GRN_CONTENT_JSON:
174     GRN_TEXT_PUTC(ctx, outbuf, '[');
175     break;
176   case GRN_CONTENT_XML:
177     GRN_TEXT_PUTC(ctx, outbuf, '<');
178     GRN_TEXT_PUTS(ctx, outbuf, name);
179     GRN_TEXT_PUTC(ctx, outbuf, '>');
180     grn_vector_add_element(ctx,
181                            &ctx->impl->output.names,
182                            name, strlen(name),
183                            0, GRN_DB_SHORT_TEXT);
184     break;
185   case GRN_CONTENT_TSV:
186     if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "[\t"); }
187     break;
188   case GRN_CONTENT_MSGPACK :
189 #ifdef GRN_WITH_MESSAGE_PACK
190     if (nelements < 0) {
191       GRN_LOG(ctx, GRN_LOG_DEBUG,
192               "grn_output_array_open nelements (%d) for <%s>",
193               nelements,
194               name);
195     }
196     msgpack_pack_array(&ctx->impl->output.msgpacker, nelements);
197 #endif
198     break;
199   case GRN_CONTENT_GROONGA_COMMAND_LIST :
200     break;
201   case GRN_CONTENT_NONE:
202     break;
203   }
204   INCR_DEPTH(0);
205 }
206 
207 void
grn_output_array_close(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)208 grn_output_array_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
209 {
210   switch (output_type) {
211   case GRN_CONTENT_JSON:
212     if (ctx->impl->output.is_pretty) {
213       GRN_TEXT_PUTC(ctx, outbuf, '\n');
214       indent(ctx, outbuf, DEPTH);
215     }
216     GRN_TEXT_PUTC(ctx, outbuf, ']');
217     break;
218   case GRN_CONTENT_TSV:
219     if (DEPTH > 3) {
220       if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
221       GRN_TEXT_PUTC(ctx, outbuf, ']');
222     }
223     break;
224   case GRN_CONTENT_XML:
225     {
226       const char *name;
227       unsigned int name_len;
228       name_len = grn_vector_pop_element(ctx,
229                                         &ctx->impl->output.names,
230                                         &name, NULL, NULL);
231       GRN_TEXT_PUTS(ctx, outbuf, "</");
232       GRN_TEXT_PUT(ctx, outbuf, name, name_len);
233       GRN_TEXT_PUTC(ctx, outbuf, '>');
234     }
235     break;
236   case GRN_CONTENT_MSGPACK :
237     // do nothing
238     break;
239   case GRN_CONTENT_GROONGA_COMMAND_LIST :
240     break;
241   case GRN_CONTENT_NONE:
242     break;
243   }
244   DECR_DEPTH;
245   INCR_LENGTH;
246 }
247 
248 void
grn_output_map_open(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,const char * name,int nelements)249 grn_output_map_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
250                     const char *name, int nelements)
251 {
252   put_delimiter(ctx, outbuf, output_type);
253   switch (output_type) {
254   case GRN_CONTENT_JSON:
255     GRN_TEXT_PUTS(ctx, outbuf, "{");
256     break;
257   case GRN_CONTENT_XML:
258     GRN_TEXT_PUTC(ctx, outbuf, '<');
259     GRN_TEXT_PUTS(ctx, outbuf, name);
260     GRN_TEXT_PUTC(ctx, outbuf, '>');
261     grn_vector_add_element(ctx,
262                            &ctx->impl->output.names,
263                            name, strlen(name), 0, GRN_DB_SHORT_TEXT);
264     break;
265   case GRN_CONTENT_TSV:
266     if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "{\t"); }
267     break;
268   case GRN_CONTENT_MSGPACK :
269 #ifdef GRN_WITH_MESSAGE_PACK
270     if (nelements < 0) {
271       GRN_LOG(ctx, GRN_LOG_DEBUG,
272               "grn_output_map_open nelements (%d) for <%s>",
273               nelements,
274               name);
275     }
276     msgpack_pack_map(&ctx->impl->output.msgpacker, nelements);
277 #endif
278     break;
279   case GRN_CONTENT_GROONGA_COMMAND_LIST :
280     break;
281   case GRN_CONTENT_NONE:
282     break;
283   }
284   INCR_DEPTH(1);
285 }
286 
287 void
grn_output_map_close(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)288 grn_output_map_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
289 {
290   switch (output_type) {
291   case GRN_CONTENT_JSON:
292     if (ctx->impl->output.is_pretty) {
293       GRN_TEXT_PUTC(ctx, outbuf, '\n');
294       indent(ctx, outbuf, DEPTH);
295     }
296     GRN_TEXT_PUTS(ctx, outbuf, "}");
297     break;
298   case GRN_CONTENT_TSV:
299     if (DEPTH > 3) {
300       if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
301       GRN_TEXT_PUTC(ctx, outbuf, '}');
302     }
303     break;
304   case GRN_CONTENT_XML:
305     {
306       const char *name;
307       unsigned int name_len;
308       name_len = grn_vector_pop_element(ctx,
309                                         &ctx->impl->output.names,
310                                         &name, NULL, NULL);
311       GRN_TEXT_PUTS(ctx, outbuf, "</");
312       GRN_TEXT_PUT(ctx, outbuf, name, name_len);
313       GRN_TEXT_PUTC(ctx, outbuf, '>');
314     }
315     break;
316   case GRN_CONTENT_MSGPACK :
317     // do nothing
318     break;
319   case GRN_CONTENT_GROONGA_COMMAND_LIST :
320     break;
321   case GRN_CONTENT_NONE:
322     break;
323   }
324   DECR_DEPTH;
325   INCR_LENGTH;
326 }
327 
328 void
grn_output_int32(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,int value)329 grn_output_int32(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int value)
330 {
331   put_delimiter(ctx, outbuf, output_type);
332   switch (output_type) {
333   case GRN_CONTENT_JSON:
334     grn_text_itoa(ctx, outbuf, value);
335     break;
336   case GRN_CONTENT_TSV:
337     grn_text_itoa(ctx, outbuf, value);
338     break;
339   case GRN_CONTENT_XML:
340     GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
341     grn_text_itoa(ctx, outbuf, value);
342     GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
343     break;
344   case GRN_CONTENT_MSGPACK :
345 #ifdef GRN_WITH_MESSAGE_PACK
346     msgpack_pack_int32(&ctx->impl->output.msgpacker, value);
347 #endif
348     break;
349   case GRN_CONTENT_GROONGA_COMMAND_LIST :
350     grn_text_itoa(ctx, outbuf, value);
351     break;
352   case GRN_CONTENT_NONE:
353     break;
354   }
355   INCR_LENGTH;
356 }
357 
358 void
grn_output_int64(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,int64_t value)359 grn_output_int64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
360 {
361   put_delimiter(ctx, outbuf, output_type);
362   switch (output_type) {
363   case GRN_CONTENT_JSON:
364     grn_text_lltoa(ctx, outbuf, value);
365     break;
366   case GRN_CONTENT_TSV:
367     grn_text_lltoa(ctx, outbuf, value);
368     break;
369   case GRN_CONTENT_XML:
370     GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
371     grn_text_lltoa(ctx, outbuf, value);
372     GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
373     break;
374   case GRN_CONTENT_MSGPACK :
375 #ifdef GRN_WITH_MESSAGE_PACK
376     msgpack_pack_int64(&ctx->impl->output.msgpacker, value);
377 #endif
378     break;
379   case GRN_CONTENT_GROONGA_COMMAND_LIST :
380     grn_text_lltoa(ctx, outbuf, value);
381     break;
382   case GRN_CONTENT_NONE:
383     break;
384   }
385   INCR_LENGTH;
386 }
387 
388 void
grn_output_uint64(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,uint64_t value)389 grn_output_uint64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, uint64_t value)
390 {
391   put_delimiter(ctx, outbuf, output_type);
392   switch (output_type) {
393   case GRN_CONTENT_JSON:
394     grn_text_ulltoa(ctx, outbuf, value);
395     break;
396   case GRN_CONTENT_TSV:
397     grn_text_ulltoa(ctx, outbuf, value);
398     break;
399   case GRN_CONTENT_XML:
400     GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
401     grn_text_ulltoa(ctx, outbuf, value);
402     GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
403     break;
404   case GRN_CONTENT_MSGPACK :
405 #ifdef GRN_WITH_MESSAGE_PACK
406     msgpack_pack_uint64(&ctx->impl->output.msgpacker, value);
407 #endif
408     break;
409   case GRN_CONTENT_GROONGA_COMMAND_LIST :
410     grn_text_ulltoa(ctx, outbuf, value);
411     break;
412   case GRN_CONTENT_NONE:
413     break;
414   }
415   INCR_LENGTH;
416 }
417 
418 void
grn_output_float(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,double value)419 grn_output_float(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, double value)
420 {
421   put_delimiter(ctx, outbuf, output_type);
422   switch (output_type) {
423   case GRN_CONTENT_JSON:
424     grn_text_ftoa(ctx, outbuf, value);
425     break;
426   case GRN_CONTENT_TSV:
427     grn_text_ftoa(ctx, outbuf, value);
428     break;
429   case GRN_CONTENT_XML:
430     GRN_TEXT_PUTS(ctx, outbuf, "<FLOAT>");
431     grn_text_ftoa(ctx, outbuf, value);
432     GRN_TEXT_PUTS(ctx, outbuf, "</FLOAT>");
433     break;
434   case GRN_CONTENT_MSGPACK :
435 #ifdef GRN_WITH_MESSAGE_PACK
436     msgpack_pack_double(&ctx->impl->output.msgpacker, value);
437 #endif
438     break;
439   case GRN_CONTENT_GROONGA_COMMAND_LIST :
440     grn_text_ftoa(ctx, outbuf, value);
441     break;
442   case GRN_CONTENT_NONE:
443     break;
444   }
445   INCR_LENGTH;
446 }
447 
448 void
grn_output_str(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,const char * value,size_t value_len)449 grn_output_str(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
450                const char *value, size_t value_len)
451 {
452   put_delimiter(ctx, outbuf, output_type);
453   switch (output_type) {
454   case GRN_CONTENT_JSON:
455     grn_text_esc(ctx, outbuf, value, value_len);
456     break;
457   case GRN_CONTENT_TSV:
458     grn_text_esc(ctx, outbuf, value, value_len);
459     break;
460   case GRN_CONTENT_XML:
461     GRN_TEXT_PUTS(ctx, outbuf, "<TEXT>");
462     grn_text_escape_xml(ctx, outbuf, value, value_len);
463     GRN_TEXT_PUTS(ctx, outbuf, "</TEXT>");
464     break;
465   case GRN_CONTENT_MSGPACK :
466 #ifdef GRN_WITH_MESSAGE_PACK
467     msgpack_pack_str(&ctx->impl->output.msgpacker, value_len);
468     msgpack_pack_str_body(&ctx->impl->output.msgpacker, value, value_len);
469 #endif
470     break;
471   case GRN_CONTENT_GROONGA_COMMAND_LIST :
472     GRN_TEXT_PUT(ctx, outbuf, value, value_len);
473     break;
474   case GRN_CONTENT_NONE:
475     break;
476   }
477   INCR_LENGTH;
478 }
479 
480 void
grn_output_cstr(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,const char * value)481 grn_output_cstr(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
482                 const char *value)
483 {
484   grn_output_str(ctx, outbuf, output_type, value, strlen(value));
485 }
486 
487 void
grn_output_bool(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_bool value)488 grn_output_bool(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_bool value)
489 {
490   put_delimiter(ctx, outbuf, output_type);
491   switch (output_type) {
492   case GRN_CONTENT_JSON:
493     GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
494     break;
495   case GRN_CONTENT_TSV:
496     GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
497     break;
498   case GRN_CONTENT_XML:
499     GRN_TEXT_PUTS(ctx, outbuf, "<BOOL>");
500     GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
501     GRN_TEXT_PUTS(ctx, outbuf, "</BOOL>");
502     break;
503   case GRN_CONTENT_MSGPACK :
504 #ifdef GRN_WITH_MESSAGE_PACK
505     if (value) {
506       msgpack_pack_true(&ctx->impl->output.msgpacker);
507     } else {
508       msgpack_pack_false(&ctx->impl->output.msgpacker);
509     }
510 #endif
511     break;
512   case GRN_CONTENT_GROONGA_COMMAND_LIST :
513     GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
514     break;
515   case GRN_CONTENT_NONE:
516     break;
517   }
518   INCR_LENGTH;
519 }
520 
521 void
grn_output_null(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)522 grn_output_null(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
523 {
524   put_delimiter(ctx, outbuf, output_type);
525   switch (output_type) {
526   case GRN_CONTENT_JSON:
527     GRN_TEXT_PUTS(ctx, outbuf, "null");
528     break;
529   case GRN_CONTENT_TSV:
530     break;
531   case GRN_CONTENT_XML:
532     GRN_TEXT_PUTS(ctx, outbuf, "<NULL/>");
533     break;
534   case GRN_CONTENT_MSGPACK :
535 #ifdef GRN_WITH_MESSAGE_PACK
536     msgpack_pack_nil(&ctx->impl->output.msgpacker);
537 #endif
538     break;
539   case GRN_CONTENT_GROONGA_COMMAND_LIST :
540     break;
541   case GRN_CONTENT_NONE:
542     break;
543   }
544   INCR_LENGTH;
545 }
546 
547 static inline void
grn_output_bulk_void(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,const char * value,size_t value_len)548 grn_output_bulk_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
549                      const char *value, size_t value_len)
550 {
551   if (value_len == sizeof(grn_id) && *(grn_id *)value == GRN_ID_NIL) {
552     grn_output_null(ctx, outbuf, output_type);
553   } else {
554     grn_output_str(ctx, outbuf, output_type, value, value_len);
555   }
556 }
557 
558 void
grn_output_time(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,int64_t value)559 grn_output_time(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
560 {
561   double dv = value;
562   dv /= 1000000.0;
563   put_delimiter(ctx, outbuf, output_type);
564   switch (output_type) {
565   case GRN_CONTENT_JSON:
566     grn_text_ftoa(ctx, outbuf, dv);
567     break;
568   case GRN_CONTENT_TSV:
569     grn_text_ftoa(ctx, outbuf, dv);
570     break;
571   case GRN_CONTENT_XML:
572     GRN_TEXT_PUTS(ctx, outbuf, "<DATE>");
573     grn_text_ftoa(ctx, outbuf, dv);
574     GRN_TEXT_PUTS(ctx, outbuf, "</DATE>");
575     break;
576   case GRN_CONTENT_MSGPACK :
577 #ifdef GRN_WITH_MESSAGE_PACK
578     msgpack_pack_double(&ctx->impl->output.msgpacker, dv);
579 #endif
580     break;
581   case GRN_CONTENT_GROONGA_COMMAND_LIST :
582     grn_text_ftoa(ctx, outbuf, dv);
583     break;
584   case GRN_CONTENT_NONE:
585     break;
586   }
587   INCR_LENGTH;
588 }
589 
590 void
grn_output_geo_point(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_geo_point * value)591 grn_output_geo_point(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
592                      grn_geo_point *value)
593 {
594   put_delimiter(ctx, outbuf, output_type);
595   switch (output_type) {
596   case GRN_CONTENT_JSON:
597     if (value) {
598       GRN_TEXT_PUTC(ctx, outbuf, '"');
599       grn_text_itoa(ctx, outbuf, value->latitude);
600       GRN_TEXT_PUTC(ctx, outbuf, 'x');
601       grn_text_itoa(ctx, outbuf, value->longitude);
602       GRN_TEXT_PUTC(ctx, outbuf, '"');
603     } else {
604       GRN_TEXT_PUTS(ctx, outbuf, "null");
605     }
606     break;
607   case GRN_CONTENT_TSV:
608     if (value) {
609       GRN_TEXT_PUTC(ctx, outbuf, '"');
610       grn_text_itoa(ctx, outbuf, value->latitude);
611       GRN_TEXT_PUTC(ctx, outbuf, 'x');
612       grn_text_itoa(ctx, outbuf, value->longitude);
613       GRN_TEXT_PUTC(ctx, outbuf, '"');
614     } else {
615       GRN_TEXT_PUTS(ctx, outbuf, "\"\"");
616     }
617     break;
618   case GRN_CONTENT_XML:
619     GRN_TEXT_PUTS(ctx, outbuf, "<GEO_POINT>");
620     if (value) {
621       grn_text_itoa(ctx, outbuf, value->latitude);
622       GRN_TEXT_PUTC(ctx, outbuf, 'x');
623       grn_text_itoa(ctx, outbuf, value->longitude);
624     }
625     GRN_TEXT_PUTS(ctx, outbuf, "</GEO_POINT>");
626     break;
627   case GRN_CONTENT_MSGPACK :
628 #ifdef GRN_WITH_MESSAGE_PACK
629     if (value) {
630       grn_obj buf;
631       GRN_TEXT_INIT(&buf, 0);
632       grn_text_itoa(ctx, &buf, value->latitude);
633       GRN_TEXT_PUTC(ctx, &buf, 'x');
634       grn_text_itoa(ctx, &buf, value->longitude);
635       msgpack_pack_str(&ctx->impl->output.msgpacker, GRN_TEXT_LEN(&buf));
636       msgpack_pack_str_body(&ctx->impl->output.msgpacker,
637                             GRN_TEXT_VALUE(&buf),
638                             GRN_TEXT_LEN(&buf));
639       grn_obj_close(ctx, &buf);
640     } else {
641       msgpack_pack_nil(&ctx->impl->output.msgpacker);
642     }
643 #endif
644     break;
645   case GRN_CONTENT_GROONGA_COMMAND_LIST :
646     if (value) {
647       GRN_TEXT_PUTC(ctx, outbuf, '"');
648       grn_text_itoa(ctx, outbuf, value->latitude);
649       GRN_TEXT_PUTC(ctx, outbuf, 'x');
650       grn_text_itoa(ctx, outbuf, value->longitude);
651       GRN_TEXT_PUTC(ctx, outbuf, '"');
652     } else {
653       GRN_TEXT_PUTS(ctx, outbuf, "\"\"");
654     }
655     break;
656   case GRN_CONTENT_NONE:
657     break;
658   }
659   INCR_LENGTH;
660 }
661 
662 static void
grn_text_atoj(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * obj,grn_id id)663 grn_text_atoj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
664               grn_obj *obj, grn_id id)
665 {
666   uint32_t vs;
667   grn_obj buf;
668   if (obj->header.type == GRN_ACCESSOR) {
669     grn_accessor *a = (grn_accessor *)obj;
670     GRN_TEXT_INIT(&buf, 0);
671     for (;;) {
672       buf.header.domain = grn_obj_get_range(ctx, obj);
673       GRN_BULK_REWIND(&buf);
674       switch (a->action) {
675       case GRN_ACCESSOR_GET_ID :
676         GRN_UINT32_PUT(ctx, &buf, id);
677         buf.header.domain = GRN_DB_UINT32;
678         break;
679       case GRN_ACCESSOR_GET_KEY :
680         grn_table_get_key2(ctx, a->obj, id, &buf);
681         buf.header.domain = DB_OBJ(a->obj)->header.domain;
682         break;
683       case GRN_ACCESSOR_GET_VALUE :
684         grn_obj_get_value(ctx, a->obj, id, &buf);
685         buf.header.domain = DB_OBJ(a->obj)->range;
686         break;
687       case GRN_ACCESSOR_GET_SCORE :
688         {
689           grn_rset_recinfo *ri =
690             (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
691           if (grn_ctx_get_command_version(ctx) == GRN_COMMAND_VERSION_1) {
692             int32_t int32_score = ri->score;
693             GRN_INT32_PUT(ctx, &buf, int32_score);
694             buf.header.domain = GRN_DB_INT32;
695           } else {
696             double float_score = ri->score;
697             GRN_FLOAT_PUT(ctx, &buf, float_score);
698             buf.header.domain = GRN_DB_FLOAT;
699           }
700         }
701         break;
702       case GRN_ACCESSOR_GET_NSUBRECS :
703         {
704           grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
705           GRN_INT32_PUT(ctx, &buf, ri->n_subrecs);
706         }
707         buf.header.domain = GRN_DB_INT32;
708         break;
709       case GRN_ACCESSOR_GET_MAX :
710         {
711           grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
712           int64_t max;
713           max = grn_rset_recinfo_get_max(ctx, ri, a->obj);
714           GRN_INT64_PUT(ctx, &buf, max);
715         }
716         buf.header.domain = GRN_DB_INT64;
717         break;
718       case GRN_ACCESSOR_GET_MIN :
719         {
720           grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
721           int64_t min;
722           min = grn_rset_recinfo_get_min(ctx, ri, a->obj);
723           GRN_INT64_PUT(ctx, &buf, min);
724         }
725         buf.header.domain = GRN_DB_INT64;
726         break;
727       case GRN_ACCESSOR_GET_SUM :
728         {
729           grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
730           int64_t sum;
731           sum = grn_rset_recinfo_get_sum(ctx, ri, a->obj);
732           GRN_INT64_PUT(ctx, &buf, sum);
733         }
734         buf.header.domain = GRN_DB_INT64;
735         break;
736       case GRN_ACCESSOR_GET_AVG :
737         {
738           grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
739           double avg;
740           avg = grn_rset_recinfo_get_avg(ctx, ri, a->obj);
741           GRN_FLOAT_PUT(ctx, &buf, avg);
742         }
743         buf.header.domain = GRN_DB_FLOAT;
744         break;
745       case GRN_ACCESSOR_GET_COLUMN_VALUE :
746         if ((a->obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
747           if (a->next) {
748             grn_id *idp;
749             grn_obj_get_value(ctx, a->obj, id, &buf);
750             idp = (grn_id *)GRN_BULK_HEAD(&buf);
751             vs = GRN_BULK_VSIZE(&buf) / sizeof(grn_id);
752             grn_output_array_open(ctx, outbuf, output_type, "VECTOR", vs);
753             for (; vs--; idp++) {
754               grn_text_atoj(ctx, outbuf, output_type, (grn_obj *)a->next, *idp);
755             }
756             grn_output_array_close(ctx, outbuf, output_type);
757           } else {
758             grn_text_atoj(ctx, outbuf, output_type, a->obj, id);
759           }
760           goto exit;
761         } else {
762           grn_obj_get_value(ctx, a->obj, id, &buf);
763         }
764         break;
765       case GRN_ACCESSOR_GET_DB_OBJ :
766         /* todo */
767         break;
768       case GRN_ACCESSOR_LOOKUP :
769         /* todo */
770         break;
771       case GRN_ACCESSOR_FUNCALL :
772         /* todo */
773         break;
774       }
775       if (a->next) {
776         a = a->next;
777         if (GRN_BULK_VSIZE(&buf) >= sizeof(grn_id)) {
778           id = *((grn_id *)GRN_BULK_HEAD(&buf));
779         } else {
780           id = GRN_ID_NIL;
781         }
782       } else {
783         break;
784       }
785     }
786     grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
787   } else {
788     grn_obj_format *format_argument = NULL;
789     grn_obj_format format;
790     GRN_OBJ_FORMAT_INIT(&format, 0, 0, 0, 0);
791     switch (obj->header.type) {
792     case GRN_COLUMN_FIX_SIZE :
793       GRN_VALUE_FIX_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
794       break;
795     case GRN_COLUMN_VAR_SIZE :
796       if ((obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
797         grn_obj *range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
798         if (GRN_OBJ_TABLEP(range) ||
799             (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) == 0) {
800           GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
801         } else {
802           GRN_VALUE_VAR_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
803         }
804         if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
805           format.flags |= GRN_OBJ_FORMAT_WITH_WEIGHT;
806           format_argument = &format;
807         }
808       } else {
809         GRN_VALUE_VAR_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
810       }
811       break;
812     case GRN_COLUMN_INDEX :
813       GRN_UINT32_INIT(&buf, 0);
814       break;
815     default:
816       GRN_TEXT_INIT(&buf, 0);
817       break;
818     }
819     grn_obj_get_value(ctx, obj, id, &buf);
820     grn_output_obj(ctx, outbuf, output_type, &buf, format_argument);
821   }
822 exit :
823   grn_obj_close(ctx, &buf);
824 }
825 
826 static inline void
grn_output_void(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * bulk,grn_obj_format * format)827 grn_output_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
828                 grn_obj *bulk, grn_obj_format *format)
829 {
830   grn_output_null(ctx, outbuf, output_type);
831 }
832 
833 static inline void
grn_output_bulk(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * bulk,grn_obj_format * format)834 grn_output_bulk(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
835                 grn_obj *bulk, grn_obj_format *format)
836 {
837   grn_obj buf;
838   GRN_TEXT_INIT(&buf, 0);
839   switch (bulk->header.domain) {
840   case GRN_DB_VOID :
841     grn_output_bulk_void(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
842     break;
843   case GRN_DB_SHORT_TEXT :
844   case GRN_DB_TEXT :
845   case GRN_DB_LONG_TEXT :
846     grn_output_str(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
847     break;
848   case GRN_DB_BOOL :
849     grn_output_bool(ctx, outbuf, output_type,
850                     GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
851     break;
852   case GRN_DB_INT8 :
853     grn_output_int32(ctx, outbuf, output_type,
854                      GRN_BULK_VSIZE(bulk) ? GRN_INT8_VALUE(bulk) : 0);
855     break;
856   case GRN_DB_UINT8 :
857     grn_output_int32(ctx, outbuf, output_type,
858                      GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
859     break;
860   case GRN_DB_INT16 :
861     grn_output_int32(ctx, outbuf, output_type,
862                      GRN_BULK_VSIZE(bulk) ? GRN_INT16_VALUE(bulk) : 0);
863     break;
864   case GRN_DB_UINT16 :
865     grn_output_int32(ctx, outbuf, output_type,
866                      GRN_BULK_VSIZE(bulk) ? GRN_UINT16_VALUE(bulk) : 0);
867     break;
868   case GRN_DB_INT32 :
869     grn_output_int32(ctx, outbuf, output_type,
870                      GRN_BULK_VSIZE(bulk) ? GRN_INT32_VALUE(bulk) : 0);
871     break;
872   case GRN_DB_UINT32 :
873     grn_output_int64(ctx, outbuf, output_type,
874                      GRN_BULK_VSIZE(bulk) ? GRN_UINT32_VALUE(bulk) : 0);
875     break;
876   case GRN_DB_INT64 :
877     grn_output_int64(ctx, outbuf, output_type,
878                      GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
879     break;
880   case GRN_DB_UINT64 :
881     grn_output_uint64(ctx, outbuf, output_type,
882                       GRN_BULK_VSIZE(bulk) ? GRN_UINT64_VALUE(bulk) : 0);
883     break;
884   case GRN_DB_FLOAT :
885     grn_output_float(ctx, outbuf, output_type,
886                      GRN_BULK_VSIZE(bulk) ? GRN_FLOAT_VALUE(bulk) : 0);
887     break;
888   case GRN_DB_TIME :
889     grn_output_time(ctx, outbuf, output_type,
890                     GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
891     break;
892   case GRN_DB_TOKYO_GEO_POINT :
893   case GRN_DB_WGS84_GEO_POINT :
894     grn_output_geo_point(ctx, outbuf, output_type,
895                          GRN_BULK_VSIZE(bulk) ? (grn_geo_point *)GRN_BULK_HEAD(bulk) : NULL);
896     break;
897   default :
898     if (format) {
899       int j;
900       int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
901       grn_id id = GRN_RECORD_VALUE(bulk);
902       grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
903       if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
904         grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
905         for (j = 0; j < ncolumns; j++) {
906           grn_id range_id;
907           grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
908           GRN_BULK_REWIND(&buf);
909           grn_column_name_(ctx, columns[j], &buf);
910           grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
911           /* column range */
912           range_id = grn_obj_get_range(ctx, columns[j]);
913           if (range_id == GRN_ID_NIL) {
914             GRN_TEXT_PUTS(ctx, outbuf, "null");
915           } else {
916             int name_len;
917             grn_obj *range_obj;
918             char name_buf[GRN_TABLE_MAX_KEY_SIZE];
919 
920             range_obj = grn_ctx_at(ctx, range_id);
921             name_len = grn_obj_name(ctx, range_obj, name_buf,
922                                     GRN_TABLE_MAX_KEY_SIZE);
923             GRN_BULK_REWIND(&buf);
924             GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
925             grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
926           }
927           grn_output_array_close(ctx, outbuf, output_type);
928         }
929         grn_output_array_close(ctx, outbuf, output_type);
930       }
931       grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
932       for (j = 0; j < ncolumns; j++) {
933         grn_text_atoj(ctx, outbuf, output_type, columns[j], id);
934       }
935       grn_output_array_close(ctx, outbuf, output_type);
936     } else {
937       grn_obj *table = grn_ctx_at(ctx, bulk->header.domain);
938       grn_id id = GRN_RECORD_VALUE(bulk);
939       if (table && table->header.type != GRN_TABLE_NO_KEY) {
940         grn_obj *accessor = grn_obj_column(ctx, table,
941                                            GRN_COLUMN_NAME_KEY,
942                                            GRN_COLUMN_NAME_KEY_LEN);
943         if (accessor) {
944           if (id == GRN_ID_NIL) {
945             grn_obj_reinit_for(ctx, &buf, accessor);
946           } else {
947             grn_obj_get_value(ctx, accessor, id, &buf);
948           }
949           grn_obj_unlink(ctx, accessor);
950         }
951         grn_output_obj(ctx, outbuf, output_type, &buf, format);
952       } else {
953         grn_output_int64(ctx, outbuf, output_type, id);
954       }
955     }
956     break;
957   }
958   GRN_OBJ_FIN(ctx, &buf);
959 }
960 
961 static void
grn_output_uvector_result_set(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * uvector,grn_obj_format * format)962 grn_output_uvector_result_set(grn_ctx *ctx,
963                               grn_obj *outbuf,
964                               grn_content_type output_type,
965                               grn_obj *uvector,
966                               grn_obj_format *format)
967 {
968   unsigned int i_hit, n_hits;
969   unsigned int i_column, n_columns;
970   unsigned int n_elements;
971   grn_obj **columns;
972   grn_obj buf;
973   grn_bool with_column_names = GRN_FALSE;
974 
975   n_hits = grn_vector_size(ctx, uvector);
976 
977   n_columns = GRN_BULK_VSIZE(&format->columns) / sizeof(grn_obj *);
978   columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
979 
980   GRN_TEXT_INIT(&buf, 0);
981 
982   if (n_hits > 0 && format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
983     with_column_names = GRN_TRUE;
984   }
985 
986   n_elements = 1; /* for NHITS */
987   if (with_column_names) {
988     n_elements += 1; /* for COLUMNS */
989   }
990   n_elements += n_hits; /* for HITS */
991   grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", n_elements);
992 
993   grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
994   grn_text_itoa(ctx, outbuf, n_hits);
995   grn_output_array_close(ctx, outbuf, output_type);
996 
997   if (with_column_names) {
998     grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_columns);
999     for (i_column = 0; i_column < n_columns; i_column++) {
1000       grn_id range_id;
1001       grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
1002 
1003       /* name */
1004       GRN_BULK_REWIND(&buf);
1005       grn_column_name_(ctx, columns[i_column], &buf);
1006       grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
1007 
1008       /* type */
1009       range_id = grn_obj_get_range(ctx, columns[i_column]);
1010       if (range_id == GRN_ID_NIL) {
1011         GRN_TEXT_PUTS(ctx, outbuf, "null");
1012       } else {
1013         int name_len;
1014         grn_obj *range_obj;
1015         char name_buf[GRN_TABLE_MAX_KEY_SIZE];
1016 
1017         range_obj = grn_ctx_at(ctx, range_id);
1018         name_len = grn_obj_name(ctx, range_obj, name_buf,
1019                                 GRN_TABLE_MAX_KEY_SIZE);
1020         GRN_BULK_REWIND(&buf);
1021         GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
1022         grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
1023       }
1024 
1025       grn_output_array_close(ctx, outbuf, output_type);
1026     }
1027     grn_output_array_close(ctx, outbuf, output_type);
1028   }
1029 
1030   for (i_hit = 0; i_hit < n_hits++; i_hit++) {
1031     grn_id id;
1032 
1033     id = grn_uvector_get_element(ctx, uvector, i_hit, NULL);
1034     grn_output_array_open(ctx, outbuf, output_type, "HITS", n_columns);
1035     for (i_column = 0; i_column < n_columns; i_column++) {
1036       GRN_BULK_REWIND(&buf);
1037       grn_obj_get_value(ctx, columns[i_column], id, &buf);
1038       grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
1039     }
1040     grn_output_array_close(ctx, outbuf, output_type);
1041   }
1042 
1043   grn_output_array_close(ctx, outbuf, output_type);
1044 
1045   GRN_OBJ_FIN(ctx, &buf);
1046 }
1047 
1048 static inline void
grn_output_uvector(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * uvector,grn_obj_format * format)1049 grn_output_uvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
1050                    grn_obj *uvector, grn_obj_format *format)
1051 {
1052   grn_bool output_result_set = GRN_FALSE;
1053   grn_bool with_weight = GRN_FALSE;
1054   grn_obj *range;
1055   grn_bool range_is_type;
1056 
1057   if (format) {
1058     if (GRN_BULK_VSIZE(&(format->columns)) > 0) {
1059       output_result_set = GRN_TRUE;
1060     }
1061     if (format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT) {
1062       with_weight = GRN_TRUE;
1063     }
1064   }
1065 
1066   if (output_result_set) {
1067     grn_output_uvector_result_set(ctx, outbuf, output_type, uvector, format);
1068     return;
1069   }
1070 
1071   range = grn_ctx_at(ctx, uvector->header.domain);
1072   range_is_type = (range->header.type == GRN_TYPE);
1073   if (range_is_type) {
1074     unsigned int i, n;
1075     char *raw_elements;
1076     unsigned int element_size;
1077     grn_obj element;
1078 
1079     raw_elements = GRN_BULK_HEAD(uvector);
1080     element_size = GRN_TYPE_SIZE(DB_OBJ(range));
1081     n = GRN_BULK_VSIZE(uvector) / element_size;
1082 
1083     grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
1084     GRN_OBJ_INIT(&element, GRN_BULK, 0, uvector->header.domain);
1085     for (i = 0; i < n; i++) {
1086       GRN_BULK_REWIND(&element);
1087       grn_bulk_write_from(ctx, &element, raw_elements + (element_size * i),
1088                           0, element_size);
1089       grn_output_obj(ctx, outbuf, output_type, &element, NULL);
1090     }
1091     GRN_OBJ_FIN(ctx, &element);
1092     grn_output_array_close(ctx, outbuf, output_type);
1093   } else {
1094     unsigned int i, n;
1095     grn_obj id_value;
1096     grn_obj key_value;
1097 
1098     GRN_UINT32_INIT(&id_value, 0);
1099     GRN_OBJ_INIT(&key_value, GRN_BULK, 0, range->header.domain);
1100 
1101     n = grn_vector_size(ctx, uvector);
1102     if (with_weight) {
1103       grn_output_map_open(ctx, outbuf, output_type, "WEIGHT_VECTOR", n);
1104     } else {
1105       grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
1106     }
1107 
1108     for (i = 0; i < n; i++) {
1109       grn_id id;
1110       unsigned int weight;
1111 
1112       id = grn_uvector_get_element(ctx, uvector, i, &weight);
1113       if (range->header.type == GRN_TABLE_NO_KEY) {
1114         GRN_UINT32_SET(ctx, &id_value, id);
1115         grn_output_obj(ctx, outbuf, output_type, &id_value, NULL);
1116       } else {
1117         GRN_BULK_REWIND(&key_value);
1118         grn_table_get_key2(ctx, range, id, &key_value);
1119         grn_output_obj(ctx, outbuf, output_type, &key_value, NULL);
1120       }
1121 
1122       if (with_weight) {
1123         grn_output_uint64(ctx, outbuf, output_type, weight);
1124       }
1125     }
1126 
1127     if (with_weight) {
1128       grn_output_map_close(ctx, outbuf, output_type);
1129     } else {
1130       grn_output_array_close(ctx, outbuf, output_type);
1131     }
1132 
1133     GRN_OBJ_FIN(ctx, &id_value);
1134     GRN_OBJ_FIN(ctx, &key_value);
1135   }
1136   grn_obj_unlink(ctx, range);
1137 }
1138 
1139 static inline void
grn_output_vector(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * vector,grn_obj_format * format)1140 grn_output_vector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
1141                   grn_obj *vector, grn_obj_format *format)
1142 {
1143   grn_bool with_weight = GRN_FALSE;
1144 
1145   if (vector->header.domain == GRN_DB_VOID) {
1146     ERR(GRN_INVALID_ARGUMENT, "invalid obj->header.domain");
1147     return;
1148   }
1149 
1150   if (format) {
1151     if (format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT) {
1152       with_weight = GRN_TRUE;
1153     }
1154   }
1155 
1156   if (with_weight) {
1157     unsigned int i, n;
1158     grn_obj value;
1159 
1160     GRN_VOID_INIT(&value);
1161     n = grn_vector_size(ctx, vector);
1162     grn_output_map_open(ctx, outbuf, output_type, "WEIGHT_VECTOR", n);
1163     for (i = 0; i < n; i++) {
1164       const char *_value;
1165       unsigned int weight, length;
1166       grn_id domain;
1167 
1168       length = grn_vector_get_element(ctx, vector, i,
1169                                       &_value, &weight, &domain);
1170       if (domain != GRN_DB_VOID) {
1171         grn_obj_reinit(ctx, &value, domain, 0);
1172       } else {
1173         grn_obj_reinit(ctx, &value, vector->header.domain, 0);
1174       }
1175       grn_bulk_write(ctx, &value, _value, length);
1176       grn_output_obj(ctx, outbuf, output_type, &value, NULL);
1177       grn_output_uint64(ctx, outbuf, output_type, weight);
1178     }
1179     grn_output_map_close(ctx, outbuf, output_type);
1180     GRN_OBJ_FIN(ctx, &value);
1181   } else {
1182     unsigned int i, n;
1183     grn_obj value;
1184     GRN_VOID_INIT(&value);
1185     n = grn_vector_size(ctx, vector);
1186     grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
1187     for (i = 0; i < n; i++) {
1188       const char *_value;
1189       unsigned int weight, length;
1190       grn_id domain;
1191 
1192       length = grn_vector_get_element(ctx, vector, i,
1193                                       &_value, &weight, &domain);
1194       if (domain != GRN_DB_VOID) {
1195         grn_obj_reinit(ctx, &value, domain, 0);
1196       } else {
1197         grn_obj_reinit(ctx, &value, vector->header.domain, 0);
1198       }
1199       grn_bulk_write(ctx, &value, _value, length);
1200       grn_output_obj(ctx, outbuf, output_type, &value, NULL);
1201     }
1202     grn_output_array_close(ctx, outbuf, output_type);
1203     GRN_OBJ_FIN(ctx, &value);
1204   }
1205 }
1206 
1207 static inline void
grn_output_pvector(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * pvector,grn_obj_format * format)1208 grn_output_pvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
1209                    grn_obj *pvector, grn_obj_format *format)
1210 {
1211   if (format) {
1212     ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
1213         "cannot print GRN_PVECTOR using grn_obj_format");
1214   } else {
1215     unsigned int i, n;
1216     grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1);
1217     n = GRN_BULK_VSIZE(pvector) / sizeof(grn_obj *);
1218     for (i = 0; i < n; i++) {
1219       grn_obj *value;
1220 
1221       value = GRN_PTR_VALUE_AT(pvector, i);
1222       grn_output_obj(ctx, outbuf, output_type, value, NULL);
1223     }
1224     grn_output_array_close(ctx, outbuf, output_type);
1225   }
1226 }
1227 
1228 static inline void
grn_output_result_set_n_hits_v1(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj_format * format)1229 grn_output_result_set_n_hits_v1(grn_ctx *ctx,
1230                                 grn_obj *outbuf,
1231                                 grn_content_type output_type,
1232                                 grn_obj_format *format)
1233 {
1234   grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
1235   if (output_type == GRN_CONTENT_XML) {
1236     grn_text_itoa(ctx, outbuf, format->nhits);
1237   } else {
1238     grn_output_int32(ctx, outbuf, output_type, format->nhits);
1239   }
1240   grn_output_array_close(ctx, outbuf, output_type);
1241 }
1242 
1243 static inline void
grn_output_result_set_n_hits_v3(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj_format * format)1244 grn_output_result_set_n_hits_v3(grn_ctx *ctx,
1245                                 grn_obj *outbuf,
1246                                 grn_content_type output_type,
1247                                 grn_obj_format *format)
1248 {
1249   grn_output_cstr(ctx, outbuf, output_type, "n_hits");
1250   grn_output_int32(ctx, outbuf, output_type, format->nhits);
1251 }
1252 
1253 static inline void
grn_output_result_set_n_hits(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj_format * format)1254 grn_output_result_set_n_hits(grn_ctx *ctx,
1255                              grn_obj *outbuf,
1256                              grn_content_type output_type,
1257                              grn_obj_format *format)
1258 {
1259   if (format->nhits == -1) {
1260     return;
1261   }
1262 
1263   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1264     grn_output_result_set_n_hits_v1(ctx, outbuf, output_type, format);
1265   } else {
1266     grn_output_result_set_n_hits_v3(ctx, outbuf, output_type, format);
1267   }
1268 }
1269 
1270 static inline void
grn_output_table_column_info(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,const char * name,const char * type)1271 grn_output_table_column_info(grn_ctx *ctx,
1272                              grn_obj *outbuf,
1273                              grn_content_type output_type,
1274                              const char *name,
1275                              const char *type)
1276 {
1277   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1278     grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
1279     if (name) {
1280       grn_output_cstr(ctx, outbuf, output_type, name);
1281     } else {
1282       grn_output_null(ctx, outbuf, output_type);
1283     }
1284     if (type) {
1285       grn_output_cstr(ctx, outbuf, output_type, type);
1286     } else {
1287       grn_output_null(ctx, outbuf, output_type);
1288     }
1289     grn_output_array_close(ctx, outbuf, output_type);
1290   } else {
1291     grn_output_map_open(ctx, outbuf, output_type, "column", 2);
1292     grn_output_cstr(ctx, outbuf, output_type, "name");
1293     if (name) {
1294       grn_output_cstr(ctx, outbuf, output_type, name);
1295     } else {
1296       grn_output_null(ctx, outbuf, output_type);
1297     }
1298     grn_output_cstr(ctx, outbuf, output_type, "type");
1299     if (type) {
1300       grn_output_cstr(ctx, outbuf, output_type, type);
1301     } else {
1302       grn_output_null(ctx, outbuf, output_type);
1303     }
1304     grn_output_map_close(ctx, outbuf, output_type);
1305   }
1306 }
1307 
1308 static inline int
count_n_elements_in_expression(grn_ctx * ctx,grn_obj * expression)1309 count_n_elements_in_expression(grn_ctx *ctx, grn_obj *expression)
1310 {
1311   int n_elements = 0;
1312   grn_bool is_first_comma = GRN_TRUE;
1313   grn_expr *expr = (grn_expr *)expression;
1314   grn_expr_code *code;
1315   grn_expr_code *code_end = expr->codes + expr->codes_curr;
1316 
1317   for (code = expr->codes; code < code_end; code++) {
1318     if (code->op == GRN_OP_COMMA) {
1319       n_elements++;
1320       if (is_first_comma) {
1321         n_elements++;
1322         is_first_comma = GRN_FALSE;
1323       }
1324     }
1325   }
1326 
1327   return n_elements;
1328 }
1329 
1330 static grn_bool
is_score_accessor(grn_ctx * ctx,grn_obj * obj)1331 is_score_accessor(grn_ctx *ctx, grn_obj *obj)
1332 {
1333   grn_accessor *a;
1334 
1335   if (obj->header.type != GRN_ACCESSOR) {
1336     return GRN_FALSE;
1337   }
1338 
1339   for (a = (grn_accessor *)obj; a->next; a = a->next) {
1340   }
1341   return a->action == GRN_ACCESSOR_GET_SCORE;
1342 }
1343 
1344 static inline void
grn_output_table_column(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * column,grn_obj * buf)1345 grn_output_table_column(grn_ctx *ctx, grn_obj *outbuf,
1346                         grn_content_type output_type,
1347                         grn_obj *column, grn_obj *buf)
1348 {
1349   grn_id range_id = GRN_ID_NIL;
1350 
1351   if (!column) {
1352     grn_output_table_column_info(ctx, outbuf, output_type, NULL, NULL);
1353     return;
1354   }
1355 
1356   GRN_BULK_REWIND(buf);
1357   grn_column_name_(ctx, column, buf);
1358   GRN_TEXT_PUTC(ctx, buf, '\0');
1359 
1360   if (column->header.type == GRN_COLUMN_INDEX) {
1361     range_id = GRN_DB_UINT32;
1362   } else if (is_score_accessor(ctx, column)) {
1363     if (grn_ctx_get_command_version(ctx) == GRN_COMMAND_VERSION_1) {
1364       range_id = GRN_DB_INT32;
1365     } else {
1366       range_id = GRN_DB_FLOAT;
1367     }
1368   }
1369   if (range_id == GRN_ID_NIL) {
1370     range_id = grn_obj_get_range(ctx, column);
1371   }
1372   if (range_id == GRN_ID_NIL) {
1373     grn_output_table_column_info(ctx,
1374                                  outbuf,
1375                                  output_type,
1376                                  GRN_TEXT_VALUE(buf),
1377                                  NULL);
1378   } else {
1379     grn_obj *range_obj;
1380     char type_name[GRN_TABLE_MAX_KEY_SIZE];
1381     int type_name_len;
1382 
1383     range_obj = grn_ctx_at(ctx, range_id);
1384     type_name_len = grn_obj_name(ctx,
1385                                  range_obj,
1386                                  type_name,
1387                                  GRN_TABLE_MAX_KEY_SIZE);
1388     type_name[type_name_len] = '\0';
1389     grn_output_table_column_info(ctx,
1390                                  outbuf,
1391                                  output_type,
1392                                  GRN_TEXT_VALUE(buf),
1393                                  type_name);
1394   }
1395 }
1396 
1397 static inline void
grn_output_table_column_by_expression(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_expr_code * code,grn_expr_code * code_end,grn_obj * buf)1398 grn_output_table_column_by_expression(grn_ctx *ctx, grn_obj *outbuf,
1399                                       grn_content_type output_type,
1400                                       grn_expr_code *code,
1401                                       grn_expr_code *code_end,
1402                                       grn_obj *buf)
1403 {
1404   if (code_end <= code) {
1405     grn_output_table_column_info(ctx,
1406                                  outbuf,
1407                                  output_type,
1408                                  NULL,
1409                                  NULL);
1410     return;
1411   }
1412 
1413   switch (code_end[-1].op) {
1414   case GRN_OP_GET_MEMBER :
1415     if ((code_end - code) == 3) {
1416       GRN_BULK_REWIND(buf);
1417       grn_column_name_(ctx, code[0].value, buf);
1418       GRN_TEXT_PUTC(ctx, buf, '[');
1419       grn_inspect(ctx, buf, code[1].value);
1420       GRN_TEXT_PUTC(ctx, buf, ']');
1421       GRN_TEXT_PUTC(ctx, buf, '\0');
1422 
1423       grn_output_table_column_info(ctx,
1424                                    outbuf,
1425                                    output_type,
1426                                    GRN_TEXT_VALUE(buf),
1427                                    NULL);
1428     } else {
1429       grn_output_table_column(ctx, outbuf, output_type, code->value, buf);
1430     }
1431     break;
1432   default :
1433     grn_output_table_column(ctx, outbuf, output_type, code->value, buf);
1434     break;
1435   }
1436 }
1437 
1438 static inline void
grn_output_table_columns_open(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,int n_columns)1439 grn_output_table_columns_open(grn_ctx *ctx,
1440                               grn_obj *outbuf,
1441                               grn_content_type output_type,
1442                               int n_columns)
1443 {
1444   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1445     grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_columns);
1446   } else {
1447     grn_output_cstr(ctx, outbuf, output_type, "columns");
1448     grn_output_array_open(ctx, outbuf, output_type, "columns", n_columns);
1449   }
1450 }
1451 
1452 static inline void
grn_output_table_columns_close(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)1453 grn_output_table_columns_close(grn_ctx *ctx,
1454                                grn_obj *outbuf,
1455                                grn_content_type output_type)
1456 {
1457   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1458     grn_output_array_close(ctx, outbuf, output_type);
1459   } else {
1460     grn_output_array_close(ctx, outbuf, output_type);
1461   }
1462 }
1463 
1464 static inline void
grn_output_table_columns_by_expression(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * table,grn_obj_format * format,grn_obj * buf)1465 grn_output_table_columns_by_expression(grn_ctx *ctx, grn_obj *outbuf,
1466                                        grn_content_type output_type,
1467                                        grn_obj *table, grn_obj_format *format,
1468                                        grn_obj *buf)
1469 {
1470   int n_elements;
1471   int previous_comma_offset = -1;
1472   grn_bool is_first_comma = GRN_TRUE;
1473   grn_bool have_comma = GRN_FALSE;
1474   grn_expr *expr = (grn_expr *)format->expression;
1475   grn_expr_code *code;
1476   grn_expr_code *code_end = expr->codes + expr->codes_curr;
1477 
1478   n_elements = count_n_elements_in_expression(ctx, format->expression);
1479 
1480   grn_output_table_columns_open(ctx, outbuf, output_type, n_elements);
1481 
1482   for (code = expr->codes; code < code_end; code++) {
1483     int code_start_offset;
1484 
1485     if (code->op != GRN_OP_COMMA) {
1486       continue;
1487     }
1488 
1489     have_comma = GRN_TRUE;
1490     if (is_first_comma) {
1491       unsigned int n_used_codes;
1492       int code_end_offset;
1493 
1494       n_used_codes = grn_expr_code_n_used_codes(ctx, expr->codes, code - 1);
1495       code_end_offset = code - expr->codes - n_used_codes;
1496 
1497       grn_output_table_column_by_expression(ctx, outbuf, output_type,
1498                                             expr->codes,
1499                                             expr->codes + code_end_offset,
1500                                             buf);
1501       code_start_offset = code_end_offset;
1502       is_first_comma = GRN_FALSE;
1503     } else {
1504       code_start_offset = previous_comma_offset + 1;
1505     }
1506 
1507     grn_output_table_column_by_expression(ctx, outbuf, output_type,
1508                                           expr->codes + code_start_offset,
1509                                           code,
1510                                           buf);
1511     previous_comma_offset = code - expr->codes;
1512   }
1513 
1514   if (!have_comma && expr->codes_curr > 0) {
1515     grn_output_table_column_by_expression(ctx, outbuf, output_type,
1516                                           expr->codes,
1517                                           code_end,
1518                                           buf);
1519   }
1520 
1521   grn_output_table_columns_close(ctx, outbuf, output_type);
1522 }
1523 
1524 static inline void
grn_output_table_columns_by_columns(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * table,grn_obj_format * format,grn_obj * buf)1525 grn_output_table_columns_by_columns(grn_ctx *ctx, grn_obj *outbuf,
1526                                     grn_content_type output_type,
1527                                     grn_obj *table, grn_obj_format *format,
1528                                     grn_obj *buf)
1529 {
1530   int i;
1531   int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
1532   grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
1533 
1534   grn_output_table_columns_open(ctx, outbuf, output_type, ncolumns);
1535   for (i = 0; i < ncolumns; i++) {
1536     grn_output_table_column(ctx, outbuf, output_type, columns[i], buf);
1537   }
1538   grn_output_table_columns_close(ctx, outbuf, output_type);
1539 }
1540 
1541 void
grn_output_table_columns(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * table,grn_obj_format * format)1542 grn_output_table_columns(grn_ctx *ctx, grn_obj *outbuf,
1543                          grn_content_type output_type,
1544                          grn_obj *table, grn_obj_format *format)
1545 {
1546   grn_obj buf;
1547 
1548   GRN_TEXT_INIT(&buf, 0);
1549   if (format->expression) {
1550     grn_output_table_columns_by_expression(ctx, outbuf, output_type,
1551                                            table, format, &buf);
1552   } else {
1553     grn_output_table_columns_by_columns(ctx, outbuf, output_type,
1554                                         table, format, &buf);
1555   }
1556   GRN_OBJ_FIN(ctx, &buf);
1557 }
1558 
1559 static inline void
grn_output_table_record_open(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,int n_columns)1560 grn_output_table_record_open(grn_ctx *ctx,
1561                               grn_obj *outbuf,
1562                               grn_content_type output_type,
1563                               int n_columns)
1564 {
1565   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1566     grn_output_array_open(ctx, outbuf, output_type, "HIT", n_columns);
1567   } else {
1568     grn_output_array_open(ctx, outbuf, output_type, "record", n_columns);
1569   }
1570 }
1571 
1572 static inline void
grn_output_table_record_close(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)1573 grn_output_table_record_close(grn_ctx *ctx,
1574                                grn_obj *outbuf,
1575                                grn_content_type output_type)
1576 {
1577   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1578     grn_output_array_close(ctx, outbuf, output_type);
1579   } else {
1580     grn_output_array_close(ctx, outbuf, output_type);
1581   }
1582 }
1583 
1584 static inline void
grn_output_table_record_by_column(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * column,grn_id id)1585 grn_output_table_record_by_column(grn_ctx *ctx,
1586                                   grn_obj *outbuf,
1587                                   grn_content_type output_type,
1588                                   grn_obj *column,
1589                                   grn_id id)
1590 {
1591   grn_text_atoj(ctx, outbuf, output_type, column, id);
1592 }
1593 
1594 static inline void
grn_output_table_record_by_expression(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * expression,grn_obj * record)1595 grn_output_table_record_by_expression(grn_ctx *ctx,
1596                                       grn_obj *outbuf,
1597                                       grn_content_type output_type,
1598                                       grn_obj *expression,
1599                                       grn_obj *record)
1600 {
1601   grn_expr *expr = (grn_expr *)expression;
1602 
1603   if (expr->codes_curr == 1 && expr->codes[0].op == GRN_OP_GET_VALUE) {
1604     grn_obj *column = expr->codes[0].value;
1605     grn_output_table_record_by_column(ctx,
1606                                       outbuf,
1607                                       output_type,
1608                                       column,
1609                                       GRN_RECORD_VALUE(record));
1610   } else {
1611     grn_obj *result;
1612     result = grn_expr_exec(ctx, expression, 0);
1613     if (result) {
1614       grn_output_obj(ctx, outbuf, output_type, result, NULL);
1615     } else {
1616       grn_output_cstr(ctx, outbuf, output_type, ctx->errbuf);
1617     }
1618   }
1619 }
1620 
1621 static inline void
grn_output_table_records_by_expression(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_table_cursor * tc,grn_obj_format * format)1622 grn_output_table_records_by_expression(grn_ctx *ctx, grn_obj *outbuf,
1623                                        grn_content_type output_type,
1624                                        grn_table_cursor *tc,
1625                                        grn_obj_format *format)
1626 {
1627   int n_elements = 0;
1628   grn_id id;
1629   grn_obj *record;
1630   grn_expr *expr = (grn_expr *)format->expression;
1631   grn_expr_code *code;
1632   grn_expr_code *code_end = expr->codes + expr->codes_curr;
1633 
1634   n_elements = count_n_elements_in_expression(ctx, format->expression);
1635   record = grn_expr_get_var_by_offset(ctx, format->expression, 0);
1636   while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
1637     int previous_comma_offset = -1;
1638     grn_bool is_first_comma = GRN_TRUE;
1639     grn_bool have_comma = GRN_FALSE;
1640     GRN_RECORD_SET(ctx, record, id);
1641     grn_output_table_record_open(ctx, outbuf, output_type, n_elements);
1642     for (code = expr->codes; code < code_end; code++) {
1643       if (code->op == GRN_OP_COMMA) {
1644         int code_start_offset = previous_comma_offset + 1;
1645         int code_end_offset;
1646         int original_codes_curr = expr->codes_curr;
1647 
1648         have_comma = GRN_TRUE;
1649         if (is_first_comma) {
1650           int second_code_offset;
1651           unsigned int second_code_n_used_codes;
1652           second_code_offset = code - expr->codes - 1;
1653           second_code_n_used_codes =
1654             grn_expr_code_n_used_codes(ctx,
1655                                        expr->codes,
1656                                        expr->codes + second_code_offset);
1657           expr->codes_curr = second_code_offset - second_code_n_used_codes + 1;
1658           grn_output_table_record_by_expression(ctx,
1659                                                 outbuf,
1660                                                 output_type,
1661                                                 format->expression,
1662                                                 record);
1663           code_start_offset = expr->codes_curr;
1664           is_first_comma = GRN_FALSE;
1665         }
1666         code_end_offset = code - expr->codes - code_start_offset;
1667         expr->codes += code_start_offset;
1668         expr->codes_curr = code_end_offset;
1669         grn_output_table_record_by_expression(ctx,
1670                                               outbuf,
1671                                               output_type,
1672                                               format->expression,
1673                                               record);
1674         expr->codes -= code_start_offset;
1675         expr->codes_curr = original_codes_curr;
1676         previous_comma_offset = code - expr->codes;
1677       }
1678     }
1679 
1680     if (!have_comma && expr->codes_curr > 0) {
1681       grn_output_table_record_by_expression(ctx,
1682                                             outbuf,
1683                                             output_type,
1684                                             format->expression,
1685                                             record);
1686     }
1687 
1688     grn_output_table_record_close(ctx, outbuf, output_type);
1689   }
1690 }
1691 
1692 static inline void
grn_output_table_records_by_columns(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_table_cursor * tc,grn_obj_format * format)1693 grn_output_table_records_by_columns(grn_ctx *ctx, grn_obj *outbuf,
1694                                     grn_content_type output_type,
1695                                     grn_table_cursor *tc,
1696                                     grn_obj_format *format)
1697 {
1698   int i;
1699   grn_id id;
1700   int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
1701   grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
1702   while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
1703     grn_output_table_record_open(ctx, outbuf, output_type, ncolumns);
1704     for (i = 0; i < ncolumns; i++) {
1705       grn_output_table_record_by_column(ctx,
1706                                         outbuf,
1707                                         output_type,
1708                                         columns[i],
1709                                         id);
1710     }
1711     grn_output_table_record_close(ctx, outbuf, output_type);
1712   }
1713 }
1714 
1715 static inline void
grn_output_table_records_open(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,int n_records)1716 grn_output_table_records_open(grn_ctx *ctx,
1717                               grn_obj *outbuf,
1718                               grn_content_type output_type,
1719                               int n_records)
1720 {
1721   if (grn_ctx_get_command_version(ctx) >= GRN_COMMAND_VERSION_3) {
1722     grn_output_cstr(ctx, outbuf, output_type, "records");
1723     grn_output_array_open(ctx, outbuf, output_type, "records", n_records);
1724   }
1725 }
1726 
1727 static inline void
grn_output_table_records_close(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type)1728 grn_output_table_records_close(grn_ctx *ctx,
1729                                grn_obj *outbuf,
1730                                grn_content_type output_type)
1731 {
1732   if (grn_ctx_get_command_version(ctx) >= GRN_COMMAND_VERSION_3) {
1733     grn_output_array_close(ctx, outbuf, output_type);
1734   }
1735 }
1736 
1737 void
grn_output_table_records(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * table,grn_obj_format * format)1738 grn_output_table_records(grn_ctx *ctx, grn_obj *outbuf,
1739                          grn_content_type output_type,
1740                          grn_obj *table, grn_obj_format *format)
1741 {
1742   grn_table_cursor *tc;
1743 
1744   grn_output_table_records_open(ctx, outbuf, output_type, format->limit);
1745   tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
1746                              format->offset, format->limit,
1747                              GRN_CURSOR_ASCENDING);
1748   if (tc) {
1749     if (format->expression) {
1750       grn_output_table_records_by_expression(ctx, outbuf, output_type,
1751                                              tc, format);
1752     } else {
1753       grn_output_table_records_by_columns(ctx, outbuf, output_type,
1754                                           tc, format);
1755     }
1756     grn_table_cursor_close(ctx, tc);
1757   } else {
1758     ERRCLR(ctx);
1759   }
1760   grn_output_table_records_close(ctx, outbuf, output_type);
1761 }
1762 
1763 static void
grn_output_result_set_open_v1(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * table,grn_obj_format * format,uint32_t n_additional_elements)1764 grn_output_result_set_open_v1(grn_ctx *ctx,
1765                               grn_obj *outbuf,
1766                               grn_content_type output_type,
1767                               grn_obj *table,
1768                               grn_obj_format *format,
1769                               uint32_t n_additional_elements)
1770 {
1771   grn_obj buf;
1772   GRN_TEXT_INIT(&buf, 0);
1773   if (format) {
1774     int resultset_size = 1;
1775     /* resultset: [NHITS, (COLUMNS), (HITS)] */
1776     if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
1777       resultset_size++;
1778     }
1779     resultset_size += format->limit;
1780     resultset_size += n_additional_elements;
1781     grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", resultset_size);
1782     grn_output_result_set_n_hits(ctx, outbuf, output_type, format);
1783     if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
1784       grn_output_table_columns(ctx, outbuf, output_type, table, format);
1785     }
1786     grn_output_table_records(ctx, outbuf, output_type, table, format);
1787   } else {
1788     int i;
1789     grn_obj *column = grn_obj_column(ctx, table,
1790                                      GRN_COLUMN_NAME_KEY,
1791                                      GRN_COLUMN_NAME_KEY_LEN);
1792     grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
1793                                                  0, -1, GRN_CURSOR_ASCENDING);
1794     grn_output_array_open(ctx, outbuf, output_type, "HIT", -1);
1795     if (tc) {
1796       grn_id id;
1797       for (i = 0; (id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL; i++) {
1798         GRN_BULK_REWIND(&buf);
1799         grn_obj_get_value(ctx, column, id, &buf);
1800         grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf));
1801       }
1802       grn_table_cursor_close(ctx, tc);
1803     }
1804     grn_obj_unlink(ctx, column);
1805   }
1806   GRN_OBJ_FIN(ctx, &buf);
1807 }
1808 
1809 static void
grn_output_result_set_close_v1(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * table,grn_obj_format * format)1810 grn_output_result_set_close_v1(grn_ctx *ctx,
1811                                grn_obj *outbuf,
1812                                grn_content_type output_type,
1813                                grn_obj *table,
1814                                grn_obj_format *format)
1815 {
1816   grn_output_array_close(ctx, outbuf, output_type);
1817 }
1818 
1819 static void
grn_output_result_set_open_v3(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * result_set,grn_obj_format * format,uint32_t n_additional_elements)1820 grn_output_result_set_open_v3(grn_ctx *ctx,
1821                               grn_obj *outbuf,
1822                               grn_content_type output_type,
1823                               grn_obj *result_set,
1824                               grn_obj_format *format,
1825                               uint32_t n_additional_elements)
1826 {
1827   grn_obj buf;
1828   GRN_TEXT_INIT(&buf, 0);
1829   if (format) {
1830     int n_elements = 2;
1831     /* result_set: {"n_hits": N, ("columns": COLUMNS,) "records": records} */
1832     if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
1833       n_elements++;
1834     }
1835     n_elements += n_additional_elements;
1836     grn_output_map_open(ctx, outbuf, output_type, "result_set", n_elements);
1837     grn_output_result_set_n_hits(ctx, outbuf, output_type, format);
1838     if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
1839       grn_output_table_columns(ctx, outbuf, output_type, result_set, format);
1840     }
1841     grn_output_table_records(ctx, outbuf, output_type, result_set, format);
1842   } else {
1843     grn_obj *column;
1844     int n_records;
1845     int n_elements = 1;
1846 
1847     column = grn_obj_column(ctx,
1848                             result_set,
1849                             GRN_COLUMN_NAME_KEY,
1850                             GRN_COLUMN_NAME_KEY_LEN);
1851     n_elements += n_additional_elements;
1852     grn_output_map_open(ctx, outbuf, output_type, "result_set", n_elements);
1853     n_records = grn_table_size(ctx, result_set);
1854     grn_output_cstr(ctx, outbuf, output_type, "keys");
1855     grn_output_array_open(ctx, outbuf, output_type, "keys", n_records);
1856     GRN_TABLE_EACH_BEGIN(ctx, result_set, cursor, id) {
1857       GRN_BULK_REWIND(&buf);
1858       grn_obj_get_value(ctx, column, id, &buf);
1859       grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf));
1860     } GRN_TABLE_EACH_END(ctx, cursor);
1861     grn_output_array_close(ctx, outbuf, output_type);
1862     grn_obj_unlink(ctx, column);
1863   }
1864   GRN_OBJ_FIN(ctx, &buf);
1865 }
1866 
1867 static void
grn_output_result_set_close_v3(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * result_set,grn_obj_format * format)1868 grn_output_result_set_close_v3(grn_ctx *ctx,
1869                                grn_obj *outbuf,
1870                                grn_content_type output_type,
1871                                grn_obj *result_set,
1872                                grn_obj_format *format)
1873 {
1874   grn_output_map_close(ctx, outbuf, output_type);
1875 }
1876 
1877 void
grn_output_result_set_open(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * result_set,grn_obj_format * format,uint32_t n_additional_elements)1878 grn_output_result_set_open(grn_ctx *ctx,
1879                            grn_obj *outbuf,
1880                            grn_content_type output_type,
1881                            grn_obj *result_set,
1882                            grn_obj_format *format,
1883                            uint32_t n_additional_elements)
1884 {
1885   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1886     grn_output_result_set_open_v1(ctx,
1887                                   outbuf,
1888                                   output_type,
1889                                   result_set,
1890                                   format,
1891                                   n_additional_elements);
1892   } else {
1893     grn_output_result_set_open_v3(ctx,
1894                                   outbuf,
1895                                   output_type,
1896                                   result_set,
1897                                   format,
1898                                   n_additional_elements);
1899   }
1900 }
1901 
1902 void
grn_output_result_set_close(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * result_set,grn_obj_format * format)1903 grn_output_result_set_close(grn_ctx *ctx,
1904                             grn_obj *outbuf,
1905                             grn_content_type output_type,
1906                             grn_obj *result_set,
1907                             grn_obj_format *format)
1908 {
1909   if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) {
1910     grn_output_result_set_close_v1(ctx, outbuf, output_type, result_set, format);
1911   } else {
1912     grn_output_result_set_close_v3(ctx, outbuf, output_type, result_set, format);
1913   }
1914 }
1915 
1916 void
grn_output_result_set(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * result_set,grn_obj_format * format)1917 grn_output_result_set(grn_ctx *ctx,
1918                       grn_obj *outbuf,
1919                       grn_content_type output_type,
1920                       grn_obj *result_set,
1921                       grn_obj_format *format)
1922 {
1923   uint32_t n_additional_elements = 0;
1924 
1925   grn_output_result_set_open(ctx,
1926                              outbuf,
1927                              output_type,
1928                              result_set,
1929                              format,
1930                              n_additional_elements);
1931   grn_output_result_set_close(ctx, outbuf, output_type, result_set, format);
1932 }
1933 
1934 void
grn_output_obj(grn_ctx * ctx,grn_obj * outbuf,grn_content_type output_type,grn_obj * obj,grn_obj_format * format)1935 grn_output_obj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
1936                grn_obj *obj, grn_obj_format *format)
1937 {
1938   grn_obj buf;
1939   GRN_TEXT_INIT(&buf, 0);
1940   switch (obj->header.type) {
1941   case GRN_VOID :
1942     grn_output_void(ctx, outbuf, output_type, obj, format);
1943     break;
1944   case GRN_BULK :
1945     grn_output_bulk(ctx, outbuf, output_type, obj, format);
1946     break;
1947   case GRN_UVECTOR :
1948     grn_output_uvector(ctx, outbuf, output_type, obj, format);
1949     break;
1950   case GRN_VECTOR :
1951     grn_output_vector(ctx, outbuf, output_type, obj, format);
1952     break;
1953   case GRN_PVECTOR :
1954     grn_output_pvector(ctx, outbuf, output_type, obj, format);
1955     break;
1956   case GRN_TABLE_HASH_KEY :
1957   case GRN_TABLE_PAT_KEY :
1958   case GRN_TABLE_DAT_KEY :
1959   case GRN_TABLE_NO_KEY :
1960     /* Deprecated. Use grn_output_result_set() directly. */
1961     grn_output_result_set(ctx, outbuf, output_type, obj, format);
1962     break;
1963   }
1964   GRN_OBJ_FIN(ctx, &buf);
1965 }
1966 
1967 typedef enum {
1968   XML_START,
1969   XML_START_ELEMENT,
1970   XML_END_ELEMENT,
1971   XML_TEXT
1972 } xml_status;
1973 
1974 typedef enum {
1975   XML_PLACE_NONE,
1976   XML_PLACE_COLUMN,
1977   XML_PLACE_HIT
1978 } xml_place;
1979 
1980 static char *
transform_xml_next_column(grn_obj * columns,int n)1981 transform_xml_next_column(grn_obj *columns, int n)
1982 {
1983   char *column = GRN_TEXT_VALUE(columns);
1984   while (n--) {
1985     while (*column) {
1986       column++;
1987     }
1988     column++;
1989   }
1990   return column;
1991 }
1992 
1993 static void
transform_xml(grn_ctx * ctx,grn_obj * output,grn_obj * transformed)1994 transform_xml(grn_ctx *ctx, grn_obj *output, grn_obj *transformed)
1995 {
1996   char *s, *e;
1997   xml_status status = XML_START;
1998   xml_place place = XML_PLACE_NONE;
1999   grn_obj buf, name, columns, *expr;
2000   unsigned int len;
2001   int offset = 0, limit = 0, record_n = 0;
2002   int column_n = 0, column_text_n = 0, result_set_n = -1;
2003   grn_bool in_vector = GRN_FALSE;
2004   unsigned int vector_element_n = 0;
2005   grn_bool in_weight_vector = GRN_FALSE;
2006   unsigned int weight_vector_item_n = 0;
2007 
2008   s = GRN_TEXT_VALUE(output);
2009   e = GRN_BULK_CURR(output);
2010   GRN_TEXT_INIT(&buf, 0);
2011   GRN_TEXT_INIT(&name, 0);
2012   GRN_TEXT_INIT(&columns, 0);
2013 
2014   expr = ctx->impl->curr_expr;
2015 
2016 #define EQUAL_NAME_P(_name) \
2017   (GRN_TEXT_LEN(&name) == strlen(_name) && \
2018    !memcmp(GRN_TEXT_VALUE(&name), _name, strlen(_name)))
2019 
2020   while (s < e) {
2021     switch (*s) {
2022     case '<' :
2023       s++;
2024       switch (*s) {
2025       case '/' :
2026         status = XML_END_ELEMENT;
2027         s++;
2028         break;
2029       default :
2030         status = XML_START_ELEMENT;
2031         break;
2032       }
2033       GRN_BULK_REWIND(&name);
2034       break;
2035     case '>' :
2036       switch (status) {
2037       case XML_START_ELEMENT :
2038         if (EQUAL_NAME_P("COLUMN")) {
2039           place = XML_PLACE_COLUMN;
2040           column_text_n = 0;
2041         } else if (EQUAL_NAME_P("HIT")) {
2042           place = XML_PLACE_HIT;
2043           column_n = 0;
2044           if (result_set_n == 0) {
2045             GRN_TEXT_PUTS(ctx, transformed, "<HIT NO=\"");
2046             grn_text_itoa(ctx, transformed, record_n++);
2047             GRN_TEXT_PUTS(ctx, transformed, "\">\n");
2048           } else {
2049             GRN_TEXT_PUTS(ctx, transformed, "<NAVIGATIONELEMENT ");
2050           }
2051         } else if (EQUAL_NAME_P("RESULTSET")) {
2052           GRN_BULK_REWIND(&columns);
2053           result_set_n++;
2054           if (result_set_n == 0) {
2055           } else {
2056             GRN_TEXT_PUTS(ctx, transformed, "<NAVIGATIONENTRY>\n");
2057           }
2058         } else if (EQUAL_NAME_P("VECTOR")) {
2059           char *c = transform_xml_next_column(&columns, column_n++);
2060           in_vector = GRN_TRUE;
2061           vector_element_n = 0;
2062           GRN_TEXT_PUTS(ctx, transformed, "<FIELD NAME=\"");
2063           GRN_TEXT_PUTS(ctx, transformed, c);
2064           GRN_TEXT_PUTS(ctx, transformed, "\">");
2065         } else if (EQUAL_NAME_P("WEIGHT_VECTOR")) {
2066           char *c = transform_xml_next_column(&columns, column_n++);
2067           in_weight_vector = GRN_TRUE;
2068           weight_vector_item_n = 0;
2069           GRN_TEXT_PUTS(ctx, transformed, "<FIELD NAME=\"");
2070           GRN_TEXT_PUTS(ctx, transformed, c);
2071           GRN_TEXT_PUTS(ctx, transformed, "\">");
2072         }
2073         break;
2074       case XML_END_ELEMENT :
2075         if (EQUAL_NAME_P("HIT")) {
2076           place = XML_PLACE_NONE;
2077           if (result_set_n == 0) {
2078             GRN_TEXT_PUTS(ctx, transformed, "</HIT>\n");
2079           } else {
2080             GRN_TEXT_PUTS(ctx, transformed, "/>\n");
2081           }
2082         } else if (EQUAL_NAME_P("RESULTSET")) {
2083           place = XML_PLACE_NONE;
2084           if (result_set_n == 0) {
2085             GRN_TEXT_PUTS(ctx, transformed, "</RESULTSET>\n");
2086           } else {
2087             GRN_TEXT_PUTS(ctx, transformed,
2088                           "</NAVIGATIONELEMENTS>\n"
2089                           "</NAVIGATIONENTRY>\n");
2090           }
2091         } else if (EQUAL_NAME_P("RESULT")) {
2092           GRN_TEXT_PUTS(ctx, transformed,
2093                         "</RESULTPAGE>\n"
2094                         "</SEGMENT>\n"
2095                         "</SEGMENTS>\n");
2096         } else if (EQUAL_NAME_P("VECTOR")) {
2097           in_vector = GRN_FALSE;
2098           GRN_TEXT_PUTS(ctx, transformed, "</FIELD>\n");
2099         } else if (EQUAL_NAME_P("WEIGHT_VECTOR")) {
2100           in_weight_vector = GRN_FALSE;
2101           GRN_TEXT_PUTS(ctx, transformed, "</FIELD>\n");
2102         } else {
2103           switch (place) {
2104           case XML_PLACE_HIT :
2105             if (result_set_n == 0) {
2106               if (in_vector) {
2107                 if (vector_element_n > 0) {
2108                   GRN_TEXT_PUTS(ctx, transformed, ", ");
2109                 }
2110                 GRN_TEXT_PUT(ctx, transformed,
2111                              GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
2112                 vector_element_n++;
2113               } else if (in_weight_vector) {
2114                 grn_bool is_key;
2115                 is_key = ((weight_vector_item_n % 2) == 0);
2116                 if (is_key) {
2117                   unsigned int weight_vector_key_n;
2118                   weight_vector_key_n = weight_vector_item_n / 2;
2119                   if (weight_vector_key_n > 0) {
2120                     GRN_TEXT_PUTS(ctx, transformed, ", ");
2121                   }
2122                 } else {
2123                   GRN_TEXT_PUTS(ctx, transformed, ":");
2124                 }
2125                 GRN_TEXT_PUT(ctx, transformed,
2126                              GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
2127                 weight_vector_item_n++;
2128               } else {
2129                 char *c = transform_xml_next_column(&columns, column_n++);
2130                 GRN_TEXT_PUTS(ctx, transformed, "<FIELD NAME=\"");
2131                 GRN_TEXT_PUTS(ctx, transformed, c);
2132                 GRN_TEXT_PUTS(ctx, transformed, "\">");
2133                 GRN_TEXT_PUT(ctx, transformed,
2134                              GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
2135                 GRN_TEXT_PUTS(ctx, transformed, "</FIELD>\n");
2136               }
2137             } else {
2138               char *c = transform_xml_next_column(&columns, column_n++);
2139               GRN_TEXT_PUTS(ctx, transformed, c);
2140               GRN_TEXT_PUTS(ctx, transformed, "=\"");
2141               GRN_TEXT_PUT(ctx, transformed,
2142                            GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
2143               GRN_TEXT_PUTS(ctx, transformed, "\" ");
2144             }
2145             break;
2146           default :
2147             if (EQUAL_NAME_P("NHITS")) {
2148               if (result_set_n == 0) {
2149                 uint32_t nhits;
2150                 grn_obj *offset_value, *limit_value;
2151 
2152                 nhits = grn_atoui(GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf),
2153                                   NULL);
2154                 offset_value = grn_expr_get_var(ctx, expr,
2155                                                 "offset", strlen("offset"));
2156                 limit_value = grn_expr_get_var(ctx, expr,
2157                                                "limit", strlen("limit"));
2158                 if (GRN_TEXT_LEN(offset_value)) {
2159                   offset = grn_atoi(GRN_TEXT_VALUE(offset_value),
2160                                     GRN_BULK_CURR(offset_value),
2161                                     NULL);
2162                 } else {
2163                   offset = 0;
2164                 }
2165                 if (GRN_TEXT_LEN(limit_value)) {
2166                   limit = grn_atoi(GRN_TEXT_VALUE(limit_value),
2167                                    GRN_BULK_CURR(limit_value),
2168                                    NULL);
2169                 } else {
2170 #define DEFAULT_LIMIT 10
2171                   limit = DEFAULT_LIMIT;
2172 #undef DEFAULT_LIMIT
2173                 }
2174                 grn_normalize_offset_and_limit(ctx, nhits, &offset, &limit);
2175                 record_n = offset + 1;
2176                 GRN_TEXT_PUTS(ctx, transformed,
2177                               "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
2178                               "<SEGMENTS>\n"
2179                               "<SEGMENT>\n"
2180                               "<RESULTPAGE>\n"
2181                               "<RESULTSET OFFSET=\"");
2182                 grn_text_lltoa(ctx, transformed, offset);
2183                 GRN_TEXT_PUTS(ctx, transformed, "\" LIMIT=\"");
2184                 grn_text_lltoa(ctx, transformed, limit);
2185                 GRN_TEXT_PUTS(ctx, transformed, "\" NHITS=\"");
2186                 grn_text_lltoa(ctx, transformed, nhits);
2187                 GRN_TEXT_PUTS(ctx, transformed, "\">\n");
2188               } else {
2189                 GRN_TEXT_PUTS(ctx, transformed,
2190                               "<NAVIGATIONELEMENTS COUNT=\"");
2191                 GRN_TEXT_PUT(ctx, transformed,
2192                              GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
2193                 GRN_TEXT_PUTS(ctx, transformed,
2194                               "\">\n");
2195               }
2196             } else if (EQUAL_NAME_P("TEXT")) {
2197               switch (place) {
2198               case XML_PLACE_COLUMN :
2199                 if (column_text_n == 0) {
2200                   GRN_TEXT_PUT(ctx, &columns,
2201                                GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
2202                   GRN_TEXT_PUTC(ctx, &columns, '\0');
2203                 }
2204                 column_text_n++;
2205                 break;
2206               default :
2207                 break;
2208               }
2209             }
2210           }
2211         }
2212       default :
2213         break;
2214       }
2215       s++;
2216       GRN_BULK_REWIND(&buf);
2217       status = XML_TEXT;
2218       break;
2219     default :
2220       len = grn_charlen(ctx, s, e);
2221       switch (status) {
2222       case XML_START_ELEMENT :
2223       case XML_END_ELEMENT :
2224         GRN_TEXT_PUT(ctx, &name, s, len);
2225         break;
2226       default :
2227         GRN_TEXT_PUT(ctx, &buf, s, len);
2228         break;
2229       }
2230       s += len;
2231       break;
2232     }
2233   }
2234 #undef EQUAL_NAME_P
2235 
2236   GRN_OBJ_FIN(ctx, &buf);
2237   GRN_OBJ_FIN(ctx, &name);
2238   GRN_OBJ_FIN(ctx, &columns);
2239 }
2240 
2241 #ifdef GRN_WITH_MESSAGE_PACK
2242 typedef struct {
2243   grn_ctx *ctx;
2244   grn_obj *buffer;
2245 } msgpack_writer_ctx;
2246 
2247 static inline int
msgpack_buffer_writer(void * data,const char * buf,msgpack_size_t len)2248 msgpack_buffer_writer(void* data, const char* buf, msgpack_size_t len)
2249 {
2250   msgpack_writer_ctx *writer_ctx = (msgpack_writer_ctx *)data;
2251   return grn_bulk_write(writer_ctx->ctx, writer_ctx->buffer, buf, len);
2252 }
2253 #endif
2254 
2255 #define JSON_CALLBACK_PARAM "callback"
2256 
2257 static void
grn_output_envelope_json_v1(grn_ctx * ctx,grn_rc rc,grn_obj * head,grn_obj * body,grn_obj * foot,double started,double elapsed,const char * file,int line)2258 grn_output_envelope_json_v1(grn_ctx *ctx,
2259                             grn_rc rc,
2260                             grn_obj *head,
2261                             grn_obj *body,
2262                             grn_obj *foot,
2263                             double started,
2264                             double elapsed,
2265                             const char *file,
2266                             int line)
2267 {
2268   size_t indent_level = 0;
2269 
2270   json_array_open(ctx, head, &indent_level);
2271   {
2272     json_array_open(ctx, head, &indent_level);
2273     {
2274       grn_text_itoa(ctx, head, rc);
2275 
2276       json_element_end(ctx, head, indent_level);
2277       grn_text_ftoa(ctx, head, started);
2278 
2279       json_element_end(ctx, head, indent_level);
2280       grn_text_ftoa(ctx, head, elapsed);
2281 
2282       if (rc != GRN_SUCCESS) {
2283         json_element_end(ctx, head, indent_level);
2284         grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
2285 
2286         if (ctx->errfunc && ctx->errfile) {
2287           grn_obj *command;
2288 
2289           json_element_end(ctx, head, indent_level);
2290           json_array_open(ctx, head, &indent_level);
2291           {
2292             json_array_open(ctx, head, &indent_level);
2293             {
2294               grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
2295 
2296               json_element_end(ctx, head, indent_level);
2297               grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile));
2298 
2299               json_element_end(ctx, head, indent_level);
2300               grn_text_itoa(ctx, head, ctx->errline);
2301             }
2302             json_array_close(ctx, head, &indent_level);
2303 
2304             if (file && (command = GRN_CTX_USER_DATA(ctx)->ptr)) {
2305               json_element_end(ctx, head, indent_level);
2306               json_array_open(ctx, head, &indent_level);
2307               {
2308                 grn_text_esc(ctx, head, file, strlen(file));
2309 
2310                 json_element_end(ctx, head, indent_level);
2311                 grn_text_itoa(ctx, head, line);
2312 
2313                 json_element_end(ctx, head, indent_level);
2314                 grn_text_esc(ctx, head,
2315                              GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command));
2316               }
2317               json_array_close(ctx, head, &indent_level);
2318             }
2319           }
2320           json_array_close(ctx, head, &indent_level);
2321         }
2322       }
2323     }
2324     json_array_close(ctx, head, &indent_level);
2325   }
2326 
2327   if (GRN_TEXT_LEN(body)) {
2328     json_element_end(ctx, head, indent_level);
2329   }
2330 
2331   json_array_close(ctx, foot, &indent_level);
2332 }
2333 
2334 static void
grn_output_envelope_json(grn_ctx * ctx,grn_rc rc,grn_obj * head,grn_obj * body,grn_obj * foot,double started,double elapsed,const char * file,int line)2335 grn_output_envelope_json(grn_ctx *ctx,
2336                          grn_rc rc,
2337                          grn_obj *head,
2338                          grn_obj *body,
2339                          grn_obj *foot,
2340                          double started,
2341                          double elapsed,
2342                          const char *file,
2343                          int line)
2344 {
2345   size_t indent_level = 0;
2346 
2347   json_map_open(ctx, head, &indent_level);
2348   {
2349     json_key(ctx, head, "header");
2350     json_map_open(ctx, head, &indent_level);
2351     {
2352       json_key(ctx, head, "return_code");
2353       grn_text_itoa(ctx, head, rc);
2354 
2355       json_value_end(ctx, head, indent_level);
2356       json_key(ctx, head, "start_time");
2357       grn_text_ftoa(ctx, head, started);
2358 
2359       json_value_end(ctx, head, indent_level);
2360       json_key(ctx, head, "elapsed_time");
2361       grn_text_ftoa(ctx, head, elapsed);
2362 
2363       if (rc != GRN_SUCCESS) {
2364         json_value_end(ctx, head, indent_level);
2365         json_key(ctx, head, "error");
2366         json_map_open(ctx, head, &indent_level);
2367         {
2368           json_key(ctx, head, "message");
2369           grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
2370 
2371           if (ctx->errfunc && ctx->errfile) {
2372             json_value_end(ctx, head, indent_level);
2373             json_key(ctx, head, "function");
2374             grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
2375 
2376             json_value_end(ctx, head, indent_level);
2377             json_key(ctx, head, "file");
2378             grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile));
2379 
2380             json_value_end(ctx, head, indent_level);
2381             json_key(ctx, head, "line");
2382             grn_text_itoa(ctx, head, ctx->errline);
2383           }
2384 
2385           if (file) {
2386             grn_obj *command;
2387 
2388             command = GRN_CTX_USER_DATA(ctx)->ptr;
2389             if (command) {
2390               json_value_end(ctx, head, indent_level);
2391               json_key(ctx, head, "input");
2392               json_map_open(ctx, head, &indent_level);
2393               {
2394                 json_key(ctx, head, "file");
2395                 grn_text_esc(ctx, head, file, strlen(file));
2396 
2397                 json_value_end(ctx, head, indent_level);
2398                 json_key(ctx, head, "line");
2399                 grn_text_itoa(ctx, head, line);
2400 
2401                 json_value_end(ctx, head, indent_level);
2402                 json_key(ctx, head, "command");
2403                 grn_text_esc(ctx, head,
2404                              GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command));
2405               }
2406               json_map_close(ctx, head, &indent_level);
2407             }
2408           }
2409         }
2410         json_map_close(ctx, head, &indent_level);
2411       }
2412     }
2413     json_map_close(ctx, head, &indent_level);
2414 
2415     if (GRN_TEXT_LEN(body)) {
2416       json_value_end(ctx, head, indent_level);
2417       json_key(ctx, head, "body");
2418     }
2419 
2420     json_map_close(ctx, foot, &indent_level);
2421   }
2422 }
2423 
2424 #ifdef GRN_WITH_MESSAGE_PACK
2425 static void
msgpack_pack_cstr(msgpack_packer * packer,const char * string)2426 msgpack_pack_cstr(msgpack_packer *packer,
2427                   const char *string)
2428 {
2429   size_t size;
2430 
2431   size = strlen(string);
2432   msgpack_pack_str(packer, size);
2433   msgpack_pack_str_body(packer, string, size);
2434 }
2435 
2436 static void
grn_output_envelope_msgpack_v1(grn_ctx * ctx,grn_rc rc,grn_obj * head,grn_obj * body,grn_obj * foot,double started,double elapsed,const char * file,int line)2437 grn_output_envelope_msgpack_v1(grn_ctx *ctx,
2438                                grn_rc rc,
2439                                grn_obj *head,
2440                                grn_obj *body,
2441                                grn_obj *foot,
2442                                double started,
2443                                double elapsed,
2444                                const char *file,
2445                                int line)
2446 {
2447   msgpack_writer_ctx head_writer_ctx;
2448   msgpack_packer header_packer;
2449   int header_size;
2450 
2451   head_writer_ctx.ctx    = ctx;
2452   head_writer_ctx.buffer = head;
2453   msgpack_packer_init(&header_packer, &head_writer_ctx, msgpack_buffer_writer);
2454 
2455   /* [HEADER, (BODY)] */
2456   if (GRN_TEXT_LEN(body) > 0) {
2457     msgpack_pack_array(&header_packer, 2);
2458   } else {
2459     msgpack_pack_array(&header_packer, 1);
2460   }
2461 
2462   /* HEADER := [rc, started, elapsed, (error, (ERROR DETAIL))] */
2463   header_size = 3;
2464   if (rc != GRN_SUCCESS) {
2465     header_size++;
2466     if (ctx->errfunc && ctx->errfile) {
2467       header_size++;
2468     }
2469   }
2470   msgpack_pack_array(&header_packer, header_size);
2471   msgpack_pack_int(&header_packer, rc);
2472 
2473   msgpack_pack_double(&header_packer, started);
2474   msgpack_pack_double(&header_packer, elapsed);
2475 
2476   if (rc != GRN_SUCCESS) {
2477     msgpack_pack_str(&header_packer, strlen(ctx->errbuf));
2478     msgpack_pack_str_body(&header_packer, ctx->errbuf, strlen(ctx->errbuf));
2479     if (ctx->errfunc && ctx->errfile) {
2480       grn_obj *command = GRN_CTX_USER_DATA(ctx)->ptr;
2481       int      error_detail_size;
2482 
2483       /* ERROR DETAIL :    = [[errfunc, errfile, errline,
2484          (file, line, command)]] */
2485       /* TODO: output backtrace */
2486       msgpack_pack_array(&header_packer, 1);
2487       error_detail_size    = 3;
2488       if (command) {
2489         error_detail_size += 3;
2490       }
2491       msgpack_pack_array(&header_packer, error_detail_size);
2492 
2493       msgpack_pack_str(&header_packer, strlen(ctx->errfunc));
2494       msgpack_pack_str_body(&header_packer, ctx->errfunc, strlen(ctx->errfunc));
2495 
2496       msgpack_pack_str(&header_packer, strlen(ctx->errfile));
2497       msgpack_pack_str_body(&header_packer, ctx->errfile, strlen(ctx->errfile));
2498 
2499       msgpack_pack_int(&header_packer, ctx->errline);
2500 
2501       if (command) {
2502         if (file) {
2503           msgpack_pack_str(&header_packer, strlen(file));
2504           msgpack_pack_str_body(&header_packer, file, strlen(file));
2505         } else {
2506           msgpack_pack_str(&header_packer, 7);
2507           msgpack_pack_str_body(&header_packer, "(stdin)", 7);
2508         }
2509 
2510         msgpack_pack_int(&header_packer, line);
2511 
2512         msgpack_pack_str(&header_packer, GRN_TEXT_LEN(command));
2513         msgpack_pack_str_body(&header_packer, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command));
2514       }
2515     }
2516   }
2517 }
2518 
2519 static void
grn_output_envelope_msgpack(grn_ctx * ctx,grn_rc rc,grn_obj * head,grn_obj * body,grn_obj * foot,double started,double elapsed,const char * file,int line)2520 grn_output_envelope_msgpack(grn_ctx    *ctx,
2521                             grn_rc      rc,
2522                             grn_obj    *head,
2523                             grn_obj    *body,
2524                             grn_obj    *foot,
2525                             double      started,
2526                             double      elapsed,
2527                             const char *file,
2528                             int         line)
2529 {
2530   msgpack_writer_ctx writer_ctx;
2531   msgpack_packer packer;
2532   int n_elements;
2533 
2534   writer_ctx.ctx = ctx;
2535   writer_ctx.buffer = head;
2536   msgpack_packer_init(&packer, &writer_ctx, msgpack_buffer_writer);
2537 
2538   /*
2539    * ENVELOPE := {
2540    *   "header": HEADER,
2541    *   "body":   BODY    (optional)
2542    * }
2543    */
2544   if (GRN_TEXT_LEN(body) > 0) {
2545     n_elements = 2;
2546   } else {
2547     n_elements = 1;
2548   }
2549 
2550   msgpack_pack_map(&packer, n_elements);
2551   {
2552     int n_header_elements = 3;
2553 
2554     /*
2555      * HEADER := {
2556      *   "return_code":  rc,
2557      *   "start_time":   started,
2558      *   "elapsed_time": elapsed,
2559      "   "error": {                   (optional)
2560      *      "message":  errbuf,
2561      *      "function": errfunc,
2562      *      "file":     errfile,
2563      *      "line":     errline,
2564      *      "input": {                (optional)
2565      *        "file":    input_file,
2566      *        "line":    line,
2567      *        "command": command
2568      *      }
2569      *   }
2570      * }
2571      */
2572 
2573     if (rc != GRN_SUCCESS) {
2574       n_header_elements++;
2575     }
2576 
2577     msgpack_pack_cstr(&packer, "header");
2578     msgpack_pack_map(&packer, n_header_elements);
2579     {
2580       msgpack_pack_cstr(&packer, "return_code");
2581       msgpack_pack_int(&packer, rc);
2582 
2583       msgpack_pack_cstr(&packer, "start_time");
2584       msgpack_pack_double(&packer, started);
2585 
2586       msgpack_pack_cstr(&packer, "elapsed_time");
2587       msgpack_pack_double(&packer, elapsed);
2588 
2589       if (rc != GRN_SUCCESS) {
2590         int n_error_elements = 1;
2591         grn_obj *command;
2592 
2593         if (ctx->errfunc) {
2594           n_error_elements++;
2595         }
2596         if (ctx->errfile) {
2597           n_error_elements += 2;
2598         }
2599 
2600         command = GRN_CTX_USER_DATA(ctx)->ptr;
2601         if (file || command) {
2602           n_error_elements++;
2603         }
2604 
2605         msgpack_pack_cstr(&packer, "error");
2606         msgpack_pack_map(&packer, n_error_elements);
2607         {
2608           msgpack_pack_cstr(&packer, "message");
2609           msgpack_pack_cstr(&packer, ctx->errbuf);
2610 
2611           if (ctx->errfunc) {
2612             msgpack_pack_cstr(&packer, "function");
2613             msgpack_pack_cstr(&packer, ctx->errfunc);
2614           }
2615 
2616           if (ctx->errfile) {
2617             msgpack_pack_cstr(&packer, "file");
2618             msgpack_pack_cstr(&packer, ctx->errfile);
2619 
2620             msgpack_pack_cstr(&packer, "line");
2621             msgpack_pack_int(&packer, ctx->errline);
2622           }
2623 
2624           if (file || command) {
2625             int n_input_elements = 0;
2626 
2627             if (file) {
2628               n_input_elements += 2;
2629             }
2630             if (command) {
2631               n_input_elements++;
2632             }
2633 
2634             msgpack_pack_cstr(&packer, "input");
2635             msgpack_pack_map(&packer, n_input_elements);
2636 
2637             if (file) {
2638               msgpack_pack_cstr(&packer, "file");
2639               msgpack_pack_cstr(&packer, file);
2640 
2641               msgpack_pack_cstr(&packer, "line");
2642               msgpack_pack_int(&packer, line);
2643             }
2644 
2645             if (command) {
2646               msgpack_pack_cstr(&packer, "command");
2647               msgpack_pack_str(&packer, GRN_TEXT_LEN(command));
2648               msgpack_pack_str_body(&packer,
2649                                     GRN_TEXT_VALUE(command),
2650                                     GRN_TEXT_LEN(command));
2651             }
2652           }
2653         }
2654       }
2655     }
2656 
2657     if (GRN_TEXT_LEN(body) > 0) {
2658       msgpack_pack_cstr(&packer, "body");
2659     }
2660   }
2661 }
2662 #endif /* GRN_WITH_MESSAGE_PACK */
2663 
2664 void
grn_output_envelope(grn_ctx * ctx,grn_rc rc,grn_obj * head,grn_obj * body,grn_obj * foot,const char * file,int line)2665 grn_output_envelope(grn_ctx *ctx,
2666                     grn_rc rc,
2667                     grn_obj *head,
2668                     grn_obj *body,
2669                     grn_obj *foot,
2670                     const char *file,
2671                     int line)
2672 {
2673   double started, finished, elapsed;
2674 
2675   grn_timeval tv_now;
2676   grn_timeval_now(ctx, &tv_now);
2677   started = ctx->impl->tv.tv_sec;
2678   started += ctx->impl->tv.tv_nsec / GRN_TIME_NSEC_PER_SEC_F;
2679   finished = tv_now.tv_sec;
2680   finished += tv_now.tv_nsec / GRN_TIME_NSEC_PER_SEC_F;
2681   elapsed = finished - started;
2682 
2683   switch (ctx->impl->output.type) {
2684   case GRN_CONTENT_JSON:
2685     {
2686       grn_obj *expr;
2687       grn_obj *jsonp_func = NULL;
2688 
2689       expr = ctx->impl->curr_expr;
2690       if (expr) {
2691         jsonp_func = grn_expr_get_var(ctx, expr, JSON_CALLBACK_PARAM,
2692                                       strlen(JSON_CALLBACK_PARAM));
2693       }
2694       if (jsonp_func && GRN_TEXT_LEN(jsonp_func)) {
2695         GRN_TEXT_PUT(ctx, head,
2696                      GRN_TEXT_VALUE(jsonp_func), GRN_TEXT_LEN(jsonp_func));
2697         GRN_TEXT_PUTC(ctx, head, '(');
2698       }
2699 
2700       if (grn_ctx_get_command_version(ctx) <= GRN_COMMAND_VERSION_2) {
2701         grn_output_envelope_json_v1(ctx, rc,
2702                                     head, body, foot,
2703                                     started, elapsed,
2704                                     file, line);
2705       } else {
2706         grn_output_envelope_json(ctx, rc,
2707                                  head, body, foot,
2708                                  started, elapsed,
2709                                  file, line);
2710       }
2711 
2712       if (jsonp_func && GRN_TEXT_LEN(jsonp_func)) {
2713         GRN_TEXT_PUTS(ctx, foot, ");");
2714       }
2715     }
2716     break;
2717   case GRN_CONTENT_TSV:
2718     grn_text_itoa(ctx, head, rc);
2719     GRN_TEXT_PUTC(ctx, head, '\t');
2720     grn_text_ftoa(ctx, head, started);
2721     GRN_TEXT_PUTC(ctx, head, '\t');
2722     grn_text_ftoa(ctx, head, elapsed);
2723     if (rc != GRN_SUCCESS) {
2724       GRN_TEXT_PUTC(ctx, head, '\t');
2725       grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
2726       if (ctx->errfunc && ctx->errfile) {
2727         /* TODO: output backtrace */
2728         GRN_TEXT_PUTC(ctx, head, '\t');
2729         grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
2730         GRN_TEXT_PUTC(ctx, head, '\t');
2731         grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile));
2732         GRN_TEXT_PUTC(ctx, head, '\t');
2733         grn_text_itoa(ctx, head, ctx->errline);
2734       }
2735     }
2736     GRN_TEXT_PUTS(ctx, head, "\n");
2737     GRN_TEXT_PUTS(ctx, foot, "\nEND");
2738     break;
2739   case GRN_CONTENT_XML:
2740     {
2741       char buf[GRN_TABLE_MAX_KEY_SIZE];
2742       int is_select = 0;
2743       if (!rc && ctx->impl->curr_expr) {
2744         int len = grn_obj_name(ctx, ctx->impl->curr_expr,
2745                                buf, GRN_TABLE_MAX_KEY_SIZE);
2746         buf[len] = '\0';
2747         is_select = strcmp(buf, "select") == 0;
2748       }
2749       if (is_select) {
2750         grn_obj transformed;
2751         GRN_TEXT_INIT(&transformed, 0);
2752         transform_xml(ctx, body, &transformed);
2753         GRN_TEXT_SET(ctx, body,
2754                      GRN_TEXT_VALUE(&transformed), GRN_TEXT_LEN(&transformed));
2755         GRN_OBJ_FIN(ctx, &transformed);
2756       } else {
2757         GRN_TEXT_PUTS(ctx, head, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RESULT CODE=\"");
2758         grn_text_itoa(ctx, head, rc);
2759         GRN_TEXT_PUTS(ctx, head, "\" UP=\"");
2760         grn_text_ftoa(ctx, head, started);
2761         GRN_TEXT_PUTS(ctx, head, "\" ELAPSED=\"");
2762         grn_text_ftoa(ctx, head, elapsed);
2763         GRN_TEXT_PUTS(ctx, head, "\">\n");
2764         if (rc != GRN_SUCCESS) {
2765           GRN_TEXT_PUTS(ctx, head, "<ERROR>");
2766           grn_text_escape_xml(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
2767           if (ctx->errfunc && ctx->errfile) {
2768             /* TODO: output backtrace */
2769             GRN_TEXT_PUTS(ctx, head, "<INFO FUNC=\"");
2770             grn_text_escape_xml(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
2771             GRN_TEXT_PUTS(ctx, head, "\" FILE=\"");
2772             grn_text_escape_xml(ctx, head, ctx->errfile, strlen(ctx->errfile));
2773             GRN_TEXT_PUTS(ctx, head, "\" LINE=\"");
2774             grn_text_itoa(ctx, head, ctx->errline);
2775             GRN_TEXT_PUTS(ctx, head, "\"/>");
2776           }
2777           GRN_TEXT_PUTS(ctx, head, "</ERROR>");
2778         }
2779         GRN_TEXT_PUTS(ctx, foot, "\n</RESULT>");
2780       }
2781     }
2782     break;
2783   case GRN_CONTENT_MSGPACK:
2784 #ifdef GRN_WITH_MESSAGE_PACK
2785     if (grn_ctx_get_command_version(ctx) <= GRN_COMMAND_VERSION_2) {
2786       grn_output_envelope_msgpack_v1(ctx, rc,
2787                                      head, body, foot,
2788                                      started, elapsed,
2789                                      file, line);
2790     } else {
2791       grn_output_envelope_msgpack(ctx, rc,
2792                                   head, body, foot,
2793                                   started, elapsed,
2794                                   file, line);
2795     }
2796 #endif /* GRN_WITH_MESSAGE_PACK */
2797     break;
2798   case GRN_CONTENT_GROONGA_COMMAND_LIST :
2799     break;
2800   case GRN_CONTENT_NONE:
2801     break;
2802   }
2803 }
2804 
2805 static inline grn_bool
is_output_columns_format_v1(grn_ctx * ctx,const char * output_columns,unsigned int output_columns_len)2806 is_output_columns_format_v1(grn_ctx *ctx,
2807                             const char *output_columns,
2808                             unsigned int output_columns_len)
2809 {
2810   const char *current;
2811   const char *end;
2812   grn_bool in_identifier = GRN_FALSE;
2813 
2814   current = output_columns;
2815   end = current + output_columns_len;
2816   while (current < end) {
2817     int char_length;
2818 
2819     char_length = grn_charlen(ctx, current, end);
2820     if (char_length != 1) {
2821       return GRN_FALSE;
2822     }
2823 
2824     switch (current[0]) {
2825     case ' ' :
2826     case ',' :
2827       in_identifier = GRN_FALSE;
2828       break;
2829     case '_' :
2830       in_identifier = GRN_TRUE;
2831       break;
2832     case '.' :
2833     case '-' :
2834     case '#' :
2835     case '@' :
2836       if (!in_identifier) {
2837         return GRN_FALSE;
2838       }
2839       break;
2840     default :
2841       if ('a' <= current[0] && current[0] <= 'z') {
2842         in_identifier = GRN_TRUE;
2843         break;
2844       } else if ('A' <= current[0] && current[0] <= 'Z') {
2845         in_identifier = GRN_TRUE;
2846         break;
2847       } else if ('0' <= current[0] && current[0] <= '9') {
2848         in_identifier = GRN_TRUE;
2849         break;
2850       } else {
2851         return GRN_FALSE;
2852       }
2853     }
2854 
2855     current += char_length;
2856   }
2857 
2858   return GRN_TRUE;
2859 }
2860 
2861 grn_rc
grn_output_format_set_columns(grn_ctx * ctx,grn_obj_format * format,grn_obj * table,const char * columns,int columns_len)2862 grn_output_format_set_columns(grn_ctx *ctx, grn_obj_format *format,
2863                               grn_obj *table,
2864                               const char *columns, int columns_len)
2865 {
2866   grn_rc rc;
2867 
2868   if (is_output_columns_format_v1(ctx, columns, columns_len)) {
2869     rc = grn_obj_columns(ctx, table, columns, columns_len, &(format->columns));
2870   } else {
2871     grn_obj *variable;
2872     GRN_EXPR_CREATE_FOR_QUERY(ctx, table, format->expression, variable);
2873     rc = grn_expr_parse(ctx, format->expression,
2874                         columns, columns_len, NULL,
2875                         GRN_OP_MATCH, GRN_OP_AND,
2876                         GRN_EXPR_SYNTAX_OUTPUT_COLUMNS);
2877   }
2878 
2879   return rc;
2880 }
2881