mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 19:06:14 +01:00 
			
		
		
		
	 dbac2039e8
			
		
	
	
	dbac2039e8
	
	
	
		
			
			- pcretest.c could use macro with side effect - maria_chk could access freed memory - Initialized some variables that could be accessed uninitalized - Fixed compiler warning in my_atomic-t.c
		
			
				
	
	
		
			906 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			906 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- c-basic-offset: 2 -*- */
 | |
| /*
 | |
|   Copyright(C) 2015 Brazil
 | |
| 
 | |
|   This library is free software; you can redistribute it and/or
 | |
|   modify it under the terms of the GNU Lesser General Public
 | |
|   License version 2.1 as published by the Free Software Foundation.
 | |
| 
 | |
|   This library is distributed in the hope that it will be useful,
 | |
|   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|   Lesser General Public License for more details.
 | |
| 
 | |
|   You should have received a copy of the GNU Lesser General Public
 | |
|   License along with this library; if not, write to the Free Software
 | |
|   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
 | |
| */
 | |
| 
 | |
| /* TS is an acronym for "Turbo Selector". */
 | |
| 
 | |
| #include "grn_ts.h"
 | |
| 
 | |
| #include "grn_output.h"
 | |
| #include "grn_str.h"
 | |
| 
 | |
| #include "ts/ts_buf.h"
 | |
| #include "ts/ts_cursor.h"
 | |
| #include "ts/ts_expr.h"
 | |
| #include "ts/ts_expr_parser.h"
 | |
| #include "ts/ts_log.h"
 | |
| #include "ts/ts_sorter.h"
 | |
| #include "ts/ts_str.h"
 | |
| #include "ts/ts_types.h"
 | |
| #include "ts/ts_util.h"
 | |
| 
 | |
| #include <string.h>
 | |
| 
 | |
| /*-------------------------------------------------------------
 | |
|  * Miscellaneous.
 | |
|  */
 | |
| 
 | |
| enum { GRN_TS_BATCH_SIZE = 1024 };
 | |
| 
 | |
| /* grn_ts_bool_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_bool_output(grn_ctx *ctx, grn_ts_bool value)
 | |
| {
 | |
|   if (value) {
 | |
|     return grn_bulk_write(ctx, ctx->impl->output.buf, "true", 4);
 | |
|   } else {
 | |
|     return grn_bulk_write(ctx, ctx->impl->output.buf, "false", 5);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* grn_ts_int_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_int_output(grn_ctx *ctx, grn_ts_int value)
 | |
| {
 | |
|   return grn_text_lltoa(ctx, ctx->impl->output.buf, value);
 | |
| }
 | |
| 
 | |
| /* grn_ts_float_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_float_output(grn_ctx *ctx, grn_ts_float value)
 | |
| {
 | |
|   return grn_text_ftoa(ctx, ctx->impl->output.buf, value);
 | |
| }
 | |
| 
 | |
| /* grn_ts_time_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_time_output(grn_ctx *ctx, grn_ts_time value)
 | |
| {
 | |
|   return grn_text_ftoa(ctx, ctx->impl->output.buf, value * 0.000001);
 | |
| }
 | |
| 
 | |
| /* grn_ts_text_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_text_output(grn_ctx *ctx, grn_ts_text value)
 | |
| {
 | |
|   return grn_text_esc(ctx, ctx->impl->output.buf, value.ptr, value.size);
 | |
| }
 | |
| 
 | |
| /* grn_ts_geo_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_geo_output(grn_ctx *ctx, grn_ts_geo value)
 | |
| {
 | |
|   grn_rc rc = grn_bulk_write(ctx, ctx->impl->output.buf, "\"", 1);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   rc = grn_text_itoa(ctx, ctx->impl->output.buf, value.latitude);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   rc = grn_bulk_write(ctx, ctx->impl->output.buf, "x", 1);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   rc = grn_text_itoa(ctx, ctx->impl->output.buf, value.longitude);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   return grn_bulk_write(ctx, ctx->impl->output.buf, "\"", 1);
 | |
| }
 | |
| 
 | |
| #define GRN_TS_VECTOR_OUTPUT(kind)\
 | |
|   size_t i;\
 | |
|   grn_rc rc = grn_bulk_write(ctx, ctx->impl->output.buf, "[", 1);\
 | |
|   if (rc != GRN_SUCCESS) {\
 | |
|     return rc;\
 | |
|   }\
 | |
|   for (i = 0; i < value.size; ++i) {\
 | |
|     if (i) {\
 | |
|       rc = grn_bulk_write(ctx, ctx->impl->output.buf, ",", 1);\
 | |
|       if (rc != GRN_SUCCESS) {\
 | |
|         return rc;\
 | |
|       }\
 | |
|     }\
 | |
|     rc = grn_ts_ ## kind ## _output(ctx, value.ptr[i]);\
 | |
|     if (rc != GRN_SUCCESS) {\
 | |
|       return rc;\
 | |
|     }\
 | |
|   }\
 | |
|   return grn_bulk_write(ctx, ctx->impl->output.buf, "]", 1);
 | |
| /* grn_ts_bool_vector_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_bool_vector_output(grn_ctx *ctx, grn_ts_bool_vector value)
 | |
| {
 | |
|   GRN_TS_VECTOR_OUTPUT(bool)
 | |
| }
 | |
| 
 | |
| /* grn_ts_int_vector_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_int_vector_output(grn_ctx *ctx, grn_ts_int_vector value)
 | |
| {
 | |
|   GRN_TS_VECTOR_OUTPUT(int)
 | |
| }
 | |
| 
 | |
| /* grn_ts_float_vector_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_float_vector_output(grn_ctx *ctx, grn_ts_float_vector value)
 | |
| {
 | |
|   GRN_TS_VECTOR_OUTPUT(float)
 | |
| }
 | |
| 
 | |
| /* grn_ts_time_vector_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_time_vector_output(grn_ctx *ctx, grn_ts_time_vector value)
 | |
| {
 | |
|   GRN_TS_VECTOR_OUTPUT(time)
 | |
| }
 | |
| 
 | |
| /* grn_ts_text_vector_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_text_vector_output(grn_ctx *ctx, grn_ts_text_vector value)
 | |
| {
 | |
|   GRN_TS_VECTOR_OUTPUT(text)
 | |
| }
 | |
| 
 | |
| /* grn_ts_geo_vector_output() outputs a value. */
 | |
