예제 #1
0
/*
 * 新しくデータベースを作成する。
 * _options_ にはハッシュでオプションを指定する。
 *
 * @example
 *   # 一時データベースを作成:
 *   Groonga::Database.create
 *
 *   # 永続データベースを作成:
 *   Groonga::Database.create(:path => "/tmp/db.groonga")
 *
 * @overload create(options=nil)
 *   @return [Groonga::Database] 作成されたデータベースを返す。
 *   @param [::Hash] options The name and value
 *     pairs. Omitted names are initialized as the default value.
 *   @option options :path
 *     データベースを保存するパス。省略すると一時データベース
 *     となる。
 *   @option options :context (Groonga::Context.default)
 *     データベースを結びつけるコンテキスト。省略すると
 *     {Groonga::Context.default} を利用する。
 */
static VALUE
rb_grn_database_s_create (int argc, VALUE *argv, VALUE klass)
{
    grn_ctx *context;
    grn_obj *old_database, *database;
    grn_db_create_optarg create_args;
    const char *path = NULL;
    VALUE rb_database;
    VALUE rb_path, options, rb_context, builtin_type_names;
    grn_bool owner;

    rb_scan_args(argc, argv, "01", &options);

    rb_grn_scan_options(options,
                        "path", &rb_path,
                        "context", &rb_context,
                        "builtin_type_names", &builtin_type_names,
                        NULL);

    if (!NIL_P(rb_path))
        path = StringValuePtr(rb_path);
    context = rb_grn_context_ensure(&rb_context);

    create_args.builtin_type_names = NULL;
    create_args.n_builtin_type_names = 0;

    old_database = grn_ctx_db(context);
    if (old_database)
        grn_obj_unlink(context, old_database);
    reset_floating_objects(rb_context);
    database = grn_db_create(context, path, &create_args);
    rb_grn_context_check(context, rb_ary_new_from_values(argc, argv));
    owner = (context->flags & GRN_CTX_PER_DB) ? GRN_FALSE : GRN_TRUE;
    rb_database = GRNOBJECT2RVAL(klass, context, database, owner);
    rb_iv_set(rb_database, "@context", rb_context);
    if (!NIL_P(rb_context))
        rb_iv_set(rb_context, "database", rb_database);
    rb_grn_context_check(context, rb_ary_new_from_values(argc, argv));

    if (rb_block_given_p())
        return rb_ensure(rb_yield, rb_database,
                         rb_grn_database_close, rb_database);
    else
        return rb_database;
}
예제 #2
0
static inline void
args_copy(struct args_info *args)
{
    if (args->rest != Qfalse) {
	int argc = args->argc;
	args->argc = 0;
	args->rest = rb_ary_dup(args->rest); /* make dup */

	/*
	 * argv: [m0, m1, m2, m3]
	 * rest: [a0, a1, a2, a3, a4, a5]
	 *                ^
	 *                rest_index
	 *
	 * #=> first loop
	 *
	 * argv: [m0, m1]
	 * rest: [m2, m3, a2, a3, a4, a5]
	 *        ^
	 *        rest_index
	 *
	 * #=> 2nd loop
	 *
	 * argv: [] (argc == 0)
	 * rest: [m0, m1, m2, m3, a2, a3, a4, a5]
	 *        ^
	 *        rest_index
	 */
	while (args->rest_index > 0 && argc > 0) {
	    RARRAY_ASET(args->rest, --args->rest_index, args->argv[--argc]);
	}
	while (argc > 0) {
	    rb_ary_unshift(args->rest, args->argv[--argc]);
	}
    }
    else if (args->argc > 0) {
	args->rest = rb_ary_new_from_values(args->argc, args->argv);
	args->rest_index = 0;
	args->argc = 0;
    }
}
예제 #3
0
/*
 * _expression_ から {Groonga::Snippet} を生成する。 _tags_ には
 * キーワードの前後に挿入するタグの配列を以下のような形式で指定
 * する。
 *
 * <pre>
 * !!!ruby
 *   [
 *    ["キーワード前に挿入する文字列1", "キーワード後に挿入する文字列1"],
 *    ["キーワード前に挿入する文字列2", "キーワード後に挿入する文字列2"],
 *    # ...,
 *   ]
 * </pre>
 *
 * もし、1つのスニペットの中に _tags_ で指定したタグより多くの
 * キーワードが含まれている場合は、以下のように、また、先頭
 * のタグから順番に使われる。
 *
 * <pre>
 * !!!ruby
 *   expression.parse("Ruby groonga 検索")
 *   tags = [["<tag1>", "</tag1>"], ["<tag2>", "</tag2>"]]
 *   snippet = expression.snippet(tags)
 *   p snippet.execute("Rubyでgroonga使って全文検索、高速検索。")
 *      # => ["<tag1>Ruby</tag1>で<tag2>groonga</tag2>"
 *      # =>  "使って全文<tag1>検索</tag1>、高速<tag2>検索</tag2>。"]
 * </pre>
 *
 * @overload snippet(tags, options)
 *   @param tags [Array<string>] キーワードの前後に挿入するタグの配列
 *     (詳細は上記を参照)
 *   @param options [::Hash] The name and value
 *     pairs. Omitted names are initialized as the default value.
 *   @option options :normalize (false)
 *     キーワード文字列・スニペット元の文字列を正規化するかど
 *     うか。省略した場合は +false+ で正規化しない。
 *   @option options :skip_leading_spaces (false)
 *     先頭の空白を無視するかどうか。省略した場合は +false+ で無
 *     視しない。
 *   @option options :width (100)
 *     スニペット文字列の長さ。省略した場合は100文字。
 *   @option options :max_results (3)
 *     生成するスニペットの最大数。省略した場合は3。
 *   @option options :html_escape (false)
 *     スニペット内の +<+, +>+, +&+, +"+ をHTMLエスケープするか
 *     どうか。省略した場合は +false+ で、HTMLエスケープしない。
 * @return [Groonga::Snippet]
 */
