Kouhei Sutou
null+****@clear*****
Wed Sep 16 00:06:43 JST 2015
Kouhei Sutou 2015-09-16 00:06:43 +0900 (Wed, 16 Sep 2015) New Revision: 770f1285fe391d3afbf8aff5c55fd4ac38568dc9 https://github.com/groonga/groonga/commit/770f1285fe391d3afbf8aff5c55fd4ac38568dc9 Merged b962058: Merge pull request #401 from groonga/support-column-for-temporary-table Message: Support columns for temporary table Use cases: * Named output columns in select like "AS" in SQL * Creating a column to temporary table and assigning return value of function to the column: select --filter all_records() \ --column[snippet].type ShortText \ --column[snippet].value 'snippet_html(body)' \ --output_columns _id,snippet * Drilldown by computed value * Select -> Create temporary column -> Compute value -> Drilldown: select --filter all_records() \ --column[year].type UInt8 \ --column[year].value 'time_to_year(updated_at)' \ --drilldown year Modified files: lib/ctx.c lib/db.c lib/grn_ctx_impl.h lib/grn_db.h test/unit/core/test-column.c Modified: lib/ctx.c (+20 -0) =================================================================== --- lib/ctx.c 2015-09-15 18:37:28 +0900 (4f34f4f) +++ lib/ctx.c 2015-09-16 00:06:43 +0900 (82c7986) @@ -539,10 +539,21 @@ grn_ctx_impl_init(grn_ctx *ctx) ctx->impl = NULL; return ctx->rc; } + if (!(ctx->impl->temporary_columns = grn_pat_create(ctx, NULL, + GRN_TABLE_MAX_KEY_SIZE, + sizeof(grn_obj *), + 0))) { + grn_array_close(ctx, ctx->impl->values); + CRITICAL_SECTION_FIN(ctx->impl->lock); + grn_io_anon_unmap(ctx, &mi, IMPL_SIZE); + ctx->impl = NULL; + return ctx->rc; + } if (!(ctx->impl->ios = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE, sizeof(grn_io *), GRN_OBJ_KEY_VAR_SIZE|GRN_HASH_TINY))) { grn_array_close(ctx, ctx->impl->values); + grn_pat_close(ctx, ctx->impl->temporary_columns); CRITICAL_SECTION_FIN(ctx->impl->lock); grn_io_anon_unmap(ctx, &mi, IMPL_SIZE); ctx->impl = NULL; @@ -735,6 +746,15 @@ grn_ctx_fin(grn_ctx *ctx) #endif grn_array_close(ctx, ctx->impl->values); } + if (ctx->impl->temporary_columns) { +#ifndef USE_MEMORY_DEBUG + grn_obj *value; + GRN_PAT_EACH(ctx, ctx->impl->temporary_columns, id, NULL, NULL, &value, { + grn_obj_close(ctx, *((grn_obj **)value)); + }); +#endif + grn_pat_close(ctx, ctx->impl->temporary_columns); + } if (ctx->impl->ios) { grn_hash_close(ctx, ctx->impl->ios); } Modified: lib/db.c (+203 -71) =================================================================== --- lib/db.c 2015-09-15 18:37:28 +0900 (73b6b63) +++ lib/db.c 2015-09-16 00:06:43 +0900 (c3c9ee0) @@ -4112,20 +4112,34 @@ static grn_obj *grn_obj_get_accessor(grn_ctx *ctx, grn_obj *obj, 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; - 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"); + + 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 { - /* todo : support temporary table */ + 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; } @@ -4150,14 +4164,41 @@ grn_table_columns(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int n grn_obj *res) { int n = 0; + grn_id id; + GRN_API_ENTER; - if (GRN_OBJ_TABLEP(table) && DB_OBJ(table)->id && - !(DB_OBJ(table)->id & GRN_OBJ_TMP_OBJECT)) { + + if (!GRN_OBJ_TABLEP(table)) { + GRN_API_RETURN(n); + } + + id = DB_OBJ(table)->id; + 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, DB_OBJ(table)->id, &bulk); + 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), @@ -4166,6 +4207,7 @@ grn_table_columns(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int n n = grn_table_size(ctx, res); } } + GRN_API_RETURN(n); } @@ -4211,8 +4253,10 @@ grn_column_create(grn_ctx *ctx, grn_obj *table, 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]; char buffer[PATH_MAX]; + GRN_API_ENTER; if (!table) { ERR(GRN_INVALID_ARGUMENT, "[column][create] table is missing"); @@ -4237,40 +4281,50 @@ grn_column_create(grn_ctx *ctx, grn_obj *table, table_name_len, table_name, name_size, name); goto exit; } - if (DB_OBJ(table)->id & GRN_OBJ_TMP_OBJECT) { - ERR(GRN_INVALID_ARGUMENT, - "[column][create] temporary table doesn't support column: <%.*s>", - name_size, name); + + 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 (is_persistent_table) { uint32_t s = 0; - const char *n = _grn_table_key(ctx, ctx->impl->db, DB_OBJ(table)->id, &s); + const char *n = _grn_table_key(ctx, ctx->impl->db, domain, &s); GRN_LOG(ctx, GRN_LOG_NOTICE, "DDL:column_create %.*s %.*s", s, n, name_size, name); } - if (grn_db_check_name(ctx, name, name_size)) { - GRN_DB_CHECK_NAME_ERR("[column][create]", name, name_size); + + if (!domain) { + ERR(GRN_FUNCTION_NOT_IMPLEMENTED, + "[column][create] [todo] table-less column isn't supported yet"); 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) { + + { + 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 - len, - len, fullname, name_size, name); + name_size, GRN_TABLE_MAX_KEY_SIZE - 1 - table_name_len, + table_name_len, fullname, name_size, name); goto exit; } - fullname[len] = GRN_DB_DELIMITER; - grn_memcpy(fullname + len + 1, name, name_size); - name_size += len + 1; - } else { - ERR(GRN_FUNCTION_NOT_IMPLEMENTED, - "[column][create] [todo] table-less column isn't supported yet"); - goto exit; + fullname[table_name_len] = GRN_DB_DELIMITER; + grn_memcpy(fullname + table_name_len + 1, name, name_size); + name_size += table_name_len + 1; } + range = DB_OBJ(type)->id; switch (type->header.type) { case GRN_TYPE : @@ -4294,9 +4348,32 @@ grn_column_create(grn_ctx *ctx, grn_obj *table, */ value_size = sizeof(grn_id); } - id = grn_obj_register(ctx, db, fullname, name_size); - if (ERRP(ctx, GRN_ERROR)) { goto exit; } - if (GRN_OBJ_PERSISTENT & flags) { + + if (is_persistent_table) { + id = grn_obj_register(ctx, db, fullname, name_size); + if (ERRP(ctx, GRN_ERROR)) { goto exit; } + } else { + int added; + id = grn_pat_add(ctx, ctx->impl->temporary_columns, + fullname, name_size, NULL, + &added); + if (!id) { + ERR(GRN_NO_MEMORY_AVAILABLE, + "[column][create][temporary] " + "failed to register temporary column name: <%.*s>", + name_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>", + name_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)) { gen_pathname(grn_obj_io(db)->path, buffer, id); @@ -9023,9 +9100,19 @@ grn_obj_delete_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, grn_bool removep) GRN_API_ENTER; if (id) { if (id & GRN_OBJ_TMP_OBJECT) { - if (ctx->impl && ctx->impl->values) { - rc = grn_array_delete_by_id(ctx, ctx->impl->values, - id & ~GRN_OBJ_TMP_OBJECT, NULL); + 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; @@ -9074,9 +9161,17 @@ 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 (ctx->impl && ctx->impl->values) { - rc = grn_array_set_value(ctx, ctx->impl->values, - id & ~GRN_OBJ_TMP_OBJECT, &obj, GRN_OBJ_SET); + 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; @@ -9198,11 +9293,27 @@ grn_ctx_at(grn_ctx *ctx, grn_id id) if (!ctx || !ctx->impl || !id) { return res; } 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) { - res = *tmp_obj; + 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 { @@ -9801,8 +9912,15 @@ grn_obj_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size) if (GRN_DB_OBJP(obj)) { if (DB_OBJ(obj)->id) { grn_db *s = (grn_db *)DB_OBJ(obj)->db; - if (!(DB_OBJ(obj)->id & GRN_OBJ_TMP_OBJECT)) { - len = grn_table_get_key(ctx, s->keys, DB_OBJ(obj)->id, namebuf, buf_size); + 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); } } } @@ -9817,19 +9935,26 @@ grn_column_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size) if (!obj) { return len; } GRN_API_ENTER; if (GRN_DB_OBJP(obj)) { - if (DB_OBJ(obj)->id && DB_OBJ(obj)->id < GRN_ID_MAX) { + 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, DB_OBJ(obj)->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); - } + 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) { @@ -9885,18 +10010,25 @@ grn_rc grn_column_name_(grn_ctx *ctx, grn_obj *obj, grn_obj *buf) { if (GRN_DB_OBJP(obj)) { - if (DB_OBJ(obj)->id && DB_OBJ(obj)->id < GRN_ID_MAX) { - uint32_t len; + 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; - const char *p = _grn_table_key(ctx, s->keys, DB_OBJ(obj)->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); + 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; Modified: lib/grn_ctx_impl.h (+1 -0) =================================================================== --- lib/grn_ctx_impl.h 2015-09-15 18:37:28 +0900 (b653f35) +++ lib/grn_ctx_impl.h 2015-09-16 00:06:43 +0900 (fb3e032) @@ -171,6 +171,7 @@ struct _grn_ctx_impl { grn_obj *db; grn_array *values; /* temporary objects */ + grn_pat *temporary_columns; grn_hash *ios; /* IOs */ grn_obj *outbuf; void (*output)(grn_ctx *, int, void *); Modified: lib/grn_db.h (+1 -0) =================================================================== --- lib/grn_db.h 2015-09-15 18:37:28 +0900 (6c84ad9) +++ lib/grn_db.h 2015-09-16 00:06:43 +0900 (dca1cfc) @@ -111,6 +111,7 @@ struct _grn_type { #define GRN_TABLE_SORT_GEO (0x02<<0) #define GRN_OBJ_TMP_OBJECT 0x80000000 +#define GRN_OBJ_TMP_COLUMN 0x40000000 #define GRN_DB_OBJP(obj) \ (obj &&\ Modified: test/unit/core/test-column.c (+15 -5) =================================================================== --- test/unit/core/test-column.c 2015-09-15 18:37:28 +0900 (23d5bac) +++ test/unit/core/test-column.c 2015-09-16 00:06:43 +0900 (bbdba09) @@ -1,6 +1,6 @@ /* -*- c-basic-offset: 2; coding: utf-8 -*- */ /* - Copyright (C) 2009-2012 Kouhei Sutou <kou �� clear-code.com> + Copyright (C) 2009-2015 Kouhei Sutou <kou �� clear-code.com> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -33,6 +33,7 @@ static grn_obj *database; static grn_obj *bookmarks; static grn_obj *count_column; static grn_id groonga_bookmark_id; +static grn_hash *columns; static void create_bookmarks_table(void) @@ -88,6 +89,8 @@ cut_setup(void) context = g_new0(grn_ctx, 1); grn_ctx_init(context, 0); database = grn_db_create(context, NULL, NULL); + columns = grn_hash_create(context, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY); create_bookmarks_table(); add_count_column_to_bookmarks_table(); @@ -97,6 +100,7 @@ cut_setup(void) void cut_teardown(void) { + grn_hash_close(context, columns); grn_obj_close(context, database); grn_ctx_fin(context); g_free(context); @@ -173,8 +177,14 @@ test_create_on_temporary_table(void) strlen(column_name), NULL, 0, get_object("Int32")); - grn_test_assert_error(GRN_INVALID_ARGUMENT, - "[column][create] " - "temporary table doesn't support column: <count>", - context); + grn_test_assert_context(context); + cut_assert_equal_int(1, + grn_table_columns(context, table, NULL, 0, + (grn_obj *)columns)); + { + grn_id found_column_id = GRN_ID_NIL; + grn_hash_get_key(context, columns, 1, &found_column_id, sizeof(grn_id)); + cut_assert_equal_uint(grn_obj_id(context, column), + found_column_id); + } } -------------- next part -------------- HTML����������������������������...Download