| static grn_rc
 | |
| grn_ts_geo_vector_output(grn_ctx *ctx, grn_ts_geo_vector value)
 | |
| {
 | |
|   GRN_TS_VECTOR_OUTPUT(geo)
 | |
| }
 | |
| #undef GRN_TS_VECTOR_OUTPUT
 | |
| 
 | |
| /*-------------------------------------------------------------
 | |
|  * grn_ts_writer.
 | |
|  */
 | |
| 
 | |
| typedef struct {
 | |
|   grn_ts_expr_parser *parser;
 | |
|   grn_ts_expr **exprs;
 | |
|   size_t n_exprs;
 | |
|   size_t max_n_exprs;
 | |
|   grn_obj name_buf;
 | |
|   grn_ts_str *names;
 | |
|   grn_ts_buf *bufs;
 | |
| } grn_ts_writer;
 | |
| 
 | |
| /* grn_ts_writer_init() initializes a writer. */
 | |
| static void
 | |
| grn_ts_writer_init(grn_ctx *ctx, grn_ts_writer *writer)
 | |
| {
 | |
|   memset(writer, 0, sizeof(*writer));
 | |
|   writer->parser = NULL;
 | |
|   writer->exprs = NULL;
 | |
|   GRN_TEXT_INIT(&writer->name_buf, GRN_OBJ_VECTOR);
 | |
|   writer->names = NULL;
 | |
|   writer->bufs = NULL;
 | |
| }
 | |
| 
 | |
| /* grn_ts_writer_fin() finalizes a writer. */
 | |
| static void
 | |
| grn_ts_writer_fin(grn_ctx *ctx, grn_ts_writer *writer)
 | |
