mirror of
https://github.com/MariaDB/server.git
synced 2025-02-20 20:33:15 +01:00

- 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;
|
|
}
|