mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			14062 lines
		
	
	
	
		
			403 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			14062 lines
		
	
	
	
		
			403 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- 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 <my_attribute.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(offsetof(grn_rset_recinfo, 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;
 | |
| }
 | |
| 
 | |
| PRAGMA_DISABLE_CHECK_STACK_FRAME
 | |
| 
 | |
| 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;
 | |
| }
 | |
| PRAGMA_REENABLE_CHECK_STACK_FRAME
 | |
| 
 | |
| 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)
 | |
|       goto fail;
 | |
|     query = &casted_query;
 | |
|   }
 | |
| 
 | |
|   key = GRN_BULK_HEAD(query);
 | |
|   key_len = GRN_BULK_VSIZE(query);
 | |
| 
 | |
|   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);
 | |
|  fail:
 | |
|   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 */
 | |
| 
 | |
| 
 | |
| PRAGMA_DISABLE_CHECK_STACK_FRAME
 | |
| 
 | |
| 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);
 | |
| }
 | |
| PRAGMA_REENABLE_CHECK_STACK_FRAME
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| PRAGMA_DISABLE_CHECK_STACK_FRAME
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| PRAGMA_REENABLE_CHECK_STACK_FRAME
 | |
| 
 | |
| 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 __attribute__((unused));
 | |
|   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 __attribute__((unused));
 | |
|   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_rc rc;
 | |
|   uint32_t spec_size;
 | |
| 
 | |
|   db = ctx->impl->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);
 | |
| }
 | 