| {
 | |
|   size_t i;
 | |
|   if (writer->bufs) {
 | |
|     for (i = 0; i < writer->n_exprs; i++) {
 | |
|       grn_ts_buf_fin(ctx, &writer->bufs[i]);
 | |
|     }
 | |
|     GRN_FREE(writer->bufs);
 | |
|   }
 | |
|   if (writer->names) {
 | |
|     GRN_FREE(writer->names);
 | |
|   }
 | |
|   GRN_OBJ_FIN(ctx, &writer->name_buf);
 | |
|   if (writer->exprs) {
 | |
|     for (i = 0; i < writer->n_exprs; i++) {
 | |
|       grn_ts_expr_close(ctx, writer->exprs[i]);
 | |
|     }
 | |
|     GRN_FREE(writer->exprs);
 | |
|   }
 | |
|   if (writer->parser) {
 | |
|     grn_ts_expr_parser_close(ctx, writer->parser);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* grn_ts_writer_expand() expands a wildcard. */
 | |
| static grn_rc
 | |
| grn_ts_writer_expand(grn_ctx *ctx, grn_ts_writer *writer,
 | |
|                      grn_obj *table, grn_ts_str str)
 | |
| {
 | |
|   grn_rc rc = GRN_SUCCESS;
 | |
|   grn_hash_cursor *cursor;
 | |
|   grn_hash *hash = grn_hash_create(ctx, NULL, sizeof(grn_ts_id), 0,
 | |
|                                    GRN_OBJ_TABLE_HASH_KEY | GRN_HASH_TINY);
 | |
|   if (!hash) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   grn_table_columns(ctx, table, str.ptr, str.size - 1, (grn_obj *)hash);
 | |
|   if (ctx->rc != GRN_SUCCESS) {
 | |
|     return ctx->rc;
 | |
|   }
 | |
|   cursor = grn_hash_cursor_open(ctx, hash, NULL, 0, NULL, 0, 0, -1, 0);
 | |
|   if (!cursor) {
 | |
|     rc = GRN_INVALID_ARGUMENT;
 | |
|   } else {
 | |
|     while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) {
 | |
|       char name_buf[GRN_TABLE_MAX_KEY_SIZE];
 | |
|       size_t name_size;
 | |
|       grn_obj *column;
 | |
|       grn_ts_id *column_id;
 | |
|       if (!grn_hash_cursor_get_key(ctx, cursor, (void **)&column_id)) {
 | |
|         rc = GRN_INVALID_ARGUMENT;
 | |
|         break;
 | |
|       }
 | |
|       column = grn_ctx_at(ctx, *column_id);
 | |
|       if (!column) {
 | |
|         rc = GRN_INVALID_ARGUMENT;
 | |
|         break;
 | |
|       }
 | |
|       name_size = grn_column_name(ctx, column, name_buf, sizeof(name_buf));
 | |
|       grn_obj_unlink(ctx, column);
 | |
|       rc = grn_vector_add_element(ctx, &writer->name_buf,
 | |
|                                   name_buf, name_size, 0, GRN_DB_TEXT);
 | |
|       if (rc != GRN_SUCCESS) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     grn_hash_cursor_close(ctx, cursor);
 | |
|   }
 | |
|   grn_hash_close(ctx, hash);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| /* grn_ts_writer_parse() parses output expressions. */
 | |
| static grn_rc
 | |
| grn_ts_writer_parse(grn_ctx *ctx, grn_ts_writer *writer,
 | |
|                     grn_obj *table, grn_ts_str str)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ts_str rest = str;
 | |
|   rc = grn_ts_expr_parser_open(ctx, table, &writer->parser);
 | |
|   for ( ; ; ) {
 | |
|     grn_ts_str first = { NULL, 0 };
 | |
|     rc = grn_ts_expr_parser_split(ctx, writer->parser, rest, &first, &rest);
 | |
|     if (rc != GRN_SUCCESS) {
 | |
|       return (rc == GRN_END_OF_DATA) ? GRN_SUCCESS : rc;
 | |
|     }
 | |
|     if ((first.ptr[first.size - 1] == '*') &&
 | |
|         grn_ts_str_is_name_prefix((grn_ts_str){ first.ptr, first.size - 1 })) {
 | |
|       rc = grn_ts_writer_expand(ctx, writer, table, first);
 | |
|       if (rc != GRN_SUCCESS) {
 | |
|         return rc;
 | |
|       }
 | |
|     } else if (grn_ts_str_is_key_name(first) &&
 | |
|                !grn_ts_table_has_key(ctx, table)) {
 | |
|       /*
 | |
|        * Skip _key if the table has no _key, because the default output_columns
 | |
|        * option contains _key.
 | |
|        */
 | |
|       GRN_TS_DEBUG("skip \"_key\" because the table has no _key");
 | |
|     } else {
 | |
|       rc = grn_vector_add_element(ctx, &writer->name_buf,
 | |
|                                   first.ptr, first.size, 0, GRN_DB_TEXT);
 | |
|       if (rc != GRN_SUCCESS) {
 | |
|         return rc;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* grn_ts_writer_build() builds output expresions. */
 | |
| static grn_rc
 | |
| grn_ts_writer_build(grn_ctx *ctx, grn_ts_writer *writer, grn_obj *table)
 | |
| {
 | |
|   size_t i, n_names = grn_vector_size(ctx, &writer->name_buf);
 | |
|   if (!n_names) {
 | |
|     return GRN_SUCCESS;
 | |
|   }
 | |
|   writer->names = GRN_MALLOCN(grn_ts_str, n_names);
 | |
|   if (!writer->names) {
 | |
|     GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
 | |
|                       "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
 | |
|                       sizeof(grn_ts_str), n_names);
 | |
|   }
 | |
|   writer->exprs = GRN_MALLOCN(grn_ts_expr *, n_names);
 | |
|   if (!writer->exprs) {
 | |
|     GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
 | |
|                       "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
 | |
|                       sizeof(grn_ts_expr *), n_names);
 | |
|   }
 | |
|   for (i = 0; i < n_names; i++) {
 | |
|     grn_rc rc;
 | |
|     grn_ts_expr *new_expr;
 | |
|     const char *name_ptr;
 | |
|     size_t name_size = grn_vector_get_element(ctx, &writer->name_buf, i,
 | |
|                                               &name_ptr, NULL, NULL);
 | |
|     rc = grn_ts_expr_parser_parse(ctx, writer->parser,
 | |
|                                   (grn_ts_str){ name_ptr, name_size },
 | |
|                                   &new_expr);
 | |
|     if (rc != GRN_SUCCESS) {
 | |
|       return rc;
 | |
|     }
 | |
|     writer->names[i].ptr = name_ptr;
 | |
|     writer->names[i].size = name_size;
 | |
|     writer->exprs[i] = new_expr;
 | |
|     writer->n_exprs++;
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* grn_ts_writer_open() creates a writer. */
 | |
| static grn_rc
 | |
| grn_ts_writer_open(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
 | |
|                    grn_ts_writer **writer)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ts_writer *new_writer = GRN_MALLOCN(grn_ts_writer, 1);
 | |
|   if (!new_writer) {
 | |
|     GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
 | |
|                       "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
 | |
|                       sizeof(grn_ts_writer));
 | |
|   }
 | |
|   grn_ts_writer_init(ctx, new_writer);
 | |
|   rc = grn_ts_writer_parse(ctx, new_writer, table, str);
 | |
|   if (rc == GRN_SUCCESS) {
 | |
|     rc = grn_ts_writer_build(ctx, new_writer, table);
 | |
|   }
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     grn_ts_writer_fin(ctx, new_writer);
 | |
|     GRN_FREE(new_writer);
 | |
|     return rc;
 | |
|   }
 | |
|   *writer = new_writer;
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* grn_ts_writer_close() destroys a writer. */
 | |
| static void
 | |
| grn_ts_writer_close(grn_ctx *ctx, grn_ts_writer *writer)
 | |
| {
 | |
|   grn_ts_writer_fin(ctx, writer);
 | |
|   GRN_FREE(writer);
 | |
| }
 | |
| 
 | |
| /* TODO: Errors of output macros, such as GRN_TEXT_*(), are ignored. */
 | |
| 
 | |
| #define GRN_TS_WRITER_OUTPUT_HEADER_CASE(TYPE, name)\
 | |
|   case GRN_DB_ ## TYPE: {\
 | |
|     GRN_TEXT_PUTS(ctx, ctx->impl->output.buf, name);\
 | |
|     break;\
 | |
|   }
 | |
| /* grn_ts_writer_output_header() outputs names and data types. */
 | |
| static grn_rc
 | |
| grn_ts_writer_output_header(grn_ctx *ctx, grn_ts_writer *writer)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   GRN_OUTPUT_ARRAY_OPEN("COLUMNS", writer->n_exprs);
 | |
|   for (size_t i = 0; i < writer->n_exprs; ++i) {
 | |
|     GRN_OUTPUT_ARRAY_OPEN("COLUMN", 2);
 | |
|     rc = grn_text_esc(ctx, ctx->impl->output.buf,
 | |
|                       writer->names[i].ptr, writer->names[i].size);
 | |
|     if (rc != GRN_SUCCESS) {
 | |
|       return rc;
 | |
|     }
 | |
|     GRN_TEXT_PUT(ctx, ctx->impl->output.buf, ",\"", 2);
 | |
|     switch (writer->exprs[i]->data_type) {
 | |
|       case GRN_DB_VOID: {
 | |
|         if (writer->exprs[i]->data_kind == GRN_TS_GEO) {
 | |
|           GRN_TEXT_PUTS(ctx, ctx->impl->output.buf, "GeoPoint");
 | |
|         } else {
 | |
|           GRN_TEXT_PUTS(ctx, ctx->impl->output.buf, "Void");
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(BOOL, "Bool")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT8, "Int8")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT16, "Int16")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT32, "Int32")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT64, "Int64")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT8, "UInt8")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT16, "UInt16")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT32, "UInt32")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT64, "UInt64")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(FLOAT, "Float")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(TIME, "Time")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(SHORT_TEXT, "ShortText")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(TEXT, "Text")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(LONG_TEXT, "LongText")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(TOKYO_GEO_POINT, "TokyoGeoPoint")
 | |
