diff options
Diffstat (limited to 'storage/mroonga/vendor/groonga/lib/db.c')
-rw-r--r-- | storage/mroonga/vendor/groonga/lib/db.c | 14054 |
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, + ¤t_spec_raw_len); + if (current_spec_raw) { + grn_rc rc; + grn_obj current_spec; + + GRN_OBJ_INIT(¤t_spec, GRN_VECTOR, 0, GRN_DB_TEXT); + rc = grn_vector_decode(ctx, + ¤t_spec, + current_spec_raw, + current_spec_raw_len); + if (rc == GRN_SUCCESS) { + need_update = !grn_obj_encoded_spec_equal(ctx, &v, ¤t_spec); + } + GRN_OBJ_FIN(ctx, ¤t_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); +} |