summaryrefslogtreecommitdiffstats
path: root/storage/mroonga/vendor/groonga/lib/db.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/mroonga/vendor/groonga/lib/db.c')
-rw-r--r--storage/mroonga/vendor/groonga/lib/db.c14054
1 files changed, 14054 insertions, 0 deletions
diff --git a/storage/mroonga/vendor/groonga/lib/db.c b/storage/mroonga/vendor/groonga/lib/db.c
new file mode 100644
index 00000000..7749d4c0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/db.c
@@ -0,0 +1,14054 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2017 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 "grn.h"
+#include "grn_config.h"
+#include "grn_db.h"
+#include "grn_obj.h"
+#include "grn_hash.h"
+#include "grn_pat.h"
+#include "grn_dat.h"
+#include "grn_ii.h"
+#include "grn_index_column.h"
+#include "grn_ctx_impl.h"
+#include "grn_token_cursor.h"
+#include "grn_tokenizers.h"
+#include "grn_proc.h"
+#include "grn_plugin.h"
+#include "grn_geo.h"
+#include "grn_scorers.h"
+#include "grn_snip.h"
+#include "grn_string.h"
+#include "grn_normalizer.h"
+#include "grn_report.h"
+#include "grn_util.h"
+#include "grn_cache.h"
+#include "grn_window_functions.h"
+#include <string.h>
+#include <math.h>
+
+typedef struct {
+ grn_id id;
+ unsigned int weight;
+} weight_uvector_entry;
+
+#define IS_WEIGHT_UVECTOR(obj) ((obj)->header.flags & GRN_OBJ_WITH_WEIGHT)
+
+#define GRN_TABLE_GROUPED (0x01<<0)
+#define GRN_TABLE_IS_GROUPED(table)\
+ ((table)->header.impl_flags & GRN_TABLE_GROUPED)
+#define GRN_TABLE_GROUPED_ON(table)\
+ ((table)->header.impl_flags |= GRN_TABLE_GROUPED)
+#define GRN_TABLE_IS_MULTI_KEYS_GROUPED(table)\
+ (GRN_TABLE_IS_GROUPED(table) &&\
+ table->header.domain == GRN_ID_NIL)
+
+#define WITH_NORMALIZE(table,key,key_size,block) do {\
+ if ((table)->normalizer && key && key_size > 0) {\
+ grn_obj *nstr;\
+ if ((nstr = grn_string_open(ctx, key, key_size,\
+ (table)->normalizer, 0))) {\
+ const char *key;\
+ unsigned int key_size;\
+ grn_string_get_normalized(ctx, nstr, &key, &key_size, NULL);\
+ block\
+ grn_obj_close(ctx, nstr);\
+ }\
+ } else {\
+ block\
+ }\
+} while (0)
+
+inline static grn_id
+grn_table_add_v_inline(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added);
+inline static void
+grn_table_add_subrec_inline(grn_obj *table, grn_rset_recinfo *ri, double score,
+ grn_rset_posinfo *pi, int dir);
+inline static grn_id
+grn_table_cursor_next_inline(grn_ctx *ctx, grn_table_cursor *tc);
+inline static int
+grn_table_cursor_get_value_inline(grn_ctx *ctx, grn_table_cursor *tc, void **value);
+
+static void grn_obj_ensure_bulk(grn_ctx *ctx, grn_obj *obj);
+static void grn_obj_ensure_vector(grn_ctx *ctx, grn_obj *obj);
+
+inline static void
+grn_obj_get_range_info(grn_ctx *ctx, grn_obj *obj,
+ grn_id *range_id, grn_obj_flags *range_flags);
+
+static char grn_db_key[GRN_ENV_BUFFER_SIZE];
+
+void
+grn_db_init_from_env(void)
+{
+ grn_getenv("GRN_DB_KEY",
+ grn_db_key,
+ GRN_ENV_BUFFER_SIZE);
+}
+
+inline static void
+gen_pathname(const char *path, char *buffer, int fno)
+{
+ size_t len = strlen(path);
+ grn_memcpy(buffer, path, len);
+ if (fno >= 0) {
+ buffer[len] = '.';
+ grn_itoh(fno, buffer + len + 1, 7);
+ buffer[len + 8] = '\0';
+ } else {
+ buffer[len] = '\0';
+ }
+}
+
+void
+grn_db_generate_pathname(grn_ctx *ctx, grn_obj *db, grn_id id, char *buffer)
+{
+ gen_pathname(grn_obj_get_io(ctx, db)->path, buffer, id);
+}
+
+typedef struct {
+ grn_obj *ptr;
+ uint32_t lock;
+ uint32_t done;
+} db_value;
+
+static const char *GRN_DB_CONFIG_PATH_FORMAT = "%s.conf";
+
+static grn_bool
+grn_db_config_create(grn_ctx *ctx, grn_db *s, const char *path,
+ const char *context_tag)
+{
+ char *config_path;
+ char config_path_buffer[PATH_MAX];
+ uint32_t flags = GRN_OBJ_KEY_VAR_SIZE;
+
+ if (path) {
+ grn_snprintf(config_path_buffer, PATH_MAX, PATH_MAX,
+ GRN_DB_CONFIG_PATH_FORMAT, path);
+ config_path = config_path_buffer;
+ } else {
+ config_path = NULL;
+ }
+ s->config = grn_hash_create(ctx, config_path,
+ GRN_CONFIG_MAX_KEY_SIZE,
+ GRN_CONFIG_VALUE_SPACE_SIZE,
+ flags);
+ if (!s->config) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "%s failed to create data store for configuration: <%s>",
+ context_tag,
+ config_path ? config_path : "(temporary)");
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+static grn_bool
+grn_db_config_open(grn_ctx *ctx, grn_db *s, const char *path)
+{
+ char config_path[PATH_MAX];
+
+ grn_snprintf(config_path, PATH_MAX, PATH_MAX, GRN_DB_CONFIG_PATH_FORMAT, path);
+ if (grn_path_exist(config_path)) {
+ s->config = grn_hash_open(ctx, config_path);
+ if (!s->config) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[db][open] failed to open data store for configuration: <%s>",
+ config_path);
+ return GRN_FALSE;
+ }
+ return GRN_TRUE;
+ } else {
+ return grn_db_config_create(ctx, s, path, "[db][open]");
+ }
+}
+
+static grn_rc
+grn_db_config_remove(grn_ctx *ctx, const char *path)
+{
+ char config_path[PATH_MAX];
+
+ grn_snprintf(config_path, PATH_MAX, PATH_MAX, GRN_DB_CONFIG_PATH_FORMAT, path);
+ return grn_hash_remove(ctx, config_path);
+}
+
+grn_obj *
+grn_db_create(grn_ctx *ctx, const char *path, grn_db_create_optarg *optarg)
+{
+ grn_db *s = NULL;
+
+ GRN_API_ENTER;
+
+ if (path && strlen(path) > PATH_MAX - 14) {
+ ERR(GRN_INVALID_ARGUMENT, "too long path");
+ goto exit;
+ }
+
+ s = GRN_MALLOC(sizeof(grn_db));
+ if (!s) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_db alloc failed");
+ goto exit;
+ }
+
+ CRITICAL_SECTION_INIT(s->lock);
+ grn_tiny_array_init(ctx, &s->values, sizeof(db_value),
+ GRN_TINY_ARRAY_CLEAR|
+ GRN_TINY_ARRAY_THREADSAFE|
+ GRN_TINY_ARRAY_USE_MALLOC);
+ s->keys = NULL;
+ s->specs = NULL;
+ s->config = NULL;
+
+ {
+ grn_bool use_default_db_key = GRN_TRUE;
+ grn_bool use_pat_as_db_keys = GRN_FALSE;
+ if (grn_db_key[0]) {
+ if (!strcmp(grn_db_key, "pat")) {
+ use_default_db_key = GRN_FALSE;
+ use_pat_as_db_keys = GRN_TRUE;
+ } else if (!strcmp(grn_db_key, "dat")) {
+ use_default_db_key = GRN_FALSE;
+ }
+ }
+
+ if (use_default_db_key && !strcmp(GRN_DEFAULT_DB_KEY, "pat")) {
+ use_pat_as_db_keys = GRN_TRUE;
+ }
+ if (use_pat_as_db_keys) {
+ s->keys = (grn_obj *)grn_pat_create(ctx, path, GRN_TABLE_MAX_KEY_SIZE,
+ 0, GRN_OBJ_KEY_VAR_SIZE);
+ } else {
+ s->keys = (grn_obj *)grn_dat_create(ctx, path, GRN_TABLE_MAX_KEY_SIZE,
+ 0, GRN_OBJ_KEY_VAR_SIZE);
+ }
+ }
+
+ if (!s->keys) {
+ goto exit;
+ }
+
+ GRN_DB_OBJ_SET_TYPE(s, GRN_DB);
+ s->obj.db = (grn_obj *)s;
+ s->obj.header.domain = GRN_ID_NIL;
+ DB_OBJ(&s->obj)->range = GRN_ID_NIL;
+ /* prepare builtin classes and load builtin plugins. */
+ if (path) {
+ {
+ char specs_path[PATH_MAX];
+ gen_pathname(path, specs_path, 0);
+ s->specs = grn_ja_create(ctx, specs_path, 65536, 0);
+ if (!s->specs) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "failed to create specs: <%s>", specs_path);
+ goto exit;
+ }
+ }
+ if (!grn_db_config_create(ctx, s, path, "[db][create]")) {
+ goto exit;
+ }
+ grn_ctx_use(ctx, (grn_obj *)s);
+ grn_db_init_builtin_types(ctx);
+ grn_obj_flush(ctx, (grn_obj *)s);
+ GRN_API_RETURN((grn_obj *)s);
+ } else {
+ if (!grn_db_config_create(ctx, s, NULL, "[db][create]")) {
+ goto exit;
+ }
+ grn_ctx_use(ctx, (grn_obj *)s);
+ grn_db_init_builtin_types(ctx);
+ GRN_API_RETURN((grn_obj *)s);
+ }
+
+exit:
+ if (s) {
+ if (s->keys) {
+ if (s->keys->header.type == GRN_TABLE_PAT_KEY) {
+ grn_pat_close(ctx, (grn_pat *)s->keys);
+ grn_pat_remove(ctx, path);
+ } else {
+ grn_dat_close(ctx, (grn_dat *)s->keys);
+ grn_dat_remove(ctx, path);
+ }
+ }
+ if (s->specs) {
+ const char *specs_path;
+ specs_path = grn_obj_path(ctx, (grn_obj *)(s->specs));
+ grn_ja_close(ctx, s->specs);
+ grn_ja_remove(ctx, specs_path);
+ }
+ grn_tiny_array_fin(&s->values);
+ CRITICAL_SECTION_FIN(s->lock);
+ GRN_FREE(s);
+ }
+
+ GRN_API_RETURN(NULL);
+}
+
+grn_obj *
+grn_db_open(grn_ctx *ctx, const char *path)
+{
+ grn_db *s = NULL;
+
+ GRN_API_ENTER;
+
+ if (!path) {
+ ERR(GRN_INVALID_ARGUMENT, "[db][open] path is missing");
+ goto exit;
+ }
+
+ if (strlen(path) > PATH_MAX - 14) {
+ ERR(GRN_INVALID_ARGUMENT, "inappropriate path");
+ goto exit;
+ }
+
+ s = GRN_MALLOC(sizeof(grn_db));
+ if (!s) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_db alloc failed");
+ goto exit;
+ }
+
+ CRITICAL_SECTION_INIT(s->lock);
+ grn_tiny_array_init(ctx, &s->values, sizeof(db_value),
+ GRN_TINY_ARRAY_CLEAR|
+ GRN_TINY_ARRAY_THREADSAFE|
+ GRN_TINY_ARRAY_USE_MALLOC);
+ s->keys = NULL;
+ s->specs = NULL;
+ s->config = NULL;
+
+ {
+ uint32_t type = grn_io_detect_type(ctx, path);
+ switch (type) {
+ case GRN_TABLE_PAT_KEY :
+ s->keys = (grn_obj *)grn_pat_open(ctx, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ s->keys = (grn_obj *)grn_dat_open(ctx, path);
+ break;
+ default :
+ s->keys = NULL;
+ if (ctx->rc == GRN_SUCCESS) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[db][open] invalid keys table's type: %#x", type);
+ goto exit;
+ }
+ break;
+ }
+ }
+
+ if (!s->keys) {
+ goto exit;
+ }
+
+ {
+ char specs_path[PATH_MAX];
+ gen_pathname(path, specs_path, 0);
+ s->specs = grn_ja_open(ctx, specs_path);
+ if (!s->specs) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[db][open] failed to open specs: <%s>", specs_path);
+ goto exit;
+ }
+ }
+ if (!grn_db_config_open(ctx, s, path)) {
+ goto exit;
+ }
+
+ GRN_DB_OBJ_SET_TYPE(s, GRN_DB);
+ s->obj.db = (grn_obj *)s;
+ s->obj.header.domain = GRN_ID_NIL;
+ DB_OBJ(&s->obj)->range = GRN_ID_NIL;
+ grn_ctx_use(ctx, (grn_obj *)s);
+ {
+ unsigned int n_records;
+
+ n_records = grn_table_size(ctx, (grn_obj *)s);
+#ifdef GRN_WITH_MECAB
+ if (grn_db_init_mecab_tokenizer(ctx)) {
+ ERRCLR(ctx);
+ }
+#endif
+ grn_db_init_builtin_tokenizers(ctx);
+ grn_db_init_builtin_normalizers(ctx);
+ grn_db_init_builtin_scorers(ctx);
+ grn_db_init_builtin_commands(ctx);
+ grn_db_init_builtin_window_functions(ctx);
+
+ if (grn_table_size(ctx, (grn_obj *)s) > n_records) {
+ grn_obj_flush(ctx, (grn_obj *)s);
+ }
+ }
+ GRN_API_RETURN((grn_obj *)s);
+
+exit:
+ if (s) {
+ if (s->specs) {
+ grn_ja_close(ctx, s->specs);
+ }
+ if (s->keys) {
+ if (s->keys->header.type == GRN_TABLE_PAT_KEY) {
+ grn_pat_close(ctx, (grn_pat *)s->keys);
+ } else {
+ grn_dat_close(ctx, (grn_dat *)s->keys);
+ }
+ }
+ grn_tiny_array_fin(&s->values);
+ CRITICAL_SECTION_FIN(s->lock);
+ GRN_FREE(s);
+ }
+
+ GRN_API_RETURN(NULL);
+}
+
+static grn_id
+grn_db_curr_id(grn_ctx *ctx, grn_obj *db)
+{
+ grn_id curr_id = GRN_ID_NIL;
+ grn_db *s = (grn_db *)db;
+ switch (s->keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ curr_id = grn_pat_curr_id(ctx, (grn_pat *)s->keys);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ curr_id = grn_dat_curr_id(ctx, (grn_dat *)s->keys);
+ break;
+ }
+ return curr_id;
+}
+
+/* s must be validated by caller */
+grn_rc
+grn_db_close(grn_ctx *ctx, grn_obj *db)
+{
+ grn_id id;
+ db_value *vp;
+ grn_db *s = (grn_db *)db;
+ grn_bool ctx_used_db;
+ if (!s) { return GRN_INVALID_ARGUMENT; }
+ GRN_API_ENTER;
+
+ ctx_used_db = ctx->impl && ctx->impl->db == db;
+ if (ctx_used_db) {
+#ifdef GRN_WITH_MECAB
+ grn_db_fin_mecab_tokenizer(ctx);
+#endif
+ grn_ctx_loader_clear(ctx);
+ if (ctx->impl->parser) {
+ grn_expr_parser_close(ctx);
+ }
+ }
+
+ GRN_TINY_ARRAY_EACH(&s->values, 1, grn_db_curr_id(ctx, db), id, vp, {
+ if (vp->ptr) { grn_obj_close(ctx, vp->ptr); }
+ });
+
+ if (ctx_used_db) {
+ if (ctx->impl->values) {
+ grn_db_obj *o;
+ GRN_ARRAY_EACH(ctx, ctx->impl->values, 0, 0, id, &o, {
+ grn_obj_close(ctx, *((grn_obj **)o));
+ });
+ grn_array_truncate(ctx, ctx->impl->values);
+ }
+ }
+
+/* grn_tiny_array_fin should be refined.. */
+#ifdef WIN32
+ {
+ grn_tiny_array *a = &s->values;
+ CRITICAL_SECTION_FIN(a->lock);
+ }
+#endif
+ grn_tiny_array_fin(&s->values);
+
+ switch (s->keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ grn_pat_close(ctx, (grn_pat *)s->keys);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ grn_dat_close(ctx, (grn_dat *)s->keys);
+ break;
+ }
+ CRITICAL_SECTION_FIN(s->lock);
+ if (s->specs) { grn_ja_close(ctx, s->specs); }
+ grn_hash_close(ctx, s->config);
+ GRN_FREE(s);
+
+ if (ctx_used_db) {
+ grn_cache *cache;
+ cache = grn_cache_current_get(ctx);
+ if (cache) {
+ grn_cache_expire(cache, -1);
+ }
+ ctx->impl->db = NULL;
+ }
+
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_obj *
+grn_ctx_get(grn_ctx *ctx, const char *name, int name_size)
+{
+ grn_obj *obj = NULL;
+ grn_obj *db;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (GRN_DB_P(db)) {
+ grn_db *s = (grn_db *)db;
+ grn_obj *alias_table = NULL;
+ grn_obj *alias_column = NULL;
+ grn_obj alias_name_buffer;
+
+ if (name_size < 0) {
+ name_size = strlen(name);
+ }
+ GRN_TEXT_INIT(&alias_name_buffer, 0);
+ while (GRN_TRUE) {
+ grn_id id;
+
+ id = grn_table_get(ctx, s->keys, name, name_size);
+ if (id) {
+ obj = grn_ctx_at(ctx, id);
+ break;
+ }
+
+ if (!alias_column) {
+ grn_id alias_column_id;
+ const char *alias_column_name;
+ uint32_t alias_column_name_size;
+
+ grn_config_get(ctx,
+ "alias.column", -1,
+ &alias_column_name, &alias_column_name_size);
+ if (!alias_column_name) {
+ break;
+ }
+ alias_column_id = grn_table_get(ctx,
+ s->keys,
+ alias_column_name,
+ alias_column_name_size);
+ if (!alias_column_id) {
+ break;
+ }
+ alias_column = grn_ctx_at(ctx, alias_column_id);
+ if (alias_column->header.type != GRN_COLUMN_VAR_SIZE) {
+ break;
+ }
+ if (alias_column->header.flags & GRN_OBJ_VECTOR) {
+ break;
+ }
+ if (DB_OBJ(alias_column)->range != GRN_DB_SHORT_TEXT) {
+ break;
+ }
+ alias_table = grn_ctx_at(ctx, alias_column->header.domain);
+ if (alias_table->header.type == GRN_TABLE_NO_KEY) {
+ break;
+ }
+ }
+
+ {
+ grn_id alias_id;
+ alias_id = grn_table_get(ctx, alias_table, name, name_size);
+ if (!alias_id) {
+ break;
+ }
+ GRN_BULK_REWIND(&alias_name_buffer);
+ grn_obj_get_value(ctx, alias_column, alias_id, &alias_name_buffer);
+ name = GRN_TEXT_VALUE(&alias_name_buffer);
+ name_size = GRN_TEXT_LEN(&alias_name_buffer);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &alias_name_buffer);
+ }
+ GRN_API_RETURN(obj);
+}
+
+grn_obj *
+grn_ctx_db(grn_ctx *ctx)
+{
+ return (ctx && ctx->impl) ? ctx->impl->db : NULL;
+}
+
+grn_obj *
+grn_db_keys(grn_obj *s)
+{
+ return (grn_obj *)(((grn_db *)s)->keys);
+}
+
+uint32_t
+grn_obj_get_last_modified(grn_ctx *ctx, grn_obj *obj)
+{
+ if (!obj) {
+ return 0;
+ }
+
+ return grn_obj_get_io(ctx, obj)->header->last_modified;
+}
+
+grn_bool
+grn_obj_is_dirty(grn_ctx *ctx, grn_obj *obj)
+{
+ if (!obj) {
+ return GRN_FALSE;
+ }
+
+ switch (obj->header.type) {
+ case GRN_DB :
+ return grn_db_is_dirty(ctx, obj);
+ case GRN_TABLE_PAT_KEY :
+ return grn_pat_is_dirty(ctx, (grn_pat *)obj);
+ case GRN_TABLE_DAT_KEY :
+ return grn_dat_is_dirty(ctx, (grn_dat *)obj);
+ default :
+ return GRN_FALSE;
+ }
+}
+
+uint32_t
+grn_db_get_last_modified(grn_ctx *ctx, grn_obj *db)
+{
+ return grn_obj_get_last_modified(ctx, db);
+}
+
+grn_bool
+grn_db_is_dirty(grn_ctx *ctx, grn_obj *db)
+{
+ grn_obj *keys;
+
+ if (!db) {
+ return GRN_FALSE;
+ }
+
+ keys = ((grn_db *)db)->keys;
+ return grn_obj_is_dirty(ctx, keys);
+}
+
+static grn_rc
+grn_db_dirty(grn_ctx *ctx, grn_obj *db)
+{
+ grn_obj *keys;
+
+ if (!db) {
+ return GRN_SUCCESS;
+ }
+
+ keys = ((grn_db *)db)->keys;
+ switch (keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ return grn_pat_dirty(ctx, (grn_pat *)keys);
+ case GRN_TABLE_DAT_KEY :
+ return grn_dat_dirty(ctx, (grn_dat *)keys);
+ default :
+ return GRN_SUCCESS;
+ }
+}
+
+static grn_rc
+grn_db_clean(grn_ctx *ctx, grn_obj *db)
+{
+ grn_obj *keys;
+
+ if (!db) {
+ return GRN_SUCCESS;
+ }
+
+ keys = ((grn_db *)db)->keys;
+ switch (keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ return grn_pat_clean(ctx, (grn_pat *)keys);
+ case GRN_TABLE_DAT_KEY :
+ return grn_dat_clean(ctx, (grn_dat *)keys);
+ default :
+ return GRN_SUCCESS;
+ }
+}
+
+static grn_rc
+grn_db_clear_dirty(grn_ctx *ctx, grn_obj *db)
+{
+ grn_obj *keys;
+
+ if (!db) {
+ return GRN_SUCCESS;
+ }
+
+ keys = ((grn_db *)db)->keys;
+ switch (keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ return grn_pat_clear_dirty(ctx, (grn_pat *)keys);
+ case GRN_TABLE_DAT_KEY :
+ return grn_dat_clear_dirty(ctx, (grn_dat *)keys);
+ default :
+ return GRN_SUCCESS;
+ }
+}
+
+void
+grn_db_touch(grn_ctx *ctx, grn_obj *s)
+{
+ grn_obj_touch(ctx, s, NULL);
+}
+
+grn_bool
+grn_obj_is_corrupt(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_bool is_corrupt = GRN_FALSE;
+
+ GRN_API_ENTER;
+
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "[object][corrupt] object must not be NULL");
+ GRN_API_RETURN(GRN_FALSE);
+ }
+
+ switch (obj->header.type) {
+ case GRN_DB :
+ is_corrupt = grn_io_is_corrupt(ctx, grn_obj_get_io(ctx, obj));
+ if (!is_corrupt) {
+ is_corrupt = grn_io_is_corrupt(ctx, ((grn_db *)obj)->specs->io);
+ }
+ if (!is_corrupt) {
+ is_corrupt = grn_io_is_corrupt(ctx, ((grn_db *)obj)->config->io);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ is_corrupt = grn_io_is_corrupt(ctx, grn_obj_get_io(ctx, obj));
+ break;
+ case GRN_TABLE_DAT_KEY :
+ is_corrupt = grn_dat_is_corrupt(ctx, (grn_dat *)obj);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ is_corrupt = grn_io_is_corrupt(ctx, grn_obj_get_io(ctx, obj));
+ break;
+ case GRN_COLUMN_INDEX :
+ is_corrupt = grn_io_is_corrupt(ctx, ((grn_ii *)obj)->seg);
+ if (!is_corrupt) {
+ is_corrupt = grn_io_is_corrupt(ctx, ((grn_ii *)obj)->chunk);
+ }
+ break;
+ default :
+ break;
+ }
+
+ GRN_API_RETURN(is_corrupt);
+}
+
+#define IS_TEMP(obj) (DB_OBJ(obj)->id & GRN_OBJ_TMP_OBJECT)
+
+static inline void
+grn_obj_touch_db(grn_ctx *ctx, grn_obj *obj, grn_timeval *tv)
+{
+ grn_obj_get_io(ctx, obj)->header->last_modified = tv->tv_sec;
+ grn_db_dirty(ctx, obj);
+}
+
+void
+grn_obj_touch(grn_ctx *ctx, grn_obj *obj, grn_timeval *tv)
+{
+ grn_timeval tv_;
+ if (!tv) {
+ grn_timeval_now(ctx, &tv_);
+ tv = &tv_;
+ }
+ if (obj) {
+ switch (obj->header.type) {
+ case GRN_DB :
+ grn_obj_touch_db(ctx, obj, tv);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_INDEX :
+ if (!IS_TEMP(obj)) {
+ grn_obj_get_io(ctx, obj)->header->last_modified = tv->tv_sec;
+ grn_obj_touch(ctx, DB_OBJ(obj)->db, tv);
+ }
+ break;
+ }
+ }
+}
+
+grn_rc
+grn_db_check_name(grn_ctx *ctx, const char *name, unsigned int name_size)
+{
+ int len;
+ const char *name_end = name + name_size;
+ if (name_size > 0 &&
+ *name == GRN_DB_PSEUDO_COLUMN_PREFIX) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ while (name < name_end) {
+ char c = *name;
+ if ((unsigned int)((c | 0x20) - 'a') >= 26u &&
+ (unsigned int)(c - '0') >= 10u &&
+ c != '_' &&
+ c != '-' &&
+ c != '#' &&
+ c != '@') {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (!(len = grn_charlen(ctx, name, name_end))) { break; }
+ name += len;
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_obj *
+grn_type_open(grn_ctx *ctx, grn_obj_spec *spec)
+{
+ struct _grn_type *res;
+ res = GRN_MALLOC(sizeof(struct _grn_type));
+ if (res) {
+ GRN_DB_OBJ_SET_TYPE(res, GRN_TYPE);
+ res->obj.header = spec->header;
+ GRN_TYPE_SIZE(&res->obj) = GRN_TYPE_SIZE(spec);
+ }
+ return (grn_obj *)res;
+}
+
+grn_obj *
+grn_proc_create(grn_ctx *ctx, const char *name, int name_size, grn_proc_type type,
+ grn_proc_func *init, grn_proc_func *next, grn_proc_func *fin,
+ unsigned int nvars, grn_expr_var *vars)
+{
+ grn_proc *res = NULL;
+ grn_id id = GRN_ID_NIL;
+ grn_id range = GRN_ID_NIL;
+ int added = 0;
+ grn_obj *db;
+ const char *path;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ path = ctx->impl->plugin_path;
+ if (path) {
+ range = grn_plugin_reference(ctx, path);
+ }
+ if (name_size < 0) {
+ name_size = strlen(name);
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[proc][create]", name, name_size);
+ GRN_API_RETURN(NULL);
+ }
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ GRN_API_RETURN(NULL);
+ }
+ if (name && name_size) {
+ grn_db *s = (grn_db *)db;
+ if (!(id = grn_table_get(ctx, s->keys, name, name_size))) {
+ if (!(id = grn_table_add(ctx, s->keys, name, name_size, &added))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_table_add failed");
+ GRN_API_RETURN(NULL);
+ }
+ }
+ if (!added) {
+ db_value *vp;
+ if ((vp = grn_tiny_array_at(&s->values, id)) && (res = (grn_proc *)vp->ptr)) {
+ /* TODO: Do more robust check. */
+ if (res->funcs[PROC_INIT] ||
+ res->funcs[PROC_NEXT] ||
+ res->funcs[PROC_FIN]) {
+ ERR(GRN_INVALID_ARGUMENT, "already used name");
+ GRN_API_RETURN(NULL);
+ }
+ if (range != GRN_ID_NIL) {
+ grn_plugin_close(ctx, range);
+ }
+ GRN_API_RETURN((grn_obj *)res);
+ } else {
+ added = 1;
+ }
+ }
+ } else if (ctx->impl && ctx->impl->values) {
+ id = grn_array_add(ctx, ctx->impl->values, NULL) | GRN_OBJ_TMP_OBJECT;
+ added = 1;
+ }
+ if (!res) { res = GRN_MALLOCN(grn_proc, 1); }
+ if (res) {
+ GRN_DB_OBJ_SET_TYPE(res, GRN_PROC);
+ res->obj.db = db;
+ res->obj.id = id;
+ res->obj.header.domain = GRN_ID_NIL;
+ res->obj.header.flags = path ? GRN_OBJ_CUSTOM_NAME : 0;
+ res->obj.range = range;
+ res->type = type;
+ res->funcs[PROC_INIT] = init;
+ res->funcs[PROC_NEXT] = next;
+ res->funcs[PROC_FIN] = fin;
+ memset(&(res->callbacks), 0, sizeof(res->callbacks));
+ res->callbacks.function.selector_op = GRN_OP_NOP;
+ res->callbacks.function.is_stable = GRN_TRUE;
+ GRN_TEXT_INIT(&res->name_buf, 0);
+ res->vars = NULL;
+ res->nvars = 0;
+ if (added) {
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ // grn_obj_delete(ctx, db, id);
+ GRN_FREE(res);
+ GRN_API_RETURN(NULL);
+ }
+ }
+ while (nvars--) {
+ grn_obj *v = grn_expr_add_var(ctx, (grn_obj *)res, vars->name, vars->name_size);
+ GRN_OBJ_INIT(v, vars->value.header.type, 0, vars->value.header.domain);
+ GRN_TEXT_PUT(ctx, v, GRN_TEXT_VALUE(&vars->value), GRN_TEXT_LEN(&vars->value));
+ vars++;
+ }
+ }
+ GRN_API_RETURN((grn_obj *)res);
+}
+
+/* grn_table */
+
+static void
+calc_rec_size(grn_table_flags flags, uint32_t max_n_subrecs, uint32_t range_size,
+ uint32_t additional_value_size,
+ uint8_t *subrec_size, uint8_t *subrec_offset,
+ uint32_t *key_size, uint32_t *value_size)
+{
+ *subrec_size = 0;
+ *subrec_offset = 0;
+ if (flags & GRN_OBJ_WITH_SUBREC) {
+ switch (flags & GRN_OBJ_UNIT_MASK) {
+ case GRN_OBJ_UNIT_DOCUMENT_NONE :
+ break;
+ case GRN_OBJ_UNIT_DOCUMENT_SECTION :
+ *subrec_offset = sizeof(grn_id);
+ *subrec_size = sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_DOCUMENT_POSITION :
+ *subrec_offset = sizeof(grn_id);
+ *subrec_size = sizeof(uint32_t) + sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_SECTION_NONE :
+ *key_size += sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_SECTION_POSITION :
+ *key_size += sizeof(uint32_t);
+ *subrec_offset = sizeof(grn_id) + sizeof(uint32_t);
+ *subrec_size = sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_POSITION_NONE :
+ *key_size += sizeof(uint32_t) + sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_USERDEF_DOCUMENT :
+ *subrec_size = range_size;
+ break;
+ case GRN_OBJ_UNIT_USERDEF_SECTION :
+ *subrec_size = range_size + sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_USERDEF_POSITION :
+ *subrec_size = range_size + sizeof(uint32_t) + sizeof(uint32_t);
+ break;
+ }
+ *value_size = (uintptr_t)GRN_RSET_SUBRECS_NTH((((grn_rset_recinfo *)0)->subrecs),
+ *subrec_size, max_n_subrecs);
+ } else {
+ *value_size = range_size;
+ }
+ *value_size += additional_value_size;
+}
+
+static grn_rc _grn_obj_remove(grn_ctx *ctx, grn_obj *obj, grn_bool dependent);
+
+static grn_rc
+grn_table_create_validate(grn_ctx *ctx, const char *name, unsigned int name_size,
+ const char *path, grn_table_flags flags,
+ grn_obj *key_type, grn_obj *value_type)
+{
+ grn_table_flags table_type;
+ const char *table_type_name = NULL;
+
+ table_type = (flags & GRN_OBJ_TABLE_TYPE_MASK);
+ switch (table_type) {
+ case GRN_OBJ_TABLE_HASH_KEY :
+ table_type_name = "TABLE_HASH_KEY";
+ break;
+ case GRN_OBJ_TABLE_PAT_KEY :
+ table_type_name = "TABLE_PAT_KEY";
+ break;
+ case GRN_OBJ_TABLE_DAT_KEY :
+ table_type_name = "TABLE_DAT_KEY";
+ break;
+ case GRN_OBJ_TABLE_NO_KEY :
+ table_type_name = "TABLE_NO_KEY";
+ break;
+ default :
+ table_type_name = "unknown";
+ break;
+ }
+
+ if (!key_type && table_type != GRN_OBJ_TABLE_NO_KEY &&
+ !(flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key type is required for TABLE_HASH_KEY, TABLE_PAT_KEY or "
+ "TABLE_DAT_KEY: <%.*s>", name_size, name);
+ return ctx->rc;
+ }
+
+ if (key_type && table_type == GRN_OBJ_TABLE_NO_KEY) {
+ int key_name_size;
+ char key_name[GRN_TABLE_MAX_KEY_SIZE];
+ key_name_size = grn_obj_name(ctx, key_type, key_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key isn't available for TABLE_NO_KEY table: <%.*s> (%.*s)",
+ name_size, name, key_name_size, key_name);
+ return ctx->rc;
+ }
+
+ if ((flags & GRN_OBJ_KEY_WITH_SIS) &&
+ table_type != GRN_OBJ_TABLE_PAT_KEY) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key with SIS is available only for TABLE_PAT_KEY table: "
+ "<%.*s>(%s)",
+ name_size, name,
+ table_type_name);
+ return ctx->rc;
+ }
+
+ if ((flags & GRN_OBJ_KEY_NORMALIZE) &&
+ table_type == GRN_OBJ_TABLE_NO_KEY) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key normalization isn't available for TABLE_NO_KEY table: <%.*s>",
+ name_size, name);
+ return ctx->rc;
+ }
+
+ if ((flags & GRN_OBJ_KEY_LARGE) &&
+ table_type != GRN_OBJ_TABLE_HASH_KEY) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "large key support is available only for TABLE_HASH_KEY key table: "
+ "<%.*s>(%s)",
+ name_size, name,
+ table_type_name);
+ return ctx->rc;
+ }
+
+ return ctx->rc;
+}
+
+static grn_obj *
+grn_table_create_with_max_n_subrecs(grn_ctx *ctx, const char *name,
+ unsigned int name_size, const char *path,
+ grn_table_flags flags, grn_obj *key_type,
+ grn_obj *value_type,
+ uint32_t max_n_subrecs,
+ uint32_t additional_value_size)
+{
+ grn_id id;
+ grn_id domain = GRN_ID_NIL, range = GRN_ID_NIL;
+ uint32_t key_size, value_size = 0, range_size = 0;
+ uint8_t subrec_size, subrec_offset;
+ grn_obj *res = NULL;
+ grn_obj *db;
+ char buffer[PATH_MAX];
+ if (!ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][create] db not initialized");
+ return NULL;
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[table][create]", name, name_size);
+ return NULL;
+ }
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][create] invalid db assigned");
+ return NULL;
+ }
+ if (grn_table_create_validate(ctx, name, name_size, path, flags,
+ key_type, value_type)) {
+ return NULL;
+ }
+ if (key_type) {
+ domain = DB_OBJ(key_type)->id;
+ switch (key_type->header.type) {
+ case GRN_TYPE :
+ {
+ grn_db_obj *t = (grn_db_obj *)key_type;
+ flags |= t->header.flags;
+ key_size = GRN_TYPE_SIZE(t);
+ if (key_size > GRN_TABLE_MAX_KEY_SIZE) {
+ int type_name_size;
+ char type_name[GRN_TABLE_MAX_KEY_SIZE];
+ type_name_size = grn_obj_name(ctx, key_type, type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] key size too big: <%.*s> <%.*s>(%u) (max:%u)",
+ name_size, name,
+ type_name_size, type_name,
+ key_size, GRN_TABLE_MAX_KEY_SIZE);
+ return NULL;
+ }
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ key_size = sizeof(grn_id);
+ break;
+ default :
+ {
+ int key_name_size;
+ char key_name[GRN_TABLE_MAX_KEY_SIZE];
+ key_name_size = grn_obj_name(ctx, key_type, key_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] key type must be type or table: <%.*s> (%.*s)",
+ name_size, name, key_name_size, key_name);
+ return NULL;
+ }
+ break;
+ }
+ } else {
+ key_size = (flags & GRN_OBJ_KEY_VAR_SIZE) ? GRN_TABLE_MAX_KEY_SIZE : sizeof(grn_id);
+ }
+ if (value_type) {
+ range = DB_OBJ(value_type)->id;
+ switch (value_type->header.type) {
+ case GRN_TYPE :
+ {
+ grn_db_obj *t = (grn_db_obj *)value_type;
+ if (t->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ int type_name_size;
+ char type_name[GRN_TABLE_MAX_KEY_SIZE];
+ type_name_size = grn_obj_name(ctx, value_type, type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] value type must be fixed size: <%.*s> (%.*s)",
+ name_size, name, type_name_size, type_name);
+ return NULL;
+ }
+ range_size = GRN_TYPE_SIZE(t);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ range_size = sizeof(grn_id);
+ break;
+ default :
+ {
+ int value_name_size;
+ char value_name[GRN_TABLE_MAX_KEY_SIZE];
+ value_name_size = grn_obj_name(ctx, value_type, value_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] value type must be type or table: <%.*s> (%.*s)",
+ name_size, name, value_name_size, value_name);
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ id = grn_obj_register(ctx, db, name, name_size);
+ if (ERRP(ctx, GRN_ERROR)) { return NULL; }
+ if (GRN_OBJ_PERSISTENT & flags) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "DDL:%u:table_create %.*s", id, name_size, name);
+ if (!path) {
+ if (GRN_DB_PERSISTENT_P(db)) {
+ grn_db_generate_pathname(ctx, db, id, buffer);
+ path = buffer;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "path not assigned for persistent table");
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ return NULL;
+ }
+ } else {
+ flags |= GRN_OBJ_CUSTOM_NAME;
+ }
+ } else {
+ if (path) {
+ ERR(GRN_INVALID_ARGUMENT, "path assigned for temporary table");
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ return NULL;
+ }
+ if (GRN_DB_PERSISTENT_P(db) && name && name_size) {
+ ERR(GRN_INVALID_ARGUMENT, "name assigned for temporary table");
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ return NULL;
+ }
+ }
+ calc_rec_size(flags, max_n_subrecs, range_size, additional_value_size,
+ &subrec_size, &subrec_offset, &key_size, &value_size);
+ switch (flags & GRN_OBJ_TABLE_TYPE_MASK) {
+ case GRN_OBJ_TABLE_HASH_KEY :
+ res = (grn_obj *)grn_hash_create(ctx, path, key_size, value_size, flags);
+ break;
+ case GRN_OBJ_TABLE_PAT_KEY :
+ res = (grn_obj *)grn_pat_create(ctx, path, key_size, value_size, flags);
+ break;
+ case GRN_OBJ_TABLE_DAT_KEY :
+ res = (grn_obj *)grn_dat_create(ctx, path, key_size, value_size, flags);
+ break;
+ case GRN_OBJ_TABLE_NO_KEY :
+ domain = range;
+ res = (grn_obj *)grn_array_create(ctx, path, value_size, flags);
+ break;
+ }
+ if (res) {
+ DB_OBJ(res)->header.impl_flags = 0;
+ DB_OBJ(res)->header.domain = domain;
+ DB_OBJ(res)->range = range;
+ DB_OBJ(res)->max_n_subrecs = max_n_subrecs;
+ DB_OBJ(res)->subrec_size = subrec_size;
+ DB_OBJ(res)->subrec_offset = subrec_offset;
+ DB_OBJ(res)->flags.group = 0;
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ _grn_obj_remove(ctx, res, GRN_FALSE);
+ res = NULL;
+ }
+ } else {
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ }
+ return res;
+}
+
+grn_obj *
+grn_table_create(grn_ctx *ctx, const char *name, unsigned int name_size,
+ const char *path, grn_table_flags flags,
+ grn_obj *key_type, grn_obj *value_type)
+{
+ grn_obj *res;
+ GRN_API_ENTER;
+ res = grn_table_create_with_max_n_subrecs(ctx, name, name_size, path,
+ flags, key_type, value_type,
+ 0, 0);
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_table_create_for_group(grn_ctx *ctx, const char *name,
+ unsigned int name_size, const char *path,
+ grn_obj *group_key, grn_obj *value_type,
+ unsigned int max_n_subrecs)
+{
+ grn_obj *res = NULL;
+ GRN_API_ENTER;
+ if (group_key) {
+ grn_obj *key_type;
+ key_type = grn_ctx_at(ctx, grn_obj_get_range(ctx, group_key));
+ if (key_type) {
+ res = grn_table_create_with_max_n_subrecs(ctx, name, name_size, path,
+ GRN_TABLE_HASH_KEY|
+ GRN_OBJ_WITH_SUBREC|
+ GRN_OBJ_UNIT_USERDEF_DOCUMENT,
+ key_type, value_type,
+ max_n_subrecs, 0);
+ grn_obj_unlink(ctx, key_type);
+ }
+ } else {
+ res = grn_table_create_with_max_n_subrecs(ctx, name, name_size, path,
+ GRN_TABLE_HASH_KEY|
+ GRN_OBJ_KEY_VAR_SIZE|
+ GRN_OBJ_WITH_SUBREC|
+ GRN_OBJ_UNIT_USERDEF_DOCUMENT,
+ NULL, value_type,
+ max_n_subrecs, 0);
+ }
+ GRN_API_RETURN(res);
+}
+
+unsigned int
+grn_table_get_subrecs(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_id *subrecbuf, int *scorebuf, int buf_size)
+{
+ unsigned int count = 0;
+ GRN_API_ENTER;
+ if (GRN_OBJ_TABLEP(table)) {
+ uint32_t value_size;
+ grn_rset_recinfo *ri;
+ uint32_t subrec_size = DB_OBJ(table)->subrec_size;
+ uint32_t max_n_subrecs = DB_OBJ(table)->max_n_subrecs;
+ if (subrec_size < sizeof(grn_id)) { goto exit; }
+ if (!max_n_subrecs) { goto exit; }
+ ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, table, id, &value_size);
+ if (ri) {
+ byte *psubrec = (byte *)ri->subrecs;
+ uint32_t n_subrecs = (uint32_t)GRN_RSET_N_SUBRECS(ri);
+ uint32_t limit = value_size / (GRN_RSET_SCORE_SIZE + subrec_size);
+ if ((int) limit > buf_size) {
+ limit = buf_size;
+ }
+ if (limit > n_subrecs) {
+ limit = n_subrecs;
+ }
+ if (limit > max_n_subrecs) {
+ limit = max_n_subrecs;
+ }
+ for (; count < limit; count++) {
+ if (scorebuf) {
+ scorebuf[count] = *((double *)psubrec);
+ }
+ psubrec += GRN_RSET_SCORE_SIZE;
+ if (subrecbuf) {
+ subrecbuf[count] = *((grn_id *)psubrec);
+ }
+ psubrec += subrec_size;
+ }
+ }
+ }
+exit :
+ GRN_API_RETURN(count);
+}
+
+grn_obj *
+grn_table_open(grn_ctx *ctx, const char *name, unsigned int name_size, const char *path)
+{
+ grn_obj *db;
+ if (!ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ GRN_API_RETURN(NULL);
+ } else {
+ grn_obj *res = grn_ctx_get(ctx, name, name_size);
+ if (res) {
+ const char *path2 = grn_obj_path(ctx, res);
+ if (path && (!path2 || strcmp(path, path2))) {
+ ERR(GRN_INVALID_ARGUMENT, "path unmatch");
+ GRN_API_RETURN(NULL);
+ }
+ } else if (path) {
+ uint32_t type = grn_io_detect_type(ctx, path);
+ if (!type) { GRN_API_RETURN(NULL); }
+ switch (type) {
+ case GRN_TABLE_HASH_KEY :
+ res = (grn_obj *)grn_hash_open(ctx, path);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ res = (grn_obj *)grn_pat_open(ctx, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ res = (grn_obj *)grn_dat_open(ctx, path);
+ break;
+ case GRN_TABLE_NO_KEY :
+ res = (grn_obj *)grn_array_open(ctx, path);
+ break;
+ }
+ if (res) {
+ grn_id id = grn_obj_register(ctx, db, name, name_size);
+ res->header.flags |= GRN_OBJ_CUSTOM_NAME;
+ res->header.domain = GRN_ID_NIL; /* unknown */
+ DB_OBJ(res)->range = GRN_ID_NIL; /* unknown */
+ grn_db_obj_init(ctx, db, id, DB_OBJ(res));
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "path is missing");
+ }
+ GRN_API_RETURN(res);
+ }
+}
+
+grn_id
+grn_table_lcp_search(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, key, key_size, {
+ id = grn_pat_lcp_search(ctx, pat, key, key_size);
+ });
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, key, key_size, {
+ id = grn_dat_lcp_search(ctx, dat, key, key_size);
+ });
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ WITH_NORMALIZE(hash, key, key_size, {
+ id = grn_hash_get(ctx, hash, key, key_size, NULL);
+ });
+ }
+ break;
+ }
+ GRN_API_RETURN(id);
+}
+
+grn_obj *
+grn_obj_default_set_value_hook(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ if (!pctx) {
+ ERR(GRN_INVALID_ARGUMENT, "default_set_value_hook failed");
+ } else {
+ grn_obj *flags = grn_ctx_pop(ctx);
+ grn_obj *newvalue = grn_ctx_pop(ctx);
+ grn_obj *oldvalue = grn_ctx_pop(ctx);
+ grn_obj *id = grn_ctx_pop(ctx);
+ grn_hook *h = pctx->currh;
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(h);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section = data->section;
+ if (flags) { /* todo */ }
+ if (target) {
+ switch (target->header.type) {
+ case GRN_COLUMN_INDEX :
+ grn_ii_column_update(ctx, (grn_ii *)target,
+ GRN_UINT32_VALUE(id),
+ section, oldvalue, newvalue, NULL);
+ }
+ }
+ }
+ return NULL;
+}
+
+grn_id
+grn_table_add(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size, int *added)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ int added_ = 0;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, key, key_size, {
+ if (pat->io && !(pat->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, pat->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_pat_add(ctx, pat, key, key_size, NULL, &added_);
+ grn_io_unlock(pat->io);
+ }
+ } else {
+ id = grn_pat_add(ctx, pat, key, key_size, NULL, &added_);
+ }
+ });
+ if (added) { *added = added_; }
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, key, key_size, {
+ if (dat->io && !(dat->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, dat->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_dat_add(ctx, dat, key, key_size, NULL, &added_);
+ grn_io_unlock(dat->io);
+ }
+ } else {
+ id = grn_dat_add(ctx, dat, key, key_size, NULL, &added_);
+ }
+ });
+ if (added) { *added = added_; }
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ WITH_NORMALIZE(hash, key, key_size, {
+ if (hash->io && !(hash->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, hash->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_hash_add(ctx, hash, key, key_size, NULL, &added_);
+ grn_io_unlock(hash->io);
+ }
+ } else {
+ id = grn_hash_add(ctx, hash, key, key_size, NULL, &added_);
+ }
+ });
+ if (added) { *added = added_; }
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *array = (grn_array *)table;
+ if (array->io && !(array->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, array->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_array_add(ctx, array, NULL);
+ grn_io_unlock(array->io);
+ }
+ } else {
+ id = grn_array_add(ctx, array, NULL);
+ }
+ added_ = id ? 1 : 0;
+ if (added) { *added = added_; }
+ }
+ break;
+ }
+ if (added_) {
+ grn_hook *hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT];
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_, oldvalue_, value_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4, {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}}};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_TEXT_INIT(&oldvalue_, 0);
+ GRN_TEXT_INIT(&value_, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&value_, key, key_size);
+ GRN_UINT32_SET(ctx, &id_, id);
+ GRN_UINT32_SET(ctx, &flags_, GRN_OBJ_SET);
+ while (hooks) {
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, &oldvalue_);
+ grn_ctx_push(ctx, &value_);
+ grn_ctx_push(ctx, &flags_);
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &table, &pctx.user_data);
+ } else {
+ grn_obj_default_set_value_hook(ctx, 1, &table, &pctx.user_data);
+ }
+ if (ctx->rc) { break; }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+grn_id
+grn_table_get_by_key(grn_ctx *ctx, grn_obj *table, grn_obj *key)
+{
+ grn_id id = GRN_ID_NIL;
+ if (table->header.domain == key->header.domain) {
+ id = grn_table_get(ctx, table, GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key));
+ } else {
+ grn_rc rc;
+ grn_obj buf;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, table->header.domain);
+ if ((rc = grn_obj_cast(ctx, key, &buf, GRN_TRUE))) {
+ grn_obj *domain = grn_ctx_at(ctx, table->header.domain);
+ ERR_CAST(table, domain, key);
+ } else {
+ id = grn_table_get(ctx, table, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ return id;
+}
+
+grn_id
+grn_table_add_by_key(grn_ctx *ctx, grn_obj *table, grn_obj *key, int *added)
+{
+ grn_id id = GRN_ID_NIL;
+ if (table->header.domain == key->header.domain) {
+ id = grn_table_add(ctx, table, GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key), added);
+ } else {
+ grn_rc rc;
+ grn_obj buf;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, table->header.domain);
+ if ((rc = grn_obj_cast(ctx, key, &buf, GRN_TRUE))) {
+ grn_obj *domain = grn_ctx_at(ctx, table->header.domain);
+ ERR_CAST(table, domain, key);
+ } else {
+ id = grn_table_add(ctx, table, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf), added);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ return id;
+}
+
+grn_id
+grn_table_get(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ if (table->header.type == GRN_DB) {
+ grn_db *db = (grn_db *)table;
+ table = db->keys;
+ }
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ id = grn_pat_get(ctx, (grn_pat *)table, key, key_size, NULL);
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ id = grn_dat_get(ctx, (grn_dat *)table, key, key_size, NULL);
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ id = grn_hash_get(ctx, (grn_hash *)table, key, key_size, NULL);
+ });
+ break;
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+grn_id
+grn_table_at(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_DB :
+ {
+ grn_db *db = (grn_db *)table;
+ id = grn_table_at(ctx, db->keys, id);
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ id = grn_pat_at(ctx, (grn_pat *)table, id);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ id = grn_dat_at(ctx, (grn_dat *)table, id);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ id = grn_hash_at(ctx, (grn_hash *)table, id);
+ break;
+ case GRN_TABLE_NO_KEY :
+ id = grn_array_at(ctx, (grn_array *)table, id);
+ break;
+ default :
+ id = GRN_ID_NIL;
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+inline static grn_id
+grn_table_add_v_inline(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added)
+{
+ grn_id id = GRN_ID_NIL;
+ if (!key || !key_size) { return GRN_ID_NIL; }
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ id = grn_pat_add(ctx, (grn_pat *)table, key, key_size, value, added);
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ id = grn_dat_add(ctx, (grn_dat *)table, key, key_size, value, added);
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ id = grn_hash_add(ctx, (grn_hash *)table, key, key_size, value, added);
+ });
+ break;
+ case GRN_TABLE_NO_KEY :
+ id = grn_array_add(ctx, (grn_array *)table, value);
+ if (added) { *added = id ? 1 : 0; }
+ break;
+ }
+ }
+ return id;
+}
+
+grn_id
+grn_table_add_v(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added) {
+ grn_id id;
+ GRN_API_ENTER;
+ id = grn_table_add_v_inline(ctx, table, key, key_size, value, added);
+ GRN_API_RETURN(id);
+}
+
+grn_id
+grn_table_get_v(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ id = grn_pat_get(ctx, (grn_pat *)table, key, key_size, value);
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ id = grn_dat_get(ctx, (grn_dat *)table, key, key_size, value);
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ id = grn_hash_get(ctx, (grn_hash *)table, key, key_size, value);
+ });
+ break;
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+int
+grn_table_get_key(grn_ctx *ctx, grn_obj *table, grn_id id, void *keybuf, int buf_size)
+{
+ int r = 0;
+ GRN_API_ENTER;
+ if (table) {
+ if (table->header.type == GRN_DB) {
+ table = ((grn_db *)table)->keys;
+ }
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ r = grn_hash_get_key(ctx, (grn_hash *)table, id, keybuf, buf_size);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ r = grn_pat_get_key(ctx, (grn_pat *)table, id, keybuf, buf_size);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ r = grn_dat_get_key(ctx, (grn_dat *)table, id, keybuf, buf_size);
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *a = (grn_array *)table;
+ if (a->obj.header.domain) {
+ if ((unsigned int) buf_size >= a->value_size) {
+ r = grn_array_get_value(ctx, a, id, keybuf);
+ } else {
+ r = a->value_size;
+ }
+ }
+ }
+ break;
+ }
+ }
+ GRN_API_RETURN(r);
+}
+
+int
+grn_table_get_key2(grn_ctx *ctx, grn_obj *table, grn_id id, grn_obj *bulk)
+{
+ int r = 0;
+ GRN_API_ENTER;
+ if (table) {
+ if (table->header.type == GRN_DB) {
+ table = ((grn_db *)table)->keys;
+ }
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ r = grn_hash_get_key2(ctx, (grn_hash *)table, id, bulk);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ r = grn_pat_get_key2(ctx, (grn_pat *)table, id, bulk);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ r = grn_dat_get_key2(ctx, (grn_dat *)table, id, bulk);
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *a = (grn_array *)table;
+ if (a->obj.header.domain) {
+ if (!grn_bulk_space(ctx, bulk, a->value_size)) {
+ char *curr = GRN_BULK_CURR(bulk);
+ r = grn_array_get_value(ctx, a, id, curr - a->value_size);
+ }
+ }
+ }
+ break;
+ }
+ }
+ GRN_API_RETURN(r);
+}
+
+static grn_rc
+grn_obj_clear_value(grn_ctx *ctx, grn_obj *obj, grn_id id)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_DB_OBJP(obj)) {
+ grn_obj buf;
+ grn_id range = DB_OBJ(obj)->range;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ switch (obj->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ rc = grn_obj_set_value(ctx, obj, id, &buf, GRN_OBJ_SET);
+ break;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ return rc;
+}
+
+static void
+call_delete_hook(grn_ctx *ctx, grn_obj *table, grn_id rid, const void *key, unsigned int key_size)
+{
+ if (rid) {
+ grn_hook *hooks = DB_OBJ(table)->hooks[GRN_HOOK_DELETE];
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_, oldvalue_, value_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4, {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}}};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_TEXT_INIT(&oldvalue_, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_INIT(&value_, 0);
+ GRN_TEXT_SET_REF(&oldvalue_, key, key_size);
+ GRN_UINT32_SET(ctx, &id_, rid);
+ GRN_UINT32_SET(ctx, &flags_, GRN_OBJ_SET);
+ while (hooks) {
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, &oldvalue_);
+ grn_ctx_push(ctx, &value_);
+ grn_ctx_push(ctx, &flags_);
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &table, &pctx.user_data);
+ } else {
+ grn_obj_default_set_value_hook(ctx, 1, &table, &pctx.user_data);
+ }
+ if (ctx->rc) { break; }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ }
+}
+
+static void
+clear_column_values(grn_ctx *ctx, grn_obj *table, grn_id rid)
+{
+ if (rid) {
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { grn_obj_clear_value(ctx, col, rid); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ }
+}
+
+static void
+delete_reference_records_in_index(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_obj *index)
+{
+ grn_ii *ii = (grn_ii *)index;
+ grn_ii_cursor *ii_cursor = NULL;
+ grn_posting *posting;
+ grn_obj source_ids;
+ unsigned int i, n_ids;
+ grn_obj sources;
+ grn_bool have_reference_source = GRN_FALSE;
+
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+ GRN_PTR_INIT(&sources, GRN_OBJ_VECTOR, 0);
+
+ grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &source_ids);
+ n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id);
+ if (n_ids == 0) {
+ goto exit;
+ }
+
+ for (i = 0; i < n_ids; i++) {
+ grn_id source_id;
+ grn_obj *source;
+
+ source_id = GRN_UINT32_VALUE_AT(&source_ids, i);
+ source = grn_ctx_at(ctx, source_id);
+ if (grn_obj_get_range(ctx, source) == index->header.domain) {
+ GRN_PTR_PUT(ctx, &sources, source);
+ have_reference_source = GRN_TRUE;
+ } else {
+ grn_obj_unlink(ctx, source);
+ GRN_PTR_PUT(ctx, &sources, NULL);
+ }
+ }
+
+ if (!have_reference_source) {
+ goto exit;
+ }
+
+ ii_cursor = grn_ii_cursor_open(ctx, ii, id, GRN_ID_NIL, GRN_ID_MAX,
+ ii->n_elements, 0);
+ if (!ii_cursor) {
+ goto exit;
+ }
+
+ while ((posting = grn_ii_cursor_next(ctx, ii_cursor))) {
+ grn_obj *source = GRN_PTR_VALUE_AT(&sources, posting->sid - 1);
+ if (!source) {
+ continue;
+ }
+ switch (source->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ switch (source->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR :
+ grn_obj_clear_value(ctx, source, posting->rid);
+ break;
+ case GRN_OBJ_COLUMN_VECTOR :
+ {
+ grn_obj value;
+ grn_obj new_value;
+ GRN_TEXT_INIT(&value, 0);
+ grn_obj_get_value(ctx, source, posting->rid, &value);
+ if (value.header.type == GRN_UVECTOR) {
+ int i, n_ids;
+ GRN_RECORD_INIT(&new_value, GRN_OBJ_VECTOR, value.header.domain);
+ n_ids = GRN_BULK_VSIZE(&value) / sizeof(grn_id);
+ for (i = 0; i < n_ids; i++) {
+ grn_id reference_id = GRN_RECORD_VALUE_AT(&value, i);
+ if (reference_id == id) {
+ continue;
+ }
+ GRN_RECORD_PUT(ctx, &new_value, reference_id);
+ }
+ } else {
+ unsigned int i, n_elements;
+ GRN_TEXT_INIT(&new_value, GRN_OBJ_VECTOR);
+ n_elements = grn_vector_size(ctx, &value);
+ for (i = 0; i < n_elements; i++) {
+ const char *content;
+ unsigned int content_length;
+ unsigned int weight;
+ grn_id domain;
+ content_length =
+ grn_vector_get_element(ctx, &value, i,
+ &content, &weight, &domain);
+ if (grn_table_get(ctx, table, content, content_length) == id) {
+ continue;
+ }
+ grn_vector_add_element(ctx, &new_value, content, content_length,
+ weight, domain);
+ }
+ }
+ grn_obj_set_value(ctx, source, posting->rid, &new_value,
+ GRN_OBJ_SET);
+ GRN_OBJ_FIN(ctx, &new_value);
+ GRN_OBJ_FIN(ctx, &value);
+ }
+ break;
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ grn_obj_clear_value(ctx, source, posting->rid);
+ break;
+ }
+ }
+
+exit:
+ if (ii_cursor) {
+ grn_ii_cursor_close(ctx, ii_cursor);
+ }
+ grn_obj_unlink(ctx, &source_ids);
+ {
+ int i, n_sources;
+ n_sources = GRN_BULK_VSIZE(&sources) / sizeof(grn_obj *);
+ for (i = 0; i < n_sources; i++) {
+ grn_obj *source = GRN_PTR_VALUE_AT(&sources, i);
+ grn_obj_unlink(ctx, source);
+ }
+ grn_obj_unlink(ctx, &sources);
+ }
+}
+
+static grn_rc
+delete_reference_records(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ grn_hash *cols;
+ grn_id *key;
+
+ cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (!cols) {
+ return ctx->rc;
+ }
+
+ if (!grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_hash_close(ctx, cols);
+ return ctx->rc;
+ }
+
+ GRN_HASH_EACH(ctx, cols, tid, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (!col) {
+ continue;
+ }
+ if (col->header.type != GRN_COLUMN_INDEX) {
+ continue;
+ }
+ delete_reference_records_in_index(ctx, table, id, col);
+ if (ctx->rc != GRN_SUCCESS) {
+ break;
+ }
+ });
+
+ grn_hash_close(ctx, cols);
+
+ return ctx->rc;
+}
+
+static grn_rc
+grn_table_delete_prepare(grn_ctx *ctx, grn_obj *table,
+ grn_id id, const void *key, unsigned int key_size)
+{
+ grn_rc rc;
+
+ rc = delete_reference_records(ctx, table, id);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ call_delete_hook(ctx, table, id, key, key_size);
+ clear_column_values(ctx, table, id);
+
+ return rc;
+}
+
+grn_rc
+grn_table_delete(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size)
+{
+ grn_id rid = GRN_ID_NIL;
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (table) {
+ if (key && key_size) { rid = grn_table_get(ctx, table, key, key_size); }
+ if (rid) {
+ rc = grn_table_delete_prepare(ctx, table, rid, key, key_size);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ switch (table->header.type) {
+ case GRN_DB :
+ /* todo : delete tables and columns from db */
+ break;
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ grn_pat *pat = (grn_pat *)table;
+ if (pat->io && !(pat->io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, pat->io, grn_lock_timeout))) {
+ rc = grn_pat_delete(ctx, pat, key, key_size, NULL);
+ grn_io_unlock(pat->io);
+ }
+ } else {
+ rc = grn_pat_delete(ctx, pat, key, key_size, NULL);
+ }
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ grn_dat *dat = (grn_dat *)table;
+ if (dat->io && !(dat->io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, dat->io, grn_lock_timeout))) {
+ rc = grn_dat_delete(ctx, dat, key, key_size, NULL);
+ grn_io_unlock(dat->io);
+ }
+ } else {
+ rc = grn_dat_delete(ctx, dat, key, key_size, NULL);
+ }
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ grn_hash *hash = (grn_hash *)table;
+ if (hash->io && !(hash->io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, hash->io, grn_lock_timeout))) {
+ rc = grn_hash_delete(ctx, hash, key, key_size, NULL);
+ grn_io_unlock(hash->io);
+ }
+ } else {
+ rc = grn_hash_delete(ctx, hash, key, key_size, NULL);
+ }
+ });
+ break;
+ }
+ if (rc == GRN_SUCCESS) {
+ grn_obj_touch(ctx, table, NULL);
+ }
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+_grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ if (table) {
+ if (id) {
+ const void *key = NULL;
+ unsigned int key_size = 0;
+
+ if (table->header.type != GRN_TABLE_NO_KEY) {
+ key = _grn_table_key(ctx, table, id, &key_size);
+ }
+ rc = grn_table_delete_prepare(ctx, table, id, key, key_size);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ // todo : support optarg
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_delete_by_id(ctx, (grn_pat *)table, id, optarg);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_delete_by_id(ctx, (grn_dat *)table, id, optarg);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = grn_hash_delete_by_id(ctx, (grn_hash *)table, id, optarg);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_array_delete_by_id(ctx, (grn_array *)table, id, optarg);
+ break;
+ }
+ }
+ }
+exit :
+ return rc;
+}
+
+grn_rc
+grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ grn_rc rc;
+ grn_io *io;
+ GRN_API_ENTER;
+ if ((io = grn_obj_get_io(ctx, table)) && !(io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, io, grn_lock_timeout))) {
+ rc = _grn_table_delete_by_id(ctx, table, id, NULL);
+ grn_io_unlock(io);
+ }
+ } else {
+ rc = _grn_table_delete_by_id(ctx, table, id, NULL);
+ }
+ if (rc == GRN_SUCCESS) {
+ grn_obj_touch(ctx, table, NULL);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc grn_ja_truncate(grn_ctx *ctx, grn_ja *ja);
+grn_rc grn_ra_truncate(grn_ctx *ctx, grn_ra *ra);
+
+grn_rc
+grn_column_truncate(grn_ctx *ctx, grn_obj *column)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (column) {
+ grn_hook *hooks;
+ switch (column->header.type) {
+ case GRN_COLUMN_INDEX :
+ rc = grn_ii_truncate(ctx, (grn_ii *)column);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ for (hooks = DB_OBJ(column)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_ja_truncate(ctx, (grn_ja *)column);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ for (hooks = DB_OBJ(column)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_ra_truncate(ctx, (grn_ra *)column);
+ break;
+ }
+ if (rc == GRN_SUCCESS) {
+ grn_obj_touch(ctx, column, NULL);
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_truncate(grn_ctx *ctx, grn_obj *table)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (table) {
+ grn_hook *hooks;
+ grn_hash *cols;
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+ grn_obj token_filters;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { grn_column_truncate(ctx, col); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ if (table->header.type != GRN_TABLE_NO_KEY) {
+ grn_table_get_info(ctx, table, NULL, NULL, &tokenizer, &normalizer, NULL);
+ GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ grn_obj_get_info(ctx, table, GRN_INFO_TOKEN_FILTERS, &token_filters);
+ }
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ for (hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_pat_truncate(ctx, (grn_pat *)table);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ for (hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_dat_truncate(ctx, (grn_dat *)table);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ for (hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_hash_truncate(ctx, (grn_hash *)table);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_array_truncate(ctx, (grn_array *)table);
+ break;
+ }
+ if (table->header.type != GRN_TABLE_NO_KEY) {
+ grn_obj_set_info(ctx, table, GRN_INFO_DEFAULT_TOKENIZER, tokenizer);
+ grn_obj_set_info(ctx, table, GRN_INFO_NORMALIZER, normalizer);
+ grn_obj_set_info(ctx, table, GRN_INFO_TOKEN_FILTERS, &token_filters);
+ GRN_OBJ_FIN(ctx, &token_filters);
+ }
+ if (rc == GRN_SUCCESS) {
+ grn_obj_touch(ctx, table, NULL);
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_get_info(grn_ctx *ctx, grn_obj *table, grn_table_flags *flags,
+ grn_encoding *encoding, grn_obj **tokenizer,
+ grn_obj **normalizer,
+ grn_obj **token_filters)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ if (flags) { *flags = ((grn_pat *)table)->header->flags; }
+ if (encoding) { *encoding = ((grn_pat *)table)->encoding; }
+ if (tokenizer) { *tokenizer = ((grn_pat *)table)->tokenizer; }
+ if (normalizer) { *normalizer = ((grn_pat *)table)->normalizer; }
+ if (token_filters) { *token_filters = &(((grn_pat *)table)->token_filters); }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ if (flags) { *flags = ((grn_dat *)table)->header->flags; }
+ if (encoding) { *encoding = ((grn_dat *)table)->encoding; }
+ if (tokenizer) { *tokenizer = ((grn_dat *)table)->tokenizer; }
+ if (normalizer) { *normalizer = ((grn_dat *)table)->normalizer; }
+ if (token_filters) { *token_filters = &(((grn_dat *)table)->token_filters); }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ if (flags) { *flags = ((grn_hash *)table)->header.common->flags; }
+ if (encoding) { *encoding = ((grn_hash *)table)->encoding; }
+ if (tokenizer) { *tokenizer = ((grn_hash *)table)->tokenizer; }
+ if (normalizer) { *normalizer = ((grn_hash *)table)->normalizer; }
+ if (token_filters) { *token_filters = &(((grn_hash *)table)->token_filters); }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (flags) { *flags = grn_array_get_flags(ctx, ((grn_array *)table)); }
+ if (encoding) { *encoding = GRN_ENC_NONE; }
+ if (tokenizer) { *tokenizer = NULL; }
+ if (normalizer) { *normalizer = NULL; }
+ if (token_filters) { *token_filters = NULL; }
+ rc = GRN_SUCCESS;
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+unsigned int
+grn_table_size(grn_ctx *ctx, grn_obj *table)
+{
+ unsigned int n = 0;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_DB :
+ n = grn_table_size(ctx, ((grn_db *)table)->keys);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ n = grn_pat_size(ctx, (grn_pat *)table);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ n = grn_dat_size(ctx, (grn_dat *)table);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ n = grn_hash_size(ctx, (grn_hash *)table);
+ break;
+ case GRN_TABLE_NO_KEY :
+ n = grn_array_size(ctx, (grn_array *)table);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "not supported");
+ break;
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid table assigned");
+ }
+ GRN_API_RETURN(n);
+}
+
+inline static void
+subrecs_push(byte *subrecs, int size, int n_subrecs, double score, void *body, int dir)
+{
+ byte *v;
+ double *c2;
+ int n = n_subrecs - 1, n2;
+ while (n) {
+ n2 = (n - 1) >> 1;
+ c2 = GRN_RSET_SUBRECS_NTH(subrecs,size,n2);
+ if (GRN_RSET_SUBRECS_CMP(score, *c2, dir) >= 0) { break; }
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ }
+ v = subrecs + n * (GRN_RSET_SCORE_SIZE + size);
+ *((double *)v) = score;
+ grn_memcpy(v + GRN_RSET_SCORE_SIZE, body, size);
+}
+
+inline static void
+subrecs_replace_min(byte *subrecs, int size, int n_subrecs, double score, void *body, int dir)
+{
+ byte *v;
+ int n = 0, n1, n2;
+ double *c1, *c2;
+ for (;;) {
+ n1 = n * 2 + 1;
+ n2 = n1 + 1;
+ c1 = n1 < n_subrecs ? GRN_RSET_SUBRECS_NTH(subrecs,size,n1) : NULL;
+ c2 = n2 < n_subrecs ? GRN_RSET_SUBRECS_NTH(subrecs,size,n2) : NULL;
+ if (c1 && GRN_RSET_SUBRECS_CMP(score, *c1, dir) > 0) {
+ if (c2 &&
+ GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0 &&
+ GRN_RSET_SUBRECS_CMP(*c1, *c2, dir) > 0) {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ } else {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c1);
+ n = n1;
+ }
+ } else {
+ if (c2 && GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0) {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ } else {
+ break;
+ }
+ }
+ }
+ v = subrecs + n * (GRN_RSET_SCORE_SIZE + size);
+ grn_memcpy(v, &score, GRN_RSET_SCORE_SIZE);
+ grn_memcpy(v + GRN_RSET_SCORE_SIZE, body, size);
+}
+
+inline static void
+grn_table_add_subrec_inline(grn_obj *table, grn_rset_recinfo *ri, double score,
+ grn_rset_posinfo *pi, int dir)
+{
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ int limit = DB_OBJ(table)->max_n_subrecs;
+ ri->score += score;
+ ri->n_subrecs += 1;
+ if (limit) {
+ int subrec_size = DB_OBJ(table)->subrec_size;
+ int n_subrecs = GRN_RSET_N_SUBRECS(ri);
+ if (pi) {
+ byte *body = (byte *)pi + DB_OBJ(table)->subrec_offset;
+ if (limit < n_subrecs) {
+ if (GRN_RSET_SUBRECS_CMP(score, *((double *)(ri->subrecs)), dir) > 0) {
+ subrecs_replace_min((byte *)ri->subrecs, subrec_size, limit, score, body, dir);
+ }
+ } else {
+ subrecs_push((byte *)ri->subrecs, subrec_size, n_subrecs, score, body, dir);
+ }
+ }
+ }
+ }
+}
+
+void
+grn_table_add_subrec(grn_obj *table, grn_rset_recinfo *ri, double score,
+ grn_rset_posinfo *pi, int dir)
+{
+ grn_table_add_subrec_inline(table, ri, score, pi, dir);
+}
+
+grn_table_cursor *
+grn_table_cursor_open(grn_ctx *ctx, grn_obj *table,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags)
+{
+ grn_rc rc;
+ grn_table_cursor *tc = NULL;
+ unsigned int table_size;
+ if (!table) { return tc; }
+ GRN_API_ENTER;
+ table_size = grn_table_size(ctx, table);
+ if (flags & GRN_CURSOR_PREFIX) {
+ if (offset < 0) {
+ ERR(GRN_TOO_SMALL_OFFSET,
+ "can't use negative offset with GRN_CURSOR_PREFIX: %d", offset);
+ } else if (offset != 0 && offset >= (int) table_size) {
+ ERR(GRN_TOO_LARGE_OFFSET,
+ "offset is not less than table size: offset:%d, table_size:%d",
+ offset, table_size);
+ } else {
+ if (limit < -1) {
+ ERR(GRN_TOO_SMALL_LIMIT,
+ "can't use smaller limit than -1 with GRN_CURSOR_PREFIX: %d",
+ limit);
+ } else if (limit == -1) {
+ limit = table_size;
+ }
+ }
+ } else {
+ rc = grn_normalize_offset_and_limit(ctx, table_size, &offset, &limit);
+ if (rc) {
+ ERR(rc, "grn_normalize_offset_and_limit failed");
+ }
+ }
+ if (!ctx->rc) {
+ if (table->header.type == GRN_DB) { table = ((grn_db *)table)->keys; }
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, min, min_size, {
+ WITH_NORMALIZE(pat, max, max_size, {
+ grn_pat_cursor *pat_cursor;
+ pat_cursor = grn_pat_cursor_open(ctx, pat,
+ min, min_size,
+ max, max_size,
+ offset, limit, flags);
+ tc = (grn_table_cursor *)pat_cursor;
+ });
+ });
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, min, min_size, {
+ WITH_NORMALIZE(dat, max, max_size, {
+ grn_dat_cursor *dat_cursor;
+ dat_cursor = grn_dat_cursor_open(ctx, dat,
+ min, min_size,
+ max, max_size,
+ offset, limit, flags);
+ tc = (grn_table_cursor *)dat_cursor;
+ });
+ });
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ WITH_NORMALIZE(hash, min, min_size, {
+ WITH_NORMALIZE(hash, max, max_size, {
+ grn_hash_cursor *hash_cursor;
+ hash_cursor = grn_hash_cursor_open(ctx, hash,
+ min, min_size,
+ max, max_size,
+ offset, limit, flags);
+ tc = (grn_table_cursor *)hash_cursor;
+ });
+ });
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ tc = (grn_table_cursor *)grn_array_cursor_open(ctx, (grn_array *)table,
+ GRN_ID_NIL, GRN_ID_NIL,
+ offset, limit, flags);
+ break;
+ }
+ }
+ if (tc) {
+ grn_id id = grn_obj_register(ctx, ctx->impl->db, NULL, 0);
+ DB_OBJ(tc)->header.domain = GRN_ID_NIL;
+ DB_OBJ(tc)->range = GRN_ID_NIL;
+ grn_db_obj_init(ctx, ctx->impl->db, id, DB_OBJ(tc));
+ }
+ GRN_API_RETURN(tc);
+}
+
+grn_table_cursor *
+grn_table_cursor_open_by_id(grn_ctx *ctx, grn_obj *table,
+ grn_id min, grn_id max, int flags)
+{
+ grn_table_cursor *tc = NULL;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ tc = (grn_table_cursor *)grn_pat_cursor_open(ctx, (grn_pat *)table,
+ NULL, 0, NULL, 0, 0, -1, flags);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ tc = (grn_table_cursor *)grn_dat_cursor_open(ctx, (grn_dat *)table,
+ NULL, 0, NULL, 0, 0, -1, flags);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ tc = (grn_table_cursor *)grn_hash_cursor_open(ctx, (grn_hash *)table,
+ NULL, 0, NULL, 0, 0, -1, flags);
+ break;
+ case GRN_TABLE_NO_KEY :
+ tc = (grn_table_cursor *)grn_array_cursor_open(ctx, (grn_array *)table,
+ min, max, 0, -1, flags);
+ break;
+ }
+ }
+ GRN_API_RETURN(tc);
+}
+
+grn_rc
+grn_table_cursor_close(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ const char *tag = "[table][cursor][close]";
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (!tc) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "%s invalid cursor", tag);
+ } else {
+ {
+ if (DB_OBJ(tc)->finalizer) {
+ DB_OBJ(tc)->finalizer(ctx, 1, (grn_obj **)&tc, &DB_OBJ(tc)->user_data);
+ }
+ if (DB_OBJ(tc)->source) {
+ GRN_FREE(DB_OBJ(tc)->source);
+ }
+ /*
+ grn_hook_entry entry;
+ for (entry = 0; entry < N_HOOK_ENTRIES; entry++) {
+ grn_hook_free(ctx, DB_OBJ(tc)->hooks[entry]);
+ }
+ */
+ grn_obj_delete_by_id(ctx, DB_OBJ(tc)->db, DB_OBJ(tc)->id, GRN_FALSE);
+ }
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ grn_pat_cursor_close(ctx, (grn_pat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ grn_dat_cursor_close(ctx, (grn_dat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ grn_hash_cursor_close(ctx, (grn_hash_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ grn_array_cursor_close(ctx, (grn_array_cursor *)tc);
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+inline static grn_id
+grn_table_cursor_next_inline(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ const char *tag = "[table][cursor][next]";
+ grn_id id = GRN_ID_NIL;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid cursor", tag);
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ id = grn_pat_cursor_next(ctx, (grn_pat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ id = grn_dat_cursor_next(ctx, (grn_dat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ id = grn_hash_cursor_next(ctx, (grn_hash_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ id = grn_array_cursor_next(ctx, (grn_array_cursor *)tc);
+ break;
+ case GRN_CURSOR_COLUMN_INDEX :
+ {
+ grn_posting *ip = grn_index_cursor_next(ctx, (grn_obj *)tc, NULL);
+ if (ip) { id = ip->rid; }
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+ return id;
+}
+
+grn_id
+grn_table_cursor_next(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ grn_id id;
+ GRN_API_ENTER;
+ id = grn_table_cursor_next_inline(ctx, tc);
+ GRN_API_RETURN(id);
+}
+
+int
+grn_table_cursor_get_key(grn_ctx *ctx, grn_table_cursor *tc, void **key)
+{
+ const char *tag = "[table][cursor][get-key]";
+ int len = 0;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid cursor", tag);
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ len = grn_pat_cursor_get_key(ctx, (grn_pat_cursor *)tc, key);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ len = grn_dat_cursor_get_key(ctx, (grn_dat_cursor *)tc, (const void **)key);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ len = grn_hash_cursor_get_key(ctx, (grn_hash_cursor *)tc, key);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(len);
+}
+
+inline static int
+grn_table_cursor_get_value_inline(grn_ctx *ctx, grn_table_cursor *tc, void **value)
+{
+ const char *tag = "[table][cursor][get-value]";
+ int len = 0;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid cursor", tag);
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ len = grn_pat_cursor_get_value(ctx, (grn_pat_cursor *)tc, value);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ *value = NULL;
+ len = 0;
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ len = grn_hash_cursor_get_value(ctx, (grn_hash_cursor *)tc, value);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ len = grn_array_cursor_get_value(ctx, (grn_array_cursor *)tc, value);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+ return len;
+}
+
+int
+grn_table_cursor_get_value(grn_ctx *ctx, grn_table_cursor *tc, void **value)
+{
+ int len;
+ GRN_API_ENTER;
+ len = grn_table_cursor_get_value_inline(ctx, tc, value);
+ GRN_API_RETURN(len);
+}
+
+grn_rc
+grn_table_cursor_set_value(grn_ctx *ctx, grn_table_cursor *tc,
+ const void *value, int flags)
+{
+ const char *tag = "[table][cursor][set-value]";
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid cursor", tag);
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ rc = grn_pat_cursor_set_value(ctx, (grn_pat_cursor *)tc, value, flags);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ rc = grn_hash_cursor_set_value(ctx, (grn_hash_cursor *)tc, value, flags);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ rc = grn_array_cursor_set_value(ctx, (grn_array_cursor *)tc, value, flags);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_cursor_delete(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ const char *tag = "[table][cursor][delete]";
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid cursor", tag);
+ } else {
+ grn_id id;
+ grn_obj *table;
+ const void *key = NULL;
+ unsigned int key_size = 0;
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ {
+ grn_pat_cursor *pc = (grn_pat_cursor *)tc;
+ id = pc->curr_rec;
+ table = (grn_obj *)(pc->pat);
+ key = _grn_pat_key(ctx, pc->pat, id, &key_size);
+ rc = grn_table_delete_prepare(ctx, table, id, key, key_size);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ rc = grn_pat_cursor_delete(ctx, pc, NULL);
+ }
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ {
+ grn_hash_cursor *hc = (grn_hash_cursor *)tc;
+ id = hc->curr_rec;
+ table = (grn_obj *)(hc->hash);
+ key = _grn_hash_key(ctx, hc->hash, id, &key_size);
+ rc = grn_table_delete_prepare(ctx, table, id, key, key_size);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ rc = grn_hash_cursor_delete(ctx, hc, NULL);
+ }
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ {
+ grn_array_cursor *ac = (grn_array_cursor *)tc;
+ id = ac->curr_rec;
+ table = (grn_obj *)(ac->array);
+ rc = grn_table_delete_prepare(ctx, table, id, key, key_size);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ rc = grn_array_cursor_delete(ctx, ac, NULL);
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_table_cursor_table(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ const char *tag = "[table][cursor][table]";
+ grn_obj *obj = NULL;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid cursor", tag);
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ obj = (grn_obj *)(((grn_pat_cursor *)tc)->pat);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ obj = (grn_obj *)(((grn_dat_cursor *)tc)->dat);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ obj = (grn_obj *)(((grn_hash_cursor *)tc)->hash);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ obj = (grn_obj *)(((grn_array_cursor *)tc)->array);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "%s invalid type %d", tag, tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(obj);
+}
+
+typedef struct {
+ grn_db_obj obj;
+ grn_obj *index;
+ grn_table_cursor *tc;
+ grn_ii_cursor *iic;
+ grn_id tid;
+ grn_id rid_min;
+ grn_id rid_max;
+ int flags;
+} grn_index_cursor;
+
+grn_obj *
+grn_index_cursor_open(grn_ctx *ctx, grn_table_cursor *tc,
+ grn_obj *index, grn_id rid_min, grn_id rid_max, int flags)
+{
+ grn_index_cursor *ic = NULL;
+ GRN_API_ENTER;
+ if (tc && (ic = GRN_MALLOCN(grn_index_cursor, 1))) {
+ ic->tc = tc;
+ ic->index = index;
+ ic->iic = NULL;
+ ic->tid = GRN_ID_NIL;
+ ic->rid_min = rid_min;
+ ic->rid_max = rid_max;
+ ic->flags = flags;
+ GRN_DB_OBJ_SET_TYPE(ic, GRN_CURSOR_COLUMN_INDEX);
+ {
+ grn_id id = grn_obj_register(ctx, ctx->impl->db, NULL, 0);
+ DB_OBJ(ic)->header.domain = GRN_ID_NIL;
+ DB_OBJ(ic)->range = GRN_ID_NIL;
+ grn_db_obj_init(ctx, ctx->impl->db, id, DB_OBJ(ic));
+ }
+ }
+ GRN_API_RETURN((grn_obj *)ic);
+}
+
+grn_posting *
+grn_index_cursor_next(grn_ctx *ctx, grn_obj *c, grn_id *tid)
+{
+ grn_posting *ip = NULL;
+ grn_index_cursor *ic = (grn_index_cursor *)c;
+ GRN_API_ENTER;
+ if (ic->iic) {
+ if (ic->flags & GRN_OBJ_WITH_POSITION) {
+ ip = grn_ii_cursor_next_pos(ctx, ic->iic);
+ while (!ip && grn_ii_cursor_next(ctx, ic->iic)) {
+ ip = grn_ii_cursor_next_pos(ctx, ic->iic);
+ break;
+ }
+ } else {
+ ip = grn_ii_cursor_next(ctx, ic->iic);
+ }
+ }
+ if (!ip) {
+ while ((ic->tid = grn_table_cursor_next_inline(ctx, ic->tc))) {
+ grn_ii *ii = (grn_ii *)ic->index;
+ if (ic->iic) { grn_ii_cursor_close(ctx, ic->iic); }
+ if ((ic->iic = grn_ii_cursor_open(ctx, ii, ic->tid,
+ ic->rid_min, ic->rid_max,
+ ii->n_elements, ic->flags))) {
+ ip = grn_ii_cursor_next(ctx, ic->iic);
+ if (ip && ic->flags & GRN_OBJ_WITH_POSITION) {
+ ip = grn_ii_cursor_next_pos(ctx, ic->iic);
+ }
+ if (ip) {
+ break;
+ }
+ }
+ }
+ }
+ if (tid) { *tid = ic->tid; }
+ GRN_API_RETURN((grn_posting *)ip);
+}
+
+grn_rc
+grn_table_search(grn_ctx *ctx, grn_obj *table, const void *key, uint32_t key_size,
+ grn_operator mode, grn_obj *res, grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, key, key_size, {
+ switch (mode) {
+ case GRN_OP_EXACT :
+ {
+ grn_id id = grn_pat_get(ctx, pat, key, key_size, NULL);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ // todo : support op;
+ break;
+ case GRN_OP_LCP :
+ {
+ grn_id id = grn_pat_lcp_search(ctx, pat, key, key_size);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ // todo : support op;
+ break;
+ case GRN_OP_SUFFIX :
+ rc = grn_pat_suffix_search(ctx, pat, key, key_size, (grn_hash *)res);
+ // todo : support op;
+ break;
+ case GRN_OP_PREFIX :
+ rc = grn_pat_prefix_search(ctx, pat, key, key_size, (grn_hash *)res);
+ // todo : support op;
+ break;
+ case GRN_OP_TERM_EXTRACT :
+ {
+ int len;
+ grn_id tid;
+ const char *sp = key;
+ const char *se = sp + key_size;
+ for (; sp < se; sp += len) {
+ if ((tid = grn_pat_lcp_search(ctx, pat, sp, se - sp))) {
+ grn_table_add(ctx, res, &tid, sizeof(grn_id), NULL);
+ /* todo : nsubrec++ if GRN_OBJ_TABLE_SUBSET assigned */
+ }
+ if (!(len = grn_charlen(ctx, sp, se))) { break; }
+ }
+ }
+ // todo : support op;
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "invalid mode %d", mode);
+ }
+ });
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, key, key_size, {
+ switch (mode) {
+ case GRN_OP_EXACT :
+ {
+ grn_id id = grn_dat_get(ctx, dat, key, key_size, NULL);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ break;
+ case GRN_OP_PREFIX :
+ {
+ grn_dat_cursor *dc = grn_dat_cursor_open(ctx, dat, key, key_size, NULL, 0,
+ 0, -1, GRN_CURSOR_PREFIX);
+ if (dc) {
+ grn_id id;
+ while ((id = grn_dat_cursor_next(ctx, dc))) {
+ grn_table_add(ctx, res, &id, sizeof(grn_id), NULL);
+ }
+ grn_dat_cursor_close(ctx, dc);
+ }
+ }
+ break;
+ case GRN_OP_LCP :
+ {
+ grn_id id = grn_dat_lcp_search(ctx, dat, key, key_size);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ break;
+ case GRN_OP_TERM_EXTRACT :
+ {
+ int len;
+ grn_id tid;
+ const char *sp = key;
+ const char *se = sp + key_size;
+ for (; sp < se; sp += len) {
+ if ((tid = grn_dat_lcp_search(ctx, dat, sp, se - sp))) {
+ grn_table_add(ctx, res, &tid, sizeof(grn_id), NULL);
+ /* todo : nsubrec++ if GRN_OBJ_TABLE_SUBSET assigned */
+ }
+ if (!(len = grn_charlen(ctx, sp, se))) { break; }
+ }
+ }
+ // todo : support op;
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "invalid mode %d", mode);
+ }
+ });
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ grn_id id = GRN_ID_NIL;
+ WITH_NORMALIZE(hash, key, key_size, {
+ id = grn_hash_get(ctx, hash, key, key_size, NULL);
+ });
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ break;
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_fuzzy_search(grn_ctx *ctx, grn_obj *table, const void *key, uint32_t key_size,
+ grn_fuzzy_search_optarg *args, grn_obj *res, grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ if (!grn_table_size(ctx, res) && op == GRN_OP_OR) {
+ WITH_NORMALIZE(pat, key, key_size, {
+ rc = grn_pat_fuzzy_search(ctx, pat, key, key_size,
+ args, (grn_hash *)res);
+ });
+ } else {
+ grn_obj *hash;
+ hash = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ table, NULL);
+ WITH_NORMALIZE(pat, key, key_size, {
+ rc = grn_pat_fuzzy_search(ctx, pat, key, key_size,
+ args, (grn_hash *)hash);
+ });
+ if (rc == GRN_SUCCESS) {
+ rc = grn_table_setoperation(ctx, res, hash, res, op);
+ }
+ grn_obj_unlink(ctx, hash);
+ }
+ }
+ break;
+ default :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_id
+grn_table_next(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ grn_id r = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ r = grn_pat_next(ctx, (grn_pat *)table, id);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ r = grn_dat_next(ctx, (grn_dat *)table, id);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ r = grn_hash_next(ctx, (grn_hash *)table, id);
+ break;
+ case GRN_TABLE_NO_KEY :
+ r = grn_array_next(ctx, (grn_array *)table, id);
+ break;
+ }
+ }
+ GRN_API_RETURN(r);
+}
+
+static grn_rc
+grn_accessor_resolve_one_index_column(grn_ctx *ctx, grn_accessor *accessor,
+ grn_obj *current_res, grn_obj **next_res)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *column = NULL;
+ grn_id next_res_domain_id = GRN_ID_NIL;
+
+ {
+ grn_obj *index;
+ grn_obj source_ids;
+ unsigned int i, n_ids;
+
+ index = accessor->obj;
+ next_res_domain_id = index->header.domain;
+
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+ grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &source_ids);
+ n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id);
+ for (i = 0; i < n_ids; i++) {
+ grn_id source_id;
+ grn_obj *source;
+
+ source_id = GRN_UINT32_VALUE_AT(&source_ids, i);
+ source = grn_ctx_at(ctx, source_id);
+ if (DB_OBJ(source)->range == next_res_domain_id) {
+ column = source;
+ break;
+ }
+ grn_obj_unlink(ctx, source);
+ }
+
+ if (!column) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+
+ {
+ grn_rc rc;
+ grn_obj *next_res_domain = grn_ctx_at(ctx, next_res_domain_id);
+ *next_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ next_res_domain, NULL);
+ rc = ctx->rc;
+ grn_obj_unlink(ctx, next_res_domain);
+ if (!*next_res) {
+ return rc;
+ }
+ }
+
+ {
+ grn_obj_flags column_value_flags = 0;
+ grn_obj column_value;
+ grn_posting add_posting;
+ grn_id *tid;
+ grn_rset_recinfo *recinfo;
+
+ if (column->header.type == GRN_COLUMN_VAR_SIZE) {
+ column_value_flags |= GRN_OBJ_VECTOR;
+ }
+ GRN_VALUE_FIX_SIZE_INIT(&column_value,
+ column_value_flags,
+ next_res_domain_id);
+
+ add_posting.sid = 0;
+ add_posting.pos = 0;
+ add_posting.weight = 0;
+
+ GRN_HASH_EACH(ctx, (grn_hash *)current_res, id, &tid, NULL, &recinfo, {
+ int i;
+ int n_elements;
+
+ add_posting.weight = recinfo->score - 1;
+
+ GRN_BULK_REWIND(&column_value);
+ grn_obj_get_value(ctx, column, *tid, &column_value);
+
+ n_elements = GRN_BULK_VSIZE(&column_value) / sizeof(grn_id);
+ for (i = 0; i < n_elements; i++) {
+ add_posting.rid = GRN_RECORD_VALUE_AT(&column_value, i);
+ rc = grn_ii_posting_add(ctx,
+ &add_posting,
+ (grn_hash *)*next_res,
+ GRN_OP_OR);
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ });
+
+ GRN_OBJ_FIN(ctx, &column_value);
+ }
+
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, *next_res);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_accessor_resolve_one_table(grn_ctx *ctx, grn_accessor *accessor,
+ grn_obj *current_res, grn_obj **next_res)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *table;
+
+ table = accessor->obj;
+ *next_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ table, NULL);
+ if (!*next_res) {
+ return ctx->rc;
+ }
+
+ grn_report_table(ctx,
+ "[accessor][resolve]",
+ "",
+ table);
+
+ {
+ grn_posting posting;
+
+ memset(&posting, 0, sizeof(posting));
+ GRN_HASH_EACH_BEGIN(ctx, (grn_hash *)current_res, cursor, id) {
+ void *key;
+ void *value;
+ grn_id *record_id;
+ grn_rset_recinfo *recinfo;
+ grn_id next_record_id;
+
+ grn_hash_cursor_get_key_value(ctx, cursor, &key, NULL, &value);
+ record_id = key;
+ recinfo = value;
+ next_record_id = grn_table_get(ctx,
+ table,
+ record_id,
+ sizeof(grn_id));
+ if (next_record_id == GRN_ID_NIL) {
+ continue;
+ }
+
+ posting.rid = next_record_id;
+ posting.weight = recinfo->score;
+ rc = grn_ii_posting_add(ctx,
+ &posting,
+ (grn_hash *)*next_res,
+ GRN_OP_OR);
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ } GRN_HASH_EACH_END(ctx, cursor);
+ }
+
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, *next_res);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_accessor_resolve_one_data_column(grn_ctx *ctx, grn_accessor *accessor,
+ grn_obj *current_res, grn_obj **next_res)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_index_datum index_datum;
+ unsigned int n_index_data;
+ grn_id next_res_domain_id = GRN_ID_NIL;
+
+ n_index_data = grn_column_get_all_index_data(ctx,
+ accessor->obj,
+ &index_datum,
+ 1);
+ if (n_index_data == 0) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ {
+ grn_obj *lexicon;
+ lexicon = grn_ctx_at(ctx, index_datum.index->header.domain);
+ if (grn_obj_id(ctx, lexicon) != current_res->header.domain) {
+ char index_name[GRN_TABLE_MAX_KEY_SIZE];
+ int index_name_size;
+ grn_obj *expected;
+ char expected_name[GRN_TABLE_MAX_KEY_SIZE];
+ int expected_name_size;
+
+ index_name_size = grn_obj_name(ctx,
+ index_datum.index,
+ index_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ expected = grn_ctx_at(ctx, current_res->header.domain);
+ expected_name_size = grn_obj_name(ctx,
+ expected,
+ expected_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[accessor][resolve][data-column] lexicon mismatch index: "
+ "<%.*s> "
+ "expected:<%.*s>",
+ index_name_size,
+ index_name,
+ expected_name_size,
+ expected_name);
+ return ctx->rc;
+ }
+ }
+
+ next_res_domain_id = DB_OBJ(index_datum.index)->range;
+
+ grn_report_index(ctx,
+ "[accessor][resolve][data-column]",
+ "",
+ index_datum.index);
+ {
+ grn_rc rc;
+ grn_obj *next_res_domain = grn_ctx_at(ctx, next_res_domain_id);
+ *next_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ next_res_domain, NULL);
+ rc = ctx->rc;
+ grn_obj_unlink(ctx, next_res_domain);
+ if (!*next_res) {
+ return rc;
+ }
+ }
+
+ {
+ grn_id *tid;
+ grn_rset_recinfo *recinfo;
+
+ GRN_HASH_EACH(ctx, (grn_hash *)current_res, id, &tid, NULL, &recinfo, {
+ grn_ii *ii = (grn_ii *)(index_datum.index);
+ grn_ii_cursor *ii_cursor;
+ grn_posting *posting;
+
+ ii_cursor = grn_ii_cursor_open(ctx, ii, *tid,
+ GRN_ID_NIL, GRN_ID_MAX,
+ ii->n_elements,
+ 0);
+ if (!ii_cursor) {
+ continue;
+ }
+
+ while ((posting = grn_ii_cursor_next(ctx, ii_cursor))) {
+ grn_posting add_posting;
+
+ if (index_datum.section > 0 && posting->sid != index_datum.section) {
+ continue;
+ }
+
+ add_posting = *posting;
+ add_posting.weight += recinfo->score - 1;
+ rc = grn_ii_posting_add(ctx,
+ &add_posting,
+ (grn_hash *)*next_res,
+ GRN_OP_OR);
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_ii_cursor_close(ctx, ii_cursor);
+
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ });
+ }
+
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, *next_res);
+ }
+
+ return rc;
+}
+
+grn_rc
+grn_accessor_resolve(grn_ctx *ctx, grn_obj *accessor, int deep,
+ grn_obj *base_res, grn_obj *res,
+ grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_accessor *a;
+ grn_obj accessor_stack;
+ int i, n_accessors;
+ grn_obj *current_res = base_res;
+
+ GRN_PTR_INIT(&accessor_stack, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ n_accessors = 0;
+ for (a = (grn_accessor *)accessor; a; a = a->next) {
+ if (deep == n_accessors) {
+ break;
+ }
+ GRN_PTR_PUT(ctx, &accessor_stack, a);
+ n_accessors++;
+ }
+
+ for (i = n_accessors; i > 0; i--) {
+ grn_obj *next_res = NULL;
+
+ a = (grn_accessor *)GRN_PTR_VALUE_AT(&accessor_stack, i - 1);
+ if (a->obj->header.type == GRN_COLUMN_INDEX) {
+ rc = grn_accessor_resolve_one_index_column(ctx, a,
+ current_res, &next_res);
+ } else if (grn_obj_is_table(ctx, a->obj)) {
+ rc = grn_accessor_resolve_one_table(ctx, a,
+ current_res, &next_res);
+ } else {
+ rc = grn_accessor_resolve_one_data_column(ctx, a,
+ current_res, &next_res);
+ }
+
+ if (current_res != base_res) {
+ grn_obj_unlink(ctx, current_res);
+ }
+
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+
+ current_res = next_res;
+ }
+
+ if (rc == GRN_SUCCESS && current_res != base_res) {
+ grn_id *record_id;
+ grn_rset_recinfo *recinfo;
+ GRN_HASH_EACH(ctx, (grn_hash *)current_res, id, &record_id, NULL, &recinfo, {
+ grn_posting posting;
+ posting.rid = *record_id;
+ posting.sid = 1;
+ posting.pos = 0;
+ posting.weight = recinfo->score - 1;
+ grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op);
+ });
+ grn_obj_unlink(ctx, current_res);
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op);
+ } else {
+ if (rc == GRN_SUCCESS) {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ }
+
+ GRN_OBJ_FIN(ctx, &accessor_stack);
+ return rc;
+}
+
+static inline void
+grn_obj_search_index_report(grn_ctx *ctx, const char *tag, grn_obj *index)
+{
+ grn_report_index(ctx, "[object][search]", tag, index);
+}
+
+static inline grn_rc
+grn_obj_search_accessor(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op, grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_accessor *a;
+ grn_obj *last_obj = NULL;
+ int n_accessors;
+
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ if (!a->next) {
+ last_obj = a->obj;
+ }
+ }
+ n_accessors = 0;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ n_accessors++;
+ if (GRN_OBJ_INDEX_COLUMNP(a->obj)) {
+ break;
+ }
+ }
+
+ {
+ grn_obj *index;
+ grn_operator index_op = GRN_OP_MATCH;
+ if (optarg && optarg->mode != GRN_OP_EXACT) {
+ index_op = optarg->mode;
+ }
+ if (grn_column_index(ctx, last_obj, index_op, &index, 1, NULL) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (n_accessors == 1) {
+ rc = grn_obj_search(ctx, index, query, res, op, optarg);
+ } else {
+ grn_obj *base_res;
+ grn_obj *range = grn_ctx_at(ctx, DB_OBJ(index)->range);
+ base_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ range,
+ NULL);
+ rc = ctx->rc;
+ grn_obj_unlink(ctx, range);
+ if (!base_res) {
+ goto exit;
+ }
+ if (optarg) {
+ optarg->match_info.min = GRN_ID_NIL;
+ }
+ rc = grn_obj_search(ctx, index, query, base_res, GRN_OP_OR, optarg);
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, base_res);
+ goto exit;
+ }
+ rc = grn_accessor_resolve(ctx, obj, n_accessors - 1, base_res, res, op);
+ grn_obj_unlink(ctx, base_res);
+ }
+ }
+
+exit :
+ return rc;
+}
+
+static grn_rc
+grn_obj_search_column_index_by_id(grn_ctx *ctx, grn_obj *obj,
+ grn_id tid,
+ grn_obj *res, grn_operator op,
+ grn_search_optarg *optarg)
+{
+ grn_ii_cursor *c;
+
+ grn_obj_search_index_report(ctx, "[id]", obj);
+
+ c = grn_ii_cursor_open(ctx, (grn_ii *)obj, tid,
+ GRN_ID_NIL, GRN_ID_MAX, 1, 0);
+ if (c) {
+ grn_posting *pos;
+ grn_hash *s = (grn_hash *)res;
+ while ((pos = grn_ii_cursor_next(ctx, c))) {
+ /* todo: support orgarg(op)
+ res_add(ctx, s, (grn_rset_posinfo *) pos,
+ get_weight(ctx, s, pos->rid, pos->sid, wvm, optarg), op);
+ */
+ grn_hash_add(ctx, s, pos, s->key_size, NULL, NULL);
+ }
+ grn_ii_cursor_close(ctx, c);
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_obj_search_column_index_by_key(grn_ctx *ctx, grn_obj *obj,
+ grn_obj *query,
+ grn_obj *res, grn_operator op,
+ grn_search_optarg *optarg)
+{
+ grn_rc rc;
+ unsigned int key_type = GRN_ID_NIL;
+ const char *key;
+ unsigned int key_len;
+ grn_obj *table;
+ grn_obj casted_query;
+ grn_bool need_cast = GRN_FALSE;
+
+ table = grn_ctx_at(ctx, obj->header.domain);
+ if (table) {
+ key_type = table->header.domain;
+ need_cast = (query->header.domain != key_type);
+ grn_obj_unlink(ctx, table);
+ }
+ if (need_cast) {
+ GRN_OBJ_INIT(&casted_query, GRN_BULK, 0, key_type);
+ rc = grn_obj_cast(ctx, query, &casted_query, GRN_FALSE);
+ if (rc == GRN_SUCCESS) {
+ key = GRN_BULK_HEAD(&casted_query);
+ key_len = GRN_BULK_VSIZE(&casted_query);
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ key = GRN_BULK_HEAD(query);
+ key_len = GRN_BULK_VSIZE(query);
+ }
+ if (rc == GRN_SUCCESS) {
+ if (grn_logger_pass(ctx, GRN_REPORT_INDEX_LOG_LEVEL)) {
+ const char *tag;
+ if (optarg) {
+ switch (optarg->mode) {
+ case GRN_OP_MATCH :
+ tag = "[key][match]";
+ break;
+ case GRN_OP_EXACT :
+ tag = "[key][exact]";
+ break;
+ case GRN_OP_NEAR :
+ tag = "[key][near]";
+ break;
+ case GRN_OP_NEAR2 :
+ tag = "[key][near2]";
+ break;
+ case GRN_OP_SIMILAR :
+ tag = "[key][similar]";
+ break;
+ case GRN_OP_REGEXP :
+ tag = "[key][regexp]";
+ break;
+ case GRN_OP_FUZZY :
+ tag = "[key][fuzzy]";
+ break;
+ default :
+ tag = "[key][unknown]";
+ break;
+ }
+ } else {
+ tag = "[key][exact]";
+ }
+ grn_obj_search_index_report(ctx, tag, obj);
+ }
+ rc = grn_ii_sel(ctx, (grn_ii *)obj, key, key_len,
+ (grn_hash *)res, op, optarg);
+ }
+ if (need_cast) {
+ GRN_OBJ_FIN(ctx, &casted_query);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_search_column_index(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op,
+ grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+
+ if (DB_OBJ(obj)->range == res->header.domain) {
+ switch (query->header.type) {
+ case GRN_BULK :
+ if (query->header.domain == obj->header.domain &&
+ GRN_BULK_VSIZE(query) == sizeof(grn_id)) {
+ grn_id tid = GRN_RECORD_VALUE(query);
+ rc = grn_obj_search_column_index_by_id(ctx, obj, tid, res, op, optarg);
+ } else {
+ rc = grn_obj_search_column_index_by_key(ctx, obj, query,
+ res, op, optarg);
+ }
+ break;
+ case GRN_QUERY :
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+grn_rc
+grn_obj_search(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op, grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (GRN_ACCESSORP(obj)) {
+ rc = grn_obj_search_accessor(ctx, obj, query, res, op, optarg);
+ } else if (GRN_DB_OBJP(obj)) {
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ {
+ const void *key = GRN_BULK_HEAD(query);
+ uint32_t key_size = GRN_BULK_VSIZE(query);
+ grn_operator mode = optarg ? optarg->mode : GRN_OP_EXACT;
+ if (key && key_size) {
+ if (grn_logger_pass(ctx, GRN_REPORT_INDEX_LOG_LEVEL)) {
+ const char *tag;
+ if (optarg) {
+ switch (optarg->mode) {
+ case GRN_OP_EXACT :
+ tag = "[table][exact]";
+ break;
+ case GRN_OP_LCP :
+ tag = "[table][lcp]";
+ break;
+ case GRN_OP_SUFFIX :
+ tag = "[table][suffix]";
+ break;
+ case GRN_OP_PREFIX :
+ tag = "[table][prefix]";
+ break;
+ case GRN_OP_TERM_EXTRACT :
+ tag = "[table][term-extract]";
+ break;
+ case GRN_OP_FUZZY :
+ tag = "[table][fuzzy]";
+ break;
+ default :
+ tag = "[table][unknown]";
+ break;
+ }
+ } else {
+ tag = "[table][exact]";
+ }
+ grn_obj_search_index_report(ctx, tag, obj);
+ }
+ if (optarg && optarg->mode == GRN_OP_FUZZY) {
+ rc = grn_table_fuzzy_search(ctx, obj, key, key_size,
+ &(optarg->fuzzy), res, op);
+ } else {
+ rc = grn_table_search(ctx, obj, key, key_size, mode, res, op);
+ }
+ }
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_obj_search_column_index(ctx, obj, query, res, op, optarg);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+#define GRN_TABLE_GROUP_BY_KEY 0
+#define GRN_TABLE_GROUP_BY_VALUE 1
+#define GRN_TABLE_GROUP_BY_COLUMN_VALUE 2
+
+#define GRN_TABLE_GROUP_FILTER_PREFIX 0
+#define GRN_TABLE_GROUP_FILTER_SUFFIX (1L<<2)
+
+inline static void
+grn_table_group_add_subrec(grn_ctx *ctx,
+ grn_obj *table,
+ grn_rset_recinfo *ri, double score,
+ grn_rset_posinfo *pi, int dir,
+ grn_obj *calc_target,
+ grn_obj *value_buffer)
+{
+ grn_table_group_flags flags;
+
+ if (!(DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC)) {
+ return;
+ }
+
+ grn_table_add_subrec_inline(table, ri, score, pi, dir);
+
+ flags = DB_OBJ(table)->flags.group;
+
+ if (!(flags & (GRN_TABLE_GROUP_CALC_MAX |
+ GRN_TABLE_GROUP_CALC_MIN |
+ GRN_TABLE_GROUP_CALC_SUM |
+ GRN_TABLE_GROUP_CALC_AVG))) {
+ return;
+ }
+
+ GRN_BULK_REWIND(value_buffer);
+ grn_obj_get_value(ctx, calc_target, pi->rid, value_buffer);
+ grn_rset_recinfo_update_calc_values(ctx, ri, table, value_buffer);
+}
+
+static grn_bool
+accelerated_table_group(grn_ctx *ctx, grn_obj *table, grn_obj *key,
+ grn_table_group_result *result)
+{
+ grn_obj *res = result->table;
+ grn_obj *calc_target = result->calc_target;
+ if (key->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)key;
+ if (a->action == GRN_ACCESSOR_GET_KEY &&
+ a->next && a->next->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ a->next->obj && !a->next->next) {
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, key));
+ int idp = GRN_OBJ_TABLEP(range);
+ grn_table_cursor *tc;
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_bool processed = GRN_TRUE;
+ grn_obj value_buffer;
+ GRN_VOID_INIT(&value_buffer);
+ switch (a->next->obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ {
+ grn_id id;
+ grn_ra *ra = (grn_ra *)a->next->obj;
+ unsigned int element_size = (ra)->header->element_size;
+ grn_ra_cache cache;
+ GRN_RA_CACHE_INIT(ra, &cache);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ void *v, *value;
+ grn_id *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ v = grn_ra_ref_cache(ctx, ra, *id_, &cache);
+ if (idp && *((grn_id *)v) &&
+ grn_table_at(ctx, range, *((grn_id *)v)) == GRN_ID_NIL) {
+ continue;
+ }
+ if ((!idp || *((grn_id *)v)) &&
+ grn_table_add_v_inline(ctx, res, v, element_size, &value, NULL)) {
+ grn_table_group_add_subrec(ctx, res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ calc_target,
+ &value_buffer);
+ }
+ }
+ GRN_RA_CACHE_FIN(ra, &cache);
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ if (idp) { /* todo : support other type */
+ grn_id id;
+ grn_ja *ja = (grn_ja *)a->next->obj;
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_io_win jw;
+ unsigned int len = 0;
+ void *value;
+ grn_id *v, *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ if ((v = grn_ja_ref(ctx, ja, *id_, &jw, &len))) {
+ while (len) {
+ if ((*v != GRN_ID_NIL) &&
+ grn_table_add_v_inline(ctx, res, v, sizeof(grn_id), &value, NULL)) {
+ grn_table_group_add_subrec(ctx, res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ calc_target,
+ &value_buffer);
+ }
+ v++;
+ len -= sizeof(grn_id);
+ }
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+ } else {
+ processed = GRN_FALSE;
+ }
+ break;
+ default :
+ processed = GRN_FALSE;
+ break;
+ }
+ GRN_OBJ_FIN(ctx, &value_buffer);
+ grn_table_cursor_close(ctx, tc);
+ return processed;
+ }
+ }
+ }
+ return GRN_FALSE;
+}
+
+static void
+grn_table_group_single_key_records(grn_ctx *ctx, grn_obj *table,
+ grn_obj *key, grn_table_group_result *result)
+{
+ grn_obj bulk;
+ grn_obj value_buffer;
+ grn_table_cursor *tc;
+ grn_obj *res = result->table;
+ grn_obj *calc_target = result->calc_target;
+
+ GRN_TEXT_INIT(&bulk, 0);
+ GRN_VOID_INIT(&value_buffer);
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, key));
+ int idp = GRN_OBJ_TABLEP(range);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ void *value;
+ grn_rset_recinfo *ri = NULL;
+ GRN_BULK_REWIND(&bulk);
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ grn_obj_get_value(ctx, key, id, &bulk);
+ switch (bulk.header.type) {
+ case GRN_UVECTOR :
+ {
+ grn_bool is_reference;
+ unsigned int element_size;
+ uint8_t *elements;
+ int i, n_elements;
+
+ is_reference = !grn_type_id_is_builtin(ctx, bulk.header.type);
+
+ element_size = grn_uvector_element_size(ctx, &bulk);
+ elements = GRN_BULK_HEAD(&bulk);
+ n_elements = GRN_BULK_VSIZE(&bulk) / element_size;
+ for (i = 0; i < n_elements; i++) {
+ uint8_t *element = elements + (element_size * i);
+
+ if (is_reference) {
+ grn_id id = *((grn_id *)element);
+ if (id == GRN_ID_NIL) {
+ continue;
+ }
+ }
+
+ if (!grn_table_add_v_inline(ctx, res, element, element_size,
+ &value, NULL)) {
+ continue;
+ }
+
+ grn_table_group_add_subrec(ctx, res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ calc_target,
+ &value_buffer);
+ }
+ }
+ break;
+ case GRN_VECTOR :
+ {
+ unsigned int i, n_elements;
+ n_elements = grn_vector_size(ctx, &bulk);
+ for (i = 0; i < n_elements; i++) {
+ const char *content;
+ unsigned int content_length;
+ content_length = grn_vector_get_element(ctx, &bulk, i,
+ &content, NULL, NULL);
+ if (grn_table_add_v_inline(ctx, res,
+ content, content_length,
+ &value, NULL)) {
+ grn_table_group_add_subrec(ctx, res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ calc_target,
+ &value_buffer);
+ }
+ }
+ }
+ break;
+ case GRN_BULK :
+ {
+ if ((!idp || *((grn_id *)GRN_BULK_HEAD(&bulk))) &&
+ grn_table_add_v_inline(ctx, res,
+ GRN_BULK_HEAD(&bulk), GRN_BULK_VSIZE(&bulk),
+ &value, NULL)) {
+ grn_table_group_add_subrec(ctx, res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ calc_target,
+ &value_buffer);
+ }
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid column");
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_OBJ_FIN(ctx, &value_buffer);
+ GRN_OBJ_FIN(ctx, &bulk);
+}
+
+#define GRN_TABLE_GROUP_ALL_NAME "_all"
+#define GRN_TABLE_GROUP_ALL_NAME_LEN (sizeof(GRN_TABLE_GROUP_ALL_NAME) - 1)
+
+static void
+grn_table_group_all_records(grn_ctx *ctx, grn_obj *table,
+ grn_table_group_result *result)
+{
+ grn_obj value_buffer;
+ grn_table_cursor *tc;
+ grn_obj *res = result->table;
+ grn_obj *calc_target = result->calc_target;
+
+ GRN_VOID_INIT(&value_buffer);
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ void *value;
+ if (grn_table_add_v_inline(ctx, res,
+ GRN_TABLE_GROUP_ALL_NAME,
+ GRN_TABLE_GROUP_ALL_NAME_LEN,
+ &value, NULL)) {
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ grn_table_group_add_subrec(ctx, res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ calc_target,
+ &value_buffer);
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_OBJ_FIN(ctx, &value_buffer);
+}
+
+grn_rc
+grn_table_group_with_range_gap(grn_ctx *ctx, grn_obj *table,
+ grn_table_sort_key *group_key,
+ grn_obj *res, uint32_t range_gap)
+{
+ grn_obj *key = group_key->key;
+ if (key->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)key;
+ if (a->action == GRN_ACCESSOR_GET_KEY &&
+ a->next && a->next->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ a->next->obj && !a->next->next) {
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, key));
+ int idp = GRN_OBJ_TABLEP(range);
+ grn_table_cursor *tc;
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL,
+ 0, 0, -1, 0))) {
+ switch (a->next->obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ {
+ grn_id id;
+ grn_ra *ra = (grn_ra *)a->next->obj;
+ unsigned int element_size = (ra)->header->element_size;
+ grn_ra_cache cache;
+ GRN_RA_CACHE_INIT(ra, &cache);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ void *v, *value;
+ grn_id *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ v = grn_ra_ref_cache(ctx, ra, *id_, &cache);
+ if (idp && *((grn_id *)v) &&
+ grn_table_at(ctx, range, *((grn_id *)v)) == GRN_ID_NIL) {
+ continue;
+ }
+ if ((!idp || *((grn_id *)v))) {
+ grn_id id;
+ if (element_size == sizeof(uint32_t)) {
+ uint32_t quantized = (*(uint32_t *)v);
+ quantized -= quantized % range_gap;
+ id = grn_table_add_v_inline(ctx, res, &quantized,
+ element_size, &value, NULL);
+ } else {
+ id = grn_table_add_v_inline(ctx, res, v,
+ element_size, &value, NULL);
+ }
+ if (id) {
+ grn_table_add_subrec_inline(res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ }
+ }
+ GRN_RA_CACHE_FIN(ra, &cache);
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ if (idp) { /* todo : support other type */
+ grn_id id;
+ grn_ja *ja = (grn_ja *)a->next->obj;
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_io_win jw;
+ unsigned int len = 0;
+ void *value;
+ grn_id *v, *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ if ((v = grn_ja_ref(ctx, ja, *id_, &jw, &len))) {
+ while (len) {
+ if ((*v != GRN_ID_NIL) &&
+ grn_table_add_v_inline(ctx, res, v, sizeof(grn_id), &value, NULL)) {
+ grn_table_add_subrec_inline(res, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ v++;
+ len -= sizeof(grn_id);
+ }
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+ } else {
+ return 0;
+ }
+ break;
+ default :
+ return 0;
+ }
+ grn_table_cursor_close(ctx, tc);
+ GRN_TABLE_GROUPED_ON(res);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static inline void
+grn_table_group_multi_keys_add_record(grn_ctx *ctx,
+ grn_table_sort_key *keys,
+ int n_keys,
+ grn_table_group_result *results,
+ int n_results,
+ grn_id id,
+ grn_rset_recinfo *ri,
+ grn_obj *vector,
+ grn_obj *bulk)
+{
+ int r;
+ grn_table_group_result *rp;
+
+ for (r = 0, rp = results; r < n_results; r++, rp++) {
+ void *value;
+ int i;
+ int end;
+
+ if (rp->key_end > n_keys) {
+ end = n_keys;
+ } else {
+ end = rp->key_end + 1;
+ }
+ GRN_BULK_REWIND(bulk);
+ grn_text_benc(ctx, bulk, end - rp->key_begin);
+ for (i = rp->key_begin; i < end; i++) {
+ grn_section section = vector->u.v.sections[i];
+ grn_text_benc(ctx, bulk, section.length);
+ }
+ {
+ grn_obj *body = vector->u.v.body;
+ if (body) {
+ GRN_TEXT_PUT(ctx, bulk, GRN_BULK_HEAD(body), GRN_BULK_VSIZE(body));
+ }
+ }
+ for (i = rp->key_begin; i < end; i++) {
+ grn_section section = vector->u.v.sections[i];
+ grn_text_benc(ctx, bulk, section.weight);
+ grn_text_benc(ctx, bulk, section.domain);
+ }
+
+ // todo : cut off GRN_ID_NIL
+ if (grn_table_add_v_inline(ctx, rp->table,
+ GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk),
+ &value, NULL)) {
+ grn_table_group_add_subrec(ctx, rp->table, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0,
+ rp->calc_target,
+ bulk);
+ }
+ }
+}
+
+static void
+grn_table_group_multi_keys_scalar_records(grn_ctx *ctx,
+ grn_obj *table,
+ grn_table_sort_key *keys,
+ int n_keys,
+ grn_table_group_result *results,
+ int n_results)
+{
+ grn_id id;
+ grn_table_cursor *tc;
+ grn_obj bulk;
+ grn_obj vector;
+
+ tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!tc) {
+ return;
+ }
+
+ GRN_TEXT_INIT(&bulk, 0);
+ GRN_OBJ_INIT(&vector, GRN_VECTOR, 0, GRN_DB_VOID);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ int k;
+ grn_table_sort_key *kp;
+ grn_rset_recinfo *ri = NULL;
+
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+
+ GRN_BULK_REWIND(&vector);
+ for (k = 0, kp = keys; k < n_keys; k++, kp++) {
+ GRN_BULK_REWIND(&bulk);
+ grn_obj_get_value(ctx, kp->key, id, &bulk);
+ grn_vector_add_element(ctx, &vector,
+ GRN_BULK_HEAD(&bulk), GRN_BULK_VSIZE(&bulk),
+ 0,
+ bulk.header.domain);
+ }
+
+ grn_table_group_multi_keys_add_record(ctx, keys, n_keys, results, n_results,
+ id, ri, &vector, &bulk);
+ }
+ GRN_OBJ_FIN(ctx, &vector);
+ GRN_OBJ_FIN(ctx, &bulk);
+ grn_table_cursor_close(ctx, tc);
+}
+
+static inline void
+grn_table_group_multi_keys_vector_record(grn_ctx *ctx,
+ grn_table_sort_key *keys,
+ grn_obj *key_buffers,
+ int nth_key,
+ int n_keys,
+ grn_table_group_result *results,
+ int n_results,
+ grn_id id,
+ grn_rset_recinfo *ri,
+ grn_obj *vector,
+ grn_obj *bulk)
+{
+ int k;
+ grn_table_sort_key *kp;
+
+ for (k = nth_key, kp = &(keys[nth_key]); k < n_keys; k++, kp++) {
+ grn_obj *key_buffer = &(key_buffers[k]);
+ switch (key_buffer->header.type) {
+ case GRN_UVECTOR :
+ {
+ unsigned int n_vector_elements;
+ grn_id domain;
+ grn_id *ids;
+ unsigned int i, n_ids;
+
+ n_vector_elements = grn_vector_size(ctx, vector);
+ domain = key_buffer->header.domain;
+ ids = (grn_id *)GRN_BULK_HEAD(key_buffer);
+ n_ids = GRN_BULK_VSIZE(key_buffer) / sizeof(grn_id);
+ for (i = 0; i < n_ids; i++) {
+ grn_id element_id = ids[i];
+ grn_vector_add_element(ctx, vector,
+ (const char *)(&element_id), sizeof(grn_id),
+ 0,
+ domain);
+ grn_table_group_multi_keys_vector_record(ctx,
+ keys, key_buffers,
+ k + 1, n_keys,
+ results, n_results,
+ id, ri, vector, bulk);
+ while (grn_vector_size(ctx, vector) != n_vector_elements) {
+ const char *content;
+ grn_vector_pop_element(ctx, vector, &content, NULL, NULL);
+ }
+ }
+ return;
+ }
+ break;
+ case GRN_VECTOR :
+ {
+ unsigned int n_vector_elements;
+ unsigned int i, n_key_elements;
+
+ n_vector_elements = grn_vector_size(ctx, vector);
+ n_key_elements = grn_vector_size(ctx, key_buffer);
+ for (i = 0; i < n_key_elements; i++) {
+ const char *content;
+ unsigned int content_length;
+ grn_id domain;
+ content_length = grn_vector_get_element(ctx, key_buffer, i,
+ &content, NULL, &domain);
+ grn_vector_add_element(ctx, vector,
+ content, content_length,
+ 0,
+ domain);
+ grn_table_group_multi_keys_vector_record(ctx,
+ keys, key_buffers,
+ k + 1, n_keys,
+ results, n_results,
+ id, ri, vector, bulk);
+ while (grn_vector_size(ctx, vector) != n_vector_elements) {
+ grn_vector_pop_element(ctx, vector, &content, NULL, NULL);
+ }
+ }
+ return;
+ }
+ break;
+ default :
+ grn_vector_add_element(ctx, vector,
+ GRN_BULK_HEAD(key_buffer),
+ GRN_BULK_VSIZE(key_buffer),
+ 0,
+ key_buffer->header.domain);
+ }
+ }
+
+ if (k == n_keys) {
+ grn_table_group_multi_keys_add_record(ctx,
+ keys, n_keys,
+ results, n_results,
+ id, ri, vector, bulk);
+ }
+}
+
+static void
+grn_table_group_multi_keys_vector_records(grn_ctx *ctx,
+ grn_obj *table,
+ grn_table_sort_key *keys,
+ int n_keys,
+ grn_table_group_result *results,
+ int n_results)
+{
+ grn_id id;
+ grn_table_cursor *tc;
+ grn_obj bulk;
+ grn_obj vector;
+ grn_obj *key_buffers;
+ int k;
+
+ tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!tc) {
+ return;
+ }
+
+ key_buffers = GRN_MALLOCN(grn_obj, n_keys);
+ if (!key_buffers) {
+ grn_table_cursor_close(ctx, tc);
+ return;
+ }
+
+ GRN_TEXT_INIT(&bulk, 0);
+ GRN_OBJ_INIT(&vector, GRN_VECTOR, 0, GRN_DB_VOID);
+ for (k = 0; k < n_keys; k++) {
+ GRN_VOID_INIT(&(key_buffers[k]));
+ }
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_table_sort_key *kp;
+ grn_rset_recinfo *ri = NULL;
+
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+
+ for (k = 0, kp = keys; k < n_keys; k++, kp++) {
+ grn_obj *key_buffer = &(key_buffers[k]);
+ GRN_BULK_REWIND(key_buffer);
+ grn_obj_get_value(ctx, kp->key, id, key_buffer);
+ }
+
+ GRN_BULK_REWIND(&vector);
+ grn_table_group_multi_keys_vector_record(ctx,
+ keys, key_buffers, 0, n_keys,
+ results, n_results,
+ id, ri, &vector, &bulk);
+ }
+ for (k = 0; k < n_keys; k++) {
+ GRN_OBJ_FIN(ctx, &(key_buffers[k]));
+ }
+ GRN_FREE(key_buffers);
+ GRN_OBJ_FIN(ctx, &vector);
+ GRN_OBJ_FIN(ctx, &bulk);
+ grn_table_cursor_close(ctx, tc);
+}
+
+grn_rc
+grn_table_group(grn_ctx *ctx, grn_obj *table,
+ grn_table_sort_key *keys, int n_keys,
+ grn_table_group_result *results, int n_results)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_bool group_by_all_records = GRN_FALSE;
+ if (n_keys == 0 && n_results == 1) {
+ group_by_all_records = GRN_TRUE;
+ } else if (!table || !n_keys || !n_results) {
+ ERR(GRN_INVALID_ARGUMENT, "table or n_keys or n_results is void");
+ return GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_ENTER;
+ {
+ int k, r;
+ grn_table_sort_key *kp;
+ grn_table_group_result *rp;
+ for (k = 0, kp = keys; k < n_keys; k++, kp++) {
+ if ((kp->flags & GRN_TABLE_GROUP_BY_COLUMN_VALUE) && !kp->key) {
+ ERR(GRN_INVALID_ARGUMENT, "column missing in (%d)", k);
+ goto exit;
+ }
+ }
+ for (r = 0, rp = results; r < n_results; r++, rp++) {
+ if (!rp->table) {
+ grn_table_flags flags;
+ grn_obj *key_type = NULL;
+ uint32_t additional_value_size;
+
+ flags = GRN_TABLE_HASH_KEY|
+ GRN_OBJ_WITH_SUBREC|
+ GRN_OBJ_UNIT_USERDEF_DOCUMENT;
+ if (group_by_all_records) {
+ key_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT);
+ } else if (n_keys == 1) {
+ key_type = grn_ctx_at(ctx, grn_obj_get_range(ctx, keys[0].key));
+ } else {
+ flags |= GRN_OBJ_KEY_VAR_SIZE;
+ }
+ additional_value_size = grn_rset_recinfo_calc_values_size(ctx,
+ rp->flags);
+ rp->table = grn_table_create_with_max_n_subrecs(ctx, NULL, 0, NULL,
+ flags,
+ key_type, table,
+ rp->max_n_subrecs,
+ additional_value_size);
+ if (key_type) {
+ grn_obj_unlink(ctx, key_type);
+ }
+ if (!rp->table) {
+ goto exit;
+ }
+ DB_OBJ(rp->table)->flags.group = rp->flags;
+ }
+ }
+ if (group_by_all_records) {
+ grn_table_group_all_records(ctx, table, results);
+ } else if (n_keys == 1 && n_results == 1) {
+ if (!accelerated_table_group(ctx, table, keys->key, results)) {
+ grn_table_group_single_key_records(ctx, table, keys->key, results);
+ }
+ } else {
+ grn_bool have_vector = GRN_FALSE;
+ for (k = 0, kp = keys; k < n_keys; k++, kp++) {
+ grn_id range_id;
+ grn_obj_flags range_flags = 0;
+ grn_obj_get_range_info(ctx, kp->key, &range_id, &range_flags);
+ if (range_flags == GRN_OBJ_VECTOR) {
+ have_vector = GRN_TRUE;
+ break;
+ }
+ }
+ if (have_vector) {
+ grn_table_group_multi_keys_vector_records(ctx, table,
+ keys, n_keys,
+ results, n_results);
+ } else {
+ grn_table_group_multi_keys_scalar_records(ctx, table,
+ keys, n_keys,
+ results, n_results);
+ }
+ }
+ for (r = 0, rp = results; r < n_results; r++, rp++) {
+ GRN_TABLE_GROUPED_ON(rp->table);
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_setoperation(grn_ctx *ctx, grn_obj *table1, grn_obj *table2, grn_obj *res,
+ grn_operator op)
+{
+ void *key = NULL, *value1 = NULL, *value2 = NULL;
+ uint32_t value_size = 0;
+ uint32_t key_size = 0;
+ grn_bool have_subrec;
+
+ GRN_API_ENTER;
+ if (!table1) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][setoperation] table1 is NULL");
+ GRN_API_RETURN(ctx->rc);
+ }
+ if (!table2) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][setoperation] table2 is NULL");
+ GRN_API_RETURN(ctx->rc);
+ }
+ if (!res) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][setoperation] result table is NULL");
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ if (table1 != res) {
+ if (table2 == res) {
+ grn_obj *t = table1;
+ table1 = table2;
+ table2 = t;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][setoperation] table1 or table2 must be result table");
+ GRN_API_RETURN(ctx->rc);
+ }
+ }
+ have_subrec = ((DB_OBJ(table1)->header.flags & GRN_OBJ_WITH_SUBREC) &&
+ (DB_OBJ(table2)->header.flags & GRN_OBJ_WITH_SUBREC));
+ switch (table1->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ value_size = ((grn_hash *)table1)->value_size;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ value_size = ((grn_pat *)table1)->value_size;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ value_size = 0;
+ break;
+ case GRN_TABLE_NO_KEY :
+ value_size = ((grn_array *)table1)->value_size;
+ break;
+ }
+ switch (table2->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ if (value_size < ((grn_hash *)table2)->value_size) {
+ value_size = ((grn_hash *)table2)->value_size;
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ if (value_size < ((grn_pat *)table2)->value_size) {
+ value_size = ((grn_pat *)table2)->value_size;
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ value_size = 0;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (value_size < ((grn_array *)table2)->value_size) {
+ value_size = ((grn_array *)table2)->value_size;
+ }
+ break;
+ }
+ switch (op) {
+ case GRN_OP_OR :
+ if (have_subrec) {
+ int added;
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_add_v_inline(ctx, table1, key, key_size, &value1, &added)) {
+ if (added) {
+ grn_memcpy(value1, value2, value_size);
+ } else {
+ grn_rset_recinfo *ri1 = value1;
+ grn_rset_recinfo *ri2 = value2;
+ grn_table_add_subrec_inline(table1, ri1, ri2->score, NULL, 0);
+ }
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_add_v_inline(ctx, table1, key, key_size, &value1, NULL)) {
+ grn_memcpy(value1, value2, value_size);
+ }
+ });
+ }
+ break;
+ case GRN_OP_AND :
+ if (have_subrec) {
+ GRN_TABLE_EACH(ctx, table1, 0, 0, id, &key, &key_size, &value1, {
+ if (grn_table_get_v(ctx, table2, key, key_size, &value2)) {
+ grn_rset_recinfo *ri1 = value1;
+ grn_rset_recinfo *ri2 = value2;
+ ri1->score += ri2->score;
+ } else {
+ _grn_table_delete_by_id(ctx, table1, id, NULL);
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table1, 0, 0, id, &key, &key_size, &value1, {
+ if (!grn_table_get_v(ctx, table2, key, key_size, &value2)) {
+ _grn_table_delete_by_id(ctx, table1, id, NULL);
+ }
+ });
+ }
+ break;
+ case GRN_OP_AND_NOT :
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ grn_table_delete(ctx, table1, key, key_size);
+ });
+ break;
+ case GRN_OP_ADJUST :
+ if (have_subrec) {
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_get_v(ctx, table1, key, key_size, &value1)) {
+ grn_rset_recinfo *ri1 = value1;
+ grn_rset_recinfo *ri2 = value2;
+ ri1->score += ri2->score;
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_get_v(ctx, table1, key, key_size, &value1)) {
+ grn_memcpy(value1, value2, value_size);
+ }
+ });
+ }
+ break;
+ default :
+ break;
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_table_difference(grn_ctx *ctx, grn_obj *table1, grn_obj *table2,
+ grn_obj *res1, grn_obj *res2)
+{
+ void *key = NULL;
+ uint32_t key_size = 0;
+ if (table1 != res1 || table2 != res2) { return GRN_INVALID_ARGUMENT; }
+ if (grn_table_size(ctx, table1) > grn_table_size(ctx, table2)) {
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, NULL, {
+ grn_id id1;
+ if ((id1 = grn_table_get(ctx, table1, key, key_size))) {
+ _grn_table_delete_by_id(ctx, table1, id1, NULL);
+ _grn_table_delete_by_id(ctx, table2, id, NULL);
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table1, 0, 0, id, &key, &key_size, NULL, {
+ grn_id id2;
+ if ((id2 = grn_table_get(ctx, table2, key, key_size))) {
+ _grn_table_delete_by_id(ctx, table1, id, NULL);
+ _grn_table_delete_by_id(ctx, table2, id2, NULL);
+ }
+ });
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_obj *grn_obj_get_accessor(grn_ctx *ctx, grn_obj *obj,
+ const char *name, unsigned int name_size);
+
+static grn_obj *
+grn_obj_column_(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size)
+{
+ grn_id table_id = DB_OBJ(table)->id;
+ grn_obj *column = NULL;
+
+ if (table_id & GRN_OBJ_TMP_OBJECT) {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ void *value = NULL;
+ grn_snprintf(column_name, GRN_TABLE_MAX_KEY_SIZE, GRN_TABLE_MAX_KEY_SIZE,
+ "%u%c%.*s", table_id, GRN_DB_DELIMITER, name_size, name);
+ grn_pat_get(ctx, ctx->impl->temporary_columns,
+ column_name, strlen(column_name),
+ &value);
+ if (value) {
+ column = *((grn_obj **)value);
+ }
+ } else {
+ char buf[GRN_TABLE_MAX_KEY_SIZE];
+ int len = grn_obj_name(ctx, table, buf, GRN_TABLE_MAX_KEY_SIZE);
+ if (len) {
+ buf[len++] = GRN_DB_DELIMITER;
+ if (len + name_size <= GRN_TABLE_MAX_KEY_SIZE) {
+ grn_memcpy(buf + len, name, name_size);
+ column = grn_ctx_get(ctx, buf, len + name_size);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "name is too long");
+ }
+ }
+ }
+
+ return column;
+}
+
+grn_obj *
+grn_obj_column(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size)
+{
+ grn_obj *column = NULL;
+ GRN_API_ENTER;
+ if (GRN_OBJ_TABLEP(table)) {
+ if (grn_db_check_name(ctx, name, name_size) ||
+ !(column = grn_obj_column_(ctx, table, name, name_size))) {
+ column = grn_obj_get_accessor(ctx, table, name, name_size);
+ }
+ } else if (GRN_ACCESSORP(table)) {
+ column = grn_obj_get_accessor(ctx, table, name, name_size);
+ }
+ GRN_API_RETURN(column);
+}
+
+int
+grn_table_columns(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size,
+ grn_obj *res)
+{
+ int n = 0;
+ grn_id id;
+
+ GRN_API_ENTER;
+
+ if (!GRN_OBJ_TABLEP(table)) {
+ GRN_API_RETURN(n);
+ }
+
+ id = DB_OBJ(table)->id;
+
+ if (id == GRN_ID_NIL) {
+ GRN_API_RETURN(n);
+ }
+
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ char search_key[GRN_TABLE_MAX_KEY_SIZE];
+ grn_pat_cursor *cursor;
+ grn_snprintf(search_key, GRN_TABLE_MAX_KEY_SIZE, GRN_TABLE_MAX_KEY_SIZE,
+ "%u%c%.*s", id, GRN_DB_DELIMITER, name_size, name);
+ cursor = grn_pat_cursor_open(ctx, ctx->impl->temporary_columns,
+ search_key, strlen(search_key),
+ NULL, 0,
+ 0, -1, GRN_CURSOR_PREFIX);
+ if (cursor) {
+ grn_id column_id;
+ while ((column_id = grn_pat_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ column_id |= GRN_OBJ_TMP_OBJECT | GRN_OBJ_TMP_COLUMN;
+ grn_hash_add(ctx, (grn_hash *)res,
+ &column_id, sizeof(grn_id),
+ NULL, NULL);
+ n++;
+ }
+ grn_pat_cursor_close(ctx, cursor);
+ }
+ } else {
+ grn_db *s = (grn_db *)DB_OBJ(table)->db;
+ if (s->keys) {
+ grn_obj bulk;
+ GRN_TEXT_INIT(&bulk, 0);
+ grn_table_get_key2(ctx, s->keys, id, &bulk);
+ GRN_TEXT_PUTC(ctx, &bulk, GRN_DB_DELIMITER);
+ grn_bulk_write(ctx, &bulk, name, name_size);
+ grn_table_search(ctx, s->keys, GRN_BULK_HEAD(&bulk), GRN_BULK_VSIZE(&bulk),
+ GRN_OP_PREFIX, res, GRN_OP_OR);
+ grn_obj_close(ctx, &bulk);
+ n = grn_table_size(ctx, res);
+ }
+ }
+
+ GRN_API_RETURN(n);
+}
+
+const char *
+_grn_table_key(grn_ctx *ctx, grn_obj *table, grn_id id, uint32_t *key_size)
+{
+ GRN_ASSERT(table);
+ if (table->header.type == GRN_DB) { table = ((grn_db *)table)->keys; }
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ return _grn_hash_key(ctx, (grn_hash *)table, id, key_size);
+ case GRN_TABLE_PAT_KEY :
+ return _grn_pat_key(ctx, (grn_pat *)table, id, key_size);
+ case GRN_TABLE_DAT_KEY :
+ return _grn_dat_key(ctx, (grn_dat *)table, id, key_size);
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *a = (grn_array *)table;
+ const char *v;
+ if (a->obj.header.domain && a->value_size &&
+ (v = _grn_array_get_value(ctx, a, id))) {
+ *key_size = a->value_size;
+ return v;
+ } else {
+ *key_size = 0;
+ }
+ }
+ break;
+ }
+ return NULL;
+}
+
+/* column */
+
+grn_obj *
+grn_column_create(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ const char *path, grn_column_flags flags, grn_obj *type)
+{
+ grn_db *s;
+ uint32_t value_size;
+ grn_obj *db= NULL, *res = NULL;
+ grn_id id = GRN_ID_NIL;
+ grn_id range = GRN_ID_NIL;
+ grn_id domain = GRN_ID_NIL;
+ grn_bool is_persistent_table;
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int fullname_size;
+ char buffer[PATH_MAX];
+
+ GRN_API_ENTER;
+ if (!table) {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] table is missing");
+ goto exit;
+ }
+ if (!type) {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] type is missing");
+ goto exit;
+ }
+ if (!name || !name_size) {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] name is missing");
+ goto exit;
+ }
+ db = DB_OBJ(table)->db;
+ s = (grn_db *)db;
+ if (!GRN_DB_P(s)) {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] invalid db assigned: <%.*s>.<%.*s>",
+ table_name_len, table_name, name_size, name);
+ goto exit;
+ }
+
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[column][create]", name, name_size);
+ goto exit;
+ }
+
+ domain = DB_OBJ(table)->id;
+ is_persistent_table = !(domain & GRN_OBJ_TMP_OBJECT);
+
+ if (!domain) {
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
+ "[column][create] [todo] table-less column isn't supported yet");
+ goto exit;
+ }
+
+ {
+ int table_name_len;
+ if (is_persistent_table) {
+ table_name_len = grn_table_get_key(ctx, s->keys, domain,
+ fullname, GRN_TABLE_MAX_KEY_SIZE);
+ } else {
+ grn_snprintf(fullname, GRN_TABLE_MAX_KEY_SIZE, GRN_TABLE_MAX_KEY_SIZE,
+ "%u", domain);
+ table_name_len = strlen(fullname);
+ }
+ if (name_size + 1 + table_name_len > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] too long column name: required name_size(%d) < %d"
+ ": <%.*s>.<%.*s>",
+ name_size, GRN_TABLE_MAX_KEY_SIZE - 1 - table_name_len,
+ table_name_len, fullname, name_size, name);
+ goto exit;
+ }
+ fullname[table_name_len] = GRN_DB_DELIMITER;
+ grn_memcpy(fullname + table_name_len + 1, name, name_size);
+ fullname_size = table_name_len + 1 + name_size;
+ }
+
+ range = DB_OBJ(type)->id;
+ switch (type->header.type) {
+ case GRN_TYPE :
+ {
+ grn_db_obj *t = (grn_db_obj *)type;
+ flags |= t->header.flags & ~GRN_OBJ_KEY_MASK;
+ value_size = GRN_TYPE_SIZE(t);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ value_size = sizeof(grn_id);
+ break;
+ default :
+ /*
+ if (type == grn_type_any) {
+ value_size = sizeof(grn_id) + sizeof(grn_id);
+ }
+ */
+ value_size = sizeof(grn_id);
+ }
+
+ if (is_persistent_table) {
+ id = grn_obj_register(ctx, db, fullname, fullname_size);
+ if (ERRP(ctx, GRN_ERROR)) { goto exit; }
+
+ {
+ uint32_t table_name_size = 0;
+ const char *table_name;
+ table_name = _grn_table_key(ctx, ctx->impl->db, domain, &table_name_size);
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "DDL:%u:column_create %.*s %.*s",
+ id,
+ table_name_size, table_name,
+ name_size, name);
+ }
+ } else {
+ int added;
+ id = grn_pat_add(ctx, ctx->impl->temporary_columns,
+ fullname, fullname_size, NULL,
+ &added);
+ if (!id) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[column][create][temporary] "
+ "failed to register temporary column name: <%.*s>",
+ fullname_size, fullname);
+ goto exit;
+ } else if (!added) {
+ id = GRN_ID_NIL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[column][create][temporary] already used name was assigned: <%.*s>",
+ fullname_size, fullname);
+ goto exit;
+ }
+ id |= GRN_OBJ_TMP_OBJECT | GRN_OBJ_TMP_COLUMN;
+ }
+
+ if (is_persistent_table && flags & GRN_OBJ_PERSISTENT) {
+ if (!path) {
+ if (GRN_DB_PERSISTENT_P(db)) {
+ grn_db_generate_pathname(ctx, db, id, buffer);
+ path = buffer;
+ } else {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] path not assigned for persistent column"
+ ": <%.*s>.<%.*s>",
+ table_name_len, table_name, name_size, name);
+ goto exit;
+ }
+ } else {
+ flags |= GRN_OBJ_CUSTOM_NAME;
+ }
+ } else {
+ if (path) {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] path assigned for temporary column"
+ ": <%.*s>.<%.*s>",
+ table_name_len, table_name, name_size, name);
+ goto exit;
+ }
+ }
+ switch (flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR :
+ if ((flags & GRN_OBJ_KEY_VAR_SIZE) || value_size > sizeof(int64_t)) {
+ res = (grn_obj *)grn_ja_create(ctx, path, value_size, flags);
+ } else {
+ res = (grn_obj *)grn_ra_create(ctx, path, value_size);
+ }
+ break;
+ case GRN_OBJ_COLUMN_VECTOR :
+ res = (grn_obj *)grn_ja_create(ctx, path, value_size * 30/*todo*/, flags);
+ //todo : zlib support
+ break;
+ case GRN_OBJ_COLUMN_INDEX :
+ res = (grn_obj *)grn_ii_create(ctx, path, table, flags); //todo : ii layout support
+ break;
+ }
+ if (res) {
+ DB_OBJ(res)->header.domain = domain;
+ DB_OBJ(res)->header.impl_flags = 0;
+ DB_OBJ(res)->range = range;
+ DB_OBJ(res)->header.flags = flags;
+ res->header.flags = flags;
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ _grn_obj_remove(ctx, res, GRN_FALSE);
+ res = NULL;
+ } else {
+ grn_obj_touch(ctx, res, NULL);
+ }
+ }
+exit :
+ if (!res && id) { grn_obj_delete_by_id(ctx, db, id, GRN_TRUE); }
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_column_open(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ const char *path, grn_obj *type)
+{
+ grn_id domain;
+ grn_obj *res = NULL;
+ grn_db *s;
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ GRN_API_ENTER;
+ if (!table || !type || !name || !name_size) {
+ ERR(GRN_INVALID_ARGUMENT, "missing type or name");
+ goto exit;
+ }
+ s = (grn_db *)DB_OBJ(table)->db;
+ if (!GRN_DB_P(s)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ goto exit;
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[column][open]", name, name_size);
+ goto exit;
+ }
+ if ((domain = DB_OBJ(table)->id)) {
+ int len = grn_table_get_key(ctx, s->keys, domain, fullname, GRN_TABLE_MAX_KEY_SIZE);
+ if (name_size + 1 + len > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT, "too long column name");
+ goto exit;
+ }
+ fullname[len] = GRN_DB_DELIMITER;
+ grn_memcpy(fullname + len + 1, name, name_size);
+ name_size += len + 1;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "todo : not supported yet");
+ goto exit;
+ }
+ res = grn_ctx_get(ctx, fullname, name_size);
+ if (res) {
+ const char *path2 = grn_obj_path(ctx, res);
+ if (path && (!path2 || strcmp(path, path2))) { goto exit; }
+ } else if (path) {
+ uint32_t dbtype = grn_io_detect_type(ctx, path);
+ if (!dbtype) { goto exit; }
+ switch (dbtype) {
+ case GRN_COLUMN_VAR_SIZE :
+ res = (grn_obj *)grn_ja_open(ctx, path);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ res = (grn_obj *)grn_ra_open(ctx, path);
+ break;
+ case GRN_COLUMN_INDEX :
+ res = (grn_obj *)grn_ii_open(ctx, path, table);
+ break;
+ }
+ if (res) {
+ grn_id id = grn_obj_register(ctx, (grn_obj *)s, fullname, name_size);
+ DB_OBJ(res)->header.domain = domain;
+ DB_OBJ(res)->range = DB_OBJ(type)->id;
+ res->header.flags |= GRN_OBJ_CUSTOM_NAME;
+ grn_db_obj_init(ctx, (grn_obj *)s, id, DB_OBJ(res));
+ }
+ }
+exit :
+ GRN_API_RETURN(res);
+}
+
+/*
+typedef struct {
+ grn_id id;
+ int flags;
+} grn_column_set_value_arg;
+
+static grn_rc
+default_column_set_value(grn_ctx *ctx, grn_proc_ctx *pctx, grn_obj *in, grn_obj *out)
+{
+ grn_user_data *data = grn_proc_ctx_get_local_data(pctx);
+ if (data) {
+ grn_column_set_value_arg *arg = data->ptr;
+ unsigned int value_size = in->u.p.size; //todo
+ if (!pctx->obj) { return GRN_ID_NIL; }
+ switch (pctx->obj->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ return grn_ja_put(ctx, (grn_ja *)pctx->obj, arg->id,
+ in->u.p.ptr, value_size, 0, NULL); // todo type->flag
+ case GRN_COLUMN_FIX_SIZE :
+ if (((grn_ra *)pctx->obj)->header->element_size < value_size) {
+ ERR(GRN_INVALID_ARGUMENT, "too long value (%d)", value_size);
+ return GRN_INVALID_ARGUMENT;
+ } else {
+ void *v = grn_ra_ref(ctx, (grn_ra *)pctx->obj, arg->id);
+ if (!v) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "ra get failed");
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ grn_memcpy(v, in->u.p.ptr, value_size);
+ grn_ra_unref(ctx, (grn_ra *)pctx->obj, arg->id);
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ // todo : how??
+ break;
+ }
+ return GRN_SUCCESS;
+ } else {
+ ERR(GRN_OBJECT_CORRUPT, "grn_proc_ctx_get_local_data failed");
+ return ctx->rc;
+ }
+}
+*/
+
+/**** grn_vector ****/
+
+//#define VECTOR(obj) ((grn_vector *)obj)
+
+/*
+#define INITIAL_VECTOR_SIZE 256
+
+int
+grn_vector_delimit(grn_ctx *ctx, grn_obj *vector)
+{
+ grn_vector *v = VECTOR(vector);
+ uint32_t *offsets;
+ if (!(v->n_entries & (INITIAL_VECTOR_SIZE - 1))) {
+ offsets = GRN_REALLOC(v->offsets, sizeof(uint32_t) *
+ (v->n_entries + INITIAL_VECTOR_SIZE));
+ if (!offsets) { return -1; }
+ v->offsets = offsets;
+ }
+ v->offsets[v->n_entries] = GRN_BULK_VSIZE(vector);
+ return ++(v->n_entries);
+}
+*/
+
+static unsigned int
+grn_uvector_element_size_internal(grn_ctx *ctx, grn_obj *uvector)
+{
+ unsigned int element_size;
+
+ if (IS_WEIGHT_UVECTOR(uvector)) {
+ element_size = sizeof(weight_uvector_entry);
+ } else {
+ switch (uvector->header.domain) {
+ case GRN_DB_BOOL :
+ element_size = sizeof(grn_bool);
+ break;
+ case GRN_DB_INT8 :
+ element_size = sizeof(int8_t);
+ break;
+ case GRN_DB_UINT8 :
+ element_size = sizeof(uint8_t);
+ break;
+ case GRN_DB_INT16 :
+ element_size = sizeof(int16_t);
+ break;
+ case GRN_DB_UINT16 :
+ element_size = sizeof(uint16_t);
+ break;
+ case GRN_DB_INT32 :
+ element_size = sizeof(int32_t);
+ break;
+ case GRN_DB_UINT32 :
+ element_size = sizeof(uint32_t);
+ break;
+ case GRN_DB_INT64 :
+ element_size = sizeof(int64_t);
+ break;
+ case GRN_DB_UINT64 :
+ element_size = sizeof(uint64_t);
+ break;
+ case GRN_DB_FLOAT :
+ element_size = sizeof(double);
+ break;
+ case GRN_DB_TIME :
+ element_size = sizeof(int64_t);
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ element_size = sizeof(grn_geo_point);
+ break;
+ default :
+ element_size = sizeof(grn_id);
+ break;
+ }
+ }
+
+ return element_size;
+}
+
+static unsigned int
+grn_uvector_size_internal(grn_ctx *ctx, grn_obj *uvector)
+{
+ unsigned int element_size;
+
+ element_size = grn_uvector_element_size_internal(ctx, uvector);
+ return GRN_BULK_VSIZE(uvector) / element_size;
+}
+
+unsigned int
+grn_vector_size(grn_ctx *ctx, grn_obj *vector)
+{
+ unsigned int size;
+ if (!vector) {
+ ERR(GRN_INVALID_ARGUMENT, "vector is null");
+ return 0;
+ }
+ GRN_API_ENTER;
+ switch (vector->header.type) {
+ case GRN_BULK :
+ size = GRN_BULK_VSIZE(vector);
+ break;
+ case GRN_UVECTOR :
+ size = grn_uvector_size_internal(ctx, vector);
+ break;
+ case GRN_VECTOR :
+ size = vector->u.v.n_sections;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "not vector");
+ size = 0;
+ break;
+ }
+ GRN_API_RETURN(size);
+}
+
+static grn_obj *
+grn_vector_body(grn_ctx *ctx, grn_obj *v)
+{
+ if (!v) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid argument");
+ return NULL;
+ }
+ switch (v->header.type) {
+ case GRN_VECTOR :
+ if (!v->u.v.body) {
+ v->u.v.body = grn_obj_open(ctx, GRN_BULK, 0, v->header.domain);
+ }
+ return v->u.v.body;
+ case GRN_BULK :
+ case GRN_UVECTOR :
+ return v;
+ default :
+ return NULL;
+ }
+}
+
+unsigned int
+grn_vector_get_element(grn_ctx *ctx, grn_obj *vector,
+ unsigned int offset, const char **str,
+ unsigned int *weight, grn_id *domain)
+{
+ unsigned int length = 0;
+ GRN_API_ENTER;
+ if (!vector || vector->header.type != GRN_VECTOR) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid vector");
+ goto exit;
+ }
+ if ((unsigned int) vector->u.v.n_sections <= offset) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+ {
+ grn_section *vp = &vector->u.v.sections[offset];
+ grn_obj *body = grn_vector_body(ctx, vector);
+ *str = GRN_BULK_HEAD(body) + vp->offset;
+ if (weight) { *weight = vp->weight; }
+ if (domain) { *domain = vp->domain; }
+ length = vp->length;
+ }
+exit :
+ GRN_API_RETURN(length);
+}
+
+unsigned int
+grn_vector_pop_element(grn_ctx *ctx, grn_obj *vector,
+ const char **str, unsigned int *weight, grn_id *domain)
+{
+ unsigned int offset, length = 0;
+ GRN_API_ENTER;
+ if (!vector || vector->header.type != GRN_VECTOR) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid vector");
+ goto exit;
+ }
+ if (!vector->u.v.n_sections) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+ offset = --vector->u.v.n_sections;
+ {
+ grn_section *vp = &vector->u.v.sections[offset];
+ grn_obj *body = grn_vector_body(ctx, vector);
+ *str = GRN_BULK_HEAD(body) + vp->offset;
+ if (weight) { *weight = vp->weight; }
+ if (domain) { *domain = vp->domain; }
+ length = vp->length;
+ grn_bulk_truncate(ctx, body, vp->offset);
+ }
+exit :
+ GRN_API_RETURN(length);
+}
+
+#define W_SECTIONS_UNIT 8
+#define S_SECTIONS_UNIT (1 << W_SECTIONS_UNIT)
+#define M_SECTIONS_UNIT (S_SECTIONS_UNIT - 1)
+
+grn_rc
+grn_vector_delimit(grn_ctx *ctx, grn_obj *v, unsigned int weight, grn_id domain)
+{
+ if (v->header.type != GRN_VECTOR) { return GRN_INVALID_ARGUMENT; }
+ if (!(v->u.v.n_sections & M_SECTIONS_UNIT)) {
+ grn_section *vp = GRN_REALLOC(v->u.v.sections, sizeof(grn_section) *
+ (v->u.v.n_sections + S_SECTIONS_UNIT));
+ if (!vp) { return GRN_NO_MEMORY_AVAILABLE; }
+ v->u.v.sections = vp;
+ }
+ {
+ grn_obj *body = grn_vector_body(ctx, v);
+ grn_section *vp = &v->u.v.sections[v->u.v.n_sections];
+ vp->offset = v->u.v.n_sections ? vp[-1].offset + vp[-1].length : 0;
+ vp->length = GRN_BULK_VSIZE(body) - vp->offset;
+ vp->weight = weight;
+ vp->domain = domain;
+ }
+ v->u.v.n_sections++;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_vector_decode(grn_ctx *ctx, grn_obj *v, const char *data, uint32_t data_size)
+{
+ uint8_t *p = (uint8_t *)data;
+ uint8_t *pe = p + data_size;
+ uint32_t n, n0 = v->u.v.n_sections;
+ GRN_B_DEC(n, p);
+ if (((n0 + M_SECTIONS_UNIT) >> W_SECTIONS_UNIT) !=
+ ((n0 + n + M_SECTIONS_UNIT) >> W_SECTIONS_UNIT)) {
+ grn_section *vp = GRN_REALLOC(v->u.v.sections, sizeof(grn_section) *
+ ((n0 + n + M_SECTIONS_UNIT) & ~M_SECTIONS_UNIT));
+ if (!vp) { return GRN_NO_MEMORY_AVAILABLE; }
+ v->u.v.sections = vp;
+ }
+ {
+ grn_section *vp;
+ grn_obj *body = grn_vector_body(ctx, v);
+ uint32_t offset = GRN_BULK_VSIZE(body);
+ uint32_t o = 0, l, i;
+ for (i = n, vp = v->u.v.sections + n0; i; i--, vp++) {
+ if (pe <= p) { return GRN_INVALID_ARGUMENT; }
+ GRN_B_DEC(l, p);
+ vp->length = l;
+ vp->offset = offset + o;
+ vp->weight = 0;
+ vp->domain = 0;
+ o += l;
+ }
+ if (pe < p + o) { return GRN_INVALID_ARGUMENT; }
+ grn_bulk_write(ctx, body, (char *)p, o);
+ p += o;
+ if (p < pe) {
+ for (i = n, vp = v->u.v.sections + n0; i; i--, vp++) {
+ if (pe <= p) { return GRN_INVALID_ARGUMENT; }
+ GRN_B_DEC(vp->weight, p);
+ GRN_B_DEC(vp->domain, p);
+ }
+ }
+ }
+ v->u.v.n_sections += n;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_vector_add_element(grn_ctx *ctx, grn_obj *vector,
+ const char *str, unsigned int str_len,
+ unsigned int weight, grn_id domain)
+{
+ grn_obj *body;
+ GRN_API_ENTER;
+ if (!vector) {
+ ERR(GRN_INVALID_ARGUMENT, "vector is null");
+ goto exit;
+ }
+ if ((body = grn_vector_body(ctx, vector))) {
+ grn_bulk_write(ctx, body, str, str_len);
+ grn_vector_delimit(ctx, vector, weight, domain);
+ }
+exit :
+ GRN_API_RETURN(ctx->rc);
+}
+
+/*
+grn_obj *
+grn_sections_to_vector(grn_ctx *ctx, grn_obj *sections)
+{
+ grn_obj *vector = grn_vector_open(ctx, 0);
+ if (vector) {
+ grn_section *vp;
+ int i;
+ for (i = sections->u.v.n_sections, vp = sections->u.v.sections; i; i--, vp++) {
+ grn_text_benc(ctx, vector, vp->weight);
+ grn_text_benc(ctx, vector, vp->domain);
+ grn_bulk_write(ctx, vector, vp->str, vp->str_len);
+ grn_vector_delimit(ctx, vector);
+ }
+ }
+ return vector;
+}
+
+grn_obj *
+grn_vector_to_sections(grn_ctx *ctx, grn_obj *vector, grn_obj *sections)
+{
+ if (!sections) {
+ sections = grn_obj_open(ctx, GRN_VECTOR, GRN_OBJ_DO_SHALLOW_COPY, 0);
+ }
+ if (sections) {
+ int i, n = grn_vector_size(ctx, vector);
+ sections->u.v.src = vector;
+ for (i = 0; i < n; i++) {
+ unsigned int size;
+ const uint8_t *pe, *p = (uint8_t *)grn_vector_fetch(ctx, vector, i, &size);
+ if (p) {
+ grn_id domain;
+ unsigned int weight;
+ pe = p + size;
+ if (p < pe) {
+ GRN_B_DEC(weight, p);
+ if (p < pe) {
+ GRN_B_DEC(domain, p);
+ if (p <= pe) {
+ grn_vector_add(ctx, sections, (char *)p, pe - p, weight, domain);
+ }
+ }
+ }
+ }
+ }
+ }
+ return sections;
+}
+*/
+
+/**** uvector ****/
+
+unsigned int
+grn_uvector_size(grn_ctx *ctx, grn_obj *uvector)
+{
+ unsigned int size;
+
+ if (!uvector) {
+ ERR(GRN_INVALID_ARGUMENT, "uvector must not be NULL");
+ return 0;
+ }
+
+ if (uvector->header.type != GRN_UVECTOR) {
+ grn_obj type_name;
+ GRN_TEXT_INIT(&type_name, 0);
+ grn_inspect_type(ctx, &type_name, uvector->header.type);
+ ERR(GRN_INVALID_ARGUMENT, "must be GRN_UVECTOR: %.*s",
+ (int)GRN_TEXT_LEN(&type_name), GRN_TEXT_VALUE(&type_name));
+ GRN_OBJ_FIN(ctx, &type_name);
+ return 0;
+ }
+
+ GRN_API_ENTER;
+ size = grn_uvector_size_internal(ctx, uvector);
+ GRN_API_RETURN(size);
+}
+
+unsigned int
+grn_uvector_element_size(grn_ctx *ctx, grn_obj *uvector)
+{
+ unsigned int element_size;
+
+ if (!uvector) {
+ ERR(GRN_INVALID_ARGUMENT, "uvector must not be NULL");
+ return 0;
+ }
+
+ if (uvector->header.type != GRN_UVECTOR) {
+ grn_obj type_name;
+ GRN_TEXT_INIT(&type_name, 0);
+ grn_inspect_type(ctx, &type_name, uvector->header.type);
+ ERR(GRN_INVALID_ARGUMENT, "must be GRN_UVECTOR: %.*s",
+ (int)GRN_TEXT_LEN(&type_name), GRN_TEXT_VALUE(&type_name));
+ GRN_OBJ_FIN(ctx, &type_name);
+ return 0;
+ }
+
+ GRN_API_ENTER;
+ element_size = grn_uvector_element_size_internal(ctx, uvector);
+ GRN_API_RETURN(element_size);
+}
+
+grn_rc
+grn_uvector_add_element(grn_ctx *ctx, grn_obj *uvector,
+ grn_id id, unsigned int weight)
+{
+ GRN_API_ENTER;
+ if (!uvector) {
+ ERR(GRN_INVALID_ARGUMENT, "uvector is null");
+ goto exit;
+ }
+ if (IS_WEIGHT_UVECTOR(uvector)) {
+ weight_uvector_entry entry;
+ entry.id = id;
+ entry.weight = weight;
+ grn_bulk_write(ctx, uvector,
+ (const char *)&entry, sizeof(weight_uvector_entry));
+ } else {
+ grn_bulk_write(ctx, uvector,
+ (const char *)&id, sizeof(grn_id));
+ }
+exit :
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_id
+grn_uvector_get_element(grn_ctx *ctx, grn_obj *uvector,
+ unsigned int offset, unsigned int *weight)
+{
+ grn_id id = GRN_ID_NIL;
+
+ GRN_API_ENTER;
+ if (!uvector || uvector->header.type != GRN_UVECTOR) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid uvector");
+ goto exit;
+ }
+
+ if (IS_WEIGHT_UVECTOR(uvector)) {
+ const weight_uvector_entry *entry;
+ const weight_uvector_entry *entries_start;
+ const weight_uvector_entry *entries_end;
+
+ entries_start = (const weight_uvector_entry *)GRN_BULK_HEAD(uvector);
+ entries_end = (const weight_uvector_entry *)GRN_BULK_CURR(uvector);
+ if (offset > entries_end - entries_start) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+
+ entry = entries_start + offset;
+ id = entry->id;
+ if (weight) { *weight = entry->weight; }
+ } else {
+ const grn_id *ids_start;
+ const grn_id *ids_end;
+
+ ids_start = (const grn_id *)GRN_BULK_HEAD(uvector);
+ ids_end = (const grn_id *)GRN_BULK_CURR(uvector);
+ if (offset > ids_end - ids_start) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+ id = ids_start[offset];
+ if (weight) { *weight = 0; }
+ }
+exit :
+ GRN_API_RETURN(id);
+}
+
+/**** accessor ****/
+
+static grn_accessor *
+accessor_new(grn_ctx *ctx)
+{
+ grn_accessor *res = GRN_MALLOCN(grn_accessor, 1);
+ if (res) {
+ res->header.type = GRN_ACCESSOR;
+ res->header.impl_flags = GRN_OBJ_ALLOCATED;
+ res->header.flags = 0;
+ res->header.domain = GRN_ID_NIL;
+ res->range = GRN_ID_NIL;
+ res->action = GRN_ACCESSOR_VOID;
+ res->offset = 0;
+ res->obj = NULL;
+ res->next = NULL;
+ }
+ return res;
+}
+
+inline static grn_bool
+grn_obj_get_accessor_rset_value(grn_ctx *ctx, grn_obj *obj,
+ grn_accessor **res, uint8_t action)
+{
+ grn_bool succeeded = GRN_FALSE;
+ grn_accessor **rp;
+
+ for (rp = res; GRN_TRUE; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+
+#define CHECK_GROUP_CALC_FLAG(flag) do { \
+ if (GRN_TABLE_IS_GROUPED(obj)) { \
+ grn_table_group_flags flags; \
+ flags = DB_OBJ(obj)->flags.group; \
+ if (flags & flag) { \
+ succeeded = GRN_TRUE; \
+ (*rp)->action = action; \
+ goto exit; \
+ } \
+ } \
+ } while(GRN_FALSE)
+ switch (action) {
+ case GRN_ACCESSOR_GET_SCORE :
+ if (DB_OBJ(obj)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ (*rp)->action = action;
+ succeeded = GRN_TRUE;
+ goto exit;
+ }
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ CHECK_GROUP_CALC_FLAG(GRN_TABLE_GROUP_CALC_MAX);
+ break;
+ case GRN_ACCESSOR_GET_MIN :
+ CHECK_GROUP_CALC_FLAG(GRN_TABLE_GROUP_CALC_MIN);
+ break;
+ case GRN_ACCESSOR_GET_SUM :
+ CHECK_GROUP_CALC_FLAG(GRN_TABLE_GROUP_CALC_SUM);
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ CHECK_GROUP_CALC_FLAG(GRN_TABLE_GROUP_CALC_AVG);
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ if (GRN_TABLE_IS_GROUPED(obj)) {
+ (*rp)->action = action;
+ succeeded = GRN_TRUE;
+ goto exit;
+ }
+ break;
+ }
+#undef CHECK_GROUP_CALC_FLAG
+
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (!obj->header.domain) {
+ goto exit;
+ }
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ break;
+ default :
+ /* lookup failed */
+ goto exit;
+ }
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ goto exit;
+ }
+ }
+
+exit :
+ if (!succeeded) {
+ grn_obj_close(ctx, (grn_obj *)*res);
+ *res = NULL;
+ }
+
+ return succeeded;
+}
+
+static grn_obj *
+grn_obj_get_accessor(grn_ctx *ctx, grn_obj *obj, const char *name, unsigned int name_size)
+{
+ grn_accessor *res = NULL, **rp = NULL, **rp0 = NULL;
+ grn_bool is_chained = GRN_FALSE;
+ if (!obj) { return NULL; }
+ GRN_API_ENTER;
+ if (obj->header.type == GRN_ACCESSOR) {
+ is_chained = GRN_TRUE;
+ for (rp0 = (grn_accessor **)&obj; *rp0; rp0 = &(*rp0)->next) {
+ res = *rp0;
+ }
+ switch (res->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ obj = grn_ctx_at(ctx, res->obj->header.domain);
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ case GRN_ACCESSOR_GET_SCORE :
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ case GRN_ACCESSOR_GET_MAX :
+ case GRN_ACCESSOR_GET_MIN :
+ case GRN_ACCESSOR_GET_SUM :
+ case GRN_ACCESSOR_GET_AVG :
+ obj = grn_ctx_at(ctx, DB_OBJ(res->obj)->range);
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ obj = grn_ctx_at(ctx, DB_OBJ(res->obj)->range);
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ }
+ if (!obj) {
+ res = NULL;
+ goto exit;
+ }
+ {
+ size_t len;
+ const char *sp, *se = name + name_size;
+ if (*name == GRN_DB_DELIMITER) { name++; }
+ for (sp = name; (len = grn_charlen(ctx, sp, se)); sp += len) {
+ if (*sp == GRN_DB_DELIMITER) { break; }
+ }
+ if (!(len = sp - name)) { goto exit; }
+ if (*name == GRN_DB_PSEUDO_COLUMN_PREFIX) { /* pseudo column */
+ int done = 0;
+ if (len < 2) { goto exit; }
+ switch (name[1]) {
+ case 'k' : /* key */
+ if (len != GRN_COLUMN_NAME_KEY_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (GRN_TABLE_IS_MULTI_KEYS_GROUPED(obj)) {
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ done++;
+ break;
+ }
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ rp = &(*rp)->next;
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ (*rp)->action = GRN_ACCESSOR_GET_DB_OBJ;
+ done++;
+ break;
+ case GRN_TYPE :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ done++;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (obj->header.domain) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ break;
+ }
+ /* fallthru */
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ break;
+ case 'i' : /* id */
+ if (len != GRN_COLUMN_NAME_ID_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_ID, GRN_COLUMN_NAME_ID_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (!obj->header.domain) {
+ (*rp)->action = GRN_ACCESSOR_GET_ID;
+ done++;
+ } else {
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ case GRN_TYPE :
+ (*rp)->action = GRN_ACCESSOR_GET_ID;
+ done++;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_NO_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ break;
+ case 'v' : /* value */
+ if (len != GRN_COLUMN_NAME_VALUE_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_VALUE, GRN_COLUMN_NAME_VALUE_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (!obj->header.domain) {
+ if (DB_OBJ((*rp)->obj)->range) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ done++;
+ } else {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ done++;
+ } else {
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ case GRN_TYPE :
+ if (DB_OBJ((*rp)->obj)->range) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ done++;
+ } else {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_NO_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ break;
+ case 's' : /* score, sum */
+ if (len == GRN_COLUMN_NAME_SCORE_LEN &&
+ memcmp(name, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN) == 0) {
+ if (!grn_obj_get_accessor_rset_value(ctx, obj, &res,
+ GRN_ACCESSOR_GET_SCORE)) {
+ goto exit;
+ }
+ } else if (len == GRN_COLUMN_NAME_SUM_LEN &&
+ memcmp(name,
+ GRN_COLUMN_NAME_SUM,
+ GRN_COLUMN_NAME_SUM_LEN) == 0) {
+ if (!grn_obj_get_accessor_rset_value(ctx, obj, &res,
+ GRN_ACCESSOR_GET_SUM)) {
+ goto exit;
+ }
+ } else {
+ goto exit;
+ }
+ break;
+ case 'n' : /* nsubrecs */
+ if (len != GRN_COLUMN_NAME_NSUBRECS_LEN ||
+ memcmp(name,
+ GRN_COLUMN_NAME_NSUBRECS,
+ GRN_COLUMN_NAME_NSUBRECS_LEN)) {
+ goto exit;
+ }
+ if (!grn_obj_get_accessor_rset_value(ctx, obj, &res,
+ GRN_ACCESSOR_GET_NSUBRECS)) {
+ goto exit;
+ }
+ break;
+ case 'm' : /* max, min */
+ if (len == GRN_COLUMN_NAME_MAX_LEN &&
+ memcmp(name,
+ GRN_COLUMN_NAME_MAX,
+ GRN_COLUMN_NAME_MAX_LEN) == 0) {
+ if (!grn_obj_get_accessor_rset_value(ctx, obj, &res,
+ GRN_ACCESSOR_GET_MAX)) {
+ goto exit;
+ }
+ } else if (len == GRN_COLUMN_NAME_MIN_LEN &&
+ memcmp(name,
+ GRN_COLUMN_NAME_MIN,
+ GRN_COLUMN_NAME_MIN_LEN) == 0) {
+ if (!grn_obj_get_accessor_rset_value(ctx, obj, &res,
+ GRN_ACCESSOR_GET_MIN)) {
+ goto exit;
+ }
+ } else {
+ goto exit;
+ }
+ break;
+ case 'a' : /* avg */
+ if (len == GRN_COLUMN_NAME_AVG_LEN &&
+ memcmp(name,
+ GRN_COLUMN_NAME_AVG,
+ GRN_COLUMN_NAME_AVG_LEN) == 0) {
+ if (!grn_obj_get_accessor_rset_value(ctx, obj, &res,
+ GRN_ACCESSOR_GET_AVG)) {
+ goto exit;
+ }
+ } else {
+ goto exit;
+ }
+ break;
+ default :
+ res = NULL;
+ goto exit;
+ }
+ } else {
+ /* if obj->header.type == GRN_TYPE ... lookup table */
+ for (rp = &res; ; rp = &(*rp)->next) {
+ grn_obj *column = grn_obj_column_(ctx, obj, name, len);
+ if (column) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = column;
+ /*
+ switch (column->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ break;
+ case GRN_COLUMN_INDEX :
+ break;
+ }
+ */
+ (*rp)->action = GRN_ACCESSOR_GET_COLUMN_VALUE;
+ break;
+ } else {
+ grn_id next_obj_id;
+ next_obj_id = obj->header.domain;
+ if (!next_obj_id) {
+ // ERR(GRN_INVALID_ARGUMENT, "no such column: <%s>", name);
+ if (!is_chained) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ }
+ res = NULL;
+ goto exit;
+ }
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ obj = grn_ctx_at(ctx, next_obj_id);
+ if (!obj) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_NO_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ }
+ if (sp != se) {
+ if (!grn_obj_get_accessor(ctx, (grn_obj *)res, sp, se - sp)) {
+ if (!is_chained) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ }
+ if (rp0) { *rp0 = res; }
+ exit :
+ GRN_API_RETURN((grn_obj *)res);
+}
+
+inline static grn_bool
+grn_column_is_vector(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj_flags type;
+
+ if (column->header.type != GRN_COLUMN_VAR_SIZE) {
+ return GRN_FALSE;
+ }
+
+ type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK;
+ return type == GRN_OBJ_COLUMN_VECTOR;
+}
+
+inline static grn_bool
+grn_column_is_index(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj_flags type;
+
+ if (column->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)column; a; a = a->next) {
+ if (a->next) {
+ continue;
+ }
+ if (a->action != GRN_ACCESSOR_GET_COLUMN_VALUE) {
+ return GRN_FALSE;
+ }
+
+ column = a->obj;
+ }
+ }
+
+ if (column->header.type != GRN_COLUMN_INDEX) {
+ return GRN_FALSE;
+ }
+
+ type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK;
+ return type == GRN_OBJ_COLUMN_INDEX;
+}
+
+inline static void
+grn_obj_get_range_info(grn_ctx *ctx, grn_obj *obj,
+ grn_id *range_id, grn_obj_flags *range_flags)
+{
+ if (!obj) {
+ *range_id = GRN_ID_NIL;
+ } else if (grn_obj_is_proc(ctx, obj)) {
+ /* TODO */
+ *range_id = GRN_ID_NIL;
+ } else if (GRN_DB_OBJP(obj)) {
+ *range_id = DB_OBJ(obj)->range;
+ if (grn_column_is_vector(ctx, obj)) {
+ *range_flags = GRN_OBJ_VECTOR;
+ }
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ *range_id = GRN_DB_UINT32;
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (GRN_DB_OBJP(a->obj)) {
+ *range_id = DB_OBJ(a->obj)->range;
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ *range_id = GRN_DB_FLOAT;
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ *range_id = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ case GRN_ACCESSOR_GET_MIN :
+ case GRN_ACCESSOR_GET_SUM :
+ *range_id = GRN_DB_INT64;
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ *range_id = GRN_DB_FLOAT;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ grn_obj_get_range_info(ctx, a->obj, range_id, range_flags);
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ if (GRN_DB_OBJP(a->obj)) { *range_id = DB_OBJ(a->obj)->header.domain; }
+ break;
+ default :
+ if (GRN_DB_OBJP(a->obj)) { *range_id = DB_OBJ(a->obj)->range; }
+ break;
+ }
+ }
+ }
+}
+
+grn_id
+grn_obj_get_range(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id range_id = GRN_ID_NIL;
+ grn_obj_flags range_flags = 0;
+
+ grn_obj_get_range_info(ctx, obj, &range_id, &range_flags);
+
+ return range_id;
+}
+
+int
+grn_obj_is_persistent(grn_ctx *ctx, grn_obj *obj)
+{
+ int res = 0;
+ if (GRN_DB_OBJP(obj)) {
+ res = IS_TEMP(obj) ? 0 : 1;
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_SCORE :
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ case GRN_ACCESSOR_GET_MAX :
+ case GRN_ACCESSOR_GET_MIN :
+ case GRN_ACCESSOR_GET_SUM :
+ case GRN_ACCESSOR_GET_AVG :
+ res = 0;
+ break;
+ case GRN_ACCESSOR_GET_ID :
+ case GRN_ACCESSOR_GET_VALUE :
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ case GRN_ACCESSOR_GET_KEY :
+ if (GRN_DB_OBJP(a->obj)) { res = IS_TEMP(obj) ? 0 : 1; }
+ break;
+ default :
+ if (GRN_DB_OBJP(a->obj)) { res = IS_TEMP(obj) ? 0 : 1; }
+ break;
+ }
+ }
+ }
+ return res;
+}
+
+#define SRC2RECORD() do {\
+ grn_obj *table = grn_ctx_at(ctx, dest->header.domain);\
+ if (GRN_OBJ_TABLEP(table)) {\
+ grn_obj *p_key = src;\
+ grn_id id;\
+ if (table->header.type != GRN_TABLE_NO_KEY) {\
+ grn_obj key;\
+ GRN_OBJ_INIT(&key, GRN_BULK, 0, table->header.domain);\
+ if (src->header.domain != table->header.domain) {\
+ rc = grn_obj_cast(ctx, src, &key, GRN_TRUE);\
+ p_key = &key;\
+ }\
+ if (!rc) {\
+ if (GRN_BULK_VSIZE(p_key)) {\
+ if (add_record_if_not_exist) {\
+ id = grn_table_add_by_key(ctx, table, p_key, NULL);\
+ } else {\
+ id = grn_table_get_by_key(ctx, table, p_key);\
+ }\
+ if (id) {\
+ GRN_RECORD_SET(ctx, dest, id);\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ } else {\
+ GRN_RECORD_SET(ctx, dest, GRN_ID_NIL);\
+ }\
+ }\
+ GRN_OBJ_FIN(ctx, &key);\
+ } else {\
+ grn_obj record_id;\
+ GRN_UINT32_INIT(&record_id, 0);\
+ rc = grn_obj_cast(ctx, src, &record_id, GRN_TRUE);\
+ if (!rc) {\
+ id = GRN_UINT32_VALUE(&record_id);\
+ if (id) {\
+ GRN_RECORD_SET(ctx, dest, id);\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ }\
+ }\
+ } else {\
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;\
+ }\
+} while (0)
+
+inline static grn_rc
+grn_obj_cast_bool(grn_ctx *ctx, grn_obj *src, grn_obj *dest,
+ grn_bool add_record_if_not_exist)
+{
+ grn_rc rc = GRN_SUCCESS;
+
+ switch (dest->header.domain) {
+ case GRN_DB_BOOL :
+ GRN_BOOL_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT8 :
+ GRN_INT8_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT8 :
+ GRN_UINT8_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT16 :
+ GRN_INT16_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT16 :
+ GRN_UINT16_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT32 :
+ GRN_INT32_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT32 :
+ GRN_UINT32_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT64 :
+ GRN_INT64_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT64 :
+ GRN_UINT64_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_FLOAT :
+ GRN_FLOAT_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_TIME :
+ GRN_TIME_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ {
+ const char *bool_text;
+ bool_text = GRN_BOOL_VALUE(src) ? "true" : "false";
+ GRN_TEXT_PUTS(ctx, dest, bool_text);
+ }
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ rc = GRN_INVALID_ARGUMENT;
+ break;
+ default :
+ SRC2RECORD();
+ break;
+ }
+ return rc;
+}
+
+#define NUM2DEST(getvalue,totext,tobool,totime,tofloat)\
+ switch (dest->header.domain) {\
+ case GRN_DB_BOOL :\
+ tobool(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT8 :\
+ GRN_INT8_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT8 :\
+ GRN_UINT8_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT16 :\
+ GRN_INT16_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT16 :\
+ GRN_UINT16_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT32 :\
+ GRN_INT32_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT32 :\
+ GRN_UINT32_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_TIME :\
+ totime(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT64 :\
+ GRN_INT64_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT64 :\
+ GRN_UINT64_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_FLOAT :\
+ tofloat(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ totext(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_TOKYO_GEO_POINT :\
+ case GRN_DB_WGS84_GEO_POINT :\
+ rc = GRN_INVALID_ARGUMENT;\
+ break;\
+ default :\
+ SRC2RECORD();\
+ break;\
+ }
+
+#define TEXT2DEST(type,tonum,setvalue) do {\
+ const char *cur, *str = GRN_TEXT_VALUE(src);\
+ const char *str_end = GRN_BULK_CURR(src);\
+ type i = tonum(str, str_end, &cur);\
+ if (cur == str_end) {\
+ setvalue(ctx, dest, i);\
+ } else if (cur != str) {\
+ const char *rest;\
+ grn_obj buf;\
+ GRN_VOID_INIT(&buf);\
+ rc = grn_aton(ctx, str, str_end, &rest, &buf);\
+ if (!rc) {\
+ rc = grn_obj_cast(ctx, &buf, dest, add_record_if_not_exist);\
+ }\
+ GRN_OBJ_FIN(ctx, &buf);\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+} while (0)
+
+#define NUM2BOOL(ctx, dest, value) GRN_BOOL_SET(ctx, dest, value != 0)
+#define FLOAT2BOOL(ctx, dest, value) do {\
+ double value_ = value;\
+ GRN_BOOL_SET(ctx, dest, value_ < -DBL_EPSILON || DBL_EPSILON < value_);\
+} while (0)
+
+#define NUM2TIME(ctx, dest, value)\
+ GRN_TIME_SET(ctx, dest, (long long int)(value) * GRN_TIME_USEC_PER_SEC);
+#define TIME2TIME(ctx, dest, value)\
+ GRN_TIME_SET(ctx, dest, value);
+#define FLOAT2TIME(ctx, dest, value) do {\
+ int64_t usec = llround(value * GRN_TIME_USEC_PER_SEC);\
+ GRN_TIME_SET(ctx, dest, usec);\
+} while (0)
+
+#define NUM2FLOAT(ctx, dest, value)\
+ GRN_FLOAT_SET(ctx, dest, value);
+#define TIME2FLOAT(ctx, dest, value)\
+ GRN_FLOAT_SET(ctx, dest, (double)(value) / GRN_TIME_USEC_PER_SEC);
+#define FLOAT2FLOAT(ctx, dest, value)\
+ GRN_FLOAT_SET(ctx, dest, value);
+
+static grn_rc
+grn_obj_cast_record(grn_ctx *ctx,
+ grn_obj *src,
+ grn_obj *dest,
+ grn_bool add_record_if_not_exist)
+{
+ grn_obj *src_table;
+ grn_obj *dest_table;
+ const char *key;
+ uint32_t key_size;
+ grn_id dest_id;
+
+ if (src->header.domain == dest->header.domain) {
+ GRN_RECORD_SET(ctx, dest, GRN_RECORD_VALUE(src));
+ return GRN_SUCCESS;
+ }
+
+ src_table = grn_ctx_at(ctx, src->header.domain);
+ if (!src_table) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (src_table->header.type == GRN_TABLE_NO_KEY) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ dest_table = grn_ctx_at(ctx, dest->header.domain);
+ if (!dest_table) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ switch (dest_table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ break;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ if (GRN_RECORD_VALUE(src) == GRN_ID_NIL) {
+ GRN_RECORD_SET(ctx, dest, GRN_RECORD_VALUE(src));
+ return GRN_SUCCESS;
+ }
+
+ key = _grn_table_key(ctx, src_table, GRN_RECORD_VALUE(src), &key_size);
+ if (add_record_if_not_exist) {
+ dest_id = grn_table_add(ctx, dest_table, key, key_size, NULL);
+ } else {
+ dest_id = grn_table_get(ctx, dest_table, key, key_size);
+ }
+ GRN_RECORD_SET(ctx, dest, dest_id);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_obj_cast(grn_ctx *ctx, grn_obj *src, grn_obj *dest,
+ grn_bool add_record_if_not_exist)
+{
+ grn_rc rc = GRN_SUCCESS;
+ switch (src->header.domain) {
+ case GRN_DB_BOOL :
+ rc = grn_obj_cast_bool(ctx, src, dest, add_record_if_not_exist);
+ break;
+ case GRN_DB_INT8 :
+ NUM2DEST(GRN_INT8_VALUE, grn_text_itoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_UINT8 :
+ NUM2DEST(GRN_UINT8_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_INT16 :
+ NUM2DEST(GRN_INT16_VALUE, grn_text_itoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_UINT16 :
+ NUM2DEST(GRN_UINT16_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_INT32 :
+ NUM2DEST(GRN_INT32_VALUE, grn_text_itoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_UINT32 :
+ NUM2DEST(GRN_UINT32_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_INT64 :
+ NUM2DEST(GRN_INT64_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_TIME :
+ NUM2DEST(GRN_TIME_VALUE, grn_text_lltoa, NUM2BOOL, TIME2TIME, TIME2FLOAT);
+ break;
+ case GRN_DB_UINT64 :
+ NUM2DEST(GRN_UINT64_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_FLOAT :
+ NUM2DEST(GRN_FLOAT_VALUE, grn_text_ftoa, FLOAT2BOOL, FLOAT2TIME,
+ FLOAT2FLOAT);
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ switch (dest->header.domain) {
+ case GRN_DB_BOOL :
+ GRN_BOOL_SET(ctx, dest, GRN_TEXT_LEN(src) > 0);
+ break;
+ case GRN_DB_INT8 :
+ TEXT2DEST(int8_t, grn_atoi8, GRN_INT8_SET);
+ break;
+ case GRN_DB_UINT8 :
+ TEXT2DEST(uint8_t, grn_atoui8, GRN_UINT8_SET);
+ break;
+ case GRN_DB_INT16 :
+ TEXT2DEST(int16_t, grn_atoi16, GRN_INT16_SET);
+ break;
+ case GRN_DB_UINT16 :
+ TEXT2DEST(uint16_t, grn_atoui16, GRN_UINT16_SET);
+ break;
+ case GRN_DB_INT32 :
+ TEXT2DEST(int32_t, grn_atoi, GRN_INT32_SET);
+ break;
+ case GRN_DB_UINT32 :
+ TEXT2DEST(uint32_t, grn_atoui, GRN_UINT32_SET);
+ break;
+ case GRN_DB_TIME :
+ {
+ grn_timeval v;
+ int len = GRN_TEXT_LEN(src);
+ char *str = GRN_TEXT_VALUE(src);
+ if (grn_str2timeval(str, len, &v)) {
+ double d;
+ char *end;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, len);
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ errno = 0;
+ d = strtod(GRN_TEXT_VALUE(&buf), &end);
+ if (!errno && end + 1 == GRN_BULK_CURR(&buf)) {
+ v.tv_sec = d;
+ v.tv_nsec = ((d - v.tv_sec) * GRN_TIME_NSEC_PER_SEC);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ GRN_TIME_SET(ctx, dest,
+ GRN_TIME_PACK((int64_t)v.tv_sec,
+ GRN_TIME_NSEC_TO_USEC(v.tv_nsec)));
+ }
+ break;
+ case GRN_DB_INT64 :
+ TEXT2DEST(int64_t, grn_atoll, GRN_INT64_SET);
+ break;
+ case GRN_DB_UINT64 :
+ TEXT2DEST(int64_t, grn_atoll, GRN_UINT64_SET);
+ break;
+ case GRN_DB_FLOAT :
+ {
+ double d;
+ char *end;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ errno = 0;
+ d = strtod(GRN_TEXT_VALUE(&buf), &end);
+ if (!errno && end + 1 == GRN_BULK_CURR(&buf)) {
+ GRN_FLOAT_SET(ctx, dest, d);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ GRN_TEXT_PUT(ctx, dest, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ {
+ int latitude, longitude;
+ double degree;
+ const char *cur, *str = GRN_TEXT_VALUE(src);
+ const char *str_end = GRN_BULK_CURR(src);
+ if (str == str_end) {
+ GRN_GEO_POINT_SET(ctx, dest, 0, 0);
+ } else {
+ char *end;
+ grn_obj buf, *buf_p = NULL;
+ latitude = grn_atoi(str, str_end, &cur);
+ if (cur < str_end && cur[0] == '.') {
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, GRN_TEXT_LEN(src));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ buf_p = &buf;
+ errno = 0;
+ degree = strtod(GRN_TEXT_VALUE(buf_p), &end);
+ if (errno) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ latitude = GRN_GEO_DEGREE2MSEC(degree);
+ cur = str + (end - GRN_TEXT_VALUE(buf_p));
+ }
+ }
+ if (!rc && (cur[0] == 'x' || cur[0] == ',') && cur + 1 < str_end) {
+ const char *c = cur + 1;
+ longitude = grn_atoi(c, str_end, &cur);
+ if (cur < str_end && cur[0] == '.') {
+ if (!buf_p) {
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, GRN_TEXT_LEN(src));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ buf_p = &buf;
+ }
+ errno = 0;
+ degree = strtod(GRN_TEXT_VALUE(buf_p) + (c - str), &end);
+ if (errno) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ longitude = GRN_GEO_DEGREE2MSEC(degree);
+ cur = str + (end - GRN_TEXT_VALUE(buf_p));
+ }
+ }
+ if (!rc && cur == str_end) {
+ if ((GRN_GEO_MIN_LATITUDE <= latitude &&
+ latitude <= GRN_GEO_MAX_LATITUDE) &&
+ (GRN_GEO_MIN_LONGITUDE <= longitude &&
+ longitude <= GRN_GEO_MAX_LONGITUDE)) {
+ GRN_GEO_POINT_SET(ctx, dest, latitude, longitude);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ if (buf_p) { GRN_OBJ_FIN(ctx, buf_p); }
+ }
+ }
+ break;
+ default :
+ SRC2RECORD();
+ break;
+ }
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (src->header.domain == dest->header.domain) {
+ GRN_TEXT_PUT(ctx, dest, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ } else {
+ int latitude, longitude;
+ double latitude_in_degree, longitude_in_degree;
+ GRN_GEO_POINT_VALUE(src, latitude, longitude);
+ latitude_in_degree = GRN_GEO_MSEC2DEGREE(latitude);
+ longitude_in_degree = GRN_GEO_MSEC2DEGREE(longitude);
+ /* TokyoGeoPoint <-> WGS84GeoPoint is based on
+ http://www.jalan.net/jw/jwp0200/jww0203.do
+
+ jx: longitude in degree in Tokyo Geodetic System.
+ jy: latitude in degree in Tokyo Geodetic System.
+ wx: longitude in degree in WGS 84.
+ wy: latitude in degree in WGS 84.
+
+ jy = wy * 1.000106961 - wx * 0.000017467 - 0.004602017
+ jx = wx * 1.000083049 + wy * 0.000046047 - 0.010041046
+
+ wy = jy - jy * 0.00010695 + jx * 0.000017464 + 0.0046017
+ wx = jx - jy * 0.000046038 - jx * 0.000083043 + 0.010040
+ */
+ if (dest->header.domain == GRN_DB_TOKYO_GEO_POINT) {
+ double wgs84_latitude_in_degree = latitude_in_degree;
+ double wgs84_longitude_in_degree = longitude_in_degree;
+ int tokyo_latitude, tokyo_longitude;
+ double tokyo_latitude_in_degree, tokyo_longitude_in_degree;
+ tokyo_latitude_in_degree =
+ wgs84_latitude_in_degree * 1.000106961 -
+ wgs84_longitude_in_degree * 0.000017467 -
+ 0.004602017;
+ tokyo_longitude_in_degree =
+ wgs84_longitude_in_degree * 1.000083049 +
+ wgs84_latitude_in_degree * 0.000046047 -
+ 0.010041046;
+ tokyo_latitude = GRN_GEO_DEGREE2MSEC(tokyo_latitude_in_degree);
+ tokyo_longitude = GRN_GEO_DEGREE2MSEC(tokyo_longitude_in_degree);
+ GRN_GEO_POINT_SET(ctx, dest, tokyo_latitude, tokyo_longitude);
+ } else {
+ double tokyo_latitude_in_degree = latitude_in_degree;
+ double tokyo_longitude_in_degree = longitude_in_degree;
+ int wgs84_latitude, wgs84_longitude;
+ double wgs84_latitude_in_degree, wgs84_longitude_in_degree;
+ wgs84_latitude_in_degree =
+ tokyo_latitude_in_degree -
+ tokyo_latitude_in_degree * 0.00010695 +
+ tokyo_longitude_in_degree * 0.000017464 +
+ 0.0046017;
+ wgs84_longitude_in_degree =
+ tokyo_longitude_in_degree -
+ tokyo_latitude_in_degree * 0.000046038 -
+ tokyo_longitude_in_degree * 0.000083043 +
+ 0.010040;
+ wgs84_latitude = GRN_GEO_DEGREE2MSEC(wgs84_latitude_in_degree);
+ wgs84_longitude = GRN_GEO_DEGREE2MSEC(wgs84_longitude_in_degree);
+ GRN_GEO_POINT_SET(ctx, dest, wgs84_latitude, wgs84_longitude);
+ }
+ }
+ break;
+ case GRN_VOID :
+ rc = grn_obj_reinit(ctx, dest, dest->header.domain, dest->header.flags);
+ break;
+ default :
+ if (src->header.domain >= GRN_N_RESERVED_TYPES) {
+ grn_obj *table;
+ table = grn_ctx_at(ctx, src->header.domain);
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ rc = grn_obj_cast_record(ctx, src, dest, add_record_if_not_exist);
+ break;
+ default :
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;
+ break;
+ }
+ } else {
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;
+ }
+ break;
+ }
+ return rc;
+}
+
+const char *
+grn_accessor_get_value_(grn_ctx *ctx, grn_accessor *a, grn_id id, uint32_t *size)
+{
+ const char *value = NULL;
+ for (;;) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ value = (const char *)(uintptr_t)id;
+ *size = GRN_OBJ_GET_VALUE_IMD;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ value = _grn_table_key(ctx, a->obj, id, size);
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ value = grn_obj_get_value_(ctx, a->obj, id, size);
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value = (const char *)&((grn_rset_recinfo *)value)->score;
+ *size = sizeof(double);
+ }
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value = (const char *)&((grn_rset_recinfo *)value)->n_subrecs;
+ *size = sizeof(int);
+ }
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value =
+ (const char *)grn_rset_recinfo_get_max_(ctx,
+ (grn_rset_recinfo *)value,
+ a->obj);
+ *size = GRN_RSET_MAX_SIZE;
+ }
+ break;
+ case GRN_ACCESSOR_GET_MIN :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value =
+ (const char *)grn_rset_recinfo_get_min_(ctx,
+ (grn_rset_recinfo *)value,
+ a->obj);
+ *size = GRN_RSET_MIN_SIZE;
+ }
+ break;
+ case GRN_ACCESSOR_GET_SUM :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value =
+ (const char *)grn_rset_recinfo_get_sum_(ctx,
+ (grn_rset_recinfo *)value,
+ a->obj);
+ *size = GRN_RSET_SUM_SIZE;
+ }
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value =
+ (const char *)grn_rset_recinfo_get_avg_(ctx,
+ (grn_rset_recinfo *)value,
+ a->obj);
+ *size = GRN_RSET_AVG_SIZE;
+ }
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ /* todo : support vector */
+ value = grn_obj_get_value_(ctx, a->obj, id, size);
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ value = _grn_table_key(ctx, ((grn_db *)ctx->impl->db)->keys, id, size);
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if (value && (a = a->next)) {
+ id = *((grn_id *)value);
+ } else {
+ break;
+ }
+ }
+ return value;
+}
+
+static grn_obj *
+grn_accessor_get_value(grn_ctx *ctx, grn_accessor *a, grn_id id, grn_obj *value)
+{
+ uint32_t vs = 0;
+ uint32_t size0;
+ void *vp = NULL;
+ if (!value) {
+ if (!(value = grn_obj_open(ctx, GRN_BULK, 0, 0))) { return NULL; }
+ } else {
+ value->header.type = GRN_BULK;
+ }
+ size0 = GRN_BULK_VSIZE(value);
+ for (;;) {
+ grn_bulk_truncate(ctx, value, size0);
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_UINT32_PUT(ctx, value, id);
+ value->header.domain = GRN_DB_UINT32;
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ if (!a->next && GRN_TABLE_IS_MULTI_KEYS_GROUPED(a->obj)) {
+ grn_obj_ensure_vector(ctx, value);
+ if (id) {
+ grn_obj raw_vector;
+ GRN_TEXT_INIT(&raw_vector, 0);
+ grn_table_get_key2(ctx, a->obj, id, &raw_vector);
+ grn_vector_decode(ctx, value,
+ GRN_BULK_HEAD(&raw_vector),
+ GRN_BULK_VSIZE(&raw_vector));
+ GRN_OBJ_FIN(ctx, &raw_vector);
+ }
+ vp = NULL;
+ vs = 0;
+ } else {
+ if (id) {
+ grn_table_get_key2(ctx, a->obj, id, value);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ } else {
+ vp = NULL;
+ vs = 0;
+ }
+ value->header.domain = a->obj->header.domain;
+ }
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ grn_obj_get_value(ctx, a->obj, id, value);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ if (id) {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_FLOAT_PUT(ctx, value, ri->score);
+ } else {
+ GRN_FLOAT_PUT(ctx, value, 0.0);
+ }
+ value->header.domain = GRN_DB_FLOAT;
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ if (id) {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, value, ri->n_subrecs);
+ } else {
+ GRN_INT32_PUT(ctx, value, 0);
+ }
+ value->header.domain = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ if (id) {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ int64_t max;
+ max = grn_rset_recinfo_get_max(ctx, ri, a->obj);
+ GRN_INT64_PUT(ctx, value, max);
+ } else {
+ GRN_INT64_PUT(ctx, value, 0);
+ }
+ value->header.domain = GRN_DB_INT64;
+ break;
+ case GRN_ACCESSOR_GET_MIN :
+ if (id) {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ int64_t min;
+ min = grn_rset_recinfo_get_min(ctx, ri, a->obj);
+ GRN_INT64_PUT(ctx, value, min);
+ } else {
+ GRN_INT64_PUT(ctx, value, 0);
+ }
+ value->header.domain = GRN_DB_INT64;
+ break;
+ case GRN_ACCESSOR_GET_SUM :
+ if (id) {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ int64_t sum;
+ sum = grn_rset_recinfo_get_sum(ctx, ri, a->obj);
+ GRN_INT64_PUT(ctx, value, sum);
+ } else {
+ GRN_INT64_PUT(ctx, value, 0);
+ }
+ value->header.domain = GRN_DB_INT64;
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ if (id) {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ double avg;
+ avg = grn_rset_recinfo_get_avg(ctx, ri, a->obj);
+ GRN_FLOAT_PUT(ctx, value, avg);
+ } else {
+ GRN_FLOAT_PUT(ctx, value, 0.0);
+ }
+ value->header.domain = GRN_DB_FLOAT;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ /* todo : support vector */
+ grn_obj_get_value(ctx, a->obj, id, value);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ value = grn_ctx_at(ctx, id);
+ grn_obj_close(ctx, value);
+ return value;
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if ((a = a->next)) {
+ if (vs > 0) {
+ id = *((grn_id *)vp);
+ } else {
+ id = GRN_ID_NIL;
+ }
+ } else {
+ break;
+ }
+ }
+ return value;
+}
+
+static grn_rc
+grn_accessor_set_value(grn_ctx *ctx, grn_accessor *a, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (!value) { value = grn_obj_open(ctx, GRN_BULK, 0, 0); }
+ if (value) {
+ grn_obj buf;
+ void *vp = NULL;
+ GRN_TEXT_INIT(&buf, 0);
+ for (;;) {
+ GRN_BULK_REWIND(&buf);
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ grn_table_get_key2(ctx, a->obj, id, &buf);
+ vp = GRN_BULK_HEAD(&buf);
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (a->next) {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ vp = GRN_BULK_HEAD(&buf);
+ } else {
+ rc = grn_obj_set_value(ctx, a->obj, id, value, flags);
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ {
+ grn_rset_recinfo *ri;
+ if (a->next) {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ vp = &ri->score;
+ } else {
+ uint32_t size;
+ if ((ri = (grn_rset_recinfo *) grn_obj_get_value_(ctx, a->obj, id, &size))) {
+ // todo : flags support
+ if (value->header.domain == GRN_DB_FLOAT) {
+ ri->score = GRN_FLOAT_VALUE(value);
+ } else {
+ grn_obj buf;
+ GRN_FLOAT_INIT(&buf, 0);
+ grn_obj_cast(ctx, value, &buf, GRN_FALSE);
+ ri->score = GRN_FLOAT_VALUE(&buf);
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ }
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ vp = &ri->n_subrecs;
+ }
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ if (value->header.type == GRN_DB_INT64) {
+ grn_rset_recinfo_set_max(ctx, ri, a->obj, GRN_INT64_VALUE(value));
+ } else {
+ grn_obj value_int64;
+ GRN_INT64_INIT(&value_int64, 0);
+ if (!grn_obj_cast(ctx, value, &value_int64, GRN_FALSE)) {
+ grn_rset_recinfo_set_max(ctx, ri, a->obj,
+ GRN_INT64_VALUE(&value_int64));
+ }
+ GRN_OBJ_FIN(ctx, &value_int64);
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_MIN :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ if (value->header.type == GRN_DB_INT64) {
+ grn_rset_recinfo_set_min(ctx, ri, a->obj, GRN_INT64_VALUE(value));
+ } else {
+ grn_obj value_int64;
+ GRN_INT64_INIT(&value_int64, 0);
+ if (!grn_obj_cast(ctx, value, &value_int64, GRN_FALSE)) {
+ grn_rset_recinfo_set_min(ctx, ri, a->obj,
+ GRN_INT64_VALUE(&value_int64));
+ }
+ GRN_OBJ_FIN(ctx, &value_int64);
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_SUM :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ if (value->header.type == GRN_DB_INT64) {
+ grn_rset_recinfo_set_sum(ctx, ri, a->obj, GRN_INT64_VALUE(value));
+ } else {
+ grn_obj value_int64;
+ GRN_INT64_INIT(&value_int64, 0);
+ if (!grn_obj_cast(ctx, value, &value_int64, GRN_FALSE)) {
+ grn_rset_recinfo_set_sum(ctx, ri, a->obj,
+ GRN_INT64_VALUE(&value_int64));
+ }
+ GRN_OBJ_FIN(ctx, &value_int64);
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ if (value->header.type == GRN_DB_FLOAT) {
+ grn_rset_recinfo_set_avg(ctx, ri, a->obj, GRN_FLOAT_VALUE(value));
+ } else {
+ grn_obj value_float;
+ GRN_FLOAT_INIT(&value_float, 0);
+ if (!grn_obj_cast(ctx, value, &value_float, GRN_FALSE)) {
+ grn_rset_recinfo_set_avg(ctx, ri, a->obj,
+ GRN_FLOAT_VALUE(&value_float));
+ }
+ GRN_OBJ_FIN(ctx, &value_float);
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ /* todo : support vector */
+ if (a->next) {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ vp = GRN_BULK_HEAD(&buf);
+ } else {
+ rc = grn_obj_set_value(ctx, a->obj, id, value, flags);
+ }
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if ((a = a->next)) {
+ id = *((grn_id *)vp);
+ } else {
+ break;
+ }
+ }
+ grn_obj_close(ctx, &buf);
+ }
+ return rc;
+}
+
+#define INCRDECR(op) \
+ switch (DB_OBJ(obj)->range) {\
+ case GRN_DB_INT8 :\
+ if (s == sizeof(int8_t)) {\
+ int8_t *vp = (int8_t *)p;\
+ *vp op *(int8_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_UINT8 :\
+ if (s == sizeof(uint8_t)) {\
+ uint8_t *vp = (uint8_t *)p;\
+ *vp op *(int8_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_INT16 :\
+ if (s == sizeof(int16_t)) {\
+ int16_t *vp = (int16_t *)p;\
+ *vp op *(int16_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_UINT16 :\
+ if (s == sizeof(uint16_t)) {\
+ uint16_t *vp = (uint16_t *)p;\
+ *vp op *(int16_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_INT32 :\
+ if (s == sizeof(int32_t)) {\
+ int32_t *vp = (int32_t *)p;\
+ *vp op *(int32_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_UINT32 :\
+ if (s == sizeof(uint32_t)) {\
+ uint32_t *vp = (uint32_t *)p;\
+ *vp op *(int32_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_INT64 :\
+ case GRN_DB_TIME :\
+ if (s == sizeof(int64_t)) {\
+ int64_t *vp = (int64_t *)p;\
+ *vp op *(int64_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_FLOAT :\
+ if (s == sizeof(double)) {\
+ double *vp = (double *)p;\
+ *vp op *(double *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ default :\
+ rc = GRN_OPERATION_NOT_SUPPORTED;\
+ break;\
+ }
+
+uint32_t
+grn_obj_size(grn_ctx *ctx, grn_obj *obj)
+{
+ if (!obj) { return 0; }
+ switch (obj->header.type) {
+ case GRN_VOID :
+ case GRN_BULK :
+ case GRN_PTR :
+ case GRN_UVECTOR :
+ case GRN_PVECTOR :
+ case GRN_MSG :
+ return GRN_BULK_VSIZE(obj);
+ case GRN_VECTOR :
+ return obj->u.v.body ? GRN_BULK_VSIZE(obj->u.v.body) : 0;
+ default :
+ return 0;
+ }
+}
+
+inline static int
+call_hook(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value, int flags)
+{
+ grn_hook *hooks = DB_OBJ(obj)->hooks[GRN_HOOK_SET];
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ if (hooks || obj->header.type == GRN_COLUMN_VAR_SIZE) {
+ grn_obj oldbuf, *oldvalue;
+ GRN_TEXT_INIT(&oldbuf, 0);
+ oldvalue = grn_obj_get_value(ctx, obj, id, &oldbuf);
+ if (flags & GRN_OBJ_SET) {
+ void *ov;
+ unsigned int os;
+ ov = GRN_BULK_HEAD(oldvalue);
+ os = grn_obj_size(ctx, oldvalue);
+ if ((ov && v && os == s && !memcmp(ov, v, s)) &&
+ !(obj->header.type == GRN_COLUMN_FIX_SIZE &&
+ grn_bulk_is_zero(ctx, value))) {
+ grn_obj_close(ctx, oldvalue);
+ return 0;
+ }
+ }
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4, {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}}};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_UINT32_SET(ctx, &id_, id);
+ GRN_UINT32_SET(ctx, &flags_, flags);
+ while (hooks) {
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, oldvalue);
+ grn_ctx_push(ctx, value);
+ grn_ctx_push(ctx, &flags_);
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &obj, &pctx.user_data);
+ } else {
+ grn_obj_default_set_value_hook(ctx, 1, &obj, &pctx.user_data);
+ }
+ if (ctx->rc) {
+ grn_obj_close(ctx, oldvalue);
+ return 1;
+ }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ grn_obj_close(ctx, oldvalue);
+ }
+ return 0;
+}
+
+static grn_rc
+grn_obj_set_value_table_pat_key(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ grn_obj buf;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ if (ctx->rc) {
+ rc = ctx->rc;
+ }
+ return rc;
+ }
+
+ if (range != value->header.domain) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ }
+ }
+ rc = grn_pat_set_value(ctx, (grn_pat *)obj, id, v, flags);
+ if (range != value->header.domain) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_table_hash_key(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ grn_obj buf;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ if (ctx->rc) {
+ rc = ctx->rc;
+ }
+ return rc;
+ }
+
+ if (range != value->header.domain) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ }
+ }
+ rc = grn_hash_set_value(ctx, (grn_hash *)obj, id, v, flags);
+ if (range != value->header.domain) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_table_no_key(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ grn_obj buf;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ if (ctx->rc) {
+ rc = ctx->rc;
+ }
+ return rc;
+ }
+
+ if (range != value->header.domain) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ }
+ }
+ rc = grn_array_set_value(ctx, (grn_array *)obj, id, v, flags);
+ if (range != value->header.domain) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_var_size_scalar(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ grn_obj buf;
+ grn_id buf_domain = GRN_DB_VOID;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ if (ctx->rc) {
+ rc = ctx->rc;
+ }
+ return rc;
+ }
+
+ switch (flags & GRN_OBJ_SET_MASK) {
+ case GRN_OBJ_INCR :
+ case GRN_OBJ_DECR :
+ if (value->header.domain == GRN_DB_INT32 ||
+ value->header.domain == GRN_DB_INT64) {
+ /* do nothing */
+ } else if (GRN_DB_INT8 <= value->header.domain &&
+ value->header.domain < GRN_DB_INT32) {
+ buf_domain = GRN_DB_INT32;
+ } else {
+ buf_domain = GRN_DB_INT64;
+ }
+ break;
+ default :
+ if (range != value->header.domain) {
+ buf_domain = range;
+ }
+ break;
+ }
+
+ if (buf_domain != GRN_DB_VOID) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, buf_domain);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ s = GRN_BULK_VSIZE(&buf);
+ }
+ }
+
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id, v, s, flags, NULL);
+
+ if (buf_domain != GRN_DB_VOID) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_var_size_vector_uvector(grn_ctx *ctx, grn_obj *column,
+ grn_id id, grn_obj *value,
+ int flags)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj uvector;
+ grn_obj_flags uvector_flags = 0;
+ grn_bool need_convert = GRN_FALSE;
+ grn_bool need_cast = GRN_FALSE;
+ grn_id column_range_id;
+ void *raw_value;
+ unsigned int size;
+
+ if (column->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ if (!IS_WEIGHT_UVECTOR(value)) {
+ need_convert = GRN_TRUE;
+ }
+ } else {
+ if (IS_WEIGHT_UVECTOR(value)) {
+ need_convert = GRN_TRUE;
+ uvector_flags = GRN_OBJ_WITH_WEIGHT;
+ }
+ }
+ column_range_id = DB_OBJ(column)->range;
+ if (column_range_id != value->header.domain) {
+ need_convert = GRN_TRUE;
+ need_cast = GRN_TRUE;
+ }
+
+ if (need_convert) {
+ unsigned int i, n;
+
+ GRN_VALUE_FIX_SIZE_INIT(&uvector, GRN_OBJ_VECTOR, column_range_id);
+ uvector.header.flags |= uvector_flags;
+ n = grn_uvector_size(ctx, value);
+ if (need_cast) {
+ grn_obj value_record;
+ grn_obj casted_record;
+
+ GRN_VALUE_FIX_SIZE_INIT(&value_record, 0, value->header.domain);
+ GRN_VALUE_FIX_SIZE_INIT(&casted_record, 0, column_range_id);
+ for (i = 0; i < n; i++) {
+ grn_id id;
+ grn_id casted_id;
+ unsigned int weight = 0;
+
+ GRN_BULK_REWIND(&value_record);
+ GRN_BULK_REWIND(&casted_record);
+
+ id = grn_uvector_get_element(ctx, value, i, NULL);
+ GRN_RECORD_SET(ctx, &value_record, id);
+ rc = grn_obj_cast(ctx, &value_record, &casted_record, GRN_TRUE);
+ if (rc != GRN_SUCCESS) {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ grn_obj inspected;
+ column_name_size = grn_obj_name(ctx,
+ column,
+ column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, &value_record);
+ ERR(rc,
+ "[column][set-value] failed to cast: <%.*s>: <%.*s>",
+ column_name_size,
+ column_name,
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ break;
+ }
+ casted_id = GRN_RECORD_VALUE(&casted_record);
+ grn_uvector_add_element(ctx, &uvector, casted_id, weight);
+ }
+
+ GRN_OBJ_FIN(ctx, &value_record);
+ GRN_OBJ_FIN(ctx, &casted_record);
+ } else {
+ for (i = 0; i < n; i++) {
+ grn_id id;
+ unsigned int weight = 0;
+ id = grn_uvector_get_element(ctx, value, i, NULL);
+ grn_uvector_add_element(ctx, &uvector, id, weight);
+ }
+ }
+ raw_value = GRN_BULK_HEAD(&uvector);
+ size = GRN_BULK_VSIZE(&uvector);
+ } else {
+ raw_value = GRN_BULK_HEAD(value);
+ size = GRN_BULK_VSIZE(value);
+ }
+
+ if (rc == GRN_SUCCESS) {
+ rc = grn_ja_put(ctx, (grn_ja *)column, id, raw_value, size, flags, NULL);
+ }
+
+ if (need_convert) {
+ GRN_OBJ_FIN(ctx, &uvector);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_var_size_vector(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ grn_obj *lexicon = grn_ctx_at(ctx, range);
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ if (ctx->rc) {
+ rc = ctx->rc;
+ }
+ return rc;
+ }
+
+ if (value->header.type == GRN_UVECTOR) {
+ rc = grn_obj_set_value_column_var_size_vector_uvector(ctx, obj,
+ id, value,
+ flags);
+ return rc;
+ }
+
+ if (GRN_OBJ_TABLEP(lexicon)) {
+ grn_obj uvector;
+ GRN_RECORD_INIT(&uvector, GRN_OBJ_VECTOR, range);
+ if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ uvector.header.flags |= GRN_OBJ_WITH_WEIGHT;
+ }
+ switch (value->header.type) {
+ case GRN_BULK :
+ {
+ unsigned int token_flags = 0;
+ grn_token_cursor *token_cursor;
+ if (v && s &&
+ (token_cursor = grn_token_cursor_open(ctx, lexicon, v, s,
+ GRN_TOKEN_ADD, token_flags))) {
+ while (token_cursor->status == GRN_TOKEN_CURSOR_DOING) {
+ grn_id tid = grn_token_cursor_next(ctx, token_cursor);
+ grn_uvector_add_element(ctx, &uvector, tid, 0);
+ }
+ grn_token_cursor_close(ctx, token_cursor);
+ }
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id,
+ GRN_BULK_HEAD(&uvector), GRN_BULK_VSIZE(&uvector),
+ flags, NULL);
+ }
+ break;
+ case GRN_VECTOR :
+ {
+ unsigned int n;
+ n = grn_vector_size(ctx, value);
+ if (n > 0) {
+ unsigned int i;
+ grn_obj value_buf, cast_buf;
+ GRN_OBJ_INIT(&value_buf, GRN_BULK, 0, GRN_DB_VOID);
+ GRN_OBJ_INIT(&cast_buf, GRN_BULK, 0, lexicon->header.domain);
+ for (i = 0; i < n; i++) {
+ grn_id tid;
+ const char *element;
+ unsigned int element_length;
+ unsigned int weight;
+ grn_id element_domain;
+
+ element_length = grn_vector_get_element(ctx, value, i,
+ &element, &weight,
+ &element_domain);
+ if (element_domain != lexicon->header.domain) {
+ GRN_BULK_REWIND(&cast_buf);
+ GRN_BULK_REWIND(&value_buf);
+ grn_bulk_write(ctx, &value_buf, element, element_length);
+ value_buf.header.domain = element_domain;
+ rc = grn_obj_cast(ctx, &value_buf, &cast_buf, GRN_TRUE);
+ if (rc) {
+ grn_obj *range_obj;
+ range_obj = grn_ctx_at(ctx, range);
+ ERR_CAST(obj, range_obj, &value_buf);
+ grn_obj_unlink(ctx, range_obj);
+ } else {
+ element = GRN_BULK_HEAD(&cast_buf);
+ element_length = GRN_BULK_VSIZE(&cast_buf);
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ }
+ if (rc) {
+ continue;
+ }
+ tid = grn_table_add(ctx, lexicon, element, element_length, NULL);
+ grn_uvector_add_element(ctx, &uvector, tid, weight);
+ }
+ GRN_OBJ_FIN(ctx, &value_buf);
+ GRN_OBJ_FIN(ctx, &cast_buf);
+ }
+ }
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id,
+ GRN_BULK_HEAD(&uvector), GRN_BULK_VSIZE(&uvector),
+ flags, NULL);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "vector, uvector or bulk required");
+ break;
+ }
+ grn_obj_close(ctx, &uvector);
+ } else {
+ switch (value->header.type) {
+ case GRN_BULK :
+ if (!GRN_BULK_VSIZE(value)) {
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id, NULL, 0, flags, NULL);
+ } else {
+ grn_obj v;
+ GRN_OBJ_INIT(&v, GRN_VECTOR, GRN_OBJ_DO_SHALLOW_COPY, GRN_DB_TEXT);
+ v.u.v.body = value;
+ grn_vector_delimit(ctx, &v, 0, GRN_ID_NIL);
+ rc = grn_ja_putv(ctx, (grn_ja *)obj, id, &v, 0);
+ grn_obj_close(ctx, &v);
+ }
+ break;
+ case GRN_VECTOR :
+ rc = grn_ja_putv(ctx, (grn_ja *)obj, id, value, 0);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "vector or bulk required");
+ break;
+ }
+ }
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_fix_size(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ grn_obj buf, *value_ = value;
+ uint32_t element_size = ((grn_ra *)obj)->header->element_size;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (range != value->header.domain) {
+ rc = grn_obj_cast(ctx, value, &buf, GRN_TRUE);
+ if (rc) {
+ grn_obj *range_obj;
+ range_obj = grn_ctx_at(ctx, range);
+ ERR_CAST(obj, range_obj, value);
+ grn_obj_unlink(ctx, range_obj);
+ } else {
+ value_ = &buf;
+ v = GRN_BULK_HEAD(&buf);
+ s = GRN_BULK_VSIZE(&buf);
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ }
+ if (rc) {
+ /* do nothing because it already has error. */
+ } else if (element_size < s) {
+ ERR(GRN_INVALID_ARGUMENT, "too long value (%d)", s);
+ } else {
+ void *p = grn_ra_ref(ctx, (grn_ra *)obj, id);
+ if (!p) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "ra get failed");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ return rc;
+ }
+ switch (flags & GRN_OBJ_SET_MASK) {
+ case GRN_OBJ_SET :
+ if (call_hook(ctx, obj, id, value_, flags)) {
+ if (ctx->rc) {
+ rc = ctx->rc;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ return rc;
+ }
+ if (element_size != s) {
+ if (!s) {
+ memset(p, 0, element_size);
+ } else {
+ void *b;
+ if ((b = GRN_CALLOC(element_size))) {
+ grn_memcpy(b, v, s);
+ grn_memcpy(p, b, element_size);
+ GRN_FREE(b);
+ }
+ }
+ } else {
+ grn_memcpy(p, v, s);
+ }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_OBJ_INCR :
+ /* todo : support hook */
+ INCRDECR(+=);
+ break;
+ case GRN_OBJ_DECR :
+ /* todo : support hook */
+ INCRDECR(-=);
+ break;
+ default :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ }
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_index(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ column_name_size = grn_obj_name(ctx, obj, column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "can't set value to index column directly: <%.*s>",
+ column_name_size, column_name);
+ return ctx->rc;
+}
+
+grn_rc
+grn_obj_set_value(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!GRN_DB_OBJP(obj)) {
+ if (obj->header.type == GRN_ACCESSOR) {
+ rc = grn_accessor_set_value(ctx, (grn_accessor *)obj, id, value, flags);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "not db_obj");
+ }
+ } else {
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_obj_set_value_table_pat_key(ctx, obj, id, value, flags);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = grn_obj_set_value_table_hash_key(ctx, obj, id, value, flags);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_obj_set_value_table_no_key(ctx, obj, id, value, flags);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ switch (obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR :
+ rc = grn_obj_set_value_column_var_size_scalar(ctx, obj, id, value,
+ flags);
+ break;
+ case GRN_OBJ_COLUMN_VECTOR :
+ rc = grn_obj_set_value_column_var_size_vector(ctx, obj, id, value,
+ flags);
+ break;
+ default :
+ ERR(GRN_FILE_CORRUPT, "invalid GRN_OBJ_COLUMN_TYPE");
+ break;
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ rc = grn_obj_set_value_column_fix_size(ctx, obj, id, value, flags);
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_obj_set_value_column_index(ctx, obj, id, value, flags);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+const char *
+grn_obj_get_value_(grn_ctx *ctx, grn_obj *obj, grn_id id, uint32_t *size)
+{
+ const char *value = NULL;
+ *size = 0;
+ switch (obj->header.type) {
+ case GRN_ACCESSOR :
+ value = grn_accessor_get_value_(ctx, (grn_accessor *)obj, id, size);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ value = grn_pat_get_value_(ctx, (grn_pat *)obj, id, size);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "GRN_TABLE_DAT_KEY not supported");
+ break;
+ case GRN_TABLE_HASH_KEY :
+ value = grn_hash_get_value_(ctx, (grn_hash *)obj, id, size);
+ break;
+ case GRN_TABLE_NO_KEY :
+ if ((value = _grn_array_get_value(ctx, (grn_array *)obj, id))) {
+ *size = ((grn_array *)obj)->value_size;
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ {
+ grn_io_win jw;
+ if ((value = grn_ja_ref(ctx, (grn_ja *)obj, id, &jw, size))) {
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ if ((value = grn_ra_ref(ctx, (grn_ra *)obj, id))) {
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ *size = ((grn_ra *)obj)->header->element_size;
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "todo: GRN_COLUMN_INDEX");
+ break;
+ }
+ return value;
+}
+
+static void
+grn_obj_get_value_expr(grn_ctx *ctx, grn_obj *expr, grn_id id, grn_obj *value)
+{
+ grn_expr *e = (grn_expr *)expr;
+ grn_expr_code *code;
+
+ if (e->codes_curr != 1) {
+ return;
+ }
+
+ code = e->codes;
+ if (code->op != GRN_OP_GET_VALUE) {
+ return;
+ }
+
+ if (!code->value) {
+ return;
+ }
+
+ switch (code->value->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ grn_obj_get_value(ctx, code->value, id, value);
+ break;
+ default :
+ break;
+ }
+}
+
+static void
+grn_obj_get_value_column_index(grn_ctx *ctx, grn_obj *index_column,
+ grn_id id, grn_obj *value)
+{
+ grn_ii *ii = (grn_ii *)index_column;
+ grn_obj_ensure_bulk(ctx, value);
+ if (id) {
+ GRN_UINT32_SET(ctx, value, grn_ii_estimate_size(ctx, ii, id));
+ } else {
+ GRN_UINT32_SET(ctx, value, 0);
+ }
+ value->header.domain = GRN_DB_UINT32;
+}
+
+static grn_obj *
+grn_obj_get_value_column_vector(grn_ctx *ctx, grn_obj *obj,
+ grn_id id, grn_obj *value)
+{
+ grn_obj *lexicon;
+
+ lexicon = grn_ctx_at(ctx, DB_OBJ(obj)->range);
+ if (lexicon && !GRN_OBJ_TABLEP(lexicon) &&
+ (lexicon->header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ grn_obj_ensure_vector(ctx, value);
+ if (id) {
+ grn_obj v_;
+ GRN_TEXT_INIT(&v_, 0);
+ grn_ja_get_value(ctx, (grn_ja *)obj, id, &v_);
+ grn_vector_decode(ctx, value, GRN_TEXT_VALUE(&v_), GRN_TEXT_LEN(&v_));
+ GRN_OBJ_FIN(ctx, &v_);
+ }
+ } else {
+ grn_obj_ensure_bulk(ctx, value);
+ if (id) {
+ grn_ja_get_value(ctx, (grn_ja *)obj, id, value);
+ }
+ value->header.type = GRN_UVECTOR;
+ if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ value->header.flags |= GRN_OBJ_WITH_WEIGHT;
+ } else {
+ value->header.flags &= ~GRN_OBJ_WITH_WEIGHT;
+ }
+ }
+
+ return value;
+}
+
+grn_obj *
+grn_obj_get_value(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value)
+{
+ GRN_API_ENTER;
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ if (!value) {
+ if (!(value = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ }
+ switch (value->header.type) {
+ case GRN_VOID :
+ grn_obj_reinit(ctx, value, GRN_DB_TEXT, 0);
+ break;
+ case GRN_BULK :
+ case GRN_VECTOR :
+ case GRN_UVECTOR :
+ case GRN_MSG :
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_ACCESSOR :
+ grn_obj_ensure_bulk(ctx, value);
+ value = grn_accessor_get_value(ctx, (grn_accessor *)obj, id, value);
+ break;
+ case GRN_EXPR :
+ grn_obj_get_value_expr(ctx, obj, id, value);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)obj;
+ uint32_t size = pat->value_size;
+ grn_obj_ensure_bulk(ctx, value);
+ if (id) {
+ if (grn_bulk_space(ctx, value, size)) {
+ MERR("grn_bulk_space failed");
+ goto exit;
+ }
+ {
+ char *curr = GRN_BULK_CURR(value);
+ grn_pat_get_value(ctx, pat, id, curr - size);
+ }
+ }
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "GRN_TABLE_DAT_KEY not supported");
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_bool processed = GRN_FALSE;
+ grn_obj_ensure_bulk(ctx, value);
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ if (id) {
+ if (GRN_TABLE_IS_MULTI_KEYS_GROUPED(obj)) {
+ grn_obj *domain;
+ domain = grn_ctx_at(ctx, value->header.domain);
+ if (GRN_OBJ_TABLEP(domain)) {
+ grn_id subrec_id;
+ if (grn_table_get_subrecs(ctx, obj, id, &subrec_id, NULL, 1) == 1) {
+ GRN_RECORD_SET(ctx, value, subrec_id);
+ processed = GRN_TRUE;
+ }
+ }
+ }
+ if (!processed) {
+ grn_hash *hash = (grn_hash *)obj;
+ uint32_t size = hash->value_size;
+ if (grn_bulk_space(ctx, value, size)) {
+ MERR("grn_bulk_space failed");
+ goto exit;
+ }
+ {
+ char *curr = GRN_BULK_CURR(value);
+ grn_hash_get_value(ctx, hash, id, curr - size);
+ }
+ }
+ }
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *array = (grn_array *)obj;
+ uint32_t size = array->value_size;
+ grn_obj_ensure_bulk(ctx, value);
+ if (id) {
+ if (grn_bulk_space(ctx, value, size)) {
+ MERR("grn_bulk_space failed");
+ goto exit;
+ }
+ {
+ char *curr = GRN_BULK_CURR(value);
+ grn_array_get_value(ctx, array, id, curr - size);
+ }
+ }
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ switch (obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_VECTOR :
+ grn_obj_get_value_column_vector(ctx, obj, id, value);
+ break;
+ case GRN_OBJ_COLUMN_SCALAR :
+ grn_obj_ensure_bulk(ctx, value);
+ if (id) {
+ grn_ja_get_value(ctx, (grn_ja *)obj, id, value);
+ }
+ value->header.type = GRN_BULK;
+ break;
+ default :
+ ERR(GRN_FILE_CORRUPT, "invalid GRN_OBJ_COLUMN_TYPE");
+ break;
+ }
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ grn_obj_ensure_bulk(ctx, value);
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ if (id) {
+ unsigned int element_size;
+ void *v = grn_ra_ref(ctx, (grn_ra *)obj, id);
+ if (v) {
+ element_size = ((grn_ra *)obj)->header->element_size;
+ grn_bulk_write(ctx, value, v, element_size);
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ }
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ grn_obj_get_value_column_index(ctx, obj, id, value);
+ break;
+ }
+exit :
+ GRN_API_RETURN(value);
+}
+
+int
+grn_obj_get_values(grn_ctx *ctx, grn_obj *obj, grn_id offset, void **values)
+{
+ int nrecords = -1;
+ GRN_API_ENTER;
+ if (obj->header.type == GRN_COLUMN_FIX_SIZE) {
+ grn_obj *domain = grn_column_table(ctx, obj);
+ if (domain) {
+ int table_size = (int)grn_table_size(ctx, domain);
+ if (0 < offset && offset <= (grn_id) table_size) {
+ grn_ra *ra = (grn_ra *)obj;
+ void *p = grn_ra_ref(ctx, ra, offset);
+ if (p) {
+ if ((offset >> ra->element_width) == ((unsigned int) table_size >> ra->element_width)) {
+ nrecords = (table_size & ra->element_mask) + 1 - (offset & ra->element_mask);
+ } else {
+ nrecords = ra->element_mask + 1 - (offset & ra->element_mask);
+ }
+ if (values) { *values = p; }
+ grn_ra_unref(ctx, ra, offset);
+ } else {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "ra get failed");
+ }
+ } else {
+ nrecords = 0;
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "no domain found");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "obj is not a fix sized column");
+ }
+ GRN_API_RETURN(nrecords);
+}
+
+grn_rc
+grn_column_index_update(grn_ctx *ctx, grn_obj *column,
+ grn_id id, unsigned int section,
+ grn_obj *oldvalue, grn_obj *newvalue)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (column->header.type != GRN_COLUMN_INDEX) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid column assigned");
+ } else {
+ rc = grn_ii_column_update(ctx, (grn_ii *)column, id, section, oldvalue, newvalue, NULL);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_column_table(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj *obj = NULL;
+ grn_db_obj *col = DB_OBJ(column);
+ GRN_API_ENTER;
+ if (col) {
+ obj = grn_ctx_at(ctx, col->header.domain);
+ }
+ GRN_API_RETURN(obj);
+}
+
+grn_obj *
+grn_obj_get_info(grn_ctx *ctx, grn_obj *obj, grn_info_type type, grn_obj *valuebuf)
+{
+ GRN_API_ENTER;
+ switch (type) {
+ case GRN_INFO_SUPPORT_ZLIB :
+ if (!valuebuf && !(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_BOOL))) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "failed to open value buffer for GRN_INFO_ZLIB_SUPPORT");
+ goto exit;
+ }
+#ifdef GRN_WITH_ZLIB
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_TRUE);
+#else
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_FALSE);
+#endif
+ break;
+ case GRN_INFO_SUPPORT_LZ4 :
+ if (!valuebuf && !(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_BOOL))) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "failed to open value buffer for GRN_INFO_LZ4_SUPPORT");
+ goto exit;
+ }
+#ifdef GRN_WITH_LZ4
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_TRUE);
+#else /* GRN_WITH_LZ4 */
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_FALSE);
+#endif /* GRN_WITH_LZ4 */
+ break;
+ case GRN_INFO_SUPPORT_ZSTD :
+ if (!valuebuf && !(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_BOOL))) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "failed to open value buffer for GRN_INFO_ZSTD_SUPPORT");
+ goto exit;
+ }
+#ifdef GRN_WITH_ZSTD
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_TRUE);
+#else /* GRN_WITH_ZSTD */
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_FALSE);
+#endif /* GRN_WITH_ZSTD */
+ break;
+ case GRN_INFO_SUPPORT_ARROW :
+ if (!valuebuf && !(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_BOOL))) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "failed to open value buffer for GRN_INFO_ARROW_SUPPORT");
+ goto exit;
+ }
+#ifdef GRN_WITH_ARROW
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_TRUE);
+#else /* GRN_WITH_ARROW */
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_FALSE);
+#endif /* GRN_WITH_ARROW */
+ break;
+ default :
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ goto exit;
+ }
+ switch (type) {
+ case GRN_INFO_ENCODING :
+ if (!valuebuf) {
+ if (!(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ goto exit;
+ }
+ }
+ {
+ grn_encoding enc;
+ if (obj->header.type == GRN_DB) { obj = ((grn_db *)obj)->keys; }
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ enc = ((grn_pat *)obj)->encoding;
+ grn_bulk_write(ctx, valuebuf, (const char *)&enc, sizeof(grn_encoding));
+ break;
+ case GRN_TABLE_DAT_KEY :
+ enc = ((grn_dat *)obj)->encoding;
+ grn_bulk_write(ctx, valuebuf, (const char *)&enc, sizeof(grn_encoding));
+ break;
+ case GRN_TABLE_HASH_KEY :
+ enc = ((grn_hash *)obj)->encoding;
+ grn_bulk_write(ctx, valuebuf, (const char *)&enc, sizeof(grn_encoding));
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ }
+ }
+ break;
+ case GRN_INFO_SOURCE :
+ if (!valuebuf) {
+ if (!(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ goto exit;
+ }
+ }
+ if (!GRN_DB_OBJP(obj)) {
+ ERR(GRN_INVALID_ARGUMENT, "only db_obj can accept GRN_INFO_SOURCE");
+ goto exit;
+ }
+ grn_bulk_write(ctx, valuebuf, DB_OBJ(obj)->source, DB_OBJ(obj)->source_size);
+ break;
+ case GRN_INFO_DEFAULT_TOKENIZER :
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ valuebuf = ((grn_hash *)obj)->tokenizer;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ valuebuf = ((grn_pat *)obj)->tokenizer;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ valuebuf = ((grn_dat *)obj)->tokenizer;
+ break;
+ }
+ break;
+ case GRN_INFO_NORMALIZER :
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ valuebuf = ((grn_hash *)obj)->normalizer;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ valuebuf = ((grn_pat *)obj)->normalizer;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ valuebuf = ((grn_dat *)obj)->normalizer;
+ break;
+ }
+ break;
+ case GRN_INFO_TOKEN_FILTERS :
+ if (!valuebuf) {
+ if (!(valuebuf = grn_obj_open(ctx, GRN_PVECTOR, 0, 0))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "grn_obj_get_info: failed to allocate value buffer");
+ goto exit;
+ }
+ }
+ {
+ grn_obj *token_filters = NULL;
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ token_filters = &(((grn_hash *)obj)->token_filters);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ token_filters = &(((grn_pat *)obj)->token_filters);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ token_filters = &(((grn_dat *)obj)->token_filters);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT,
+ /* TODO: Show type name instead of type ID */
+ "[info][get][token-filters] target object must be one of "
+ "GRN_TABLE_HASH_KEY, GRN_TABLE_PAT_KEY and GRN_TABLE_DAT_KEY: %d",
+ obj->header.type);
+ break;
+ }
+ if (token_filters) {
+ grn_bulk_write(ctx,
+ valuebuf,
+ GRN_BULK_HEAD(token_filters),
+ GRN_BULK_VSIZE(token_filters));
+ }
+ }
+ break;
+ default :
+ /* todo */
+ break;
+ }
+ }
+exit :
+ GRN_API_RETURN(valuebuf);
+}
+
+static void
+update_source_hook(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id *s = DB_OBJ(obj)->source;
+ int i, n = DB_OBJ(obj)->source_size / sizeof(grn_id);
+ grn_obj_default_set_value_hook_data hook_data = { DB_OBJ(obj)->id, 0 };
+ grn_obj *source, data;
+ GRN_TEXT_INIT(&data, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&data, &hook_data, sizeof(hook_data));
+ for (i = 1; i <= n; i++, s++) {
+ hook_data.section = i;
+ if ((source = grn_ctx_at(ctx, *s))) {
+ switch (source->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ grn_obj_add_hook(ctx, source, GRN_HOOK_INSERT, 0, NULL, &data);
+ grn_obj_add_hook(ctx, source, GRN_HOOK_DELETE, 0, NULL, &data);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_INDEX :
+ grn_obj_add_hook(ctx, source, GRN_HOOK_SET, 0, NULL, &data);
+ break;
+ default :
+ /* invalid target */
+ break;
+ }
+ }
+ }
+ grn_obj_close(ctx, &data);
+}
+
+static void
+del_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry, grn_obj *hld)
+{
+ int i;
+ void *hld_value = NULL;
+ uint32_t hld_size = 0;
+ grn_hook **last;
+ hld_value = GRN_BULK_HEAD(hld);
+ hld_size = GRN_BULK_VSIZE(hld);
+ if (!hld_size) { return; }
+ for (i = 0, last = &DB_OBJ(obj)->hooks[entry]; *last; i++, last = &(*last)->next) {
+ if (!memcmp(GRN_NEXT_ADDR(*last), hld_value, hld_size)) {
+ grn_obj_delete_hook(ctx, obj, entry, i);
+ return;
+ }
+ }
+}
+
+static void
+delete_source_hook(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id *s = DB_OBJ(obj)->source;
+ int i, n = DB_OBJ(obj)->source_size / sizeof(grn_id);
+ grn_obj_default_set_value_hook_data hook_data = { DB_OBJ(obj)->id, 0 };
+ grn_obj *source, data;
+ GRN_TEXT_INIT(&data, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&data, &hook_data, sizeof(hook_data));
+ for (i = 1; i <= n; i++, s++) {
+ hook_data.section = i;
+
+ source = grn_ctx_at(ctx, *s);
+ if (!source) {
+ ERRCLR(ctx);
+ continue;
+ }
+
+ switch (source->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ del_hook(ctx, source, GRN_HOOK_INSERT, &data);
+ del_hook(ctx, source, GRN_HOOK_DELETE, &data);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ del_hook(ctx, source, GRN_HOOK_SET, &data);
+ break;
+ default :
+ /* invalid target */
+ break;
+ }
+ }
+ grn_obj_close(ctx, &data);
+}
+
+#define N_HOOK_ENTRIES 5
+
+grn_rc
+grn_hook_pack(grn_ctx *ctx, grn_db_obj *obj, grn_obj *buf)
+{
+ grn_rc rc;
+ grn_hook_entry e;
+ for (e = 0; e < N_HOOK_ENTRIES; e++) {
+ grn_hook *hooks;
+ for (hooks = obj->hooks[e]; hooks; hooks = hooks->next) {
+ grn_id id = hooks->proc ? hooks->proc->obj.id : 0;
+ if ((rc = grn_text_benc(ctx, buf, id + 1))) { goto exit; }
+ if ((rc = grn_text_benc(ctx, buf, hooks->hld_size))) { goto exit; }
+ if ((rc = grn_bulk_write(ctx, buf, (char *)GRN_NEXT_ADDR(hooks), hooks->hld_size))) { goto exit; }
+ }
+ if ((rc = grn_text_benc(ctx, buf, 0))) { goto exit; }
+ }
+exit :
+ return rc;
+}
+
+static grn_rc
+grn_hook_unpack(grn_ctx *ctx, grn_db_obj *obj, const char *buf, uint32_t buf_size)
+{
+ grn_hook_entry e;
+ const uint8_t *p = (uint8_t *)buf, *pe = p + buf_size;
+ for (e = 0; e < N_HOOK_ENTRIES; e++) {
+ grn_hook *new, **last = &obj->hooks[e];
+ for (;;) {
+ grn_id id;
+ uint32_t hld_size;
+ GRN_B_DEC(id, p);
+ if (!id--) { break; }
+ if (p >= pe) { return GRN_FILE_CORRUPT; }
+ GRN_B_DEC(hld_size, p);
+ if (p >= pe) { return GRN_FILE_CORRUPT; }
+ if (!(new = GRN_MALLOC(sizeof(grn_hook) + hld_size))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (id) {
+ new->proc = (grn_proc *)grn_ctx_at(ctx, id);
+ if (!new->proc) {
+ GRN_FREE(new);
+ return ctx->rc;
+ }
+ } else {
+ new->proc = NULL;
+ }
+ if ((new->hld_size = hld_size)) {
+ grn_memcpy(GRN_NEXT_ADDR(new), p, hld_size);
+ p += hld_size;
+ }
+ *last = new;
+ last = &new->next;
+ if (p >= pe) { return GRN_FILE_CORRUPT; }
+ }
+ *last = NULL;
+ }
+ return GRN_SUCCESS;
+}
+
+static void
+grn_token_filters_pack(grn_ctx *ctx,
+ grn_obj *token_filters,
+ grn_obj *buffer)
+{
+ unsigned int i, n_token_filters;
+
+ n_token_filters = GRN_BULK_VSIZE(token_filters) / sizeof(grn_obj *);
+ for (i = 0; i < n_token_filters; i++) {
+ grn_obj *token_filter = GRN_PTR_VALUE_AT(token_filters, i);
+ grn_id token_filter_id;
+
+ token_filter_id = grn_obj_id(ctx, token_filter);
+ GRN_RECORD_PUT(ctx, buffer, token_filter_id);
+ }
+}
+
+static grn_bool
+grn_obj_encoded_spec_equal(grn_ctx *ctx,
+ grn_obj *encoded_spec1,
+ grn_obj *encoded_spec2)
+{
+ unsigned int i, n_elements;
+
+ if (encoded_spec1->header.type != GRN_VECTOR) {
+ return GRN_FALSE;
+ }
+
+ if (encoded_spec1->header.type != encoded_spec2->header.type) {
+ return GRN_FALSE;
+ }
+
+ n_elements = grn_vector_size(ctx, encoded_spec1);
+ if (grn_vector_size(ctx, encoded_spec2) != n_elements) {
+ return GRN_FALSE;
+ }
+
+ for (i = 0; i < n_elements; i++) {
+ const char *content1;
+ const char *content2;
+ unsigned int content_size1;
+ unsigned int content_size2;
+ unsigned int weight1;
+ unsigned int weight2;
+ grn_id domain1;
+ grn_id domain2;
+
+ content_size1 = grn_vector_get_element(ctx,
+ encoded_spec1,
+ i,
+ &content1,
+ &weight1,
+ &domain1);
+ content_size2 = grn_vector_get_element(ctx,
+ encoded_spec2,
+ i,
+ &content2,
+ &weight2,
+ &domain2);
+ if (content_size1 != content_size2) {
+ return GRN_FALSE;
+ }
+ if (memcmp(content1, content2, content_size1) != 0) {
+ return GRN_FALSE;
+ }
+ if (weight1 != weight2) {
+ return GRN_FALSE;
+ }
+ if (domain1 != domain2) {
+ return GRN_FALSE;
+ }
+ }
+
+ return GRN_TRUE;
+}
+
+void
+grn_obj_spec_save(grn_ctx *ctx, grn_db_obj *obj)
+{
+ grn_db *s;
+ grn_obj v, *b;
+ grn_obj_spec spec;
+ grn_bool need_update = GRN_TRUE;
+
+ if (obj->id & GRN_OBJ_TMP_OBJECT) { return; }
+ if (!ctx->impl || !GRN_DB_OBJP(obj)) { return; }
+ if (!(s = (grn_db *)ctx->impl->db) || !s->specs) { return; }
+ if (obj->header.type == GRN_PROC && obj->range == GRN_ID_NIL) {
+ return;
+ }
+ GRN_OBJ_INIT(&v, GRN_VECTOR, 0, GRN_DB_TEXT);
+ if (!(b = grn_vector_body(ctx, &v))) { return; }
+ spec.header = obj->header;
+ spec.range = obj->range;
+ grn_bulk_write(ctx, b, (void *)&spec, sizeof(grn_obj_spec));
+ grn_vector_delimit(ctx, &v, 0, 0);
+ if (obj->header.flags & GRN_OBJ_CUSTOM_NAME) {
+ GRN_TEXT_PUTS(ctx, b, grn_obj_path(ctx, (grn_obj *)obj));
+ }
+ grn_vector_delimit(ctx, &v, 0, 0);
+ grn_bulk_write(ctx, b, obj->source, obj->source_size);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ grn_hook_pack(ctx, obj, b);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ grn_token_filters_pack(ctx, &(((grn_hash *)obj)->token_filters), b);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ grn_token_filters_pack(ctx, &(((grn_pat *)obj)->token_filters), b);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ grn_token_filters_pack(ctx, &(((grn_dat *)obj)->token_filters), b);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ break;
+ case GRN_EXPR :
+ grn_expr_pack(ctx, b, (grn_obj *)obj);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ break;
+ }
+
+ {
+ grn_io_win jw;
+ uint32_t current_spec_raw_len;
+ char *current_spec_raw;
+
+ current_spec_raw = grn_ja_ref(ctx,
+ s->specs,
+ obj->id,
+ &jw,
+ &current_spec_raw_len);
+ if (current_spec_raw) {
+ grn_rc rc;
+ grn_obj current_spec;
+
+ GRN_OBJ_INIT(&current_spec, GRN_VECTOR, 0, GRN_DB_TEXT);
+ rc = grn_vector_decode(ctx,
+ &current_spec,
+ current_spec_raw,
+ current_spec_raw_len);
+ if (rc == GRN_SUCCESS) {
+ need_update = !grn_obj_encoded_spec_equal(ctx, &v, &current_spec);
+ }
+ GRN_OBJ_FIN(ctx, &current_spec);
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+
+ if (!need_update) {
+ grn_obj_close(ctx, &v);
+ return;
+ }
+
+ {
+ const char *name;
+ uint32_t name_size = 0;
+ const char *range_name = NULL;
+ uint32_t range_name_size = 0;
+
+ name = _grn_table_key(ctx, s->keys, obj->id, &name_size);
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_INDEX :
+ if (obj->range != GRN_ID_NIL) {
+ range_name = _grn_table_key(ctx, s->keys, obj->range, &range_name_size);
+ }
+ break;
+ default :
+ break;
+ }
+ /* TODO: reduce log level. */
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "spec:%u:update:%.*s:%u(%s):%u%s%.*s%s",
+ obj->id,
+ name_size, name,
+ obj->header.type,
+ grn_obj_type_to_string(obj->header.type),
+ obj->range,
+ range_name_size == 0 ? "" : "(",
+ range_name_size, range_name,
+ range_name_size == 0 ? "" : ")");
+ }
+ grn_ja_putv(ctx, s->specs, obj->id, &v, 0);
+ grn_obj_close(ctx, &v);
+}
+
+inline static void
+grn_obj_set_info_source_invalid_lexicon_error(grn_ctx *ctx,
+ const char *message,
+ grn_obj *actual_type,
+ grn_obj *expected_type,
+ grn_obj *index_column,
+ grn_obj *source)
+{
+ char actual_type_name[GRN_TABLE_MAX_KEY_SIZE];
+ int actual_type_name_size;
+ char expected_type_name[GRN_TABLE_MAX_KEY_SIZE];
+ int expected_type_name_size;
+ char index_column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int index_column_name_size;
+ char source_name[GRN_TABLE_MAX_KEY_SIZE];
+ int source_name_size;
+
+ actual_type_name_size = grn_obj_name(ctx, actual_type,
+ actual_type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ expected_type_name_size = grn_obj_name(ctx, expected_type,
+ expected_type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ index_column_name_size = grn_obj_name(ctx, index_column,
+ index_column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+
+ source_name_size = grn_obj_name(ctx, source,
+ source_name, GRN_TABLE_MAX_KEY_SIZE);
+ if (grn_obj_is_table(ctx, source)) {
+ source_name[source_name_size] = '\0';
+ grn_strncat(source_name,
+ GRN_TABLE_MAX_KEY_SIZE,
+ "._key",
+ GRN_TABLE_MAX_KEY_SIZE - source_name_size - 1);
+ source_name_size = strlen(source_name);
+ }
+
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][index][source] %s: "
+ "<%.*s> -> <%.*s>: "
+ "index-column:<%.*s> "
+ "source:<%.*s>",
+ message,
+ actual_type_name_size, actual_type_name,
+ expected_type_name_size, expected_type_name,
+ index_column_name_size, index_column_name,
+ source_name_size, source_name);
+}
+
+inline static grn_rc
+grn_obj_set_info_source_validate(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ grn_id lexicon_id;
+ grn_obj *lexicon = NULL;
+ grn_id lexicon_domain_id;
+ grn_obj *lexicon_domain = NULL;
+ grn_bool lexicon_domain_is_table;
+ grn_bool lexicon_have_tokenizer;
+ grn_id *source_ids;
+ int i, n_source_ids;
+
+ lexicon_id = obj->header.domain;
+ lexicon = grn_ctx_at(ctx, lexicon_id);
+ if (!lexicon) {
+ goto exit;
+ }
+
+ lexicon_domain_id = lexicon->header.domain;
+ lexicon_domain = grn_ctx_at(ctx, lexicon_domain_id);
+ if (!lexicon_domain) {
+ goto exit;
+ }
+
+ source_ids = (grn_id *)GRN_BULK_HEAD(value);
+ n_source_ids = GRN_BULK_VSIZE(value) / sizeof(grn_id);
+ if (n_source_ids > 1 && !(obj->header.flags & GRN_OBJ_WITH_SECTION)) {
+ char index_name[GRN_TABLE_MAX_KEY_SIZE];
+ int index_name_size;
+ index_name_size = grn_obj_name(ctx, obj,
+ index_name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "grn_obj_set_info(): GRN_INFO_SOURCE: "
+ "multi column index must be created with WITH_SECTION flag: <%.*s>",
+ index_name_size, index_name);
+ goto exit;
+ }
+
+ lexicon_domain_is_table = grn_obj_is_table(ctx, lexicon_domain);
+ {
+ grn_obj *tokenizer;
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL, NULL);
+ lexicon_have_tokenizer = (tokenizer != NULL);
+ }
+
+ for (i = 0; i < n_source_ids; i++) {
+ grn_id source_id = source_ids[i];
+ grn_obj *source;
+ grn_id source_type_id;
+ grn_obj *source_type;
+
+ source = grn_ctx_at(ctx, source_id);
+ if (!source) {
+ continue;
+ }
+ if (grn_obj_is_table(ctx, source)) {
+ source_type_id = source->header.domain;
+ } else {
+ source_type_id = DB_OBJ(source)->range;
+ }
+ source_type = grn_ctx_at(ctx, source_type_id);
+ if (!lexicon_have_tokenizer) {
+ if (grn_obj_is_table(ctx, source_type)) {
+ if (lexicon_id != source_type_id) {
+ grn_obj_set_info_source_invalid_lexicon_error(
+ ctx,
+ "index table must equal to source type",
+ lexicon,
+ source_type,
+ obj,
+ source);
+ }
+ } else {
+ if (!(lexicon_domain_id == source_type_id ||
+ (grn_type_id_is_text_family(ctx, lexicon_domain_id) &&
+ grn_type_id_is_text_family(ctx, source_type_id)))) {
+ grn_obj_set_info_source_invalid_lexicon_error(
+ ctx,
+ "index table's key must equal source type",
+ lexicon_domain,
+ source_type,
+ obj,
+ source);
+ }
+ }
+ }
+ grn_obj_unlink(ctx, source);
+ if (ctx->rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ }
+
+exit:
+ if (lexicon) {
+ grn_obj_unlink(ctx, lexicon);
+ }
+ if (lexicon_domain) {
+ grn_obj_unlink(ctx, lexicon_domain);
+ }
+ return ctx->rc;
+}
+
+inline static void
+grn_obj_set_info_source_log(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ grn_obj buf;
+ grn_id *vp = (grn_id *)GRN_BULK_HEAD(value);
+ uint32_t vs = GRN_BULK_VSIZE(value), s = 0;
+ grn_id id;
+ const char *n;
+
+ id = DB_OBJ(obj)->id;
+ n = _grn_table_key(ctx, ctx->impl->db, id, &s);
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, n, s);
+ GRN_TEXT_PUTC(ctx, &buf, ' ');
+ while (vs) {
+ n = _grn_table_key(ctx, ctx->impl->db, *vp++, &s);
+ GRN_TEXT_PUT(ctx, &buf, n, s);
+ vs -= sizeof(grn_id);
+ if (vs) { GRN_TEXT_PUTC(ctx, &buf, ','); }
+ }
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "DDL:%u:set_source %.*s",
+ id,
+ (int)GRN_BULK_VSIZE(&buf), GRN_BULK_HEAD(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+inline static grn_rc
+grn_obj_set_info_source_update(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ void *v = GRN_BULK_HEAD(value);
+ uint32_t s = GRN_BULK_VSIZE(value);
+ if (s) {
+ void *v2 = GRN_MALLOC(s);
+ if (!v2) {
+ return ctx->rc;
+ }
+ grn_memcpy(v2, v, s);
+ if (DB_OBJ(obj)->source) { GRN_FREE(DB_OBJ(obj)->source); }
+ DB_OBJ(obj)->source = v2;
+ DB_OBJ(obj)->source_size = s;
+
+ if (obj->header.type == GRN_COLUMN_INDEX) {
+ update_source_hook(ctx, obj);
+ grn_index_column_build(ctx, obj);
+ }
+ } else {
+ DB_OBJ(obj)->source = NULL;
+ DB_OBJ(obj)->source_size = 0;
+ }
+
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+grn_obj_set_info_source(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ grn_rc rc;
+
+ rc = grn_obj_set_info_source_validate(ctx, obj, value);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ grn_obj_set_info_source_log(ctx, obj, value);
+ rc = grn_obj_set_info_source_update(ctx, obj, value);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ grn_obj_spec_save(ctx, DB_OBJ(obj));
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_info_token_filters(grn_ctx *ctx,
+ grn_obj *table,
+ grn_obj *token_filters)
+{
+ grn_obj *current_token_filters;
+ unsigned int i, n_current_token_filters, n_token_filters;
+ grn_obj token_filter_names;
+
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ current_token_filters = &(((grn_hash *)table)->token_filters);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ current_token_filters = &(((grn_pat *)table)->token_filters);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ current_token_filters = &(((grn_dat *)table)->token_filters);
+ break;
+ default :
+ /* TODO: Show type name instead of type ID */
+ ERR(GRN_INVALID_ARGUMENT,
+ "[info][set][token-filters] target object must be one of "
+ "GRN_TABLE_HASH_KEY, GRN_TABLE_PAT_KEY and GRN_TABLE_DAT_KEY: %d",
+ table->header.type);
+ return ctx->rc;
+ }
+
+ n_current_token_filters =
+ GRN_BULK_VSIZE(current_token_filters) / sizeof(grn_obj *);
+ n_token_filters = GRN_BULK_VSIZE(token_filters) / sizeof(grn_obj *);
+
+ GRN_TEXT_INIT(&token_filter_names, 0);
+ GRN_BULK_REWIND(current_token_filters);
+ for (i = 0; i < n_token_filters; i++) {
+ grn_obj *token_filter = GRN_PTR_VALUE_AT(token_filters, i);
+ char token_filter_name[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int token_filter_name_size;
+
+ GRN_PTR_PUT(ctx, current_token_filters, token_filter);
+
+ if (i > 0) {
+ GRN_TEXT_PUTC(ctx, &token_filter_names, ',');
+ }
+ token_filter_name_size = grn_obj_name(ctx,
+ token_filter,
+ token_filter_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_PUT(ctx,
+ &token_filter_names,
+ token_filter_name,
+ token_filter_name_size);
+ }
+ if (n_token_filters > 0 || n_token_filters != n_current_token_filters) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "DDL:%u:set_token_filters %.*s",
+ DB_OBJ(table)->id,
+ (int)GRN_BULK_VSIZE(&token_filter_names),
+ GRN_BULK_HEAD(&token_filter_names));
+ }
+ GRN_OBJ_FIN(ctx, &token_filter_names);
+ grn_obj_spec_save(ctx, DB_OBJ(table));
+
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_obj_set_info(grn_ctx *ctx, grn_obj *obj, grn_info_type type, grn_obj *value)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_set_info failed");
+ goto exit;
+ }
+ switch (type) {
+ case GRN_INFO_SOURCE :
+ if (!GRN_DB_OBJP(obj)) {
+ ERR(GRN_INVALID_ARGUMENT, "only db_obj can accept GRN_INFO_SOURCE");
+ goto exit;
+ }
+ rc = grn_obj_set_info_source(ctx, obj, value);
+ break;
+ case GRN_INFO_DEFAULT_TOKENIZER :
+ if (!value || DB_OBJ(value)->header.type == GRN_PROC) {
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ ((grn_hash *)obj)->tokenizer = value;
+ ((grn_hash *)obj)->header.common->tokenizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ ((grn_pat *)obj)->tokenizer = value;
+ ((grn_pat *)obj)->header->tokenizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ((grn_dat *)obj)->tokenizer = value;
+ ((grn_dat *)obj)->header->tokenizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ }
+ }
+ break;
+ case GRN_INFO_NORMALIZER :
+ if (!value || DB_OBJ(value)->header.type == GRN_PROC) {
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ ((grn_hash *)obj)->normalizer = value;
+ ((grn_hash *)obj)->header.common->normalizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ ((grn_pat *)obj)->normalizer = value;
+ ((grn_pat *)obj)->header->normalizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ((grn_dat *)obj)->normalizer = value;
+ ((grn_dat *)obj)->header->normalizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ }
+ }
+ break;
+ case GRN_INFO_TOKEN_FILTERS :
+ rc = grn_obj_set_info_token_filters(ctx, obj, value);
+ break;
+ default :
+ /* todo */
+ break;
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_obj_get_element_info(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_info_type type, grn_obj *valuebuf)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(valuebuf);
+}
+
+grn_rc
+grn_obj_set_element_info(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_info_type type, grn_obj *value)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+static void
+grn_hook_free(grn_ctx *ctx, grn_hook *h)
+{
+ grn_hook *curr, *next;
+ for (curr = h; curr; curr = next) {
+ next = curr->next;
+ GRN_FREE(curr);
+ }
+}
+
+grn_rc
+grn_obj_add_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry,
+ int offset, grn_obj *proc, grn_obj *hld)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (!GRN_DB_OBJP(obj)) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ int i;
+ void *hld_value = NULL;
+ uint32_t hld_size = 0;
+ grn_hook *new, **last = &DB_OBJ(obj)->hooks[entry];
+ if (hld) {
+ hld_value = GRN_BULK_HEAD(hld);
+ hld_size = GRN_BULK_VSIZE(hld);
+ }
+ if (!(new = GRN_MALLOC(sizeof(grn_hook) + hld_size))) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ new->proc = (grn_proc *)proc;
+ new->hld_size = hld_size;
+ if (hld_size) {
+ grn_memcpy(GRN_NEXT_ADDR(new), hld_value, hld_size);
+ }
+ for (i = 0; i != offset && *last; i++) { last = &(*last)->next; }
+ new->next = *last;
+ *last = new;
+ grn_obj_spec_save(ctx, DB_OBJ(obj));
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+int
+grn_obj_get_nhooks(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry)
+{
+ int res = 0;
+ GRN_API_ENTER;
+ {
+ grn_hook *hook = DB_OBJ(obj)->hooks[entry];
+ while (hook) {
+ res++;
+ hook = hook->next;
+ }
+ }
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_obj_get_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry,
+ int offset, grn_obj *hldbuf)
+{
+ grn_obj *res = NULL;
+ GRN_API_ENTER;
+ {
+ int i;
+ grn_hook *hook = DB_OBJ(obj)->hooks[entry];
+ for (i = 0; i < offset; i++) {
+ hook = hook->next;
+ if (!hook) { return NULL; }
+ }
+ res = (grn_obj *)hook->proc;
+ grn_bulk_write(ctx, hldbuf, (char *)GRN_NEXT_ADDR(hook), hook->hld_size);
+ }
+ GRN_API_RETURN(res);
+}
+
+grn_rc
+grn_obj_delete_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry, int offset)
+{
+ GRN_API_ENTER;
+ {
+ int i = 0;
+ grn_hook *h, **last = &DB_OBJ(obj)->hooks[entry];
+ for (;;) {
+ if (!(h = *last)) { return GRN_INVALID_ARGUMENT; }
+ if (++i > offset) { break; }
+ last = &h->next;
+ }
+ *last = h->next;
+ GRN_FREE(h);
+ }
+ grn_obj_spec_save(ctx, DB_OBJ(obj));
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+static grn_rc
+remove_index(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_hook *h0, *hooks = DB_OBJ(obj)->hooks[entry];
+ DB_OBJ(obj)->hooks[entry] = NULL; /* avoid mutual recursive call */
+ while (hooks) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (!target) {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int length;
+ char hook_name[GRN_TABLE_MAX_KEY_SIZE];
+ int hook_name_length;
+
+ length = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE);
+ hook_name_length = grn_table_get_key(ctx,
+ ctx->impl->db,
+ data->target,
+ hook_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_OBJECT_CORRUPT,
+ "[column][remove][index] "
+ "hook has a dangling reference: <%.*s> -> <%.*s>",
+ length, name,
+ hook_name_length, hook_name);
+ rc = ctx->rc;
+ } else if (target->header.type == GRN_COLUMN_INDEX) {
+ //TODO: multicolumn MULTI_COLUMN_INDEXP
+ rc = _grn_obj_remove(ctx, target, GRN_FALSE);
+ } else {
+ //TODO: err
+ char fn[GRN_TABLE_MAX_KEY_SIZE];
+ int flen;
+ flen = grn_obj_name(ctx, target, fn, GRN_TABLE_MAX_KEY_SIZE);
+ fn[flen] = '\0';
+ ERR(GRN_UNKNOWN_ERROR, "column has unsupported hooks, col=%s",fn);
+ rc = ctx->rc;
+ }
+ if (rc != GRN_SUCCESS) {
+ DB_OBJ(obj)->hooks[entry] = hooks;
+ break;
+ }
+ h0 = hooks;
+ hooks = hooks->next;
+ GRN_FREE(h0);
+ }
+ return rc;
+}
+
+static grn_rc
+remove_columns(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ GRN_HASH_EACH_BEGIN(ctx, cols, cursor, id) {
+ grn_id *key;
+ grn_obj *col;
+
+ grn_hash_cursor_get_key(ctx, cursor, (void **)&key);
+ col = grn_ctx_at(ctx, *key);
+
+ if (!col) {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_size;
+ name_size = grn_table_get_key(ctx, ctx->impl->db, *key,
+ name, GRN_TABLE_MAX_KEY_SIZE);
+ if (ctx->rc == GRN_SUCCESS) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[object][remove] column is broken: <%.*s>",
+ name_size, name);
+ } else {
+ ERR(ctx->rc,
+ "[object][remove] column is broken: <%.*s>: %s",
+ name_size, name,
+ ctx->errbuf);
+ }
+ rc = ctx->rc;
+ break;
+ }
+
+ rc = _grn_obj_remove(ctx, col, GRN_FALSE);
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, col);
+ break;
+ }
+ } GRN_HASH_EACH_END(ctx, cursor);
+ }
+ grn_hash_close(ctx, cols);
+ }
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_db_index_columns(grn_ctx *ctx, grn_obj *db)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+ if (obj && obj->header.type == GRN_COLUMN_INDEX) {
+ rc = _grn_obj_remove(ctx, obj, GRN_FALSE);
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, obj);
+ break;
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_db_reference_columns(grn_ctx *ctx, grn_obj *db)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+ grn_obj *range = NULL;
+
+ if (!obj) {
+ continue;
+ }
+
+ switch (obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ if (!DB_OBJ(obj)->range) {
+ break;
+ }
+
+ range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
+ if (!range) {
+ break;
+ }
+
+ switch (range->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ rc = _grn_obj_remove(ctx, obj, GRN_FALSE);
+ break;
+ }
+ break;
+ }
+
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_db_reference_tables(grn_ctx *ctx, grn_obj *db)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+ grn_obj *domain = NULL;
+
+ if (!obj) {
+ continue;
+ }
+
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ if (!obj->header.domain) {
+ break;
+ }
+
+ domain = grn_ctx_at(ctx, obj->header.domain);
+ if (!domain) {
+ break;
+ }
+
+ switch (domain->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ rc = _grn_obj_remove(ctx, obj, GRN_FALSE);
+ break;
+ }
+ break;
+ }
+
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_db_all_tables(grn_ctx *ctx, grn_obj *db)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+
+ if (!obj) {
+ continue;
+ }
+
+ switch (obj->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ rc = _grn_obj_remove(ctx, obj, GRN_FALSE);
+ break;
+ }
+
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_db(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_rc rc = GRN_SUCCESS;
+ const char *io_spath;
+ char *spath;
+ grn_db *s = (grn_db *)db;
+ unsigned char key_type;
+
+ rc = _grn_obj_remove_db_index_columns(ctx, db);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_db_reference_columns(ctx, db);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_db_reference_tables(ctx, db);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_db_all_tables(ctx, db);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (s->specs &&
+ (io_spath = grn_obj_path(ctx, (grn_obj *)s->specs)) && *io_spath != '\0') {
+ if (!(spath = GRN_STRDUP(io_spath))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_spath);
+ return ctx->rc;
+ }
+ } else {
+ spath = NULL;
+ }
+
+ key_type = s->keys->header.type;
+
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) {
+ if (spath) {
+ GRN_FREE(spath);
+ }
+ return rc;
+ }
+
+ if (spath) {
+ rc = grn_ja_remove(ctx, spath);
+ GRN_FREE(spath);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ if (path) {
+ switch (key_type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_remove(ctx, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_remove(ctx, path);
+ break;
+ }
+ if (rc == GRN_SUCCESS) {
+ rc = grn_db_config_remove(ctx, path);
+ } else {
+ grn_db_config_remove(ctx, path);
+ }
+ }
+
+ return rc;
+}
+
+static grn_rc
+remove_reference_tables(grn_ctx *ctx, grn_obj *table, grn_obj *db)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_bool is_close_opened_object_mode = GRN_FALSE;
+ grn_id table_id;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int table_name_size;
+ grn_table_cursor *cursor;
+
+ if (grn_thread_get_limit() == 1) {
+ is_close_opened_object_mode = GRN_TRUE;
+ }
+
+ table_id = DB_OBJ(table)->id;
+ table_name_size = grn_obj_name(ctx, table, table_name, GRN_TABLE_MAX_KEY_SIZE);
+ if ((cursor = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_ID))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *object;
+ grn_bool is_removed = GRN_FALSE;
+
+ if (is_close_opened_object_mode) {
+ grn_ctx_push_temporary_open_space(ctx);
+ }
+
+ object = grn_ctx_at(ctx, id);
+ if (!object) {
+ ERRCLR(ctx);
+ if (is_close_opened_object_mode) {
+ grn_ctx_pop_temporary_open_space(ctx);
+ }
+ continue;
+ }
+
+ switch (object->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ if (DB_OBJ(object)->id == table_id) {
+ break;
+ }
+
+ if (object->header.domain == table_id) {
+ rc = _grn_obj_remove(ctx, object, GRN_TRUE);
+ is_removed = (grn_table_at(ctx, db, id) == GRN_ID_NIL);
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ if (object->header.domain == table_id) {
+ break;
+ }
+ if (DB_OBJ(object)->range == table_id) {
+ rc = _grn_obj_remove(ctx, object, GRN_FALSE);
+ is_removed = (grn_table_at(ctx, db, id) == GRN_ID_NIL);
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ break;
+ default:
+ break;
+ }
+
+ if (!is_removed) {
+ grn_obj_unlink(ctx, object);
+ }
+
+ if (is_close_opened_object_mode) {
+ grn_ctx_pop_temporary_open_space(ctx);
+ }
+
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cursor);
+ }
+
+ return rc;
+}
+
+static grn_bool
+is_removable_table(grn_ctx *ctx, grn_obj *table, grn_obj *db)
+{
+ grn_id table_id;
+ grn_id reference_object_id;
+
+ table_id = DB_OBJ(table)->id;
+ if (table_id & GRN_OBJ_TMP_OBJECT) {
+ return GRN_TRUE;
+ }
+
+ reference_object_id = grn_table_find_reference_object(ctx, table);
+ if (reference_object_id == GRN_ID_NIL) {
+ return GRN_TRUE;
+ }
+
+ {
+ grn_obj *db;
+ const char *table_name;
+ int table_name_size;
+ grn_obj *reference_object;
+ const char *reference_object_name;
+ int reference_object_name_size;
+
+ db = grn_ctx_db(ctx);
+
+ table_name = _grn_table_key(ctx, db, table_id,&table_name_size);
+
+ reference_object = grn_ctx_at(ctx, reference_object_id);
+ reference_object_name = _grn_table_key(ctx,
+ db,
+ reference_object_id,
+ &reference_object_name_size);
+ if (reference_object) {
+ if (grn_obj_is_table(ctx, reference_object)) {
+ ERR(GRN_OPERATION_NOT_PERMITTED,
+ "[table][remove] a table that references the table exists: "
+ "<%.*s._key> -> <%.*s>",
+ reference_object_name_size, reference_object_name,
+ table_name_size, table_name);
+ } else {
+ ERR(GRN_OPERATION_NOT_PERMITTED,
+ "[table][remove] a column that references the table exists: "
+ "<%.*s> -> <%.*s>",
+ reference_object_name_size, reference_object_name,
+ table_name_size, table_name);
+ }
+ } else {
+ ERR(GRN_OPERATION_NOT_PERMITTED,
+ "[table][remove] a dangling object that references the table exists: "
+ "<%.*s(%u)> -> <%.*s>",
+ reference_object_name_size,
+ reference_object_name,
+ reference_object_id,
+ table_name_size, table_name);
+ }
+ }
+
+ return GRN_FALSE;
+}
+
+static inline grn_rc
+_grn_obj_remove_spec(grn_ctx *ctx, grn_obj *db, grn_id id, uint8_t type)
+{
+ const char *name;
+ uint32_t name_size = 0;
+
+ name = _grn_table_key(ctx, db, id, &name_size);
+ /* TODO: reduce log level. */
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "spec:%u:remove:%.*s:%u(%s)",
+ id,
+ name_size, name,
+ type,
+ grn_obj_type_to_string(type));
+
+ return grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+}
+
+static grn_rc
+_grn_obj_remove_pat(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path, grn_bool dependent)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ if (dependent) {
+ rc = remove_reference_tables(ctx, obj, db);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ } else {
+ if (!is_removable_table(ctx, obj, db)) {
+ return ctx->rc;
+ }
+ }
+
+ rc = remove_index(ctx, obj, GRN_HOOK_INSERT);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = remove_columns(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_pat_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_dat(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path, grn_bool dependent)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ if (dependent) {
+ rc = remove_reference_tables(ctx, obj, db);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ } else {
+ if (!is_removable_table(ctx, obj, db)) {
+ return ctx->rc;
+ }
+ }
+
+ rc = remove_index(ctx, obj, GRN_HOOK_INSERT);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = remove_columns(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_dat_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_hash(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path, grn_bool dependent)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ if (dependent) {
+ rc = remove_reference_tables(ctx, obj, db);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ } else {
+ if (!is_removable_table(ctx, obj, db)) {
+ return ctx->rc;
+ }
+ }
+
+ rc = remove_index(ctx, obj, GRN_HOOK_INSERT);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = remove_columns(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_hash_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_array(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path, grn_bool dependent)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ if (dependent) {
+ rc = remove_reference_tables(ctx, obj, db);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ } else {
+ if (!is_removable_table(ctx, obj, db)) {
+ return ctx->rc;
+ }
+ }
+
+ rc = remove_columns(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_array_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_ja(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ rc = remove_index(ctx, obj, GRN_HOOK_SET);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_ja_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_ra(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ rc = remove_index(ctx, obj, GRN_HOOK_SET);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_ra_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_index(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ delete_source_hook(ctx, obj);
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_ii_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_db_obj(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t type;
+
+ type = obj->header.type;
+
+ rc = grn_obj_close(ctx, obj);
+ if (rc != GRN_SUCCESS) { return rc; }
+
+ if (path) {
+ rc = grn_io_remove(ctx, path);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ if (!(id & GRN_OBJ_TMP_OBJECT)) {
+ rc = _grn_obj_remove_spec(ctx, db, id, type);
+ if (rc != GRN_SUCCESS) { return rc; }
+ rc = grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (rc != GRN_SUCCESS) { return rc; }
+ }
+
+ grn_obj_touch(ctx, db, NULL);
+
+ return rc;
+}
+
+static grn_rc
+_grn_obj_remove_other(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ return grn_obj_close(ctx, obj);
+}
+
+static grn_rc
+_grn_obj_remove(grn_ctx *ctx, grn_obj *obj, grn_bool dependent)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_id id = GRN_ID_NIL;
+ grn_obj *db = NULL;
+ const char *io_path;
+ char *path;
+ grn_bool is_temporary_open_target = GRN_FALSE;
+
+ if (ctx->impl && ctx->impl->db) {
+ grn_id id;
+ uint32_t s = 0;
+ const char *n;
+
+ id = DB_OBJ(obj)->id;
+ n = _grn_table_key(ctx, ctx->impl->db, id, &s);
+ if (s > 0) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "DDL:%u:obj_remove %.*s", id, s, n);
+ }
+ }
+ if (obj->header.type != GRN_PROC &&
+ (io_path = grn_obj_path(ctx, obj)) && *io_path != '\0') {
+ if (!(path = GRN_STRDUP(io_path))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return ctx->rc;
+ }
+ } else {
+ path = NULL;
+ }
+ if (GRN_DB_OBJP(obj)) {
+ id = DB_OBJ(obj)->id;
+ db = DB_OBJ(obj)->db;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ rc = _grn_obj_remove_db(ctx, obj, db, id, path);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ rc = _grn_obj_remove_pat(ctx, obj, db, id, path, dependent);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = _grn_obj_remove_dat(ctx, obj, db, id, path, dependent);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = _grn_obj_remove_hash(ctx, obj, db, id, path, dependent);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = _grn_obj_remove_array(ctx, obj, db, id, path, dependent);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ rc = _grn_obj_remove_ja(ctx, obj, db, id, path);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ rc = _grn_obj_remove_ra(ctx, obj, db, id, path);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = _grn_obj_remove_index(ctx, obj, db, id, path);
+ is_temporary_open_target = GRN_TRUE;
+ break;
+ default :
+ if (GRN_DB_OBJP(obj)) {
+ rc = _grn_obj_remove_db_obj(ctx, obj, db, id, path);
+ } else {
+ rc = _grn_obj_remove_other(ctx, obj, db, id, path);
+ }
+ }
+ if (path) {
+ GRN_FREE(path);
+ } else {
+ is_temporary_open_target = GRN_FALSE;
+ }
+
+ if (is_temporary_open_target && rc == GRN_SUCCESS) {
+ grn_obj *space;
+ space = ctx->impl->temporary_open_spaces.current;
+ if (space) {
+ unsigned int i, n_elements;
+ n_elements = GRN_BULK_VSIZE(space) / sizeof(grn_obj *);
+ for (i = 0; i < n_elements; i++) {
+ if (GRN_PTR_VALUE_AT(space, i) == obj) {
+ GRN_PTR_SET_AT(ctx, space, i, NULL);
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+grn_rc
+grn_obj_remove(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (ctx->impl && ctx->impl->db && ctx->impl->db != obj) {
+ grn_io *io = grn_obj_get_io(ctx, ctx->impl->db);
+ rc = grn_io_lock(ctx, io, grn_lock_timeout);
+ if (rc == GRN_SUCCESS) {
+ rc = _grn_obj_remove(ctx, obj, GRN_FALSE);
+ grn_io_unlock(io);
+ }
+ } else {
+ rc = _grn_obj_remove(ctx, obj, GRN_FALSE);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_remove_dependent(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (ctx->impl && ctx->impl->db && ctx->impl->db != obj) {
+ grn_io *io = grn_obj_get_io(ctx, ctx->impl->db);
+ rc = grn_io_lock(ctx, io, grn_lock_timeout);
+ if (rc == GRN_SUCCESS) {
+ rc = _grn_obj_remove(ctx, obj, GRN_TRUE);
+ grn_io_unlock(io);
+ }
+ } else {
+ rc = _grn_obj_remove(ctx, obj, GRN_TRUE);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_remove_force(grn_ctx *ctx, const char *name, int name_size)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *db;
+ grn_id obj_id;
+ char path[PATH_MAX];
+
+ GRN_API_ENTER;
+
+ if (!(ctx->impl && ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[object][remove][force] database isn't initialized");
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ db = ctx->impl->db;
+ if (name_size == -1) {
+ name_size = strlen(name);
+ }
+ obj_id = grn_table_get(ctx, db, name, name_size);
+ if (obj_id == GRN_ID_NIL) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[object][remove][force] nonexistent object: <%.*s>",
+ name_size, name);
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ grn_obj_delete_by_id(ctx, db, obj_id, GRN_TRUE);
+ grn_obj_path_by_id(ctx, db, obj_id, path);
+ grn_io_remove_if_exist(ctx, path);
+ grn_strcat(path, PATH_MAX, ".c");
+ grn_io_remove_if_exist(ctx, path);
+
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_update_by_id(grn_ctx *ctx, grn_obj *table, grn_id id,
+ const void *dest_key, unsigned int dest_key_size)
+{
+ grn_rc rc = GRN_OPERATION_NOT_SUPPORTED;
+ GRN_API_ENTER;
+ if (table->header.type == GRN_TABLE_DAT_KEY) {
+ grn_dat *dat = (grn_dat *)table;
+ if (dat->io && !(dat->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, dat->io, grn_lock_timeout)) {
+ rc = ctx->rc;
+ } else {
+ rc = grn_dat_update_by_id(ctx, dat, id, dest_key, dest_key_size);
+ grn_io_unlock(dat->io);
+ }
+ } else {
+ rc = grn_dat_update_by_id(ctx, dat, id, dest_key, dest_key_size);
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_update(grn_ctx *ctx, grn_obj *table,
+ const void *src_key, unsigned int src_key_size,
+ const void *dest_key, unsigned int dest_key_size)
+{
+ grn_rc rc = GRN_OPERATION_NOT_SUPPORTED;
+ GRN_API_ENTER;
+ if (table->header.type == GRN_TABLE_DAT_KEY) {
+ rc = grn_dat_update(ctx, (grn_dat *)table,
+ src_key, src_key_size,
+ dest_key, dest_key_size);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_rename(grn_ctx *ctx, grn_obj *obj, const char *name, unsigned int name_size)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (ctx && ctx->impl && GRN_DB_P(ctx->impl->db) && GRN_DB_OBJP(obj) && !IS_TEMP(obj)) {
+ grn_db *s = (grn_db *)ctx->impl->db;
+ grn_obj *keys = (grn_obj *)s->keys;
+ rc = grn_table_update_by_id(ctx, keys, DB_OBJ(obj)->id, name, name_size);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_rename(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_hash *cols;
+
+ GRN_API_ENTER;
+
+ if (!GRN_OBJ_TABLEP(table)) {
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int table_name_size;
+ table_name_size = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][rename] isn't table: <%.*s> -> <%.*s>",
+ table_name_size, table_name,
+ name_size, name);
+ goto exit;
+ }
+ if (IS_TEMP(table)) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][rename] temporary table doesn't have name: "
+ "(anonymous) -> <%.*s>",
+ name_size, name);
+ goto exit;
+ }
+
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ grn_table_columns(ctx, table, "", 0, (grn_obj *)cols);
+ if (!(rc = grn_obj_rename(ctx, table, name, name_size))) {
+ grn_id *key;
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ grn_memcpy(fullname, name, name_size);
+ fullname[name_size] = GRN_DB_DELIMITER;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) {
+ int colname_len = grn_column_name(ctx, col, fullname + name_size + 1,
+ GRN_TABLE_MAX_KEY_SIZE - name_size - 1);
+ if (colname_len) {
+ if ((rc = grn_obj_rename(ctx, col, fullname,
+ name_size + 1 + colname_len))) {
+ break;
+ }
+ }
+ }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+exit:
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_column_rename(grn_ctx *ctx, grn_obj *column, const char *name, unsigned int name_size)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(column)) {
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ grn_db *s = (grn_db *)DB_OBJ(column)->db;
+ int len = grn_table_get_key(ctx, s->keys, DB_OBJ(column)->header.domain,
+ fullname, GRN_TABLE_MAX_KEY_SIZE);
+ if (name_size + 1 + len > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][rename] too long column name: required name_size(%d) < %d"
+ ": <%.*s>.<%.*s>",
+ name_size, GRN_TABLE_MAX_KEY_SIZE - 1 - len,
+ len, fullname, name_size, name);
+ goto exit;
+ }
+ fullname[len] = GRN_DB_DELIMITER;
+ grn_memcpy(fullname + len + 1, name, name_size);
+ name_size += len + 1;
+ rc = grn_obj_rename(ctx, column, fullname, name_size);
+ if (rc == GRN_SUCCESS) {
+ grn_obj_touch(ctx, column, NULL);
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_path_rename(grn_ctx *ctx, const char *old_path, const char *new_path)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+/* db must be validated by caller */
+grn_id
+grn_obj_register(grn_ctx *ctx, grn_obj *db, const char *name, unsigned int name_size)
+{
+ grn_id id = GRN_ID_NIL;
+ if (name && name_size) {
+ grn_db *s = (grn_db *)db;
+ int added;
+ if (!(id = grn_table_add(ctx, s->keys, name, name_size, &added))) {
+ grn_rc rc;
+ rc = ctx->rc;
+ if (rc == GRN_SUCCESS) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ ERR(rc,
+ "[object][register] failed to register a name: <%.*s>%s%s%s",
+ name_size, name,
+ ctx->rc == GRN_SUCCESS ? "" : ": <",
+ ctx->rc == GRN_SUCCESS ? "" : ctx->errbuf,
+ ctx->rc == GRN_SUCCESS ? "" : ">");
+ } else if (!added) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[object][register] already used name was assigned: <%.*s>",
+ name_size, name);
+ id = GRN_ID_NIL;
+ }
+ } else if (ctx->impl && ctx->impl->values) {
+ id = grn_array_add(ctx, ctx->impl->values, NULL) | GRN_OBJ_TMP_OBJECT;
+ }
+ return id;
+}
+
+grn_rc
+grn_obj_delete_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, grn_bool removep)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (id) {
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (ctx->impl) {
+ if (id & GRN_OBJ_TMP_COLUMN) {
+ if (ctx->impl->temporary_columns) {
+ rc = grn_pat_delete_by_id(ctx, ctx->impl->temporary_columns,
+ id & ~(GRN_OBJ_TMP_COLUMN | GRN_OBJ_TMP_OBJECT),
+ NULL);
+ }
+ } else {
+ if (ctx->impl->values) {
+ rc = grn_array_delete_by_id(ctx, ctx->impl->values,
+ id & ~GRN_OBJ_TMP_OBJECT, NULL);
+ }
+ }
+ }
+ } else {
+ db_value *vp;
+ grn_db *s = (grn_db *)db;
+ if ((vp = grn_tiny_array_at(&s->values, id))) {
+ GRN_ASSERT(!vp->lock);
+ vp->lock = 0;
+ vp->ptr = NULL;
+ vp->done = 0;
+ }
+ if (removep) {
+ switch (s->keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_delete_by_id(ctx, (grn_pat *)s->keys, id, NULL);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_delete_by_id(ctx, (grn_dat *)s->keys, id, NULL);
+ break;
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ }
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+
+grn_rc
+grn_obj_path_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, char *buffer)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (!GRN_DB_P(db) || !buffer) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ grn_db_generate_pathname(ctx, db, id, buffer);
+ }
+ GRN_API_RETURN(rc);
+}
+
+/* db must be validated by caller */
+grn_rc
+grn_db_obj_init(grn_ctx *ctx, grn_obj *db, grn_id id, grn_db_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (id) {
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (id & GRN_OBJ_TMP_COLUMN) {
+ if (ctx->impl && ctx->impl->temporary_columns) {
+ grn_id real_id = id & ~(GRN_OBJ_TMP_COLUMN | GRN_OBJ_TMP_OBJECT);
+ rc = grn_pat_set_value(ctx, ctx->impl->temporary_columns,
+ real_id, &obj, GRN_OBJ_SET);
+ }
+ } else {
+ if (ctx->impl && ctx->impl->values) {
+ rc = grn_array_set_value(ctx, ctx->impl->values,
+ id & ~GRN_OBJ_TMP_OBJECT, &obj, GRN_OBJ_SET);
+ }
+ }
+ } else {
+ db_value *vp;
+ vp = grn_tiny_array_at(&((grn_db *)db)->values, id);
+ if (!vp) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ ERR(rc, "grn_tiny_array_at failed (%d)", id);
+ return rc;
+ }
+ vp->lock = 1;
+ vp->ptr = (grn_obj *)obj;
+ }
+ }
+ obj->id = id;
+ obj->db = db;
+ obj->source = NULL;
+ obj->source_size = 0;
+ {
+ grn_hook_entry entry;
+ for (entry = 0; entry < N_HOOK_ENTRIES; entry++) {
+ obj->hooks[entry] = NULL;
+ }
+ }
+ grn_obj_spec_save(ctx, obj);
+ return rc;
+}
+
+#define GET_PATH(spec,decoded_spec,buffer,s,id) do {\
+ if (spec->header.flags & GRN_OBJ_CUSTOM_NAME) {\
+ const char *path;\
+ unsigned int size = grn_vector_get_element(ctx,\
+ decoded_spec,\
+ GRN_SERIALIZED_SPEC_INDEX_PATH,\
+ &path,\
+ NULL,\
+ NULL);\
+ if (size > PATH_MAX) { ERR(GRN_FILENAME_TOO_LONG, "too long path"); }\
+ grn_memcpy(buffer, path, size);\
+ buffer[size] = '\0';\
+ } else {\
+ grn_db_generate_pathname(ctx, (grn_obj *)s, id, buffer);\
+ }\
+} while (0)
+
+#define UNPACK_INFO(spec,decoded_spec) do {\
+ if (vp->ptr) {\
+ const char *p;\
+ uint32_t size;\
+ grn_db_obj *r = DB_OBJ(vp->ptr);\
+ r->header = spec->header;\
+ r->id = id;\
+ r->range = spec->range;\
+ r->db = (grn_obj *)s;\
+ size = grn_vector_get_element(ctx,\
+ decoded_spec,\
+ GRN_SERIALIZED_SPEC_INDEX_SOURCE,\
+ &p,\
+ NULL,\
+ NULL);\
+ if (size) {\
+ if ((r->source = GRN_MALLOC(size))) {\
+ grn_memcpy(r->source, p, size);\
+ r->source_size = size;\
+ }\
+ }\
+ size = grn_vector_get_element(ctx,\
+ decoded_spec,\
+ GRN_SERIALIZED_SPEC_INDEX_HOOK,\
+ &p,\
+ NULL,\
+ NULL);\
+ grn_hook_unpack(ctx, r, p, size);\
+ }\
+} while (0)
+
+static void
+grn_token_filters_unpack(grn_ctx *ctx,
+ grn_obj *token_filters,
+ grn_obj *spec_vector)
+{
+ grn_id *token_filter_ids;
+ unsigned int element_size;
+ unsigned int i, n_token_filter_ids;
+
+ if (grn_vector_size(ctx, spec_vector) <= GRN_SERIALIZED_SPEC_INDEX_TOKEN_FILTERS) {
+ return;
+ }
+
+ element_size = grn_vector_get_element(ctx,
+ spec_vector,
+ GRN_SERIALIZED_SPEC_INDEX_TOKEN_FILTERS,
+ (const char **)(&token_filter_ids),
+ NULL,
+ NULL);
+ n_token_filter_ids = element_size / sizeof(grn_id);
+ for (i = 0; i < n_token_filter_ids; i++) {
+ grn_id token_filter_id = token_filter_ids[i];
+ grn_obj *token_filter;
+
+ token_filter = grn_ctx_at(ctx, token_filter_id);
+ if (!token_filter) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "nonexistent token filter ID: %d", token_filter_id);
+ return;
+ }
+ GRN_PTR_PUT(ctx, token_filters, token_filter);
+ }
+}
+
+grn_bool
+grn_db_spec_unpack(grn_ctx *ctx,
+ grn_id id,
+ void *encoded_spec,
+ uint32_t encoded_spec_size,
+ grn_obj_spec **spec,
+ grn_obj *decoded_spec,
+ const char *error_message_tag)
+{
+ grn_obj *db;
+ grn_db *db_raw;
+ grn_rc rc;
+ uint32_t spec_size;
+
+ db = ctx->impl->db;
+ db_raw = (grn_db *)db;
+
+ rc = grn_vector_decode(ctx,
+ decoded_spec,
+ encoded_spec,
+ encoded_spec_size);
+ if (rc != GRN_SUCCESS) {
+ const char *name;
+ uint32_t name_size;
+ name = _grn_table_key(ctx, db, id, &name_size);
+ GRN_LOG((ctx), GRN_LOG_ERROR,
+ "%s: failed to decode spec: <%u>(<%.*s>):<%u>: %s",
+ error_message_tag,
+ id,
+ name_size, name,
+ encoded_spec_size,
+ grn_rc_to_string(rc));
+ return GRN_FALSE;
+ }
+
+ spec_size = grn_vector_get_element(ctx,
+ decoded_spec,
+ GRN_SERIALIZED_SPEC_INDEX_SPEC,
+ (const char **)spec,
+ NULL,
+ NULL);
+ if (spec_size == 0) {
+ const char *name;
+ uint32_t name_size;
+ name = _grn_table_key(ctx, db, id, &name_size);
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "%s: spec value is empty: <%u>(<%.*s>)",
+ error_message_tag,
+ id,
+ name_size, name);
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+grn_obj *
+grn_ctx_at(grn_ctx *ctx, grn_id id)
+{
+ grn_obj *res = NULL;
+ if (!ctx || !ctx->impl || !id) { return res; }
+ GRN_API_ENTER;
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (id & GRN_OBJ_TMP_COLUMN) {
+ if (ctx->impl->temporary_columns) {
+ grn_id real_id = id & ~(GRN_OBJ_TMP_COLUMN | GRN_OBJ_TMP_OBJECT);
+ grn_obj **tmp_obj;
+ uint32_t size;
+ tmp_obj = (grn_obj **)grn_pat_get_value_(ctx,
+ ctx->impl->temporary_columns,
+ real_id,
+ &size);
+ if (tmp_obj) {
+ res = *tmp_obj;
+ }
+ }
+ } else {
+ if (ctx->impl->values) {
+ grn_obj **tmp_obj;
+ tmp_obj = _grn_array_get_value(ctx, ctx->impl->values,
+ id & ~GRN_OBJ_TMP_OBJECT);
+ if (tmp_obj) {
+ res = *tmp_obj;
+ }
+ }
+ }
+ } else {
+ grn_db *s = (grn_db *)ctx->impl->db;
+ if (s) {
+ db_value *vp;
+ uint32_t l, *pl, ntrial;
+ if (!(vp = grn_tiny_array_at(&s->values, id))) { goto exit; }
+#ifdef USE_NREF
+ pl = &vp->lock;
+ for (ntrial = 0;; ntrial++) {
+ GRN_ATOMIC_ADD_EX(pl, 1, l);
+ if (l < GRN_IO_MAX_REF) { break; }
+ if (ntrial >= 10) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "max trial in ctx_at(%p,%d)", vp->ptr, vp->lock);
+ break;
+ }
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+ GRN_FUTEX_WAIT(pl);
+ }
+#endif /* USE_NREF */
+ if (s->specs && !vp->ptr /* && !vp->done */) {
+#ifndef USE_NREF
+ pl = &vp->lock;
+ for (ntrial = 0;; ntrial++) {
+ GRN_ATOMIC_ADD_EX(pl, 1, l);
+ if (l < GRN_IO_MAX_REF) { break; }
+ if (ntrial >= 10) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "max trial in ctx_at(%p,%d)", vp->ptr, vp->lock);
+ break;
+ }
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+ GRN_FUTEX_WAIT(pl);
+ }
+#endif /* USE_NREF */
+ if (!l) {
+ grn_io_win iw;
+ uint32_t encoded_spec_size;
+ void *encoded_spec;
+
+ encoded_spec = grn_ja_ref(ctx, s->specs, id, &iw, &encoded_spec_size);
+ if (encoded_spec) {
+ grn_bool success;
+ grn_obj_spec *spec;
+ grn_obj decoded_spec;
+
+ GRN_OBJ_INIT(&decoded_spec, GRN_VECTOR, 0, GRN_DB_TEXT);
+ success = grn_db_spec_unpack(ctx,
+ id,
+ encoded_spec,
+ encoded_spec_size,
+ &spec,
+ &decoded_spec,
+ "grn_ctx_at");
+ if (success) {
+ char buffer[PATH_MAX];
+ switch (spec->header.type) {
+ case GRN_TYPE :
+ vp->ptr = (grn_obj *)grn_type_open(ctx, spec);
+ UNPACK_INFO(spec, &decoded_spec);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_hash_open(ctx, buffer);
+ if (vp->ptr) {
+ grn_hash *hash = (grn_hash *)(vp->ptr);
+ grn_obj_flags flags = vp->ptr->header.flags;
+ UNPACK_INFO(spec, &decoded_spec);
+ vp->ptr->header.flags = flags;
+ grn_token_filters_unpack(ctx,
+ &(hash->token_filters),
+ &decoded_spec);
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_pat_open(ctx, buffer);
+ if (vp->ptr) {
+ grn_pat *pat = (grn_pat *)(vp->ptr);
+ grn_obj_flags flags = vp->ptr->header.flags;
+ UNPACK_INFO(spec, &decoded_spec);
+ vp->ptr->header.flags = flags;
+ grn_token_filters_unpack(ctx,
+ &(pat->token_filters),
+ &decoded_spec);
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_dat_open(ctx, buffer);
+ if (vp->ptr) {
+ grn_dat *dat = (grn_dat *)(vp->ptr);
+ grn_obj_flags flags = vp->ptr->header.flags;
+ UNPACK_INFO(spec, &decoded_spec);
+ vp->ptr->header.flags = flags;
+ grn_token_filters_unpack(ctx,
+ &(dat->token_filters),
+ &decoded_spec);
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_array_open(ctx, buffer);
+ UNPACK_INFO(spec, &decoded_spec);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_ja_open(ctx, buffer);
+ UNPACK_INFO(spec, &decoded_spec);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_ra_open(ctx, buffer);
+ UNPACK_INFO(spec, &decoded_spec);
+ break;
+ case GRN_COLUMN_INDEX :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ {
+ grn_obj *table = grn_ctx_at(ctx, spec->header.domain);
+ vp->ptr = (grn_obj *)grn_ii_open(ctx, buffer, table);
+ }
+ UNPACK_INFO(spec, &decoded_spec);
+ break;
+ case GRN_PROC :
+ GET_PATH(spec, &decoded_spec, buffer, s, id);
+ grn_plugin_register(ctx, buffer);
+ break;
+ case GRN_EXPR :
+ {
+ const char *p;
+ uint32_t size;
+ uint8_t *u;
+ size = grn_vector_get_element(ctx,
+ &decoded_spec,
+ GRN_SERIALIZED_SPEC_INDEX_EXPR,
+ &p,
+ NULL,
+ NULL);
+ u = (uint8_t *)p;
+ vp->ptr = grn_expr_open(ctx, spec, u, u + size);
+ }
+ break;
+ }
+ if (!vp->ptr) {
+ const char *name;
+ uint32_t name_size = 0;
+ name = _grn_table_key(ctx, (grn_obj *)s, id, &name_size);
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "grn_ctx_at: failed to open object: "
+ "<%u>(<%.*s>):<%u>(<%s>)",
+ id,
+ name_size, name,
+ spec->header.type,
+ grn_obj_type_to_string(spec->header.type));
+ }
+ }
+ GRN_OBJ_FIN(ctx, &decoded_spec);
+ grn_ja_unref(ctx, &iw);
+ }
+#ifndef USE_NREF
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+#endif /* USE_NREF */
+ vp->done = 1;
+ GRN_FUTEX_WAKE(&vp->ptr);
+ } else {
+ for (ntrial = 0; !vp->ptr; ntrial++) {
+ if (ntrial >= 1000) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "max trial in ctx_at(%d,%p,%d)!", id, vp->ptr, vp->lock);
+ break;
+ }
+ GRN_FUTEX_WAIT(&vp->ptr);
+ }
+ }
+ if (vp->ptr) {
+ switch (vp->ptr->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_INDEX :
+ {
+ grn_obj *space;
+ space = ctx->impl->temporary_open_spaces.current;
+ if (space) {
+ GRN_PTR_PUT(ctx, space, vp->ptr);
+ }
+ }
+ break;
+ }
+ }
+ }
+ res = vp->ptr;
+ if (res && res->header.type == GRN_PROC) {
+ grn_plugin_ensure_registered(ctx, res);
+ }
+ }
+ }
+exit :
+ GRN_API_RETURN(res);
+}
+
+grn_bool
+grn_ctx_is_opened(grn_ctx *ctx, grn_id id)
+{
+ grn_bool is_opened = GRN_FALSE;
+
+ if (!ctx || !ctx->impl || !id) {
+ return GRN_FALSE;
+ }
+
+ GRN_API_ENTER;
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (ctx->impl->values) {
+ grn_obj **tmp_obj;
+ tmp_obj = _grn_array_get_value(ctx, ctx->impl->values,
+ id & ~GRN_OBJ_TMP_OBJECT);
+ if (tmp_obj) {
+ is_opened = GRN_TRUE;
+ }
+ }
+ } else {
+ grn_db *s = (grn_db *)ctx->impl->db;
+ if (s) {
+ db_value *vp;
+ vp = grn_tiny_array_at(&s->values, id);
+ if (vp && vp->ptr) {
+ is_opened = GRN_TRUE;
+ }
+ }
+ }
+ GRN_API_RETURN(is_opened);
+}
+
+grn_obj *
+grn_obj_open(grn_ctx *ctx, unsigned char type, grn_obj_flags flags, grn_id domain)
+{
+ grn_obj *obj = GRN_MALLOCN(grn_obj, 1);
+ if (obj) {
+ GRN_OBJ_INIT(obj, type, flags, domain);
+ obj->header.impl_flags |= GRN_OBJ_ALLOCATED;
+ }
+ return obj;
+}
+
+grn_obj *
+grn_obj_graft(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj *new = grn_obj_open(ctx, obj->header.type, obj->header.impl_flags, obj->header.domain);
+ if (new) {
+ /* todo : deep copy if (obj->header.impl_flags & GRN_OBJ_DO_SHALLOW_COPY) */
+ new->u.b.head = obj->u.b.head;
+ new->u.b.curr = obj->u.b.curr;
+ new->u.b.tail = obj->u.b.tail;
+ obj->u.b.head = NULL;
+ obj->u.b.curr = NULL;
+ obj->u.b.tail = NULL;
+ }
+ return new;
+}
+
+grn_rc
+grn_pvector_fin(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc;
+ if (obj->header.impl_flags & GRN_OBJ_OWN) {
+ /*
+ * Note that GRN_OBJ_OWN should not be used outside the DB API function
+ * because grn_obj_close is a DB API function.
+ */
+ unsigned int i, n_elements;
+ n_elements = GRN_BULK_VSIZE(obj) / sizeof(grn_obj *);
+ for (i = 0; i < n_elements; i++) {
+ grn_obj *element = GRN_PTR_VALUE_AT(obj, n_elements - i - 1);
+ if (element) {
+ grn_obj_close(ctx, element);
+ }
+ }
+ }
+ obj->header.type = GRN_VOID;
+ rc = grn_bulk_fin(ctx, obj);
+ if (obj->header.impl_flags & GRN_OBJ_ALLOCATED) {
+ GRN_FREE(obj);
+ }
+ return rc;
+}
+
+static void
+grn_table_close_columns(grn_ctx *ctx, grn_obj *table)
+{
+ grn_hash *columns;
+ int n_columns;
+
+ columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_HASH_TINY);
+ if (!columns) {
+ return;
+ }
+
+ n_columns = grn_table_columns(ctx, table, "", 0, (grn_obj *)columns);
+ if (n_columns > 0) {
+ grn_hash_cursor *cursor;
+ cursor = grn_hash_cursor_open(ctx, columns, NULL, 0, NULL, 0, 0, -1, 0);
+ if (cursor) {
+ while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) {
+ grn_id *id;
+ grn_obj *column;
+
+ grn_hash_cursor_get_key(ctx, cursor, (void **)&id);
+ column = grn_ctx_at(ctx, *id);
+ if (column) {
+ grn_obj_close(ctx, column);
+ }
+ }
+ grn_hash_cursor_close(ctx, cursor);
+ }
+ }
+
+ grn_hash_close(ctx, columns);
+}
+
+grn_rc
+grn_obj_close(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (obj) {
+ if (grn_obj_is_table(ctx, obj) &&
+ (DB_OBJ(obj)->id & GRN_OBJ_TMP_OBJECT)) {
+ grn_table_close_columns(ctx, obj);
+ }
+ if (GRN_DB_OBJP(obj)) {
+ grn_hook_entry entry;
+ if (DB_OBJ(obj)->finalizer) {
+ DB_OBJ(obj)->finalizer(ctx, 1, &obj, &DB_OBJ(obj)->user_data);
+ }
+ if (DB_OBJ(obj)->source) {
+ GRN_FREE(DB_OBJ(obj)->source);
+ }
+ for (entry = 0; entry < N_HOOK_ENTRIES; entry++) {
+ grn_hook_free(ctx, DB_OBJ(obj)->hooks[entry]);
+ }
+ grn_obj_delete_by_id(ctx, DB_OBJ(obj)->db, DB_OBJ(obj)->id, GRN_FALSE);
+ }
+ switch (obj->header.type) {
+ case GRN_VECTOR :
+ if (obj->u.v.body && !(obj->header.impl_flags & GRN_OBJ_REFER)) {
+ grn_obj_close(ctx, obj->u.v.body);
+ }
+ if (obj->u.v.sections) { GRN_FREE(obj->u.v.sections); }
+ if (obj->header.impl_flags & GRN_OBJ_ALLOCATED) { GRN_FREE(obj); }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_VOID :
+ case GRN_BULK :
+ case GRN_UVECTOR :
+ case GRN_MSG :
+ obj->header.type = GRN_VOID;
+ rc = grn_bulk_fin(ctx, obj);
+ if (obj->header.impl_flags & GRN_OBJ_ALLOCATED) { GRN_FREE(obj); }
+ break;
+ case GRN_PTR :
+ if (obj->header.impl_flags & GRN_OBJ_OWN) {
+ if (GRN_BULK_VSIZE(obj) == sizeof(grn_obj *)) {
+ grn_obj_close(ctx, GRN_PTR_VALUE(obj));
+ }
+ }
+ obj->header.type = GRN_VOID;
+ rc = grn_bulk_fin(ctx, obj);
+ if (obj->header.impl_flags & GRN_OBJ_ALLOCATED) { GRN_FREE(obj); }
+ break;
+ case GRN_PVECTOR :
+ rc = grn_pvector_fin(ctx, obj);
+ break;
+ case GRN_ACCESSOR :
+ {
+ grn_accessor *p, *n;
+ for (p = (grn_accessor *)obj; p; p = n) {
+ n = p->next;
+ GRN_FREE(p);
+ }
+ }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_SNIP :
+ rc = grn_snip_close(ctx, (grn_snip *)obj);
+ break;
+ case GRN_STRING :
+ rc = grn_string_close(ctx, obj);
+ break;
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ grn_pat_cursor_close(ctx, (grn_pat_cursor *)obj);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ grn_dat_cursor_close(ctx, (grn_dat_cursor *)obj);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ grn_hash_cursor_close(ctx, (grn_hash_cursor *)obj);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ grn_array_cursor_close(ctx, (grn_array_cursor *)obj);
+ break;
+ case GRN_CURSOR_COLUMN_INDEX :
+ {
+ grn_index_cursor *ic = (grn_index_cursor *)obj;
+ if (ic->iic) { grn_ii_cursor_close(ctx, ic->iic); }
+ GRN_FREE(ic);
+ }
+ break;
+ case GRN_CURSOR_COLUMN_GEO_INDEX :
+ grn_geo_cursor_close(ctx, obj);
+ break;
+ case GRN_CURSOR_CONFIG :
+ grn_config_cursor_close(ctx, (grn_config_cursor *)obj);
+ break;
+ case GRN_TYPE :
+ GRN_FREE(obj);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_DB :
+ rc = grn_db_close(ctx, obj);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_close(ctx, (grn_pat *)obj);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_close(ctx, (grn_dat *)obj);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = grn_hash_close(ctx, (grn_hash *)obj);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_array_close(ctx, (grn_array *)obj);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ rc = grn_ja_close(ctx, (grn_ja *)obj);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ rc = grn_ra_close(ctx, (grn_ra *)obj);
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_ii_close(ctx, (grn_ii *)obj);
+ break;
+ case GRN_PROC :
+ {
+ uint32_t i;
+ grn_proc *p = (grn_proc *)obj;
+ /*
+ if (obj->header.domain) {
+ grn_hash_delete(ctx, ctx->impl->qe, &obj->header.domain, sizeof(grn_id), NULL);
+ }
+ */
+ for (i = 0; i < p->nvars; i++) {
+ grn_obj_close(ctx, &p->vars[i].value);
+ }
+ GRN_REALLOC(p->vars, 0);
+ grn_obj_close(ctx, &p->name_buf);
+ if (p->obj.range != GRN_ID_NIL) {
+ grn_plugin_close(ctx, p->obj.range);
+ }
+ GRN_FREE(obj);
+ rc = GRN_SUCCESS;
+ }
+ break;
+ case GRN_EXPR :
+ rc = grn_expr_close(ctx, obj);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+void
+grn_obj_unlink(grn_ctx *ctx, grn_obj *obj)
+{
+ if (obj &&
+ (!GRN_DB_OBJP(obj) ||
+ (((grn_db_obj *)obj)->id & GRN_OBJ_TMP_OBJECT) ||
+ (((grn_db_obj *)obj)->id == GRN_ID_NIL) ||
+ obj->header.type == GRN_DB)) {
+ grn_obj_close(ctx, obj);
+ } else if (GRN_DB_OBJP(obj)) {
+#ifdef USE_NREF
+ grn_db_obj *dob = DB_OBJ(obj);
+ grn_db *s = (grn_db *)dob->db;
+ db_value *vp = grn_tiny_array_at(&s->values, dob->id);
+ if (vp) {
+ uint32_t l, *pl = &vp->lock;
+ if (!vp->lock) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "invalid unlink(%p,%d)", obj, vp->lock);
+ return;
+ }
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+ if (l == 1) {
+ GRN_ATOMIC_ADD_EX(pl, GRN_IO_MAX_REF, l);
+ if (l == GRN_IO_MAX_REF) {
+#ifdef CALL_FINALIZER
+ grn_obj_close(ctx, obj);
+ vp->done = 0;
+ if (dob->finalizer) {
+ dob->finalizer(ctx, 1, &obj, &dob->user_data);
+ dob->finalizer = NULL;
+ dob->user_data.ptr = NULL;
+ }
+#endif /* CALL_FINALIZER */
+ }
+ GRN_ATOMIC_ADD_EX(pl, -GRN_IO_MAX_REF, l);
+ GRN_FUTEX_WAKE(pl);
+ }
+ }
+#endif /* USE_NREF */
+ }
+}
+
+#define VECTOR_CLEAR(ctx,obj) do {\
+ if ((obj)->u.v.body && !((obj)->header.impl_flags & GRN_OBJ_REFER)) {\
+ grn_obj_close((ctx), (obj)->u.v.body);\
+ }\
+ if ((obj)->u.v.sections) { GRN_FREE((obj)->u.v.sections); }\
+ (obj)->header.impl_flags &= ~GRN_OBJ_DO_SHALLOW_COPY;\
+ (obj)->u.b.head = NULL;\
+ (obj)->u.b.curr = NULL;\
+ (obj)->u.b.tail = NULL;\
+} while (0)
+
+static void
+grn_obj_ensure_vector(grn_ctx *ctx, grn_obj *obj)
+{
+ if (obj->header.type != GRN_VECTOR) { grn_bulk_fin(ctx, obj); }
+ obj->header.type = GRN_VECTOR;
+ obj->header.flags &= ~GRN_OBJ_WITH_WEIGHT;
+}
+
+static void
+grn_obj_ensure_bulk(grn_ctx *ctx, grn_obj *obj)
+{
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_BULK;
+ obj->header.flags &= ~GRN_OBJ_WITH_WEIGHT;
+}
+
+grn_rc
+grn_obj_reinit(grn_ctx *ctx, grn_obj *obj, grn_id domain, unsigned char flags)
+{
+ if (!GRN_OBJ_MUTABLE(obj)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid obj assigned");
+ } else {
+ switch (obj->header.type) {
+ case GRN_PTR :
+ if (obj->header.impl_flags & GRN_OBJ_OWN) {
+ if (GRN_BULK_VSIZE(obj) == sizeof(grn_obj *)) {
+ grn_obj_close(ctx, GRN_PTR_VALUE(obj));
+ }
+ obj->header.impl_flags &= ~GRN_OBJ_OWN;
+ }
+ break;
+ case GRN_PVECTOR :
+ if (obj->header.impl_flags & GRN_OBJ_OWN) {
+ unsigned int i, n_elements;
+ n_elements = GRN_BULK_VSIZE(obj) / sizeof(grn_obj *);
+ for (i = 0; i < n_elements; i++) {
+ grn_obj *element = GRN_PTR_VALUE_AT(obj, i);
+ grn_obj_close(ctx, element);
+ }
+ obj->header.impl_flags &= ~GRN_OBJ_OWN;
+ }
+ break;
+ default :
+ break;
+ }
+
+ switch (domain) {
+ case GRN_DB_VOID :
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_VOID;
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ break;
+ case GRN_DB_OBJECT :
+ case GRN_DB_BOOL :
+ case GRN_DB_INT8 :
+ case GRN_DB_UINT8 :
+ case GRN_DB_INT16 :
+ case GRN_DB_UINT16 :
+ case GRN_DB_INT32 :
+ case GRN_DB_UINT32 :
+ case GRN_DB_INT64 :
+ case GRN_DB_UINT64 :
+ case GRN_DB_FLOAT :
+ case GRN_DB_TIME :
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = (flags & GRN_OBJ_VECTOR) ? GRN_UVECTOR : GRN_BULK;
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ if (flags & GRN_OBJ_VECTOR) {
+ if (obj->header.type != GRN_VECTOR) { grn_bulk_fin(ctx, obj); }
+ obj->header.type = GRN_VECTOR;
+ if (obj->u.v.body) {
+ grn_obj_reinit(ctx, obj->u.v.body, domain, 0);
+ }
+ obj->u.v.n_sections = 0;
+ } else {
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_BULK;
+ }
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ break;
+ default :
+ {
+ grn_obj *d = grn_ctx_at(ctx, domain);
+ if (!d) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid domain assigned");
+ } else {
+ if (d->header.type == GRN_TYPE && (d->header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ if (flags & GRN_OBJ_VECTOR) {
+ if (obj->header.type != GRN_VECTOR) { grn_bulk_fin(ctx, obj); }
+ obj->header.type = GRN_VECTOR;
+ } else {
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_BULK;
+ }
+ } else {
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = (flags & GRN_OBJ_VECTOR) ? GRN_UVECTOR : GRN_BULK;
+ }
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ }
+ }
+ break;
+ }
+ }
+ return ctx->rc;
+}
+
+grn_rc
+grn_obj_reinit_for(grn_ctx *ctx, grn_obj *obj, grn_obj *domain_obj)
+{
+ grn_id domain = GRN_ID_NIL;
+ grn_obj_flags flags = 0;
+
+ if (!GRN_DB_OBJP(domain_obj) && domain_obj->header.type != GRN_ACCESSOR) {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect_limited(ctx, &inspected, domain_obj);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[reinit] invalid domain object: <%.*s>",
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ return ctx->rc;
+ }
+
+ if (grn_column_is_index(ctx, domain_obj)) {
+ domain = GRN_DB_UINT32;
+ } else {
+ grn_obj_get_range_info(ctx, domain_obj, &domain, &flags);
+ if (GRN_OBJ_TABLEP(domain_obj) &&
+ domain_obj->header.type != GRN_TABLE_NO_KEY) {
+ domain = domain_obj->header.domain;
+ }
+ }
+ return grn_obj_reinit(ctx, obj, domain, flags);
+}
+
+const char *
+grn_obj_path(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_io *io;
+ const char *path = NULL;
+ GRN_API_ENTER;
+ if (obj->header.type == GRN_PROC) {
+ path = grn_plugin_path(ctx, DB_OBJ(obj)->range);
+ GRN_API_RETURN(path);
+ }
+ io = grn_obj_get_io(ctx, obj);
+ if (io && !(io->flags & GRN_IO_TEMPORARY)) { path = io->path; }
+ GRN_API_RETURN(path);
+}
+
+int
+grn_obj_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size)
+{
+ int len = 0;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ if (DB_OBJ(obj)->id) {
+ grn_db *s = (grn_db *)DB_OBJ(obj)->db;
+ grn_id id = DB_OBJ(obj)->id;
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (id & GRN_OBJ_TMP_COLUMN) {
+ grn_id real_id = id & ~(GRN_OBJ_TMP_OBJECT | GRN_OBJ_TMP_COLUMN);
+ len = grn_pat_get_key(ctx, ctx->impl->temporary_columns,
+ real_id, namebuf, buf_size);
+ }
+ } else {
+ len = grn_table_get_key(ctx, s->keys, id, namebuf, buf_size);
+ }
+ }
+ }
+ GRN_API_RETURN(len);
+}
+
+int
+grn_column_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size)
+{
+ int len = 0;
+ char buf[GRN_TABLE_MAX_KEY_SIZE];
+ if (!obj) { return len; }
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ grn_id id = DB_OBJ(obj)->id;
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (id & GRN_OBJ_TMP_COLUMN) {
+ grn_id real_id = id & ~(GRN_OBJ_TMP_OBJECT | GRN_OBJ_TMP_COLUMN);
+ len = grn_pat_get_key(ctx, ctx->impl->temporary_columns,
+ real_id, buf, GRN_TABLE_MAX_KEY_SIZE);
+ }
+ } else if (id && id < GRN_ID_MAX) {
+ grn_db *s = (grn_db *)DB_OBJ(obj)->db;
+ len = grn_table_get_key(ctx, s->keys, id, buf, GRN_TABLE_MAX_KEY_SIZE);
+ }
+ if (len) {
+ int cl;
+ char *p = buf, *p0 = p, *pe = p + len;
+ for (; p < pe && (cl = grn_charlen(ctx, p, pe)); p += cl) {
+ if (*p == GRN_DB_DELIMITER && cl == 1) { p0 = p + cl; }
+ }
+ len = pe - p0;
+ if (len && len <= buf_size) {
+ grn_memcpy(namebuf, p0, len);
+ }
+ }
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_obj name;
+ grn_accessor *a;
+
+ GRN_TEXT_INIT(&name, 0);
+
+#define ADD_DELMITER() do { \
+ if (GRN_TEXT_LEN(&name) > 0) { \
+ GRN_TEXT_PUTC(ctx, &name, GRN_DB_DELIMITER); \
+ } \
+ } while (GRN_FALSE)
+
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_ID);
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ if (!a->next) {
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_KEY);
+ }
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (!a->next) {
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_VALUE);
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_SCORE);
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_NSUBRECS);
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_MAX);
+ break;
+ case GRN_ACCESSOR_GET_MIN :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_MIN);
+ break;
+ case GRN_ACCESSOR_GET_SUM :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_SUM);
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ ADD_DELMITER();
+ GRN_TEXT_PUTS(ctx, &name, GRN_COLUMN_NAME_AVG);
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ ADD_DELMITER();
+ {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ column_name_size = grn_column_name(ctx, a->obj,
+ column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_PUT(ctx, &name, column_name, column_name_size);
+ }
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ case GRN_ACCESSOR_LOOKUP :
+ case GRN_ACCESSOR_FUNCALL :
+ break;
+ }
+ }
+#undef ADD_DELIMITER
+
+ len = GRN_TEXT_LEN(&name);
+ if (len > 0 && len <= buf_size) {
+ grn_memcpy(namebuf, GRN_TEXT_VALUE(&name), len);
+ }
+
+ GRN_OBJ_FIN(ctx, &name);
+ }
+ GRN_API_RETURN(len);
+}
+
+grn_rc
+grn_column_name_(grn_ctx *ctx, grn_obj *obj, grn_obj *buf)
+{
+ if (GRN_DB_OBJP(obj)) {
+ uint32_t len = 0;
+ const char *p = NULL;
+ grn_id id = DB_OBJ(obj)->id;
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (id & GRN_OBJ_TMP_COLUMN) {
+ grn_id real_id = id & ~(GRN_OBJ_TMP_OBJECT | GRN_OBJ_TMP_COLUMN);
+ p = _grn_pat_key(ctx, ctx->impl->temporary_columns, real_id, &len);
+ }
+ } else if (id && id < GRN_ID_MAX) {
+ grn_db *s = (grn_db *)DB_OBJ(obj)->db;
+ p = _grn_table_key(ctx, s->keys, id, &len);
+ }
+ if (len) {
+ int cl;
+ const char *p0 = p, *pe = p + len;
+ for (; p < pe && (cl = grn_charlen(ctx, p, pe)); p += cl) {
+ if (*p == GRN_DB_DELIMITER && cl == 1) { p0 = p + cl; }
+ }
+ GRN_TEXT_PUT(ctx, buf, p0, pe - p0);
+ }
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_TEXT_PUT(ctx, buf, GRN_COLUMN_NAME_ID, GRN_COLUMN_NAME_ID_LEN);
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ if (!a->next) {
+ GRN_TEXT_PUT(ctx, buf, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN);
+ }
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (!a->next) {
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_VALUE,
+ GRN_COLUMN_NAME_VALUE_LEN);
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_SCORE,
+ GRN_COLUMN_NAME_SCORE_LEN);
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_NSUBRECS,
+ GRN_COLUMN_NAME_NSUBRECS_LEN);
+ break;
+ case GRN_ACCESSOR_GET_MAX :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_MAX,
+ GRN_COLUMN_NAME_MAX_LEN);
+ break;
+ case GRN_ACCESSOR_GET_MIN :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_MIN,
+ GRN_COLUMN_NAME_MIN_LEN);
+ break;
+ case GRN_ACCESSOR_GET_SUM :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_SUM,
+ GRN_COLUMN_NAME_SUM_LEN);
+ break;
+ case GRN_ACCESSOR_GET_AVG :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_AVG,
+ GRN_COLUMN_NAME_AVG_LEN);
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ grn_column_name_(ctx, a->obj, buf);
+ if (a->next) { GRN_TEXT_PUTC(ctx, buf, '.'); }
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ case GRN_ACCESSOR_LOOKUP :
+ case GRN_ACCESSOR_FUNCALL :
+ break;
+ }
+ }
+ }
+ return ctx->rc;
+}
+
+int
+grn_obj_expire(grn_ctx *ctx, grn_obj *obj, int threshold)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(0);
+}
+
+int
+grn_obj_check(grn_ctx *ctx, grn_obj *obj)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(0);
+}
+
+grn_rc
+grn_obj_lock(grn_ctx *ctx, grn_obj *obj, grn_id id, int timeout)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ rc = grn_io_lock(ctx, grn_obj_get_io(ctx, obj), timeout);
+ if (rc == GRN_SUCCESS && obj && obj->header.type == GRN_COLUMN_INDEX) {
+ rc = grn_io_lock(ctx, ((grn_ii *)obj)->chunk, timeout);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_unlock(grn_ctx *ctx, grn_obj *obj, grn_id id)
+{
+ GRN_API_ENTER;
+ if (obj && obj->header.type == GRN_COLUMN_INDEX) {
+ grn_io_unlock(((grn_ii *)obj)->chunk);
+ }
+ grn_io_unlock(grn_obj_get_io(ctx, obj));
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_user_data *
+grn_obj_user_data(grn_ctx *ctx, grn_obj *obj)
+{
+ if (!GRN_DB_OBJP(obj)) { return NULL; }
+ return &DB_OBJ(obj)->user_data;
+}
+
+grn_rc
+grn_obj_set_finalizer(grn_ctx *ctx, grn_obj *obj, grn_proc_func *func)
+{
+ if (!GRN_DB_OBJP(obj)) { return GRN_INVALID_ARGUMENT; }
+ DB_OBJ(obj)->finalizer = func;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_obj_clear_lock(grn_ctx *ctx, grn_obj *obj)
+{
+ GRN_API_ENTER;
+ switch (obj->header.type) {
+ case GRN_DB:
+ {
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *tbl = grn_ctx_at(ctx, id);
+ if (tbl) {
+ switch (tbl->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ grn_obj_clear_lock(ctx, tbl);
+ break;
+ }
+ } else {
+ if (ctx->rc != GRN_SUCCESS) {
+ ERRCLR(ctx);
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ }
+ grn_io_clear_lock(grn_obj_get_io(ctx, obj));
+ {
+ grn_db *db = (grn_db *)obj;
+ if (db->specs) {
+ grn_obj_clear_lock(ctx, (grn_obj *)(db->specs));
+ }
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ grn_array_queue_lock_clear(ctx, (grn_array *)obj);
+ /* fallthru */
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { grn_obj_clear_lock(ctx, col); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ grn_io_clear_lock(grn_obj_get_io(ctx, obj));
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE:
+ case GRN_COLUMN_VAR_SIZE:
+ grn_io_clear_lock(grn_obj_get_io(ctx, obj));
+ break;
+ case GRN_COLUMN_INDEX:
+ grn_io_clear_lock(grn_obj_get_io(ctx, obj));
+ if (obj) {
+ grn_io_clear_lock(((grn_ii *)obj)->chunk);
+ }
+ break;
+ }
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+unsigned int
+grn_obj_is_locked(grn_ctx *ctx, grn_obj *obj)
+{
+ unsigned int res = 0;
+ GRN_API_ENTER;
+ res = grn_io_is_locked(grn_obj_get_io(ctx, obj));
+ if (obj && obj->header.type == GRN_COLUMN_INDEX) {
+ res += grn_io_is_locked(((grn_ii *)obj)->chunk);
+ }
+ GRN_API_RETURN(res);
+}
+
+grn_rc
+grn_obj_flush(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+
+ GRN_API_ENTER;
+
+ switch (obj->header.type) {
+ case GRN_DB :
+ {
+ grn_db *db = (grn_db *)obj;
+ rc = grn_obj_flush(ctx, db->keys);
+ if (rc == GRN_SUCCESS && db->specs) {
+ rc = grn_obj_flush(ctx, (grn_obj *)(db->specs));
+ }
+ if (rc == GRN_SUCCESS) {
+ rc = grn_obj_flush(ctx, (grn_obj *)(db->config));
+ }
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_flush(ctx, (grn_dat *)obj);
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_ii_flush(ctx, (grn_ii *)obj);
+ break;
+ default :
+ {
+ grn_io *io;
+ io = grn_obj_get_io(ctx, obj);
+ if (io) {
+ rc = grn_io_flush(ctx, io);
+ }
+ }
+ break;
+ }
+
+ if (rc == GRN_SUCCESS &&
+ GRN_DB_OBJP(obj) &&
+ DB_OBJ(obj)->id != GRN_ID_NIL &&
+ !IS_TEMP(obj)) {
+ rc = grn_db_clean(ctx, DB_OBJ(obj)->db);
+ }
+
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+
+ GRN_API_ENTER;
+ switch (obj->header.type) {
+ case GRN_DB :
+ {
+ grn_table_cursor *cursor;
+ grn_id id;
+
+ cursor = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!cursor) {
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ while ((id = grn_table_cursor_next_inline(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *table = grn_ctx_at(ctx, id);
+ rc = GRN_SUCCESS;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ rc = grn_obj_flush_recursive(ctx, table);
+ break;
+ }
+ } else {
+ if (ctx->rc != GRN_SUCCESS) {
+ ERRCLR(ctx);
+ }
+ }
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cursor);
+ }
+ if (rc == GRN_SUCCESS) {
+ rc = grn_obj_flush(ctx, obj);
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_hash *columns;
+ columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (!columns) {
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)columns) > 0) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, columns, id, &key, NULL, NULL, {
+ grn_obj *column = grn_ctx_at(ctx, *key);
+ if (column) {
+ rc = grn_obj_flush(ctx, column);
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ });
+ }
+ grn_hash_close(ctx, columns);
+ }
+
+ if (rc == GRN_SUCCESS) {
+ rc = grn_obj_flush(ctx, obj);
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_INDEX :
+ rc = grn_obj_flush(ctx, obj);
+ break;
+ default :
+ {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, obj);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[flush] object must be DB, table or column: <%.*s>",
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ rc = ctx->rc;
+ GRN_OBJ_FIN(ctx, &inspected);
+ }
+ break;
+ }
+
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_obj_db(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj *db = NULL;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) { db = DB_OBJ(obj)->db; }
+ GRN_API_RETURN(db);
+}
+
+grn_id
+grn_obj_id(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ id = DB_OBJ(obj)->id;
+ }
+ GRN_API_RETURN(id);
+}
+
+int
+grn_obj_defrag(grn_ctx *ctx, grn_obj *obj, int threshold)
+{
+ int r = 0;
+ GRN_API_ENTER;
+ switch (obj->header.type) {
+ case GRN_DB:
+ {
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *ja = grn_ctx_at(ctx, id);
+ if (ja && ja->header.type == GRN_COLUMN_VAR_SIZE) {
+ r += grn_ja_defrag(ctx, (grn_ja *)ja, threshold);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) {
+ r += grn_obj_defrag(ctx, col, threshold);
+ grn_obj_unlink(ctx, col);
+ }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE:
+ r = grn_ja_defrag(ctx, (grn_ja *)obj, threshold);
+ break;
+ }
+ GRN_API_RETURN(r);
+}
+
+/**** sort ****/
+
+typedef struct {
+ grn_id id;
+ uint32_t size;
+ const void *value;
+} sort_reference_entry;
+
+enum {
+ KEY_ID = 0,
+ KEY_BULK,
+ KEY_INT8,
+ KEY_INT16,
+ KEY_INT32,
+ KEY_INT64,
+ KEY_UINT8,
+ KEY_UINT16,
+ KEY_UINT32,
+ KEY_UINT64,
+ KEY_FLOAT32,
+ KEY_FLOAT64,
+};
+
+#define CMPNUM(type) do {\
+ if (as) {\
+ if (bs) {\
+ type va = *((type *)(ap));\
+ type vb = *((type *)(bp));\
+ if (va != vb) { return va > vb; }\
+ } else {\
+ return 1;\
+ }\
+ } else {\
+ if (bs) { return 0; }\
+ }\
+} while (0)
+
+inline static int
+compare_reference(grn_ctx *ctx,
+ sort_reference_entry *a, sort_reference_entry *b,
+ grn_table_sort_key *keys, int n_keys)
+{
+ int i;
+ uint8_t type;
+ uint32_t as, bs;
+ const unsigned char *ap, *bp;
+ for (i = 0; i < n_keys; i++, keys++) {
+ if (i) {
+ const char *ap_raw, *bp_raw;
+ if (keys->flags & GRN_TABLE_SORT_DESC) {
+ ap_raw = grn_obj_get_value_(ctx, keys->key, b->id, &as);
+ bp_raw = grn_obj_get_value_(ctx, keys->key, a->id, &bs);
+ } else {
+ ap_raw = grn_obj_get_value_(ctx, keys->key, a->id, &as);
+ bp_raw = grn_obj_get_value_(ctx, keys->key, b->id, &bs);
+ }
+ ap = (const unsigned char *)ap_raw;
+ bp = (const unsigned char *)bp_raw;
+ } else {
+ if (keys->flags & GRN_TABLE_SORT_DESC) {
+ ap = b->value; as = b->size;
+ bp = a->value; bs = a->size;
+ } else {
+ ap = a->value; as = a->size;
+ bp = b->value; bs = b->size;
+ }
+ }
+ type = keys->offset;
+ switch (type) {
+ case KEY_ID :
+ if (ap != bp) { return ap > bp; }
+ break;
+ case KEY_BULK :
+ for (;; ap++, bp++, as--, bs--) {
+ if (!as) { if (bs) { return 0; } else { break; } }
+ if (!bs) { return 1; }
+ if (*ap < *bp) { return 0; }
+ if (*ap > *bp) { return 1; }
+ }
+ break;
+ case KEY_INT8 :
+ CMPNUM(int8_t);
+ break;
+ case KEY_INT16 :
+ CMPNUM(int16_t);
+ break;
+ case KEY_INT32 :
+ CMPNUM(int32_t);
+ break;
+ case KEY_INT64 :
+ CMPNUM(int64_t);
+ break;
+ case KEY_UINT8 :
+ CMPNUM(uint8_t);
+ break;
+ case KEY_UINT16 :
+ CMPNUM(uint16_t);
+ break;
+ case KEY_UINT32 :
+ CMPNUM(uint32_t);
+ break;
+ case KEY_UINT64 :
+ CMPNUM(uint64_t);
+ break;
+ case KEY_FLOAT32 :
+ if (as) {
+ if (bs) {
+ float va = *((float *)(ap));
+ float vb = *((float *)(bp));
+ if (va < vb || va > vb) { return va > vb; }
+ } else {
+ return 1;
+ }
+ } else {
+ if (bs) { return 0; }
+ }
+ break;
+ case KEY_FLOAT64 :
+ if (as) {
+ if (bs) {
+ double va = *((double *)(ap));
+ double vb = *((double *)(bp));
+ if (va < vb || va > vb) { return va > vb; }
+ } else {
+ return 1;
+ }
+ } else {
+ if (bs) { return 0; }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+inline static void
+swap_reference(sort_reference_entry *a, sort_reference_entry *b)
+{
+ sort_reference_entry c_ = *a;
+ *a = *b;
+ *b = c_;
+}
+
+inline static sort_reference_entry *
+part_reference(grn_ctx *ctx,
+ sort_reference_entry *b, sort_reference_entry *e,
+ grn_table_sort_key *keys, int n_keys)
+{
+ sort_reference_entry *c;
+ intptr_t d = e - b;
+ if (compare_reference(ctx, b, e, keys, n_keys)) {
+ swap_reference(b, e);
+ }
+ if (d < 2) { return NULL; }
+ c = b + (d >> 1);
+ if (compare_reference(ctx, b, c, keys, n_keys)) {
+ swap_reference(b, c);
+ } else {
+ if (compare_reference(ctx, c, e, keys, n_keys)) {
+ swap_reference(c, e);
+ }
+ }
+ if (d < 3) { return NULL; }
+ b++;
+ swap_reference(b, c);
+ c = b;
+ for (;;) {
+ do {
+ b++;
+ } while (compare_reference(ctx, c, b, keys, n_keys));
+ do {
+ e--;
+ } while (compare_reference(ctx, e, c, keys, n_keys));
+ if (b >= e) { break; }
+ swap_reference(b, e);
+ }
+ swap_reference(c, e);
+ return e;
+}
+
+static void
+sort_reference(grn_ctx *ctx,
+ sort_reference_entry *head, sort_reference_entry *tail,
+ int from, int to,
+ grn_table_sort_key *keys, int n_keys)
+{
+ sort_reference_entry *c;
+ if (head < tail && (c = part_reference(ctx, head, tail, keys, n_keys))) {
+ intptr_t m = c - head + 1;
+ if (from < m - 1) {
+ sort_reference(ctx, head, c - 1, from, to, keys, n_keys);
+ }
+ if (m < to) {
+ sort_reference(ctx, c + 1, tail, from - m, to - m, keys, n_keys);
+ }
+ }
+}
+
+static sort_reference_entry *
+pack_reference(grn_ctx *ctx, grn_obj *table,
+ sort_reference_entry *head, sort_reference_entry *tail,
+ grn_table_sort_key *keys, int n_keys)
+{
+ int i = 0;
+ sort_reference_entry e, c;
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!tc) { return NULL; }
+ if ((c.id = grn_table_cursor_next_inline(ctx, tc))) {
+ c.value = grn_obj_get_value_(ctx, keys->key, c.id, &c.size);
+ while ((e.id = grn_table_cursor_next_inline(ctx, tc))) {
+ e.value = grn_obj_get_value_(ctx, keys->key, e.id, &e.size);
+ if (compare_reference(ctx, &c, &e, keys, n_keys)) {
+ *head++ = e;
+ } else {
+ *tail-- = e;
+ }
+ i++;
+ }
+ *head = c;
+ i++;
+ }
+ grn_table_cursor_close(ctx, tc);
+ return i > 2 ? head : NULL;
+}
+
+static int
+grn_table_sort_reference(grn_ctx *ctx, grn_obj *table,
+ int offset, int limit,
+ grn_obj *result,
+ grn_table_sort_key *keys, int n_keys)
+{
+ int e, n;
+ sort_reference_entry *array, *ep;
+ e = offset + limit;
+ n = grn_table_size(ctx, table);
+ if (!(array = GRN_MALLOC(sizeof(sort_reference_entry) * n))) {
+ return 0;
+ }
+ if ((ep = pack_reference(ctx, table, array, array + n - 1, keys, n_keys))) {
+ intptr_t m = ep - array + 1;
+ if (offset < m - 1) {
+ sort_reference(ctx, array, ep - 1, offset, e, keys, n_keys);
+ }
+ if (m < e) {
+ sort_reference(ctx, ep + 1, array + n - 1, offset - m, e - m, keys, n_keys);
+ }
+ }
+ {
+ int i;
+ grn_id *v;
+ for (i = 0, ep = array + offset; i < limit && ep < array + n; i++, ep++) {
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+ *v = ep->id;
+ }
+ GRN_FREE(array);
+ return i;
+ }
+}
+
+
+typedef struct {
+ grn_id id;
+ grn_obj value;
+} sort_value_entry;
+
+inline static int
+compare_value(grn_ctx *ctx,
+ sort_value_entry *a, sort_value_entry *b,
+ grn_table_sort_key *keys, int n_keys,
+ grn_obj *a_buffer, grn_obj *b_buffer)
+{
+ int i;
+ uint8_t type;
+ uint32_t as, bs;
+ const unsigned char *ap, *bp;
+ for (i = 0; i < n_keys; i++, keys++) {
+ if (i) {
+ GRN_BULK_REWIND(a_buffer);
+ GRN_BULK_REWIND(b_buffer);
+ if (keys->flags & GRN_TABLE_SORT_DESC) {
+ grn_obj_get_value(ctx, keys->key, b->id, a_buffer);
+ grn_obj_get_value(ctx, keys->key, a->id, b_buffer);
+ } else {
+ grn_obj_get_value(ctx, keys->key, a->id, a_buffer);
+ grn_obj_get_value(ctx, keys->key, b->id, b_buffer);
+ }
+ ap = (const unsigned char *)GRN_BULK_HEAD(a_buffer);
+ as = GRN_BULK_VSIZE(a_buffer);
+ bp = (const unsigned char *)GRN_BULK_HEAD(b_buffer);
+ bs = GRN_BULK_VSIZE(b_buffer);
+ } else {
+ if (keys->flags & GRN_TABLE_SORT_DESC) {
+ ap = (const unsigned char *)GRN_BULK_HEAD(&b->value);
+ as = GRN_BULK_VSIZE(&b->value);
+ bp = (const unsigned char *)GRN_BULK_HEAD(&a->value);
+ bs = GRN_BULK_VSIZE(&a->value);
+ } else {
+ ap = (const unsigned char *)GRN_BULK_HEAD(&a->value);
+ as = GRN_BULK_VSIZE(&a->value);
+ bp = (const unsigned char *)GRN_BULK_HEAD(&b->value);
+ bs = GRN_BULK_VSIZE(&b->value);
+ }
+ }
+ type = keys->offset;
+ switch (type) {
+ case KEY_ID :
+ if (ap != bp) { return ap > bp; }
+ break;
+ case KEY_BULK :
+ for (;; ap++, bp++, as--, bs--) {
+ if (!as) { if (bs) { return 0; } else { break; } }
+ if (!bs) { return 1; }
+ if (*ap < *bp) { return 0; }
+ if (*ap > *bp) { return 1; }
+ }
+ break;
+ case KEY_INT8 :
+ CMPNUM(int8_t);
+ break;
+ case KEY_INT16 :
+ CMPNUM(int16_t);
+ break;
+ case KEY_INT32 :
+ CMPNUM(int32_t);
+ break;
+ case KEY_INT64 :
+ CMPNUM(int64_t);
+ break;
+ case KEY_UINT8 :
+ CMPNUM(uint8_t);
+ break;
+ case KEY_UINT16 :
+ CMPNUM(uint16_t);
+ break;
+ case KEY_UINT32 :
+ CMPNUM(uint32_t);
+ break;
+ case KEY_UINT64 :
+ CMPNUM(uint64_t);
+ break;
+ case KEY_FLOAT32 :
+ if (as) {
+ if (bs) {
+ float va = *((float *)(ap));
+ float vb = *((float *)(bp));
+ if (va < vb || va > vb) { return va > vb; }
+ } else {
+ return 1;
+ }
+ } else {
+ if (bs) { return 0; }
+ }
+ break;
+ case KEY_FLOAT64 :
+ if (as) {
+ if (bs) {
+ double va = *((double *)(ap));
+ double vb = *((double *)(bp));
+ if (va < vb || va > vb) { return va > vb; }
+ } else {
+ return 1;
+ }
+ } else {
+ if (bs) { return 0; }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+inline static void
+swap_value(sort_value_entry *a, sort_value_entry *b)
+{
+ sort_value_entry c_ = *a;
+ *a = *b;
+ *b = c_;
+}
+
+inline static sort_value_entry *
+part_value(grn_ctx *ctx,
+ sort_value_entry *b, sort_value_entry *e,
+ grn_table_sort_key *keys, int n_keys,
+ grn_obj *a_buffer, grn_obj *b_buffer)
+{
+ sort_value_entry *c;
+ intptr_t d = e - b;
+ if (compare_value(ctx, b, e, keys, n_keys, a_buffer, b_buffer)) {
+ swap_value(b, e);
+ }
+ if (d < 2) { return NULL; }
+ c = b + (d >> 1);
+ if (compare_value(ctx, b, c, keys, n_keys, a_buffer, b_buffer)) {
+ swap_value(b, c);
+ } else {
+ if (compare_value(ctx, c, e, keys, n_keys, a_buffer, b_buffer)) {
+ swap_value(c, e);
+ }
+ }
+ if (d < 3) { return NULL; }
+ b++;
+ swap_value(b, c);
+ c = b;
+ for (;;) {
+ do {
+ b++;
+ } while (compare_value(ctx, c, b, keys, n_keys, a_buffer, b_buffer));
+ do {
+ e--;
+ } while (compare_value(ctx, e, c, keys, n_keys, a_buffer, b_buffer));
+ if (b >= e) { break; }
+ swap_value(b, e);
+ }
+ swap_value(c, e);
+ return e;
+}
+
+static void
+sort_value(grn_ctx *ctx,
+ sort_value_entry *head, sort_value_entry *tail,
+ int from, int to,
+ grn_table_sort_key *keys, int n_keys,
+ grn_obj *a_buffer, grn_obj *b_buffer)
+{
+ sort_value_entry *c;
+ if (head < tail && (c = part_value(ctx, head, tail, keys, n_keys,
+ a_buffer, b_buffer))) {
+ intptr_t m = c - head + 1;
+ if (from < m - 1) {
+ sort_value(ctx, head, c - 1, from, to, keys, n_keys, a_buffer, b_buffer);
+ }
+ if (m < to) {
+ sort_value(ctx, c + 1, tail, from - m, to - m, keys, n_keys,
+ a_buffer, b_buffer);
+ }
+ }
+}
+
+static sort_value_entry *
+pack_value(grn_ctx *ctx, grn_obj *table,
+ sort_value_entry *head, sort_value_entry *tail,
+ grn_table_sort_key *keys, int n_keys,
+ grn_obj *a_buffer, grn_obj *b_buffer)
+{
+ int i = 0;
+ sort_value_entry e, c;
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!tc) { return NULL; }
+ if ((c.id = grn_table_cursor_next_inline(ctx, tc))) {
+ GRN_TEXT_INIT(&c.value, 0);
+ grn_obj_get_value(ctx, keys->key, c.id, &c.value);
+ while ((e.id = grn_table_cursor_next_inline(ctx, tc))) {
+ GRN_TEXT_INIT(&e.value, 0);
+ grn_obj_get_value(ctx, keys->key, e.id, &e.value);
+ if (compare_value(ctx, &c, &e, keys, n_keys, a_buffer, b_buffer)) {
+ *head++ = e;
+ } else {
+ *tail-- = e;
+ }
+ i++;
+ }
+ *head = c;
+ i++;
+ }
+ grn_table_cursor_close(ctx, tc);
+ return i > 2 ? head : NULL;
+}
+
+static int
+grn_table_sort_value(grn_ctx *ctx, grn_obj *table,
+ int offset, int limit,
+ grn_obj *result,
+ grn_table_sort_key *keys, int n_keys)
+{
+ int e, n;
+ sort_value_entry *array, *ep;
+ e = offset + limit;
+ n = grn_table_size(ctx, table);
+ if (!(array = GRN_MALLOC(sizeof(sort_value_entry) * n))) {
+ return 0;
+ }
+ {
+ grn_obj a_buffer;
+ grn_obj b_buffer;
+ GRN_TEXT_INIT(&a_buffer, 0);
+ GRN_TEXT_INIT(&b_buffer, 0);
+ if ((ep = pack_value(ctx, table, array, array + n - 1, keys, n_keys,
+ &a_buffer, &b_buffer))) {
+ intptr_t m = ep - array + 1;
+ if (offset < m - 1) {
+ sort_value(ctx, array, ep - 1, offset, e, keys, n_keys,
+ &a_buffer, &b_buffer);
+ }
+ if (m < e) {
+ sort_value(ctx, ep + 1, array + n - 1, offset - m, e - m, keys, n_keys,
+ &a_buffer, &b_buffer);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &a_buffer);
+ GRN_OBJ_FIN(ctx, &b_buffer);
+ }
+ {
+ int i;
+ grn_id *v;
+ for (i = 0, ep = array + offset; i < limit && ep < array + n; i++, ep++) {
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+ *v = ep->id;
+ }
+ GRN_FREE(array);
+ return i;
+ }
+}
+
+static grn_bool
+is_compressed_column(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj *target_obj;
+
+ if (!obj) {
+ return GRN_FALSE;
+ }
+
+ if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)obj;
+ while (a->next) {
+ a = a->next;
+ }
+ target_obj = a->obj;
+ } else {
+ target_obj = obj;
+ }
+
+ if (target_obj->header.type != GRN_COLUMN_VAR_SIZE) {
+ return GRN_FALSE;
+ }
+
+ switch (target_obj->header.flags & GRN_OBJ_COMPRESS_MASK) {
+ case GRN_OBJ_COMPRESS_ZLIB :
+ case GRN_OBJ_COMPRESS_LZ4 :
+ case GRN_OBJ_COMPRESS_ZSTD :
+ return GRN_TRUE;
+ default :
+ return GRN_FALSE;
+ }
+}
+
+static grn_bool
+is_sub_record_accessor(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_accessor *accessor;
+
+ if (!obj) {
+ return GRN_FALSE;
+ }
+
+ if (obj->header.type != GRN_ACCESSOR) {
+ return GRN_FALSE;
+ }
+
+ for (accessor = (grn_accessor *)obj; accessor; accessor = accessor->next) {
+ switch (accessor->action) {
+ case GRN_ACCESSOR_GET_VALUE :
+ if (GRN_TABLE_IS_MULTI_KEYS_GROUPED(accessor->obj)) {
+ return GRN_TRUE;
+ }
+ break;
+ default :
+ break;
+ }
+ }
+
+ return GRN_FALSE;
+}
+
+static grn_bool
+is_encoded_pat_key_accessor(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_accessor *accessor;
+
+ if (!grn_obj_is_accessor(ctx, obj)) {
+ return GRN_FALSE;
+ }
+
+ accessor = (grn_accessor *)obj;
+ while (accessor->next) {
+ accessor = accessor->next;
+ }
+
+ if (accessor->action != GRN_ACCESSOR_GET_KEY) {
+ return GRN_FALSE;
+ }
+
+ if (accessor->obj->header.type != GRN_TABLE_PAT_KEY) {
+ return GRN_FALSE;
+ }
+
+ return grn_pat_is_key_encoded(ctx, (grn_pat *)(accessor->obj));
+}
+
+static int
+range_is_idp(grn_obj *obj)
+{
+ if (obj && obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ if (a->action == GRN_ACCESSOR_GET_ID) { return 1; }
+ }
+ }
+ return 0;
+}
+
+int
+grn_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
+ grn_obj *result, grn_table_sort_key *keys, int n_keys)
+{
+ grn_rc rc;
+ grn_obj *index;
+ int n, e, i = 0;
+ GRN_API_ENTER;
+ if (!n_keys || !keys) {
+ WARN(GRN_INVALID_ARGUMENT, "keys is null");
+ goto exit;
+ }
+ if (!table) {
+ WARN(GRN_INVALID_ARGUMENT, "table is null");
+ goto exit;
+ }
+ if (!(result && result->header.type == GRN_TABLE_NO_KEY)) {
+ WARN(GRN_INVALID_ARGUMENT, "result is not a array");
+ goto exit;
+ }
+ n = grn_table_size(ctx, table);
+ if ((rc = grn_normalize_offset_and_limit(ctx, n, &offset, &limit))) {
+ ERR(rc, "grn_normalize_offset_and_limit failed");
+ goto exit;
+ } else {
+ e = offset + limit;
+ }
+ if (keys->flags & GRN_TABLE_SORT_GEO) {
+ if (n_keys == 2) {
+ i = grn_geo_table_sort(ctx, table, offset, limit, result,
+ keys[0].key, keys[1].key);
+ } else {
+ i = 0;
+ }
+ goto exit;
+ }
+ if (n_keys == 1 && !GRN_ACCESSORP(keys->key) &&
+ grn_column_index(ctx, keys->key, GRN_OP_LESS, &index, 1, NULL)) {
+ grn_id tid;
+ grn_pat *lexicon = (grn_pat *)grn_ctx_at(ctx, index->header.domain);
+ grn_pat_cursor *pc = grn_pat_cursor_open(ctx, lexicon, NULL, 0, NULL, 0,
+ 0 /* offset : can be used in unique index */,
+ -1 /* limit : can be used in unique index */,
+ (keys->flags & GRN_TABLE_SORT_DESC)
+ ? GRN_CURSOR_DESCENDING
+ : GRN_CURSOR_ASCENDING);
+ if (pc) {
+ while (i < e && (tid = grn_pat_cursor_next(ctx, pc))) {
+ grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+ if (ic) {
+ grn_posting *posting;
+ while (i < e && (posting = grn_ii_cursor_next(ctx, ic))) {
+ if (offset <= i) {
+ grn_id *v;
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+ *v = posting->rid;
+ }
+ i++;
+ }
+ grn_ii_cursor_close(ctx, ic);
+ }
+ }
+ grn_pat_cursor_close(ctx, pc);
+ }
+ } else {
+ int j;
+ grn_bool have_compressed_column = GRN_FALSE;
+ grn_bool have_sub_record_accessor = GRN_FALSE;
+ grn_bool have_encoded_pat_key_accessor = GRN_FALSE;
+ grn_bool have_index_value_get = GRN_FALSE;
+ grn_table_sort_key *kp;
+ for (kp = keys, j = n_keys; j; kp++, j--) {
+ if (is_compressed_column(ctx, kp->key)) {
+ have_compressed_column = GRN_TRUE;
+ }
+ if (is_sub_record_accessor(ctx, kp->key)) {
+ have_sub_record_accessor = GRN_TRUE;
+ }
+ if (is_encoded_pat_key_accessor(ctx, kp->key)) {
+ have_encoded_pat_key_accessor = GRN_TRUE;
+ }
+ if (range_is_idp(kp->key)) {
+ kp->offset = KEY_ID;
+ } else {
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, kp->key));
+ if (range->header.type == GRN_TYPE) {
+ if (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ kp->offset = KEY_BULK;
+ } else {
+ uint8_t key_type = range->header.flags & GRN_OBJ_KEY_MASK;
+ switch (key_type) {
+ case GRN_OBJ_KEY_UINT :
+ case GRN_OBJ_KEY_GEO_POINT :
+ switch (GRN_TYPE_SIZE(DB_OBJ(range))) {
+ case 1 :
+ kp->offset = KEY_UINT8;
+ break;
+ case 2 :
+ kp->offset = KEY_UINT16;
+ break;
+ case 4 :
+ kp->offset = KEY_UINT32;
+ break;
+ case 8 :
+ kp->offset = KEY_UINT64;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unsupported uint value");
+ goto exit;
+ }
+ break;
+ case GRN_OBJ_KEY_INT :
+ switch (GRN_TYPE_SIZE(DB_OBJ(range))) {
+ case 1 :
+ kp->offset = KEY_INT8;
+ break;
+ case 2 :
+ kp->offset = KEY_INT16;
+ break;
+ case 4 :
+ kp->offset = KEY_INT32;
+ break;
+ case 8 :
+ kp->offset = KEY_INT64;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unsupported int value");
+ goto exit;
+ }
+ break;
+ case GRN_OBJ_KEY_FLOAT :
+ switch (GRN_TYPE_SIZE(DB_OBJ(range))) {
+ case 4 :
+ kp->offset = KEY_FLOAT32;
+ break;
+ case 8 :
+ kp->offset = KEY_FLOAT64;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unsupported float value");
+ goto exit;
+ }
+ break;
+ }
+ }
+ } else {
+ if (kp->key->header.type == GRN_COLUMN_INDEX) {
+ have_index_value_get = GRN_TRUE;
+ }
+ kp->offset = KEY_UINT32;
+ }
+ }
+ }
+ if (have_compressed_column ||
+ have_sub_record_accessor ||
+ have_encoded_pat_key_accessor ||
+ have_index_value_get) {
+ i = grn_table_sort_value(ctx, table, offset, limit, result,
+ keys, n_keys);
+ } else {
+ i = grn_table_sort_reference(ctx, table, offset, limit, result,
+ keys, n_keys);
+ }
+ }
+exit :
+ GRN_API_RETURN(i);
+}
+
+static grn_obj *
+deftype(grn_ctx *ctx, const char *name,
+ grn_obj_flags flags, unsigned int size)
+{
+ grn_obj *o = grn_ctx_get(ctx, name, strlen(name));
+ if (!o) { o = grn_type_create(ctx, name, strlen(name), flags, size); }
+ return o;
+}
+
+grn_rc
+grn_db_init_builtin_types(grn_ctx *ctx)
+{
+ grn_id id;
+ grn_obj *obj, *db = ctx->impl->db;
+ char buf[] = "Sys00";
+ grn_obj_register(ctx, db, buf, 5);
+ obj = deftype(ctx, "Object",
+ GRN_OBJ_KEY_UINT, sizeof(uint64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_OBJECT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Bool",
+ GRN_OBJ_KEY_UINT, sizeof(uint8_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_BOOL) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int8",
+ GRN_OBJ_KEY_INT, sizeof(int8_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT8) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt8",
+ GRN_OBJ_KEY_UINT, sizeof(uint8_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT8) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int16",
+ GRN_OBJ_KEY_INT, sizeof(int16_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT16) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt16",
+ GRN_OBJ_KEY_UINT, sizeof(uint16_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT16) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int32",
+ GRN_OBJ_KEY_INT, sizeof(int32_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT32) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt32",
+ GRN_OBJ_KEY_UINT, sizeof(uint32_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT32) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int64",
+ GRN_OBJ_KEY_INT, sizeof(int64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT64) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt64",
+ GRN_OBJ_KEY_UINT, sizeof(uint64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT64) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Float",
+ GRN_OBJ_KEY_FLOAT, sizeof(double));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_FLOAT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Time",
+ GRN_OBJ_KEY_INT, sizeof(int64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_TIME) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "ShortText",
+ GRN_OBJ_KEY_VAR_SIZE, GRN_TABLE_MAX_KEY_SIZE);
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_SHORT_TEXT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Text",
+ GRN_OBJ_KEY_VAR_SIZE, 1 << 16);
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_TEXT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "LongText",
+ GRN_OBJ_KEY_VAR_SIZE, 1U << 31);
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_LONG_TEXT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "TokyoGeoPoint",
+ GRN_OBJ_KEY_GEO_POINT, sizeof(grn_geo_point));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_TOKYO_GEO_POINT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "WGS84GeoPoint",
+ GRN_OBJ_KEY_GEO_POINT, sizeof(grn_geo_point));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_WGS84_GEO_POINT) { return GRN_FILE_CORRUPT; }
+ for (id = grn_db_curr_id(ctx, db) + 1; id < GRN_DB_MECAB; id++) {
+ grn_itoh(id, buf + 3, 2);
+ grn_obj_register(ctx, db, buf, 5);
+ }
+#ifdef GRN_WITH_MECAB
+ if (grn_db_init_mecab_tokenizer(ctx)) {
+ ERRCLR(ctx);
+#endif
+ grn_obj_register(ctx, db, "TokenMecab", 10);
+#ifdef GRN_WITH_MECAB
+ }
+#endif
+ grn_db_init_builtin_tokenizers(ctx);
+ grn_db_init_builtin_normalizers(ctx);
+ grn_db_init_builtin_scorers(ctx);
+ for (id = grn_db_curr_id(ctx, db) + 1; id < 128; id++) {
+ grn_itoh(id, buf + 3, 2);
+ grn_obj_register(ctx, db, buf, 5);
+ }
+ grn_db_init_builtin_commands(ctx);
+ grn_db_init_builtin_window_functions(ctx);
+ for (id = grn_db_curr_id(ctx, db) + 1; id < GRN_N_RESERVED_TYPES; id++) {
+ grn_itoh(id, buf + 3, 2);
+ grn_obj_register(ctx, db, buf, 5);
+ }
+ return ctx->rc;
+}
+
+#define MULTI_COLUMN_INDEXP(i) (DB_OBJ(i)->source_size > sizeof(grn_id))
+
+static grn_obj *
+grn_index_column_get_tokenizer(grn_ctx *ctx, grn_obj *index_column)
+{
+ grn_obj *tokenizer;
+ grn_obj *lexicon;
+
+ lexicon = grn_ctx_at(ctx, index_column->header.domain);
+ if (!lexicon) {
+ return NULL;
+ }
+
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL, NULL);
+ return tokenizer;
+}
+
+static grn_bool
+is_full_text_searchable_index(grn_ctx *ctx, grn_obj *index_column)
+{
+ grn_obj *tokenizer;
+
+ tokenizer = grn_index_column_get_tokenizer(ctx, index_column);
+ return tokenizer != NULL;
+}
+
+static int
+grn_column_find_index_data_column_equal(grn_ctx *ctx, grn_obj *obj,
+ grn_operator op,
+ grn_index_datum *index_data,
+ unsigned int n_index_data,
+ grn_obj **index_buf, int buf_size,
+ int *section_buf)
+{
+ int n = 0;
+ grn_obj **ip = index_buf;
+ grn_hook *hooks;
+
+ for (hooks = DB_OBJ(obj)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section;
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if (obj->header.type != GRN_COLUMN_FIX_SIZE) {
+ if (is_full_text_searchable_index(ctx, target)) { continue; }
+ }
+ section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0;
+ if (section_buf) { *section_buf = section; }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static grn_bool
+is_valid_regexp_index(grn_ctx *ctx, grn_obj *index_column)
+{
+ grn_obj *tokenizer;
+
+ tokenizer = grn_index_column_get_tokenizer(ctx, index_column);
+ /* TODO: Restrict to TokenRegexp? */
+ return tokenizer != NULL;
+}
+
+static int
+grn_column_find_index_data_column_match(grn_ctx *ctx, grn_obj *obj,
+ grn_operator op,
+ grn_index_datum *index_data,
+ unsigned int n_index_data,
+ grn_obj **index_buf, int buf_size,
+ int *section_buf)
+{
+ int n = 0;
+ grn_obj **ip = index_buf;
+ grn_hook_entry hook_entry;
+ grn_hook *hooks;
+ grn_bool prefer_full_text_search_index = GRN_FALSE;
+
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ hook_entry = GRN_HOOK_INSERT;
+ break;
+ default :
+ hook_entry = GRN_HOOK_SET;
+ break;
+ }
+
+ if (op != GRN_OP_REGEXP && !grn_column_is_vector(ctx, obj)) {
+ prefer_full_text_search_index = GRN_TRUE;
+ }
+
+ if (prefer_full_text_search_index) {
+ for (hooks = DB_OBJ(obj)->hooks[hook_entry]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section;
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if (!is_full_text_searchable_index(ctx, target)) { continue; }
+ section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0;
+ if (section_buf) { *section_buf = section; }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+ }
+
+ for (hooks = DB_OBJ(obj)->hooks[hook_entry]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section;
+
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if (op == GRN_OP_REGEXP && !is_valid_regexp_index(ctx, target)) {
+ continue;
+ }
+
+ if (prefer_full_text_search_index) {
+ if (is_full_text_searchable_index(ctx, target)) { continue; }
+ }
+
+ section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0;
+ if (section_buf) { *section_buf = section; }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static int
+grn_column_find_index_data_column_range(grn_ctx *ctx, grn_obj *obj,
+ grn_operator op,
+ grn_index_datum *index_data,
+ unsigned int n_index_data,
+ grn_obj **index_buf, int buf_size,
+ int *section_buf)
+{
+ int n = 0;
+ grn_obj **ip = index_buf;
+ grn_hook_entry hook_entry;
+ grn_hook *hooks;
+
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ hook_entry = GRN_HOOK_INSERT;
+ break;
+ default :
+ hook_entry = GRN_HOOK_SET;
+ break;
+ }
+
+ for (hooks = DB_OBJ(obj)->hooks[hook_entry]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section;
+ if (!target) { continue; }
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0;
+ if (section_buf) { *section_buf = section; }
+ {
+ grn_obj *tokenizer, *lexicon = grn_ctx_at(ctx, target->header.domain);
+ if (!lexicon) { continue; }
+ if (lexicon->header.type != GRN_TABLE_PAT_KEY) { continue; }
+ /* FIXME: GRN_TABLE_DAT_KEY should be supported */
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL, NULL);
+ if (tokenizer) { continue; }
+ }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static grn_bool
+is_valid_match_index(grn_ctx *ctx, grn_obj *index_column)
+{
+ return GRN_TRUE;
+}
+
+static grn_bool
+is_valid_range_index(grn_ctx *ctx, grn_obj *index_column)
+{
+ grn_obj *tokenizer;
+ grn_obj *lexicon;
+
+ lexicon = grn_ctx_at(ctx, index_column->header.domain);
+ if (!lexicon) { return GRN_FALSE; }
+ /* FIXME: GRN_TABLE_DAT_KEY should be supported */
+ if (lexicon->header.type != GRN_TABLE_PAT_KEY) {
+ grn_obj_unlink(ctx, lexicon);
+ return GRN_FALSE;
+ }
+
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL, NULL);
+ grn_obj_unlink(ctx, lexicon);
+ if (tokenizer) { return GRN_FALSE; }
+
+ return GRN_TRUE;
+}
+
+static grn_bool
+is_valid_index(grn_ctx *ctx, grn_obj *index_column, grn_operator op)
+{
+ switch (op) {
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ return is_valid_match_index(ctx, index_column);
+ break;
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ return is_valid_range_index(ctx, index_column);
+ break;
+ case GRN_OP_REGEXP :
+ return is_valid_regexp_index(ctx, index_column);
+ break;
+ default :
+ return GRN_FALSE;
+ break;
+ }
+}
+
+static int
+find_section(grn_ctx *ctx, grn_obj *index_column, grn_obj *indexed_column)
+{
+ int section = 0;
+ grn_id indexed_column_id;
+ grn_id *source_ids;
+ int i, n_source_ids;
+
+ indexed_column_id = DB_OBJ(indexed_column)->id;
+
+ source_ids = DB_OBJ(index_column)->source;
+ n_source_ids = DB_OBJ(index_column)->source_size / sizeof(grn_id);
+ for (i = 0; i < n_source_ids; i++) {
+ grn_id source_id = source_ids[i];
+ if (source_id == indexed_column_id) {
+ section = i + 1;
+ break;
+ }
+ }
+
+ return section;
+}
+
+static int
+grn_column_find_index_data_accessor_index_column(grn_ctx *ctx, grn_accessor *a,
+ grn_operator op,
+ grn_index_datum *index_data,
+ unsigned int n_index_data,
+ grn_obj **index_buf,
+ int buf_size,
+ int *section_buf)
+{
+ grn_obj *index_column = a->obj;
+ int section = 0;
+
+ if (!is_valid_index(ctx, index_column, op)) {
+ return 0;
+ }
+
+ if (a->next) {
+ int specified_section;
+ grn_bool is_invalid_section;
+ if (a->next->next) {
+ return 0;
+ }
+ specified_section = find_section(ctx, index_column, a->next->obj);
+ is_invalid_section = (specified_section == 0);
+ if (is_invalid_section) {
+ return 0;
+ }
+ section = specified_section;
+ if (section_buf) {
+ *section_buf = section;
+ }
+ }
+ if (buf_size > 0) {
+ *index_buf = index_column;
+ }
+ if (n_index_data > 0) {
+ index_data[0].index = index_column;
+ index_data[0].section = section;
+ }
+
+ return 1;
+}
+
+static grn_bool
+grn_column_find_index_data_accessor_is_key_search(grn_ctx *ctx,
+ grn_accessor *accessor,
+ grn_operator op)
+{
+ if (accessor->next) {
+ return GRN_FALSE;
+ }
+
+ if (accessor->action != GRN_ACCESSOR_GET_KEY) {
+ return GRN_FALSE;
+ }
+
+ if (!grn_obj_is_table(ctx, accessor->obj)) {
+ return GRN_FALSE;
+ }
+
+ switch (op) {
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ switch (accessor->obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ return GRN_TRUE;
+ default :
+ return GRN_FALSE;
+ }
+ case GRN_OP_EQUAL :
+ switch (accessor->obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ return GRN_TRUE;
+ default :
+ return GRN_FALSE;
+ }
+ default :
+ return GRN_FALSE;
+ }
+}
+
+static int
+grn_column_find_index_data_accessor_match(grn_ctx *ctx, grn_obj *obj,
+ grn_operator op,
+ grn_index_datum *index_data,
+ unsigned n_index_data,
+ grn_obj **index_buf, int buf_size,
+ int *section_buf)
+{
+ int n = 0;
+ grn_obj **ip = index_buf;
+ grn_accessor *a = (grn_accessor *)obj;
+
+ while (a) {
+ grn_hook *hooks;
+ grn_bool found = GRN_FALSE;
+ grn_hook_entry entry = (grn_hook_entry)-1;
+
+ if (a->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ GRN_OBJ_INDEX_COLUMNP(a->obj)) {
+ return grn_column_find_index_data_accessor_index_column(ctx, a, op,
+ index_data,
+ n_index_data,
+ index_buf,
+ buf_size,
+ section_buf);
+ }
+
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ entry = GRN_HOOK_INSERT;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ entry = GRN_HOOK_SET;
+ break;
+ default :
+ break;
+ }
+
+ if (entry == (grn_hook_entry)-1) {
+ break;
+ }
+
+ for (hooks = DB_OBJ(a->obj)->hooks[entry]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+
+ found = GRN_TRUE;
+ if (!a->next) {
+ int section;
+
+ if (!is_valid_index(ctx, target, op)) {
+ continue;
+ }
+
+ section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0;
+ if (section_buf) {
+ *section_buf = section;
+ }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+ }
+
+ if (!found &&
+ grn_column_find_index_data_accessor_is_key_search(ctx, a, op)) {
+ grn_obj *index;
+ int section = 0;
+
+ if ((grn_obj *)a == obj) {
+ index = a->obj;
+ } else {
+ index = (grn_obj *)a;
+ }
+
+ found = GRN_TRUE;
+ if (section_buf) {
+ *section_buf = section;
+ }
+ if (n < buf_size) {
+ *ip++ = index;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = index;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+
+ if (!found &&
+ a->next &&
+ grn_obj_is_table(ctx, a->obj) &&
+ a->obj->header.domain == a->next->obj->header.domain) {
+ grn_obj *index = (grn_obj *)a;
+ int section = 0;
+
+ found = GRN_TRUE;
+ if (section_buf) {
+ *section_buf = section;
+ }
+ if (n < buf_size) {
+ *ip++ = index;
+ }
+ if ((unsigned int) n < n_index_data) {
+ index_data[n].index = index;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+
+ if (!found) {
+ break;
+ }
+ a = a->next;
+ }
+
+ return n;
+}
+
+static int
+grn_column_find_index_data_accessor(grn_ctx *ctx, grn_obj *obj,
+ grn_operator op,
+ grn_index_datum *index_data,
+ unsigned n_index_data,
+ grn_obj **index_buf, int buf_size,
+ int *section_buf)
+{
+ int n = 0;
+
+ if (section_buf) {
+ *section_buf = 0;
+ }
+ switch (op) {
+ case GRN_OP_EQUAL :
+ case GRN_OP_NOT_EQUAL :
+ case GRN_OP_TERM_EXTRACT :
+ if (buf_size > 0) {
+ index_buf[n] = obj;
+ }
+ if (n_index_data > 0) {
+ index_data[n].index = obj;
+ index_data[n].section = 0;
+ }
+ n++;
+ break;
+ case GRN_OP_PREFIX :
+ {
+ grn_accessor *a = (grn_accessor *)obj;
+ if (a->action == GRN_ACCESSOR_GET_KEY) {
+ if (a->obj->header.type == GRN_TABLE_PAT_KEY) {
+ if (buf_size > 0) {
+ index_buf[n] = obj;
+ }
+ if (n_index_data > 0) {
+ index_data[n].index = obj;
+ index_data[n].section = 0;
+ }
+ n++;
+ }
+ /* FIXME: GRN_TABLE_DAT_KEY should be supported */
+ }
+ }
+ break;
+ case GRN_OP_SUFFIX :
+ {
+ grn_accessor *a = (grn_accessor *)obj;
+ if (a->action == GRN_ACCESSOR_GET_KEY) {
+ if (a->obj->header.type == GRN_TABLE_PAT_KEY &&
+ a->obj->header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ if (buf_size > 0) {
+ index_buf[n] = obj;
+ }
+ if (n_index_data > 0) {
+ index_data[n].index = obj;
+ index_data[n].section = 0;
+ }
+ n++;
+ }
+ }
+ }
+ break;
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ case GRN_OP_REGEXP :
+ case GRN_OP_FUZZY :
+ n = grn_column_find_index_data_accessor_match(ctx, obj, op,
+ index_data, n_index_data,
+ index_buf, buf_size,
+ section_buf);
+ break;
+ default :
+ break;
+ }
+
+ return n;
+}
+
+int
+grn_column_index(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_obj **index_buf, int buf_size, int *section_buf)
+{
+ int n = 0;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ switch (op) {
+ case GRN_OP_EQUAL :
+ case GRN_OP_NOT_EQUAL :
+ n = grn_column_find_index_data_column_equal(ctx, obj, op,
+ NULL, 0,
+ index_buf, buf_size,
+ section_buf);
+ break;
+ case GRN_OP_PREFIX :
+ case GRN_OP_SUFFIX :
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_REGEXP :
+ case GRN_OP_FUZZY :
+ n = grn_column_find_index_data_column_match(ctx, obj, op,
+ NULL, 0,
+ index_buf, buf_size,
+ section_buf);
+ break;
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ n = grn_column_find_index_data_column_range(ctx, obj, op,
+ NULL, 0,
+ index_buf, buf_size,
+ section_buf);
+ break;
+ default :
+ break;
+ }
+ } else if (GRN_ACCESSORP(obj)) {
+ n = grn_column_find_index_data_accessor(ctx, obj, op,
+ NULL, 0,
+ index_buf, buf_size,
+ section_buf);
+ }
+ GRN_API_RETURN(n);
+}
+
+unsigned int
+grn_column_find_index_data(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_index_datum *index_data,
+ unsigned int n_index_data)
+{
+ unsigned int n = 0;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ switch (op) {
+ case GRN_OP_EQUAL :
+ case GRN_OP_NOT_EQUAL :
+ n = grn_column_find_index_data_column_equal(ctx, obj, op,
+ index_data, n_index_data,
+ NULL, 0, NULL);
+ break;
+ case GRN_OP_PREFIX :
+ case GRN_OP_SUFFIX :
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_REGEXP :
+ case GRN_OP_FUZZY :
+ n = grn_column_find_index_data_column_match(ctx, obj, op,
+ index_data, n_index_data,
+ NULL, 0, NULL);
+ break;
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ n = grn_column_find_index_data_column_range(ctx, obj, op,
+ index_data, n_index_data,
+ NULL, 0, NULL);
+ break;
+ default :
+ break;
+ }
+ } else if (GRN_ACCESSORP(obj)) {
+ n = grn_column_find_index_data_accessor(ctx, obj, op,
+ index_data, n_index_data,
+ NULL, 0, NULL);
+ }
+ GRN_API_RETURN(n);
+}
+
+static uint32_t
+grn_column_get_all_index_data_column(grn_ctx *ctx,
+ grn_obj *obj,
+ grn_index_datum *index_data,
+ uint32_t n_index_data)
+{
+ uint32_t n = 0;
+ grn_hook_entry hook_entry;
+ grn_hook *hooks;
+
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ hook_entry = GRN_HOOK_INSERT;
+ break;
+ default :
+ hook_entry = GRN_HOOK_SET;
+ break;
+ }
+
+ for (hooks = DB_OBJ(obj)->hooks[hook_entry]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section = 0;
+ if (!target) {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int length;
+ char hook_name[GRN_TABLE_MAX_KEY_SIZE];
+ int hook_name_length;
+
+ length = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE);
+ hook_name_length = grn_table_get_key(ctx,
+ ctx->impl->db,
+ data->target,
+ hook_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_OBJECT_CORRUPT,
+ "[column][indexes][all] "
+ "hook has a dangling reference: <%.*s> -> <%.*s>",
+ length, name,
+ hook_name_length, hook_name);
+ continue;
+ }
+ if (target->header.type != GRN_COLUMN_INDEX) {
+ continue;
+ }
+ if (MULTI_COLUMN_INDEXP(target)) {
+ section = data->section;
+ }
+ if (n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static uint32_t
+grn_column_get_all_index_data_accessor_index_column(grn_ctx *ctx,
+ grn_accessor *a,
+ grn_index_datum *index_data,
+ uint32_t n_index_data)
+{
+ grn_obj *index_column = a->obj;
+ int section = 0;
+
+ if (a->next) {
+ int specified_section;
+ grn_bool is_invalid_section;
+ if (a->next->next) {
+ return 0;
+ }
+ specified_section = find_section(ctx, index_column, a->next->obj);
+ is_invalid_section = (specified_section == 0);
+ if (is_invalid_section) {
+ return 0;
+ }
+ section = specified_section;
+ }
+ if (n_index_data > 0) {
+ index_data[0].index = index_column;
+ index_data[0].section = section;
+ }
+
+ return 1;
+}
+
+static uint32_t
+grn_column_get_all_index_data_accessor(grn_ctx *ctx,
+ grn_obj *obj,
+ grn_index_datum *index_data,
+ uint32_t n_index_data)
+{
+ uint32_t n = 0;
+ grn_accessor *a = (grn_accessor *)obj;
+
+ while (a) {
+ grn_hook *hooks;
+ grn_bool found = GRN_FALSE;
+ grn_hook_entry entry = (grn_hook_entry)-1;
+
+ if (a->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ GRN_OBJ_INDEX_COLUMNP(a->obj)) {
+ return grn_column_get_all_index_data_accessor_index_column(ctx,
+ a,
+ index_data,
+ n_index_data);
+ }
+
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ entry = GRN_HOOK_INSERT;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ entry = GRN_HOOK_SET;
+ break;
+ default :
+ break;
+ }
+
+ if (entry == (grn_hook_entry)-1) {
+ break;
+ }
+
+ for (hooks = DB_OBJ(a->obj)->hooks[entry]; hooks; hooks = hooks->next) {
+ grn_obj_default_set_value_hook_data *data = (void *)GRN_NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+
+ if (target->header.type != GRN_COLUMN_INDEX) {
+ continue;
+ }
+
+ found = GRN_TRUE;
+ if (!a->next) {
+ int section = 0;
+
+ if (MULTI_COLUMN_INDEXP(target)) {
+ section = data->section;
+ }
+ if (n < n_index_data) {
+ index_data[n].index = target;
+ index_data[n].section = section;
+ }
+ n++;
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ a = a->next;
+ }
+
+ return n;
+}
+
+uint32_t
+grn_column_get_all_index_data(grn_ctx *ctx,
+ grn_obj *obj,
+ grn_index_datum *index_data,
+ uint32_t n_index_data)
+{
+ uint32_t n = 0;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ n = grn_column_get_all_index_data_column(ctx, obj,
+ index_data, n_index_data);
+ } else if (GRN_ACCESSORP(obj)) {
+ n = grn_column_get_all_index_data_accessor(ctx, obj,
+ index_data, n_index_data);
+ }
+ GRN_API_RETURN(n);
+}
+
+grn_rc
+grn_obj_columns(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_size, grn_obj *res)
+{
+ grn_obj *col;
+ const char *p = (char *)str, *q, *r, *pe = p + str_size, *tokbuf[256];
+ while (p < pe) {
+ int i, n = grn_tokenize(p, pe - p, tokbuf, 256, &q);
+ for (i = 0; i < n; i++) {
+ r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ if (p < r) {
+ if (r[-1] == '*') {
+ grn_hash *cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (cols) {
+ grn_id *key;
+ grn_table_columns(ctx, table, p, r - p - 1, (grn_obj *)cols);
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ if ((col = grn_ctx_at(ctx, *key))) { GRN_PTR_PUT(ctx, res, col); }
+ });
+ grn_hash_close(ctx, cols);
+ }
+ {
+ grn_obj *type = grn_ctx_at(ctx, table->header.domain);
+ if (GRN_OBJ_TABLEP(type)) {
+ grn_obj *ai = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_ID,
+ GRN_COLUMN_NAME_ID_LEN);
+ if (ai) {
+ if (ai->header.type == GRN_ACCESSOR) {
+ grn_id *key;
+ grn_accessor *id_accessor;
+ for (id_accessor = ((grn_accessor *)ai)->next;
+ id_accessor;
+ id_accessor = id_accessor->next) {
+ grn_obj *target_table = id_accessor->obj;
+
+ cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (!cols) {
+ continue;
+ }
+ grn_table_columns(ctx, target_table,
+ p, r - p - 1, (grn_obj *)cols);
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ if ((col = grn_ctx_at(ctx, *key))) {
+ grn_accessor *a;
+ grn_accessor *ac;
+ ac = accessor_new(ctx);
+ GRN_PTR_PUT(ctx, res, (grn_obj *)ac);
+ for (a = (grn_accessor *)ai; a; a = a->next) {
+ if (a->action != GRN_ACCESSOR_GET_ID) {
+ ac->action = a->action;
+ ac->obj = a->obj;
+ ac->next = accessor_new(ctx);
+ if (!(ac = ac->next)) { break; }
+ } else {
+ ac->action = GRN_ACCESSOR_GET_COLUMN_VALUE;
+ ac->obj = col;
+ ac->next = NULL;
+ break;
+ }
+ }
+ }
+ });
+ grn_hash_close(ctx, cols);
+ }
+ }
+ grn_obj_unlink(ctx, ai);
+ }
+ }
+ }
+ } else if ((col = grn_obj_column(ctx, table, p, r - p))) {
+ GRN_PTR_PUT(ctx, res, col);
+ }
+ }
+ p = r;
+ }
+ p = q;
+ }
+ return ctx->rc;
+}
+
+static grn_table_sort_key *
+grn_table_sort_key_from_str_geo(grn_ctx *ctx, const char *str, unsigned int str_size,
+ grn_obj *table, unsigned int *nkeys)
+{
+ const char **tokbuf;
+ const char *p = str, *pe = str + str_size;
+ grn_table_sort_key *keys = NULL, *k = NULL;
+ while ((*p++ != '(')) { if (p == pe) { return NULL; } }
+ str = p;
+ while ((*p != ')')) { if (++p == pe) { return NULL; } }
+ str_size = p - str;
+ p = str;
+ if ((tokbuf = GRN_MALLOCN(const char *, str_size))) {
+ grn_id domain = GRN_ID_NIL;
+ int i, n = grn_tokenize(str, str_size, tokbuf, str_size, NULL);
+ if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
+ k = keys;
+ for (i = 0; i < n; i++) {
+ const char *r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ if (p < r) {
+ k->flags = GRN_TABLE_SORT_ASC;
+ k->offset = 0;
+ if (*p == '+') {
+ p++;
+ } else if (*p == '-') {
+ k->flags = GRN_TABLE_SORT_DESC;
+ p++;
+ }
+ if (k == keys) {
+ if (!(k->key = grn_obj_column(ctx, table, p, r - p))) {
+ WARN(GRN_INVALID_ARGUMENT, "invalid sort key: <%.*s>(<%.*s>)",
+ (int)(tokbuf[i] - p), p, str_size, str);
+ break;
+ }
+ domain = grn_obj_get_range(ctx, k->key);
+ } else {
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET(ctx, &buf, p + 1, r - p - 2); /* should be quoted */
+ k->key = grn_obj_open(ctx, GRN_BULK, 0, domain);
+ grn_obj_cast(ctx, &buf, k->key, GRN_FALSE);
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ k->flags |= GRN_TABLE_SORT_GEO;
+ k++;
+ }
+ p = r;
+ }
+ }
+ GRN_FREE(tokbuf);
+ }
+ if (!ctx->rc && k - keys > 0) {
+ *nkeys = k - keys;
+ } else {
+ grn_table_sort_key_close(ctx, keys, k - keys);
+ *nkeys = 0;
+ keys = NULL;
+ }
+ return keys;
+}
+
+grn_table_sort_key *
+grn_table_sort_key_from_str(grn_ctx *ctx, const char *str, unsigned int str_size,
+ grn_obj *table, unsigned int *nkeys)
+{
+ const char *p = str;
+ const char **tokbuf;
+ grn_table_sort_key *keys = NULL, *k = NULL;
+
+ if (str_size == 0) {
+ return NULL;
+ }
+
+ if ((keys = grn_table_sort_key_from_str_geo(ctx, str, str_size, table, nkeys))) {
+ return keys;
+ }
+ if ((tokbuf = GRN_MALLOCN(const char *, str_size))) {
+ int i, n = grn_tokenize(str, str_size, tokbuf, str_size, NULL);
+ if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
+ k = keys;
+ for (i = 0; i < n; i++) {
+ const char *r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ if (p < r) {
+ k->flags = GRN_TABLE_SORT_ASC;
+ k->offset = 0;
+ if (*p == '+') {
+ p++;
+ } else if (*p == '-') {
+ k->flags = GRN_TABLE_SORT_DESC;
+ p++;
+ }
+ if ((k->key = grn_obj_column(ctx, table, p, r - p))) {
+ k++;
+ } else {
+ if (r - p == GRN_COLUMN_NAME_SCORE_LEN &&
+ memcmp(p, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN) == 0) {
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int table_name_size;
+ table_name_size = grn_obj_name(ctx, table,
+ table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ if (table_name_size == 0) {
+ grn_strcpy(table_name, GRN_TABLE_MAX_KEY_SIZE, "(anonymous)");
+ table_name_size = strlen(table_name);
+ }
+ GRN_LOG(ctx, GRN_WARN,
+ "ignore invalid sort key: <%.*s>: "
+ "table:<%*.s> keys:<%.*s>",
+ (int)(r - p), p,
+ table_name_size, table_name,
+ str_size, str);
+ } else {
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int table_name_size;
+ table_name_size = grn_obj_name(ctx, table,
+ table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ if (table_name_size == 0) {
+ grn_strcpy(table_name, GRN_TABLE_MAX_KEY_SIZE, "(anonymous)");
+ table_name_size = strlen(table_name);
+ }
+ WARN(GRN_INVALID_ARGUMENT,
+ "invalid sort key: <%.*s>: "
+ "table:<%.*s> keys:<%.*s>",
+ (int)(r - p), p,
+ table_name_size, table_name,
+ str_size, str);
+ break;
+ }
+ }
+ }
+ p = r;
+ }
+ }
+ GRN_FREE(tokbuf);
+ }
+ if (!ctx->rc && k - keys > 0) {
+ *nkeys = k - keys;
+ } else {
+ grn_table_sort_key_close(ctx, keys, k - keys);
+ *nkeys = 0;
+ keys = NULL;
+ }
+ return keys;
+}
+
+grn_rc
+grn_table_sort_key_close(grn_ctx *ctx, grn_table_sort_key *keys, unsigned int nkeys)
+{
+ unsigned int i;
+ if (keys) {
+ for (i = 0; i < nkeys; i++) {
+ grn_obj *key = keys[i].key;
+ if (!grn_obj_is_column(ctx, key)) {
+ grn_obj_unlink(ctx, key);
+ }
+ }
+ GRN_FREE(keys);
+ }
+ return ctx->rc;
+}
+
+grn_bool
+grn_table_is_grouped(grn_ctx *ctx, grn_obj *table)
+{
+ if (GRN_OBJ_TABLEP(table) && GRN_TABLE_IS_GROUPED(table)) {
+ return GRN_TRUE;
+ }
+ return GRN_FALSE;
+}
+
+unsigned int
+grn_table_max_n_subrecs(grn_ctx *ctx, grn_obj *table)
+{
+ if (GRN_OBJ_TABLEP(table)) {
+ return DB_OBJ(table)->max_n_subrecs;
+ }
+ return 0;
+}
+
+grn_obj *
+grn_table_tokenize(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_len,
+ grn_obj *buf, grn_bool addp)
+{
+ grn_token_cursor *token_cursor = NULL;
+ grn_tokenize_mode mode = addp ? GRN_TOKENIZE_ADD : GRN_TOKENIZE_GET;
+ GRN_API_ENTER;
+ if (!(token_cursor = grn_token_cursor_open(ctx, table, str, str_len, mode, 0))) {
+ goto exit;
+ }
+ if (buf) {
+ GRN_BULK_REWIND(buf);
+ } else {
+ if (!(buf = grn_obj_open(ctx, GRN_UVECTOR, 0, DB_OBJ(table)->id))) {
+ goto exit;
+ }
+ }
+ while (token_cursor->status != GRN_TOKEN_CURSOR_DONE && token_cursor->status != GRN_TOKEN_CURSOR_DONE_SKIP) {
+ grn_id tid;
+ if ((tid = grn_token_cursor_next(ctx, token_cursor))) {
+ GRN_RECORD_PUT(ctx, buf, tid);
+ }
+ }
+exit :
+ if (token_cursor) {
+ grn_token_cursor_close(ctx, token_cursor);
+ }
+ GRN_API_RETURN(buf);
+}
+
+static void
+grn_db_recover_database_remove_orphan_inspect(grn_ctx *ctx, grn_obj *db)
+{
+ GRN_TABLE_EACH_BEGIN_FLAGS(ctx, db, cursor, id, GRN_CURSOR_BY_ID) {
+ void *key;
+ int key_size;
+
+ key_size = grn_table_cursor_get_key(ctx, cursor, &key);
+#define INSPECT "inspect"
+#define INSPECT_LEN (sizeof(INSPECT) - 1)
+ if (key_size == INSPECT_LEN && memcmp(key, INSPECT, INSPECT_LEN) == 0) {
+ if (!grn_ctx_at(ctx, id)) {
+ ERRCLR(ctx);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ }
+ break;
+ }
+#undef INSPECT
+#undef INSPECT_LEN
+ } GRN_TABLE_EACH_END(ctx, cursor);
+}
+
+static void
+grn_db_recover_database(grn_ctx *ctx, grn_obj *db)
+{
+ if (grn_obj_is_locked(ctx, db)) {
+ ERR(GRN_OBJECT_CORRUPT,
+ "[db][recover] database may be broken. Please re-create the database");
+ return;
+ }
+
+ grn_db_clear_dirty(ctx, db);
+ grn_db_recover_database_remove_orphan_inspect(ctx, db);
+}
+
+static void
+grn_db_recover_table(grn_ctx *ctx, grn_obj *table)
+{
+ if (!grn_obj_is_locked(ctx, table)) {
+ return;
+ }
+
+ {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int name_size;
+ name_size = grn_obj_name(ctx, table, name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_OBJECT_CORRUPT,
+ "[db][recover] table may be broken: <%.*s>: "
+ "please truncate the table (or clear lock of the table) "
+ "and load data again",
+ (int)name_size, name);
+ }
+}
+
+static void
+grn_db_recover_data_column(grn_ctx *ctx, grn_obj *data_column)
+{
+ if (!grn_obj_is_locked(ctx, data_column)) {
+ return;
+ }
+
+ {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int name_size;
+ name_size = grn_obj_name(ctx, data_column, name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_OBJECT_CORRUPT,
+ "[db][recover] column may be broken: <%.*s>: "
+ "please truncate the column (or clear lock of the column) "
+ "and load data again",
+ (int)name_size, name);
+ }
+}
+
+static void
+grn_db_recover_index_column(grn_ctx *ctx, grn_obj *index_column)
+{
+ if (!grn_obj_is_locked(ctx, index_column)) {
+ return;
+ }
+
+ grn_index_column_rebuild(ctx, index_column);
+}
+
+static grn_bool
+grn_db_recover_is_builtin(grn_ctx *ctx, grn_id id, grn_table_cursor *cursor)
+{
+ void *key;
+ const char *name;
+ int name_size;
+
+ if (id < GRN_N_RESERVED_TYPES) {
+ return GRN_TRUE;
+ }
+
+ name_size = grn_table_cursor_get_key(ctx, cursor, &key);
+ name = key;
+
+#define NAME_EQUAL(value) \
+ (name_size == strlen(value) && memcmp(name, value, strlen(value)) == 0)
+
+ if (NAME_EQUAL("inspect")) {
+ /* Just for compatibility. It's needed for users who used
+ Groonga master at between 2016-02-03 and 2016-02-26. */
+ return GRN_TRUE;
+ }
+
+#undef NAME_EQUAL
+
+ return GRN_FALSE;
+}
+
+grn_rc
+grn_db_recover(grn_ctx *ctx, grn_obj *db)
+{
+ grn_table_cursor *cursor;
+ grn_id id;
+ grn_bool is_close_opened_object_mode;
+
+ GRN_API_ENTER;
+
+ is_close_opened_object_mode = (grn_thread_get_limit() == 1);
+
+ grn_db_recover_database(ctx, db);
+ if (ctx->rc != GRN_SUCCESS) {
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ cursor = grn_table_cursor_open(ctx, db,
+ NULL, 0, NULL, 0,
+ 0, -1,
+ GRN_CURSOR_BY_ID);
+ if (!cursor) {
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *object;
+
+ if (is_close_opened_object_mode) {
+ grn_ctx_push_temporary_open_space(ctx);
+ }
+
+ if ((object = grn_ctx_at(ctx, id))) {
+ switch (object->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ grn_db_recover_table(ctx, object);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ grn_db_recover_data_column(ctx, object);
+ break;
+ case GRN_COLUMN_INDEX :
+ grn_db_recover_index_column(ctx, object);
+ break;
+ default:
+ break;
+ }
+ grn_obj_unlink(ctx, object);
+ } else {
+ if (grn_db_recover_is_builtin(ctx, id, cursor)) {
+ ERRCLR(ctx);
+ }
+ }
+
+ if (is_close_opened_object_mode) {
+ grn_ctx_pop_temporary_open_space(ctx);
+ }
+
+ if (ctx->rc != GRN_SUCCESS) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cursor);
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_db_unmap(grn_ctx *ctx, grn_obj *db)
+{
+ grn_id id;
+ db_value *vp;
+ grn_db *s = (grn_db *)db;
+
+ GRN_API_ENTER;
+
+ GRN_TINY_ARRAY_EACH(&s->values, 1, grn_db_curr_id(ctx, db), id, vp, {
+ grn_obj *obj = vp->ptr;
+
+ if (obj) {
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_INDEX :
+ grn_obj_close(ctx, obj);
+ break;
+ }
+ }
+ });
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+static grn_rc
+grn_ctx_get_all_objects(grn_ctx *ctx, grn_obj *objects_buffer,
+ grn_bool (*predicate)(grn_ctx *ctx, grn_obj *object))
+{
+ grn_obj *db;
+ grn_table_cursor *cursor;
+ grn_id id;
+
+ GRN_API_ENTER;
+
+ db = ctx->impl->db;
+ if (!db) {
+ ERR(GRN_INVALID_ARGUMENT, "DB isn't associated");
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ cursor = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!cursor) {
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *object;
+
+ if ((object = grn_ctx_at(ctx, id))) {
+ if (predicate(ctx, object)) {
+ GRN_PTR_PUT(ctx, objects_buffer, object);
+ } else {
+ grn_obj_unlink(ctx, object);
+ }
+ } else {
+ if (ctx->rc != GRN_SUCCESS) {
+ ERRCLR(ctx);
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, cursor);
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_ctx_get_all_tables(grn_ctx *ctx, grn_obj *tables_buffer)
+{
+ return grn_ctx_get_all_objects(ctx, tables_buffer, grn_obj_is_table);
+}
+
+grn_rc
+grn_ctx_get_all_types(grn_ctx *ctx, grn_obj *types_buffer)
+{
+ return grn_ctx_get_all_objects(ctx, types_buffer, grn_obj_is_type);
+}
+
+grn_rc
+grn_ctx_get_all_tokenizers(grn_ctx *ctx, grn_obj *tokenizers_buffer)
+{
+ return grn_ctx_get_all_objects(ctx, tokenizers_buffer,
+ grn_obj_is_tokenizer_proc);
+}
+
+grn_rc
+grn_ctx_get_all_normalizers(grn_ctx *ctx, grn_obj *normalizers_buffer)
+{
+ return grn_ctx_get_all_objects(ctx, normalizers_buffer,
+ grn_obj_is_normalizer_proc);
+}
+
+grn_rc
+grn_ctx_get_all_token_filters(grn_ctx *ctx, grn_obj *token_filters_buffer)
+{
+ return grn_ctx_get_all_objects(ctx, token_filters_buffer,
+ grn_obj_is_token_filter_proc);
+}
+
+grn_rc
+grn_ctx_push_temporary_open_space(grn_ctx *ctx)
+{
+ grn_obj *stack;
+ grn_obj *space;
+ grn_obj buffer;
+
+ GRN_API_ENTER;
+
+ stack = &(ctx->impl->temporary_open_spaces.stack);
+ GRN_VOID_INIT(&buffer);
+ grn_bulk_write(ctx, stack, (const char *)&buffer, sizeof(grn_obj));
+ space = ((grn_obj *)GRN_BULK_CURR(stack)) - 1;
+ GRN_PTR_INIT(space, GRN_OBJ_VECTOR | GRN_OBJ_OWN, GRN_ID_NIL);
+
+ ctx->impl->temporary_open_spaces.current = space;
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_ctx_pop_temporary_open_space(grn_ctx *ctx)
+{
+ grn_obj *stack;
+ grn_obj *space;
+
+ GRN_API_ENTER;
+
+ stack = &(ctx->impl->temporary_open_spaces.stack);
+ if (GRN_BULK_EMPTYP(stack)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[ctx][temporary-open-spaces][pop] too much pop");
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ space = ctx->impl->temporary_open_spaces.current;
+ GRN_OBJ_FIN(ctx, space);
+ grn_bulk_truncate(ctx, stack, GRN_BULK_VSIZE(stack) - sizeof(grn_obj));
+
+ if (GRN_BULK_EMPTYP(stack)) {
+ space = NULL;
+ } else {
+ space = ((grn_obj *)GRN_BULK_CURR(stack)) - 1;
+ }
+ ctx->impl->temporary_open_spaces.current = space;
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_ctx_merge_temporary_open_space(grn_ctx *ctx)
+{
+ grn_obj *stack;
+ grn_obj *space;
+ grn_obj *next_space;
+
+ GRN_API_ENTER;
+
+ stack = &(ctx->impl->temporary_open_spaces.stack);
+ if ((unsigned long) GRN_BULK_VSIZE(stack) < (unsigned long) sizeof(grn_obj) * 2) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[ctx][temporary-open-spaces][merge] "
+ "merge requires at least two spaces");
+ GRN_API_RETURN(ctx->rc);
+ }
+
+ space = ctx->impl->temporary_open_spaces.current;
+ next_space = ctx->impl->temporary_open_spaces.current - 1;
+ {
+ unsigned int i, n_elements;
+ n_elements = GRN_BULK_VSIZE(space) / sizeof(grn_obj *);
+ for (i = 0; i < n_elements; i++) {
+ grn_obj *element = GRN_PTR_VALUE_AT(space, i);
+ GRN_PTR_PUT(ctx, next_space, element);
+ }
+ }
+ GRN_BULK_REWIND(space);
+ GRN_OBJ_FIN(ctx, space);
+ grn_bulk_truncate(ctx, stack, GRN_BULK_VSIZE(stack) - sizeof(grn_obj));
+
+ if (GRN_BULK_EMPTYP(stack)) {
+ space = NULL;
+ } else {
+ space = ((grn_obj *)GRN_BULK_CURR(stack)) - 1;
+ }
+ ctx->impl->temporary_open_spaces.current = space;
+
+ GRN_API_RETURN(ctx->rc);
+}