|       GRN_TS_WRITER_OUTPUT_HEADER_CASE(WGS84_GEO_POINT, "WGS84GeoPoint")
 | |
|       default: {
 | |
|         char name_buf[GRN_TABLE_MAX_KEY_SIZE];
 | |
|         size_t name_size;
 | |
|         grn_obj *obj = grn_ctx_at(ctx, writer->exprs[i]->data_type);
 | |
|         if (!obj) {
 | |
|           GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d",
 | |
|                             writer->exprs[i]->data_type);
 | |
|         }
 | |
|         if (!grn_ts_obj_is_table(ctx, obj)) {
 | |
|           grn_obj_unlink(ctx, obj);
 | |
|           GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "not table: %d",
 | |
|                             writer->exprs[i]->data_type);
 | |
|         }
 | |
|         name_size = grn_obj_name(ctx, obj, name_buf, sizeof(name_buf));
 | |
|         GRN_TEXT_PUT(ctx, ctx->impl->output.buf, name_buf, name_size);
 | |
|         grn_obj_unlink(ctx, obj);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     GRN_TEXT_PUTC(ctx, ctx->impl->output.buf, '"');
 | |
|     GRN_OUTPUT_ARRAY_CLOSE();
 | |
|   }
 | |
|   GRN_OUTPUT_ARRAY_CLOSE(); /* COLUMNS. */
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| #undef GRN_TS_WRITER_OUTPUT_HEADER_CASE
 | |