static VALUE
rb_grn_expression_snippet (int argc, VALUE *argv, VALUE self)
{
    grn_ctx *context = NULL;
    grn_obj *expression;
    grn_obj *snippet;
    VALUE options;
    VALUE rb_normalize, rb_skip_leading_spaces;
    VALUE rb_width, rb_max_results, rb_tags;
    VALUE rb_html_escape;
    VALUE *rb_tag_values;
    VALUE related_object;
    unsigned int i;
    int flags = GRN_SNIP_COPY_TAG;
    unsigned int width = 100;
    unsigned int max_results = 3;
    unsigned int n_tags = 0;
    char **open_tags = NULL;
    unsigned int *open_tag_lengths = NULL;
    char **close_tags = NULL;
    unsigned int *close_tag_lengths = NULL;
    grn_snip_mapping *mapping = NULL;

    rb_grn_expression_deconstruct(SELF(self), &expression, &context,
                                  NULL, NULL,
                                  NULL, NULL, NULL);

    rb_scan_args(argc, argv, "11", &rb_tags, &options);

    rb_grn_scan_options(options,
                        "normalize", &rb_normalize,
                        "skip_leading_spaces", &rb_skip_leading_spaces,
                        "width", &rb_width,
                        "max_results", &rb_max_results,
                        "html_escape", &rb_html_escape,
                        NULL);

    if (TYPE(rb_tags) != T_ARRAY) {
        rb_raise(rb_eArgError,
                 "tags should be "
                 "[\"open_tag\", \"close_tag\"] or "
                 "[[\"open_tag1\", \"close_tag1\"], ...]: %s",
                 rb_grn_inspect(rb_tags));
    }

    if (TYPE(RARRAY_PTR(rb_tags)[0]) == T_STRING) {
        rb_tags = rb_ary_new_from_args(1, rb_tags);
    }

    rb_tag_values = RARRAY_PTR(rb_tags);
    n_tags = RARRAY_LEN(rb_tags);
    open_tags = ALLOCA_N(char *, n_tags);
    open_tag_lengths = ALLOCA_N(unsigned int, n_tags);
    close_tags = ALLOCA_N(char *, n_tags);
    close_tag_lengths = ALLOCA_N(unsigned int, n_tags);
    for (i = 0; i < n_tags; i++) {
        VALUE *tag_pair;

        if (TYPE(rb_tag_values[i]) != T_ARRAY ||
            RARRAY_LEN(rb_tag_values[i]) != 2) {
            rb_raise(rb_eArgError,
                     "tags should be "
                     "[\"open_tag\", \"close_tag\"] or"
                     "[[\"open_tag1\", \"close_tag1\"], ...]: %s",
                     rb_grn_inspect(rb_tags));
        }
        tag_pair = RARRAY_PTR(rb_tag_values[i]);
        open_tags[i] = StringValuePtr(tag_pair[0]);
        open_tag_lengths[i] = RSTRING_LEN(tag_pair[0]);
        close_tags[i] = StringValuePtr(tag_pair[1]);
        close_tag_lengths[i] = RSTRING_LEN(tag_pair[1]);
    }

    if (RVAL2CBOOL(rb_normalize))
        flags |= GRN_SNIP_NORMALIZE;
    if (RVAL2CBOOL(rb_skip_leading_spaces))
        flags |= GRN_SNIP_SKIP_LEADING_SPACES;

    if (!NIL_P(rb_width))
        width = NUM2UINT(rb_width);

    if (!NIL_P(rb_max_results))
        max_results = NUM2UINT(rb_max_results);

    if (RVAL2CBOOL(rb_html_escape))
        mapping = (grn_snip_mapping *)-1;

    snippet = grn_expr_snip(context, expression, flags, width, max_results,
                            n_tags,
                            (const char **)open_tags, open_tag_lengths,
                            (const char **)close_tags, close_tag_lengths,
                            mapping);
    related_object =
        rb_ary_new_from_args(2, self, rb_ary_new_from_values(argc, argv));
    rb_grn_context_check(context, related_object);

    return GRNOBJECT2RVAL(Qnil, context, snippet, GRN_TRUE);
}
예제 #4
0
/*
 * 文字列 _query_ をパースする。
 * @overload parse(query, options={})
 *   @param [String] query パースする文字列
 *   @param [::Hash] options The name and value
 *     pairs. Omitted names are initialized as the default value.
 *   @option options :default_column
 *     "column_name:hoge"ではなく"hoge"のようにcolumn_nameが指
 *     定されない条件の検索対象となるカラムを指定する。
 *   @option options :default_operator (Groonga::Operator::AND)
 *     "+"や"OR"で繋がれず、ただ列挙された複数の条件があった時、
 *     _expression_ 全体として各レコードをヒットとみなすかの論理
 *     条件を指定する。省略した場合はGroonga::Operator::AND。
 *
 *     - Groonga::Operator::OR :=
 *       レコードはいずれかの条件にマッチすればいい。 =:
 *     - Groonga::Operator::AND :=
 *       レコードは全ての条件にマッチしなければならない。 =:
 *     - Groonga::Operator::AND_NOT :=
 *       最初の条件にレコードはマッチし、残りの条件にレコードは
 *       マッチしてはならない。 =:
 *
 *   @option options :default_mode (Groonga::Operator::MATCH)
 *     検索時のモードを指定する。省略した場合はGroonga::Operator::MATCH。
 *     (FIXME: モードによってどういう動作になるかを書く。)
 *   @option options :syntax (:query)
 *     _query_ の構文を指定する。指定可能な値は以下の通り。省略
 *     した場合は +:query+ 。
 *
 *     - +:query+ :=
 *       「文字列1 OR 文字列2」で「"文字列1"あるいは"文字列2"
 *       にマッチという検索エンジンで利用できるような構文を使
 *       う。
 *       参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html =:
 *     - +nil+ :=
 *       +:query+と同様 =:
 *     - +:script+ :=
 *       「[カラム名] == [値]」というようにECMAScript風の構文を使う。
 *       参考: "Groongaのscript構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/script_syntax.html =:
 *   @option options :allow_pragma
 *     _query_ の構文に query を用いているとき( +:syntax+
 *     オプション参照)、「*E-1」というようにクエリの先頭で
 *     pragmaを利用できるようにする。script構文を用いている
 *     ときはこのオプションを利用できない。
 *
 *     デフォルトではプラグマを利用できる。
 *
 *     参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html
 *   @option options :allow_column
 *     _query_ の構文にqueryを用いているとき( +:syntax+ オプショ
 *     ン参照)、「カラム名:値」というようにカラム名を指定した
 *     条件式を利用できるようにする。script構文を用いていると
 *     きはこのオプションを利用できない。
 *
 *     デフォルトではカラム名を指定した条件式を利用できる。
 *
 *     参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html
 *   @option options :allow_update
 *     _query_ の構文にscriptを用いているとき( +:syntax+ オプショ
 *     ン参照)、「カラム名 = 値」というように更新操作を利用で
 *     きるようにする。query構文を用いているときはこのオプショ
 *     ンを利用できない。
 *
 *     デフォルトでは更新操作を利用できる。
 *
 *     参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html
 */
