From a175314c3e5827eb193872241446f2f8f5c9d33c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 20:07:14 +0200 Subject: Adding upstream version 1:10.5.12. Signed-off-by: Daniel Baumann --- storage/mroonga/vendor/groonga/lib/ts/Makefile.am | 20 + storage/mroonga/vendor/groonga/lib/ts/sources.am | 25 + storage/mroonga/vendor/groonga/lib/ts/ts_buf.c | 244 + storage/mroonga/vendor/groonga/lib/ts/ts_buf.h | 111 + storage/mroonga/vendor/groonga/lib/ts/ts_cursor.c | 163 + storage/mroonga/vendor/groonga/lib/ts/ts_cursor.h | 59 + storage/mroonga/vendor/groonga/lib/ts/ts_expr.c | 219 + storage/mroonga/vendor/groonga/lib/ts/ts_expr.h | 87 + .../vendor/groonga/lib/ts/ts_expr_builder.c | 757 +++ .../vendor/groonga/lib/ts/ts_expr_builder.h | 128 + .../mroonga/vendor/groonga/lib/ts/ts_expr_node.c | 5325 ++++++++++++++++++++ .../mroonga/vendor/groonga/lib/ts/ts_expr_node.h | 128 + .../mroonga/vendor/groonga/lib/ts/ts_expr_parser.c | 1329 +++++ .../mroonga/vendor/groonga/lib/ts/ts_expr_parser.h | 107 + storage/mroonga/vendor/groonga/lib/ts/ts_log.h | 46 + storage/mroonga/vendor/groonga/lib/ts/ts_op.c | 131 + storage/mroonga/vendor/groonga/lib/ts/ts_op.h | 87 + storage/mroonga/vendor/groonga/lib/ts/ts_plan.c | 21 + storage/mroonga/vendor/groonga/lib/ts/ts_plan.h | 87 + storage/mroonga/vendor/groonga/lib/ts/ts_sorter.c | 2174 ++++++++ storage/mroonga/vendor/groonga/lib/ts/ts_sorter.h | 98 + storage/mroonga/vendor/groonga/lib/ts/ts_str.c | 191 + storage/mroonga/vendor/groonga/lib/ts/ts_str.h | 106 + storage/mroonga/vendor/groonga/lib/ts/ts_types.h | 168 + storage/mroonga/vendor/groonga/lib/ts/ts_util.c | 129 + storage/mroonga/vendor/groonga/lib/ts/ts_util.h | 61 + 26 files changed, 12001 insertions(+) create mode 100644 storage/mroonga/vendor/groonga/lib/ts/Makefile.am create mode 100644 storage/mroonga/vendor/groonga/lib/ts/sources.am create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_buf.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_buf.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_cursor.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_cursor.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_log.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_op.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_op.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_plan.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_plan.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_sorter.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_sorter.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_str.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_str.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_types.h create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_util.c create mode 100644 storage/mroonga/vendor/groonga/lib/ts/ts_util.h (limited to 'storage/mroonga/vendor/groonga/lib/ts') diff --git a/storage/mroonga/vendor/groonga/lib/ts/Makefile.am b/storage/mroonga/vendor/groonga/lib/ts/Makefile.am new file mode 100644 index 00000000..f1f21df4 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/Makefile.am @@ -0,0 +1,20 @@ +BUNDLED_LIBRARIES_CFLAGS = \ + $(MRUBY_CFLAGS) \ + $(ONIGMO_CFLAGS) + +DEFAULT_INCLUDES = \ + -I$(top_builddir) \ + -I$(top_srcdir)/include \ + $(BUNDLED_LIBRARIES_CFLAGS) + +AM_CFLAGS = \ + $(NO_STRICT_ALIASING_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(GRN_CFLAGS) \ + $(MESSAGE_PACK_CFLAGS) + +noinst_LTLIBRARIES = libgrnts.la + +include sources.am + +CLEANFILES = *.gcno *.gcda diff --git a/storage/mroonga/vendor/groonga/lib/ts/sources.am b/storage/mroonga/vendor/groonga/lib/ts/sources.am new file mode 100644 index 00000000..5d89b059 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/sources.am @@ -0,0 +1,25 @@ +libgrnts_la_SOURCES = \ + ts_buf.c \ + ts_buf.h \ + ts_cursor.c \ + ts_cursor.h \ + ts_expr.c \ + ts_expr.h \ + ts_expr_builder.c \ + ts_expr_builder.h \ + ts_expr_node.c \ + ts_expr_node.h \ + ts_expr_parser.c \ + ts_expr_parser.h \ + ts_log.h \ + ts_op.c \ + ts_op.h \ + ts_plan.c \ + ts_plan.h \ + ts_sorter.c \ + ts_sorter.h \ + ts_str.c \ + ts_str.h \ + ts_types.h \ + ts_util.c \ + ts_util.h diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_buf.c b/storage/mroonga/vendor/groonga/lib/ts/ts_buf.c new file mode 100644 index 00000000..65521d71 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_buf.c @@ -0,0 +1,244 @@ +/* -*- 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 +*/ + +#include "ts_buf.h" + +#include "../grn_ctx.h" + +#include "ts_log.h" + +#include + +/*------------------------------------------------------------- + * grn_ts_buf + */ + +void +grn_ts_buf_init(grn_ctx *ctx, grn_ts_buf *buf) +{ + buf->ptr = NULL; + buf->size = 0; + buf->pos = 0; +} + +/* +grn_rc +grn_ts_buf_open(grn_ctx *ctx, grn_ts_buf **buf) +{ + grn_ts_buf *new_buf = GRN_MALLOCN(grn_ts_buf, 1); + if (!new_buf) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_buf)); + } + grn_ts_buf_init(ctx, new_buf); + *buf = new_buf; + return GRN_SUCCESS; +} +*/ + +void +grn_ts_buf_fin(grn_ctx *ctx, grn_ts_buf *buf) +{ + if (buf->ptr) { + GRN_FREE(buf->ptr); + } +} + +/* +void +grn_ts_buf_close(grn_ctx *ctx, grn_ts_buf *buf) +{ + if (buf) { + grn_ts_buf_fin(ctx, buf); + } +} +*/ + +grn_rc +grn_ts_buf_reserve(grn_ctx *ctx, grn_ts_buf *buf, size_t min_size) +{ + void *new_ptr; + size_t enough_size; + if (min_size <= buf->size) { + return GRN_SUCCESS; + } + enough_size = buf->size ? (buf->size << 1) : 1; + while (enough_size < min_size) { + if ((enough_size << 1) < enough_size) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "size overflow: %" GRN_FMT_SIZE, + min_size); + } + enough_size <<= 1; + } + new_ptr = GRN_REALLOC(buf->ptr, enough_size); + if (!new_ptr) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, + enough_size); + } + buf->ptr = new_ptr; + buf->size = enough_size; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_buf_resize(grn_ctx *ctx, grn_ts_buf *buf, size_t new_size) +{ + void *new_ptr; + if (new_size == buf->size) { + return GRN_SUCCESS; + } + if (!new_size) { + if (buf->ptr) { + GRN_FREE(buf->ptr); + buf->ptr = NULL; + buf->size = new_size; + } + return GRN_SUCCESS; + } + new_ptr = GRN_REALLOC(buf->ptr, new_size); + if (!new_ptr) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, + new_size); + } + buf->ptr = new_ptr; + buf->size = new_size; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_buf_write(grn_ctx *ctx, grn_ts_buf *buf, const void *ptr, size_t size) +{ + size_t new_pos = buf->pos + size; + if (new_pos < buf->pos) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "size overflow: %" GRN_FMT_SIZE " + %" GRN_FMT_SIZE, + buf->pos, size); + } + if (new_pos > buf->size) { + grn_rc rc = grn_ts_buf_reserve(ctx, buf, new_pos); + if (rc != GRN_SUCCESS) { + return rc; + } + } + grn_memcpy((char *)buf->ptr + buf->pos, ptr, size); + buf->pos += size; + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_rbuf + */ + +void +grn_ts_rbuf_init(grn_ctx *ctx, grn_ts_rbuf *rbuf) +{ + rbuf->recs = NULL; + rbuf->n_recs = 0; + rbuf->max_n_recs = 0; +} + +void +grn_ts_rbuf_fin(grn_ctx *ctx, grn_ts_rbuf *rbuf) +{ + if (rbuf->recs) { + GRN_FREE(rbuf->recs); + } +} + +grn_rc +grn_ts_rbuf_open(grn_ctx *ctx, grn_ts_rbuf **rbuf) +{ + grn_ts_rbuf *new_rbuf = GRN_MALLOCN(grn_ts_rbuf, 1); + if (!new_rbuf) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_rbuf)); + } + grn_ts_rbuf_init(ctx, new_rbuf); + *rbuf = new_rbuf; + return GRN_SUCCESS; +} + +void +grn_ts_rbuf_close(grn_ctx *ctx, grn_ts_rbuf *rbuf) +{ + if (rbuf) { + grn_ts_rbuf_fin(ctx, rbuf); + } +} + +grn_rc +grn_ts_rbuf_reserve(grn_ctx *ctx, grn_ts_rbuf *rbuf, size_t min_max_n_recs) +{ + size_t n_bytes, enough_max_n_recs; + grn_ts_record *new_recs; + if (min_max_n_recs <= rbuf->max_n_recs) { + return GRN_SUCCESS; + } + enough_max_n_recs = rbuf->max_n_recs ? (rbuf->max_n_recs << 1) : 1; + while (enough_max_n_recs < min_max_n_recs) { + if ((enough_max_n_recs << 1) < enough_max_n_recs) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "size overflow: %" GRN_FMT_SIZE, + min_max_n_recs); + } + enough_max_n_recs <<= 1; + } + n_bytes = sizeof(grn_ts_record) * enough_max_n_recs; + new_recs = GRN_REALLOC(rbuf->recs, n_bytes); + if (!new_recs) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, + n_bytes); + } + rbuf->recs = new_recs; + rbuf->max_n_recs = enough_max_n_recs; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_rbuf_resize(grn_ctx *ctx, grn_ts_rbuf *rbuf, size_t new_max_n_recs) +{ + size_t n_bytes; + grn_ts_record *new_recs; + if (new_max_n_recs == rbuf->max_n_recs) { + return GRN_SUCCESS; + } + if (!new_max_n_recs) { + if (rbuf->recs) { + GRN_FREE(rbuf->recs); + rbuf->recs = NULL; + rbuf->max_n_recs = new_max_n_recs; + } + return GRN_SUCCESS; + } + n_bytes = sizeof(grn_ts_record) * new_max_n_recs; + new_recs = GRN_REALLOC(rbuf->recs, n_bytes); + if (!new_recs) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, + new_max_n_recs); + } + rbuf->recs = new_recs; + rbuf->max_n_recs = new_max_n_recs; + return GRN_SUCCESS; +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_buf.h b/storage/mroonga/vendor/groonga/lib/ts/ts_buf.h new file mode 100644 index 00000000..f8077f0f --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_buf.h @@ -0,0 +1,111 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------- + * grn_ts_buf + */ + +/* grn_ts_buf works as a buffer for arbitrary data. */ + +typedef struct { + void *ptr; /* The starting address. */ + size_t size; /* The size in bytes. */ + size_t pos; /* The current position for grn_ts_buf_write(). */ +} grn_ts_buf; + +/* grn_ts_buf_init() initializes a buffer. */ +void grn_ts_buf_init(grn_ctx *ctx, grn_ts_buf *buf); + +/* grn_ts_buf_fin() finalizes a buffer. */ +void grn_ts_buf_fin(grn_ctx *ctx, grn_ts_buf *buf); + +#if 0 +/* grn_ts_buf_open() creates a buffer. */ +grn_rc grn_ts_buf_open(grn_ctx *ctx, grn_ts_buf **buf); + +/* grn_ts_buf_close() destroys a buffer. */ +void grn_ts_buf_close(grn_ctx *ctx, grn_ts_buf *buf); +#endif + +/* + * grn_ts_buf_reserve() reserves enough memory to store `min_size` bytes. + * Note that this function never shrinks a buffer and does nothing if + * `min_size` is not greater than `buf->size`. + */ +grn_rc grn_ts_buf_reserve(grn_ctx *ctx, grn_ts_buf *buf, size_t min_size); + +/* grn_ts_buf_resize() resizes a buffer. */ +grn_rc grn_ts_buf_resize(grn_ctx *ctx, grn_ts_buf *buf, size_t new_size); + +/* + * grn_ts_buf_write() writes data into a buffer. `buf->pos` specifies the + * position and it will be modified on success. + * Note that this function resizes a buffer if required. + */ +grn_rc grn_ts_buf_write(grn_ctx *ctx, grn_ts_buf *buf, + const void *ptr, size_t size); + +/*------------------------------------------------------------- + * grn_ts_rbuf + */ + +/* grn_ts_rbuf works as a buffer for records. */ + +typedef struct { + grn_ts_record *recs; /* Pointer to records. */ + size_t n_recs; /* The number of records. */ + size_t max_n_recs; /* The maximum number of records. */ +} grn_ts_rbuf; + +/* grn_ts_rbuf_init() initializes a buffer. */ +void grn_ts_rbuf_init(grn_ctx *ctx, grn_ts_rbuf *rbuf); + +/* grn_ts_rbuf_fin() finalizes a buffer. */ +void grn_ts_rbuf_fin(grn_ctx *ctx, grn_ts_rbuf *rbuf); + +/* grn_ts_rbuf_open() creates a buffer. */ +/*grn_rc grn_ts_rbuf_open(grn_ctx *ctx, grn_ts_rbuf **rbuf);*/ + +/* grn_ts_rbuf_close() destroys a buffer. */ +/*void grn_ts_rbuf_close(grn_ctx *ctx, grn_ts_rbuf *rbuf);*/ + +/* + * grn_ts_rbuf_reserve() reserves enough memory to store `n_recs` records. + * Note that this function never shrinks a buffer and does nothing if `n_recs` + * is not greater than the `rbuf->max_n_recs`. + */ +grn_rc grn_ts_rbuf_reserve(grn_ctx *ctx, grn_ts_rbuf *rbuf, size_t n_recs); + +/* grn_ts_rbuf_resize() resizes a buffer. */ +grn_rc grn_ts_rbuf_resize(grn_ctx *ctx, grn_ts_rbuf *rbuf, + size_t new_max_n_recs); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_cursor.c b/storage/mroonga/vendor/groonga/lib/ts/ts_cursor.c new file mode 100644 index 00000000..779e4cae --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_cursor.c @@ -0,0 +1,163 @@ +/* -*- 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 +*/ + +#include "ts_cursor.h" + +#include "../grn_ctx.h" +#include "../grn_dat.h" +#include "../grn_hash.h" +#include "../grn_pat.h" + +#include "ts_log.h" +#include "ts_util.h" + +/*------------------------------------------------------------- + * grn_ts_obj_cursor. + */ + +typedef struct { + GRN_TS_CURSOR_COMMON_MEMBERS + grn_obj *obj; /* Wrapped cursor object. */ +} grn_ts_obj_cursor; + +grn_rc +grn_ts_obj_cursor_open(grn_ctx *ctx, grn_obj *obj, grn_ts_cursor **cursor) +{ + grn_ts_obj_cursor *new_cursor; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!obj || !cursor) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + switch (obj->header.type) { + case GRN_CURSOR_TABLE_HASH_KEY: + case GRN_CURSOR_TABLE_PAT_KEY: + case GRN_CURSOR_TABLE_DAT_KEY: + case GRN_CURSOR_TABLE_NO_KEY: { + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + } + new_cursor = GRN_MALLOCN(grn_ts_obj_cursor, 1); + if (!new_cursor) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_obj_cursor)); + } + new_cursor->type = GRN_TS_OBJ_CURSOR; + new_cursor->obj = obj; + *cursor = (grn_ts_cursor *)new_cursor; + return GRN_SUCCESS; +} + +/* grn_ts_obj_cursor_close() destroys a wrapper cursor. */ +static grn_rc +grn_ts_obj_cursor_close(grn_ctx *ctx, grn_ts_obj_cursor *cursor) +{ + if (cursor->obj) { + grn_obj_close(ctx, cursor->obj); + } + GRN_FREE(cursor); + return GRN_SUCCESS; +} + +#define GRN_TS_OBJ_CURSOR_READ(type)\ + size_t i;\ + grn_ ## type ## _cursor *obj = (grn_ ## type ## _cursor *)cursor->obj;\ + for (i = 0; i < max_n_recs; i++) {\ + recs[i].id = grn_ ## type ## _cursor_next(ctx, obj);\ + if (!recs[i].id) {\ + break;\ + }\ + recs[i].score = 0;\ + }\ + *n_recs = i;\ + return GRN_SUCCESS; +/* grn_ts_obj_cursor_read() reads records from a wrapper cursor. */ +static grn_rc +grn_ts_obj_cursor_read(grn_ctx *ctx, grn_ts_obj_cursor *cursor, + grn_ts_record *recs, size_t max_n_recs, size_t *n_recs) +{ + switch (cursor->obj->header.type) { + case GRN_CURSOR_TABLE_HASH_KEY: { + GRN_TS_OBJ_CURSOR_READ(hash) + } + case GRN_CURSOR_TABLE_PAT_KEY: { + GRN_TS_OBJ_CURSOR_READ(pat) + } + case GRN_CURSOR_TABLE_DAT_KEY: { + GRN_TS_OBJ_CURSOR_READ(dat) + } + case GRN_CURSOR_TABLE_NO_KEY: { + GRN_TS_OBJ_CURSOR_READ(array) + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + } + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_cursor. + */ + +grn_rc +grn_ts_cursor_close(grn_ctx *ctx, grn_ts_cursor *cursor) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!cursor) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + switch (cursor->type) { + case GRN_TS_OBJ_CURSOR: { + return grn_ts_obj_cursor_close(ctx, (grn_ts_obj_cursor *)cursor); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid cursor type: %d", + cursor->type); + } + } +} + +grn_rc +grn_ts_cursor_read(grn_ctx *ctx, grn_ts_cursor *cursor, + grn_ts_record *out, size_t max_n_out, size_t *n_out) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!cursor || (!out && max_n_out) || !n_out) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + switch (cursor->type) { + case GRN_TS_OBJ_CURSOR: { + return grn_ts_obj_cursor_read(ctx, (grn_ts_obj_cursor *)cursor, + out, max_n_out, n_out); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid cursor type: %d", + cursor->type); + } + } +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_cursor.h b/storage/mroonga/vendor/groonga/lib/ts/ts_cursor.h new file mode 100644 index 00000000..f12034f7 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_cursor.h @@ -0,0 +1,59 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GRN_TS_OBJ_CURSOR /* Wrapper cursor. */ +} grn_ts_cursor_type; + +#define GRN_TS_CURSOR_COMMON_MEMBERS\ + grn_ts_cursor_type type; /* Cursor type. */ + +typedef struct { + GRN_TS_CURSOR_COMMON_MEMBERS +} grn_ts_cursor; + +/* + * grn_ts_obj_cursor_open() creates a wrapper cursor. + * The new cursor will be a wrapper for a Groonga cursor specified by `obj`. + * On success, `obj` will be closed in grn_ts_cursor_close(). + * On failure, `obj` is left as is. + */ +grn_rc grn_ts_obj_cursor_open(grn_ctx *ctx, grn_obj *obj, + grn_ts_cursor **cursor); + +/* grn_ts_cursor_close() destroys a cursor. */ +grn_rc grn_ts_cursor_close(grn_ctx *ctx, grn_ts_cursor *cursor); + +/* grn_ts_cursor_read() reads records from a cursor. */ +grn_rc grn_ts_cursor_read(grn_ctx *ctx, grn_ts_cursor *cursor, + grn_ts_record *out, size_t max_n_out, size_t *n_out); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr.c b/storage/mroonga/vendor/groonga/lib/ts/ts_expr.c new file mode 100644 index 00000000..16576fbc --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr.c @@ -0,0 +1,219 @@ +/* -*- 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 +*/ + +#include "ts_expr.h" + +#include + +#include "../grn_ctx.h" + +#include "ts_log.h" +#include "ts_str.h" +#include "ts_util.h" +#include "ts_expr_parser.h" + +/* grn_ts_expr_init() initializes an expression. */ +static void +grn_ts_expr_init(grn_ctx *ctx, grn_ts_expr *expr) +{ + memset(expr, 0, sizeof(*expr)); + expr->table = NULL; + expr->root = NULL; +} + +/* grn_ts_expr_fin() finalizes an expression. */ +static void +grn_ts_expr_fin(grn_ctx *ctx, grn_ts_expr *expr) +{ + if (expr->root) { + grn_ts_expr_node_close(ctx, expr->root); + } + if (expr->table) { + grn_obj_unlink(ctx, expr->table); + } +} + +grn_rc +grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr_node *root, + grn_ts_expr **expr) +{ + grn_rc rc; + grn_ts_expr *new_expr; + grn_ts_expr_type type; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || !root || !expr) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + switch (root->type) { + case GRN_TS_EXPR_ID_NODE: { + type = GRN_TS_EXPR_ID; + break; + } + case GRN_TS_EXPR_SCORE_NODE: { + type = GRN_TS_EXPR_SCORE; + break; + } + case GRN_TS_EXPR_KEY_NODE: + case GRN_TS_EXPR_VALUE_NODE: { + type = GRN_TS_EXPR_VARIABLE; + break; + } + case GRN_TS_EXPR_CONST_NODE: { + type = GRN_TS_EXPR_CONST; + break; + } + case GRN_TS_EXPR_COLUMN_NODE: + case GRN_TS_EXPR_OP_NODE: + case GRN_TS_EXPR_BRIDGE_NODE: { + type = GRN_TS_EXPR_VARIABLE; + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + } + new_expr = GRN_MALLOCN(grn_ts_expr, 1); + if (!new_expr) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE, + sizeof(grn_ts_expr)); + } + rc = grn_ts_obj_increment_ref_count(ctx, table); + if (rc != GRN_SUCCESS) { + GRN_FREE(new_expr); + return rc; + } + grn_ts_expr_init(ctx, new_expr); + new_expr->table = table; + new_expr->type = type; + new_expr->data_kind = root->data_kind; + new_expr->data_type = root->data_type; + new_expr->root = root; + *expr = new_expr; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table, grn_ts_str str, + grn_ts_expr **expr) +{ + grn_rc rc; + grn_ts_expr *new_expr; + grn_ts_expr_parser *parser; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || + (!str.ptr && str.size) || !expr) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_parser_open(ctx, table, &parser); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_parser_parse(ctx, parser, str, &new_expr); + grn_ts_expr_parser_close(ctx, parser); + if (rc != GRN_SUCCESS) { + return rc; + } + *expr = new_expr; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!expr) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + grn_ts_expr_fin(ctx, expr); + GRN_FREE(expr); + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr *expr, + const grn_ts_record *in, size_t n_in, + grn_ts_buf *out) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!expr || (!in && n_in) || !out) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (!n_in) { + return GRN_SUCCESS; + } + return grn_ts_expr_node_evaluate_to_buf(ctx, expr->root, in, n_in, out); +} + +grn_rc +grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr, + const grn_ts_record *in, size_t n_in, void *out) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!expr || (!in && n_in) || (n_in && !out)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (!n_in) { + return GRN_SUCCESS; + } + return grn_ts_expr_node_evaluate(ctx, expr->root, in, n_in, out); +} + +grn_rc +grn_ts_expr_filter(grn_ctx *ctx, grn_ts_expr *expr, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!expr || (!in && n_in) || !out || !n_out) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (!n_in) { + *n_out = 0; + return GRN_SUCCESS; + } + return grn_ts_expr_node_filter(ctx, expr->root, in, n_in, out, n_out); +} + +grn_rc +grn_ts_expr_adjust(grn_ctx *ctx, grn_ts_expr *expr, + grn_ts_record *io, size_t n_io) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!expr || (!io && n_io)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (!n_io) { + return GRN_SUCCESS; + } + return grn_ts_expr_node_adjust(ctx, expr->root, io, n_io); +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr.h b/storage/mroonga/vendor/groonga/lib/ts/ts_expr.h new file mode 100644 index 00000000..b50e886d --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr.h @@ -0,0 +1,87 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_buf.h" +#include "ts_expr_node.h" +#include "ts_str.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------- + * Enumeration types. + */ + +typedef enum { + GRN_TS_EXPR_ID, /* IDs (_id). */ + GRN_TS_EXPR_SCORE, /* Scores (_score). */ + GRN_TS_EXPR_CONST, /* A const. */ + GRN_TS_EXPR_VARIABLE /* An expression that contains a variable. */ +} grn_ts_expr_type; + +/*------------------------------------------------------------- + * Expression components. + */ + +typedef struct { + grn_obj *table; /* Associated table. */ + grn_ts_expr_type type; /* Expression type. */ + grn_ts_data_kind data_kind; /* Abstract data type. */ + grn_ts_data_type data_type; /* Detailed data type. */ + grn_ts_expr_node *root; /* Root node. */ +} grn_ts_expr; + +/* grn_ts_expr_open() creates an expression. */ +grn_rc grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr_node *root, + grn_ts_expr **expr); + +/* grn_ts_expr_parse() parses a string and creates an expression. */ +grn_rc grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table, grn_ts_str str, + grn_ts_expr **expr); + +/* grn_ts_expr_close() destroys an expression. */ +grn_rc grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr); + +/* grn_ts_expr_evaluate() evaluates an expression. */ +grn_rc grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr, + const grn_ts_record *in, size_t n_in, void *out); + +/* grn_ts_expr_evaluate_to_buf() evaluates an expression. */ +grn_rc grn_ts_expr_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr *expr, + const grn_ts_record *in, size_t n_in, + grn_ts_buf *out); + +/* grn_ts_expr_filter() filters records. */ +grn_rc grn_ts_expr_filter(grn_ctx *ctx, grn_ts_expr *expr, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out); + +/* grn_ts_expr_adjust() updates scores. */ +grn_rc grn_ts_expr_adjust(grn_ctx *ctx, grn_ts_expr *expr, + grn_ts_record *io, size_t n_io); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.c b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.c new file mode 100644 index 00000000..a742e062 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.c @@ -0,0 +1,757 @@ +/* -*- 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 +*/ + +#include "ts_expr_builder.h" + +#include + +#include "../grn_ctx.h" +#include "../grn_db.h" + +#include "ts_log.h" +#include "ts_util.h" + +/*------------------------------------------------------------- + * grn_ts_expr_bridge. + */ + +/* grn_ts_expr_bridge_init() initializes a bridge. */ +static void +grn_ts_expr_bridge_init(grn_ctx *ctx, grn_ts_expr_bridge *bridge) +{ + memset(bridge, 0, sizeof(*bridge)); + bridge->src_table = NULL; + bridge->dest_table = NULL; +} + +/* grn_ts_expr_bridge_fin() finalizes a bridge. */ +static void +grn_ts_expr_bridge_fin(grn_ctx *ctx, grn_ts_expr_bridge *bridge) +{ + if (bridge->dest_table) { + grn_obj_unlink(ctx, bridge->dest_table); + } + /* Note: bridge->src_table does not increment a reference count. */ +} + +/*------------------------------------------------------------- + * grn_ts_expr_builder. + */ + +/* grn_ts_expr_builder_init() initializes an expression builder. */ +static void +grn_ts_expr_builder_init(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + memset(builder, 0, sizeof(*builder)); + builder->table = NULL; + builder->curr_table = NULL; + builder->nodes = NULL; + builder->bridges = NULL; +} + +/* grn_ts_expr_builder_fin() finalizes an expression builder. */ +static void +grn_ts_expr_builder_fin(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + size_t i; + if (builder->bridges) { + for (i = 0; i < builder->n_bridges; i++) { + grn_ts_expr_bridge_fin(ctx, &builder->bridges[i]); + } + GRN_FREE(builder->bridges); + } + if (builder->nodes) { + for (i = 0; i < builder->n_nodes; i++) { + if (builder->nodes[i]) { + grn_ts_expr_node_close(ctx, builder->nodes[i]); + } + } + GRN_FREE(builder->nodes); + } + /* Note: builder->curr_table does not increment a reference count. */ + if (builder->table) { + grn_obj_unlink(ctx, builder->table); + } +} + +grn_rc +grn_ts_expr_builder_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_builder **builder) +{ + grn_rc rc; + grn_ts_expr_builder *new_builder; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || !builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + new_builder = GRN_MALLOCN(grn_ts_expr_builder, 1); + if (!new_builder) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE, + sizeof(grn_ts_expr_builder)); + } + rc = grn_ts_obj_increment_ref_count(ctx, table); + if (rc != GRN_SUCCESS) { + GRN_FREE(new_builder); + return rc; + } + grn_ts_expr_builder_init(ctx, new_builder); + new_builder->table = table; + new_builder->curr_table = table; + *builder = new_builder; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_builder_close(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + grn_ts_expr_builder_fin(ctx, builder); + GRN_FREE(builder); + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_builder_complete(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_expr **expr) +{ + grn_rc rc; + grn_ts_expr *new_expr; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder || (builder->n_nodes != 1) || builder->n_bridges || !expr) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_node_deref(ctx, &builder->nodes[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_open(ctx, builder->table, builder->nodes[0], &new_expr); + if (rc != GRN_SUCCESS) { + return rc; + } + builder->n_nodes = 0; + *expr = new_expr; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_builder_clear(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + size_t i; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (builder->bridges) { + for (i = 0; i < builder->n_bridges; i++) { + grn_ts_expr_bridge_fin(ctx, &builder->bridges[i]); + } + builder->n_bridges = 0; + } + if (builder->nodes) { + for (i = 0; i < builder->n_nodes; i++) { + if (builder->nodes[i]) { + grn_ts_expr_node_close(ctx, builder->nodes[i]); + } + } + builder->n_nodes = 0; + } + builder->curr_table = builder->table; + return GRN_SUCCESS; +} + +/* + * grn_ts_expr_builder_push_node() pushes a node. + * The given node will be closed on failure. + */ +static grn_rc +grn_ts_expr_builder_push_node(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_expr_node *node) +{ + if (builder->n_nodes == builder->max_n_nodes) { + size_t n_bytes, new_max_n_nodes; + grn_ts_expr_node **new_nodes; + new_max_n_nodes = builder->n_nodes ? (builder->n_nodes * 2) : 1; + n_bytes = sizeof(grn_ts_expr_node *) * new_max_n_nodes; + new_nodes = (grn_ts_expr_node **)GRN_REALLOC(builder->nodes, n_bytes); + if (!new_nodes) { + grn_ts_expr_node_close(ctx, node); + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, n_bytes); + } + builder->nodes = new_nodes; + builder->max_n_nodes = new_max_n_nodes; + } + builder->nodes[builder->n_nodes++] = node; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_builder_push_name(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_str name) +{ + grn_obj *column; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder || !grn_ts_str_is_name(name)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (grn_ts_str_is_id_name(name)) { + return grn_ts_expr_builder_push_id(ctx, builder); + } + if (grn_ts_str_is_score_name(name)) { + return grn_ts_expr_builder_push_score(ctx, builder); + } + if (grn_ts_str_is_key_name(name)) { + return grn_ts_expr_builder_push_key(ctx, builder); + } + if (grn_ts_str_is_value_name(name)) { + return grn_ts_expr_builder_push_value(ctx, builder); + } + /* grn_obj_column() returns a column or accessor. */ + column = grn_obj_column(ctx, builder->curr_table, name.ptr, name.size); + if (!column) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "object not found: \"%.*s\"", + (int)name.size, name.ptr); + } + return grn_ts_expr_builder_push_obj(ctx, builder, column); +} + +#define GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(TYPE, KIND, kind)\ + case GRN_DB_ ## TYPE: {\ + value.as_ ## kind = (grn_ts_ ## kind)GRN_ ## TYPE ## _VALUE(obj);\ + return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\ + obj->header.domain, value);\ + } +/* grn_ts_expr_push_builder_bulk() pushes a scalar const. */ +static grn_rc +grn_ts_expr_builder_push_bulk(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_obj *obj) +{ + grn_ts_any value; + switch (obj->header.domain) { + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(BOOL, BOOL, bool) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT8, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT16, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT32, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(INT64, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT8, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT16, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT32, INT, int) + /* The behavior is undefined if a value is greater than 2^63 - 1. */ + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(UINT64, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(FLOAT, FLOAT, float) + GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE(TIME, TIME, time) + case GRN_DB_SHORT_TEXT: + case GRN_DB_TEXT: + case GRN_DB_LONG_TEXT: { + value.as_text.ptr = GRN_TEXT_VALUE(obj); + value.as_text.size = GRN_TEXT_LEN(obj); + return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_TEXT, + obj->header.domain, value); + } + case GRN_DB_TOKYO_GEO_POINT: + case GRN_DB_WGS84_GEO_POINT: { + GRN_GEO_POINT_VALUE(obj, value.as_geo.latitude, value.as_geo.longitude); + return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_GEO, + obj->header.domain, value); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "not bulk"); + } + } +} +#undef GRN_TS_EXPR_BUILDER_PUSH_BULK_CASE + +#define GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(TYPE, KIND, kind)\ + case GRN_DB_ ## TYPE: {\ + value.as_ ## kind ## _vector.ptr = (grn_ts_ ## kind *)GRN_BULK_HEAD(obj);\ + value.as_ ## kind ## _vector.size = grn_uvector_size(ctx, obj);\ + return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\ + obj->header.domain, value);\ + } +#define GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(TYPE, KIND, kind)\ + case GRN_DB_ ## TYPE: {\ + size_t i;\ + grn_rc rc;\ + grn_ts_ ## kind *buf;\ + grn_ts_ ## kind ## _vector vector = { NULL, grn_uvector_size(ctx, obj) };\ + if (!vector.size) {\ + value.as_ ## kind ## _vector = vector;\ + return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\ + obj->header.domain, value);\ + }\ + buf = GRN_MALLOCN(grn_ts_ ## kind, vector.size);\ + if (!buf) {\ + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,\ + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",\ + sizeof(grn_ts_ ## kind));\ + }\ + for (i = 0; i < vector.size; i++) {\ + buf[i] = GRN_ ## TYPE ##_VALUE_AT(obj, i);\ + }\ + vector.ptr = buf;\ + value.as_ ## kind ## _vector = vector;\ + rc = grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_ ## KIND,\ + obj->header.domain, value);\ + GRN_FREE(buf);\ + return rc;\ + } +/* grn_ts_expr_builder_push_uvector() pushes an array of fixed-size values. */ +static grn_rc +grn_ts_expr_builder_push_uvector(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_obj *obj) +{ + grn_ts_any value; + switch (obj->header.domain) { + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(BOOL, BOOL, bool) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT8, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT16, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(INT32, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(INT64, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT8, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT16, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST(UINT32, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(UINT64, INT, int) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(TIME, TIME, time) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(TOKYO_GEO_POINT, GEO, geo) + GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE(WGS84_GEO_POINT, GEO, geo) + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data type: %d", + obj->header.domain); + } + } +} +#undef GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE_WITH_TYPECAST +#undef GRN_TS_EXPR_BUILDER_PUSH_UVECTOR_CASE + +/* grn_ts_expr_builder_push_vector() pushes a Text vector. */ +static grn_rc +grn_ts_expr_builder_push_vector(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_obj *obj) +{ + switch (obj->header.domain) { + case GRN_DB_SHORT_TEXT: + case GRN_DB_TEXT: + case GRN_DB_LONG_TEXT: { + size_t i; + grn_rc rc; + grn_ts_any value; + grn_ts_text *buf; + grn_ts_text_vector vector = { NULL, grn_vector_size(ctx, obj) }; + if (!vector.size) { + value.as_text_vector = vector; + return grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_TEXT_VECTOR, + obj->header.domain, value); + } + buf = GRN_MALLOCN(grn_ts_text, vector.size); + if (!buf) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: " + "%" GRN_FMT_SIZE " x %" GRN_FMT_SIZE, + sizeof(grn_ts_text), vector.size); + } + for (i = 0; i < vector.size; i++) { + buf[i].size = grn_vector_get_element(ctx, obj, i, &buf[i].ptr, + NULL, NULL); + } + vector.ptr = buf; + value.as_text_vector = vector; + rc = grn_ts_expr_builder_push_const(ctx, builder, GRN_TS_TEXT_VECTOR, + obj->header.domain, value); + GRN_FREE(buf); + return rc; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data type: %d", + obj->header.domain); + } + } +} + +static grn_rc +grn_ts_expr_builder_push_single_accessor(grn_ctx *ctx, + grn_ts_expr_builder *builder, + grn_accessor *accessor) +{ + switch (accessor->action) { + case GRN_ACCESSOR_GET_ID: { + return grn_ts_expr_builder_push_id(ctx, builder); + } + case GRN_ACCESSOR_GET_SCORE: { + return grn_ts_expr_builder_push_score(ctx, builder); + } + case GRN_ACCESSOR_GET_KEY: { + if (accessor->obj != builder->curr_table) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table conflict"); + } + return grn_ts_expr_builder_push_key(ctx, builder); + } + case GRN_ACCESSOR_GET_VALUE: { + if (accessor->obj != builder->curr_table) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table conflict"); + } + return grn_ts_expr_builder_push_value(ctx, builder); + } + case GRN_ACCESSOR_GET_COLUMN_VALUE: { + return grn_ts_expr_builder_push_column(ctx, builder, accessor->obj); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid accessor action: %d", + accessor->action); + } + } +} + +static grn_rc +grn_ts_expr_builder_push_accessor(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_accessor *accessor) +{ + grn_rc rc = grn_ts_expr_builder_push_single_accessor(ctx, builder, accessor); + if (rc != GRN_SUCCESS) { + return rc; + } + for (accessor = accessor->next; accessor; accessor = accessor->next) { + rc = grn_ts_expr_builder_begin_subexpr(ctx, builder); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_builder_push_single_accessor(ctx, builder, accessor); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_builder_end_subexpr(ctx, builder); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_builder_push_obj(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_obj *obj) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder || !obj) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + switch (obj->header.type) { + case GRN_BULK: { + return grn_ts_expr_builder_push_bulk(ctx, builder, obj); + } + case GRN_UVECTOR: { + return grn_ts_expr_builder_push_uvector(ctx, builder, obj); + } + case GRN_VECTOR: { + return grn_ts_expr_builder_push_vector(ctx, builder, obj); + } + case GRN_ACCESSOR: { + return grn_ts_expr_builder_push_accessor(ctx, builder, + (grn_accessor *)obj); + } + case GRN_COLUMN_FIX_SIZE: + case GRN_COLUMN_VAR_SIZE: { + return grn_ts_expr_builder_push_column(ctx, builder, obj); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid object type: %d", + obj->header.type); + } + } +} + +grn_rc +grn_ts_expr_builder_push_id(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_rc rc; + grn_ts_expr_node *node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_id_node_open(ctx, &node); + if (rc == GRN_SUCCESS) { + rc = grn_ts_expr_builder_push_node(ctx, builder, node); + } + return rc; +} + +grn_rc +grn_ts_expr_builder_push_score(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_rc rc; + grn_ts_expr_node *node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_score_node_open(ctx, &node); + if (rc == GRN_SUCCESS) { + rc = grn_ts_expr_builder_push_node(ctx, builder, node); + } + return rc; +} + +grn_rc +grn_ts_expr_builder_push_key(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_rc rc; + grn_ts_expr_node *node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_key_node_open(ctx, builder->curr_table, &node); + if (rc == GRN_SUCCESS) { + rc = grn_ts_expr_builder_push_node(ctx, builder, node); + } + return rc; +} + +grn_rc +grn_ts_expr_builder_push_value(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_rc rc; + grn_ts_expr_node *node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_value_node_open(ctx, builder->curr_table, &node); + if (rc == GRN_SUCCESS) { + rc = grn_ts_expr_builder_push_node(ctx, builder, node); + } + return rc; +} + +grn_rc +grn_ts_expr_builder_push_const(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_data_kind kind, grn_ts_data_type type, + grn_ts_any value) +{ + grn_rc rc; + grn_ts_expr_node *node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_const_node_open(ctx, kind, type, value, &node); + if (rc == GRN_SUCCESS) { + rc = grn_ts_expr_builder_push_node(ctx, builder, node); + } + return rc; +} + +grn_rc +grn_ts_expr_builder_push_column(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_obj *column) +{ + grn_rc rc; + grn_ts_expr_node *node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder || !column || !grn_ts_obj_is_column(ctx, column) || + (DB_OBJ(builder->curr_table)->id != column->header.domain)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_column_node_open(ctx, column, &node); + if (rc == GRN_SUCCESS) { + rc = grn_ts_expr_builder_push_node(ctx, builder, node); + } + return rc; +} + +/* + * grn_ts_expr_builder_get_max_n_args() returns the number of nodes in the + * current subexpression. + */ +static size_t +grn_ts_expr_builder_get_max_n_args(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + size_t max_n_args = builder->n_nodes; + if (builder->n_bridges) { + max_n_args -= builder->bridges[builder->n_bridges - 1].n_nodes; + } + return max_n_args; +} + +grn_rc +grn_ts_expr_builder_push_op(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_op_type op_type) +{ + grn_rc rc; + grn_ts_expr_node **args, *node; + size_t n_args, max_n_args; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + n_args = grn_ts_op_get_n_args(op_type); + if (!n_args) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "invalid #arguments: %" GRN_FMT_SIZE, + n_args); + } + max_n_args = grn_ts_expr_builder_get_max_n_args(ctx, builder); + if (n_args > max_n_args) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "invalid #arguments: %" GRN_FMT_SIZE ", %" GRN_FMT_SIZE, + n_args, builder->n_nodes); + } + /* Arguments are the top n_args nodes in the stack. */ + args = &builder->nodes[builder->n_nodes - n_args]; + builder->n_nodes -= n_args; + rc = grn_ts_expr_op_node_open(ctx, op_type, args, n_args, &node); + if (rc == GRN_SUCCESS) { + builder->nodes[builder->n_nodes++] = node; + } + return rc; +} + +/* grn_ts_expr_builder_push_bridge() pushes a bridge. */ +static grn_rc +grn_ts_expr_builder_push_bridge(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_expr_bridge *bridge) +{ + if (builder->n_bridges == builder->max_n_bridges) { + size_t n_bytes, new_max_n_bridges; + grn_ts_expr_bridge *new_bridges; + new_max_n_bridges = builder->n_bridges ? (builder->n_bridges * 2) : 1; + n_bytes = sizeof(grn_ts_expr_bridge) * new_max_n_bridges; + new_bridges = (grn_ts_expr_bridge *)GRN_REALLOC(builder->bridges, n_bytes); + if (!new_bridges) { + grn_ts_expr_bridge_fin(ctx, bridge); + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, n_bytes); + } + builder->bridges = new_bridges; + builder->max_n_bridges = new_max_n_bridges; + } + builder->bridges[builder->n_bridges++] = *bridge; + builder->curr_table = bridge->dest_table; + return GRN_SUCCESS; +} + +/* grn_ts_expr_builder_pop_bridge() pops a bridge. */ +static void +grn_ts_expr_builder_pop_bridge(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_ts_expr_bridge *bridge = &builder->bridges[builder->n_bridges - 1]; + builder->curr_table = bridge->src_table; + grn_ts_expr_bridge_fin(ctx, bridge); + builder->n_bridges--; +} + +grn_rc +grn_ts_expr_builder_begin_subexpr(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_rc rc; + size_t max_n_args; + grn_obj *obj; + grn_ts_expr_node *node; + grn_ts_expr_bridge bridge; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + max_n_args = grn_ts_expr_builder_get_max_n_args(ctx, builder); + if (!max_n_args) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + /* Check whehter or not the latest node refers to a table. */ + node = builder->nodes[builder->n_nodes - 1]; + if ((node->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->data_kind); + } + obj = grn_ctx_at(ctx, node->data_type); + if (!obj) { + GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d", + node->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", node->data_type); + } + /* Creates a bridge to a subexpression. */ + grn_ts_expr_bridge_init(ctx, &bridge); + bridge.src_table = builder->curr_table; + bridge.dest_table = obj; + bridge.n_nodes = builder->n_nodes; + rc = grn_ts_expr_builder_push_bridge(ctx, builder, &bridge); + if (rc != GRN_SUCCESS) { + grn_obj_unlink(ctx, obj); + return rc; + } + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_builder_end_subexpr(grn_ctx *ctx, grn_ts_expr_builder *builder) +{ + grn_rc rc; + grn_ts_expr_node **args, *node; + if (!ctx || !builder || (builder->n_nodes < 2) || !builder->n_bridges) { + return GRN_INVALID_ARGUMENT; + } + /* Check whehter or not the subexpression is complete.*/ + if (grn_ts_expr_builder_get_max_n_args(ctx, builder) != 1) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + /* Creates a bridge node. */ + args = &builder->nodes[builder->n_nodes - 2]; + rc = grn_ts_expr_bridge_node_open(ctx, args[0], args[1], &node); + if (rc != GRN_SUCCESS) { + return rc; + } + /* Note: The following grn_ts_expr_push_node() must not fail. */ + builder->n_nodes -= 2; + grn_ts_expr_builder_push_node(ctx, builder, node); + grn_ts_expr_builder_pop_bridge(ctx, builder); + return GRN_SUCCESS; +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.h b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.h new file mode 100644 index 00000000..f9795ed1 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_builder.h @@ -0,0 +1,128 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_buf.h" +#include "ts_expr.h" +#include "ts_expr_node.h" +#include "ts_op.h" +#include "ts_str.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + grn_obj *src_table; /* The source table of a bridge (no ref. count). */ + grn_obj *dest_table; /* The destination table of a bridge. */ + size_t n_nodes; /* The stack depth (position) of a bridge. */ +} grn_ts_expr_bridge; + +typedef struct { + grn_obj *table; /* Associated table. */ + grn_obj *curr_table; /* Current table (no ref. count). */ + grn_ts_expr_node **nodes; /* Node stack. */ + size_t n_nodes; /* Number of nodes (stack depth). */ + size_t max_n_nodes; /* Maximum number of nodes (stack capacity). */ + grn_ts_expr_bridge *bridges; /* Bridges to subexpressions. */ + size_t n_bridges; /* Number of bridges (subexpression depth). */ + size_t max_n_bridges; /* Max. number (capacity) of bridges. */ +} grn_ts_expr_builder; + +/* grn_ts_expr_builder_open() creates an expression builder. */ +grn_rc grn_ts_expr_builder_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_builder **builder); + +/* grn_ts_expr_builder_close() destroys an expression builder. */ +grn_rc grn_ts_expr_builder_close(grn_ctx *ctx, grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_complete() completes an expression. */ +grn_rc grn_ts_expr_builder_complete(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_expr **expr); + +/* grn_ts_expr_builder_clear() clears the internal states. */ +grn_rc grn_ts_expr_builder_clear(grn_ctx *ctx, grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_push_name() pushes a named object. */ +grn_rc grn_ts_expr_builder_push_name(grn_ctx *ctx, + grn_ts_expr_builder *builder, + grn_ts_str name); + +/* + * grn_ts_expr_builder_push_obj() pushes an object. + * + * Acceptable objects are as follows: + * - Consts + * - GRN_BULK: GRN_DB_*. + * - GRN_UVECTOR: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT. + * - GRN_VECTOR: GRN_DB_[SHORT/LONG_]TEXT. + * - Columns + * - GRN_ACCESSOR: _id, _score, _key, _value, and columns. + * - GRN_COLUMN_FIX_SIZE: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT. + * - GRN_COLUMN_VAR_SIZE: GRN_DB_[SHORT/LONG_]TEXT. + */ +grn_rc grn_ts_expr_builder_push_obj(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_obj *obj); + +/* grn_ts_expr_builder_push_id() pushes "_id". */ +grn_rc grn_ts_expr_builder_push_id(grn_ctx *ctx, grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_push_score() pushes "_score". */ +grn_rc grn_ts_expr_builder_push_score(grn_ctx *ctx, + grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_push_key() pushes "_key". */ +grn_rc grn_ts_expr_builder_push_key(grn_ctx *ctx, + grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_push_value() pushes "_value". */ +grn_rc grn_ts_expr_builder_push_value(grn_ctx *ctx, + grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_push_const() pushes a const. */ +grn_rc grn_ts_expr_builder_push_const(grn_ctx *ctx, + grn_ts_expr_builder *builder, + grn_ts_data_kind kind, + grn_ts_data_type type, + grn_ts_any value); + +/* grn_ts_expr_builder_push_column() pushes a column. */ +grn_rc grn_ts_expr_builder_push_column(grn_ctx *ctx, + grn_ts_expr_builder *builder, + grn_obj *column); + +/* grn_ts_expr_builder_push_op() pushes an operator. */ +grn_rc grn_ts_expr_builder_push_op(grn_ctx *ctx, grn_ts_expr_builder *builder, + grn_ts_op_type op_type); + +/* grn_ts_expr_builder_begin_subexpr() begins a subexpression. */ +grn_rc grn_ts_expr_builder_begin_subexpr(grn_ctx *ctx, + grn_ts_expr_builder *builder); + +/* grn_ts_expr_builder_end_subexpr() ends a subexpression. */ +grn_rc grn_ts_expr_builder_end_subexpr(grn_ctx *ctx, + grn_ts_expr_builder *builder); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.c b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.c new file mode 100644 index 00000000..4ae90003 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.c @@ -0,0 +1,5325 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#include "ts_expr_node.h" + +#include +#include + +#include "../grn_ctx.h" +#include "../grn_dat.h" +#include "../grn_db.h" +#include "../grn_geo.h" +#include "../grn_hash.h" +#include "../grn_pat.h" +#include "../grn_store.h" + +#include "ts_log.h" +#include "ts_str.h" +#include "ts_util.h" + +/*------------------------------------------------------------- + * Built-in data kinds. + */ + +/* grn_ts_bool_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_bool_is_valid(grn_ts_bool value) +{ + return GRN_TRUE; +} + +/* grn_ts_int_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_int_is_valid(grn_ts_int value) +{ + return GRN_TRUE; +} + +/* grn_ts_float_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_float_is_valid(grn_ts_float value) +{ + return isfinite(value); +} + +/* grn_ts_time_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_time_is_valid(grn_ts_time value) +{ + return GRN_TRUE; +} + +/* grn_ts_text_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_text_is_valid(grn_ts_text value) +{ + return value.ptr || !value.size; +} + +/* grn_ts_geo_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_geo_is_valid(grn_ts_geo value) +{ + return ((value.latitude >= GRN_GEO_MIN_LATITUDE) && + (value.latitude <= GRN_GEO_MAX_LATITUDE)) && + ((value.longitude >= GRN_GEO_MIN_LONGITUDE) && + (value.longitude <= GRN_GEO_MAX_LONGITUDE)); +} + +#define GRN_TS_VECTOR_IS_VALID(type)\ + if (value.size) {\ + size_t i;\ + if (!value.ptr) {\ + return GRN_FALSE;\ + }\ + for (i = 0; i < value.size; i++) {\ + if (!grn_ts_ ## type ## _is_valid(value.ptr[i])) {\ + return GRN_FALSE;\ + }\ + }\ + }\ + return GRN_TRUE; +/* grn_ts_bool_vector_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_bool_vector_is_valid(grn_ts_bool_vector value) +{ + GRN_TS_VECTOR_IS_VALID(bool) +} + +/* grn_ts_int_vector_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_int_vector_is_valid(grn_ts_int_vector value) +{ + GRN_TS_VECTOR_IS_VALID(int) +} + +/* grn_ts_float_vector_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_float_vector_is_valid(grn_ts_float_vector value) +{ + GRN_TS_VECTOR_IS_VALID(float) +} + +/* grn_ts_time_vector_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_time_vector_is_valid(grn_ts_time_vector value) +{ + GRN_TS_VECTOR_IS_VALID(time) +} + +/* grn_ts_text_vector_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_text_vector_is_valid(grn_ts_text_vector value) +{ + GRN_TS_VECTOR_IS_VALID(text) +} + +/* grn_ts_geo_vector_is_valid() returns whether a value is valid or not. */ +inline static grn_ts_bool +grn_ts_geo_vector_is_valid(grn_ts_geo_vector value) +{ + GRN_TS_VECTOR_IS_VALID(geo) +} +#undef GRN_TS_VECTOR_IS_VALID + +/* grn_ts_bool_zero() returns a zero. */ +inline static grn_ts_bool +grn_ts_bool_zero(void) +{ + return GRN_FALSE; +} + +/* grn_ts_int_zero() returns a zero. */ +inline static grn_ts_int +grn_ts_int_zero(void) +{ + return 0; +} + +/* grn_ts_float_zero() returns a zero. */ +inline static grn_ts_float +grn_ts_float_zero(void) +{ + return 0.0; +} + +/* grn_ts_time_zero() returns a zero. */ +inline static grn_ts_time +grn_ts_time_zero(void) +{ + return 0; +} + +/* grn_ts_text_zero() returns a zero. */ +inline static grn_ts_text +grn_ts_text_zero(void) +{ + return (grn_ts_text){ NULL, 0 }; +} + +/* grn_ts_geo_zero() returns a zero. */ +inline static grn_ts_geo +grn_ts_geo_zero(void) +{ + return (grn_ts_geo){ 0, 0 }; +} + +/* grn_ts_ref_zero() returns a zero. */ +inline static grn_ts_ref +grn_ts_ref_zero(void) +{ + return (grn_ts_ref){ 0, 0.0 }; +} + +/* grn_ts_data_type_to_kind() returns a kind associated with a type. */ +static grn_ts_data_kind +grn_ts_data_type_to_kind(grn_ts_data_type type) +{ + switch (type) { + case GRN_DB_VOID: { + return GRN_TS_VOID; + } + case GRN_DB_BOOL: { + return GRN_TS_BOOL; + } + case GRN_DB_INT8: + case GRN_DB_INT16: + case GRN_DB_INT32: + case GRN_DB_INT64: + case GRN_DB_UINT8: + case GRN_DB_UINT16: + case GRN_DB_UINT32: + case GRN_DB_UINT64: { + return GRN_TS_INT; + } + case GRN_DB_FLOAT: { + return GRN_TS_FLOAT; + } + case GRN_DB_TIME: { + return GRN_TS_TIME; + } + case GRN_DB_SHORT_TEXT: + case GRN_DB_TEXT: + case GRN_DB_LONG_TEXT: { + return GRN_TS_TEXT; + } + case GRN_DB_TOKYO_GEO_POINT: + case GRN_DB_WGS84_GEO_POINT: { + return GRN_TS_GEO; + } + default: { + return GRN_TS_REF; + } + } +} + +/* grn_ts_data_kind_to_type() returns a type associated with a kind. */ +static grn_ts_data_type +grn_ts_data_kind_to_type(grn_ts_data_kind kind) +{ + switch (kind & ~GRN_TS_VECTOR_FLAG) { + case GRN_TS_BOOL: { + return GRN_DB_BOOL; + } + case GRN_TS_INT: { + return GRN_DB_INT64; + } + case GRN_TS_FLOAT: { + return GRN_DB_FLOAT; + } + case GRN_TS_TIME: { + return GRN_DB_TIME; + } + case GRN_TS_TEXT: { + return GRN_DB_TEXT; + } + case GRN_TS_GEO: { + /* GRN_DB_TOKYO_GEO_POINT or GRN_DB_WGS84_GEO_POINT. */ + return GRN_DB_VOID; + } + case GRN_TS_REF: { + /* + * grn_ts_data_kind does not have enough information to get a correct + * table ID. + */ + return GRN_DB_VOID; + } + default: { + return GRN_DB_VOID; + } + } +} + +/*------------------------------------------------------------- + * Operators. + */ + +/* grn_ts_op_logical_not_bool() returns !arg. */ +inline static grn_ts_bool +grn_ts_op_logical_not_bool(grn_ts_bool arg) +{ + return !arg; +} + +/* grn_ts_op_bitwise_not_bool() returns ~arg. */ +inline static grn_ts_bool +grn_ts_op_bitwise_not_bool(grn_ts_bool arg) +{ + return !arg; +} + +/* grn_ts_op_bitwise_not_int() returns ~arg. */ +inline static grn_ts_int +grn_ts_op_bitwise_not_int(grn_ts_int arg) +{ + return ~arg; +} + +/* grn_ts_op_positive_int() returns +arg. */ +inline static grn_ts_int +grn_ts_op_positive_int(grn_ts_int arg) +{ + return arg; +} + +/* grn_ts_op_positive_float() returns +arg. */ +inline static grn_ts_float +grn_ts_op_positive_float(grn_ts_float arg) +{ + return arg; +} + +/* grn_ts_op_negative_int() returns -arg. */ +inline static grn_ts_int +grn_ts_op_negative_int(grn_ts_int arg) +{ + return -arg; +} + +/* grn_ts_op_negative_float() returns -arg. */ +inline static grn_ts_float +grn_ts_op_negative_float(grn_ts_float arg) +{ + return -arg; +} + +/* grn_ts_op_float() returns (Float)arg. */ +static grn_rc +grn_ts_op_float(grn_ctx *ctx, grn_ts_int arg, grn_ts_float *out) +{ + *out = (grn_ts_float)arg; + return GRN_SUCCESS; +} + +/* grn_ts_op_time() returns (Time)arg. */ +static grn_rc +grn_ts_op_time(grn_ctx *ctx, grn_ts_text arg, grn_ts_time *out) +{ + grn_timeval value; + grn_rc rc = grn_str2timeval(arg.ptr, arg.size, &value); + if (rc != GRN_SUCCESS) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "grn_str2timeval failed"); + } + *out = (grn_ts_time)((value.tv_sec * 1000000) + (value.tv_nsec / 1000)); + return GRN_SUCCESS; +} + +/* grn_ts_op_bitwise_and_bool() returns lhs & rhs. */ +inline static grn_ts_bool +grn_ts_op_bitwise_and_bool(grn_ts_bool lhs, grn_ts_bool rhs) +{ + return lhs & rhs; +} + +/* grn_ts_op_bitwise_and_int() returns lhs & rhs. */ +inline static grn_ts_int +grn_ts_op_bitwise_and_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs & rhs; +} + +/* grn_ts_op_bitwise_or_bool() returns lhs | rhs. */ +inline static grn_ts_bool +grn_ts_op_bitwise_or_bool(grn_ts_bool lhs, grn_ts_bool rhs) +{ + return lhs | rhs; +} + +/* grn_ts_op_bitwise_or_int() returns lhs | rhs. */ +inline static grn_ts_int +grn_ts_op_bitwise_or_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs | rhs; +} + +/* grn_ts_op_bitwise_xor_bool() returns lhs ^ rhs. */ +inline static grn_ts_bool +grn_ts_op_bitwise_xor_bool(grn_ts_bool lhs, grn_ts_bool rhs) +{ + return lhs ^ rhs; +} + +/* grn_ts_op_bitwise_xor_int() returns lhs ^ rhs. */ +inline static grn_ts_int +grn_ts_op_bitwise_xor_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs ^ rhs; +} + +/* grn_ts_op_equal_bool() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_bool(grn_ts_bool lhs, grn_ts_bool rhs) +{ + return lhs == rhs; +} + +/* grn_ts_op_equal_int() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs == rhs; +} + +/* grn_ts_op_equal_float() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_float(grn_ts_float lhs, grn_ts_float rhs) +{ + /* To suppress warnings, "lhs == rhs" is not used. */ + return (lhs <= rhs) && (lhs >= rhs); +} + +/* grn_ts_op_equal_time() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_time(grn_ts_time lhs, grn_ts_time rhs) +{ + return lhs == rhs; +} + +/* grn_ts_op_equal_text() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_text(grn_ts_text lhs, grn_ts_text rhs) +{ + return (lhs.size == rhs.size) && !memcmp(lhs.ptr, rhs.ptr, lhs.size); +} + +/* grn_ts_op_equal_geo() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_geo(grn_ts_geo lhs, grn_ts_geo rhs) +{ + return (lhs.latitude == rhs.latitude) && (lhs.longitude == rhs.longitude); +} + +/* grn_ts_op_equal_ref() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_ref(grn_ts_ref lhs, grn_ts_ref rhs) +{ + /* Ignore scores. */ + return lhs.id == rhs.id; +} + +#define GRN_TS_OP_EQUAL_VECTOR(kind)\ + size_t i;\ + if (lhs.size != rhs.size) {\ + return GRN_FALSE;\ + }\ + for (i = 0; i < lhs.size; i++) {\ + if (!grn_ts_op_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + return GRN_FALSE;\ + }\ + }\ + return GRN_TRUE; +/* grn_ts_op_equal_bool_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_bool_vector(grn_ts_bool_vector lhs, grn_ts_bool_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(bool) +} + +/* grn_ts_op_equal_int_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(int) +} + +/* grn_ts_op_equal_float_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_float_vector(grn_ts_float_vector lhs, grn_ts_float_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(float) +} + +/* grn_ts_op_equal_time_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(time) +} + +/* grn_ts_op_equal_text_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(text) +} + +/* grn_ts_op_equal_geo_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_geo_vector(grn_ts_geo_vector lhs, grn_ts_geo_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(geo) +} + +/* grn_ts_op_equal_ref_vector() returns lhs == rhs. */ +inline static grn_ts_bool +grn_ts_op_equal_ref_vector(grn_ts_ref_vector lhs, grn_ts_ref_vector rhs) +{ + GRN_TS_OP_EQUAL_VECTOR(ref) +} +#undef GRN_TS_OP_EQUAL_VECTOR + +/* grn_ts_op_not_equal_bool() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_bool(grn_ts_bool lhs, grn_ts_bool rhs) +{ + return lhs != rhs; +} + +/* grn_ts_op_not_equal_int() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs != rhs; +} + +/* grn_ts_op_not_equal_float() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_float(grn_ts_float lhs, grn_ts_float rhs) +{ + /* To suppress warnings, "lhs != rhs" is not used. */ + return !grn_ts_op_equal_float(lhs, rhs); +} + +/* grn_ts_op_not_equal_time() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_time(grn_ts_time lhs, grn_ts_time rhs) +{ + return lhs != rhs; +} + +/* grn_ts_op_not_equal_text() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_text(grn_ts_text lhs, grn_ts_text rhs) +{ + return (lhs.size != rhs.size) || memcmp(lhs.ptr, rhs.ptr, lhs.size); +} + +/* grn_ts_op_not_equal_geo() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_geo(grn_ts_geo lhs, grn_ts_geo rhs) +{ + return (lhs.latitude != rhs.latitude) || (lhs.longitude != rhs.longitude); +} + +/* grn_ts_op_not_equal_ref() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_ref(grn_ts_ref lhs, grn_ts_ref rhs) +{ + /* Ignore scores. */ + return lhs.id != rhs.id; +} + +#define GRN_TS_OP_NOT_EQUAL_VECTOR(kind)\ + size_t i;\ + if (lhs.size != rhs.size) {\ + return GRN_TRUE;\ + }\ + for (i = 0; i < lhs.size; i++) {\ + if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + return GRN_TRUE;\ + }\ + }\ + return GRN_FALSE; +/* grn_ts_op_not_equal_bool_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_bool_vector(grn_ts_bool_vector lhs, grn_ts_bool_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(bool) +} + +/* grn_ts_op_not_equal_int_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(int) +} + +/* grn_ts_op_not_equal_float_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_float_vector(grn_ts_float_vector lhs, + grn_ts_float_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(float) +} + +/* grn_ts_op_not_equal_time_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(time) +} + +/* grn_ts_op_not_equal_text_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(text) +} + +/* grn_ts_op_not_equal_geo_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_geo_vector(grn_ts_geo_vector lhs, grn_ts_geo_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(geo) +} + +/* grn_ts_op_not_equal_ref_vector() returns lhs != rhs. */ +inline static grn_ts_bool +grn_ts_op_not_equal_ref_vector(grn_ts_ref_vector lhs, grn_ts_ref_vector rhs) +{ + GRN_TS_OP_NOT_EQUAL_VECTOR(ref) +} +#undef GRN_TS_OP_NOT_EQUAL_VECTOR + +/* grn_ts_op_less_int() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs < rhs; +} + +/* grn_ts_op_less_float() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_float(grn_ts_float lhs, grn_ts_float rhs) +{ + return lhs < rhs; +} + +/* grn_ts_op_less_time() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_time(grn_ts_time lhs, grn_ts_time rhs) +{ + return lhs < rhs; +} + +/* grn_ts_op_less_text() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_text(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size; + int cmp = memcmp(lhs.ptr, rhs.ptr, min_size); + return cmp ? (cmp < 0) : (lhs.size < rhs.size); +} + +#define GRN_TS_OP_LESS_VECTOR(kind)\ + size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\ + for (i = 0; i < min_size; i++) {\ + if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + if (grn_ts_op_less_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + return GRN_TRUE;\ + }\ + }\ + }\ + return lhs.size < rhs.size; +/* grn_ts_op_less_int_vector() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs) +{ + GRN_TS_OP_LESS_VECTOR(int) +} + +/* grn_ts_op_less_float_vector() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_float_vector(grn_ts_float_vector lhs, grn_ts_float_vector rhs) +{ + GRN_TS_OP_LESS_VECTOR(float) +} + +/* grn_ts_op_less_time_vector() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs) +{ + GRN_TS_OP_LESS_VECTOR(time) +} + +/* grn_ts_op_less_text_vector() returns lhs < rhs. */ +inline static grn_ts_bool +grn_ts_op_less_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs) +{ + GRN_TS_OP_LESS_VECTOR(text) +} +#undef GRN_TS_OP_LESS_VECTOR + +/* grn_ts_op_less_equal_int() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs <= rhs; +} + +/* grn_ts_op_less_equal_float() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_float(grn_ts_float lhs, grn_ts_float rhs) +{ + return lhs <= rhs; +} + +/* grn_ts_op_less_equal_time() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_time(grn_ts_time lhs, grn_ts_time rhs) +{ + return lhs <= rhs; +} + +/* grn_ts_op_less_equal_text() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_text(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size; + int cmp = memcmp(lhs.ptr, rhs.ptr, min_size); + return cmp ? (cmp < 0) : (lhs.size <= rhs.size); +} + +#define GRN_TS_OP_LESS_EQUAL_VECTOR(kind)\ + size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\ + for (i = 0; i < min_size; i++) {\ + if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + if (grn_ts_op_less_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + return GRN_TRUE;\ + }\ + }\ + }\ + return lhs.size <= rhs.size; +/* grn_ts_op_less_equal_int_vector() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs) +{ + GRN_TS_OP_LESS_EQUAL_VECTOR(int) +} + +/* grn_ts_op_less_equal_float_vector() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_float_vector(grn_ts_float_vector lhs, + grn_ts_float_vector rhs) +{ + GRN_TS_OP_LESS_EQUAL_VECTOR(float) +} + +/* grn_ts_op_less_equal_time_vector() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_time_vector(grn_ts_time_vector lhs, + grn_ts_time_vector rhs) +{ + GRN_TS_OP_LESS_EQUAL_VECTOR(time) +} + +/* grn_ts_op_less_equal_text_vector() returns lhs <= rhs. */ +inline static grn_ts_bool +grn_ts_op_less_equal_text_vector(grn_ts_text_vector lhs, + grn_ts_text_vector rhs) +{ + GRN_TS_OP_LESS_EQUAL_VECTOR(text) +} +#undef GRN_TS_OP_LESS_EQUAL_VECTOR + +/* grn_ts_op_greater_int() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs > rhs; +} + +/* grn_ts_op_greater_float() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_float(grn_ts_float lhs, grn_ts_float rhs) +{ + return lhs > rhs; +} + +/* grn_ts_op_greater_time() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_time(grn_ts_time lhs, grn_ts_time rhs) +{ + return lhs > rhs; +} + +/* grn_ts_op_greater_text() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_text(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size; + int cmp = memcmp(lhs.ptr, rhs.ptr, min_size); + return cmp ? (cmp > 0) : (lhs.size > rhs.size); +} + +#define GRN_TS_OP_GREATER_VECTOR(kind)\ + size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\ + for (i = 0; i < min_size; i++) {\ + if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + if (grn_ts_op_greater_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + return GRN_TRUE;\ + }\ + }\ + }\ + return lhs.size > rhs.size; +/* grn_ts_op_greater_int_vector() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_int_vector(grn_ts_int_vector lhs, grn_ts_int_vector rhs) +{ + GRN_TS_OP_GREATER_VECTOR(int) +} + +/* grn_ts_op_greater_float_vector() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_float_vector(grn_ts_float_vector lhs, + grn_ts_float_vector rhs) +{ + GRN_TS_OP_GREATER_VECTOR(float) +} + +/* grn_ts_op_greater_time_vector() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_time_vector(grn_ts_time_vector lhs, grn_ts_time_vector rhs) +{ + GRN_TS_OP_GREATER_VECTOR(time) +} + +/* grn_ts_op_greater_text_vector() returns lhs > rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_text_vector(grn_ts_text_vector lhs, grn_ts_text_vector rhs) +{ + GRN_TS_OP_GREATER_VECTOR(text) +} +#undef GRN_TS_OP_GREATER_VECTOR + +/* grn_ts_op_greater_equal_int() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_int(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs >= rhs; +} + +/* grn_ts_op_greater_equal_float() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_float(grn_ts_float lhs, grn_ts_float rhs) +{ + return lhs >= rhs; +} + +/* grn_ts_op_greater_equal_time() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_time(grn_ts_time lhs, grn_ts_time rhs) +{ + return lhs >= rhs; +} + +/* grn_ts_op_greater_equal_text() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_text(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size; + int cmp = memcmp(lhs.ptr, rhs.ptr, min_size); + return cmp ? (cmp > 0) : (lhs.size >= rhs.size); +} + +#define GRN_TS_OP_GREATER_EQUAL_VECTOR(kind)\ + size_t i, min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;\ + for (i = 0; i < min_size; i++) {\ + if (grn_ts_op_not_equal_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + if (grn_ts_op_greater_ ## kind(lhs.ptr[i], rhs.ptr[i])) {\ + return GRN_TRUE;\ + }\ + }\ + }\ + return lhs.size >= rhs.size; +/* grn_ts_op_greater_equal_int_vector() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_int_vector(grn_ts_int_vector lhs, + grn_ts_int_vector rhs) +{ + GRN_TS_OP_GREATER_EQUAL_VECTOR(int) +} + +/* grn_ts_op_greater_equal_float_vector() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_float_vector(grn_ts_float_vector lhs, + grn_ts_float_vector rhs) +{ + GRN_TS_OP_GREATER_EQUAL_VECTOR(float) +} + +/* grn_ts_op_greater_equal_time_vector() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_time_vector(grn_ts_time_vector lhs, + grn_ts_time_vector rhs) +{ + GRN_TS_OP_GREATER_EQUAL_VECTOR(time) +} + +/* grn_ts_op_greater_equal_text_vector() returns lhs >= rhs. */ +inline static grn_ts_bool +grn_ts_op_greater_equal_text_vector(grn_ts_text_vector lhs, + grn_ts_text_vector rhs) +{ + GRN_TS_OP_GREATER_EQUAL_VECTOR(text) +} +#undef GRN_TS_OP_GREATER_EQUAL_VECTOR + +/* grn_ts_op_shift_arithmetic_left() returns lhs << rhs. */ +inline static grn_ts_int +grn_ts_op_shift_arithmetic_left(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs << rhs; +} + +/* grn_ts_op_shift_arithmetic_right() returns lhs << rhs. */ +inline static grn_ts_int +grn_ts_op_shift_arithmetic_right(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs >> rhs; +} + +/* grn_ts_op_shift_logical_left() returns lhs << rhs. */ +inline static grn_ts_int +grn_ts_op_shift_logical_left(grn_ts_int lhs, grn_ts_int rhs) +{ + return lhs << rhs; +} + +/* grn_ts_op_shift_logical_right() returns lhs << rhs. */ +inline static grn_ts_int +grn_ts_op_shift_logical_right(grn_ts_int lhs, grn_ts_int rhs) +{ + return (uint64_t)lhs >> rhs; +} + +inline static grn_rc +grn_ts_op_plus_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs, + grn_ts_int *out) +{ + *out = lhs + rhs; + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_plus_float_float(grn_ctx *ctx, grn_ts_float lhs, grn_ts_float rhs, + grn_ts_float *out) +{ + *out = lhs + rhs; + if (!grn_ts_float_is_valid(*out)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g + %g = %g", lhs, rhs, *out); + } + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_plus_time_int(grn_ctx *ctx, grn_ts_time lhs, grn_ts_int rhs, + grn_ts_time *out) +{ + *out = lhs + (rhs * 1000000); + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_plus_time_float(grn_ctx *ctx, grn_ts_time lhs, grn_ts_float rhs, + grn_ts_time *out) +{ + *out = (grn_ts_time)(lhs + (rhs * 1000000.0)); + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_minus_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs, + grn_ts_int *out) +{ + *out = lhs - rhs; + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_minus_float_float(grn_ctx *ctx, grn_ts_float lhs, grn_ts_float rhs, + grn_ts_float *out) +{ + *out = lhs - rhs; + if (!grn_ts_float_is_valid(*out)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g - %g = %g", lhs, rhs, *out); + } + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_minus_time_time(grn_ctx *ctx, grn_ts_time lhs, grn_ts_time rhs, + grn_ts_float *out) +{ + *out = (lhs - rhs) * 0.000001; + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_minus_time_int(grn_ctx *ctx, grn_ts_time lhs, grn_ts_int rhs, + grn_ts_time *out) +{ + *out = lhs - (rhs * 1000000); + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_minus_time_float(grn_ctx *ctx, grn_ts_time lhs, grn_ts_float rhs, + grn_ts_time *out) +{ + *out = lhs - (grn_ts_int)(rhs * 1000000.0); + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_multiplication_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs, + grn_ts_int *out) +{ + *out = lhs * rhs; + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_multiplication_float_float(grn_ctx *ctx, grn_ts_float lhs, + grn_ts_float rhs, grn_ts_float *out) +{ + *out = lhs * rhs; + if (!grn_ts_float_is_valid(*out)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g * %g = %g", lhs, rhs, *out); + } + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_division_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs, + grn_ts_int *out) +{ + if (!rhs) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "%" GRN_FMT_INT64D " / %" GRN_FMT_INT64D + " causes division by zero", + lhs, rhs); + } + *out = (rhs != -1) ? (lhs / rhs) : -lhs; + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_division_float_float(grn_ctx *ctx, grn_ts_float lhs, + grn_ts_float rhs, grn_ts_float *out) +{ + *out = lhs / rhs; + if (!grn_ts_float_is_valid(*out)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g / %g = %g", lhs, rhs, *out); + } + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_modulus_int_int(grn_ctx *ctx, grn_ts_int lhs, grn_ts_int rhs, + grn_ts_int *out) +{ + if (!rhs) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "%" GRN_FMT_INT64D " %% %" GRN_FMT_INT64D + " causes division by zero", + lhs, rhs); + } + *out = (rhs != -1) ? (lhs % rhs) : -lhs; + return GRN_SUCCESS; +} + +inline static grn_rc +grn_ts_op_modulus_float_float(grn_ctx *ctx, grn_ts_float lhs, grn_ts_float rhs, + grn_ts_float *out) +{ + *out = fmod(lhs, rhs); + if (!grn_ts_float_is_valid(*out)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "%g %% %g = %g", lhs, rhs, *out); + } + return GRN_SUCCESS; +} + +static grn_ts_bool +grn_ts_op_match(grn_ts_text lhs, grn_ts_text rhs) +{ + const char *lhs_ptr, *lhs_ptr_last; + if (lhs.size < rhs.size) { + return GRN_FALSE; + } + lhs_ptr_last = lhs.ptr + lhs.size - rhs.size; + for (lhs_ptr = lhs.ptr; lhs_ptr <= lhs_ptr_last; lhs_ptr++) { + size_t i; + for (i = 0; i < rhs.size; i++) { + if (lhs_ptr[i] != rhs.ptr[i]) { + break; + } + } + if (i == rhs.size) { + return GRN_TRUE; + } + } + return GRN_FALSE; +} + +static grn_ts_bool +grn_ts_op_prefix_match(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t i; + if (lhs.size < rhs.size) { + return GRN_FALSE; + } + for (i = 0; i < rhs.size; i++) { + if (lhs.ptr[i] != rhs.ptr[i]) { + return GRN_FALSE; + } + } + return GRN_TRUE; +} + +static grn_ts_bool +grn_ts_op_suffix_match(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t i; + const char *lhs_ptr; + if (lhs.size < rhs.size) { + return GRN_FALSE; + } + lhs_ptr = lhs.ptr + lhs.size - rhs.size; + for (i = 0; i < rhs.size; i++) { + if (lhs_ptr[i] != rhs.ptr[i]) { + return GRN_FALSE; + } + } + return GRN_TRUE; +} + +/*------------------------------------------------------------- + * Groonga objects. + */ + +#define GRN_TS_TABLE_GET_KEY(type)\ + uint32_t key_size;\ + const void *key_ptr = _grn_ ## type ## _key(ctx, type, id, &key_size);\ + if (!key_ptr) {\ + GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "_grn_" #type "_key failed: %u", id);\ + }\ +/* grn_ts_hash_get_bool_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_bool_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_bool *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const grn_ts_bool *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_int8_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_int8_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const int8_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_int16_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_int16_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const int16_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_int32_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_int32_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const int32_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_int64_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_int64_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const int64_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_uint8_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_uint8_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const uint8_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_uint16_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_uint16_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const uint16_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_uint32_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_uint32_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const uint32_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_uint64_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_uint64_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = (grn_ts_int)*(const uint64_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_float_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_float_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_float *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const grn_ts_float *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_time_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_time_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_time *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const grn_ts_time *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_geo_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_geo_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_geo *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + *key = *(const grn_ts_geo *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_text_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_text_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_text *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + key->ptr = key_ptr; + key->size = key_size; + return GRN_SUCCESS; +} + +/* grn_ts_hash_get_ref_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_hash_get_ref_key(grn_ctx *ctx, grn_hash *hash, grn_ts_id id, + grn_ts_ref *key) +{ + GRN_TS_TABLE_GET_KEY(hash) + key->id = *(const grn_ts_id *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_bool_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_bool_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_bool *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + *key = *(const grn_ts_bool *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_int8_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_int8_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + int8_t tmp; + GRN_TS_TABLE_GET_KEY(pat) + grn_ntohi(&tmp, key_ptr, sizeof(tmp)); + *key = tmp; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_int16_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_int16_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + int16_t tmp; + GRN_TS_TABLE_GET_KEY(pat) + grn_ntohi(&tmp, key_ptr, sizeof(tmp)); + *key = tmp; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_int32_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_int32_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + int32_t tmp; + GRN_TS_TABLE_GET_KEY(pat) + grn_ntohi(&tmp, key_ptr, sizeof(tmp)); + *key = tmp; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_int64_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_int64_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + grn_ntohi(key, key_ptr, sizeof(grn_ts_int)); + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_uint8_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_uint8_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + *key = *(const uint8_t *)key_ptr; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_uint16_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_uint16_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + uint16_t tmp; + GRN_TS_TABLE_GET_KEY(pat) + grn_ntoh(&tmp, key_ptr, sizeof(tmp)); + *key = tmp; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_uint32_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_uint32_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + uint32_t tmp; + GRN_TS_TABLE_GET_KEY(pat) + grn_ntoh(&tmp, key_ptr, sizeof(tmp)); + *key = tmp; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_uint64_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_uint64_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_int *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + grn_ntoh(key, key_ptr, sizeof(grn_ts_int)); + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_float_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_float_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_float *key) +{ + int64_t tmp; + GRN_TS_TABLE_GET_KEY(pat) + grn_ntoh(&tmp, key_ptr, sizeof(tmp)); + tmp ^= (((tmp ^ ((int64_t)1 << 63)) >> 63) | ((int64_t)1 << 63)); + *(int64_t *)key = tmp; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_time_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_time_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_time *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + grn_ntohi(key, key_ptr, sizeof(grn_ts_time)); + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_geo_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_geo_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_geo *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + grn_ntog(key, key_ptr, sizeof(grn_ts_geo)); + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_text_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_text_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_text *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + key->ptr = key_ptr; + key->size = key_size; + return GRN_SUCCESS; +} + +/* grn_ts_pat_get_ref_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_pat_get_ref_key(grn_ctx *ctx, grn_pat *pat, grn_ts_id id, + grn_ts_ref *key) +{ + GRN_TS_TABLE_GET_KEY(pat) + grn_ntoh(&key->id, key_ptr, sizeof(key->id)); + return GRN_SUCCESS; +} + +/* grn_ts_dat_get_text_key() gets a reference to a key (_key). */ +static grn_rc +grn_ts_dat_get_text_key(grn_ctx *ctx, grn_dat *dat, grn_ts_id id, + grn_ts_text *key) +{ + GRN_TS_TABLE_GET_KEY(dat) + key->ptr = key_ptr; + key->size = key_size; + return GRN_SUCCESS; +} +#undef GRN_TS_TABLE_GET_KEY + +/*------------------------------------------------------------- + * grn_ts_expr_id_node. + */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS +} grn_ts_expr_id_node; + +/* grn_ts_expr_id_node_init() initializes a node. */ +static void +grn_ts_expr_id_node_init(grn_ctx *ctx, grn_ts_expr_id_node *node) +{ + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_ID_NODE; + node->data_kind = GRN_TS_INT; + node->data_type = GRN_DB_UINT32; +} + +/* grn_ts_expr_id_node_fin() finalizes a node. */ +static void +grn_ts_expr_id_node_fin(grn_ctx *ctx, grn_ts_expr_id_node *node) +{ + /* Nothing to do. */ +} + +grn_rc +grn_ts_expr_id_node_open(grn_ctx *ctx, grn_ts_expr_node **node) +{ + grn_ts_expr_id_node *new_node = GRN_MALLOCN(grn_ts_expr_id_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_id_node)); + } + grn_ts_expr_id_node_init(ctx, new_node); + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_id_node_close() destroys a node. */ +static void +grn_ts_expr_id_node_close(grn_ctx *ctx, grn_ts_expr_id_node *node) +{ + grn_ts_expr_id_node_fin(ctx, node); + GRN_FREE(node); +} + +/* grn_ts_expr_id_node_evaluate() outputs IDs. */ +static grn_rc +grn_ts_expr_id_node_evaluate(grn_ctx *ctx, grn_ts_expr_id_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i; + grn_ts_int *out_ptr = (grn_ts_int *)out; + for (i = 0; i < n_in; i++) { + out_ptr[i] = (grn_ts_int)in[i].id; + } + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_expr_score_node. + */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS +} grn_ts_expr_score_node; + +/* grn_ts_expr_score_node_init() initializes a node. */ +static void +grn_ts_expr_score_node_init(grn_ctx *ctx, grn_ts_expr_score_node *node) +{ + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_SCORE_NODE; + node->data_kind = GRN_TS_FLOAT; + node->data_type = GRN_DB_FLOAT; +} + +/* grn_ts_expr_score_node_fin() finalizes a node. */ +static void +grn_ts_expr_score_node_fin(grn_ctx *ctx, grn_ts_expr_score_node *node) +{ + /* Nothing to do. */ +} + +grn_rc +grn_ts_expr_score_node_open(grn_ctx *ctx, grn_ts_expr_node **node) +{ + grn_ts_expr_score_node *new_node = GRN_MALLOCN(grn_ts_expr_score_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_score_node)); + } + grn_ts_expr_score_node_init(ctx, new_node); + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_score_node_close() destroys a node. */ +static void +grn_ts_expr_score_node_close(grn_ctx *ctx, grn_ts_expr_score_node *node) +{ + grn_ts_expr_score_node_fin(ctx, node); + GRN_FREE(node); +} + +/* grn_ts_expr_score_node_evaluate() outputs scores. */ +static grn_rc +grn_ts_expr_score_node_evaluate(grn_ctx *ctx, grn_ts_expr_score_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + size_t i; + grn_ts_float *out_ptr = (grn_ts_float *)out; + for (i = 0; i < n_in; i++) { + out_ptr[i] = (grn_ts_float)in[i].score; + } + return GRN_SUCCESS; +} + +/* grn_ts_expr_score_node_adjust() does nothing. */ +static grn_rc +grn_ts_expr_score_node_adjust(grn_ctx *ctx, grn_ts_expr_score_node *node, + grn_ts_record *io, size_t n_io) +{ + /* Nothing to do. */ + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_expr_key_node. + */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS + grn_obj *table; + grn_ts_buf buf; +} grn_ts_expr_key_node; + +/* grn_ts_expr_key_node_init() initializes a node. */ +static void +grn_ts_expr_key_node_init(grn_ctx *ctx, grn_ts_expr_key_node *node) +{ + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_KEY_NODE; + node->table = NULL; + grn_ts_buf_init(ctx, &node->buf); +} + +/* grn_ts_expr_key_node_fin() finalizes a node. */ +static void +grn_ts_expr_key_node_fin(grn_ctx *ctx, grn_ts_expr_key_node *node) +{ + grn_ts_buf_fin(ctx, &node->buf); + if (node->table) { + grn_obj_unlink(ctx, node->table); + } +} + +grn_rc +grn_ts_expr_key_node_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_node **node) +{ + grn_rc rc; + grn_ts_expr_key_node *new_node; + if (!grn_ts_table_has_key(ctx, table)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "the table has no _key"); + } + new_node = GRN_MALLOCN(grn_ts_expr_key_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_key_node)); + } + grn_ts_expr_key_node_init(ctx, new_node); + rc = grn_ts_obj_increment_ref_count(ctx, table); + if (rc != GRN_SUCCESS) { + grn_ts_expr_key_node_fin(ctx, new_node); + GRN_FREE(new_node); + return rc; + } + new_node->data_kind = grn_ts_data_type_to_kind(table->header.domain); + new_node->data_type = table->header.domain; + new_node->table = table; + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_key_node_close() destroys a node. */ +static void +grn_ts_expr_key_node_close(grn_ctx *ctx, grn_ts_expr_key_node *node) +{ + grn_ts_expr_key_node_fin(ctx, node); + GRN_FREE(node); +} + +#define GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(table, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + for (i = 0; i < n_in; i++) {\ + rc = grn_ts_ ## table ## _get_ ## kind ## _key(ctx, table, in[i].id,\ + &out_ptr[i]);\ + if (rc != GRN_SUCCESS) {\ + out_ptr[i] = grn_ts_ ## kind ## _zero();\ + }\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(table, TYPE, type)\ + case GRN_DB_ ## TYPE: {\ + grn_ts_int *out_ptr = (grn_ts_int *)out;\ + for (i = 0; i < n_in; i++) {\ + rc = grn_ts_ ## table ## _get_ ## type ## _key(ctx, table, in[i].id,\ + &out_ptr[i]);\ + if (rc != GRN_SUCCESS) {\ + out_ptr[i] = grn_ts_int_zero();\ + }\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(table)\ + case GRN_TS_TEXT: {\ + char *buf_ptr;\ + grn_ts_text *out_ptr = (grn_ts_text *)out;\ + node->buf.pos = 0;\ + for (i = 0; i < n_in; i++) {\ + grn_ts_text key;\ + rc = grn_ts_ ## table ## _get_text_key(ctx, table, in[i].id, &key);\ + if (rc != GRN_SUCCESS) {\ + key = grn_ts_text_zero();\ + }\ + rc = grn_ts_buf_write(ctx, &node->buf, key.ptr, key.size);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + out_ptr[i].size = key.size;\ + }\ + buf_ptr = (char *)node->buf.ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i].ptr = buf_ptr;\ + buf_ptr += out_ptr[i].size;\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE(table)\ + case GRN_TS_REF: {\ + grn_ts_ref *out_ptr = (grn_ts_ref *)out;\ + for (i = 0; i < n_in; i++) {\ + rc = grn_ts_ ## table ## _get_ref_key(ctx, table, in[i].id,\ + &out_ptr[i]);\ + if (rc != GRN_SUCCESS) {\ + out_ptr[i] = grn_ts_ref_zero();\ + }\ + out_ptr[i].score = in[i].score;\ + }\ + return GRN_SUCCESS;\ + } +/* grn_ts_expr_key_node_evaluate() outputs keys. */ +static grn_rc +grn_ts_expr_key_node_evaluate(grn_ctx *ctx, grn_ts_expr_key_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i; + grn_rc rc; + switch (node->table->header.type) { + case GRN_TABLE_HASH_KEY: { + grn_hash *hash = (grn_hash *)node->table; + switch (node->data_kind) { + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, BOOL, bool) + case GRN_TS_INT: { + switch (node->data_type) { + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT8, int8) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT16, int16) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT32, int32) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, INT64, int64) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT8, uint8) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT16, uint16) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT32, uint32) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(hash, UINT64, uint64) + } + } + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, FLOAT, float) + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, TIME, time) + GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(hash) + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(hash, GEO, geo) + GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE(hash) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } + } + case GRN_TABLE_PAT_KEY: { + grn_pat *pat = (grn_pat *)node->table; + switch (node->data_kind) { + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, BOOL, bool) + case GRN_TS_INT: { + switch (node->data_type) { + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT8, int8) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT16, int16) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT32, int32) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, INT64, int64) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT8, uint8) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT16, uint16) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT32, uint32) + GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE(pat, UINT64, uint64) + } + } + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, FLOAT, float) + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, TIME, time) + GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(pat) + GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE(pat, GEO, geo) + GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE(pat) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } + } + case GRN_TABLE_DAT_KEY: { + grn_dat *dat = (grn_dat *)node->table; + switch (node->data_kind) { + GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE(dat) + /* GRN_TABLE_DAT_KEY supports only Text. */ + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } + } + /* GRN_TABLE_NO_KEY doesn't support _key. */ + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid table type: %d", + node->table->header.type); + } + } +} +#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_REF_CASE +#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_TEXT_CASE +#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE +#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE + +/* grn_ts_expr_key_node_filter() filters records. */ +static grn_rc +grn_ts_expr_key_node_filter(grn_ctx *ctx, grn_ts_expr_key_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, count; + grn_ts_bool key; + switch (node->table->header.type) { + case GRN_TABLE_HASH_KEY: { + grn_hash *hash = (grn_hash *)node->table; + for (i = 0, count = 0; i < n_in; i++) { + grn_rc rc = grn_ts_hash_get_bool_key(ctx, hash, in[i].id, &key); + if (rc != GRN_SUCCESS) { + key = grn_ts_bool_zero(); + } + if (key) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; + } + case GRN_TABLE_PAT_KEY: { + grn_pat *pat = (grn_pat *)node->table; + for (i = 0, count = 0; i < n_in; i++) { + grn_rc rc = grn_ts_pat_get_bool_key(ctx, pat, in[i].id, &key); + if (rc != GRN_SUCCESS) { + key = grn_ts_bool_zero(); + } + if (key) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; + } + /* GRN_TABLE_DAT_KEY and GRN_TABLE_NO_KEY don't support a Bool key. */ + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid table type: %d", + node->table->header.type); + } + } +} + +/* grn_ts_expr_key_node_adjust() updates scores. */ +static grn_rc +grn_ts_expr_key_node_adjust(grn_ctx *ctx, grn_ts_expr_key_node *node, + grn_ts_record *io, size_t n_io) +{ + size_t i; + grn_ts_float key; + switch (node->table->header.type) { + case GRN_TABLE_HASH_KEY: { + grn_hash *hash = (grn_hash *)node->table; + for (i = 0; i < n_io; i++) { + grn_rc rc = grn_ts_hash_get_float_key(ctx, hash, io[i].id, &key); + if (rc != GRN_SUCCESS) { + key = grn_ts_float_zero(); + } + io[i].score = (grn_ts_score)key; + } + return GRN_SUCCESS; + } + case GRN_TABLE_PAT_KEY: { + grn_pat *pat = (grn_pat *)node->table; + for (i = 0; i < n_io; i++) { + grn_rc rc = grn_ts_pat_get_float_key(ctx, pat, io[i].id, &key); + if (rc != GRN_SUCCESS) { + key = grn_ts_float_zero(); + } + io[i].score = (grn_ts_score)key; + } + return GRN_SUCCESS; + } + /* GRN_TABLE_DAT_KEY and GRN_TABLE_NO_KEY don't support a Float key. */ + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid table type: %d", + node->table->header.type); + } + } +} + +/*------------------------------------------------------------- + * grn_ts_expr_value_node. + */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS + grn_obj *table; +} grn_ts_expr_value_node; + +/* grn_ts_expr_value_node_init() initializes a node. */ +static void +grn_ts_expr_value_node_init(grn_ctx *ctx, grn_ts_expr_value_node *node) +{ + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_VALUE_NODE; + node->table = NULL; +} + +/* grn_ts_expr_value_node_fin() finalizes a node. */ +static void +grn_ts_expr_value_node_fin(grn_ctx *ctx, grn_ts_expr_value_node *node) +{ + if (node->table) { + grn_obj_unlink(ctx, node->table); + } +} + +grn_rc +grn_ts_expr_value_node_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_node **node) +{ + grn_rc rc; + grn_ts_expr_value_node *new_node; + if (!grn_ts_table_has_value(ctx, table)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "table has no _value"); + } + new_node = GRN_MALLOCN(grn_ts_expr_value_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_value_node)); + } + grn_ts_expr_value_node_init(ctx, new_node); + rc = grn_ts_obj_increment_ref_count(ctx, table); + if (rc != GRN_SUCCESS) { + GRN_FREE(new_node); + return rc; + } + new_node->data_kind = grn_ts_data_type_to_kind(DB_OBJ(table)->range); + new_node->data_type = DB_OBJ(table)->range; + new_node->table = table; + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_value_node_close() destroys a node. */ +static void +grn_ts_expr_value_node_close(grn_ctx *ctx, grn_ts_expr_value_node *node) +{ + grn_ts_expr_value_node_fin(ctx, node); + GRN_FREE(node); +} + +#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(KIND, kind)\ + case GRN_TS_ ## KIND: {\ + size_t i;\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + for (i = 0; i < n_in; i++) {\ + const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id);\ + if (ptr) {\ + out_ptr[i] = *(const grn_ts_ ## kind *)ptr;\ + } else {\ + out_ptr[i] = grn_ts_ ## kind ## _zero();\ + }\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(TYPE, type)\ + case GRN_DB_ ## TYPE: {\ + size_t i;\ + grn_ts_int *out_ptr = (grn_ts_int *)out;\ + for (i = 0; i < n_in; i++) {\ + const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id);\ + if (ptr) {\ + out_ptr[i] = (grn_ts_int)*(const type ## _t *)ptr;\ + } else {\ + out_ptr[i] = grn_ts_int_zero();\ + }\ + }\ + return GRN_SUCCESS;\ + } +/* grn_ts_expr_value_node_evaluate() outputs values. */ +static grn_rc +grn_ts_expr_value_node_evaluate(grn_ctx *ctx, grn_ts_expr_value_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + switch (node->data_kind) { + GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(BOOL, bool) + case GRN_TS_INT: { + switch (node->data_type) { + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT8, int8) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT16, int16) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT32, int32) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(INT64, int64) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT8, uint8) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT16, uint16) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT32, uint32) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE(UINT64, uint64) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d", + node->data_type); + } + } + } + GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(FLOAT, float) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(TIME, time) + GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE(GEO, geo) + case GRN_TS_REF: { + size_t i; + grn_ts_ref *out_ptr = (grn_ts_ref *)out; + for (i = 0; i < n_in; i++) { + const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id); + if (ptr) { + out_ptr[i].id = *(const grn_ts_id *)ptr; + out_ptr[i].score = in[i].score; + } else { + out_ptr[i] = grn_ts_ref_zero(); + } + } + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE +#undef GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE + +/* grn_ts_expr_value_node_filter() filters records. */ +static grn_rc +grn_ts_expr_value_node_filter(grn_ctx *ctx, grn_ts_expr_value_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, count = 0; + for (i = 0; i < n_in; i++) { + const void *ptr = grn_ts_table_get_value(ctx, node->table, in[i].id); + if (ptr && *(const grn_ts_bool *)ptr) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; +} + +/* grn_ts_expr_value_node_adjust() updates scores. */ +static grn_rc +grn_ts_expr_value_node_adjust(grn_ctx *ctx, grn_ts_expr_value_node *node, + grn_ts_record *io, size_t n_io) +{ + size_t i; + for (i = 0; i < n_io; i++) { + const void *ptr = grn_ts_table_get_value(ctx, node->table, io[i].id); + if (ptr) { + io[i].score = (grn_ts_score)*(const grn_ts_float *)ptr; + } + } + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_expr_const_node. + */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS + grn_ts_any content; + grn_ts_buf text_buf; + grn_ts_buf vector_buf; +} grn_ts_expr_const_node; + +/* grn_ts_expr_const_node_init() initializes a node. */ +static void +grn_ts_expr_const_node_init(grn_ctx *ctx, grn_ts_expr_const_node *node) +{ + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_CONST_NODE; + grn_ts_buf_init(ctx, &node->text_buf); + grn_ts_buf_init(ctx, &node->vector_buf); +} + +/* grn_ts_expr_const_node_fin() finalizes a node. */ +static void +grn_ts_expr_const_node_fin(grn_ctx *ctx, grn_ts_expr_const_node *node) +{ + grn_ts_buf_fin(ctx, &node->vector_buf); + grn_ts_buf_fin(ctx, &node->text_buf); +} + +#define GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(KIND, kind)\ + case GRN_TS_ ## KIND: {\ + node->content.as_ ## kind = value.as_ ## kind;\ + return GRN_SUCCESS;\ + } +/* grn_ts_expr_const_node_set_scalar() sets a scalar value. */ +static grn_rc +grn_ts_expr_const_node_set_scalar(grn_ctx *ctx, grn_ts_expr_const_node *node, + grn_ts_any value) +{ + switch (node->data_kind) { + GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(BOOL, bool) + GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(INT, int) + GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(FLOAT, float) + GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(TIME, time) + case GRN_TS_TEXT: { + grn_rc rc = grn_ts_buf_write(ctx, &node->text_buf, + value.as_text.ptr, value.as_text.size); + if (rc != GRN_SUCCESS) { + return rc; + } + node->content.as_text.ptr = (const char *)node->text_buf.ptr; + node->content.as_text.size = value.as_text.size; + return GRN_SUCCESS; + } + GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE(GEO, geo) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_EXPR_CONST_NODE_SET_SCALAR_CASE + +#define GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(KIND, kind)\ + case GRN_TS_ ## KIND ## _VECTOR: {\ + grn_rc rc;\ + size_t n_bytes;\ + const grn_ts_ ## kind *buf_ptr;\ + grn_ts_ ## kind ## _vector vector;\ + vector = value.as_ ## kind ## _vector;\ + n_bytes = sizeof(grn_ts_ ## kind) * vector.size;\ + rc = grn_ts_buf_write(ctx, &node->vector_buf, vector.ptr, n_bytes);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + buf_ptr = (const grn_ts_ ## kind *)node->vector_buf.ptr;\ + node->content.as_ ## kind ## _vector.ptr = buf_ptr;\ + node->content.as_ ## kind ## _vector.size = vector.size;\ + return GRN_SUCCESS;\ + } +/* grn_ts_expr_const_node_set_vector() sets a vector value. */ +static grn_rc +grn_ts_expr_const_node_set_vector(grn_ctx *ctx, grn_ts_expr_const_node *node, + grn_ts_any value) +{ + switch (node->data_kind) { + GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(BOOL, bool) + GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(INT, int) + GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(FLOAT, float) + GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(TIME, time) + case GRN_TS_TEXT_VECTOR: { + grn_rc rc; + size_t i, n_bytes, offset, total_size; + grn_ts_text_vector vector = value.as_text_vector; + grn_ts_text *vector_buf; + char *text_buf; + n_bytes = sizeof(grn_ts_text) * vector.size; + rc = grn_ts_buf_resize(ctx, &node->vector_buf, n_bytes); + if (rc != GRN_SUCCESS) { + return rc; + } + vector_buf = (grn_ts_text *)node->vector_buf.ptr; + total_size = 0; + for (i = 0; i < vector.size; i++) { + total_size += vector.ptr[i].size; + } + rc = grn_ts_buf_resize(ctx, &node->text_buf, total_size); + if (rc != GRN_SUCCESS) { + return rc; + } + text_buf = (char *)node->text_buf.ptr; + offset = 0; + for (i = 0; i < vector.size; i++) { + grn_memcpy(text_buf + offset, vector.ptr[i].ptr, vector.ptr[i].size); + vector_buf[i].ptr = text_buf + offset; + vector_buf[i].size = vector.ptr[i].size; + offset += vector.ptr[i].size; + } + node->content.as_text_vector.ptr = vector_buf; + node->content.as_text_vector.size = vector.size; + return GRN_SUCCESS; + } + GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE(GEO, geo) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_EXPR_CONST_NODE_SET_VECTOR_CASE + +#define GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(KIND, kind)\ + case GRN_TS_ ## KIND: {\ + if (!grn_ts_ ## kind ## _is_valid(value.as_ ## kind)) {\ + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");\ + }\ + return GRN_SUCCESS;\ + } +static grn_rc +grn_ts_expr_const_node_check_value(grn_ctx *ctx, grn_ts_data_kind kind, + grn_ts_any value) +{ + switch (kind) { + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(BOOL, bool) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(INT, int) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(FLOAT, float) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TIME, time) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TEXT, text) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(GEO, geo) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(BOOL_VECTOR, bool_vector) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(INT_VECTOR, int_vector) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(FLOAT_VECTOR, float_vector) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TIME_VECTOR, time_vector) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(TEXT_VECTOR, text_vector) + GRN_TS_EXPR_CONST_NODE_CHECK_VALUE(GEO_VECTOR, geo_vector) + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + } +} +#undef GRN_TS_EXPR_CONST_NODE_CHECK_VALUE + +grn_rc +grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind data_kind, + grn_ts_data_type data_type, + grn_ts_any value, grn_ts_expr_node **node) +{ + grn_rc rc = grn_ts_expr_const_node_check_value(ctx, data_kind, value); + grn_ts_expr_const_node *new_node; + if (rc != GRN_SUCCESS) { + return rc; + } + new_node = GRN_MALLOCN(grn_ts_expr_const_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_const_node)); + } + grn_ts_expr_const_node_init(ctx, new_node); + new_node->data_kind = data_kind; + if (data_type != GRN_DB_VOID) { + new_node->data_type = data_type; + } else { + new_node->data_type = grn_ts_data_kind_to_type(data_kind); + } + if (data_kind & GRN_TS_VECTOR_FLAG) { + rc = grn_ts_expr_const_node_set_vector(ctx, new_node, value); + } else { + rc = grn_ts_expr_const_node_set_scalar(ctx, new_node, value); + } + if (rc != GRN_SUCCESS) { + grn_ts_expr_const_node_fin(ctx, new_node); + GRN_FREE(new_node); + return rc; + } + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_const_node_close() destroys a node. */ +static void +grn_ts_expr_const_node_close(grn_ctx *ctx, grn_ts_expr_const_node *node) +{ + grn_ts_expr_const_node_fin(ctx, node); + GRN_FREE(node); +} + +#define GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(KIND, kind)\ + case GRN_TS_ ## KIND: {\ + size_t i;\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = node->content.as_ ## kind;\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(KIND, kind)\ + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(KIND ## _VECTOR, kind ## _vector) +/* grn_ts_expr_const_node_evaluate() outputs the stored const. */ +static grn_rc +grn_ts_expr_const_node_evaluate(grn_ctx *ctx, grn_ts_expr_const_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + switch (node->data_kind) { + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(BOOL, bool) + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(INT, int) + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(FLOAT, float) + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(TIME, time) + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(TEXT, text) + GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE(GEO, geo) + GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(BOOL, bool) + GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(INT, int) + GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(FLOAT, float) + GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(TIME, time) + GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(TEXT, text) + GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE(GEO, geo) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE +#undef GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE + +/* grn_ts_expr_const_node_filter() filters records. */ +static grn_rc +grn_ts_expr_const_node_filter(grn_ctx *ctx, grn_ts_expr_const_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + if (node->content.as_bool) { + /* All the records pass through the filter. */ + if (in != out) { + size_t i; + for (i = 0; i < n_in; i++) { + out[i] = in[i]; + } + } + *n_out = n_in; + } else { + /* All the records are discarded. */ + *n_out = 0; + } + return GRN_SUCCESS; +} + +/* grn_ts_expr_const_node_adjust() updates scores. */ +static grn_rc +grn_ts_expr_const_node_adjust(grn_ctx *ctx, grn_ts_expr_const_node *node, + grn_ts_record *io, size_t n_io) +{ + size_t i; + grn_ts_score score = (grn_ts_score)node->content.as_float; + for (i = 0; i < n_io; i++) { + io[i].score = score; + } + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_expr_column_node. + */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS + grn_obj *column; + grn_ts_buf buf; + grn_ts_buf body_buf; + grn_ja_reader *reader; +} grn_ts_expr_column_node; + +/* grn_ts_expr_column_node_init() initializes a node. */ +static void +grn_ts_expr_column_node_init(grn_ctx *ctx, grn_ts_expr_column_node *node) +{ + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_COLUMN_NODE; + node->column = NULL; + grn_ts_buf_init(ctx, &node->buf); + grn_ts_buf_init(ctx, &node->body_buf); + node->reader = NULL; +} + +/* grn_ts_expr_column_node_fin() finalizes a node. */ +static void +grn_ts_expr_column_node_fin(grn_ctx *ctx, grn_ts_expr_column_node *node) +{ + if (node->reader) { + grn_ja_reader_close(ctx, node->reader); + } + grn_ts_buf_fin(ctx, &node->body_buf); + grn_ts_buf_fin(ctx, &node->buf); + if (node->column) { + grn_obj_unlink(ctx, node->column); + } +} + +#define GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE(TYPE)\ + case GRN_DB_ ## TYPE: {\ + GRN_ ## TYPE ## _INIT(&new_node->buf, GRN_OBJ_VECTOR);\ + break;\ + } +grn_rc +grn_ts_expr_column_node_open(grn_ctx *ctx, grn_obj *column, + grn_ts_expr_node **node) +{ + grn_rc rc; + grn_ts_expr_column_node *new_node = GRN_MALLOCN(grn_ts_expr_column_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_column_node)); + } + grn_ts_expr_column_node_init(ctx, new_node); + new_node->data_kind = grn_ts_data_type_to_kind(DB_OBJ(column)->range); + if (column->header.type == GRN_COLUMN_VAR_SIZE) { + grn_obj_flags type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK; + if (type == GRN_OBJ_COLUMN_VECTOR) { + new_node->data_kind |= GRN_TS_VECTOR_FLAG; + } + } + new_node->data_type = DB_OBJ(column)->range; + rc = grn_ts_obj_increment_ref_count(ctx, column); + if (rc != GRN_SUCCESS) { + grn_ts_expr_column_node_fin(ctx, new_node); + GRN_FREE(new_node); + return rc; + } + new_node->column = column; + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} +#undef GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE + +/* grn_ts_expr_column_node_close() destroys a node. */ +static void +grn_ts_expr_column_node_close(grn_ctx *ctx, grn_ts_expr_column_node *node) +{ + grn_ts_expr_column_node_fin(ctx, node); + GRN_FREE(node); +} + +#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(KIND, kind)\ + case GRN_TS_ ## KIND: {\ + size_t i;\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + grn_ra *ra = (grn_ra *)node->column;\ + grn_ra_cache cache;\ + GRN_RA_CACHE_INIT(ra, &cache);\ + for (i = 0; i < n_in; i++) {\ + grn_ts_ ## kind *ptr = NULL;\ + if (in[i].id) {\ + ptr = (grn_ts_ ## kind *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);\ + }\ + out_ptr[i] = ptr ? *ptr : grn_ts_ ## kind ## _zero();\ + }\ + GRN_RA_CACHE_FIN(ra, &cache);\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(TYPE, type)\ + case GRN_DB_ ## TYPE: {\ + size_t i;\ + grn_ts_int *out_ptr = (grn_ts_int *)out;\ + grn_ra *ra = (grn_ra *)node->column;\ + grn_ra_cache cache;\ + GRN_RA_CACHE_INIT(ra, &cache);\ + for (i = 0; i < n_in; i++) {\ + type ## _t *ptr = NULL;\ + if (in[i].id) {\ + ptr = (type ## _t *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);\ + }\ + out_ptr[i] = ptr ? (grn_ts_int)*ptr : grn_ts_int_zero();\ + }\ + GRN_RA_CACHE_FIN(ra, &cache);\ + return GRN_SUCCESS;\ + } +/* grn_ts_expr_column_node_evaluate_scalar() outputs scalar column values. */ +static grn_rc +grn_ts_expr_column_node_evaluate_scalar(grn_ctx *ctx, + grn_ts_expr_column_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + switch (node->data_kind) { + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(BOOL, bool) + case GRN_TS_INT: { + switch (node->data_type) { + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT8, int8) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT16, int16) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT32, int32) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(INT64, int64) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT8, uint8) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT16, uint16) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT32, uint32) + /* The behavior is undefined if a value is greater than 2^63 - 1. */ + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE(UINT64, uint64) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d", + node->data_type); + } + } + } + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(FLOAT, float) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(TIME, time) + case GRN_TS_TEXT: { + size_t i; + char *buf_ptr; + grn_rc rc; + grn_ts_text *out_ptr = (grn_ts_text *)out; + if (!node->reader) { + rc = grn_ja_reader_open(ctx, (grn_ja *)node->column, &node->reader); + if (rc != GRN_SUCCESS) { + GRN_TS_ERR_RETURN(rc, "grn_ja_reader_open failed"); + } + } else { + grn_ja_reader_unref(ctx, node->reader); + } + node->buf.pos = 0; + for (i = 0; i < n_in; i++) { + rc = grn_ja_reader_seek(ctx, node->reader, in[i].id); + if (rc == GRN_SUCCESS) { + if (node->reader->ref_avail) { + void *addr; + rc = grn_ja_reader_ref(ctx, node->reader, &addr); + if (rc == GRN_SUCCESS) { + out_ptr[i].ptr = (char *)addr; + } + } else { + rc = grn_ts_buf_reserve(ctx, &node->buf, + node->buf.pos + node->reader->value_size); + if (rc == GRN_SUCCESS) { + rc = grn_ja_reader_read(ctx, node->reader, + (char *)node->buf.ptr + node->buf.pos); + if (rc == GRN_SUCCESS) { + out_ptr[i].ptr = NULL; + node->buf.pos += node->reader->value_size; + } + } + } + } + if (rc == GRN_SUCCESS) { + out_ptr[i].size = node->reader->value_size; + } else { + out_ptr[i].ptr = NULL; + out_ptr[i].size = 0; + } + } + buf_ptr = (char *)node->buf.ptr; + for (i = 0; i < n_in; i++) { + if (!out_ptr[i].ptr) { + out_ptr[i].ptr = buf_ptr; + buf_ptr += out_ptr[i].size; + } + } + return GRN_SUCCESS; + } + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE(GEO, geo) + case GRN_TS_REF: { + size_t i; + grn_ts_ref *out_ptr = (grn_ts_ref *)out; + grn_ra *ra = (grn_ra *)node->column; + grn_ra_cache cache; + GRN_RA_CACHE_INIT(ra, &cache); + for (i = 0; i < n_in; i++) { + grn_ts_id *ptr = NULL; + if (in[i].id) { + ptr = (grn_ts_id *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache); + } + out_ptr[i].id = ptr ? *ptr : GRN_ID_NIL; + out_ptr[i].score = in[i].score; + } + GRN_RA_CACHE_FIN(ra, &cache); + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE +#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE + +/* + * grn_ts_expr_column_node_evaluate_text_vector() outputs text vector column + * values. + */ +static grn_rc +grn_ts_expr_column_node_evaluate_text_vector(grn_ctx *ctx, + grn_ts_expr_column_node *node, + const grn_ts_record *in, + size_t n_in, void *out) +{ + grn_rc rc; + char *buf_ptr; + size_t i, j, n_bytes, n_values, total_n_bytes = 0, total_n_values = 0; + grn_ts_text *text_ptr; + grn_ts_text_vector *out_ptr = (grn_ts_text_vector *)out; + /* Read encoded values into node->body_buf and get the size of each value. */ + node->body_buf.pos = 0; + for (i = 0; i < n_in; i++) { + char *ptr; + rc = grn_ts_ja_get_value(ctx, node->column, in[i].id, + &node->body_buf, &n_bytes); + if (rc == GRN_SUCCESS) { + ptr = (char *)node->body_buf.ptr + total_n_bytes; + GRN_B_DEC(n_values, ptr); + } else { + n_bytes = 0; + n_values = 0; + } + grn_memcpy(&out_ptr[i].ptr, &n_bytes, sizeof(n_bytes)); + out_ptr[i].size = n_values; + total_n_bytes += n_bytes; + total_n_values += n_values; + } + /* Resize node->buf. */ + n_bytes = sizeof(grn_ts_text) * total_n_values; + rc = grn_ts_buf_reserve(ctx, &node->buf, n_bytes); + if (rc != GRN_SUCCESS) { + return rc; + } + /* Decode values and compose the result. */ + buf_ptr = (char *)node->body_buf.ptr; + text_ptr = (grn_ts_text *)node->buf.ptr; + for (i = 0; i < n_in; i++) { + char *ptr = buf_ptr; + grn_memcpy(&n_bytes, &out_ptr[i].ptr, sizeof(n_bytes)); + buf_ptr += n_bytes; + GRN_B_DEC(n_values, ptr); + out_ptr[i].ptr = text_ptr; + for (j = 0; j < out_ptr[i].size; j++) { + GRN_B_DEC(text_ptr[j].size, ptr); + } + for (j = 0; j < out_ptr[i].size; j++) { + text_ptr[j].ptr = ptr; + ptr += text_ptr[j].size; + } + text_ptr += out_ptr[i].size; + } + return GRN_SUCCESS; +} + +/* + * grn_ts_expr_column_node_evaluate_ref_vector() outputs ref vector column + * values. + */ +static grn_rc +grn_ts_expr_column_node_evaluate_ref_vector(grn_ctx *ctx, + grn_ts_expr_column_node *node, + const grn_ts_record *in, + size_t n_in, void *out) +{ + grn_rc rc; + size_t i, j, n_bytes, offset = 0; + grn_ts_id *buf_ptr; + grn_ts_ref *ref_ptr; + grn_ts_ref_vector *out_ptr = (grn_ts_ref_vector *)out; + /* Read column values into node->body_buf and get the size of each value. */ + node->body_buf.pos = 0; + for (i = 0; i < n_in; i++) { + size_t size; + rc = grn_ts_ja_get_value(ctx, node->column, in[i].id, + &node->body_buf, &size); + if (rc == GRN_SUCCESS) { + out_ptr[i].size = size / sizeof(grn_ts_id); + offset += out_ptr[i].size; + } else { + out_ptr[i].size = 0; + } + } + /* Resize node->buf. */ + n_bytes = sizeof(grn_ts_ref) * offset; + rc = grn_ts_buf_reserve(ctx, &node->buf, n_bytes); + if (rc != GRN_SUCCESS) { + return rc; + } + /* Compose the result. */ + buf_ptr = (grn_ts_id *)node->body_buf.ptr; + ref_ptr = (grn_ts_ref *)node->buf.ptr; + for (i = 0; i < n_in; i++) { + out_ptr[i].ptr = ref_ptr; + for (j = 0; j < out_ptr[i].size; j++, buf_ptr++, ref_ptr++) { + ref_ptr->id = *buf_ptr; + ref_ptr->score = in[i].score; + } + } + return GRN_SUCCESS; +} + +#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(KIND, kind)\ + case GRN_TS_ ## KIND ## _VECTOR: {\ + size_t i;\ + grn_ts_ ## kind *buf_ptr;\ + grn_ts_ ## kind ## _vector *out_ptr = (grn_ts_ ## kind ## _vector *)out;\ + /* Read column values into node->buf and save the size of each value. */\ + node->buf.pos = 0;\ + for (i = 0; i < n_in; i++) {\ + size_t n_bytes;\ + grn_rc rc = grn_ts_ja_get_value(ctx, node->column, in[i].id,\ + &node->buf, &n_bytes);\ + if (rc == GRN_SUCCESS) {\ + out_ptr[i].size = n_bytes / sizeof(grn_ts_ ## kind);\ + } else {\ + out_ptr[i].size = 0;\ + }\ + }\ + buf_ptr = (grn_ts_ ## kind *)node->buf.ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i].ptr = buf_ptr;\ + buf_ptr += out_ptr[i].size;\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(TYPE, type)\ + case GRN_DB_ ## TYPE: {\ + size_t i, j;\ + grn_ts_int *buf_ptr;\ + grn_ts_int_vector *out_ptr = (grn_ts_int_vector *)out;\ + /* + * Read column values into body_buf and typecast the values to grn_ts_int. + * Then, store the grn_ts_int values into node->buf and save the size of + * each value. + */\ + node->buf.pos = 0;\ + for (i = 0; i < n_in; i++) {\ + grn_rc rc;\ + size_t n_bytes, new_n_bytes;\ + node->body_buf.pos = 0;\ + rc = grn_ts_ja_get_value(ctx, node->column, in[i].id,\ + &node->body_buf, &n_bytes);\ + if (rc == GRN_SUCCESS) {\ + out_ptr[i].size = n_bytes / sizeof(type ## _t);\ + } else {\ + out_ptr[i].size = 0;\ + }\ + new_n_bytes = node->buf.pos + (sizeof(grn_ts_int) * out_ptr[i].size);\ + rc = grn_ts_buf_reserve(ctx, &node->buf, new_n_bytes);\ + if (rc == GRN_SUCCESS) {\ + type ## _t *src_ptr = (type ## _t *)node->body_buf.ptr;\ + grn_ts_int *dest_ptr;\ + dest_ptr = (grn_ts_int *)((char *)node->buf.ptr + node->buf.pos);\ + for (j = 0; j < out_ptr[i].size; j++) {\ + dest_ptr[j] = (grn_ts_int)src_ptr[j];\ + }\ + node->buf.pos = new_n_bytes;\ + } else {\ + out_ptr[i].size = 0;\ + }\ + }\ + buf_ptr = (grn_ts_int *)node->buf.ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i].ptr = buf_ptr;\ + buf_ptr += out_ptr[i].size;\ + }\ + return GRN_SUCCESS;\ + } +/* grn_ts_expr_column_node_evaluate_vector() outputs vector column values. */ +static grn_rc +grn_ts_expr_column_node_evaluate_vector(grn_ctx *ctx, + grn_ts_expr_column_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + switch (node->data_kind) { + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(BOOL, bool) + case GRN_TS_INT_VECTOR: { + switch (node->data_type) { + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT8, int8) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT16, int16) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT32, int32) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(INT64, int64) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT8, uint8) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT16, uint16) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT32, uint32) + /* The behavior is undefined if a value is greater than 2^63 - 1. */ + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE(UINT64, uint64) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d", + node->data_type); + } + } + } + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(FLOAT, float) + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(TIME, time) + case GRN_TS_TEXT_VECTOR: { + return grn_ts_expr_column_node_evaluate_text_vector(ctx, node, in, n_in, + out); + } + GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE(GEO, geo) + case GRN_TS_REF_VECTOR: { + return grn_ts_expr_column_node_evaluate_ref_vector(ctx, node, in, n_in, + out); + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE +#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE + +/* grn_ts_expr_column_node_evaluate() outputs column values. */ +static grn_rc +grn_ts_expr_column_node_evaluate(grn_ctx *ctx, grn_ts_expr_column_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + if (node->data_kind & GRN_TS_VECTOR_FLAG) { + return grn_ts_expr_column_node_evaluate_vector(ctx, node, in, n_in, out); + } else { + return grn_ts_expr_column_node_evaluate_scalar(ctx, node, in, n_in, out); + } +} + +/* grn_ts_expr_column_node_filter() filters records. */ +static grn_rc +grn_ts_expr_column_node_filter(grn_ctx *ctx, grn_ts_expr_column_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, count = 0; + grn_ra *ra = (grn_ra *)node->column; + grn_ra_cache cache; + GRN_RA_CACHE_INIT(ra, &cache); + for (i = 0; i < n_in; i++) { + grn_ts_bool *ptr = NULL; + if (in[i].id) { + ptr = grn_ra_ref_cache(ctx, ra, in[i].id, &cache); + } + if (ptr && *ptr) { + out[count++] = in[i]; + } + } + GRN_RA_CACHE_FIN(ra, &cache); + *n_out = count; + return GRN_SUCCESS; +} + +/* grn_ts_expr_column_node_adjust() updates scores. */ +static grn_rc +grn_ts_expr_column_node_adjust(grn_ctx *ctx, grn_ts_expr_column_node *node, + grn_ts_record *io, size_t n_io) +{ + size_t i; + grn_ra *ra = (grn_ra *)node->column; + grn_ra_cache cache; + GRN_RA_CACHE_INIT(ra, &cache); + for (i = 0; i < n_io; i++) { + grn_ts_float *ptr = NULL; + if (io[i].id) { + ptr = grn_ra_ref_cache(ctx, ra, io[i].id, &cache); + } + if (ptr) { + io[i].score = (grn_ts_score)*ptr; + } + } + GRN_RA_CACHE_FIN(ra, &cache); + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_expr_op_node. + */ + +enum { + GRN_TS_EXPR_OP_NODE_MAX_N_ARGS = 3, + GRN_TS_EXPR_OP_NODE_N_BUFS = 3 +}; + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS + grn_ts_op_type op_type; + grn_ts_expr_node *args[GRN_TS_EXPR_OP_NODE_MAX_N_ARGS]; + size_t n_args; + grn_ts_buf bufs[GRN_TS_EXPR_OP_NODE_N_BUFS]; +} grn_ts_expr_op_node; + +/* grn_ts_expr_op_node_init() initializes a node. */ +static void +grn_ts_expr_op_node_init(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + size_t i; + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_OP_NODE; + for (i = 0; i < GRN_TS_EXPR_OP_NODE_MAX_N_ARGS; i++) { + node->args[i] = NULL; + } + for (i = 0; i < GRN_TS_EXPR_OP_NODE_N_BUFS; i++) { + grn_ts_buf_init(ctx, &node->bufs[i]); + } +} + +/* grn_ts_expr_op_node_fin() finalizes a node. */ +static void +grn_ts_expr_op_node_fin(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + size_t i; + for (i = 0; i < GRN_TS_EXPR_OP_NODE_N_BUFS; i++) { + grn_ts_buf_fin(ctx, &node->bufs[i]); + } + for (i = 0; i < GRN_TS_EXPR_OP_NODE_MAX_N_ARGS; i++) { + if (node->args[i]) { + grn_ts_expr_node_close(ctx, node->args[i]); + } + } +} + +/* + * grn_ts_expr_op_node_deref_args_for_equal() resolves references if required. + */ +static grn_rc +grn_ts_expr_op_node_deref_args_for_equal(grn_ctx *ctx, + grn_ts_expr_op_node *node) +{ + grn_rc rc; + if (node->n_args != 2) { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid #args: %" GRN_FMT_SIZE, + node->n_args); + } + if ((node->args[0]->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) { + return grn_ts_expr_node_deref(ctx, &node->args[1]); + } + if ((node->args[1]->data_kind & ~GRN_TS_VECTOR_FLAG) != GRN_TS_REF) { + return grn_ts_expr_node_deref(ctx, &node->args[0]); + } + + /* FIXME: Arguments should be compared as references if possible. */ + rc = grn_ts_expr_node_deref(ctx, &node->args[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_node_deref(ctx, &node->args[1]); + if (rc != GRN_SUCCESS) { + return rc; + } + return GRN_SUCCESS; +} + +/* grn_ts_expr_op_node_deref_args() resolves references if required. */ +static grn_rc +grn_ts_expr_op_node_deref_args(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + switch (node->op_type) { + case GRN_TS_OP_EQUAL: + case GRN_TS_OP_NOT_EQUAL: { + return grn_ts_expr_op_node_deref_args_for_equal(ctx, node); + } + /* TODO: Add a ternary operator. */ + default: { + size_t i; + for (i = 0; i < node->n_args; i++) { + grn_rc rc = grn_ts_expr_node_deref(ctx, &node->args[i]); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; + } + } +} + +/* + * grn_ts_op_plus_check_args() checks arguments. Note that arguments are + * rearranged in some cases. + */ +static grn_rc +grn_ts_op_plus_check_args(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + grn_rc rc; + if ((node->args[0]->data_kind == GRN_TS_INT) && + (node->args[1]->data_kind == GRN_TS_FLOAT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0], + 1, &node->args[0]); + if (rc != GRN_SUCCESS) { + node->args[0] = NULL; + return rc; + } + } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) && + (node->args[1]->data_kind == GRN_TS_INT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1], + 1, &node->args[1]); + if (rc != GRN_SUCCESS) { + node->args[1] = NULL; + return rc; + } + } + + switch (node->args[0]->data_kind) { + case GRN_TS_INT: { + switch (node->args[1]->data_kind) { + case GRN_TS_INT: { + /* Int + Int = Int. */ + node->data_kind = GRN_TS_INT; + node->data_type = GRN_DB_INT64; + return GRN_SUCCESS; + } + case GRN_TS_TIME: { + /* Int + Time = Time + Int = Time. */ + grn_ts_expr_node *tmp = node->args[0]; + node->args[0] = node->args[1]; + node->args[1] = tmp; + node->data_kind = GRN_TS_TIME; + node->data_type = GRN_DB_TIME; + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[1]->data_kind); + } + } + } + case GRN_TS_FLOAT: { + switch (node->args[1]->data_kind) { + case GRN_TS_FLOAT: { + /* Float + Float = Float. */ + node->data_kind = GRN_TS_FLOAT; + node->data_type = GRN_DB_FLOAT; + return GRN_SUCCESS; + } + case GRN_TS_TIME: { + /* Float + Time = Time + Float = Time. */ + grn_ts_expr_node *tmp = node->args[0]; + node->args[0] = node->args[1]; + node->args[1] = tmp; + node->data_kind = GRN_TS_TIME; + node->data_type = GRN_DB_TIME; + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[1]->data_kind); + } + } + } + case GRN_TS_TIME: { + switch (node->args[1]->data_kind) { + case GRN_TS_INT: + case GRN_TS_FLOAT: { + /* Time + Int or Float = Time. */ + node->data_kind = GRN_TS_TIME; + node->data_type = GRN_DB_TIME; + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[1]->data_kind); + } + } + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} + +/* grn_ts_op_minus_check_args() checks arguments. */ +static grn_rc +grn_ts_op_minus_check_args(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + grn_rc rc; + if ((node->args[0]->data_kind == GRN_TS_INT) && + (node->args[1]->data_kind == GRN_TS_FLOAT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0], + 1, &node->args[0]); + if (rc != GRN_SUCCESS) { + node->args[0] = NULL; + return rc; + } + } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) && + (node->args[1]->data_kind == GRN_TS_INT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1], + 1, &node->args[1]); + if (rc != GRN_SUCCESS) { + node->args[1] = NULL; + return rc; + } + } + + switch (node->args[0]->data_kind) { + case GRN_TS_INT: { + if (node->args[1]->data_kind != GRN_TS_INT) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[1]->data_kind); + } + /* Int - Int = Int. */ + node->data_kind = GRN_TS_INT; + node->data_type = GRN_DB_INT64; + return GRN_SUCCESS; + } + case GRN_TS_FLOAT: { + if (node->args[1]->data_kind != GRN_TS_FLOAT) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[1]->data_kind); + } + /* Float - Float = Float. */ + node->data_kind = GRN_TS_FLOAT; + node->data_type = GRN_DB_FLOAT; + return GRN_SUCCESS; + } + case GRN_TS_TIME: { + switch (node->args[1]->data_kind) { + case GRN_TS_INT: + case GRN_TS_FLOAT: { + /* Time - Int or Float = Time. */ + node->data_kind = GRN_TS_TIME; + node->data_type = GRN_DB_TIME; + return GRN_SUCCESS; + } + case GRN_TS_TIME: { + /* Time - Time = Float. */ + node->data_kind = GRN_TS_FLOAT; + node->data_type = GRN_DB_FLOAT; + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[1]->data_kind); + } + } + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} + +/* + * grn_ts_expr_op_node_typecast_args_for_cmp() inserts a typecast operator for + * comparison. + */ +static grn_rc +grn_ts_expr_op_node_typecast_args_for_cmp(grn_ctx *ctx, + grn_ts_expr_op_node *node) +{ + grn_rc rc; + if ((node->args[0]->data_kind == GRN_TS_INT) && + (node->args[1]->data_kind == GRN_TS_FLOAT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0], + 1, &node->args[0]); + if (rc != GRN_SUCCESS) { + node->args[0] = NULL; + return rc; + } + } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) && + (node->args[1]->data_kind == GRN_TS_INT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1], + 1, &node->args[1]); + if (rc != GRN_SUCCESS) { + node->args[1] = NULL; + return rc; + } + } else if ((node->args[0]->data_kind == GRN_TS_TIME) && + (node->args[1]->data_kind == GRN_TS_TEXT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_TIME, &node->args[1], + 1, &node->args[1]); + if (rc != GRN_SUCCESS) { + node->args[1] = NULL; + return rc; + } + } else if ((node->args[0]->data_kind == GRN_TS_TEXT) && + (node->args[1]->data_kind == GRN_TS_TIME)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_TIME, &node->args[0], + 1, &node->args[0]); + if (rc != GRN_SUCCESS) { + node->args[0] = NULL; + return rc; + } + } else { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "data kind conflict: %d != %d", + node->args[0]->data_kind, + node->args[1]->data_kind); + } + return GRN_SUCCESS; +} + +/* + * grn_ts_expr_op_node_check_args() checks the combination of an operator and + * its arguments. + */ +static grn_rc +grn_ts_expr_op_node_check_args(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + switch (node->op_type) { + case GRN_TS_OP_LOGICAL_NOT: { + if (node->args[0]->data_kind != GRN_TS_BOOL) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + node->data_kind = GRN_TS_BOOL; + node->data_type = GRN_DB_BOOL; + return GRN_SUCCESS; + } + case GRN_TS_OP_BITWISE_NOT: { + switch (node->args[0]->data_kind) { + case GRN_TS_BOOL: + case GRN_TS_INT: { + node->data_kind = node->args[0]->data_kind; + node->data_type = grn_ts_data_kind_to_type(node->data_kind); + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } + } + case GRN_TS_OP_POSITIVE: + case GRN_TS_OP_NEGATIVE: { + if ((node->args[0]->data_kind != GRN_TS_INT) && + (node->args[0]->data_kind != GRN_TS_FLOAT)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + node->data_kind = node->args[0]->data_kind; + node->data_type = grn_ts_data_kind_to_type(node->data_kind); + return GRN_SUCCESS; + } + case GRN_TS_OP_FLOAT: { + if (node->args[0]->data_kind != GRN_TS_INT) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + node->data_kind = GRN_TS_FLOAT; + node->data_type = GRN_DB_FLOAT; + return GRN_SUCCESS; + } + case GRN_TS_OP_TIME: { + if (node->args[0]->data_kind != GRN_TS_TEXT) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + node->data_kind = GRN_TS_TIME; + node->data_type = GRN_DB_TIME; + return GRN_SUCCESS; + } + case GRN_TS_OP_LOGICAL_AND: + case GRN_TS_OP_LOGICAL_OR: + case GRN_TS_OP_LOGICAL_SUB: { + if ((node->args[0]->data_kind != GRN_TS_BOOL) || + (node->args[1]->data_kind != GRN_TS_BOOL)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d, %d", + node->args[0]->data_kind, node->args[1]->data_kind); + } + node->data_kind = GRN_TS_BOOL; + node->data_type = GRN_DB_BOOL; + return GRN_SUCCESS; + } + case GRN_TS_OP_BITWISE_AND: + case GRN_TS_OP_BITWISE_OR: + case GRN_TS_OP_BITWISE_XOR: { + if (node->args[0]->data_kind != node->args[1]->data_kind) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "data kind conflict: %d != %d", + node->args[0]->data_kind, node->args[1]->data_kind); + } + switch (node->args[0]->data_kind) { + case GRN_TS_BOOL: + case GRN_TS_INT: { + node->data_kind = node->args[0]->data_kind; + node->data_type = grn_ts_data_kind_to_type(node->data_kind); + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } + node->data_kind = GRN_TS_BOOL; + node->data_type = GRN_DB_BOOL; + return GRN_SUCCESS; + } + case GRN_TS_OP_EQUAL: + case GRN_TS_OP_NOT_EQUAL: { + grn_ts_data_kind scalar_data_kind; + if (node->args[0]->data_kind != node->args[1]->data_kind) { + grn_rc rc = grn_ts_expr_op_node_typecast_args_for_cmp(ctx, node); + if (rc != GRN_SUCCESS) { + return rc; + } + } + scalar_data_kind = node->args[0]->data_kind & ~GRN_TS_VECTOR_FLAG; + if (((scalar_data_kind == GRN_TS_REF) || + (scalar_data_kind == GRN_TS_GEO)) && + (node->args[0]->data_type != node->args[1]->data_type)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "data type conflict: %d != %d", + node->args[0]->data_type, node->args[1]->data_type); + } + node->data_kind = GRN_TS_BOOL; + node->data_type = GRN_DB_BOOL; + return GRN_SUCCESS; + } + case GRN_TS_OP_LESS: + case GRN_TS_OP_LESS_EQUAL: + case GRN_TS_OP_GREATER: + case GRN_TS_OP_GREATER_EQUAL: { + if (node->args[0]->data_kind != node->args[1]->data_kind) { + grn_rc rc = grn_ts_expr_op_node_typecast_args_for_cmp(ctx, node); + if (rc != GRN_SUCCESS) { + return rc; + } + } + switch (node->args[0]->data_kind) { + case GRN_TS_INT: + case GRN_TS_FLOAT: + case GRN_TS_TIME: + case GRN_TS_TEXT: + case GRN_TS_INT_VECTOR: + case GRN_TS_FLOAT_VECTOR: + case GRN_TS_TIME_VECTOR: + case GRN_TS_TEXT_VECTOR: { + node->data_kind = GRN_TS_BOOL; + node->data_type = GRN_DB_BOOL; + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } + case GRN_TS_OP_SHIFT_ARITHMETIC_LEFT: + case GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT: + case GRN_TS_OP_SHIFT_LOGICAL_LEFT: + case GRN_TS_OP_SHIFT_LOGICAL_RIGHT: { + if ((node->args[0]->data_kind != GRN_TS_INT) || + (node->args[1]->data_kind != GRN_TS_INT)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d, %d", + node->args[0]->data_kind, + node->args[1]->data_kind); + } + node->data_kind = GRN_TS_INT; + node->data_type = GRN_DB_INT64; + return GRN_SUCCESS; + } + case GRN_TS_OP_PLUS: { + return grn_ts_op_plus_check_args(ctx, node); + } + case GRN_TS_OP_MINUS: { + return grn_ts_op_minus_check_args(ctx, node); + } + case GRN_TS_OP_MULTIPLICATION: + case GRN_TS_OP_DIVISION: + case GRN_TS_OP_MODULUS: { + if (node->args[0]->data_kind != node->args[1]->data_kind) { + grn_rc rc; + if ((node->args[0]->data_kind == GRN_TS_INT) && + (node->args[1]->data_kind == GRN_TS_FLOAT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[0], + 1, &node->args[0]); + if (rc != GRN_SUCCESS) { + node->args[0] = NULL; + return rc; + } + } else if ((node->args[0]->data_kind == GRN_TS_FLOAT) && + (node->args[1]->data_kind == GRN_TS_INT)) { + rc = grn_ts_expr_op_node_open(ctx, GRN_TS_OP_FLOAT, &node->args[1], + 1, &node->args[1]); + if (rc != GRN_SUCCESS) { + node->args[1] = NULL; + return rc; + } + } else { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "data kind conflict: %d != %d", + node->args[0]->data_kind, + node->args[1]->data_kind); + } + } + switch (node->args[0]->data_kind) { + case GRN_TS_INT: + case GRN_TS_FLOAT: { + node->data_kind = node->args[0]->data_kind; + node->data_type = grn_ts_data_kind_to_type(node->data_kind); + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } + } + } + case GRN_TS_OP_MATCH: + case GRN_TS_OP_PREFIX_MATCH: + case GRN_TS_OP_SUFFIX_MATCH: { + if ((node->args[0]->data_kind != GRN_TS_TEXT) || + (node->args[1]->data_kind != GRN_TS_TEXT)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d, %d", + node->args[0]->data_kind, + node->args[1]->data_kind); + } + node->data_kind = GRN_TS_BOOL; + node->data_type = GRN_DB_BOOL; + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid operator: %d", + node->op_type); + } + } +} + +/* grn_ts_expr_op_node_setup() sets up an operator node. */ +static grn_rc +grn_ts_expr_op_node_setup(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + grn_rc rc = grn_ts_expr_op_node_deref_args(ctx, node); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_op_node_check_args(ctx, node); + if (rc != GRN_SUCCESS) { + return rc; + } + if (node->data_kind == GRN_TS_VOID) { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + GRN_TS_VOID); + } else if (node->data_type == GRN_DB_VOID) { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data type: %d", + GRN_DB_VOID); + } + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_op_node_open(grn_ctx *ctx, grn_ts_op_type op_type, + grn_ts_expr_node **args, size_t n_args, + grn_ts_expr_node **node) +{ + size_t i; + grn_rc rc; + grn_ts_expr_op_node *new_node = GRN_MALLOCN(grn_ts_expr_op_node, 1); + if (!new_node) { + for (i = 0; i < n_args; i++) { + grn_ts_expr_node_close(ctx, args[i]); + } + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_op_node)); + } + grn_ts_expr_op_node_init(ctx, new_node); + new_node->op_type = op_type; + for (i = 0; i < n_args; i++) { + new_node->args[i] = args[i]; + } + new_node->n_args = n_args; + rc = grn_ts_expr_op_node_setup(ctx, new_node); + if (rc != GRN_SUCCESS) { + grn_ts_expr_op_node_fin(ctx, new_node); + GRN_FREE(new_node); + return rc; + } + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_op_node_close() destroys a node. */ +static void +grn_ts_expr_op_node_close(grn_ctx *ctx, grn_ts_expr_op_node *node) +{ + grn_ts_expr_op_node_fin(ctx, node); + GRN_FREE(node); +} + +/* grn_ts_op_logical_not_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_logical_not_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i; + grn_ts_bool *out_ptr = (grn_ts_bool *)out; + grn_rc rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out); + if (rc != GRN_SUCCESS) { + return rc; + } + for (i = 0; i < n_in; i++) { + out_ptr[i] = grn_ts_op_logical_not_bool(out_ptr[i]); + } + return GRN_SUCCESS; +} + +/* grn_ts_op_bitwise_not_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_bitwise_not_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i; + grn_rc rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out); + if (rc != GRN_SUCCESS) { + return rc; + } + switch (node->data_kind) { + case GRN_TS_BOOL: { + grn_ts_bool *out_ptr = (grn_ts_bool *)out; + for (i = 0; i < n_in; i++) { + out_ptr[i] = grn_ts_op_bitwise_not_bool(out_ptr[i]); + } + return GRN_SUCCESS; + } + case GRN_TS_INT: { + grn_ts_int *out_ptr = (grn_ts_int *)out; + for (i = 0; i < n_in; i++) { + out_ptr[i] = grn_ts_op_bitwise_not_int(out_ptr[i]); + } + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} + +#define GRN_TS_OP_SIGN_EVALUATE_CASE(type, KIND, kind) \ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(out_ptr[i]);\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_OP_SIGN_EVALUATE(type) \ + size_t i;\ + grn_rc rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + switch (node->data_kind) {\ + GRN_TS_OP_SIGN_EVALUATE_CASE(type, INT, int)\ + GRN_TS_OP_SIGN_EVALUATE_CASE(type, FLOAT, float)\ + default: {\ + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\ + node->data_kind);\ + }\ + } +/* grn_ts_op_positive_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_positive_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_SIGN_EVALUATE(positive) +} + +/* grn_ts_op_negative_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_negative_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_SIGN_EVALUATE(negative) +} +#undef GRN_TS_OP_SIGN_EVALUATE +#undef GRN_TS_OP_SIGN_EVALUATE_CASE + +/* grn_ts_op_float_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_float_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i; + grn_ts_int *buf_ptr; + grn_ts_float *out_ptr = (grn_ts_float *)out; + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptr = (grn_ts_int *)node->bufs[0].ptr; + for (i = 0; i < n_in; i++) { + rc = grn_ts_op_float(ctx, buf_ptr[i], &out_ptr[i]); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_op_time_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_time_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i; + grn_ts_text *buf_ptr; + grn_ts_time *out_ptr = (grn_ts_time *)out; + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptr = (grn_ts_text *)node->bufs[0].ptr; + for (i = 0; i < n_in; i++) { + rc = grn_ts_op_time(ctx, buf_ptr[i], &out_ptr[i]); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_op_logical_and_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_logical_and_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i, j, count; + grn_rc rc; + grn_ts_bool *buf_ptrs[2], *out_ptr = (grn_ts_bool *)out; + grn_ts_buf *tmp_in_buf = &node->bufs[2]; + grn_ts_record *tmp_in; + + /* Evaluate the 1st argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr; + + /* Create a list of true records. */ + rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp_in = (grn_ts_record *)tmp_in_buf->ptr; + count = 0; + for (i = 0; i < n_in; i++) { + if (buf_ptrs[0][i]) { + tmp_in[count++] = in[i]; + } + } + + /* Evaluate the 2nd argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count, + &node->bufs[1]); + buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr; + + /* Merge the results. */ + count = 0; + for (i = 0, j = 0; i < n_in; i++) { + out_ptr[count++] = buf_ptrs[0][i] && buf_ptrs[1][j++]; + } + return GRN_SUCCESS; +} + +/* grn_ts_op_logical_or_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_logical_or_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i, j, count; + grn_rc rc; + grn_ts_bool *buf_ptrs[2], *out_ptr = (grn_ts_bool *)out; + grn_ts_buf *tmp_in_buf = &node->bufs[2]; + grn_ts_record *tmp_in; + + /* Evaluate the 1st argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr; + + /* Create a list of false records. */ + rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp_in = (grn_ts_record *)tmp_in_buf->ptr; + count = 0; + for (i = 0; i < n_in; i++) { + if (!buf_ptrs[0][i]) { + tmp_in[count++] = in[i]; + } + } + + /* Evaluate the 2nd argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count, + &node->bufs[1]); + buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr; + + /* Merge the results. */ + count = 0; + for (i = 0, j = 0; i < n_in; i++) { + out_ptr[count++] = buf_ptrs[0][i] || buf_ptrs[1][j++]; + } + return GRN_SUCCESS; +} + +/* grn_ts_op_logical_sub_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_logical_sub_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + size_t i, j, count; + grn_rc rc; + grn_ts_bool *buf_ptrs[2], *out_ptr = (grn_ts_bool *)out; + grn_ts_buf *tmp_in_buf = &node->bufs[2]; + grn_ts_record *tmp_in; + + /* Evaluate the 1st argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr; + + /* Create a list of true records. */ + rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp_in = (grn_ts_record *)tmp_in_buf->ptr; + count = 0; + for (i = 0; i < n_in; i++) { + if (buf_ptrs[0][i]) { + tmp_in[count++] = in[i]; + } + } + + /* Evaluate the 2nd argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count, + &node->bufs[1]); + buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr; + + /* Merge the results. */ + count = 0; + for (i = 0, j = 0; i < n_in; i++) { + out_ptr[count++] = buf_ptrs[0][i] && + grn_ts_op_logical_not_bool(buf_ptrs[1][j++]); + } + return GRN_SUCCESS; +} + +#define GRN_TS_OP_BITWISE_EVALUATE_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + /* + * Use the output buffer to put evaluation results of the 1st argument, + * because the data kind is same. + */\ + size_t i;\ + grn_rc rc;\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc == GRN_SUCCESS) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\ + in, n_in, &node->bufs[0]);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_ ## kind *buf_ptr = (grn_ts_ ## kind *)node->bufs[0].ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_bitwise_ ## type ## _ ## kind(out_ptr[i],\ + buf_ptr[i]);\ + }\ + }\ + }\ + return rc;\ + } +/* grn_ts_op_bitwise_and_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_bitwise_and_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->args[0]->data_kind) { + GRN_TS_OP_BITWISE_EVALUATE_CASE(and, BOOL, bool) + GRN_TS_OP_BITWISE_EVALUATE_CASE(and, INT, int) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} + +/* grn_ts_op_bitwise_or_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_bitwise_or_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->args[0]->data_kind) { + GRN_TS_OP_BITWISE_EVALUATE_CASE(or, BOOL, bool) + GRN_TS_OP_BITWISE_EVALUATE_CASE(or, INT, int) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} + +/* grn_ts_op_bitwise_xor_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_bitwise_xor_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->args[0]->data_kind) { + GRN_TS_OP_BITWISE_EVALUATE_CASE(xor, BOOL, bool) + GRN_TS_OP_BITWISE_EVALUATE_CASE(xor, INT, int) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} +#undef GRN_TS_OP_BITWISE_EVALUATE_CASE + +#define GRN_TS_OP_CHK_EVALUATE_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *buf_ptrs[] = {\ + (grn_ts_ ## kind *)node->bufs[0].ptr,\ + (grn_ts_ ## kind *)node->bufs[1].ptr\ + };\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i],\ + buf_ptrs[1][i]);\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, KIND, kind)\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, KIND ## _VECTOR, kind ## _vector) +#define GRN_TS_OP_CHK_EVALUATE(type)\ + size_t i;\ + grn_rc rc;\ + grn_ts_bool *out_ptr = (grn_ts_bool *)out;\ + if (node->args[0]->data_kind == GRN_TS_BOOL) {\ + /* + * Use the output buffer to put evaluation results of the 1st argument, + * because the data kind is same. + */\ + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_buf *buf = &node->bufs[0];\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\ + in, n_in, buf);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_bool *buf_ptr = (grn_ts_bool *)buf->ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type ## _bool(out_ptr[i], buf_ptr[i]);\ + }\ + }\ + }\ + return rc;\ + }\ + for (i = 0; i < 2; i++) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + switch (node->args[0]->data_kind) {\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, INT, int)\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, FLOAT, float)\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, TIME, time)\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, TEXT, text)\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, GEO, geo)\ + GRN_TS_OP_CHK_EVALUATE_CASE(type, REF, ref)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, BOOL, bool)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, INT, int)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, FLOAT, float)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, TIME, time)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, TEXT, text)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, GEO, geo)\ + GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE(type, REF, ref)\ + default: {\ + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\ + node->args[0]->data_kind);\ + }\ + } +/* grn_ts_op_equal_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_CHK_EVALUATE(equal) +} + +/* grn_ts_op_not_equal_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_not_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_CHK_EVALUATE(not_equal) +} +#undef GRN_TS_OP_CHK_EVALUATE +#undef GRN_TS_OP_CHK_EVALUATE_VECTOR_CASE +#undef GRN_TS_OP_CHK_EVALUATE_CASE + +#define GRN_TS_OP_CMP_EVALUATE_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *buf_ptrs[] = {\ + (grn_ts_ ## kind *)node->bufs[0].ptr,\ + (grn_ts_ ## kind *)node->bufs[1].ptr\ + };\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i],\ + buf_ptrs[1][i]);\ + }\ + return GRN_SUCCESS;\ + } +#define GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, KIND, kind)\ + GRN_TS_OP_CMP_EVALUATE_CASE(type, KIND ## _VECTOR, kind ## _vector) +#define GRN_TS_OP_CMP_EVALUATE(type)\ + size_t i;\ + grn_rc rc;\ + grn_ts_bool *out_ptr = (grn_ts_bool *)out;\ + for (i = 0; i < 2; i++) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + switch (node->args[0]->data_kind) {\ + GRN_TS_OP_CMP_EVALUATE_CASE(type, INT, int)\ + GRN_TS_OP_CMP_EVALUATE_CASE(type, FLOAT, float)\ + GRN_TS_OP_CMP_EVALUATE_CASE(type, TIME, time)\ + GRN_TS_OP_CMP_EVALUATE_CASE(type, TEXT, text)\ + GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, INT, int)\ + GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, FLOAT, float)\ + GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, TIME, time)\ + GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE(type, TEXT, text)\ + default: {\ + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\ + node->args[0]->data_kind);\ + }\ + } +/* grn_ts_op_less_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_less_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_CMP_EVALUATE(less) +} + +/* grn_ts_op_less_equal_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_less_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_CMP_EVALUATE(less_equal) +} + +/* grn_ts_op_greater_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_greater_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_CMP_EVALUATE(greater) +} + +/* grn_ts_op_greater_equal_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_greater_equal_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_CMP_EVALUATE(greater_equal) +} +#undef GRN_TS_OP_CMP_EVALUATE +#undef GRN_TS_OP_CMP_EVALUATE_VECTOR_CASE +#undef GRN_TS_OP_CMP_EVALUATE_CASE + +#define GRN_TS_OP_SHIFT_EVALUATE(type)\ + size_t i;\ + grn_rc rc;\ + grn_ts_int *out_ptr = (grn_ts_int *)out;\ + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc == GRN_SUCCESS) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\ + in, n_in, &node->bufs[0]);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_int *buf_ptr = (grn_ts_int *)node->bufs[0].ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_shift_ ## type(out_ptr[i], buf_ptr[i]);\ + }\ + }\ + }\ + return rc; +/* grn_ts_op_shift_arithmetic_left_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_shift_arithmetic_left_evaluate(grn_ctx *ctx, + grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_SHIFT_EVALUATE(arithmetic_left) +} + +/* grn_ts_op_shift_arithmetic_right_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_shift_arithmetic_right_evaluate(grn_ctx *ctx, + grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_SHIFT_EVALUATE(arithmetic_right) +} + +/* grn_ts_op_shift_logical_left_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_shift_logical_left_evaluate(grn_ctx *ctx, + grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_SHIFT_EVALUATE(logical_left) +} + +/* grn_ts_op_shift_logical_right_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_shift_logical_right_evaluate(grn_ctx *ctx, + grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_SHIFT_EVALUATE(logical_right) +} +#undef GRN_TS_OP_SHIFT_EVALUATE + +#define GRN_TS_OP_ARITH_EVALUATE(type, lhs_kind, rhs_kind)\ + /* + * Use the output buffer to put evaluation results of the 1st argument, + * because the data kind is same. + */\ + size_t i;\ + grn_rc rc;\ + grn_ts_ ## lhs_kind *out_ptr = (grn_ts_ ## lhs_kind *)out;\ + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc == GRN_SUCCESS) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\ + in, n_in, &node->bufs[0]);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_ ## rhs_kind *buf_ptr = (grn_ts_ ## rhs_kind *)node->bufs[0].ptr;\ + for (i = 0; i < n_in; i++) {\ + rc = grn_ts_op_ ## type ## _ ## lhs_kind ## _ ## rhs_kind(\ + ctx, out_ptr[i], buf_ptr[i], &out_ptr[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + }\ + }\ + return rc; + +#define GRN_TS_OP_ARITH_EVALUATE_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + /* + * Use the output buffer to put evaluation results of the 1st argument, + * because the data kind is same. + */\ + size_t i;\ + grn_rc rc;\ + grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\ + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc == GRN_SUCCESS) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\ + in, n_in, &node->bufs[0]);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_ ## kind *buf_ptr = (grn_ts_ ## kind *)node->bufs[0].ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type ## _ ## kind(out_ptr[i],\ + buf_ptr[i]);\ + }\ + }\ + }\ + return rc;\ + } +#define GRN_TS_OP_ARITH_EVALUATE_TIME_CASE(type, KIND, lhs, rhs)\ + case GRN_TS_ ## KIND: {\ + /* + * Use the output buffer to put evaluation results of the 1st argument, + * because the data kind is same. + */\ + size_t i;\ + grn_rc rc;\ + grn_ts_ ## lhs *out_ptr = (grn_ts_ ## lhs *)out;\ + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, out);\ + if (rc == GRN_SUCCESS) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1],\ + in, n_in, &node->bufs[0]);\ + if (rc == GRN_SUCCESS) {\ + grn_ts_ ## rhs *buf_ptr = (grn_ts_ ## rhs *)node->bufs[0].ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type ## _ ## lhs ## _ ## rhs(out_ptr[i],\ + buf_ptr[i]);\ + }\ + }\ + }\ + return rc;\ + } +/* grn_ts_op_plus_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_plus_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->args[0]->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(plus, int, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(plus, float, float) + } + case GRN_TS_TIME: { + switch (node->args[1]->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(plus, time, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(plus, time, float) + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "data kind conflict: %d, %d", + node->args[0]->data_kind, + node->args[1]->data_kind); + } + } + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} + +/* grn_ts_op_minus_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_minus_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->args[0]->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(minus, int, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(minus, float, float) + } + case GRN_TS_TIME: { + switch (node->args[1]->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(minus, time, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(minus, time, float) + } + case GRN_TS_TIME: { + size_t i; + grn_rc rc; + grn_ts_float *out_ptr = (grn_ts_float *)out; + grn_ts_time *buf_ptrs[2]; + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], in, n_in, + &node->bufs[1]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptrs[0] = (grn_ts_time *)node->bufs[0].ptr; + buf_ptrs[1] = (grn_ts_time *)node->bufs[1].ptr; + for (i = 0; i < n_in; i++) { + rc = grn_ts_op_minus_time_time(ctx, buf_ptrs[0][i], buf_ptrs[1][i], + &out_ptr[i]); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "data kind conflict: %d, %d", + node->args[0]->data_kind, + node->args[1]->data_kind); + } + } + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->args[0]->data_kind); + } + } +} +#undef GRN_TS_OP_ARITH_EVALUATE_TIME_CASE + +/* grn_ts_op_multiplication_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_multiplication_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + switch (node->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(multiplication, int, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(multiplication, float, float) + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} + +/* grn_ts_op_division_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_division_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(division, int, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(division, float, float) + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} + +/* grn_ts_op_modulus_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_modulus_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->data_kind) { + case GRN_TS_INT: { + GRN_TS_OP_ARITH_EVALUATE(modulus, int, int) + } + case GRN_TS_FLOAT: { + GRN_TS_OP_ARITH_EVALUATE(modulus, float, float) + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d", + node->data_kind); + } + } +} +#undef GRN_TS_OP_ARITH_EVALUATE_CASE + +#define GRN_TS_OP_MATCH_EVALUATE(type)\ + size_t i;\ + grn_rc rc;\ + grn_ts_bool *out_ptr = (grn_ts_bool *)out;\ + grn_ts_text *buf_ptrs[2];\ + for (i = 0; i < 2; i++) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + buf_ptrs[0] = (grn_ts_text *)node->bufs[0].ptr;\ + buf_ptrs[1] = (grn_ts_text *)node->bufs[1].ptr;\ + for (i = 0; i < n_in; i++) {\ + out_ptr[i] = grn_ts_op_ ## type(buf_ptrs[0][i], buf_ptrs[1][i]);\ + }\ + return GRN_SUCCESS;\ +/* grn_ts_op_match_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_match_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + GRN_TS_OP_MATCH_EVALUATE(match) +} + +/* grn_ts_op_prefix_match_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_prefix_match_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_MATCH_EVALUATE(prefix_match) +} + +/* grn_ts_op_suffix_match_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_op_suffix_match_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + GRN_TS_OP_MATCH_EVALUATE(suffix_match) +} +#undef GRN_TS_OP_MATCH_EVALUATE + +/* grn_ts_expr_op_node_evaluate() evaluates an operator. */ +static grn_rc +grn_ts_expr_op_node_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->op_type) { + case GRN_TS_OP_LOGICAL_NOT: { + return grn_ts_op_logical_not_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_BITWISE_NOT: { + return grn_ts_op_bitwise_not_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_POSITIVE: { + return grn_ts_op_positive_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_NEGATIVE: { + return grn_ts_op_negative_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_FLOAT: { + return grn_ts_op_float_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_TIME: { + return grn_ts_op_time_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_LOGICAL_AND: { + return grn_ts_op_logical_and_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_LOGICAL_OR: { + return grn_ts_op_logical_or_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_LOGICAL_SUB: { + return grn_ts_op_logical_sub_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_BITWISE_AND: { + return grn_ts_op_bitwise_and_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_BITWISE_OR: { + return grn_ts_op_bitwise_or_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_BITWISE_XOR: { + return grn_ts_op_bitwise_xor_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_EQUAL: { + return grn_ts_op_equal_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_NOT_EQUAL: { + return grn_ts_op_not_equal_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_LESS: { + return grn_ts_op_less_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_LESS_EQUAL: { + return grn_ts_op_less_equal_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_GREATER: { + return grn_ts_op_greater_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_GREATER_EQUAL: { + return grn_ts_op_greater_equal_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_SHIFT_ARITHMETIC_LEFT: { + return grn_ts_op_shift_arithmetic_left_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT: { + return grn_ts_op_shift_arithmetic_right_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_SHIFT_LOGICAL_LEFT: { + return grn_ts_op_shift_logical_left_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_SHIFT_LOGICAL_RIGHT: { + return grn_ts_op_shift_logical_right_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_PLUS: { + return grn_ts_op_plus_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_MINUS: { + return grn_ts_op_minus_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_MULTIPLICATION: { + return grn_ts_op_multiplication_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_DIVISION: { + return grn_ts_op_division_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_MODULUS: { + return grn_ts_op_modulus_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_MATCH: { + return grn_ts_op_match_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_PREFIX_MATCH: { + return grn_ts_op_prefix_match_evaluate(ctx, node, in, n_in, out); + } + case GRN_TS_OP_SUFFIX_MATCH: { + return grn_ts_op_suffix_match_evaluate(ctx, node, in, n_in, out); + } + // TODO: Add operators. + default: { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "operator not supported: %d", node->op_type); + } + } +} + +/* grn_ts_op_logical_not_filter() filters records. */ +static grn_rc +grn_ts_op_logical_not_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, count; + grn_rc rc; + grn_ts_bool *buf_ptr; + rc = grn_ts_buf_reserve(ctx, &node->bufs[0], sizeof(grn_ts_bool) * n_in); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptr = (grn_ts_bool *)node->bufs[0].ptr; + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, buf_ptr); + if (rc != GRN_SUCCESS) { + return rc; + } + for (i = 0, count = 0; i < n_in; i++) { + if (grn_ts_op_logical_not_bool(buf_ptr[i])) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; +} + +/* grn_ts_op_bitwise_not_filter() filters records. */ +static grn_rc +grn_ts_op_bitwise_not_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, count; + grn_rc rc; + grn_ts_bool *buf_ptr; + rc = grn_ts_buf_reserve(ctx, &node->bufs[0], sizeof(grn_ts_bool) * n_in); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptr = (grn_ts_bool *)node->bufs[0].ptr; + rc = grn_ts_expr_node_evaluate(ctx, node->args[0], in, n_in, buf_ptr); + if (rc != GRN_SUCCESS) { + return rc; + } + for (i = 0, count = 0; i < n_in; i++) { + if (grn_ts_op_bitwise_not_bool(buf_ptr[i])) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; +} + +/* grn_ts_op_logical_and_filter() filters records. */ +static grn_rc +grn_ts_op_logical_and_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + grn_rc rc = grn_ts_expr_node_filter(ctx, node->args[0], in, n_in, + out, n_out); + if (rc != GRN_SUCCESS) { + return rc; + } + return grn_ts_expr_node_filter(ctx, node->args[1], out, *n_out, out, n_out); +} + +/* grn_ts_op_logical_or_filter() filters records. */ +static grn_rc +grn_ts_op_logical_or_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, j, count; + grn_rc rc; + grn_ts_bool *buf_ptrs[2]; + grn_ts_buf *tmp_in_buf = &node->bufs[2]; + grn_ts_record *tmp_in; + + /* Evaluate the 1st argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptrs[0] = (grn_ts_bool *)node->bufs[0].ptr; + + /* Create a list of false records. */ + rc = grn_ts_buf_reserve(ctx, tmp_in_buf, sizeof(grn_ts_record) * n_in); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp_in = (grn_ts_record *)tmp_in_buf->ptr; + count = 0; + for (i = 0; i < n_in; i++) { + if (!buf_ptrs[0][i]) { + tmp_in[count++] = in[i]; + } + } + + /* Evaluate the 2nd argument. */ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], tmp_in, count, + &node->bufs[1]); + buf_ptrs[1] = (grn_ts_bool *)node->bufs[1].ptr; + + /* Merge the results. */ + count = 0; + for (i = 0, j = 0; i < n_in; i++) { + if (buf_ptrs[0][i] || buf_ptrs[1][j++]) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; +} + +/* grn_ts_op_logical_sub_filter() filters records. */ +static grn_rc +grn_ts_op_logical_sub_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, n, count; + grn_ts_bool *buf_ptr; + grn_rc rc = grn_ts_expr_node_filter(ctx, node->args[0], in, n_in, out, &n); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[1], out, n, + &node->bufs[0]); + buf_ptr = (grn_ts_bool *)node->bufs[0].ptr; + for (i = 0, count = 0; i < n; i++) { + if (grn_ts_op_logical_not_bool(buf_ptr[i])) { + out[count++] = out[i]; + } + } + *n_out = count; + return GRN_SUCCESS; +} + +#define GRN_TS_OP_BITWISE_FILTER(type)\ + size_t i, count = 0;\ + grn_ts_bool *buf_ptrs[2];\ + for (i = 0; i < 2; i++) {\ + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + buf_ptrs[i] = (grn_ts_bool *)node->bufs[i].ptr;\ + }\ + for (i = 0; i < n_in; i++) {\ + if (grn_ts_op_bitwise_ ## type ## _bool(buf_ptrs[0][i], buf_ptrs[1][i])) {\ + out[count++] = in[i];\ + }\ + }\ + *n_out = count;\ + return GRN_SUCCESS;\ +/* grn_ts_op_bitwise_and_filter() filters records. */ +static grn_rc +grn_ts_op_bitwise_and_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_BITWISE_FILTER(and); +} + +/* grn_ts_op_bitwise_or_filter() filters records. */ +static grn_rc +grn_ts_op_bitwise_or_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_BITWISE_FILTER(or); +} + +/* grn_ts_op_bitwise_xor_filter() filters records. */ +static grn_rc +grn_ts_op_bitwise_xor_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_BITWISE_FILTER(xor); +} +#undef GRN_TS_OP_BITWISE_FILTER_CASE + +#define GRN_TS_OP_CHK_FILTER_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *buf_ptrs[] = {\ + (grn_ts_ ## kind *)node->bufs[0].ptr,\ + (grn_ts_ ## kind *)node->bufs[1].ptr\ + };\ + for (i = 0; i < n_in; i++) {\ + if (grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i], buf_ptrs[1][i])) {\ + out[count++] = in[i];\ + }\ + }\ + *n_out = count;\ + return GRN_SUCCESS;\ + } +#define GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, KIND, kind)\ + GRN_TS_OP_CHK_FILTER_CASE(type, KIND ## _VECTOR, kind ## _vector) +#define GRN_TS_OP_CHK_FILTER(type)\ + size_t i, count = 0;\ + for (i = 0; i < 2; i++) {\ + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + switch (node->args[0]->data_kind) {\ + GRN_TS_OP_CHK_FILTER_CASE(type, BOOL, bool)\ + GRN_TS_OP_CHK_FILTER_CASE(type, INT, int)\ + GRN_TS_OP_CHK_FILTER_CASE(type, FLOAT, float)\ + GRN_TS_OP_CHK_FILTER_CASE(type, TIME, time)\ + GRN_TS_OP_CHK_FILTER_CASE(type, TEXT, text)\ + GRN_TS_OP_CHK_FILTER_CASE(type, GEO, geo)\ + GRN_TS_OP_CHK_FILTER_CASE(type, REF, ref)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, BOOL, bool)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, INT, int)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, FLOAT, float)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, TIME, time)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, TEXT, text)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, GEO, geo)\ + GRN_TS_OP_CHK_FILTER_VECTOR_CASE(type, REF, ref)\ + default: {\ + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\ + node->args[0]->data_kind);\ + }\ + } +/* grn_ts_op_equal_filter() filters records. */ +static grn_rc +grn_ts_op_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_CHK_FILTER(equal) +} + +/* grn_ts_op_not_equal_filter() filters records. */ +static grn_rc +grn_ts_op_not_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_CHK_FILTER(not_equal) +} +#undef GRN_TS_OP_CHK_FILTER +#undef GRN_TS_OP_CHK_FILTER_VECTOR_CASE +#undef GRN_TS_OP_CHK_FILTER_CASE + +#define GRN_TS_OP_CMP_FILTER_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *buf_ptrs[] = {\ + (grn_ts_ ## kind *)node->bufs[0].ptr,\ + (grn_ts_ ## kind *)node->bufs[1].ptr\ + };\ + for (i = 0; i < n_in; i++) {\ + if (grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i], buf_ptrs[1][i])) {\ + out[count++] = in[i];\ + }\ + }\ + *n_out = count;\ + return GRN_SUCCESS;\ + } +#define GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, KIND, kind)\ + GRN_TS_OP_CMP_FILTER_CASE(type, KIND ## _VECTOR, kind ## _vector) +#define GRN_TS_OP_CMP_FILTER(type)\ + size_t i, count = 0;\ + for (i = 0; i < 2; i++) {\ + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + switch (node->args[0]->data_kind) {\ + GRN_TS_OP_CMP_FILTER_CASE(type, INT, int)\ + GRN_TS_OP_CMP_FILTER_CASE(type, FLOAT, float)\ + GRN_TS_OP_CMP_FILTER_CASE(type, TIME, time)\ + GRN_TS_OP_CMP_FILTER_CASE(type, TEXT, text)\ + GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, INT, int)\ + GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, FLOAT, float)\ + GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, TIME, time)\ + GRN_TS_OP_CMP_FILTER_VECTOR_CASE(type, TEXT, text)\ + default: {\ + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid data kind: %d",\ + node->args[0]->data_kind);\ + }\ + } +/* grn_ts_op_less_filter() filters records. */ +static grn_rc +grn_ts_op_less_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_CMP_FILTER(less) +} + +/* grn_ts_op_less_equal_filter() filters records. */ +static grn_rc +grn_ts_op_less_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_CMP_FILTER(less_equal) +} + +/* grn_ts_op_greater_filter() filters records. */ +static grn_rc +grn_ts_op_greater_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_CMP_FILTER(greater) +} + +/* grn_ts_op_greater_equal_filter() filters records. */ +static grn_rc +grn_ts_op_greater_equal_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_CMP_FILTER(greater_equal) +} +#undef GRN_TS_OP_CMP_FILTER +#undef GRN_TS_OP_CMP_FILTER_VECTOR_CASE +#undef GRN_TS_OP_CMP_FILTER_CASE + +#define GRN_TS_OP_MATCH_FILTER_CASE(type, KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_ts_ ## kind *buf_ptrs[] = {\ + (grn_ts_ ## kind *)node->bufs[0].ptr,\ + (grn_ts_ ## kind *)node->bufs[1].ptr\ + };\ + for (i = 0; i < n_in; i++) {\ + if (grn_ts_op_ ## type ## _ ## kind(buf_ptrs[0][i], buf_ptrs[1][i])) {\ + out[count++] = in[i];\ + }\ + }\ + *n_out = count;\ + return GRN_SUCCESS;\ + } + +#define GRN_TS_OP_MATCH_FILTER(type)\ + size_t i, count = 0;\ + grn_ts_text *buf_ptrs[2];\ + for (i = 0; i < 2; i++) {\ + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], in, n_in,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + buf_ptrs[0] = (grn_ts_text *)node->bufs[0].ptr;\ + buf_ptrs[1] = (grn_ts_text *)node->bufs[1].ptr;\ + for (i = 0; i < n_in; i++) {\ + if (grn_ts_op_ ## type(buf_ptrs[0][i], buf_ptrs[1][i])) {\ + out[count++] = in[i];\ + }\ + }\ + *n_out = count;\ + return GRN_SUCCESS;\ +/* grn_ts_op_match_filter() filters records. */ +static grn_rc +grn_ts_op_match_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_MATCH_FILTER(match) +} + +/* grn_ts_op_prefix_match_filter() filters records. */ +static grn_rc +grn_ts_op_prefix_match_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_MATCH_FILTER(prefix_match) +} + +/* grn_ts_op_suffix_match_filter() filters records. */ +static grn_rc +grn_ts_op_suffix_match_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + GRN_TS_OP_MATCH_FILTER(suffix_match) +} +#undef GRN_TS_OP_MATCH_FILTER + +/* grn_ts_expr_op_node_filter() filters records. */ +static grn_rc +grn_ts_expr_op_node_filter(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + switch (node->op_type) { + case GRN_TS_OP_LOGICAL_NOT: { + return grn_ts_op_logical_not_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_BITWISE_NOT: { + return grn_ts_op_bitwise_not_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_LOGICAL_AND: { + return grn_ts_op_logical_and_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_LOGICAL_OR: { + return grn_ts_op_logical_or_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_LOGICAL_SUB: { + return grn_ts_op_logical_sub_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_BITWISE_AND: { + return grn_ts_op_bitwise_and_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_BITWISE_OR: { + return grn_ts_op_bitwise_or_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_BITWISE_XOR: { + return grn_ts_op_bitwise_xor_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_EQUAL: { + return grn_ts_op_equal_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_NOT_EQUAL: { + return grn_ts_op_not_equal_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_LESS: { + return grn_ts_op_less_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_LESS_EQUAL: { + return grn_ts_op_less_equal_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_GREATER: { + return grn_ts_op_greater_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_GREATER_EQUAL: { + return grn_ts_op_greater_equal_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_MATCH: { + return grn_ts_op_match_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_PREFIX_MATCH: { + return grn_ts_op_prefix_match_filter(ctx, node, in, n_in, out, n_out); + } + case GRN_TS_OP_SUFFIX_MATCH: { + return grn_ts_op_suffix_match_filter(ctx, node, in, n_in, out, n_out); + } + // TODO: Add operators. + default: { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "operator not supported: %d", node->op_type); + } + } +} + +#define GRN_TS_OP_SIGN_ADJUST(type)\ + size_t i;\ + grn_ts_float *buf_ptr;\ + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], io, n_io,\ + &node->bufs[0]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + buf_ptr = (grn_ts_float *)node->bufs[0].ptr;\ + for (i = 0; i < n_io; i++) {\ + grn_ts_float result = grn_ts_op_ ## type ## _float(buf_ptr[i]);\ + io[i].score = (grn_ts_score)result;\ + if (!isfinite(io[i].score)) {\ + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid score: %g", result);\ + }\ + }\ + return GRN_SUCCESS; +/* grn_ts_op_positive_adjust() updates scores. */ +static grn_rc +grn_ts_op_positive_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_SIGN_ADJUST(positive) +} + +/* grn_ts_op_negative_adjust() updates scores. */ +static grn_rc +grn_ts_op_negative_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_SIGN_ADJUST(negative) +} +#undef GRN_TS_OP_SIGN_ADJUST + +/* grn_ts_op_float_adjust() updates scores. */ +static grn_rc +grn_ts_op_float_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + size_t i; + grn_ts_int *buf_ptr; + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[0], io, n_io, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + buf_ptr = (grn_ts_int *)node->bufs[0].ptr; + for (i = 0; i < n_io; i++) { + grn_ts_float result; + rc = grn_ts_op_float(ctx, buf_ptr[i], &result); + io[i].score = (grn_ts_score)result; + if (!isfinite(io[i].score)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid score: %g", result); + } + } + return GRN_SUCCESS; +} + +#define GRN_TS_OP_ARITH_ADJUST(type)\ + grn_rc rc;\ + size_t i;\ + grn_ts_float *buf_ptrs[2];\ + for (i = 0; i < 2; i++) {\ + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->args[i], io, n_io,\ + &node->bufs[i]);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + }\ + buf_ptrs[0] = (grn_ts_float *)node->bufs[0].ptr;\ + buf_ptrs[1] = (grn_ts_float *)node->bufs[1].ptr;\ + for (i = 0; i < n_io; i++) {\ + grn_ts_float result;\ + rc = grn_ts_op_ ## type ## _float_float(ctx, buf_ptrs[0][i],\ + buf_ptrs[1][i], &result);\ + io[i].score = (grn_ts_score)result;\ + if (!isfinite(io[i].score)) {\ + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid score: %g", result);\ + }\ + }\ + return GRN_SUCCESS; +/* grn_ts_op_plus_adjust() updates scores. */ +static grn_rc +grn_ts_op_plus_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_ARITH_ADJUST(plus) +} + +/* grn_ts_op_minus_adjust() updates scores. */ +static grn_rc +grn_ts_op_minus_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_ARITH_ADJUST(minus) +} + +/* grn_ts_op_multiplication_adjust() updates scores. */ +static grn_rc +grn_ts_op_multiplication_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_ARITH_ADJUST(multiplication) +} + +/* grn_ts_op_division_adjust() updates scores. */ +static grn_rc +grn_ts_op_division_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_ARITH_ADJUST(division) +} + +/* grn_ts_op_modulus_adjust() updates scores. */ +static grn_rc +grn_ts_op_modulus_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + GRN_TS_OP_ARITH_ADJUST(modulus) +} +#undef GRN_TS_OP_ARITH_ADJUST + +/* grn_ts_expr_op_node_adjust() updates scores. */ +static grn_rc +grn_ts_expr_op_node_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node, + grn_ts_record *io, size_t n_io) +{ + switch (node->op_type) { + case GRN_TS_OP_POSITIVE: { + return grn_ts_op_positive_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_NEGATIVE: { + return grn_ts_op_negative_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_FLOAT: { + return grn_ts_op_float_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_PLUS: { + return grn_ts_op_plus_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_MINUS: { + return grn_ts_op_minus_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_MULTIPLICATION: { + return grn_ts_op_multiplication_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_DIVISION: { + return grn_ts_op_division_adjust(ctx, node, io, n_io); + } + case GRN_TS_OP_MODULUS: { + return grn_ts_op_modulus_adjust(ctx, node, io, n_io); + } + // TODO: Add operators. + default: { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "operator not supported: %d", node->op_type); + } + } +} + +/*------------------------------------------------------------- + * grn_ts_expr_bridge_node. + */ + +enum { GRN_TS_EXPR_BRIDGE_NODE_N_BUFS = 2 }; + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS + grn_ts_expr_node *src; + grn_ts_expr_node *dest; + grn_ts_buf bufs[GRN_TS_EXPR_BRIDGE_NODE_N_BUFS]; +} grn_ts_expr_bridge_node; + +/* grn_ts_expr_bridge_node_init() initializes a node. */ +static void +grn_ts_expr_bridge_node_init(grn_ctx *ctx, grn_ts_expr_bridge_node *node) +{ + size_t i; + memset(node, 0, sizeof(*node)); + node->type = GRN_TS_EXPR_BRIDGE_NODE; + node->src = NULL; + node->dest = NULL; + for (i = 0; i < GRN_TS_EXPR_BRIDGE_NODE_N_BUFS; i++) { + grn_ts_buf_init(ctx, &node->bufs[i]); + } +} + +/* grn_ts_expr_bridge_node_fin() finalizes a node. */ +static void +grn_ts_expr_bridge_node_fin(grn_ctx *ctx, grn_ts_expr_bridge_node *node) +{ + size_t i; + for (i = 0; i < GRN_TS_EXPR_BRIDGE_NODE_N_BUFS; i++) { + grn_ts_buf_fin(ctx, &node->bufs[i]); + } + if (node->dest) { + grn_ts_expr_node_close(ctx, node->dest); + } + if (node->src) { + grn_ts_expr_node_close(ctx, node->src); + } +} + +grn_rc +grn_ts_expr_bridge_node_open(grn_ctx *ctx, grn_ts_expr_node *src, + grn_ts_expr_node *dest, grn_ts_expr_node **node) +{ + grn_ts_expr_bridge_node *new_node = GRN_MALLOCN(grn_ts_expr_bridge_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_bridge_node)); + } + grn_ts_expr_bridge_node_init(ctx, new_node); + new_node->data_kind = dest->data_kind; + new_node->data_type = dest->data_type; + new_node->src = src; + new_node->dest = dest; + *node = (grn_ts_expr_node *)new_node; + return GRN_SUCCESS; +} + +/* grn_ts_expr_bridge_node_close() destroys a node. */ +static void +grn_ts_expr_bridge_node_close(grn_ctx *ctx, grn_ts_expr_bridge_node *node) +{ + grn_ts_expr_bridge_node_fin(ctx, node); + GRN_FREE(node); +} + +/* grn_ts_expr_bridge_node_evaluate() evaluates a bridge. */ +static grn_rc +grn_ts_expr_bridge_node_evaluate(grn_ctx *ctx, grn_ts_expr_bridge_node *node, + const grn_ts_record *in, size_t n_in, + void *out) +{ + grn_ts_record *tmp; + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->src, in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp = (grn_ts_record *)node->bufs[0].ptr; + return grn_ts_expr_node_evaluate(ctx, node->dest, tmp, n_in, out); +} + +/* grn_ts_expr_bridge_node_filter() filters records. */ +static grn_rc +grn_ts_expr_bridge_node_filter(grn_ctx *ctx, grn_ts_expr_bridge_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + size_t i, count; + grn_ts_bool *values; + grn_ts_record *tmp; + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->src, in, n_in, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp = (grn_ts_record *)node->bufs[0].ptr; + rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->dest, in, n_in, + &node->bufs[1]); + if (rc != GRN_SUCCESS) { + return rc; + } + values = (grn_ts_bool *)&node->bufs[1].ptr; + for (i = 0, count = 0; i < n_in; i++) { + if (values[i]) { + out[count++] = in[i]; + } + } + *n_out = count; + return GRN_SUCCESS; +} + +/* grn_ts_expr_bridge_node_adjust() updates scores. */ +static grn_rc +grn_ts_expr_bridge_node_adjust(grn_ctx *ctx, grn_ts_expr_bridge_node *node, + grn_ts_record *io, size_t n_io) +{ + size_t i; + grn_ts_record *tmp; + grn_rc rc = grn_ts_expr_node_evaluate_to_buf(ctx, node->src, io, n_io, + &node->bufs[0]); + if (rc != GRN_SUCCESS) { + return rc; + } + tmp = (grn_ts_record *)node->bufs[0].ptr; + rc = grn_ts_expr_node_adjust(ctx, node->dest, tmp, n_io); + if (rc != GRN_SUCCESS) { + return rc; + } + for (i = 0; i < n_io; i++) { + io[i].score = tmp[i].score; + } + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_expr_node. + */ + +#define GRN_TS_EXPR_NODE_CLOSE_CASE(TYPE, type)\ + case GRN_TS_EXPR_ ## TYPE ## _NODE: {\ + grn_ts_expr_ ## type ## _node *type ## _node;\ + type ## _node = (grn_ts_expr_ ## type ## _node *)node;\ + grn_ts_expr_ ## type ## _node_close(ctx, type ## _node);\ + return;\ + } +void +grn_ts_expr_node_close(grn_ctx *ctx, grn_ts_expr_node *node) +{ + switch (node->type) { + GRN_TS_EXPR_NODE_CLOSE_CASE(ID, id) + GRN_TS_EXPR_NODE_CLOSE_CASE(SCORE, score) + GRN_TS_EXPR_NODE_CLOSE_CASE(KEY, key) + GRN_TS_EXPR_NODE_CLOSE_CASE(VALUE, value) + GRN_TS_EXPR_NODE_CLOSE_CASE(CONST, const) + GRN_TS_EXPR_NODE_CLOSE_CASE(COLUMN, column) + GRN_TS_EXPR_NODE_CLOSE_CASE(OP, op) + GRN_TS_EXPR_NODE_CLOSE_CASE(BRIDGE, bridge) + } +} +#undef GRN_TS_EXPR_NODE_CLOSE_CASE + +/* grn_ts_expr_node_deref_once() resolves a reference. */ +static grn_rc +grn_ts_expr_node_deref_once(grn_ctx *ctx, grn_ts_expr_node *in, + grn_ts_expr_node **out) +{ + grn_rc rc; + grn_id table_id = in->data_type; + grn_ts_expr_node *key_node, *bridge_node; + grn_obj *table = grn_ctx_at(ctx, table_id); + if (!table) { + GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d", table_id); + } + if (!grn_ts_obj_is_table(ctx, table)) { + grn_obj_unlink(ctx, table); + GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "not table: %d", table_id); + } + rc = grn_ts_expr_key_node_open(ctx, table, &key_node); + grn_obj_unlink(ctx, table); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_bridge_node_open(ctx, in, key_node, &bridge_node); + if (rc != GRN_SUCCESS) { + grn_ts_expr_node_close(ctx, key_node); + return rc; + } + *out = bridge_node; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_node_deref(grn_ctx *ctx, grn_ts_expr_node **node_ptr) +{ + grn_ts_expr_node *node = *node_ptr, **in_ptr = NULL; + while ((node->data_kind & ~GRN_TS_VECTOR_FLAG) == GRN_TS_REF) { + grn_ts_expr_node *new_node= 0; + grn_rc rc = grn_ts_expr_node_deref_once(ctx, node, &new_node); + if (rc != GRN_SUCCESS) { + if (in_ptr) { + *in_ptr = NULL; + grn_ts_expr_node_close(ctx, node); + } + return rc; + } + if (node == *node_ptr) { + grn_ts_expr_bridge_node *bridge_node; + bridge_node = (grn_ts_expr_bridge_node *)new_node; + if (bridge_node->src != node) { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "broken bridge node"); + } + in_ptr = &bridge_node->src; + } + node = new_node; + } + *node_ptr = node; + return GRN_SUCCESS; +} + +#define GRN_TS_EXPR_NODE_EVALUATE_CASE(TYPE, type)\ + case GRN_TS_EXPR_ ## TYPE ## _NODE: {\ + grn_ts_expr_ ## type ## _node *type ## _node;\ + type ## _node = (grn_ts_expr_ ## type ## _node *)node;\ + return grn_ts_expr_ ## type ## _node_evaluate(ctx, type ## _node,\ + in, n_in, out);\ + } +grn_rc +grn_ts_expr_node_evaluate(grn_ctx *ctx, grn_ts_expr_node *node, + const grn_ts_record *in, size_t n_in, void *out) +{ + switch (node->type) { + GRN_TS_EXPR_NODE_EVALUATE_CASE(ID, id) + GRN_TS_EXPR_NODE_EVALUATE_CASE(SCORE, score) + GRN_TS_EXPR_NODE_EVALUATE_CASE(KEY, key) + GRN_TS_EXPR_NODE_EVALUATE_CASE(VALUE, value) + GRN_TS_EXPR_NODE_EVALUATE_CASE(CONST, const) + GRN_TS_EXPR_NODE_EVALUATE_CASE(COLUMN, column) + GRN_TS_EXPR_NODE_EVALUATE_CASE(OP, op) + GRN_TS_EXPR_NODE_EVALUATE_CASE(BRIDGE, bridge) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, + "invalid node type: %d", node->type); + } + } +} +#undef GRN_TS_EXPR_NODE_EVALUATE_CASE + +#define GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(KIND, kind)\ + case GRN_TS_ ## KIND: {\ + grn_rc rc = grn_ts_buf_reserve(ctx, out, sizeof(grn_ts_ ## kind) * n_in);\ + if (rc != GRN_SUCCESS) {\ + return rc;\ + }\ + return grn_ts_expr_node_evaluate(ctx, node, in, n_in, out->ptr);\ + } +#define GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(KIND, kind)\ + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(KIND ## _VECTOR, kind ## _vector) +grn_rc +grn_ts_expr_node_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_buf *out) +{ + switch (node->data_kind) { + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(BOOL, bool) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(INT, int) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(FLOAT, float) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(TIME, time) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(TEXT, text) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(GEO, geo) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE(REF, ref) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(BOOL, bool) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(INT, int) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(FLOAT, float) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(TIME, time) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(TEXT, text) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(GEO, geo) + GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE(REF, ref) + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, + "invalid data kind: %d", node->data_kind); + } + } +} +#undef GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_VECTOR_CASE +#undef GRN_TS_EXPR_NODE_EVALUATE_TO_BUF_CASE + +#define GRN_TS_EXPR_NODE_FILTER_CASE(TYPE, type)\ + case GRN_TS_EXPR_ ## TYPE ## _NODE: {\ + grn_ts_expr_ ## type ## _node *type ## _node;\ + type ## _node = (grn_ts_expr_ ## type ## _node *)node;\ + return grn_ts_expr_ ## type ## _node_filter(ctx, type ## _node,\ + in, n_in, out, n_out);\ + } +grn_rc +grn_ts_expr_node_filter(grn_ctx *ctx, grn_ts_expr_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out) +{ + if (node->data_kind != GRN_TS_BOOL) { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "invalid data kind: %d", node->data_kind); + } + switch (node->type) { + GRN_TS_EXPR_NODE_FILTER_CASE(KEY, key) + GRN_TS_EXPR_NODE_FILTER_CASE(VALUE, value) + GRN_TS_EXPR_NODE_FILTER_CASE(CONST, const) + GRN_TS_EXPR_NODE_FILTER_CASE(COLUMN, column) + GRN_TS_EXPR_NODE_FILTER_CASE(OP, op) + GRN_TS_EXPR_NODE_FILTER_CASE(BRIDGE, bridge) + default: { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "invalid node type: %d", node->type); + } + } +} +#undef GRN_TS_EXPR_NODE_FILTER_CASE + +#define GRN_TS_EXPR_NODE_ADJUST_CASE(TYPE, type)\ + case GRN_TS_EXPR_ ## TYPE ## _NODE: {\ + grn_ts_expr_ ## type ## _node *type ## _node;\ + type ## _node = (grn_ts_expr_ ## type ## _node *)node;\ + return grn_ts_expr_ ## type ## _node_adjust(ctx, type ## _node, io, n_io);\ + } +grn_rc +grn_ts_expr_node_adjust(grn_ctx *ctx, grn_ts_expr_node *node, + grn_ts_record *io, size_t n_io) +{ + if (node->data_kind != GRN_TS_FLOAT) { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "invalid data kind: %d", node->data_kind); + } + switch (node->type) { + GRN_TS_EXPR_NODE_ADJUST_CASE(SCORE, score) + GRN_TS_EXPR_NODE_ADJUST_CASE(KEY, key) + GRN_TS_EXPR_NODE_ADJUST_CASE(VALUE, value) + GRN_TS_EXPR_NODE_ADJUST_CASE(CONST, const) + GRN_TS_EXPR_NODE_ADJUST_CASE(COLUMN, column) + GRN_TS_EXPR_NODE_ADJUST_CASE(OP, op) + GRN_TS_EXPR_NODE_ADJUST_CASE(BRIDGE, bridge) + default: { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "invalid node type: %d", node->type); + } + } +} +#undef GRN_TS_EXPR_NODE_ADJUST_CASE diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.h b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.h new file mode 100644 index 00000000..bcc9f371 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_node.h @@ -0,0 +1,128 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_buf.h" +#include "ts_op.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GRN_TS_EXPR_ID_NODE, /* ID (_id). */ + GRN_TS_EXPR_SCORE_NODE, /* Score (_score). */ + GRN_TS_EXPR_KEY_NODE, /* Key (_key). */ + GRN_TS_EXPR_VALUE_NODE, /* Embedded value (_value). */ + GRN_TS_EXPR_CONST_NODE, /* Const. */ + GRN_TS_EXPR_COLUMN_NODE, /* Column. */ + GRN_TS_EXPR_OP_NODE, /* Operator. */ + GRN_TS_EXPR_BRIDGE_NODE /* Bridge to a subexpression. */ +} grn_ts_expr_node_type; + +#define GRN_TS_EXPR_NODE_COMMON_MEMBERS\ + grn_ts_expr_node_type type; /* Node type. */\ + grn_ts_data_kind data_kind; /* Abstract data type. */\ + grn_ts_data_type data_type; /* Detailed data type. */ + +typedef struct { + GRN_TS_EXPR_NODE_COMMON_MEMBERS +} grn_ts_expr_node; + +/* grn_ts_expr_id_node_open() creates a node associated with IDs (_id). */ +grn_rc grn_ts_expr_id_node_open(grn_ctx *ctx, grn_ts_expr_node **node); + +/* + * grn_ts_expr_score_node_open() creates a node associated with scores + * (_score). + */ +grn_rc grn_ts_expr_score_node_open(grn_ctx *ctx, grn_ts_expr_node **node); + +/* grn_ts_expr_key_node_open() creates a node associated with keys (_key). */ +grn_rc grn_ts_expr_key_node_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_node **node); + +/* + * grn_ts_expr_value_node_open() creates a node associated with values + * (_value). + */ +grn_rc grn_ts_expr_value_node_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_node **node); + +/* grn_ts_expr_const_node_open() creates a node associated with a const. */ +grn_rc grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind data_kind, + grn_ts_data_type data_type, + grn_ts_any value, grn_ts_expr_node **node); + +/* grn_ts_expr_column_node_open() creates a node associated with a column. */ +grn_rc grn_ts_expr_column_node_open(grn_ctx *ctx, grn_obj *column, + grn_ts_expr_node **node); + +/* + * grn_ts_expr_op_node_open() creates a node associated with an operator. + * Note that argument nodes are destroyed on failure. + */ +grn_rc grn_ts_expr_op_node_open(grn_ctx *ctx, grn_ts_op_type op_type, + grn_ts_expr_node **args, size_t n_args, + grn_ts_expr_node **node); + +/* grn_ts_expr_bridge_node_open() creates a node associated with a bridge. */ +grn_rc grn_ts_expr_bridge_node_open(grn_ctx *ctx, grn_ts_expr_node *src, + grn_ts_expr_node *dest, + grn_ts_expr_node **node); + +/* grn_ts_expr_node_close() destroys a node. */ +void grn_ts_expr_node_close(grn_ctx *ctx, grn_ts_expr_node *node); + +/* + * grn_ts_expr_node_deref() resolves references. + * + * If *node_ptr refers to a reference node, grn_ts_expr_node_deref() creates a + * key node associated with the destination table and creates a bridge node + * from *node_ptr to the key node. If the data kind of the bridge node is + * GRN_TS_REF, references are recursively resolved. + */ +grn_rc grn_ts_expr_node_deref(grn_ctx *ctx, grn_ts_expr_node **node_ptr); + +/* grn_ts_expr_node_evaluate() evaluates a subtree. */ +grn_rc grn_ts_expr_node_evaluate(grn_ctx *ctx, grn_ts_expr_node *node, + const grn_ts_record *in, size_t n_in, + void *out); + +/* grn_ts_expr_node_evaluate_to_buf() evaluates a subtree. */ +grn_rc grn_ts_expr_node_evaluate_to_buf(grn_ctx *ctx, grn_ts_expr_node *node, + const grn_ts_record *in, size_t n_in, + grn_ts_buf *out); + +/* grn_ts_expr_node_filter() filters records. */ +grn_rc grn_ts_expr_node_filter(grn_ctx *ctx, grn_ts_expr_node *node, + grn_ts_record *in, size_t n_in, + grn_ts_record *out, size_t *n_out); + +/* grn_ts_expr_node_adjust() updates scores. */ +grn_rc grn_ts_expr_node_adjust(grn_ctx *ctx, grn_ts_expr_node *node, + grn_ts_record *io, size_t n_io); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.c b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.c new file mode 100644 index 00000000..10e6d2fc --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.c @@ -0,0 +1,1329 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#include "ts_expr_parser.h" + +#include +#include + +#include "../grn_ctx.h" + +#include "ts_log.h" +#include "ts_str.h" +#include "ts_util.h" + +/*------------------------------------------------------------- + * grn_ts_expr_token. + */ + +#define GRN_TS_EXPR_TOKEN_INIT(TYPE)\ + memset(token, 0, sizeof(*token));\ + token->type = GRN_TS_EXPR_ ## TYPE ## _TOKEN;\ + token->src = src; +/* grn_ts_expr_dummy_token_init() initializes a token. */ +static void +grn_ts_expr_dummy_token_init(grn_ctx *ctx, grn_ts_expr_dummy_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(DUMMY) +} + +/* grn_ts_expr_start_token_init() initializes a token. */ +static void +grn_ts_expr_start_token_init(grn_ctx *ctx, grn_ts_expr_start_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(START) +} + +/* grn_ts_expr_end_token_init() initializes a token. */ +static void +grn_ts_expr_end_token_init(grn_ctx *ctx, grn_ts_expr_end_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(END) +} + +/* grn_ts_expr_const_token_init() initializes a token. */ +static void +grn_ts_expr_const_token_init(grn_ctx *ctx, grn_ts_expr_const_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(CONST); + grn_ts_buf_init(ctx, &token->buf); +} + +/* grn_ts_expr_name_token_init() initializes a token. */ +static void +grn_ts_expr_name_token_init(grn_ctx *ctx, grn_ts_expr_name_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(NAME); +} + +/* grn_ts_expr_op_token_init() initializes a token. */ +static void +grn_ts_expr_op_token_init(grn_ctx *ctx, grn_ts_expr_op_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(OP); +} + +/* grn_ts_expr_bridge_token_init() initializes a token. */ +static void +grn_ts_expr_bridge_token_init(grn_ctx *ctx, grn_ts_expr_bridge_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(BRIDGE) +} + +/* grn_ts_expr_bracket_token_init() initializes a token. */ +static void +grn_ts_expr_bracket_token_init(grn_ctx *ctx, grn_ts_expr_bracket_token *token, + grn_ts_str src) +{ + GRN_TS_EXPR_TOKEN_INIT(BRACKET) +} +#undef GRN_TS_EXPR_TOKEN_INIT + +/* grn_ts_expr_dummy_token_fin() finalizes a token. */ +static void +grn_ts_expr_dummy_token_fin(grn_ctx *ctx, grn_ts_expr_dummy_token *token) +{ + /* Nothing to do. */ +} + +/* grn_ts_expr_start_token_fin() finalizes a token. */ +static void +grn_ts_expr_start_token_fin(grn_ctx *ctx, grn_ts_expr_start_token *token) +{ + /* Nothing to do. */ +} + +/* grn_ts_expr_end_token_fin() finalizes a token. */ +static void +grn_ts_expr_end_token_fin(grn_ctx *ctx, grn_ts_expr_end_token *token) +{ + /* Nothing to do. */ +} + +/* grn_ts_expr_const_token_fin() finalizes a token. */ +static void +grn_ts_expr_const_token_fin(grn_ctx *ctx, grn_ts_expr_const_token *token) +{ + grn_ts_buf_fin(ctx, &token->buf); +} + +/* grn_ts_expr_name_token_fin() finalizes a token. */ +static void +grn_ts_expr_name_token_fin(grn_ctx *ctx, grn_ts_expr_name_token *token) +{ + /* Nothing to do. */ +} + +/* grn_ts_expr_op_token_fin() finalizes a token. */ +static void +grn_ts_expr_op_token_fin(grn_ctx *ctx, grn_ts_expr_op_token *token) +{ + /* Nothing to do. */ +} + +/* grn_ts_expr_bridge_token_fin() finalizes a token. */ +static void +grn_ts_expr_bridge_token_fin(grn_ctx *ctx, grn_ts_expr_bridge_token *token) +{ + /* Nothing to do. */ +} + +/* grn_ts_expr_bracket_token_fin() finalizes a token. */ +static void +grn_ts_expr_bracket_token_fin(grn_ctx *ctx, grn_ts_expr_bracket_token *token) +{ + /* Nothing to do. */ +} + +#define GRN_TS_EXPR_TOKEN_OPEN(TYPE, type)\ + grn_ts_expr_ ## type ## _token *new_token;\ + new_token = GRN_MALLOCN(grn_ts_expr_ ## type ## _token, 1);\ + if (!new_token) {\ + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,\ + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",\ + sizeof(grn_ts_expr_ ## type ## _token));\ + }\ + grn_ts_expr_ ## type ## _token_init(ctx, new_token, src);\ + *token = new_token; +/* grn_ts_expr_dummy_token_open() creates a token. */ +/* +static grn_rc +grn_ts_expr_dummy_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_dummy_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(DUMMY, dummy) + return GRN_SUCCESS; +} +*/ + +/* grn_ts_expr_start_token_open() creates a token. */ +static grn_rc +grn_ts_expr_start_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_start_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(START, start) + return GRN_SUCCESS; +} + +/* grn_ts_expr_end_token_open() creates a token. */ +static grn_rc +grn_ts_expr_end_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_end_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(END, end) + return GRN_SUCCESS; +} + +/* grn_ts_expr_const_token_open() creates a token. */ +static grn_rc +grn_ts_expr_const_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_const_token **token) + { + GRN_TS_EXPR_TOKEN_OPEN(CONST, const) + return GRN_SUCCESS; +} + +/* grn_ts_expr_name_token_open() creates a token. */ +static grn_rc +grn_ts_expr_name_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_name_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(NAME, name) + return GRN_SUCCESS; +} + +/* grn_ts_expr_op_token_open() creates a token. */ +static grn_rc +grn_ts_expr_op_token_open(grn_ctx *ctx, grn_ts_str src, grn_ts_op_type op_type, + grn_ts_expr_op_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(OP, op) + new_token->op_type = op_type; + return GRN_SUCCESS; +} + +/* grn_ts_expr_bridge_token_open() creates a token. */ +static grn_rc +grn_ts_expr_bridge_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_bridge_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(BRIDGE, bridge) + return GRN_SUCCESS; +} + +/* grn_ts_expr_bracket_token_open() creates a token. */ +static grn_rc +grn_ts_expr_bracket_token_open(grn_ctx *ctx, grn_ts_str src, + grn_ts_expr_bracket_token **token) +{ + GRN_TS_EXPR_TOKEN_OPEN(BRACKET, bracket) + return GRN_SUCCESS; +} +#undef GRN_TS_EXPR_TOKEN_OPEN + +#define GRN_TS_EXPR_TOKEN_CLOSE_CASE(TYPE, type)\ + case GRN_TS_EXPR_ ## TYPE ## _TOKEN: {\ + grn_ts_expr_ ## type ## _token *type ## _token;\ + type ## _token = (grn_ts_expr_ ## type ## _token *)token;\ + grn_ts_expr_ ## type ## _token_fin(ctx, type ## _token);\ + break;\ + } +/* grn_ts_expr_token_close() destroys a token. */ +static void +grn_ts_expr_token_close(grn_ctx *ctx, grn_ts_expr_token *token) +{ + switch (token->type) { + GRN_TS_EXPR_TOKEN_CLOSE_CASE(DUMMY, dummy) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(START, start) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(END, end) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(CONST, const) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(NAME, name) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(OP, op) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(BRACKET, bracket) + GRN_TS_EXPR_TOKEN_CLOSE_CASE(BRIDGE, bridge) + } + GRN_FREE(token); +} +#undef GRN_TS_EXPR_TOKEN_CLOSE_CASE + +/*------------------------------------------------------------- + * grn_ts_expr_parser. + */ + +/* grn_ts_expr_parser_init() initializes a parser. */ +static void +grn_ts_expr_parser_init(grn_ctx *ctx, grn_ts_expr_parser *parser) +{ + memset(parser, 0, sizeof(*parser)); + parser->builder = NULL; + grn_ts_buf_init(ctx, &parser->str_buf); + parser->tokens = NULL; + parser->dummy_tokens = NULL; + parser->stack = NULL; +} + +/* grn_ts_expr_parser_fin() finalizes a parser. */ +static void +grn_ts_expr_parser_fin(grn_ctx *ctx, grn_ts_expr_parser *parser) +{ + if (parser->stack) { + GRN_FREE(parser->stack); + } + if (parser->dummy_tokens) { + size_t i; + for (i = 0; i < parser->n_dummy_tokens; i++) { + grn_ts_expr_dummy_token_fin(ctx, &parser->dummy_tokens[i]); + } + GRN_FREE(parser->dummy_tokens); + } + if (parser->tokens) { + size_t i; + for (i = 0; i < parser->n_tokens; i++) { + grn_ts_expr_token_close(ctx, parser->tokens[i]); + } + GRN_FREE(parser->tokens); + } + grn_ts_buf_fin(ctx, &parser->str_buf); + if (parser->builder) { + grn_ts_expr_builder_close(ctx, parser->builder); + } +} + +grn_rc +grn_ts_expr_parser_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_parser **parser) +{ + grn_rc rc; + grn_ts_expr_parser *new_parser; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || !parser) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + new_parser = GRN_MALLOCN(grn_ts_expr_parser, 1); + if (!new_parser) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_expr_parser)); + } + grn_ts_expr_parser_init(ctx, new_parser); + rc = grn_ts_expr_builder_open(ctx, table, &new_parser->builder); + if (rc != GRN_SUCCESS) { + grn_ts_expr_parser_fin(ctx, new_parser); + GRN_FREE(new_parser); + return rc; + } + *parser = new_parser; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_expr_parser_close(grn_ctx *ctx, grn_ts_expr_parser *parser) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!parser) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + grn_ts_expr_parser_fin(ctx, parser); + GRN_FREE(parser); + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_start() creates the start token. */ +static grn_rc +grn_ts_expr_parser_tokenize_start(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + grn_ts_str token_str = { str.ptr, 0 }; + grn_ts_expr_start_token *new_token; + grn_rc rc = grn_ts_expr_start_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_end() creates the end token. */ +static grn_rc +grn_ts_expr_parser_tokenize_end(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + grn_ts_str token_str = { str.ptr, 0 }; + grn_ts_expr_end_token *new_token; + grn_rc rc = grn_ts_expr_end_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_number() tokenizes an Int or Float literal. */ +static grn_rc +grn_ts_expr_parser_tokenize_number(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + char *end; + grn_rc rc; + grn_ts_int int_value; + grn_ts_str token_str; + grn_ts_expr_const_token *new_token; + + int_value = strtol(str.ptr, &end, 0); + if ((end != str.ptr) && (*end != '.') && (*end != 'e')) { + if (grn_ts_byte_is_name_char(*end)) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, + "unterminated Int literal: \"%.*s\"", + (int)str.size, str.ptr); + } + token_str.ptr = str.ptr; + token_str.size = end - str.ptr; + rc = grn_ts_expr_const_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + new_token->data_kind = GRN_TS_INT; + new_token->content.as_int = int_value; + } else { + grn_ts_float float_value = strtod(str.ptr, &end); + if (end == str.ptr) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid number literal: \"%.*s\"", + (int)str.size, str.ptr); + } + if (grn_ts_byte_is_name_char(*end)) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, + "unterminated Float literal: \"%.*s\"", + (int)str.size, str.ptr); + } + token_str.ptr = str.ptr; + token_str.size = end - str.ptr; + rc = grn_ts_expr_const_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + new_token->data_kind = GRN_TS_FLOAT; + new_token->content.as_float = float_value; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_text() tokenizes a Text literal. */ +static grn_rc +grn_ts_expr_parser_tokenize_text(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + size_t i, n_escapes = 0; + grn_rc rc; + grn_ts_str token_str; + grn_ts_expr_const_token *new_token; + for (i = 1; i < str.size; i++) { + if (str.ptr[i] == '\\') { + i++; + n_escapes++; + } else if (str.ptr[i] == '"') { + break; + } + } + if (i >= str.size) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "no closing double quote: \"%.*s\"", + (int)str.size, str.ptr); + } + token_str.ptr = str.ptr; + token_str.size = i + 1; + rc = grn_ts_expr_const_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + new_token->data_kind = GRN_TS_TEXT; + if (n_escapes) { + char *buf_ptr; + const char *str_ptr = str.ptr + 1; + size_t size = token_str.size - 2 - n_escapes; + rc = grn_ts_buf_resize(ctx, &new_token->buf, size); + if (rc != GRN_SUCCESS) { + grn_ts_expr_token_close(ctx, (grn_ts_expr_token *)new_token); + return rc; + } + buf_ptr = (char *)new_token->buf.ptr; + for (i = 0; i < size; i++) { + if (str_ptr[i] == '\\') { + str_ptr++; + } + buf_ptr[i] = str_ptr[i]; + } + new_token->content.as_text.ptr = buf_ptr; + new_token->content.as_text.size = size; + } else { + new_token->content.as_text.ptr = token_str.ptr + 1; + new_token->content.as_text.size = token_str.size - 2; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_name() tokenizes a Bool literal or a name. */ +static grn_rc +grn_ts_expr_parser_tokenize_name(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + size_t i; + grn_ts_str token_str; + for (i = 1; i < str.size; i++) { + if (!grn_ts_byte_is_name_char(str.ptr[i])) { + break; + } + } + token_str.ptr = str.ptr; + token_str.size = i; + + if (grn_ts_str_is_bool(token_str)) { + grn_ts_expr_const_token *new_token; + grn_rc rc = grn_ts_expr_const_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + new_token->data_kind = GRN_TS_BOOL; + if (token_str.ptr[0] == 't') { + new_token->content.as_bool = GRN_TRUE; + } else { + new_token->content.as_bool = GRN_FALSE; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; + } + return grn_ts_expr_name_token_open(ctx, token_str, token); +} + +/* grn_ts_expr_parser_tokenize_bridge() tokenizes a bridge. */ +static grn_rc +grn_ts_expr_parser_tokenize_bridge(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + grn_ts_str token_str = { str.ptr, 1 }; + grn_ts_expr_bridge_token *new_token; + grn_rc rc = grn_ts_expr_bridge_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_bracket() tokenizes a bracket. */ +static grn_rc +grn_ts_expr_parser_tokenize_bracket(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, + grn_ts_expr_token **token) +{ + grn_ts_str token_str = { str.ptr, 1 }; + grn_ts_expr_bracket_token *new_token; + grn_rc rc = grn_ts_expr_bracket_token_open(ctx, token_str, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* + * grn_ts_expr_parsre_tokenize_sign() tokenizes an operator '+' or '-'. + * Note that '+' and '-' have two roles each. + * '+' is GRN_TS_OP_POSITIVE or GRN_TS_OP_PLUS. + * '-' is GRN_TS_OP_NEGATIVE or GRN_TS_OP_MINUS. + */ +static grn_rc +grn_ts_expr_parser_tokenize_sign(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + size_t n_args; + grn_rc rc; + grn_ts_op_type op_type; + grn_ts_str token_str = { str.ptr, 1 }; + grn_ts_expr_token *prev_token = parser->tokens[parser->n_tokens - 1]; + grn_ts_expr_op_token *new_token; + switch (prev_token->type) { + case GRN_TS_EXPR_START_TOKEN: + case GRN_TS_EXPR_OP_TOKEN: { + n_args = 1; + break; + } + case GRN_TS_EXPR_CONST_TOKEN: + case GRN_TS_EXPR_NAME_TOKEN: { + n_args = 2; + break; + } + case GRN_TS_EXPR_BRACKET_TOKEN: { + grn_ts_str bracket; + const grn_ts_expr_bracket_token *bracket_token; + bracket_token = (const grn_ts_expr_bracket_token *)prev_token; + bracket = bracket_token->src; + switch (bracket.ptr[0]) { + case '(': case '[': { + n_args = 1; + break; + } + case ')': case ']': { + n_args = 2; + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "undefined bracket: \"%.*s\"", + (int)bracket.size, bracket.ptr); + } + } + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence: %d", + prev_token->type); + } + } + if (token_str.ptr[0] == '+') { + op_type = (n_args == 1) ? GRN_TS_OP_POSITIVE : GRN_TS_OP_PLUS; + } else { + op_type = (n_args == 1) ? GRN_TS_OP_NEGATIVE : GRN_TS_OP_MINUS; + } + rc = grn_ts_expr_op_token_open(ctx, token_str, op_type, &new_token); + if (rc != GRN_SUCCESS) { + return rc; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_op() tokenizes an operator. */ +static grn_rc +grn_ts_expr_parser_tokenize_op(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + grn_rc rc = GRN_SUCCESS; + grn_ts_str token_str = str; + grn_ts_op_type op_type; + grn_ts_expr_op_token *new_token; + switch (str.ptr[0]) { + case '+': case '-': { + return grn_ts_expr_parser_tokenize_sign(ctx, parser, str, token); + } + case '!': { + if ((str.size >= 2) && (str.ptr[1] == '=')) { + token_str.size = 2; + op_type = GRN_TS_OP_NOT_EQUAL; + } else { + token_str.size = 1; + op_type = GRN_TS_OP_LOGICAL_NOT; + } + rc = grn_ts_expr_op_token_open(ctx, token_str, op_type, &new_token); + break; + } +#define GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE(label, TYPE_1, TYPE_2, TYPE_3,\ + TYPE_EQUAL)\ + case label: {\ + if ((str.size >= 2) && (str.ptr[1] == '=')) {\ + token_str.size = 2;\ + op_type = GRN_TS_OP_ ## TYPE_EQUAL;\ + } else if ((str.size >= 2) && (str.ptr[1] == label)) {\ + if ((str.size >= 3) && (str.ptr[2] == label)) {\ + token_str.size = 3;\ + op_type = GRN_TS_OP_ ## TYPE_3;\ + } else {\ + token_str.size = 2;\ + op_type = GRN_TS_OP_ ## TYPE_2;\ + }\ + } else {\ + token_str.size = 1;\ + op_type = GRN_TS_OP_ ## TYPE_1;\ + }\ + rc = grn_ts_expr_op_token_open(ctx, token_str, op_type, &new_token);\ + break;\ + } + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('<', LESS, SHIFT_ARITHMETIC_LEFT, + SHIFT_LOGICAL_LEFT, LESS_EQUAL) + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('>', GREATER, SHIFT_ARITHMETIC_RIGHT, + SHIFT_LOGICAL_RIGHT, GREATER_EQUAL) +#undef GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE + case '&': { + if ((str.size >= 2) && (str.ptr[1] == '&')) { + token_str.size = 2; + op_type = GRN_TS_OP_LOGICAL_AND; + } else if ((str.size >= 2) && (str.ptr[1] == '&')) { + token_str.size = 2; + op_type = GRN_TS_OP_LOGICAL_SUB; + } else { + token_str.size = 1; + op_type = GRN_TS_OP_BITWISE_AND; + } + rc = grn_ts_expr_op_token_open(ctx, token_str, op_type, &new_token); + break; + } + case '|': { + if ((str.size >= 2) && (str.ptr[1] == '|')) { + token_str.size = 2; + op_type = GRN_TS_OP_LOGICAL_OR; + } else { + token_str.size = 1; + op_type = GRN_TS_OP_BITWISE_OR; + } + rc = grn_ts_expr_op_token_open(ctx, token_str, op_type, &new_token); + break; + } + case '=': { + if ((str.size < 2) || (str.ptr[1] != '=')) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, + "single equal not available: =\"%.*s\"", + (int)str.size, str.ptr); + } + token_str.size = 2; + rc = grn_ts_expr_op_token_open(ctx, token_str, GRN_TS_OP_EQUAL, + &new_token); + break; + } +#define GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE(label, TYPE)\ + case label: {\ + token_str.size = 1;\ + rc = grn_ts_expr_op_token_open(ctx, token_str, GRN_TS_OP_ ## TYPE,\ + &new_token);\ + break;\ + } + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('~', BITWISE_NOT) + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('^', BITWISE_XOR) + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('*', MULTIPLICATION) + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('/', DIVISION) + GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE('%', MODULUS) +#undef GRN_TS_EXPR_PARSER_TOKENIZE_OP_CASE + case '@': { + if ((str.size >= 2) && (str.ptr[1] == '^')) { + token_str.size = 2; + op_type = GRN_TS_OP_PREFIX_MATCH; + } else if ((str.size >= 2) && (str.ptr[1] == '$')) { + token_str.size = 2; + op_type = GRN_TS_OP_SUFFIX_MATCH; + } else { + token_str.size = 1; + op_type = GRN_TS_OP_MATCH; + } + rc = grn_ts_expr_op_token_open(ctx, token_str, op_type, &new_token); + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid character: \"%.*s\"", + (int)str.size, str.ptr); + } + } + if (rc != GRN_SUCCESS) { + return rc; + } + *token = (grn_ts_expr_token *)new_token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize_next() extracts the next token. */ +static grn_rc +grn_ts_expr_parser_tokenize_next(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr_token **token) +{ + grn_ts_str rest; + if (!parser->n_tokens) { + return grn_ts_expr_parser_tokenize_start(ctx, parser, str, token); + } + rest = grn_ts_str_trim_left(str); + if (!rest.size) { + return grn_ts_expr_parser_tokenize_end(ctx, parser, rest, token); + } + if (grn_ts_str_has_number_prefix(rest)) { + grn_ts_expr_token *prev_token; + if ((rest.ptr[0] != '+') && (rest.ptr[0] != '-')) { + return grn_ts_expr_parser_tokenize_number(ctx, parser, rest, token); + } + prev_token = parser->tokens[parser->n_tokens - 1]; + switch (prev_token->type) { + case GRN_TS_EXPR_START_TOKEN: + case GRN_TS_EXPR_OP_TOKEN: { + return grn_ts_expr_parser_tokenize_number(ctx, parser, rest, token); + } + case GRN_TS_EXPR_BRACKET_TOKEN: { + if ((prev_token->src.ptr[0] == '(') || + (prev_token->src.ptr[0] == '[')) { + return grn_ts_expr_parser_tokenize_number(ctx, parser, rest, token); + } + break; + } + default: { + break; + } + } + } + if (rest.ptr[0] == '"') { + return grn_ts_expr_parser_tokenize_text(ctx, parser, rest, token); + } + if (grn_ts_byte_is_name_char(rest.ptr[0])) { + return grn_ts_expr_parser_tokenize_name(ctx, parser, rest, token); + } + switch (rest.ptr[0]) { + case '(': case ')': case '[': case ']': { + return grn_ts_expr_parser_tokenize_bracket(ctx, parser, rest, token); + } + case '.': { + return grn_ts_expr_parser_tokenize_bridge(ctx, parser, rest, token); + } + default: { + return grn_ts_expr_parser_tokenize_op(ctx, parser, rest, token); + } + } +} + +/* + * grn_ts_expr_parser_reserve_tokens() extends a token buffer for a new token. + */ +static grn_rc +grn_ts_expr_parser_reserve_tokens(grn_ctx *ctx, grn_ts_expr_parser *parser) +{ + size_t i, n_bytes, new_max_n_tokens; + grn_ts_expr_token **new_tokens; + if (parser->n_tokens < parser->max_n_tokens) { + return GRN_SUCCESS; + } + new_max_n_tokens = parser->n_tokens * 2; + if (!new_max_n_tokens) { + new_max_n_tokens = 1; + } + n_bytes = sizeof(grn_ts_expr_token *) * new_max_n_tokens; + new_tokens = (grn_ts_expr_token **)GRN_REALLOC(parser->tokens, n_bytes); + if (!new_tokens) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, + n_bytes); + } + for (i = parser->n_tokens; i < new_max_n_tokens; i++) { + new_tokens[i] = NULL; + } + parser->tokens = new_tokens; + parser->max_n_tokens = new_max_n_tokens; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_tokenize() tokenizes a string. */ +static grn_rc +grn_ts_expr_parser_tokenize(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str) +{ + grn_ts_str rest = str; + const char *end = str.ptr + str.size; + grn_ts_expr_token *token = NULL; + GRN_TS_DEBUG("str = \"%.*s\"", (int)str.size, str.ptr); + do { + grn_rc rc = grn_ts_expr_parser_reserve_tokens(ctx, parser); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_parser_tokenize_next(ctx, parser, rest, &token); + if (rc != GRN_SUCCESS) { + return rc; + } + if ((token->type != GRN_TS_EXPR_START_TOKEN) && + (token->type != GRN_TS_EXPR_END_TOKEN)) { + GRN_TS_DEBUG("token = \"%.*s\"", (int)token->src.size, token->src.ptr); + } + parser->tokens[parser->n_tokens++] = token; + rest.ptr = token->src.ptr + token->src.size; + rest.size = end - rest.ptr; + } while (token->type != GRN_TS_EXPR_END_TOKEN); + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_push_const() pushes a token to an expression. */ +static grn_rc +grn_ts_expr_parser_push_const(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_const_token *token) +{ + return grn_ts_expr_builder_push_const(ctx, parser->builder, token->data_kind, + GRN_DB_VOID, token->content); +} + +/* grn_ts_expr_parser_push_name() pushes a token to an expression. */ +static grn_rc +grn_ts_expr_parser_push_name(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_name_token *token) +{ + return grn_ts_expr_builder_push_name(ctx, parser->builder, token->src); +} + +/* grn_ts_expr_parser_push_op() pushes a token to an expression. */ +static grn_rc +grn_ts_expr_parser_push_op(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_op_token *token) +{ + return grn_ts_expr_builder_push_op(ctx, parser->builder, token->op_type); +} + +/* + * grn_ts_expr_parser_apply_one() applies a bridge or prior operator. + * If there is no target, this function returns GRN_END_OF_DATA. + */ +// FIXME: Support a ternary operator. +static grn_rc +grn_ts_expr_parser_apply_one(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_op_precedence precedence_threshold) +{ + grn_rc rc; + grn_ts_str src; + grn_ts_expr_token **stack = parser->stack; + grn_ts_expr_dummy_token *dummy_token; + size_t n_args, depth = parser->stack_depth; + if (depth < 2) { + return GRN_END_OF_DATA; + } + if (stack[depth - 1]->type != GRN_TS_EXPR_DUMMY_TOKEN) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "argument must be dummy token"); + } + + /* Check the number of arguments. */ + switch (stack[depth - 2]->type) { + case GRN_TS_EXPR_BRIDGE_TOKEN: { + rc = grn_ts_expr_builder_end_subexpr(ctx, parser->builder); + if (rc != GRN_SUCCESS) { + return rc; + } + n_args = 2; + break; + } + case GRN_TS_EXPR_OP_TOKEN: { + grn_ts_expr_op_token *op_token; + grn_ts_op_precedence precedence; + op_token = (grn_ts_expr_op_token *)stack[depth - 2]; + precedence = grn_ts_op_get_precedence(op_token->op_type); + if (precedence < precedence_threshold) { + return GRN_END_OF_DATA; + } + rc = grn_ts_expr_parser_push_op(ctx, parser, op_token); + if (rc != GRN_SUCCESS) { + return rc; + } + n_args = grn_ts_op_get_n_args(op_token->op_type); + break; + } + default: { + return GRN_END_OF_DATA; + } + } + + /* Concatenate the source strings. */ + switch (n_args) { + case 1: { + grn_ts_expr_token *arg = stack[depth - 1]; + src.ptr = stack[depth - 2]->src.ptr; + src.size = (arg->src.ptr + arg->src.size) - src.ptr; + break; + } + case 2: { + grn_ts_expr_token *args[2] = { stack[depth - 3], stack[depth - 1] }; + src.ptr = args[0]->src.ptr; + src.size = (args[1]->src.ptr + args[1]->src.size) - src.ptr; + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, + "invalid #arguments: %" GRN_FMT_SIZE, + n_args); + } + } + + /* Replace the operator and argument tokens with a dummy token. */ + dummy_token = &parser->dummy_tokens[parser->n_dummy_tokens++]; + GRN_TS_DEBUG("dummy token: \"%.*s\"", (int)src.size, src.ptr); + grn_ts_expr_dummy_token_init(ctx, dummy_token, src); + depth -= n_args + 1; + stack[depth++] = dummy_token; + parser->stack_depth = depth; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_apply() applies bridges and prior operators. */ +static grn_rc +grn_ts_expr_parser_apply(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_op_precedence precedence_threshold) +{ + for ( ; ; ) { + grn_rc rc = grn_ts_expr_parser_apply_one(ctx, parser, + precedence_threshold); + if (rc == GRN_END_OF_DATA) { + return GRN_SUCCESS; + } else if (rc != GRN_SUCCESS) { + return rc; + } + } +} + +/* grn_ts_expr_parser_analyze_op() analyzes a token. */ +static grn_rc +grn_ts_expr_parser_analyze_op(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_op_token *token) +{ + size_t n_args = grn_ts_op_get_n_args(token->op_type); + grn_ts_expr_token *ex_token = parser->stack[parser->stack_depth - 1]; + if (n_args == 1) { + if (ex_token->type == GRN_TS_EXPR_DUMMY_TOKEN) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + } else if (n_args == 2) { + grn_ts_op_precedence precedence = grn_ts_op_get_precedence(token->op_type); + grn_rc rc = grn_ts_expr_parser_apply(ctx, parser, precedence); + if (rc != GRN_SUCCESS) { + return rc; + } + } + parser->stack[parser->stack_depth++] = (grn_ts_expr_token *)token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_analyze_bridge() analyzes a token. */ +static grn_rc +grn_ts_expr_parser_analyze_bridge(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_bridge_token *token) +{ + grn_rc rc = grn_ts_expr_builder_begin_subexpr(ctx, parser->builder); + if (rc != GRN_SUCCESS) { + return rc; + } + parser->stack[parser->stack_depth++] = (grn_ts_expr_token *)token; + return GRN_SUCCESS; +} + +/* grn_ts_expr_parser_analyze_bracket() analyzes a token. */ +static grn_rc +grn_ts_expr_parser_analyze_bracket(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_bracket_token *token) +{ + grn_ts_expr_token *ex_token = parser->stack[parser->stack_depth - 1]; + switch (token->src.ptr[0]) { + case '(': { + if (ex_token->type == GRN_TS_EXPR_DUMMY_TOKEN) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + parser->stack[parser->stack_depth++] = (grn_ts_expr_token *)token; + return GRN_SUCCESS; + } + case '[': { + if (ex_token->type != GRN_TS_EXPR_DUMMY_TOKEN) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + parser->stack[parser->stack_depth++] = (grn_ts_expr_token *)token; + return GRN_SUCCESS; + } + case ')': case ']': { + grn_ts_expr_token *ex_ex_token; + grn_rc rc = grn_ts_expr_parser_apply(ctx, parser, 0); + if (rc != GRN_SUCCESS) { + return rc; + } + if (parser->stack_depth < 2) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + ex_ex_token = parser->stack[parser->stack_depth - 2]; + if (ex_ex_token->type != GRN_TS_EXPR_BRACKET_TOKEN) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + if (token->src.ptr[0] == ')') { + size_t depth = parser->stack_depth; + grn_ts_str src; + grn_ts_expr_dummy_token *dummy_token; + if (ex_ex_token->src.ptr[0] != '(') { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + src.ptr = ex_ex_token->src.ptr; + src.size = (token->src.ptr + token->src.size) - src.ptr; + dummy_token = &parser->dummy_tokens[parser->n_dummy_tokens++]; + GRN_TS_DEBUG("dummy token: \"%.*s\"", (int)src.size, src.ptr); + grn_ts_expr_dummy_token_init(ctx, dummy_token, src); + parser->stack[depth - 2] = dummy_token; + parser->stack_depth--; + // TODO: Apply a function. + } else if (token->src.ptr[0] == ']') { + size_t depth = parser->stack_depth; + if (ex_ex_token->src.ptr[0] != '[') { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "invalid token sequence"); + } + parser->stack[depth - 2] = parser->stack[depth - 1]; + parser->stack_depth--; + // TODO: Push a subscript operator. + } + return GRN_SUCCESS; + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, "undefined bracket: \"%.*s\"", + (int)token->src.size, token->src.ptr); + } + } +} + +/* grn_ts_expr_parser_analyze_token() analyzes a token. */ +static grn_rc +grn_ts_expr_parser_analyze_token(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_expr_token *token) +{ + switch (token->type) { + case GRN_TS_EXPR_START_TOKEN: { + parser->stack[parser->stack_depth++] = token; + return GRN_SUCCESS; + } + case GRN_TS_EXPR_END_TOKEN: { + return grn_ts_expr_parser_apply(ctx, parser, 0); + } + case GRN_TS_EXPR_CONST_TOKEN: { + grn_ts_expr_const_token *const_token = (grn_ts_expr_const_token *)token; + grn_ts_expr_dummy_token *dummy_token; + grn_rc rc = grn_ts_expr_parser_push_const(ctx, parser, const_token); + if (rc != GRN_SUCCESS) { + return rc; + } + dummy_token = &parser->dummy_tokens[parser->n_dummy_tokens++]; + grn_ts_expr_dummy_token_init(ctx, dummy_token, token->src); + parser->stack[parser->stack_depth++] = dummy_token; + return GRN_SUCCESS; + } + case GRN_TS_EXPR_NAME_TOKEN: { + grn_ts_expr_name_token *name_token = (grn_ts_expr_name_token *)token; + grn_ts_expr_dummy_token *dummy_token; + grn_rc rc = grn_ts_expr_parser_push_name(ctx, parser, name_token); + if (rc != GRN_SUCCESS) { + return rc; + } + dummy_token = &parser->dummy_tokens[parser->n_dummy_tokens++]; + grn_ts_expr_dummy_token_init(ctx, dummy_token, token->src); + parser->stack[parser->stack_depth++] = dummy_token; + return GRN_SUCCESS; + } + case GRN_TS_EXPR_OP_TOKEN: { + grn_ts_expr_op_token *op_token = (grn_ts_expr_op_token *)token; + return grn_ts_expr_parser_analyze_op(ctx, parser, op_token); + } + case GRN_TS_EXPR_BRIDGE_TOKEN: { + grn_ts_expr_bridge_token *bridge_token; + bridge_token = (grn_ts_expr_bridge_token *)token; + return grn_ts_expr_parser_analyze_bridge(ctx, parser, bridge_token); + } + case GRN_TS_EXPR_BRACKET_TOKEN: { + grn_ts_expr_bracket_token *bracket_token; + bracket_token = (grn_ts_expr_bracket_token *)token; + return grn_ts_expr_parser_analyze_bracket(ctx, parser, bracket_token); + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid token type: %d", + token->type); + } + } +} + +/* grn_ts_expr_parser_analyze() analyzes tokens. */ +static grn_rc +grn_ts_expr_parser_analyze(grn_ctx *ctx, grn_ts_expr_parser *parser) +{ + size_t i; + + /* Reserve temporary work spaces. */ + if (parser->n_tokens > parser->max_n_dummy_tokens) { + size_t n_bytes = sizeof(grn_ts_expr_dummy_token) * parser->n_tokens; + grn_ts_expr_dummy_token *dummy_tokens = parser->dummy_tokens; + grn_ts_expr_dummy_token *new_dummy_tokens; + new_dummy_tokens = (grn_ts_expr_dummy_token *)GRN_REALLOC(dummy_tokens, + n_bytes); + if (!new_dummy_tokens) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, n_bytes); + } + parser->dummy_tokens = new_dummy_tokens; + parser->max_n_dummy_tokens = parser->n_tokens; + } + if (parser->n_tokens > parser->stack_size) { + size_t n_bytes = sizeof(grn_ts_expr_token *) * parser->n_tokens; + grn_ts_expr_token **new_stack; + new_stack = (grn_ts_expr_token **)GRN_REALLOC(parser->stack, n_bytes); + if (!new_stack) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_REALLOC failed: %" GRN_FMT_SIZE, n_bytes); + } + parser->stack = new_stack; + parser->stack_size = parser->n_tokens; + } + + /* Analyze tokens. */ + for (i = 0; i < parser->n_tokens; i++) { + grn_rc rc; + rc = grn_ts_expr_parser_analyze_token(ctx, parser, parser->tokens[i]); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (parser->stack_depth != 2) { + GRN_TS_ERR_RETURN(GRN_INVALID_FORMAT, + "tokens left in stack: %" GRN_FMT_SIZE, + parser->stack_depth); + } + return GRN_SUCCESS; +} + +/* + * grn_ts_expr_parser_clear() clears the internal states for parsing the next + * string. + */ +static void +grn_ts_expr_parser_clear(grn_ctx *ctx, grn_ts_expr_parser *parser) +{ + parser->stack_depth = 0; + if (parser->dummy_tokens) { + size_t i; + for (i = 0; i < parser->n_dummy_tokens; i++) { + grn_ts_expr_dummy_token_fin(ctx, &parser->dummy_tokens[i]); + } + parser->n_dummy_tokens = 0; + } + if (parser->tokens) { + size_t i; + for (i = 0; i < parser->n_tokens; i++) { + grn_ts_expr_token_close(ctx, parser->tokens[i]); + } + parser->n_tokens = 0; + } + grn_ts_expr_builder_clear(ctx, parser->builder); +} + +grn_rc +grn_ts_expr_parser_parse(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr **expr) +{ + grn_rc rc; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!parser || (!str.ptr && str.size)) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + grn_ts_expr_parser_clear(ctx, parser); + rc = grn_ts_buf_reserve(ctx, &parser->str_buf, str.size + 1); + if (rc != GRN_SUCCESS) { + return rc; + } + grn_memcpy(parser->str_buf.ptr, str.ptr, str.size); + ((char *)parser->str_buf.ptr)[str.size] = '\0'; + str.ptr = (const char *)parser->str_buf.ptr; + rc = grn_ts_expr_parser_tokenize(ctx, parser, str); + if (rc != GRN_SUCCESS) { + return rc; + } + rc = grn_ts_expr_parser_analyze(ctx, parser); + if (rc != GRN_SUCCESS) { + return rc; + } + return grn_ts_expr_builder_complete(ctx, parser->builder, expr); +} + +grn_rc +grn_ts_expr_parser_split(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_str *first, grn_ts_str *rest) +{ + size_t i; + char stack_top; + grn_rc rc = GRN_SUCCESS; + grn_ts_buf stack; + + // FIXME: `stack` should be a member of `parser`. + grn_ts_buf_init(ctx, &stack); + for ( ; ; ) { + str = grn_ts_str_trim_left(str); + if (!str.size) { + rc = GRN_END_OF_DATA; + break; + } + for (i = 0; i < str.size; i++) { + if (stack.pos) { + if (str.ptr[i] == stack_top) { + if (--stack.pos) { + stack_top = ((char *)stack.ptr)[stack.pos - 1]; + } + continue; + } + if (stack_top == '"') { + /* Skip the next byte of an escape character. */ + if ((str.ptr[i] == '\\') && (i < (str.size - 1))) { + i++; + } + continue; + } + } else if (str.ptr[i] == ',') { + /* An expression delimiter. */ + break; + } + switch (str.ptr[i]) { + case '(': { + stack_top = ')'; + rc = grn_ts_buf_write(ctx, &stack, &stack_top, 1); + break; + } + case '[': { + stack_top = ']'; + rc = grn_ts_buf_write(ctx, &stack, &stack_top, 1); + break; + } + case '{': { + stack_top = '}'; + rc = grn_ts_buf_write(ctx, &stack, &stack_top, 1); + break; + } + case '"': { + stack_top = '"'; + rc = grn_ts_buf_write(ctx, &stack, &stack_top, 1); + break; + } + } + if (rc != GRN_SUCCESS) { + break; + } + } + if (rc != GRN_SUCCESS) { + break; + } + if (i) { + /* Set the result. */ + first->ptr = str.ptr; + first->size = i; + if (first->size == str.size) { + rest->ptr = str.ptr + str.size; + rest->size = 0; + } else { + rest->ptr = str.ptr + first->size + 1; + rest->size = str.size - first->size - 1; + } + break; + } + str.ptr++; + str.size--; + } + grn_ts_buf_fin(ctx, &stack); + return rc; +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.h b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.h new file mode 100644 index 00000000..77205983 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_expr_parser.h @@ -0,0 +1,107 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "ts_expr.h" +#include "ts_expr_builder.h" +#include "ts_str.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GRN_TS_EXPR_DUMMY_TOKEN, /* No extra data. */ + GRN_TS_EXPR_START_TOKEN, /* No extra data. */ + GRN_TS_EXPR_END_TOKEN, /* No extra data. */ + GRN_TS_EXPR_CONST_TOKEN, /* +data_kind, content and buf. */ + GRN_TS_EXPR_NAME_TOKEN, /* +name. */ + GRN_TS_EXPR_OP_TOKEN, /* +op_type. */ + GRN_TS_EXPR_BRIDGE_TOKEN, /* No extra data. */ + GRN_TS_EXPR_BRACKET_TOKEN /* No extra data. */ +} grn_ts_expr_token_type; + +#define GRN_TS_EXPR_TOKEN_COMMON_MEMBERS\ + grn_ts_str src; /* Source string. */\ + grn_ts_expr_token_type type; /* Token type. */ + +typedef struct { + GRN_TS_EXPR_TOKEN_COMMON_MEMBERS +} grn_ts_expr_token; + +typedef grn_ts_expr_token grn_ts_expr_dummy_token; +typedef grn_ts_expr_token grn_ts_expr_start_token; +typedef grn_ts_expr_token grn_ts_expr_end_token; + +typedef struct { + GRN_TS_EXPR_TOKEN_COMMON_MEMBERS + grn_ts_data_kind data_kind; /* The data kind of the const. */ + grn_ts_any content; /* The const. */ + grn_ts_buf buf; /* Buffer for content.as_text. */ +} grn_ts_expr_const_token; + +typedef grn_ts_expr_token grn_ts_expr_name_token; + +typedef struct { + GRN_TS_EXPR_TOKEN_COMMON_MEMBERS + grn_ts_op_type op_type; /* Operator type. */ +} grn_ts_expr_op_token; + +typedef grn_ts_expr_token grn_ts_expr_bridge_token; +typedef grn_ts_expr_token grn_ts_expr_bracket_token; + +typedef struct { + grn_ts_expr_builder *builder; /* Builder. */ + grn_ts_buf str_buf; /* Buffer for a source string. */ + grn_ts_expr_token **tokens; /* Tokens. */ + size_t n_tokens; /* Number of tokens. */ + size_t max_n_tokens; /* Maximum number of tokens. */ + grn_ts_expr_dummy_token *dummy_tokens; /* Dummy tokens. */ + size_t n_dummy_tokens; /* Number of dummy tokens. */ + size_t max_n_dummy_tokens; /* Maximum number of dummy tokens. */ + grn_ts_expr_token **stack; /* Token stack. */ + size_t stack_depth; /* Token stack's current depth. */ + size_t stack_size; /* Token stack's capacity. */ +} grn_ts_expr_parser; + +/* grn_ts_expr_parser_open() creates a parser. */ +grn_rc grn_ts_expr_parser_open(grn_ctx *ctx, grn_obj *table, + grn_ts_expr_parser **parser); + +/* grn_ts_expr_parser_close() destroys a parser. */ +grn_rc grn_ts_expr_parser_close(grn_ctx *ctx, grn_ts_expr_parser *parser); + +/* grn_ts_expr_parser_parse() parses a string and creates an expression. */ +grn_rc grn_ts_expr_parser_parse(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_expr **expr); + +/* + * grn_ts_expr_parser_split() splits comma-separated strings into the first + * expression and the rest. + * Note that if `str` is empty, this function returns GRN_END_OF_DATA. + */ +grn_rc grn_ts_expr_parser_split(grn_ctx *ctx, grn_ts_expr_parser *parser, + grn_ts_str str, grn_ts_str *first, + grn_ts_str *rest); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_log.h b/storage/mroonga/vendor/groonga/lib/ts/ts_log.h new file mode 100644 index 00000000..844a0ee6 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_log.h @@ -0,0 +1,46 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" +#include "../grn_ctx.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* GRN_TS_DEBUG() logs a message that is useful for debug. */ +#define GRN_TS_DEBUG(...) GRN_LOG(ctx, GRN_LOG_DEBUG, __VA_ARGS__) + +/* GRN_TS_WARN() logs a warning. */ +#define GRN_TS_WARN(rc, ...) WARN(rc, __VA_ARGS__) + +/* GRN_TS_ERR() reports an error. */ +#define GRN_TS_ERR(rc, ...) ERR(rc, __VA_ARGS__) + +/* GRN_TS_ERR_RETURN() reports an error and returns its error code. */ +#define GRN_TS_ERR_RETURN(rc, ...) do {\ + GRN_TS_ERR(rc, __VA_ARGS__);\ + return rc;\ +} while (GRN_FALSE) + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_op.c b/storage/mroonga/vendor/groonga/lib/ts/ts_op.c new file mode 100644 index 00000000..64262b77 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_op.c @@ -0,0 +1,131 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#include "ts_op.h" + +size_t +grn_ts_op_get_n_args(grn_ts_op_type op_type) +{ + switch (op_type) { + case GRN_TS_OP_LOGICAL_NOT: /* !X */ + case GRN_TS_OP_BITWISE_NOT: /* ~X */ + case GRN_TS_OP_POSITIVE: /* +X */ + case GRN_TS_OP_NEGATIVE: /* -X */ + case GRN_TS_OP_FLOAT: + case GRN_TS_OP_TIME: { + return 1; + } + case GRN_TS_OP_LOGICAL_AND: /* X && Y */ + case GRN_TS_OP_LOGICAL_OR: /* X || Y */ + case GRN_TS_OP_LOGICAL_SUB: /* X &! Y */ + case GRN_TS_OP_BITWISE_AND: /* X & Y */ + case GRN_TS_OP_BITWISE_OR: /* X | Y */ + case GRN_TS_OP_BITWISE_XOR: /* X ^ Y */ + case GRN_TS_OP_EQUAL: /* X == Y */ + case GRN_TS_OP_NOT_EQUAL: /* X != Y */ + case GRN_TS_OP_LESS: /* X < Y */ + case GRN_TS_OP_LESS_EQUAL: /* X <= Y */ + case GRN_TS_OP_GREATER: /* X > Y */ + case GRN_TS_OP_GREATER_EQUAL: /* X >= Y */ + case GRN_TS_OP_SHIFT_ARITHMETIC_LEFT: /* X << Y */ + case GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT: /* X >> Y */ + case GRN_TS_OP_SHIFT_LOGICAL_LEFT: /* X <<< Y */ + case GRN_TS_OP_SHIFT_LOGICAL_RIGHT: /* X >>> Y */ + case GRN_TS_OP_PLUS: /* X + Y */ + case GRN_TS_OP_MINUS: /* X - Y */ + case GRN_TS_OP_MULTIPLICATION: /* X * Y */ + case GRN_TS_OP_DIVISION: /* X / Y */ + case GRN_TS_OP_MODULUS: /* X % Y */ + case GRN_TS_OP_MATCH: /* X @ Y */ + case GRN_TS_OP_PREFIX_MATCH: /* X @^ Y */ + case GRN_TS_OP_SUFFIX_MATCH: { /* X @$ Y */ + return 2; + } + default: { + return 0; + } + } +} + +grn_ts_op_precedence +grn_ts_op_get_precedence(grn_ts_op_type op_type) +{ + switch (op_type) { + case GRN_TS_OP_LOGICAL_NOT: + case GRN_TS_OP_BITWISE_NOT: + case GRN_TS_OP_POSITIVE: + case GRN_TS_OP_NEGATIVE: { + return 15; + } + case GRN_TS_OP_FLOAT: + case GRN_TS_OP_TIME: { + return 16; + } + case GRN_TS_OP_LOGICAL_AND: { + return 5; + } + case GRN_TS_OP_LOGICAL_OR: { + return 3; + } + case GRN_TS_OP_LOGICAL_SUB: { + return 4; + } + case GRN_TS_OP_BITWISE_AND: { + return 8; + } + case GRN_TS_OP_BITWISE_OR: { + return 6; + } + case GRN_TS_OP_BITWISE_XOR: { + return 7; + } + case GRN_TS_OP_EQUAL: + case GRN_TS_OP_NOT_EQUAL: { + return 9; + } + case GRN_TS_OP_LESS: + case GRN_TS_OP_LESS_EQUAL: + case GRN_TS_OP_GREATER: + case GRN_TS_OP_GREATER_EQUAL: { + return 10; + } + case GRN_TS_OP_SHIFT_ARITHMETIC_LEFT: + case GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT: + case GRN_TS_OP_SHIFT_LOGICAL_LEFT: + case GRN_TS_OP_SHIFT_LOGICAL_RIGHT: { + return 11; + } + case GRN_TS_OP_PLUS: + case GRN_TS_OP_MINUS: { + return 12; + } + case GRN_TS_OP_MULTIPLICATION: + case GRN_TS_OP_DIVISION: + case GRN_TS_OP_MODULUS: { + return 13; + } + case GRN_TS_OP_MATCH: + case GRN_TS_OP_PREFIX_MATCH: + case GRN_TS_OP_SUFFIX_MATCH: { + return 14; + } + default: { + return 0; + } + } +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_op.h b/storage/mroonga/vendor/groonga/lib/ts/ts_op.h new file mode 100644 index 00000000..7c34de96 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_op.h @@ -0,0 +1,87 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------- + * Operator types. + */ + +typedef enum { + /* Invalid operator. */ + GRN_TS_OP_NOP, + + /* Unary operators. */ + GRN_TS_OP_LOGICAL_NOT, /* !X */ + GRN_TS_OP_BITWISE_NOT, /* ~X */ + GRN_TS_OP_POSITIVE, /* +X */ + GRN_TS_OP_NEGATIVE, /* -X */ + + /* Typecast operators. */ + GRN_TS_OP_FLOAT, + GRN_TS_OP_TIME, + + /* Binary operators. */ + GRN_TS_OP_LOGICAL_AND, /* X && Y */ + GRN_TS_OP_LOGICAL_OR, /* X || Y */ + GRN_TS_OP_LOGICAL_SUB, /* X &! Y */ + GRN_TS_OP_BITWISE_AND, /* X & Y */ + GRN_TS_OP_BITWISE_OR, /* X | Y */ + GRN_TS_OP_BITWISE_XOR, /* X ^ Y */ + GRN_TS_OP_EQUAL, /* X == Y */ + GRN_TS_OP_NOT_EQUAL, /* X != Y */ + GRN_TS_OP_LESS, /* X < Y */ + GRN_TS_OP_LESS_EQUAL, /* X <= Y */ + GRN_TS_OP_GREATER, /* X > Y */ + GRN_TS_OP_GREATER_EQUAL, /* X >= Y */ + GRN_TS_OP_SHIFT_ARITHMETIC_LEFT, /* X << Y */ + GRN_TS_OP_SHIFT_ARITHMETIC_RIGHT, /* X >> Y */ + GRN_TS_OP_SHIFT_LOGICAL_LEFT, /* X <<< Y */ + GRN_TS_OP_SHIFT_LOGICAL_RIGHT, /* X >>> Y */ + GRN_TS_OP_PLUS, /* X + Y */ + GRN_TS_OP_MINUS, /* X - Y */ + GRN_TS_OP_MULTIPLICATION, /* X * Y */ + GRN_TS_OP_DIVISION, /* X / Y */ + GRN_TS_OP_MODULUS, /* X % Y */ + GRN_TS_OP_MATCH, /* X @ Y */ + GRN_TS_OP_PREFIX_MATCH, /* X @^ Y */ + GRN_TS_OP_SUFFIX_MATCH /* X @$ Y */ +} grn_ts_op_type; + +/* Operator precedence. */ +typedef int grn_ts_op_precedence; + +/* grn_ts_op_get_n_args() returns the number of arguments. */ +size_t grn_ts_op_get_n_args(grn_ts_op_type op_type); + +/* + * grn_ts_op_get_precedence() returns the precedence. + * A prior operator has a higher precedence. + */ +grn_ts_op_precedence grn_ts_op_get_precedence(grn_ts_op_type op_type); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_plan.c b/storage/mroonga/vendor/groonga/lib/ts/ts_plan.c new file mode 100644 index 00000000..3a8c9c0f --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_plan.c @@ -0,0 +1,21 @@ +/* -*- 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 +*/ + +#include "ts_plan.h" + +// TODO diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_plan.h b/storage/mroonga/vendor/groonga/lib/ts/ts_plan.h new file mode 100644 index 00000000..462bccfa --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_plan.h @@ -0,0 +1,87 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_buf.h" +#include "ts_cursor.h" +#include "ts_expr.h" +#include "ts_sorter.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int REMOVE_ME; +} grn_ts_plan_node; + +typedef struct { + grn_obj *table; + grn_ts_plan_node *root; +} grn_ts_plan; + +/* grn_ts_plan_open() creates a plan. */ +grn_rc grn_ts_plan_open(grn_ctx *ctx, grn_obj *table, grn_ts_plan_node *root, + grn_ts_plan **plan); + +/* grn_ts_plan_close() destroys a plan. */ +grn_rc grn_ts_plan_close(grn_ctx *ctx, grn_ts_plan *plan); + +/* grn_ts_plan_exec() executes a plan. */ +grn_rc grn_ts_plan_exec(grn_ctx *ctx, grn_ts_plan *plan, + grn_ts_rbuf *rbuf, size_t *n_hits); + +typedef struct { + grn_obj *table; +} grn_ts_planner; + +/* grn_ts_planner_open() creates a planner. */ +grn_rc grn_ts_planner_open(grn_ctx *ctx, grn_obj *table, + grn_ts_planner **planner); + +/* grn_ts_planner_close() destroys a planner. */ +grn_rc grn_ts_planner_close(grn_ctx *ctx, grn_ts_planner *planner); + +/* grn_ts_planner_complete() completes a planner. */ +grn_rc grn_ts_planner_complete(grn_ctx *ctx, grn_ts_planner *planner, + grn_ts_plan **plan); + +/* grn_ts_planner_push_cursor() pushes a cursor. */ +grn_rc grn_ts_planner_push_cursor(grn_ctx *ctx, grn_ts_planner *planner, + grn_ts_cursor *cursor); + +/* grn_ts_planner_push_filter() pushes a filter. */ +grn_rc grn_ts_planner_push_filter(grn_ctx *ctx, grn_ts_planner *planner, + grn_ts_expr *expr); + +/* grn_ts_planner_push_scorer() pushes a scorer. */ +grn_rc grn_ts_planner_push_scorer(grn_ctx *ctx, grn_ts_planner *planner, + grn_ts_expr *expr); + +/* grn_ts_planner_push_sorter() pushes a sorter. */ +grn_rc grn_ts_planner_push_sorter(grn_ctx *ctx, grn_ts_planner *planner, + grn_ts_sorter *sorter); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_sorter.c b/storage/mroonga/vendor/groonga/lib/ts/ts_sorter.c new file mode 100644 index 00000000..6a614663 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_sorter.c @@ -0,0 +1,2174 @@ +/* -*- 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 +*/ + +#include "ts_sorter.h" + +#include + +#include "ts_expr_parser.h" +#include "ts_log.h" +#include "ts_util.h" + +/*------------------------------------------------------------- + * grn_ts_sorter_node. + */ + +/* grn_ts_sorter_node_init() initializes a sorter node. */ +static void +grn_ts_sorter_node_init(grn_ctx *ctx, grn_ts_sorter_node *node) +{ + memset(node, 0, sizeof(*node)); + node->expr = NULL; + grn_ts_buf_init(ctx, &node->buf); + node->next = NULL; +} + +/* grn_ts_sorter_node_fin() finalizes a sorter node. */ +static void +grn_ts_sorter_node_fin(grn_ctx *ctx, grn_ts_sorter_node *node) +{ + grn_ts_buf_fin(ctx, &node->buf); + if (node->expr) { + grn_ts_expr_close(ctx, node->expr); + } +} + +/* grn_ts_sorter_node_open() creates a sorter nodes. */ +static grn_rc +grn_ts_sorter_node_open(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_bool reverse, + grn_ts_sorter_node **node) +{ + grn_ts_sorter_node *new_node; + new_node = GRN_MALLOCN(grn_ts_sorter_node, 1); + if (!new_node) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_sorter_node)); + } + grn_ts_sorter_node_init(ctx, new_node); + new_node->expr = expr; + new_node->reverse = reverse; + *node = new_node; + return GRN_SUCCESS; +} + +/* grn_ts_sorter_node_close() destroys a sorter node. */ +static void +grn_ts_sorter_node_close(grn_ctx *ctx, grn_ts_sorter_node *node) +{ + grn_ts_sorter_node_fin(ctx, node); + GRN_FREE(node); +} + +/* grn_ts_sorter_node_list_close() destroys a linked list of sorter nodes. */ +static void +grn_ts_sorter_node_list_close(grn_ctx *ctx, grn_ts_sorter_node *head) +{ + grn_ts_sorter_node *node = head; + while (node) { + grn_ts_sorter_node *next = node->next; + grn_ts_sorter_node_close(ctx, node); + node = next; + } +} + +/* grn_ts_sorter_node_progress() progresses sorting. */ +static grn_rc +grn_ts_sorter_node_progress(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs, size_t *n_rest) +{ + // TODO + return GRN_FUNCTION_NOT_IMPLEMENTED; +} + +/* grn_ts_sorter_node_complete() completes sorting. */ +static grn_rc +grn_ts_sorter_node_complete(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs, size_t *n_rest) +{ + // TODO + return GRN_FUNCTION_NOT_IMPLEMENTED; +} + +/* Forward declarations. */ +static grn_rc +grn_ts_sorter_node_sort(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs); + +/* grn_ts_rec_swap() swaps records. */ +inline static void +grn_ts_rec_swap(grn_ts_record *lhs, grn_ts_record *rhs) +{ + grn_ts_record tmp = *lhs; + *lhs = *rhs; + *rhs = tmp; +} + +/* grn_ts_int_swap() swaps Int values. */ +inline static void +grn_ts_int_swap(grn_ts_int *lhs, grn_ts_int *rhs) +{ + grn_ts_int tmp = *lhs; + *lhs = *rhs; + *rhs = tmp; +} + +/* FIXME: Sorting by _id does not assume ID duplicates. */ + +/* grn_ts_move_pivot_by_id_asc() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_id_asc(grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (recs[first].id < recs[middle].id) { + /* first < middle. */ + if (recs[middle].id < recs[last].id) { + /* first < middle < last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[first].id < recs[last].id) { + /* first < last < middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { + /* last < first < middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } + } else if (recs[last].id < recs[middle].id) { + /* last < middle < first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[last].id < recs[first].id) { + /* middle < last < first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { + /* middle < first < last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } +} + +/* grn_ts_isort_by_id_asc() sorts records. */ +static grn_rc +grn_ts_isort_by_id_asc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (recs[j].id < recs[j - 1].id) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + } else { + break; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_id_asc() sorts records. */ +static grn_rc +grn_ts_qsort_by_id_asc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_record pivot; + size_t left, right; + grn_ts_move_pivot_by_id_asc(recs, n_recs); + pivot = recs[0]; + left = 1; + right = n_recs; + for ( ; ; ) { + /* Move prior records to left. */ + while (left < right) { + if (pivot.id < recs[left].id) { + break; + } + ++left; + } + while (left < right) { + --right; + if (recs[right].id < pivot.id) { + break; + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + ++left; + } + /* Move the pivot to the boundary. */ + --left; + grn_ts_rec_swap(&recs[0], &recs[left]); + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_id_asc(ctx, node, offset, next_limit, recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_id_asc(ctx, node, next_offset, next_limit, + recs + right, n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + return grn_ts_isort_by_id_asc(ctx, node, offset, limit, recs, n_recs); + } + return GRN_SUCCESS; +} + +/* grn_ts_move_pivot_by_id_desc() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_id_desc(grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (recs[first].id > recs[middle].id) { + /* first > middle. */ + if (recs[middle].id > recs[last].id) { + /* first > middle > last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[first].id > recs[last].id) { + /* first > last > middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { + /* last > first > middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } + } else if (recs[last].id > recs[middle].id) { + /* last > middle > first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[last].id > recs[first].id) { + /* middle > last > first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { + /* middle > first > last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } +} + +/* grn_ts_isort_by_id_desc() sorts records. */ +static grn_rc +grn_ts_isort_by_id_desc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (recs[j].id > recs[j - 1].id) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + } else { + break; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_id_desc() sorts records. */ +static grn_rc +grn_ts_qsort_by_id_desc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_record pivot; + size_t left, right; + grn_ts_move_pivot_by_id_desc(recs, n_recs); + pivot = recs[0]; + left = 1; + right = n_recs; + for ( ; ; ) { + /* Move prior records to left. */ + while (left < right) { + if (pivot.id > recs[left].id) { + break; + } + ++left; + } + while (left < right) { + --right; + if (recs[right].id > pivot.id) { + break; + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + ++left; + } + /* Move the pivot to the boundary. */ + --left; + grn_ts_rec_swap(&recs[0], &recs[left]); + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_id_desc(ctx, node, offset, next_limit, + recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_id_desc(ctx, node, next_offset, next_limit, + recs + right, n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + return grn_ts_isort_by_id_desc(ctx, node, offset, limit, recs, n_recs); + } + return GRN_SUCCESS; +} + +/* grn_ts_sorter_node_sort_by_id() sorts records by _id. */ +static grn_rc +grn_ts_sorter_node_sort_by_id(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + if (node->reverse) { + return grn_ts_qsort_by_id_desc(ctx, node, offset, limit, recs, n_recs); + } else { + return grn_ts_qsort_by_id_asc(ctx, node, offset, limit, recs, n_recs); + } +} + +/* grn_ts_move_pivot_by_score_asc() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_score_asc(grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (recs[first].score < recs[middle].score) { + /* first < middle. */ + if (recs[middle].score < recs[last].score) { + /* first < middle < last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[first].score < recs[last].score) { + /* first < last < middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { /* last < first < middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } + } else if (recs[last].score < recs[middle].score) { + /* last < middle < first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[last].score < recs[first].score) { + /* middle < last < first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { /* middle < first < last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } +} + +/* grn_ts_isort_by_score_asc() sorts records. */ +static grn_rc +grn_ts_isort_by_score_asc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (recs[j].score < recs[j - 1].score) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + } else { + break; + } + } + } + /* Apply the next sorting if there are score duplicates. */ + if (node->next) { + grn_rc rc; + size_t begin = 0; + for (size_t i = 1; i < n_recs; ++i) { + if ((recs[i].score < recs[begin].score) || + (recs[i].score > recs[begin].score)) { + if ((i - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, i - begin, + recs + begin, i - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + begin = i; + } + } + if ((n_recs - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, n_recs - begin, + recs + begin, n_recs - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_score_asc() sorts records. */ +static grn_rc +grn_ts_qsort_by_score_asc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_record pivot; + size_t left, right; + size_t pivot_left, pivot_right; + grn_ts_move_pivot_by_score_asc(recs, n_recs); + pivot = recs[0]; + left = 1; + right = n_recs; + pivot_left = 1; + pivot_right = n_recs; + for ( ; ; ) { + /* + * Prior entries are moved to left. Less prior entries are moved to + * right. Entries which equal to the pivot are moved to the edges. + */ + while (left < right) { + if (pivot.score < recs[left].score) { + break; + } else if ((pivot.score <= recs[left].score) && + (pivot.score >= recs[left].score)) { + grn_ts_rec_swap(&recs[left], &recs[pivot_left]); + ++pivot_left; + } + ++left; + } + while (left < right) { + --right; + if (recs[right].score < pivot.score) { + break; + } else if ((recs[right].score <= pivot.score) && + (recs[right].score >= pivot.score)) { + --pivot_right; + grn_ts_rec_swap(&recs[right], &recs[pivot_right]); + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + ++left; + } + /* Move left pivot-equivalent entries to the left of the boundary. */ + while (pivot_left > 0) { + --pivot_left; + --left; + grn_ts_rec_swap(&recs[pivot_left], &recs[left]); + } + /* Move right pivot-equivalent entries to the right of the boundary. */ + while (pivot_right < n_recs) { + grn_ts_rec_swap(&recs[pivot_right], &recs[right]); + ++pivot_right; + ++right; + } + /* Apply the next sort condition to the pivot-equivalent recs. */ + if (node->next) { + if (((right - left) >= 2) && (offset < right) && (limit > left)) { + size_t next_offset = (offset < left) ? 0 : (offset - left); + size_t next_limit = ((limit > right) ? right : limit) - left; + rc = grn_ts_sorter_node_sort(ctx, node->next, next_offset, next_limit, + recs + left, right - left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_score_asc(ctx, node, offset, next_limit, + recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_score_asc(ctx, node, next_offset, next_limit, + recs + right, n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + rc = grn_ts_isort_by_score_asc(ctx, node, offset, limit, recs, n_recs); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_move_pivot_by_score_desc() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_score_desc(grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (recs[first].score > recs[middle].score) { + /* first > middle. */ + if (recs[middle].score > recs[last].score) { + /* first > middle > last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[first].score > recs[last].score) { + /* first > last > middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { /* last > first > middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } + } else if (recs[last].score > recs[middle].score) { + /* last > middle > first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + } else if (recs[last].score > recs[first].score) { + /* middle > last > first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + } else { /* middle > first > last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + } +} + +/* grn_ts_isort_by_score_desc() sorts records. */ +static grn_rc +grn_ts_isort_by_score_desc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (recs[j].score > recs[j - 1].score) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + } else { + break; + } + } + } + /* Apply the next sorting if there are score duplicates. */ + if (node->next) { + grn_rc rc; + size_t begin = 0; + for (size_t i = 1; i < n_recs; ++i) { + if ((recs[i].score < recs[begin].score) || + (recs[i].score > recs[begin].score)) { + if ((i - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, i - begin, + recs + begin, i - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + begin = i; + } + } + if ((n_recs - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, n_recs - begin, + recs + begin, n_recs - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_score_desc() sorts records. */ +static grn_rc +grn_ts_qsort_by_score_desc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_record pivot; + size_t left = 1, right = n_recs; + size_t pivot_left = 1, pivot_right = n_recs; + grn_ts_move_pivot_by_score_desc(recs, n_recs); + pivot = recs[0]; + for ( ; ; ) { + /* + * Prior entries are moved to left. Less prior entries are moved to + * right. Entries which equal to the pivot are moved to the edges. + */ + while (left < right) { + if (pivot.score > recs[left].score) { + break; + } else if ((pivot.score <= recs[left].score) && + (pivot.score >= recs[left].score)) { + grn_ts_rec_swap(&recs[left], &recs[pivot_left]); + ++pivot_left; + } + ++left; + } + while (left < right) { + --right; + if (recs[right].score > pivot.score) { + break; + } else if ((recs[right].score <= pivot.score) && + (recs[right].score >= pivot.score)) { + --pivot_right; + grn_ts_rec_swap(&recs[right], &recs[pivot_right]); + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + ++left; + } + /* Move left pivot-equivalent entries to the left of the boundary. */ + while (pivot_left > 0) { + --pivot_left; + --left; + grn_ts_rec_swap(&recs[pivot_left], &recs[left]); + } + /* Move right pivot-equivalent entries to the right of the boundary. */ + while (pivot_right < n_recs) { + grn_ts_rec_swap(&recs[pivot_right], &recs[right]); + ++pivot_right; + ++right; + } + /* Apply the next sort condition to the pivot-equivalent recs. */ + if (node->next) { + if (((right - left) >= 2) && (offset < right) && (limit > left)) { + size_t next_offset = (offset < left) ? 0 : (offset - left); + size_t next_limit = ((limit > right) ? right : limit) - left; + rc = grn_ts_sorter_node_sort(ctx, node->next, next_offset, next_limit, + recs + left, right - left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_score_desc(ctx, node, offset, next_limit, + recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_score_desc(ctx, node, next_offset, next_limit, + recs + right, n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + rc = grn_ts_isort_by_score_desc(ctx, node, offset, limit, recs, n_recs); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_sorter_node_sort_by_score() sorts records by _score. */ +static grn_rc +grn_ts_sorter_node_sort_by_score(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + if (node->reverse) { + return grn_ts_qsort_by_score_desc(ctx, node, offset, limit, recs, n_recs); + } else { + return grn_ts_qsort_by_score_asc(ctx, node, offset, limit, recs, n_recs); + } +} + +/* grn_ts_move_pivot_by_int() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_int(grn_ts_sorter_node *node, grn_ts_int *vals, + grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (vals[first] < vals[middle]) { + /* first < middle. */ + if (vals[middle] < vals[last]) { + /* first < middle < last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_int_swap(&vals[0], &vals[middle]); + } else if (vals[first] < vals[last]) { + /* first < last < middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_int_swap(&vals[0], &vals[last]); + } else { /* last < first < middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_int_swap(&vals[0], &vals[first]); + } + } else if (vals[last] < vals[middle]) { + /* last < middle < first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_int_swap(&vals[0], &vals[middle]); + } else if (vals[last] < vals[first]) { + /* middle < last < first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_int_swap(&vals[0], &vals[last]); + } else { /* middle < first < last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_int_swap(&vals[0], &vals[first]); + } +} + +/* grn_ts_isort_by_int() sorts records. */ +static grn_rc +grn_ts_isort_by_int(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_int *vals, grn_ts_record *recs, size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (vals[j] < vals[j - 1]) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + grn_ts_int_swap(&vals[j], &vals[j - 1]); + } else { + break; + } + } + } + /* Apply the next sorting if there are score duplicates. */ + if (node->next) { + grn_rc rc; + size_t begin = 0; + for (size_t i = 1; i < n_recs; ++i) { + if (vals[i] != vals[begin]) { + if ((i - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, i - begin, + recs + begin, i - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + begin = i; + } + } + if ((n_recs - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, n_recs - begin, + recs + begin, n_recs - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_int() sorts records. */ +static grn_rc +grn_ts_qsort_by_int(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_int *vals, grn_ts_record *recs, size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_int pivot; + size_t left, right; + size_t pivot_left, pivot_right; + grn_ts_move_pivot_by_int(node, vals, recs, n_recs); + pivot = vals[0]; + left = 1; + right = n_recs; + pivot_left = 1; + pivot_right = n_recs; + for ( ; ; ) { + /* + * Prior entries are moved to left. Less prior entries are moved to + * right. Entries which equal to the pivot are moved to the edges. + */ + while (left < right) { + if (pivot < vals[left]) { + break; + } else if (pivot == vals[left]) { + grn_ts_rec_swap(&recs[left], &recs[pivot_left]); + grn_ts_int_swap(&vals[left], &vals[pivot_left]); + ++pivot_left; + } + ++left; + } + while (left < right) { + --right; + if (vals[right] < pivot) { + break; + } else if (vals[right] == pivot) { + --pivot_right; + grn_ts_rec_swap(&recs[right], &recs[pivot_right]); + grn_ts_int_swap(&vals[right], &vals[pivot_right]); + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + grn_ts_int_swap(&vals[left], &vals[right]); + ++left; + } + /* Move left pivot-equivalent entries to the left of the boundary. */ + while (pivot_left > 0) { + --pivot_left; + --left; + grn_ts_rec_swap(&recs[pivot_left], &recs[left]); + grn_ts_int_swap(&vals[pivot_left], &vals[left]); + } + /* Move right pivot-equivalent entries to the right of the boundary. */ + while (pivot_right < n_recs) { + grn_ts_rec_swap(&recs[pivot_right], &recs[right]); + grn_ts_int_swap(&vals[pivot_right], &vals[right]); + ++pivot_right; + ++right; + } + /* Apply the next sort condition to the pivot-equivalent recs. */ + if (node->next) { + if (((right - left) >= 2) && (offset < right) && (limit > left)) { + size_t next_offset = (offset < left) ? 0 : (offset - left); + size_t next_limit = ((limit > right) ? right : limit) - left; + rc = grn_ts_sorter_node_sort(ctx, node->next, next_offset, next_limit, + recs + left, right - left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_int(ctx, node, offset, next_limit, + vals, recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + vals += right; + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_int(ctx, node, next_offset, next_limit, + vals + right, recs + right, n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + rc = grn_ts_isort_by_int(ctx, node, offset, limit, vals, recs, n_recs); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_text_cmp() compares Text values. */ +inline static int +grn_ts_text_cmp(grn_ts_text lhs, grn_ts_text rhs) +{ + size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size; + int result = memcmp(lhs.ptr, rhs.ptr, min_size); + if (result != 0) { + return result; + } + if (lhs.size == rhs.size) { + return 0; + } + return (lhs.size < rhs.size) ? -1 : 1; +} + +/* grn_ts_text_swap() swaps Text values. */ +inline static void +grn_ts_text_swap(grn_ts_text *lhs, grn_ts_text *rhs) +{ + grn_ts_text tmp = *lhs; + *lhs = *rhs; + *rhs = tmp; +} + +#if 0 +/* grn_ts_move_pivot_by_text_asc() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_text_asc(grn_ts_sorter_node *node, grn_ts_text *vals, + grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (grn_ts_text_cmp(vals[first], vals[middle]) < 0) { + /* first < middle. */ + if (grn_ts_text_cmp(vals[middle], vals[last]) < 0) { + /* first < middle < last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_text_swap(&vals[0], &vals[middle]); + } else if (grn_ts_text_cmp(vals[first], vals[last]) < 0) { + /* first < last < middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_text_swap(&vals[0], &vals[last]); + } else { /* last < first < middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_text_swap(&vals[0], &vals[first]); + } + } else if (grn_ts_text_cmp(vals[last], vals[middle]) < 0) { + /* last < middle < first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_text_swap(&vals[0], &vals[middle]); + } else if (grn_ts_text_cmp(vals[last], vals[first]) < 0) { + /* middle < last < first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_text_swap(&vals[0], &vals[last]); + } else { /* middle < first < last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_text_swap(&vals[0], &vals[first]); + } +} + +/* grn_ts_isort_by_text_asc() sorts records. */ +static grn_rc +grn_ts_isort_by_text_asc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_text *vals, grn_ts_record *recs, size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (grn_ts_text_cmp(vals[j], vals[j - 1]) < 0) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + grn_ts_text_swap(&vals[j], &vals[j - 1]); + } else { + break; + } + } + } + /* Apply the next sorting if there are score duplicates. */ + if (node->next) { + grn_rc rc; + size_t begin = 0; + for (size_t i = 1; i < n_recs; ++i) { + if (grn_ts_text_cmp(vals[i], vals[begin]) != 0) { + if ((i - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, i - begin, + recs + begin, i - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + begin = i; + } + } + if ((n_recs - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, n_recs - begin, + recs + begin, n_recs - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_text_asc() sorts records. */ +static grn_rc +grn_ts_qsort_by_text_asc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_text *vals, grn_ts_record *recs, size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_move_pivot_by_text_asc(node, vals, recs, n_recs); + grn_ts_text pivot = vals[0]; + size_t left = 1, right = n_recs; + size_t pivot_left = 1, pivot_right = n_recs; + for ( ; ; ) { + /* + * Prior entries are moved to left. Less prior entries are moved to + * right. Entries which equal to the pivot are moved to the edges. + */ + while (left < right) { + int result = grn_ts_text_cmp(pivot, vals[left]); + if (result < 0) { + break; + } else if (result == 0) { + grn_ts_rec_swap(&recs[left], &recs[pivot_left]); + grn_ts_text_swap(&vals[left], &vals[pivot_left]); + ++pivot_left; + } + ++left; + } + while (left < right) { + int result; + --right; + result = grn_ts_text_cmp(vals[right], pivot); + if (result < 0) { + break; + } else if (result == 0) { + --pivot_right; + grn_ts_rec_swap(&recs[right], &recs[pivot_right]); + grn_ts_text_swap(&vals[right], &vals[pivot_right]); + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + grn_ts_text_swap(&vals[left], &vals[right]); + ++left; + } + /* Move left pivot-equivalent entries to the left of the boundary. */ + while (pivot_left > 0) { + --pivot_left; + --left; + grn_ts_rec_swap(&recs[pivot_left], &recs[left]); + grn_ts_text_swap(&vals[pivot_left], &vals[left]); + } + /* Move right pivot-equivalent entries to the right of the boundary. */ + while (pivot_right < n_recs) { + grn_ts_rec_swap(&recs[pivot_right], &recs[right]); + grn_ts_text_swap(&vals[pivot_right], &vals[right]); + ++pivot_right; + ++right; + } + /* Apply the next sort condition to the pivot-equivalent recs. */ + if (node->next) { + if (((right - left) >= 2) && (offset < right) && (limit > left)) { + size_t next_offset = (offset < left) ? 0 : (offset - left); + size_t next_limit = ((limit > right) ? right : limit) - left; + rc = grn_ts_sorter_node_sort(ctx, node->next, next_offset, next_limit, + recs + left, right - left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_text_asc(ctx, node, offset, next_limit, + vals, recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + vals += right; + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_text_asc(ctx, node, next_offset, next_limit, + vals + right, recs + right, + n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + rc = grn_ts_isort_by_text_asc(ctx, node, offset, limit, + vals, recs, n_recs); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} +#endif + +/* grn_ts_move_pivot_by_text_desc() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_text_desc(grn_ts_sorter_node *node, grn_ts_text *vals, + grn_ts_record *recs, size_t n_recs) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + if (grn_ts_text_cmp(vals[first], vals[middle]) > 0) { + /* first < middle. */ + if (grn_ts_text_cmp(vals[middle], vals[last]) > 0) { + /* first < middle < last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_text_swap(&vals[0], &vals[middle]); + } else if (grn_ts_text_cmp(vals[first], vals[last]) > 0) { + /* first < last < middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_text_swap(&vals[0], &vals[last]); + } else { /* last < first < middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_text_swap(&vals[0], &vals[first]); + } + } else if (grn_ts_text_cmp(vals[last], vals[middle]) > 0) { + /* last < middle < first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_text_swap(&vals[0], &vals[middle]); + } else if (grn_ts_text_cmp(vals[last], vals[first]) > 0) { + /* middle < last < first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_text_swap(&vals[0], &vals[last]); + } else { /* middle < first < last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_text_swap(&vals[0], &vals[first]); + } +} + +/* grn_ts_isort_by_text_desc() sorts records. */ +static grn_rc +grn_ts_isort_by_text_desc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_text *vals, grn_ts_record *recs, + size_t n_recs) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (grn_ts_text_cmp(vals[j], vals[j - 1]) > 0) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + grn_ts_text_swap(&vals[j], &vals[j - 1]); + } else { + break; + } + } + } + /* Apply the next sorting if there are score duplicates. */ + if (node->next) { + grn_rc rc; + size_t begin = 0; + for (size_t i = 1; i < n_recs; ++i) { + if (grn_ts_text_cmp(vals[i], vals[begin]) != 0) { + if ((i - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, i - begin, + recs + begin, i - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + begin = i; + } + } + if ((n_recs - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, n_recs - begin, + recs + begin, n_recs - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_text_desc() sorts records. */ +static grn_rc +grn_ts_qsort_by_text_desc(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_text *vals, grn_ts_record *recs, + size_t n_recs) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + grn_ts_text pivot; + size_t left, right; + size_t pivot_left, pivot_right; + grn_ts_move_pivot_by_text_desc(node, vals, recs, n_recs); + pivot = vals[0]; + left = 1; + right = n_recs; + pivot_left = 1; + pivot_right = n_recs; + for ( ; ; ) { + /* + * Prior entries are moved to left. Less prior entries are moved to + * right. Entries which equal to the pivot are moved to the edges. + */ + while (left < right) { + int result = grn_ts_text_cmp(pivot, vals[left]); + if (result > 0) { + break; + } else if (result == 0) { + grn_ts_rec_swap(&recs[left], &recs[pivot_left]); + grn_ts_text_swap(&vals[left], &vals[pivot_left]); + ++pivot_left; + } + ++left; + } + while (left < right) { + int result; + --right; + result = grn_ts_text_cmp(vals[right], pivot); + if (result > 0) { + break; + } else if (result == 0) { + --pivot_right; + grn_ts_rec_swap(&recs[right], &recs[pivot_right]); + grn_ts_text_swap(&vals[right], &vals[pivot_right]); + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + grn_ts_text_swap(&vals[left], &vals[right]); + ++left; + } + /* Move left pivot-equivalent entries to the left of the boundary. */ + while (pivot_left > 0) { + --pivot_left; + --left; + grn_ts_rec_swap(&recs[pivot_left], &recs[left]); + grn_ts_text_swap(&vals[pivot_left], &vals[left]); + } + /* Move right pivot-equivalent entries to the right of the boundary. */ + while (pivot_right < n_recs) { + grn_ts_rec_swap(&recs[pivot_right], &recs[right]); + grn_ts_text_swap(&vals[pivot_right], &vals[right]); + ++pivot_right; + ++right; + } + /* Apply the next sort condition to the pivot-equivalent recs. */ + if (node->next) { + if (((right - left) >= 2) && (offset < right) && (limit > left)) { + size_t next_offset = (offset < left) ? 0 : (offset - left); + size_t next_limit = ((limit > right) ? right : limit) - left; + rc = grn_ts_sorter_node_sort(ctx, node->next, next_offset, next_limit, + recs + left, right - left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_text_desc(ctx, node, offset, next_limit, + vals, recs, left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + vals += right; + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_text_desc(ctx, node, next_offset, next_limit, + vals + right, recs + right, + n_recs - right); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + rc = grn_ts_isort_by_text_desc(ctx, node, offset, limit, + vals, recs, n_recs); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_text_get_label() returns a label. */ +inline static int +grn_ts_text_get_label(grn_ts_text val, size_t depth) +{ + return (depth < val.size) ? (uint8_t)val.ptr[depth] : -1; +} + +/* grn_ts_text_cmp2() compares Text values. */ +inline static int +grn_ts_text_cmp2(grn_ts_text lhs, grn_ts_text rhs, size_t depth) +{ + size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size; + int result = memcmp(lhs.ptr + depth, rhs.ptr + depth, min_size - depth); + if (result != 0) { + return result; + } + if (lhs.size == rhs.size) { + return 0; + } + return (lhs.size < rhs.size) ? -1 : 1; +} + +/* grn_ts_move_pivot_by_text_asc2() moves the pivot to the front. */ +static void +grn_ts_move_pivot_by_text_asc2(grn_ts_sorter_node *node, grn_ts_text *vals, + grn_ts_record *recs, size_t n_recs, size_t depth) +{ + /* Choose the median from recs[1], recs[n_recs / 2], and recs[n_recs - 2]. */ + size_t first = 1; + size_t middle = n_recs / 2; + size_t last = n_recs - 2; + int first_label = grn_ts_text_get_label(vals[first], depth); + int middle_label = grn_ts_text_get_label(vals[middle], depth); + int last_label = grn_ts_text_get_label(vals[last], depth); + if (first_label < middle_label) { + /* first < middle. */ + if (middle_label < last_label) { + /* first < middle < last */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_text_swap(&vals[0], &vals[middle]); + } else if (first_label < last_label) { + /* first < last < middle. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_text_swap(&vals[0], &vals[last]); + } else { /* last < first < middle. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_text_swap(&vals[0], &vals[first]); + } + } else if (last_label < middle_label) { + /* last < middle < first. */ + grn_ts_rec_swap(&recs[0], &recs[middle]); + grn_ts_text_swap(&vals[0], &vals[middle]); + } else if (last_label < first_label) { + /* middle < last < first. */ + grn_ts_rec_swap(&recs[0], &recs[last]); + grn_ts_text_swap(&vals[0], &vals[last]); + } else { /* middle < first < last. */ + grn_ts_rec_swap(&recs[0], &recs[first]); + grn_ts_text_swap(&vals[0], &vals[first]); + } +} + +/* grn_ts_isort_by_text_asc2() sorts records. */ +static grn_rc +grn_ts_isort_by_text_asc2(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, grn_ts_text *vals, + grn_ts_record *recs, size_t n_recs, size_t depth) +{ + for (size_t i = 1; i < n_recs; ++i) { + for (size_t j = i; j > 0; --j) { + if (grn_ts_text_cmp2(vals[j], vals[j - 1], depth) < 0) { + grn_ts_rec_swap(&recs[j], &recs[j - 1]); + grn_ts_text_swap(&vals[j], &vals[j - 1]); + } else { + break; + } + } + } + /* Apply the next sorting if there are score duplicates. */ + if (node->next) { + grn_rc rc; + size_t begin = 0; + for (size_t i = 1; i < n_recs; ++i) { + if (grn_ts_text_cmp2(vals[i], vals[begin], depth) != 0) { + if ((i - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, i - begin, + recs + begin, i - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + begin = i; + } + } + if ((n_recs - begin) >= 2) { + rc = grn_ts_sorter_node_sort(ctx, node->next, 0, n_recs - begin, + recs + begin, n_recs - begin); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + return GRN_SUCCESS; +} + +/* grn_ts_qsort_by_text_asc() sorts records. */ +static grn_rc +grn_ts_qsort_by_text_asc2(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, grn_ts_text *vals, + grn_ts_record *recs, size_t n_recs, size_t depth) +{ + grn_rc rc; + /* + * FIXME: Currently, the threshold is 16. + * This value should be optimized and replaced with a named constant. + */ + while (n_recs >= 16) { + int pivot; + size_t left, right; + size_t pivot_left, pivot_right; + grn_ts_move_pivot_by_text_asc2(node, vals, recs, n_recs, depth); + pivot = grn_ts_text_get_label(vals[0], depth); + left = 1; + right = n_recs; + pivot_left = 1; + pivot_right = n_recs; + for ( ; ; ) { + /* + * Prior entries are moved to left. Less prior entries are moved to + * right. Entries which equal to the pivot are moved to the edges. + */ + while (left < right) { + int label = grn_ts_text_get_label(vals[left], depth); + if (label > pivot) { + break; + } else if (label == pivot) { + grn_ts_rec_swap(&recs[left], &recs[pivot_left]); + grn_ts_text_swap(&vals[left], &vals[pivot_left]); + ++pivot_left; + } + ++left; + } + while (left < right) { + int label; + --right; + label = grn_ts_text_get_label(vals[right], depth); + if (label < pivot) { + break; + } else if (label == pivot) { + --pivot_right; + grn_ts_rec_swap(&recs[right], &recs[pivot_right]); + grn_ts_text_swap(&vals[right], &vals[pivot_right]); + } + } + if (left >= right) { + break; + } + grn_ts_rec_swap(&recs[left], &recs[right]); + grn_ts_text_swap(&vals[left], &vals[right]); + ++left; + } + /* Move left pivot-equivalent entries to the left of the boundary. */ + while (pivot_left > 0) { + --pivot_left; + --left; + grn_ts_rec_swap(&recs[pivot_left], &recs[left]); + grn_ts_text_swap(&vals[pivot_left], &vals[left]); + } + /* Move right pivot-equivalent entries to the right of the boundary. */ + while (pivot_right < n_recs) { + grn_ts_rec_swap(&recs[pivot_right], &recs[right]); + grn_ts_text_swap(&vals[pivot_right], &vals[right]); + ++pivot_right; + ++right; + } + /* Apply the next sort condition to the pivot-equivalent recs. */ + if (((right - left) >= 2) && (offset < right) && (limit > left)) { + size_t next_offset = (offset < left) ? 0 : (offset - left); + size_t next_limit = ((limit > right) ? right : limit) - left; + if (pivot != -1) { + rc = grn_ts_qsort_by_text_asc2(ctx, node, next_offset, next_limit, + vals, recs + left, right - left, + depth + 1); + } else if (node->next) { + rc = grn_ts_sorter_node_sort(ctx, node->next, next_offset, next_limit, + recs + left, right - left); + if (rc != GRN_SUCCESS) { + return rc; + } + } + } + /* + * Use a recursive call to sort the smaller group so that the recursion + * depth is less than log_2(n_recs). + */ + if (left < (n_recs - right)) { + if ((offset < left) && (left >= 2)) { + size_t next_limit = (limit < left) ? limit : left; + rc = grn_ts_qsort_by_text_asc2(ctx, node, offset, next_limit, + vals, recs, left, depth); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (limit <= right) { + return GRN_SUCCESS; + } + vals += right; + recs += right; + n_recs -= right; + offset = (offset < right) ? 0 : (offset - right); + limit -= right; + } else { + if ((limit > right) && ((n_recs - right) >= 2)) { + size_t next_offset = (offset < right) ? 0 : (offset - right); + size_t next_limit = limit - right; + rc = grn_ts_qsort_by_text_asc2(ctx, node, next_offset, next_limit, + vals + right, recs + right, + n_recs - right, depth); + if (rc != GRN_SUCCESS) { + return rc; + } + } + if (offset >= left) { + return GRN_SUCCESS; + } + n_recs = left; + if (limit > left) { + limit = left; + } + } + } + if (n_recs >= 2) { + rc = grn_ts_isort_by_text_asc2(ctx, node, offset, limit, + vals, recs, n_recs, depth); + if (rc != GRN_SUCCESS) { + return rc; + } + } + return GRN_SUCCESS; +} + +/* grn_ts_sorter_node_sort_by_var() sorts records. */ +static grn_rc +grn_ts_sorter_node_sort_by_var(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + size_t i; + grn_rc rc; + switch (node->expr->data_kind) { + case GRN_TS_INT: { + grn_ts_int *vals; + rc = grn_ts_expr_evaluate_to_buf(ctx, node->expr, recs, n_recs, + &node->buf); + if (rc != GRN_SUCCESS) { + return rc; + } + vals = (grn_ts_int *)node->buf.ptr; + if (node->reverse) { + for (i = 0; i < n_recs; i++) { + vals[i] = -1 - vals[i]; + } + } + return grn_ts_qsort_by_int(ctx, node, offset, limit, vals, recs, n_recs); + } + case GRN_TS_FLOAT: { + grn_ts_int *vals; + rc = grn_ts_expr_evaluate_to_buf(ctx, node->expr, recs, n_recs, + &node->buf); + if (rc != GRN_SUCCESS) { + return rc; + } + vals = (grn_ts_int *)node->buf.ptr; + if (node->reverse) { + for (i = 0; i < n_recs; i++) { + if (vals[i] < 0) { + vals[i] = (vals[i] ^ INT64_MAX) + 1; + } + vals[i] = -1 - vals[i]; + } + } else { + for (i = 0; i < n_recs; i++) { + if (vals[i] < 0) { + vals[i] = (vals[i] ^ INT64_MAX) + 1; + } + } + } + return grn_ts_qsort_by_int(ctx, node, offset, limit, vals, recs, n_recs); + } + case GRN_TS_TIME: { + grn_ts_int *vals; + rc = grn_ts_expr_evaluate_to_buf(ctx, node->expr, recs, n_recs, + &node->buf); + if (rc != GRN_SUCCESS) { + return rc; + } + vals = (grn_ts_int *)node->buf.ptr; + if (node->reverse) { + for (i = 0; i < n_recs; i++) { + vals[i] = -1 - vals[i]; + } + } + return grn_ts_qsort_by_int(ctx, node, offset, limit, vals, recs, n_recs); + } + case GRN_TS_TEXT: { + grn_ts_text *vals; + rc = grn_ts_expr_evaluate_to_buf(ctx, node->expr, recs, n_recs, + &node->buf); + if (rc != GRN_SUCCESS) { + return rc; + } + vals = (grn_ts_text *)node->buf.ptr; + if (node->reverse) { + return grn_ts_qsort_by_text_desc(ctx, node, offset, limit, + vals, recs, n_recs); + } else { + return grn_ts_qsort_by_text_asc2(ctx, node, offset, limit, + vals, recs, n_recs, 0); + } + } + case GRN_TS_INT_VECTOR: + case GRN_TS_FLOAT_VECTOR: + case GRN_TS_TIME_VECTOR: + case GRN_TS_TEXT_VECTOR: { + // TODO + GRN_TS_ERR_RETURN(GRN_OPERATION_NOT_SUPPORTED, "not supported yet"); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid data kind: %d", + node->expr->data_kind); + } + } + return GRN_FUNCTION_NOT_IMPLEMENTED; +} + +/* grn_ts_sorter_node_sort() sorts records. */ +static grn_rc +grn_ts_sorter_node_sort(grn_ctx *ctx, grn_ts_sorter_node *node, + size_t offset, size_t limit, + grn_ts_record *recs, size_t n_recs) +{ + switch (node->expr->type) { + case GRN_TS_EXPR_ID: { + return grn_ts_sorter_node_sort_by_id(ctx, node, offset, limit, + recs, n_recs); + } + case GRN_TS_EXPR_SCORE: { + return grn_ts_sorter_node_sort_by_score(ctx, node, offset, limit, + recs, n_recs); + } + case GRN_TS_EXPR_CONST: { + if (!node->next) { + return GRN_SUCCESS; + } + return grn_ts_sorter_node_sort(ctx, node->next, offset, limit, recs, + n_recs); + } + case GRN_TS_EXPR_VARIABLE: { + return grn_ts_sorter_node_sort_by_var(ctx, node, offset, limit, + recs, n_recs); + break; + } + default: { + GRN_TS_ERR_RETURN(GRN_OBJECT_CORRUPT, "invalid expr type: %d", + node->expr->type); + } + } +} + +/*------------------------------------------------------------- + * grn_ts_sorter. + */ + +static void +grn_ts_sorter_init(grn_ctx *ctx, grn_ts_sorter *sorter) +{ + memset(sorter, 0, sizeof(*sorter)); + sorter->table = NULL; + sorter->head = NULL; +} + +static void +grn_ts_sorter_fin(grn_ctx *ctx, grn_ts_sorter *sorter) +{ + if (sorter->head) { + grn_ts_sorter_node_list_close(ctx, sorter->head); + } + if (sorter->table) { + grn_obj_unlink(ctx, sorter->table); + } +} + +grn_rc +grn_ts_sorter_open(grn_ctx *ctx, grn_obj *table, grn_ts_sorter_node *head, + size_t offset, size_t limit, grn_ts_sorter **sorter) +{ + grn_rc rc; + grn_ts_sorter *new_sorter; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || !head || !sorter) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + new_sorter = GRN_MALLOCN(grn_ts_sorter, 1); + if (!new_sorter) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_sorter)); + } + rc = grn_ts_obj_increment_ref_count(ctx, table); + if (rc != GRN_SUCCESS) { + GRN_FREE(new_sorter); + return rc; + } + grn_ts_sorter_init(ctx, new_sorter); + new_sorter->table = table; + new_sorter->head = head; + new_sorter->offset = offset; + new_sorter->limit = limit; + /* FIXME: Enable partial sorting. */ +/* new_sorter->partial = (offset + limit) < 1000;*/ + *sorter = new_sorter; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_parse(grn_ctx *ctx, grn_obj *table, + grn_ts_str str, size_t offset, + size_t limit, grn_ts_sorter **sorter) +{ + grn_rc rc; + grn_ts_sorter *new_sorter = NULL; + grn_ts_expr_parser *parser; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || !str.size || !sorter) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_expr_parser_open(ctx, table, &parser); + if (rc == GRN_SUCCESS) { + grn_ts_sorter_builder *builder; + rc = grn_ts_sorter_builder_open(ctx, table, &builder); + if (rc == GRN_SUCCESS) { + grn_ts_str first, rest = str; + for ( ; ; ) { + grn_ts_expr *expr; + grn_ts_bool reverse = GRN_FALSE; + rc = grn_ts_expr_parser_split(ctx, parser, rest, &first, &rest); + if (rc == GRN_END_OF_DATA) { + rc = grn_ts_sorter_builder_complete(ctx, builder, offset, limit, + &new_sorter); + break; + } else if (rc != GRN_SUCCESS) { + break; + } + if (first.ptr[0] == '-') { + reverse = GRN_TRUE; + first.ptr++; + first.size--; + } + rc = grn_ts_expr_parser_parse(ctx, parser, first, &expr); + if (rc != GRN_SUCCESS) { + break; + } + rc = grn_ts_sorter_builder_push(ctx, builder, expr, reverse); + if (rc != GRN_SUCCESS) { + grn_ts_expr_close(ctx, expr); + break; + } + } + grn_ts_sorter_builder_close(ctx, builder); + } + grn_ts_expr_parser_close(ctx, parser); + } + if (rc != GRN_SUCCESS) { + return rc; + } + *sorter = new_sorter; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_close(grn_ctx *ctx, grn_ts_sorter *sorter) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!sorter) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + grn_ts_sorter_fin(ctx, sorter); + GRN_FREE(sorter); + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_progress(grn_ctx *ctx, grn_ts_sorter *sorter, + grn_ts_record *recs, size_t n_recs, size_t *n_rest) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!sorter || (!recs && n_recs) || !n_rest) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (sorter->partial) { + return grn_ts_sorter_node_progress(ctx, sorter->head, sorter->offset, + sorter->limit, recs, n_recs, n_rest); + } + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_complete(grn_ctx *ctx, grn_ts_sorter *sorter, + grn_ts_record *recs, size_t n_recs, size_t *n_rest) +{ + grn_rc rc; + size_t i, limit; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!sorter || (!recs && n_recs) || !n_rest) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + if (sorter->offset >= n_recs) { + return GRN_SUCCESS; + } + limit = sorter->limit; + if (limit > (n_recs - sorter->offset)) { + limit = n_recs; + } else { + limit += sorter->offset; + } + if (sorter->partial) { + // FIXME: If there was no input. Partial sorting is not required. + rc = grn_ts_sorter_node_progress(ctx, sorter->head, sorter->offset, + limit, recs, n_recs, n_rest); + if (rc == GRN_SUCCESS) { + rc = grn_ts_sorter_node_complete(ctx, sorter->head, sorter->offset, + limit, recs, n_recs, n_rest); + } + } else { + rc = grn_ts_sorter_node_sort(ctx, sorter->head, sorter->offset, + limit, recs, n_recs); + } + if (rc != GRN_SUCCESS) { + return rc; + } + if (sorter->offset) { + for (i = 0; i < limit; i++) { + recs[i] = recs[sorter->offset + i]; + } + } + *n_rest = limit; + return GRN_SUCCESS; +} + +/*------------------------------------------------------------- + * grn_ts_sorter_builder. + */ + +/* grn_ts_sorter_builder_init() initializes a sorter builder. */ +static void +grn_ts_sorter_builder_init(grn_ctx *ctx, grn_ts_sorter_builder *builder) +{ + memset(builder, 0, sizeof(*builder)); + builder->table = NULL; + builder->head = NULL; + builder->tail = NULL; +} + +/* grn_ts_sorter_builder_fin() finalizes a sorter builder. */ +static void +grn_ts_sorter_builder_fin(grn_ctx *ctx, grn_ts_sorter_builder *builder) +{ + if (builder->head) { + grn_ts_sorter_node_list_close(ctx, builder->head); + } + if (builder->table) { + grn_obj_unlink(ctx, builder->table); + } +} + +grn_rc +grn_ts_sorter_builder_open(grn_ctx *ctx, grn_obj *table, + grn_ts_sorter_builder **builder) +{ + grn_rc rc; + grn_ts_sorter_builder *new_builder; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!table || !grn_ts_obj_is_table(ctx, table) || !builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + new_builder = GRN_MALLOCN(grn_ts_sorter_builder, 1); + if (!new_builder) { + GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE, + "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1", + sizeof(grn_ts_sorter_builder)); + } + grn_ts_sorter_builder_init(ctx, new_builder); + rc = grn_ts_obj_increment_ref_count(ctx, table); + if (rc != GRN_SUCCESS) { + grn_ts_sorter_builder_fin(ctx, new_builder); + GRN_FREE(new_builder); + return rc; + } + new_builder->table = table; + *builder = new_builder; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_builder_close(grn_ctx *ctx, grn_ts_sorter_builder *builder) +{ + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + grn_ts_sorter_builder_fin(ctx, builder); + GRN_FREE(builder); + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_builder_complete(grn_ctx *ctx, grn_ts_sorter_builder *builder, + size_t offset, size_t limit, + grn_ts_sorter **sorter) +{ + grn_rc rc; + grn_ts_sorter *new_sorter; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder || !builder->head || !sorter) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + rc = grn_ts_sorter_open(ctx, builder->table, builder->head, + offset, limit, &new_sorter); + if (rc != GRN_SUCCESS) { + return rc; + } + builder->head = NULL; + builder->tail = NULL; + *sorter = new_sorter; + return GRN_SUCCESS; +} + +grn_rc +grn_ts_sorter_builder_push(grn_ctx *ctx, grn_ts_sorter_builder *builder, + grn_ts_expr *expr, grn_ts_bool reverse) +{ + grn_rc rc; + grn_ts_sorter_node *new_node; + if (!ctx) { + return GRN_INVALID_ARGUMENT; + } + if (!builder || !expr || expr->table != builder->table) { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + switch (expr->data_kind) { + case GRN_TS_INT: + case GRN_TS_FLOAT: + case GRN_TS_TIME: + case GRN_TS_TEXT: { + break; + } + case GRN_TS_INT_VECTOR: + case GRN_TS_FLOAT_VECTOR: + case GRN_TS_TIME_VECTOR: + case GRN_TS_TEXT_VECTOR: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "not supported yet"); + } + default: { + GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument"); + } + } + rc = grn_ts_sorter_node_open(ctx, expr, reverse, &new_node); + if (rc != GRN_SUCCESS) { + return rc; + } + if (builder->tail) { + builder->tail->next = new_node; + } else { + builder->head = new_node; + } + builder->tail = new_node; + return GRN_SUCCESS; +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_sorter.h b/storage/mroonga/vendor/groonga/lib/ts/ts_sorter.h new file mode 100644 index 00000000..d80479e1 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_sorter.h @@ -0,0 +1,98 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_expr.h" +#include "ts_str.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO: Sorting should take into account the order of input records. */ + +typedef struct grn_ts_sorter_node { + grn_ts_expr *expr; /* Expression. */ + grn_ts_bool reverse; /* Reverse order or not. */ + grn_ts_buf buf; /* Buffer for values. */ + struct grn_ts_sorter_node *next; /* Next node. */ +} grn_ts_sorter_node; + +typedef struct { + grn_obj *table; /* Table. */ + grn_ts_sorter_node *head; /* First node. */ + size_t offset; /* Top `offset` records will be discarded. */ + size_t limit; /* At most `limit` records will be left. */ + grn_ts_bool partial; /* Partial sorting or not. */ +} grn_ts_sorter; + +/* grn_ts_sorter_open() creates a sorter. */ +grn_rc grn_ts_sorter_open(grn_ctx *ctx, grn_obj *table, + grn_ts_sorter_node *head, size_t offset, + size_t limit, grn_ts_sorter **sorter); + +/* grn_ts_sorter_parse() parses a string and creates a sorter. */ +grn_rc grn_ts_sorter_parse(grn_ctx *ctx, grn_obj *table, + grn_ts_str str, size_t offset, + size_t limit, grn_ts_sorter **sorter); + +/* grn_ts_sorter_close() destroys a sorter. */ +grn_rc grn_ts_sorter_close(grn_ctx *ctx, grn_ts_sorter *sorter); + +/* grn_ts_sorter_progress() progresses sorting. */ +grn_rc grn_ts_sorter_progress(grn_ctx *ctx, grn_ts_sorter *sorter, + grn_ts_record *recs, size_t n_recs, + size_t *n_rest); + +/* grn_ts_sorter_complete() completes sorting. */ +grn_rc grn_ts_sorter_complete(grn_ctx *ctx, grn_ts_sorter *sorter, + grn_ts_record *recs, size_t n_recs, + size_t *n_rest); + +typedef struct { + grn_obj *table; /* Table. */ + grn_ts_sorter_node *head; /* First node. */ + grn_ts_sorter_node *tail; /* Last node. */ +} grn_ts_sorter_builder; + +/* grn_ts_sorter_builder_open() creates a sorter builder. */ +grn_rc grn_ts_sorter_builder_open(grn_ctx *ctx, grn_obj *table, + grn_ts_sorter_builder **builder); + +/* grn_ts_sorter_builder_close() destroys a sorter builder. */ +grn_rc grn_ts_sorter_builder_close(grn_ctx *ctx, + grn_ts_sorter_builder *builder); + +/* grn_ts_sorter_builder_complete() completes a sorter. */ +grn_rc grn_ts_sorter_builder_complete(grn_ctx *ctx, + grn_ts_sorter_builder *builder, + size_t offset, size_t limit, + grn_ts_sorter **sorter); + +/* grn_ts_sorter_builder_push() pushes a node. */ +grn_rc grn_ts_sorter_builder_push(grn_ctx *ctx, grn_ts_sorter_builder *builder, + grn_ts_expr *expr, grn_ts_bool reverse); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_str.c b/storage/mroonga/vendor/groonga/lib/ts/ts_str.c new file mode 100644 index 00000000..9f200fa3 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_str.c @@ -0,0 +1,191 @@ +/* -*- 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 +*/ + +#include "ts_str.h" + +#include +#include + +/*------------------------------------------------------------- + * Byte. + */ + +grn_ts_bool +grn_ts_byte_is_decimal(uint8_t byte) +{ + return (byte >= '0') && (byte <= '9'); +} + +grn_ts_bool +grn_ts_byte_is_name_char(uint8_t byte) +{ + /* + * Note: A table name allows '#', '@' and '-'. + * http://groonga.org/docs/reference/commands/table_create.html#name + */ + if (((byte >= '0') && (byte <= '9')) || ((byte >= 'A') && (byte <= 'Z')) || + ((byte >= 'a') && (byte <= 'z')) || (byte == '_')) { + return GRN_TRUE; + } + return GRN_FALSE; +} + +/*------------------------------------------------------------- + * String. + */ + +grn_ts_bool +grn_ts_str_starts_with(grn_ts_str str, grn_ts_str prefix) +{ + if (str.size < prefix.size) { + return GRN_FALSE; + } + return !memcmp(str.ptr, prefix.ptr, prefix.size); +} + +grn_ts_str +grn_ts_str_trim_left(grn_ts_str str) +{ + size_t i; + for (i = 0; i < str.size; i++) { + if (!isspace((uint8_t)str.ptr[i])) { + break; + } + } + str.ptr += i; + str.size -= i; + return str; +} + +grn_ts_str +grn_ts_str_trim_score_assignment(grn_ts_str str) +{ + grn_ts_str rest; + str = grn_ts_str_trim_left(str); + if (!grn_ts_str_starts_with(str, (grn_ts_str){ "_score", 6 })) { + return str; + } + rest.ptr = str.ptr + 6; + rest.size = str.size - 6; + rest = grn_ts_str_trim_left(rest); + if (!rest.size || (rest.ptr[0] != '=') || + ((rest.size >= 2) && (rest.ptr[1] == '='))) { + return str; + } + rest.ptr++; + rest.size--; + return grn_ts_str_trim_left(rest); +} + +grn_ts_bool +grn_ts_str_has_number_prefix(grn_ts_str str) +{ + if (!str.size) { + return GRN_FALSE; + } + if (grn_ts_byte_is_decimal(str.ptr[0])) { + return GRN_TRUE; + } + if (str.size == 1) { + return GRN_FALSE; + } + switch (str.ptr[0]) { + case '+': case '-': { + if (grn_ts_byte_is_decimal(str.ptr[1])) { + return GRN_TRUE; + } + if (str.size == 2) { + return GRN_FALSE; + } + return (str.ptr[1] == '.') && grn_ts_byte_is_decimal(str.ptr[2]); + } + case '.': { + return grn_ts_byte_is_decimal(str.ptr[1]); + } + default: { + return GRN_FALSE; + } + } +} + +grn_ts_bool +grn_ts_str_is_name_prefix(grn_ts_str str) +{ + size_t i; + for (i = 0; i < str.size; i++) { + if (!grn_ts_byte_is_name_char(str.ptr[i])) { + return GRN_FALSE; + } + } + return GRN_TRUE; +} + +grn_ts_bool +grn_ts_str_is_name(grn_ts_str str) +{ + if (!str.size) { + return GRN_FALSE; + } + return grn_ts_str_is_name_prefix(str); +} + +grn_ts_bool +grn_ts_str_is_true(grn_ts_str str) +{ + return (str.size == 4) && !memcmp(str.ptr, "true", 4); +} + +grn_ts_bool +grn_ts_str_is_false(grn_ts_str str) +{ + return (str.size == 5) && !memcmp(str.ptr, "false", 5); +} + +grn_ts_bool +grn_ts_str_is_bool(grn_ts_str str) +{ + return grn_ts_str_is_true(str) || grn_ts_str_is_false(str); +} + +grn_ts_bool +grn_ts_str_is_id_name(grn_ts_str str) +{ + return (str.size == GRN_COLUMN_NAME_ID_LEN) && + !memcmp(str.ptr, GRN_COLUMN_NAME_ID, GRN_COLUMN_NAME_ID_LEN); +} + +grn_ts_bool +grn_ts_str_is_score_name(grn_ts_str str) +{ + return (str.size == GRN_COLUMN_NAME_SCORE_LEN) && + !memcmp(str.ptr, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN); +} + +grn_ts_bool +grn_ts_str_is_key_name(grn_ts_str str) +{ + return (str.size == GRN_COLUMN_NAME_KEY_LEN) && + !memcmp(str.ptr, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN); +} + +grn_ts_bool +grn_ts_str_is_value_name(grn_ts_str str) +{ + return (str.size == GRN_COLUMN_NAME_VALUE_LEN) && + !memcmp(str.ptr, GRN_COLUMN_NAME_VALUE, GRN_COLUMN_NAME_VALUE_LEN); +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_str.h b/storage/mroonga/vendor/groonga/lib/ts/ts_str.h new file mode 100644 index 00000000..11371233 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_str.h @@ -0,0 +1,106 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------- + * Byte. + */ + +/* grn_ts_byte_is_decimal() returns whether or not a byte is decimal. */ +grn_ts_bool grn_ts_byte_is_decimal(uint8_t byte); + +/* + * grn_ts_byte_is_name_char() returns whether or not a byte is allowed as a + * part of a name. + */ +grn_ts_bool grn_ts_byte_is_name_char(uint8_t byte); + +/*------------------------------------------------------------- + * String. + */ + +typedef struct { + const char *ptr; /* The starting address. */ + size_t size; /* The size in bytes. */ +} grn_ts_str; + +/* grn_ts_str_has_prefix() returns whether or not str starts with prefix. */ +grn_ts_bool grn_ts_str_starts_with(grn_ts_str str, grn_ts_str prefix); + +/* grn_ts_str_trim_left() returns a string without leading white-spaces. */ +grn_ts_str grn_ts_str_trim_left(grn_ts_str str); + +/* + * grn_ts_str_trim_score_assignment() returns a string without leading + * white-spaces and an assignment to _score. If `str` does not start with + * an assignment, this function returns `grn_ts_str_trim_left(str)`. + */ +grn_ts_str grn_ts_str_trim_score_assignment(grn_ts_str str); + +/* + * grn_ts_str_has_number_prefix() returns whether or not a string starts with a + * number or not. + */ +grn_ts_bool grn_ts_str_has_number_prefix(grn_ts_str str); + +/* + * grn_ts_str_is_name_prefix() returns whether or not a string is valid as a + * name prefix. Note that an empty string is a name prefix. + */ +grn_ts_bool grn_ts_str_is_name_prefix(grn_ts_str str); + +/* + * grn_ts_str_is_name() returns whether or not a string is valid as a name. + * Note that an empty string is invalid as a name. + */ +grn_ts_bool grn_ts_str_is_name(grn_ts_str str); + +/* grn_ts_str_is_true() returns str == "true". */ +grn_ts_bool grn_ts_str_is_true(grn_ts_str str); + +/* grn_ts_str_is_false() returns str == "false". */ +grn_ts_bool grn_ts_str_is_false(grn_ts_str str); + +/* grn_ts_str_is_bool() returns (str == "true") || (str == "false"). */ +grn_ts_bool grn_ts_str_is_bool(grn_ts_str str); + +/* grn_ts_str_is_id_name() returns str == "_id". */ +grn_ts_bool grn_ts_str_is_id_name(grn_ts_str str); + +/* grn_ts_str_is_score_name() returns str == "_score". */ +grn_ts_bool grn_ts_str_is_score_name(grn_ts_str str); + +/* grn_ts_str_is_key_name() returns str == "_key". */ +grn_ts_bool grn_ts_str_is_key_name(grn_ts_str str); + +/* grn_ts_str_is_value_name() returns str == "_value". */ +grn_ts_bool grn_ts_str_is_value_name(grn_ts_str str); + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_types.h b/storage/mroonga/vendor/groonga/lib/ts/ts_types.h new file mode 100644 index 00000000..389eec42 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_types.h @@ -0,0 +1,168 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*------------------------------------------------------------- + * Built-in data types. + */ + +/* grn_builtin_type or table ID. */ +typedef grn_id grn_ts_data_type; + +/* ID (_id). */ +typedef grn_id grn_ts_id; + +/* Score (_score). */ +typedef float grn_ts_score; + +/* Record (_id, _score). */ +typedef struct { + grn_ts_id id; + grn_ts_score score; +} grn_ts_record; + +/*------------------------------------------------------------- + * Built-in scalar data kinds. + */ + +/* Bool. */ +typedef grn_bool grn_ts_bool; + +/* Int. */ +typedef int64_t grn_ts_int; + +/* Float. */ +typedef double grn_ts_float; + +/* Time. */ +typedef int64_t grn_ts_time; + +/* Text. */ +typedef struct { + const char *ptr; + size_t size; +} grn_ts_text; + +/* Geo. */ +typedef grn_geo_point grn_ts_geo; +typedef grn_geo_point grn_ts_tokyo_geo; +typedef grn_geo_point grn_ts_wgs84_geo; + +/* Ref. */ +typedef grn_ts_record grn_ts_ref; + +/*------------------------------------------------------------- + * Built-in vector data kinds. + */ + +/* BoolVector. */ +typedef struct { + const grn_ts_bool *ptr; + size_t size; +} grn_ts_bool_vector; + +/* IntVector. */ +typedef struct { + const grn_ts_int *ptr; + size_t size; +} grn_ts_int_vector; + +/* FloatVector. */ +typedef struct { + const grn_ts_float *ptr; + size_t size; +} grn_ts_float_vector; + +/* TimeVector. */ +typedef struct { + const grn_ts_time *ptr; + size_t size; +} grn_ts_time_vector; + +/* TextVector. */ +typedef struct { + const grn_ts_text *ptr; + size_t size; +} grn_ts_text_vector; + +/* GeoVector. */ +typedef struct { + const grn_ts_geo *ptr; + size_t size; +} grn_ts_geo_vector; +typedef grn_ts_geo_vector grn_ts_tokyo_geo_vector; +typedef grn_ts_geo_vector grn_ts_wgs84_geo_vector; + +/* RefVector. */ +typedef struct { + const grn_ts_ref *ptr; + size_t size; +} grn_ts_ref_vector; + +/*------------------------------------------------------------- + * Built-in data kinds. + */ + +enum { GRN_TS_VECTOR_FLAG = 1 << 7 }; + +typedef enum { + GRN_TS_VOID = 0, /* GRN_DB_VOID */ + GRN_TS_BOOL = 1, /* GRN_DB_BOOL */ + GRN_TS_INT = 2, /* GRN_DB_[U]INT(8/16/32/64) */ + GRN_TS_FLOAT = 3, /* GRN_DB_FLOAT */ + GRN_TS_TIME = 4, /* GRN_DB_TIME */ + GRN_TS_TEXT = 5, /* GRN_DB_[SHORT_/LONG_]TEST */ + GRN_TS_GEO = 6, /* GRN_DB_(TOKYO/WGS84)_GEO_POINT */ + GRN_TS_REF = 7, /* Table reference. */ + GRN_TS_BOOL_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_BOOL, + GRN_TS_INT_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_INT, + GRN_TS_FLOAT_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_FLOAT, + GRN_TS_TIME_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_TIME, + GRN_TS_TEXT_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_TEXT, + GRN_TS_GEO_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_GEO, + GRN_TS_REF_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_REF +} grn_ts_data_kind; + +typedef union { + grn_ts_bool as_bool; + grn_ts_int as_int; + grn_ts_float as_float; + grn_ts_time as_time; + grn_ts_text as_text; + grn_ts_geo as_geo; + grn_ts_ref as_ref; + grn_ts_bool_vector as_bool_vector; + grn_ts_int_vector as_int_vector; + grn_ts_float_vector as_float_vector; + grn_ts_time_vector as_time_vector; + grn_ts_text_vector as_text_vector; + grn_ts_geo_vector as_geo_vector; + grn_ts_ref_vector as_ref_vector; +} grn_ts_any; + +#ifdef __cplusplus +} +#endif + diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_util.c b/storage/mroonga/vendor/groonga/lib/ts/ts_util.c new file mode 100644 index 00000000..18e66060 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_util.c @@ -0,0 +1,129 @@ +/* -*- 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 +*/ + +#include "ts_util.h" + +#include "../grn_dat.h" +#include "../grn_hash.h" +#include "../grn_pat.h" + +#include "ts_log.h" + +grn_rc +grn_ts_obj_increment_ref_count(grn_ctx *ctx, grn_obj *obj) +{ + grn_id id = grn_obj_id(ctx, obj); + grn_obj *obj_clone = grn_ctx_at(ctx, id); + if (!obj_clone) { + GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d", id); + } + if (obj_clone != obj) { + grn_obj_unlink(ctx, obj_clone); + GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "wrong object: %p != %p", + obj, obj_clone); + } + return GRN_SUCCESS; +} + +grn_ts_bool +grn_ts_obj_is_table(grn_ctx *ctx, grn_obj *obj) +{ + return grn_obj_is_table(ctx, obj); +} + +grn_ts_bool +grn_ts_obj_is_column(grn_ctx *ctx, grn_obj *obj) +{ + switch (obj->header.type) { + case GRN_COLUMN_FIX_SIZE: + case GRN_COLUMN_VAR_SIZE: { + return GRN_TRUE; + } + /* GRN_COLUMN_INDEX is not supported. */ + default: { + return GRN_FALSE; + } + } +} + +grn_rc +grn_ts_ja_get_value(grn_ctx *ctx, grn_obj *ja, grn_ts_id id, + grn_ts_buf *buf, size_t *value_size) +{ + grn_rc rc; + uint32_t size; + grn_io_win iw; + char *ptr = (char *)grn_ja_ref(ctx, (grn_ja *)ja, id, &iw, &size); + if (!ptr) { + if (value_size) { + *value_size = 0; + } + return GRN_SUCCESS; + } + rc = grn_ts_buf_write(ctx, buf, ptr, size); + grn_ja_unref(ctx, &iw); + if (rc != GRN_SUCCESS) { + return rc; + } + if (value_size) { + *value_size = size; + } + return GRN_SUCCESS; +} + +grn_ts_bool +grn_ts_table_has_key(grn_ctx *ctx, grn_obj *table) +{ + switch (table->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: { + return GRN_TRUE; + } + default: { + return GRN_FALSE; + } + } +} + +grn_ts_bool +grn_ts_table_has_value(grn_ctx *ctx, grn_obj *table) +{ + return DB_OBJ(table)->range != GRN_DB_VOID; +} + +const void * +grn_ts_table_get_value(grn_ctx *ctx, grn_obj *table, grn_ts_id id) +{ + switch (table->header.type) { + case GRN_TABLE_HASH_KEY: { + return grn_hash_get_value_(ctx, (grn_hash *)table, id, NULL); + } + case GRN_TABLE_PAT_KEY: { + uint32_t size; + return grn_pat_get_value_(ctx, (grn_pat *)table, id, &size); + } + /* GRN_TABLE_DAT_KEY does not support _value. */ + case GRN_TABLE_NO_KEY: { + return _grn_array_get_value(ctx, (grn_array *)table, id); + } + default: { + return NULL; + } + } +} diff --git a/storage/mroonga/vendor/groonga/lib/ts/ts_util.h b/storage/mroonga/vendor/groonga/lib/ts/ts_util.h new file mode 100644 index 00000000..24a6a507 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/ts/ts_util.h @@ -0,0 +1,61 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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 +*/ + +#pragma once + +#include "../grn.h" + +#include "ts_buf.h" +#include "ts_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* grn_ts_obj_increment_ref_count() increments an object reference count. */ +grn_rc grn_ts_obj_increment_ref_count(grn_ctx *ctx, grn_obj *obj); + +/* grn_ts_obj_is_table() returns whether or not an object is a table. */ +grn_ts_bool grn_ts_obj_is_table(grn_ctx *ctx, grn_obj *obj); + +/* grn_ts_obj_is_column() returns whether or not an object is a column. */ +grn_ts_bool grn_ts_obj_is_column(grn_ctx *ctx, grn_obj *obj); + +/* + * grn_ts_ja_get_value() gets a value from ja and writes it to buf. Note that + * the value is appended to the end of buf. + */ +grn_rc grn_ts_ja_get_value(grn_ctx *ctx, grn_obj *ja, grn_ts_id id, + grn_ts_buf *buf, size_t *value_size); + +/* grn_ts_table_has_key() returns whether or not a table has _key. */ +grn_ts_bool grn_ts_table_has_key(grn_ctx *ctx, grn_obj *table); + +/* grn_ts_table_has_value() returns whether or not a table has _value. */ +grn_ts_bool grn_ts_table_has_value(grn_ctx *ctx, grn_obj *table); + +/* + * grn_ts_table_get_value() gets a reference to a value (_value). On failure, + * this function returns NULL. + */ +const void *grn_ts_table_get_value(grn_ctx *ctx, grn_obj *table, grn_ts_id id); + +#ifdef __cplusplus +} +#endif + -- cgit v1.2.3