| 
 | |
| #define GRN_TS_WRITER_OUTPUT_BODY_CASE(KIND, kind)\
 | |
|   case GRN_TS_ ## KIND: {\
 | |
|     grn_ts_ ## kind *value = (grn_ts_ ## kind *)writer->bufs[j].ptr;\
 | |
|     grn_ts_ ## kind ## _output(ctx, value[i]);\
 | |
|     break;\
 | |
|   }
 | |
| #define GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(KIND, kind)\
 | |
|   GRN_TS_WRITER_OUTPUT_BODY_CASE(KIND ## _VECTOR, kind ## _vector)
 | |
| /*
 | |
|  * grn_ts_writer_output_body() evaluates expressions and outputs the results.
 | |
|  */
 | |
| static grn_rc
 | |
| grn_ts_writer_output_body(grn_ctx *ctx, grn_ts_writer *writer,
 | |
|                           const grn_ts_record *in, size_t n_in)
 | |
| {
 | |
|   size_t i, j, count = 0;
 | |
|   writer->bufs = GRN_MALLOCN(grn_ts_buf, writer->n_exprs);
 | |
|   if (!writer->bufs) {
 | |
|     GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
 | |
|                       "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
 | |
|                       sizeof(grn_ts_buf), writer->n_exprs);
 | |
|   }
 | |
|   for (i = 0; i < writer->n_exprs; i++) {
 | |
|     grn_ts_buf_init(ctx, &writer->bufs[i]);
 | |
|   }
 | |
|   while (count < n_in) {
 | |
|     size_t batch_size = GRN_TS_BATCH_SIZE;
 | |
|     if (batch_size > (n_in - count)) {
 | |
|       batch_size = n_in - count;
 | |
|     }
 | |
|     for (i = 0; i < writer->n_exprs; ++i) {
 | |
|       grn_rc rc = grn_ts_expr_evaluate_to_buf(ctx, writer->exprs[i], in + count,
 | |
|                                               batch_size, &writer->bufs[i]);
 | |
|       if (rc != GRN_SUCCESS) {
 | |
|         return rc;
 | |
|       }
 | |
|     }
 | |
|     for (i = 0; i < batch_size; ++i) {
 | |
|       GRN_OUTPUT_ARRAY_OPEN("HIT", writer->n_exprs);
 | |
|       for (j = 0; j < writer->n_exprs; ++j) {
 | |
|         if (j) {
 | |
|           GRN_TEXT_PUTC(ctx, ctx->impl->output.buf, ',');
 | |
|         }
 | |
|         switch (writer->exprs[j]->data_kind) {
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_CASE(BOOL, bool);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_CASE(INT, int);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_CASE(FLOAT, float);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_CASE(TIME, time);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_CASE(TEXT, text);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_CASE(GEO, geo);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(BOOL, bool);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(INT, int);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(FLOAT, float);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(TIME, time);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(TEXT, text);
 | |
|           GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(GEO, geo);
 | |
|           default: {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       GRN_OUTPUT_ARRAY_CLOSE(); /* HITS. */
 | |
|     }
 | |
|     count += batch_size;
 | |
|   }
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| #undef GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE
 | |
| #undef GRN_TS_WRITER_OUTPUT_BODY_CASE
 | |
| 
 | |
| /* grn_ts_writer_output() outputs search results into the output buffer. */
 | |
| static grn_rc
 | |
| grn_ts_writer_output(grn_ctx *ctx, grn_ts_writer *writer,
 | |
|                      const grn_ts_record *in, size_t n_in, size_t n_hits)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   GRN_OUTPUT_ARRAY_OPEN("RESULT", 1);
 | |
|   GRN_OUTPUT_ARRAY_OPEN("RESULTSET", 2 + n_in);
 | |
|   GRN_OUTPUT_ARRAY_OPEN("NHITS", 1);
 | |
|   rc = grn_text_ulltoa(ctx, ctx->impl->output.buf, n_hits);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   GRN_OUTPUT_ARRAY_CLOSE(); /* NHITS. */
 | |
|   rc = grn_ts_writer_output_header(ctx, writer);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   rc = grn_ts_writer_output_body(ctx, writer, in, n_in);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   GRN_OUTPUT_ARRAY_CLOSE(); /* RESULTSET. */
 | |
|   GRN_OUTPUT_ARRAY_CLOSE(); /* RESET. */
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* grn_ts_select_filter() applies a filter to all the records of a table. */
 | |
| static grn_rc
 | |
| grn_ts_select_filter(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
 | |
|                      size_t offset, size_t limit,
 | |
|                      grn_ts_record **out, size_t *n_out, size_t *n_hits)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_table_cursor *cursor_obj;
 | |
|   grn_ts_cursor *cursor;
 | |
|   grn_ts_expr *expr = NULL;
 | |
|   grn_ts_record *buf = NULL;
 | |
|   size_t buf_size = 0;
 | |
| 
 | |
|   *out = NULL;
 | |
|   *n_out = 0;
 | |
|   *n_hits = 0;
 | |
| 
 | |
|   cursor_obj = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1,
 | |
|                                      GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_ID);
 | |
|   if (!cursor_obj) {
 | |
|     return (ctx->rc != GRN_SUCCESS) ? ctx->rc : GRN_UNKNOWN_ERROR;
 | |
|   }
 | |
|   rc = grn_ts_obj_cursor_open(ctx, cursor_obj, &cursor);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     grn_obj_close(ctx, cursor_obj);
 | |
|     return rc;
 | |
|   }
 | |
| 
 | |
|   if (str.size) {
 | |
|     rc = grn_ts_expr_parse(ctx, table, str, &expr);
 | |
|   }
 | |
|   if (rc == GRN_SUCCESS) {
 | |
|     for ( ; ; ) {
 | |
|       size_t batch_size;
 | |
|       grn_ts_record *batch;
 | |
| 
 | |
|       /* Extend the record buffer. */
 | |
|       if (buf_size < (*n_out + GRN_TS_BATCH_SIZE)) {
 | |
|         size_t new_size = buf_size ? (buf_size * 2) : GRN_TS_BATCH_SIZE;
 | |
|         size_t n_bytes = sizeof(grn_ts_record) * new_size;
 | |
|         grn_ts_record *new_buf = (grn_ts_record *)GRN_REALLOC(buf, n_bytes);
 | |
|         if (!new_buf) {
 | |
|           GRN_TS_ERR(GRN_NO_MEMORY_AVAILABLE,
 | |
|                      "GRN_REALLOC failed: %" GRN_FMT_SIZE,
 | |
|                      n_bytes);
 | |
|           rc = ctx->rc;
 | |
|           break;
 | |
|         }
 | |
|         buf = new_buf;
 | |
|         buf_size = new_size;
 | |
|       }
 | |
| 
 | |
|       /* Read records from the cursor. */
 | |
|       batch = buf + *n_out;
 | |
|       rc = grn_ts_cursor_read(ctx, cursor, batch, GRN_TS_BATCH_SIZE,
 | |
|                               &batch_size);
 | |
|       if ((rc != GRN_SUCCESS) || !batch_size) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       /* Apply the filter. */
 | |
|       if (expr) {
 | |
|         rc = grn_ts_expr_filter(ctx, expr, batch, batch_size,
 | |
|                                 batch, &batch_size);
 | |
|         if (rc != GRN_SUCCESS) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       *n_hits += batch_size;
 | |
| 
 | |
|       /* Apply the offset and the limit. */
 | |
|       if (offset) {
 | |
|         if (batch_size <= offset) {
 | |
|           offset -= batch_size;
 | |
|           batch_size = 0;
 | |
|         } else {
 | |
|           size_t n_bytes = sizeof(grn_ts_record) * (batch_size - offset);
 | |
|           grn_memmove(batch, batch + offset, n_bytes);
 | |
|           batch_size -= offset;
 | |
|           offset = 0;
 | |
|         }
 | |
|       }
 | |
|       if (batch_size <= limit) {
 | |
|         limit -= batch_size;
 | |
|       } else {
 | |
|         batch_size = limit;
 | |
|         limit = 0;
 | |
|       }
 | |
|       *n_out += batch_size;
 | |
|     }
 | |
|     /* Ignore a failure of destruction. */
 | |
|     if (expr) {
 | |
|       grn_ts_expr_close(ctx, expr);
 | |
|     }
 | |
|   }
 | |
|   /* Ignore a failure of  destruction. */
 | |
|   grn_ts_cursor_close(ctx, cursor);
 | |
| 
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     if (buf) {
 | |
|       GRN_FREE(buf);
 | |
|     }
 | |
|     *n_out = 0;
 | |
|     *n_hits = 0;
 | |
|     return rc;
 | |
|   }
 | |
|   *out = buf;
 | |
|   return GRN_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* grn_ts_select_scorer() adjust scores. */
 | |
| static grn_rc
 | |
| grn_ts_select_scorer(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
 | |
|                      grn_ts_record *records, size_t n_records)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ts_str rest;
 | |
|   grn_ts_expr *expr;
 | |
|   rest = grn_ts_str_trim_score_assignment(str);
 | |
|   if (!rest.size) {
 | |
|     return GRN_SUCCESS;
 | |
|   }
 | |
|   rc = grn_ts_expr_parse(ctx, table, rest, &expr);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   rc = grn_ts_expr_adjust(ctx, expr, records, n_records);
 | |
|   grn_ts_expr_close(ctx, expr);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| /* grn_ts_select_output() outputs the results. */
 | |
| static grn_rc
 | |
| grn_ts_select_output(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
 | |
|                      const grn_ts_record *in, size_t n_in, size_t n_hits)
 | |
| {
 | |
|   grn_ts_writer *writer= 0;
 | |
|   grn_rc rc = grn_ts_writer_open(ctx, table, str, &writer);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     return rc;
 | |
|   }
 | |
|   rc = grn_ts_writer_output(ctx, writer, in, n_in, n_hits);
 | |
|   grn_ts_writer_close(ctx, writer);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| /* grn_ts_select_with_sortby() executes a select command with --sortby. */
 | |
| static grn_rc
 | |
| grn_ts_select_with_sortby(grn_ctx *ctx, grn_obj *table,
 | |
|                           grn_ts_str filter, grn_ts_str scorer,
 | |
|                           grn_ts_str sortby, grn_ts_str output_columns,
 | |
|                           size_t offset, size_t limit)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ts_record *recs = NULL;
 | |
|   size_t n_recs = 0, max_n_recs = 0, n_hits = 0;
 | |
|   grn_table_cursor *cursor_obj;
 | |
|   grn_ts_cursor *cursor = NULL;
 | |
|   grn_ts_expr *filter_expr = NULL;
 | |
|   grn_ts_expr *scorer_expr = NULL;
 | |
|   grn_ts_sorter *sorter = NULL;
 | |
|   cursor_obj = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1,
 | |
|                                      GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_ID);
 | |
|   if (!cursor_obj) {
 | |
|     GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_table_cursor_open failed");
 | |
|   }
 | |
|   rc = grn_ts_obj_cursor_open(ctx, cursor_obj, &cursor);
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     grn_obj_close(ctx, cursor_obj);
 | |
|     return rc;
 | |
|   }
 | |