static VALUE
rb_grn_expression_parse (int argc, VALUE *argv, VALUE self)
{
    grn_ctx *context = NULL;
    grn_obj *expression, *default_column;
    grn_bool default_column_is_created = GRN_FALSE;
    grn_operator default_operator = GRN_OP_AND;
    grn_operator default_mode = GRN_OP_MATCH;
    grn_rc rc;
    char *query = NULL;
    unsigned query_size = 0;
    grn_expr_flags flags = 0;
    VALUE options, rb_query, rb_default_column, rb_default_operator;
    VALUE rb_default_mode, rb_syntax;
    VALUE rb_allow_pragma, rb_allow_column, rb_allow_update, rb_allow_leading_not;
    VALUE exception = Qnil;

    rb_scan_args(argc, argv, "11", &rb_query, &options);
    rb_grn_scan_options(options,
                        "default_column", &rb_default_column,
                        "default_operator", &rb_default_operator,
                        "default_mode", &rb_default_mode,
                        "syntax", &rb_syntax,
                        "allow_pragma", &rb_allow_pragma,
                        "allow_column", &rb_allow_column,
                        "allow_update", &rb_allow_update,
                        "allow_leading_not", &rb_allow_leading_not,
                        NULL);

    query = StringValuePtr(rb_query);
    query_size = RSTRING_LEN(rb_query);

    rb_grn_expression_deconstruct(SELF(self), &expression, &context,
                                  NULL, NULL,
                                  NULL, NULL, NULL);

    if (NIL_P(rb_default_column)) {
        default_column = NULL;
    } else if (RVAL2CBOOL(rb_obj_is_kind_of(rb_default_column, rb_cGrnObject))) {
        default_column = RVAL2GRNOBJECT(rb_default_column, &context);
    } else {
        default_column = RVAL2GRNBULK(rb_default_column, context, NULL);
        default_column_is_created = GRN_TRUE;
    }

    if (!NIL_P(rb_default_mode))
        default_mode = RVAL2GRNOPERATOR(rb_default_mode);
    if (!NIL_P(rb_default_operator))
        default_operator = RVAL2GRNSETOPERATOR(rb_default_operator);

    if (NIL_P(rb_syntax) ||
        rb_grn_equal_option(rb_syntax, "query")) {
        flags = GRN_EXPR_SYNTAX_QUERY;
    } else if (rb_grn_equal_option(rb_syntax, "script")) {
        flags = GRN_EXPR_SYNTAX_SCRIPT;
    } else {
        rb_raise(rb_eArgError,
                 "syntax should be one of "
                 "[nil, :query, :script]: %s",
                 rb_grn_inspect(rb_syntax));
    }

    if (NIL_P(rb_allow_pragma)) {
        if (!(flags & GRN_EXPR_SYNTAX_SCRIPT))
            flags |= GRN_EXPR_ALLOW_PRAGMA;
    } else {
        if ((flags & GRN_EXPR_SYNTAX_SCRIPT))
            rb_raise(rb_eArgError,
                     ":allow_pragma isn't allowed in script syntax");
        if (RVAL2CBOOL(rb_allow_pragma))
            flags |= GRN_EXPR_ALLOW_PRAGMA;
    }

    if (NIL_P(rb_allow_column)) {
        if (!(flags & GRN_EXPR_SYNTAX_SCRIPT))
            flags |= GRN_EXPR_ALLOW_COLUMN;
    } else {
        if ((flags & GRN_EXPR_SYNTAX_SCRIPT))
            rb_raise(rb_eArgError,
                     ":allow_column isn't allowed in script syntax");
        if (RVAL2CBOOL(rb_allow_column))
            flags |= GRN_EXPR_ALLOW_COLUMN;
    }

    if (NIL_P(rb_allow_update)) {
        flags |= GRN_EXPR_ALLOW_UPDATE;
    } else {
        if (RVAL2CBOOL(rb_allow_update))
            flags |= GRN_EXPR_ALLOW_UPDATE;
    }

    if (!NIL_P(rb_allow_leading_not)) {
        if (RVAL2CBOOL(rb_allow_leading_not))
            flags |= GRN_EXPR_ALLOW_LEADING_NOT;
    }

    rc = grn_expr_parse(context, expression, query, query_size,
                        default_column, default_mode, default_operator,
                        flags);
    if (rc != GRN_SUCCESS) {
        VALUE related_object;

        related_object =
            rb_ary_new_from_args(2,
                                 self,
                                 rb_ary_new_from_values(argc, argv));
        exception = rb_grn_context_to_exception(context, related_object);
    }
    if (default_column_is_created)
        grn_obj_unlink(context, default_column);

    if (!NIL_P(exception))
        rb_exc_raise(exception);

    return Qnil;
}
예제 #5
0
/*
 * 各レコードをキーで管理するテーブルを生成する。ブロックを指
 * 定すると、そのブロックに生成したテーブルが渡され、ブロック
 * を抜けると自動的にテーブルが破棄される。
 *
 * @example
 *   #無名一時テーブルを生成する。
 *   Groonga::Hash.create
 *
 *   #無名永続テーブルを生成する。
 *   Groonga::Hash.create(:path => "/tmp/hash.grn")
 *
 *   #名前付き永続テーブルを生成する。ただし、ファイル名は気にしない。
 *   Groonga::Hash.create(:name => "Bookmarks",
 *                        :persistent => true)
 *
 *   #それぞれのレコードに512バイトの値を格納できる無名一時テーブルを生成する。
 *   Groonga::Hash.create(:value => 512)
 *
 *   #キーとして文字列を使用する無名一時テーブルを生成する。
 *   Groonga::Hash.create(:key_type => Groonga::Type::SHORT_TEXT)
 *
 *   #キーとして文字列を使用する無名一時テーブルを生成する。
 *   (キーの種類を表すオブジェクトは文字列で指定。)
 *   Groonga::Hash.create(:key_type => "ShortText")
 *
 *   #キーとしてBookmarksテーブルのレコードを使用す
 *   る無名一時テーブルを生成する。
 *   bookmarks = Groonga::Hash.create(:name => "Bookmarks")
 *   Groonga::Hash.create(:key_type => bookmarks)
 *
 *   #キーとしてBookmarksテーブルのレコードを使用す
 *   #る無名一時テーブルを生成する。(テーブルは文字列で指定。)
 *   Groonga::Hash.create(:name => "Bookmarks")
 *   Groonga::Hash.create(:key_type => "Bookmarks")
 *
 *   #全文検索用のトークンをバイグラムで切り出す無名一時テーブ
 *   #ルを生成する。
 *   bookmarks = Groonga::Hash.create(:name => "Bookmarks")
 *   bookmarks.define_column("comment", "Text")
 *   terms = Groonga::Hash.create(:name => "Terms",
 *                                :default_tokenizer => "TokenBigram")
 *   terms.define_index_column("content", bookmarks,
 *                             :source => "Bookmarks.comment")
 *
 * @overload create(options={})
 *   @return [Groonga::Hash]
 *   @!macro hash.create.options
 *     @param [::Hash] options The name and value
 *       pairs. Omitted names are initialized as the default value
 *     @option options [Groonga::Context] :context (Groonga::Context.default)
 *       テーブルが利用する {Groonga::Context} 。省略すると
 *       {Groonga::Context.default} を用いる。
 *     @option options [Groonga::Context#[]] :name
 *       テーブルの名前。名前をつけると、 {Groonga::Context#[]} に名
 *       前を指定してテーブルを取得することができる。省略すると
 *       無名テーブルになり、テーブルIDでのみ取得できる。
 *     @option options [Groonga::Context#[]] :path
 *       テーブルを保存するパス。パスを指定すると永続テーブルとな
 *       り、プロセス終了後もレコードは保持される。次回起動時に
 *       {Groonga::Context#[]} で保存されたレコードを利用することが
 *       できる。省略すると一時テーブルになり、プロセスが終了する
 *       とレコードは破棄される。
 *     @option options :persistent
 *       +true+ を指定すると永続テーブルとなる。 +path+ を省略した
 *       場合は自動的にパスが付加される。 +:context+ で指定した
 *       {Groonga::Context} に結びついているデータベースが一時デー
 *       タベースの場合は例外が発生する。
 *
 *     @option options :key_normalize (false) Keys are normalized
 *       if this value is @true@.
 *
 *       @deprecated Use @:normalizer => "NormalizerAuto"@ instead.
 *
 *     @option options [Boolean] :key_large (false)
 *       It specifies whether total key size is large or not. The
 *       default total key size is 4GiB. Large total key size is 1TiB.
 *
 *       @since 6.0.1
 *
 *     @option options :key_type
 *       キーの種類を示すオブジェクトを指定する。キーの種類には型
 *       名("Int32"や"ShortText"など)または {Groonga::Type} または
 *       テーブル( {Groonga::Array} 、 {Groonga::Hash} 、
 *       {Groonga::PatriciaTrie} のどれか)を指定する。
 *
 *       {Groonga::Type} を指定した場合は、その型が示す範囲の値をキー
 *       として使用する。ただし、キーの最大サイズは4096バイトで
 *       あるため、 {Groonga::Type::TEXT} や {Groonga::Type::LONG_TEXT}
 *       は使用できない。
 *
 *       テーブルを指定した場合はレコードIDをキーとして使用する。
 *       指定したテーブルの {Groonga::Record} をキーとして使用するこ
 *       ともでき、その場合は自動的に {Groonga::Record} からレコード
 *       IDを取得する。
 *
 *       省略した場合はShortText型をキーとして使用する。この場合、
 *       4096バイトまで使用可能である。
 *     @option options :value_type
 *       値の型を指定する。省略すると値のための領域を確保しない。
 *       値を保存したい場合は必ず指定すること。
 *
 *       参考: {Groonga::Type.new}
 *     @option options [Groonga::IndexColumn] :default_tokenizer
 *       {Groonga::IndexColumn} で使用するトークナイザを指定する。
 *       デフォルトでは何も設定されていないので、テーブルに
 *       {Groonga::IndexColumn} を定義する場合は
 *       @"TokenBigram"@ などを指定する必要がある。
 *
 *     @option options [::Array<String, Groonga::Procedure>, nil]
 *       :token_filters (nil) The token filters to be used in the
 *       table.
 *
 *     @option options [Groonga::Record#n_sub_records] :sub_records
 *       +true+ を指定すると {#group} でグループ化したときに、
 *       {Groonga::Record#n_sub_records} でグループに含まれるレコー
 *       ドの件数を取得できる。
 *
 *     @option options [String, Groonga::Procedure, nil] :normalizer
 *       The normalizer that is used by {Groonga::IndexColumn}. You
 *       can specify this by normalizer name as String such as
 *       @"NormalizerAuto"@ or normalizer object.
 *
 *   @!macro hash.create.options
 * @overload create(options={})
 *   @yield [table]
 *   @!macro hash.create.options
 */