|   if (filter.size) {
 | |
|     rc = grn_ts_expr_parse(ctx, table, filter, &filter_expr);
 | |
|   }
 | |
|   if (rc == GRN_SUCCESS) {
 | |
|     scorer = grn_ts_str_trim_score_assignment(scorer);
 | |
|     if (scorer.size) {
 | |
|       rc = grn_ts_expr_parse(ctx, table, scorer, &scorer_expr);
 | |
|     }
 | |
|     if (rc == GRN_SUCCESS) {
 | |
|       rc = grn_ts_sorter_parse(ctx, table, sortby, offset, limit, &sorter);
 | |
|     }
 | |
|   }
 | |
|   if (rc == GRN_SUCCESS) {
 | |
|     size_t n_pending_recs = 0;
 | |
|     for ( ; ; ) {
 | |
|       size_t batch_size;
 | |
|       grn_ts_record *batch;
 | |
|       /* Extend a buffer for records. */
 | |
|       if (max_n_recs < (n_recs + GRN_TS_BATCH_SIZE)) {
 | |
|         size_t n_bytes, new_max_n_recs = max_n_recs * 2;
 | |
|         grn_ts_record *new_recs;
 | |
|         if (!new_max_n_recs) {
 | |
|           new_max_n_recs = GRN_TS_BATCH_SIZE;
 | |
|         }
 | |
|         n_bytes = sizeof(grn_ts_record) * new_max_n_recs;
 | |
|         new_recs = (grn_ts_record *)GRN_REALLOC(recs, n_bytes);
 | |
|         if (!new_recs) {
 | |
|           GRN_TS_ERR(GRN_NO_MEMORY_AVAILABLE,
 | |
|                      "GRN_REALLOC failed: %" GRN_FMT_SIZE,
 | |
|                      n_bytes);
 | |
|           rc = ctx->rc;
 | |
|           break;
 | |
|         }
 | |
|         recs = new_recs;
 | |
|         max_n_recs = new_max_n_recs;
 | |
|       }
 | |
|       /* Read records from a cursor. */
 | |
|       batch = recs + n_recs;
 | |
|       rc = grn_ts_cursor_read(ctx, cursor, batch, GRN_TS_BATCH_SIZE,
 | |
|                               &batch_size);
 | |
|       if (rc != GRN_SUCCESS) {
 | |
|         break;
 | |
|       } else if (!batch_size) {
 | |
|         /* Apply a scorer and complete sorting. */
 | |
|         if (scorer_expr) {
 | |
|           rc = grn_ts_expr_adjust(ctx, scorer_expr,
 | |
|                                   recs + n_recs - n_pending_recs,
 | |
|                                   n_pending_recs);
 | |
|           if (rc != GRN_SUCCESS) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         if (n_pending_recs) {
 | |
|           rc = grn_ts_sorter_progress(ctx, sorter, recs, n_recs, &n_recs);
 | |
|           if (rc != GRN_SUCCESS) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         rc = grn_ts_sorter_complete(ctx, sorter, recs, n_recs, &n_recs);
 | |
|         break;
 | |
|       }
 | |
|       /* Apply a filter. */
 | |
|       if (filter_expr) {
 | |
|         rc = grn_ts_expr_filter(ctx, filter_expr, batch, batch_size,
 | |
|                                 batch, &batch_size);
 | |
|         if (rc != GRN_SUCCESS) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       n_hits += batch_size;
 | |
|       n_recs += batch_size;
 | |
|       n_pending_recs += batch_size;
 | |
|       /*
 | |
|        * Apply a scorer and progress sorting if there are enough pending
 | |
|        * records.
 | |
|        */
 | |
|       if (n_pending_recs >= GRN_TS_BATCH_SIZE) {
 | |
|         if (scorer_expr) {
 | |
|           rc = grn_ts_expr_adjust(ctx, scorer_expr,
 | |
|                                   recs + n_recs - n_pending_recs,
 | |
|                                   n_pending_recs);
 | |
|           if (rc != GRN_SUCCESS) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         rc = grn_ts_sorter_progress(ctx, sorter, recs, n_recs, &n_recs);
 | |
|         if (rc != GRN_SUCCESS) {
 | |
|           break;
 | |
|         }
 | |
|         n_pending_recs = 0;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   if (rc == GRN_SUCCESS) {
 | |
|     rc = grn_ts_select_output(ctx, table, output_columns,
 | |
|                               recs, n_recs, n_hits);
 | |
|   }
 | |
|   if (cursor) {
 | |
|     grn_ts_cursor_close(ctx, cursor);
 | |
|   }
 | |
|   if (recs) {
 | |
|     GRN_FREE(recs);
 | |
|   }
 | |
|   if (sorter) {
 | |
|     grn_ts_sorter_close(ctx, sorter);
 | |
|   }
 | |
|   if (scorer_expr) {
 | |
|     grn_ts_expr_close(ctx, scorer_expr);
 | |
|   }
 | |
|   if (filter_expr) {
 | |
|     grn_ts_expr_close(ctx, filter_expr);
 | |
|   }
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * grn_ts_select_without_sortby() executes a select command without --sortby.
 | |
|  */
 | |
| static grn_rc
 | |
| grn_ts_select_without_sortby(grn_ctx *ctx, grn_obj *table,
 | |
|                              grn_ts_str filter, grn_ts_str scorer,
 | |
|                              grn_ts_str output_columns,
 | |
|                              size_t offset, size_t limit)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ts_record *records = NULL;
 | |
|   size_t n_records, n_hits;
 | |
|   rc = grn_ts_select_filter(ctx, table, filter, offset, limit,
 | |
|                             &records, &n_records, &n_hits);
 | |
|   if (rc == GRN_SUCCESS) {
 | |
|     rc = grn_ts_select_scorer(ctx, table, scorer, records, n_records);
 | |
|     if (rc == GRN_SUCCESS) {
 | |
|       rc = grn_ts_select_output(ctx, table, output_columns,
 | |
|                                 records, n_records, n_hits);
 | |
|     }
 | |
|   }
 | |
|   if (records) {
 | |
|     GRN_FREE(records);
 | |
|   }
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| /*-------------------------------------------------------------
 | |
|  * API.
 | |
|  */
 | |
| 
 | |
| grn_rc
 | |
| grn_ts_select(grn_ctx *ctx, grn_obj *table,
 | |
|               const char *filter_ptr, size_t filter_len,
 | |
|               const char *scorer_ptr, size_t scorer_len,
 | |
|               const char *sortby_ptr, size_t sortby_len,
 | |
|               const char *output_columns_ptr, size_t output_columns_len,
 | |
|               size_t offset, size_t limit)
 | |
| {
 | |
|   grn_rc rc;
 | |
|   grn_ts_str filter = { filter_ptr, filter_len };
 | |
|   grn_ts_str scorer = { scorer_ptr, scorer_len };
 | |
|   grn_ts_str sortby = { sortby_ptr, sortby_len };
 | |
|   grn_ts_str output_columns = { output_columns_ptr, output_columns_len };
 | |
|   if (!ctx) {
 | |
|     return GRN_INVALID_ARGUMENT;
 | |
|   }
 | |
|   if (!table || !grn_ts_obj_is_table(ctx, table) ||
 | |
|       (!filter_ptr && filter_len) || (!scorer_ptr && scorer_len) ||
 | |
|       (!sortby_ptr && sortby_len) ||
 | |
|       (!output_columns_ptr && output_columns_len)) {
 | |
|     GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
 | |
|   }
 | |
|   filter = grn_ts_str_trim_left(filter);
 | |
|   if (sortby_len) {
 | |
|     rc = grn_ts_select_with_sortby(ctx, table, filter, scorer, sortby,
 | |
|                                    output_columns, offset, limit);
 | |
|   } else {
 | |
|     rc = grn_ts_select_without_sortby(ctx, table, filter, scorer,
 | |
|                                       output_columns, offset, limit);
 | |
|   }
 | |
|   if (rc != GRN_SUCCESS) {
 | |
|     GRN_BULK_REWIND(ctx->impl->output.buf);
 | |
|     if ((ctx->rc == GRN_SUCCESS) || !ctx->errbuf[0]) {
 | |
|       ERR(rc, "error message is missing");
 | |
|     } else if (ctx->errlvl < GRN_LOG_ERROR) {
 | |
|       ctx->errlvl = GRN_LOG_ERROR;
 | |
|     }
 | |
|   }
 | |
|   return rc;
 | |
| }
 |