static VALUE
rb_grn_hash_s_create (int argc, VALUE *argv, VALUE klass)
{
    grn_ctx *context;
    grn_obj *key_type = NULL, *value_type = NULL, *table;
    const char *name = NULL, *path = NULL;
    unsigned name_size = 0;
    grn_table_flags flags = GRN_OBJ_TABLE_HASH_KEY;
    VALUE rb_table;
    VALUE options, rb_context, rb_name, rb_path, rb_persistent;
    VALUE rb_key_normalize;
    VALUE rb_key_large;
    VALUE rb_key_type, rb_value_type, rb_default_tokenizer;
    VALUE rb_token_filters;
    VALUE rb_sub_records;
    VALUE rb_normalizer;

    rb_scan_args(argc, argv, "01", &options);

    rb_grn_scan_options(options,
                        "context", &rb_context,
                        "name", &rb_name,
                        "path", &rb_path,
                        "persistent", &rb_persistent,
                        "key_normalize", &rb_key_normalize,
                        "key_large", &rb_key_large,
                        "key_type", &rb_key_type,
                        "value_type", &rb_value_type,
                        "default_tokenizer", &rb_default_tokenizer,
                        "token_filters", &rb_token_filters,
                        "sub_records", &rb_sub_records,
                        "normalizer", &rb_normalizer,
                        NULL);

    context = rb_grn_context_ensure(&rb_context);

    if (!NIL_P(rb_name)) {
        name = StringValuePtr(rb_name);
        name_size = RSTRING_LEN(rb_name);
        flags |= GRN_OBJ_PERSISTENT;
    }

    if (!NIL_P(rb_path)) {
        path = StringValueCStr(rb_path);
        flags |= GRN_OBJ_PERSISTENT;
    }

    if (RVAL2CBOOL(rb_persistent))
        flags |= GRN_OBJ_PERSISTENT;

    if (RVAL2CBOOL(rb_key_normalize))
        flags |= GRN_OBJ_KEY_NORMALIZE;

    if (RVAL2CBOOL(rb_key_large))
        flags |= GRN_OBJ_KEY_LARGE;

    if (NIL_P(rb_key_type)) {
        key_type = grn_ctx_at(context, GRN_DB_SHORT_TEXT);
    } else {
        key_type = RVAL2GRNOBJECT(rb_key_type, &context);
    }

    if (!NIL_P(rb_value_type))
        value_type = RVAL2GRNOBJECT(rb_value_type, &context);

    if (RVAL2CBOOL(rb_sub_records))
        flags |= GRN_OBJ_WITH_SUBREC;

    table = grn_table_create(context, name, name_size, path,
                             flags, key_type, value_type);
    if (!table)
        rb_grn_context_check(context, rb_ary_new_from_values(argc, argv));
    rb_table = GRNOBJECT2RVAL(klass, context, table, GRN_TRUE);

    if (!NIL_P(rb_default_tokenizer))
        rb_funcall(rb_table, rb_intern("default_tokenizer="), 1,
                   rb_default_tokenizer);
    if (!NIL_P(rb_token_filters))
        rb_funcall(rb_table, rb_intern("token_filters="), 1,
                   rb_token_filters);
    if (!NIL_P(rb_normalizer))
        rb_funcall(rb_table, rb_intern("normalizer="), 1,
                   rb_normalizer);

    if (rb_block_given_p())
        return rb_ensure(rb_yield, rb_table, rb_grn_object_close, rb_table);
    else
        return rb_table;
}