rroonga 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS.ja.rdoc +18 -0
- data/NEWS.rdoc +18 -0
- data/bin/grndump +71 -0
- data/ext/groonga/rb-grn-accessor.c +64 -1
- data/ext/groonga/rb-grn-context.c +40 -1
- data/ext/groonga/rb-grn-database.c +47 -5
- data/ext/groonga/rb-grn-index-column.c +77 -7
- data/ext/groonga/rb-grn-object.c +8 -4
- data/ext/groonga/rb-grn-table-cursor.c +56 -1
- data/ext/groonga/rb-grn-table-key-support.c +2 -2
- data/ext/groonga/rb-grn-table.c +10 -38
- data/ext/groonga/rb-grn.h +11 -1
- data/html/index.html +6 -6
- data/html/ranguba.css +8 -1
- data/lib/groonga.rb +1 -0
- data/lib/groonga/dumper.rb +481 -0
- data/lib/groonga/schema.rb +54 -181
- data/rroonga-build.rb +1 -1
- data/test-unit-notify/Rakefile +47 -0
- data/test-unit-notify/lib/test/unit/notify.rb +104 -0
- data/test-unit/COPYING +56 -0
- data/test-unit/GPL +340 -0
- data/test-unit/PSFL +271 -0
- data/test-unit/Rakefile +18 -5
- data/test-unit/html/bar.svg +153 -0
- data/test-unit/html/developer.svg +469 -0
- data/test-unit/html/favicon.ico +0 -0
- data/test-unit/html/favicon.svg +82 -0
- data/test-unit/html/heading-mark.svg +393 -0
- data/test-unit/html/index.html +235 -13
- data/test-unit/html/index.html.ja +258 -15
- data/test-unit/html/install.svg +636 -0
- data/test-unit/html/logo.svg +483 -0
- data/test-unit/html/test-unit.css +339 -0
- data/test-unit/html/tutorial.svg +559 -0
- data/test-unit/lib/test/unit.rb +6 -1
- data/test-unit/lib/test/unit/assertions.rb +115 -11
- data/test-unit/lib/test/unit/autorunner.rb +5 -2
- data/test-unit/lib/test/unit/collector/load.rb +1 -1
- data/test-unit/lib/test/unit/color-scheme.rb +6 -2
- data/test-unit/lib/test/unit/diff.rb +17 -1
- data/test-unit/lib/test/unit/testcase.rb +7 -0
- data/test-unit/lib/test/unit/testresult.rb +34 -2
- data/test-unit/lib/test/unit/ui/console/testrunner.rb +9 -45
- data/test-unit/lib/test/unit/ui/tap/testrunner.rb +2 -12
- data/test-unit/lib/test/unit/ui/testrunner.rb +25 -0
- data/test-unit/lib/test/unit/util/backtracefilter.rb +1 -0
- data/test-unit/lib/test/unit/util/output.rb +31 -0
- data/test-unit/lib/test/unit/version.rb +1 -1
- data/test-unit/test/test-color-scheme.rb +4 -2
- data/test-unit/test/test_assertions.rb +51 -5
- data/test-unit/test/ui/test_tap.rb +33 -0
- data/test-unit/test/util/test-output.rb +11 -0
- data/test/groonga-test-utils.rb +1 -0
- data/test/test-accessor.rb +32 -0
- data/test/test-context.rb +7 -1
- data/test/test-database-dumper.rb +156 -0
- data/test/test-index-column.rb +67 -1
- data/test/test-schema-dumper.rb +181 -0
- data/test/test-schema.rb +53 -97
- data/test/test-table-dumper.rb +83 -0
- metadata +48 -11
- data/ext/groonga/mkmf.log +0 -99
- data/test-unit/html/classic.html +0 -15
data/ext/groonga/rb-grn-object.c
CHANGED
@@ -133,8 +133,8 @@ rb_grn_object_finalizer (grn_ctx *context, int n_args, grn_obj **grn_objects,
|
|
133
133
|
RB_GRN_INDEX_COLUMN(rb_grn_object));
|
134
134
|
break;
|
135
135
|
case GRN_ACCESSOR:
|
136
|
-
|
137
|
-
|
136
|
+
rb_grn_accessor_finalizer(context, grn_object,
|
137
|
+
RB_GRN_ACCESSOR(rb_grn_object));
|
138
138
|
break;
|
139
139
|
case GRN_EXPR:
|
140
140
|
rb_grn_expression_finalizer(context, grn_object,
|
@@ -378,8 +378,12 @@ rb_grn_object_assign (VALUE klass, VALUE self, VALUE rb_context,
|
|
378
378
|
rb_grn_object_bind_common(klass, self, rb_context, rb_grn_object,
|
379
379
|
context, object);
|
380
380
|
rb_grn_column_bind(RB_GRN_COLUMN(rb_grn_object), context, object);
|
381
|
-
} else if (klass == rb_cGrnAccessor
|
382
|
-
|
381
|
+
} else if (klass == rb_cGrnAccessor) {
|
382
|
+
rb_grn_object = ALLOC(RbGrnAccessor);
|
383
|
+
rb_grn_object_bind_common(klass, self, rb_context, rb_grn_object,
|
384
|
+
context, object);
|
385
|
+
rb_grn_accessor_bind(RB_GRN_ACCESSOR(rb_grn_object), context, object);
|
386
|
+
} else if (klass == rb_cGrnViewAccessor) {
|
383
387
|
rb_grn_object = ALLOC(RbGrnNamedObject);
|
384
388
|
rb_grn_object_bind_common(klass, self, rb_context, rb_grn_object,
|
385
389
|
context, object);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/* -*- c-file-style: "ruby" -*- */
|
2
2
|
/*
|
3
|
-
Copyright (C) 2009-
|
3
|
+
Copyright (C) 2009-2011 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
|
5
5
|
This library is free software; you can redistribute it and/or
|
6
6
|
modify it under the terms of the GNU Lesser General Public
|
@@ -64,6 +64,61 @@ rb_grn_table_cursor_deconstruct (RbGrnTableCursor *rb_grn_table_cursor,
|
|
64
64
|
range_id, range);
|
65
65
|
}
|
66
66
|
|
67
|
+
int
|
68
|
+
rb_grn_table_cursor_order_to_flag (VALUE rb_order)
|
69
|
+
{
|
70
|
+
int flag = 0;
|
71
|
+
|
72
|
+
if (NIL_P(rb_order) ||
|
73
|
+
rb_grn_equal_option(rb_order, "asc") ||
|
74
|
+
rb_grn_equal_option(rb_order, "ascending")) {
|
75
|
+
flag |= GRN_CURSOR_ASCENDING;
|
76
|
+
} else if (rb_grn_equal_option(rb_order, "desc") ||
|
77
|
+
rb_grn_equal_option(rb_order, "descending")) {
|
78
|
+
flag |= GRN_CURSOR_DESCENDING;
|
79
|
+
} else {
|
80
|
+
rb_raise(rb_eArgError,
|
81
|
+
"order should be one of "
|
82
|
+
"[:asc, :ascending, :desc, :descending]: %s",
|
83
|
+
rb_grn_inspect(rb_order));
|
84
|
+
}
|
85
|
+
|
86
|
+
return flag;
|
87
|
+
}
|
88
|
+
|
89
|
+
int
|
90
|
+
rb_grn_table_cursor_order_by_to_flag (unsigned char table_type,
|
91
|
+
VALUE rb_table,
|
92
|
+
VALUE rb_order_by)
|
93
|
+
{
|
94
|
+
int flag = 0;
|
95
|
+
|
96
|
+
if (NIL_P(rb_order_by)) {
|
97
|
+
if (table_type == GRN_TABLE_PAT_KEY) {
|
98
|
+
flag |= GRN_CURSOR_BY_KEY;
|
99
|
+
} else {
|
100
|
+
flag |= GRN_CURSOR_BY_ID;
|
101
|
+
}
|
102
|
+
} else if (rb_grn_equal_option(rb_order_by, "id")) {
|
103
|
+
flag |= GRN_CURSOR_BY_ID;
|
104
|
+
} else if (rb_grn_equal_option(rb_order_by, "key")) {
|
105
|
+
if (table_type != GRN_TABLE_PAT_KEY) {
|
106
|
+
rb_raise(rb_eArgError,
|
107
|
+
"order_by => :key is available "
|
108
|
+
"only for Groonga::PatriciaTrie: %s",
|
109
|
+
rb_grn_inspect(rb_table));
|
110
|
+
}
|
111
|
+
flag |= GRN_CURSOR_BY_KEY;
|
112
|
+
} else {
|
113
|
+
rb_raise(rb_eArgError,
|
114
|
+
"order_by should be one of [:id%s]: %s",
|
115
|
+
table_type == GRN_TABLE_PAT_KEY ? ", :key" : "",
|
116
|
+
rb_grn_inspect(rb_order_by));
|
117
|
+
}
|
118
|
+
|
119
|
+
return flag;
|
120
|
+
}
|
121
|
+
|
67
122
|
/*
|
68
123
|
* call-seq:
|
69
124
|
* cursor.value -> 値
|
@@ -663,7 +663,7 @@ rb_grn_table_key_support_set_default_tokenizer (VALUE self, VALUE rb_tokenizer)
|
|
663
663
|
* す。
|
664
664
|
*/
|
665
665
|
static VALUE
|
666
|
-
|
666
|
+
rb_grn_table_key_normalize_key_p (VALUE self)
|
667
667
|
{
|
668
668
|
grn_obj *table;
|
669
669
|
|
@@ -732,7 +732,7 @@ rb_grn_init_table_key_support (VALUE mGrn)
|
|
732
732
|
rb_grn_table_key_support_set_default_tokenizer, 1);
|
733
733
|
|
734
734
|
rb_define_method(rb_mGrnTableKeySupport, "normalize_key?",
|
735
|
-
|
735
|
+
rb_grn_table_key_normalize_key_p, 0);
|
736
736
|
|
737
737
|
rb_define_method(rb_mGrnTableKeySupport, "support_key?",
|
738
738
|
rb_grn_table_key_support_support_key_p, 0);
|
data/ext/groonga/rb-grn-table.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/* -*- c-file-style: "ruby" -*- */
|
2
2
|
/*
|
3
|
-
Copyright (C) 2009-
|
3
|
+
Copyright (C) 2009-2011 Kouhei Sutou <kou@clear-code.com>
|
4
4
|
|
5
5
|
This library is free software; you can redistribute it and/or
|
6
6
|
modify it under the terms of the GNU Lesser General Public
|
@@ -43,7 +43,9 @@ grn_obj *
|
|
43
43
|
rb_grn_table_from_ruby_object (VALUE object, grn_ctx **context)
|
44
44
|
{
|
45
45
|
if (!RVAL2CBOOL(rb_obj_is_kind_of(object, rb_cGrnTable))) {
|
46
|
-
rb_raise(rb_eTypeError,
|
46
|
+
rb_raise(rb_eTypeError,
|
47
|
+
"not a groonga table: <%s>",
|
48
|
+
rb_grn_inspect(object));
|
47
49
|
}
|
48
50
|
|
49
51
|
return RVAL2GRNOBJECT(object, context);
|
@@ -738,41 +740,10 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
|
|
738
740
|
if (!NIL_P(rb_limit))
|
739
741
|
limit = NUM2INT(rb_limit);
|
740
742
|
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
} else if (rb_grn_equal_option(rb_order, "desc") ||
|
746
|
-
rb_grn_equal_option(rb_order, "descending")) {
|
747
|
-
flags |= GRN_CURSOR_DESCENDING;
|
748
|
-
} else {
|
749
|
-
rb_raise(rb_eArgError,
|
750
|
-
"order should be one of "
|
751
|
-
"[:asc, :ascending, :desc, :descending]: %s",
|
752
|
-
rb_grn_inspect(rb_order));
|
753
|
-
}
|
754
|
-
if (NIL_P(rb_order_by)) {
|
755
|
-
if (table->header.type == GRN_TABLE_PAT_KEY) {
|
756
|
-
flags |= GRN_CURSOR_BY_KEY;
|
757
|
-
} else {
|
758
|
-
flags |= GRN_CURSOR_BY_ID;
|
759
|
-
}
|
760
|
-
} else if (rb_grn_equal_option(rb_order_by, "id")) {
|
761
|
-
flags |= GRN_CURSOR_BY_ID;
|
762
|
-
} else if (rb_grn_equal_option(rb_order_by, "key")) {
|
763
|
-
if (table->header.type != GRN_TABLE_PAT_KEY) {
|
764
|
-
rb_raise(rb_eArgError,
|
765
|
-
"order_by => :key is available "
|
766
|
-
"only for Groonga::PatriciaTrie: %s",
|
767
|
-
rb_grn_inspect(self));
|
768
|
-
}
|
769
|
-
flags |= GRN_CURSOR_BY_KEY;
|
770
|
-
} else {
|
771
|
-
rb_raise(rb_eArgError,
|
772
|
-
"order_by should be one of [:id%s]: %s",
|
773
|
-
table->header.type == GRN_TABLE_PAT_KEY ? ", :key" : "",
|
774
|
-
rb_grn_inspect(rb_order_by));
|
775
|
-
}
|
743
|
+
flags |= rb_grn_table_cursor_order_to_flag(rb_order);
|
744
|
+
flags |= rb_grn_table_cursor_order_by_to_flag(table->header.type,
|
745
|
+
self,
|
746
|
+
rb_order_by);
|
776
747
|
|
777
748
|
if (RVAL2CBOOL(rb_greater_than))
|
778
749
|
flags |= GRN_CURSOR_GT;
|
@@ -816,7 +787,8 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
|
|
816
787
|
*
|
817
788
|
* [+:order+]
|
818
789
|
* +:asc+または+:ascending+を指定すると昇順にレコードを取
|
819
|
-
*
|
790
|
+
* り出す。(デフォルト)
|
791
|
+
*
|
820
792
|
* +:desc+または+:descending+を指定すると降順にレコードを
|
821
793
|
* 取り出す。
|
822
794
|
*
|
data/ext/groonga/rb-grn.h
CHANGED
@@ -68,7 +68,7 @@ RB_GRN_BEGIN_DECLS
|
|
68
68
|
#endif
|
69
69
|
|
70
70
|
#define RB_GRN_MAJOR_VERSION 1
|
71
|
-
#define RB_GRN_MINOR_VERSION
|
71
|
+
#define RB_GRN_MINOR_VERSION 2
|
72
72
|
#define RB_GRN_MICRO_VERSION 0
|
73
73
|
|
74
74
|
#define RB_GRN_QUERY_DEFAULT_MAX_EXPRESSIONS 32
|
@@ -153,6 +153,7 @@ typedef struct _RbGrnAccessor RbGrnAccessor;
|
|
153
153
|
struct _RbGrnAccessor
|
154
154
|
{
|
155
155
|
RbGrnNamedObject parent;
|
156
|
+
grn_obj *value;
|
156
157
|
};
|
157
158
|
|
158
159
|
typedef struct _RbGrnTableCursor RbGrnTableCursor;
|
@@ -386,6 +387,10 @@ VALUE rb_grn_table_set_column_value (VALUE self,
|
|
386
387
|
|
387
388
|
grn_ctx *rb_grn_table_cursor_ensure_context (VALUE cursor,
|
388
389
|
VALUE *rb_context);
|
390
|
+
int rb_grn_table_cursor_order_to_flag (VALUE rb_order);
|
391
|
+
int rb_grn_table_cursor_order_by_to_flag (unsigned char table_type,
|
392
|
+
VALUE rb_table,
|
393
|
+
VALUE rb_order_by);
|
389
394
|
|
390
395
|
void rb_grn_table_key_support_bind (RbGrnTableKeySupport *rb_grn_table_key_support,
|
391
396
|
grn_ctx *context,
|
@@ -518,6 +523,11 @@ VALUE rb_grn_column_expression_builder_build
|
|
518
523
|
#define GRNCOLUMN2RVAL(klass, context, column, owner) \
|
519
524
|
(rb_grn_column_to_ruby_object(klass, context, column, owner))
|
520
525
|
|
526
|
+
#define RVAL2GRNACCESSOR(object) \
|
527
|
+
(rb_grn_accessor_from_ruby_object(object))
|
528
|
+
#define GRNACCESSOR2RVAL(context, accessor, owner) \
|
529
|
+
(rb_grn_accessor_to_ruby_object(context, accessor, owner))
|
530
|
+
|
521
531
|
#define RVAL2GRNQUERY(object) (rb_grn_query_from_ruby_object(object))
|
522
532
|
#define GRNQUERY2RVAL(context, column)(rb_grn_query_to_ruby_object(context, column))
|
523
533
|
|
data/html/index.html
CHANGED
@@ -49,7 +49,7 @@
|
|
49
49
|
|
50
50
|
<h3>rroongaの最新リリース</h3>
|
51
51
|
<p>
|
52
|
-
2011-
|
52
|
+
2011-04-01にリリースされた1.2.0が最新です。
|
53
53
|
</p>
|
54
54
|
|
55
55
|
<h3 id="install-rroonga">rroongaのインストール</h3>
|
@@ -100,7 +100,7 @@
|
|
100
100
|
これから徐々に追加されていく予定です。
|
101
101
|
</p>
|
102
102
|
<ul>
|
103
|
-
<li><a href="
|
103
|
+
<li><a href="activegroonga/">ActiveGroongaのリファレンスマニュアル</a></li>
|
104
104
|
</ul>
|
105
105
|
|
106
106
|
<h2 id="about-racknga">racknga</h2>
|
@@ -135,11 +135,11 @@
|
|
135
135
|
|
136
136
|
<h3>ChupaTextの最新リリース</h3>
|
137
137
|
<p>
|
138
|
-
|
138
|
+
2011-02-09にリリースされた0.9.0が最新です。
|
139
139
|
</p>
|
140
140
|
<p>
|
141
|
-
|
142
|
-
[<a href="chupatext/ja/news.html#news.release-0-
|
141
|
+
[<a href="http://rubyforge.org/frs/download.php/74190/chupatext-0.9.0.tar.gz">ダウンロード</a>]
|
142
|
+
[<a href="chupatext/ja/news.html#news.release-0-9-0">変更点</a>]
|
143
143
|
</p>
|
144
144
|
|
145
145
|
<h3 id="install-chupatext">ChupaTextのインストール</h3>
|
@@ -216,7 +216,7 @@
|
|
216
216
|
</p>
|
217
217
|
<p id="sponsor-github">
|
218
218
|
<a href="http://github.com/ranguba/">
|
219
|
-
ラングバプロジェクトはGitHubにホスティングしてもらっています。
|
219
|
+
<img src="github-logo.png" width="100" height="45" border="0" alt="ラングバプロジェクトはGitHubにホスティングしてもらっています。" />
|
220
220
|
</a>
|
221
221
|
</p>
|
222
222
|
<p id="sponsor-tango">
|
data/html/ranguba.css
CHANGED
@@ -248,13 +248,20 @@ div.sponsors p#sponsor-rubyforge
|
|
248
248
|
right: 50px;
|
249
249
|
}
|
250
250
|
|
251
|
-
div.sponsors p#sponsor-
|
251
|
+
div.sponsors p#sponsor-github
|
252
252
|
{
|
253
253
|
position: absolute;
|
254
254
|
top: 50px;
|
255
255
|
right: 50px;
|
256
256
|
}
|
257
257
|
|
258
|
+
div.sponsors p#sponsor-tango
|
259
|
+
{
|
260
|
+
position: absolute;
|
261
|
+
top: 105px;
|
262
|
+
right: 50px;
|
263
|
+
}
|
264
|
+
|
258
265
|
dt
|
259
266
|
{
|
260
267
|
margin-bottom: 0.25em;
|
data/lib/groonga.rb
CHANGED
@@ -0,0 +1,481 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011 Kouhei Sutou <kou@clear-code.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License version 2.1 as published by the Free Software Foundation.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
|
+
|
18
|
+
require 'stringio'
|
19
|
+
|
20
|
+
module Groonga
|
21
|
+
# データベースの内容をgrn式形式の文字列として出力するクラス。
|
22
|
+
class DatabaseDumper
|
23
|
+
def initialize(options={})
|
24
|
+
@options = options
|
25
|
+
end
|
26
|
+
|
27
|
+
def dump
|
28
|
+
options = @options.dup
|
29
|
+
have_output = !@options[:output].nil?
|
30
|
+
options[:output] ||= StringIO.new
|
31
|
+
if options[:database].nil?
|
32
|
+
options[:context] ||= Groonga::Context.default
|
33
|
+
options[:database] = options[:context].database
|
34
|
+
end
|
35
|
+
options[:dump_schema] = true if options[:dump_schema].nil?
|
36
|
+
options[:dump_tables] = true if options[:dump_tables].nil?
|
37
|
+
|
38
|
+
dump_schema(options) if options[:dump_schema]
|
39
|
+
dump_tables(options) if options[:dump_tables]
|
40
|
+
|
41
|
+
if have_output
|
42
|
+
nil
|
43
|
+
else
|
44
|
+
options[:output].string
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def dump_schema(options)
|
50
|
+
SchemaDumper.new(options.merge(:syntax => :command)).dump
|
51
|
+
end
|
52
|
+
|
53
|
+
def dump_tables(options)
|
54
|
+
first_table = true
|
55
|
+
options[:database].each(:order_by => :key) do |object|
|
56
|
+
next unless object.is_a?(Groonga::Table)
|
57
|
+
next if object.size.zero?
|
58
|
+
next if lexicon_table?(object)
|
59
|
+
next unless target_table?(options[:tables], object)
|
60
|
+
options[:output].write("\n") if !first_table or options[:dump_schema]
|
61
|
+
first_table = false
|
62
|
+
dump_records(object, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def dump_records(table, options)
|
67
|
+
TableDumper.new(table, options).dump
|
68
|
+
end
|
69
|
+
|
70
|
+
def lexicon_table?(table)
|
71
|
+
table.support_key? and
|
72
|
+
table.default_tokenizer and
|
73
|
+
table.columns.any? {|column| column.index?}
|
74
|
+
end
|
75
|
+
|
76
|
+
def target_table?(target_tables, table)
|
77
|
+
return true if target_tables.nil? or target_tables.empty?
|
78
|
+
target_tables.any? do |name|
|
79
|
+
name === table.name
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# スキーマの内容をRubyスクリプトまたはgrn式形式の文字列と
|
85
|
+
# して出力するクラス。
|
86
|
+
class SchemaDumper
|
87
|
+
def initialize(options={})
|
88
|
+
@options = (options || {}).dup
|
89
|
+
end
|
90
|
+
|
91
|
+
def dump
|
92
|
+
database = @options[:database]
|
93
|
+
if database.nil?
|
94
|
+
context = @options[:context] || Groonga::Context.default
|
95
|
+
database = context.database
|
96
|
+
end
|
97
|
+
return nil if database.nil?
|
98
|
+
|
99
|
+
output = @options[:output]
|
100
|
+
have_output = !output.nil?
|
101
|
+
output ||= StringIO.new
|
102
|
+
result = syntax(database, output).dump
|
103
|
+
if have_output
|
104
|
+
result
|
105
|
+
else
|
106
|
+
output.string
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def syntax(database, output)
|
112
|
+
case @options[:syntax]
|
113
|
+
when :command
|
114
|
+
CommandSyntax.new(database, output)
|
115
|
+
else
|
116
|
+
RubySyntax.new(database, output)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class BaseSyntax # :nodoc:
|
121
|
+
def initialize(database, output)
|
122
|
+
@database = database
|
123
|
+
@output = output
|
124
|
+
@table_defined = false
|
125
|
+
@index_columns = []
|
126
|
+
@reference_columns = []
|
127
|
+
end
|
128
|
+
|
129
|
+
def dump
|
130
|
+
header
|
131
|
+
dump_schema
|
132
|
+
footer
|
133
|
+
end
|
134
|
+
|
135
|
+
def dump_schema
|
136
|
+
@database.each(:order_by => :key) do |object|
|
137
|
+
create_table(object) if object.is_a?(Groonga::Table)
|
138
|
+
end
|
139
|
+
|
140
|
+
@reference_columns.group_by do |column|
|
141
|
+
column.table
|
142
|
+
end.each do |table, columns|
|
143
|
+
change_table(table) do
|
144
|
+
columns.each do |column|
|
145
|
+
define_reference_column(table, column)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
@index_columns.group_by do |column|
|
151
|
+
column.table
|
152
|
+
end.each do |table, columns|
|
153
|
+
change_table(table) do
|
154
|
+
columns.each do |column|
|
155
|
+
define_index_column(table, column)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
def write(content)
|
163
|
+
@output.write(content)
|
164
|
+
end
|
165
|
+
|
166
|
+
def header
|
167
|
+
write("")
|
168
|
+
end
|
169
|
+
|
170
|
+
def footer
|
171
|
+
write("")
|
172
|
+
end
|
173
|
+
|
174
|
+
def table_separator
|
175
|
+
write("\n")
|
176
|
+
end
|
177
|
+
|
178
|
+
def create_table(table)
|
179
|
+
table_separator if @table_defined
|
180
|
+
create_table_header(table)
|
181
|
+
table.columns.sort_by {|column| column.local_name}.each do |column|
|
182
|
+
if column.is_a?(Groonga::IndexColumn)
|
183
|
+
@index_columns << column
|
184
|
+
else
|
185
|
+
if column.range.is_a?(Groonga::Table)
|
186
|
+
@reference_columns << column
|
187
|
+
else
|
188
|
+
define_column(table, column)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
create_table_footer(table)
|
193
|
+
@table_defined = true
|
194
|
+
end
|
195
|
+
|
196
|
+
def change_table(table)
|
197
|
+
table_separator if @table_defined
|
198
|
+
change_table_header(table)
|
199
|
+
yield(table)
|
200
|
+
change_table_footer(table)
|
201
|
+
@table_defined = true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class RubySyntax < BaseSyntax # :nodoc:
|
206
|
+
private
|
207
|
+
def create_table_header(table)
|
208
|
+
parameters = []
|
209
|
+
unless table.is_a?(Groonga::Array)
|
210
|
+
case table
|
211
|
+
when Groonga::Hash
|
212
|
+
parameters << ":type => :hash"
|
213
|
+
when Groonga::PatriciaTrie
|
214
|
+
parameters << ":type => :patricia_trie"
|
215
|
+
end
|
216
|
+
if table.domain
|
217
|
+
parameters << ":key_type => #{table.domain.name.dump}"
|
218
|
+
if table.normalize_key?
|
219
|
+
parameters << ":key_normalize => true"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
default_tokenizer = table.default_tokenizer
|
223
|
+
if default_tokenizer
|
224
|
+
parameters << ":default_tokenizer => #{default_tokenizer.name.dump}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
parameters << ":force => true"
|
228
|
+
parameters.unshift("")
|
229
|
+
parameters = parameters.join(",\n ")
|
230
|
+
write("create_table(#{table.name.dump}#{parameters}) do |table|\n")
|
231
|
+
end
|
232
|
+
|
233
|
+
def create_table_footer(table)
|
234
|
+
write("end\n")
|
235
|
+
end
|
236
|
+
|
237
|
+
def change_table_header(table)
|
238
|
+
write("change_table(#{table.name.inspect}) do |table|\n")
|
239
|
+
end
|
240
|
+
|
241
|
+
def change_table_footer(table)
|
242
|
+
write("end\n")
|
243
|
+
end
|
244
|
+
|
245
|
+
def define_column(table, column)
|
246
|
+
type = column_method(column)
|
247
|
+
name = column.local_name
|
248
|
+
write(" table.#{type}(#{name.inspect})\n")
|
249
|
+
end
|
250
|
+
|
251
|
+
def define_reference_column(table, column)
|
252
|
+
name = column.local_name
|
253
|
+
reference = column.range
|
254
|
+
write(" table.reference(#{name.dump}, #{reference.name.dump})\n")
|
255
|
+
end
|
256
|
+
|
257
|
+
def define_index_column(table, column)
|
258
|
+
target_table_name = column.range.name
|
259
|
+
sources = column.sources
|
260
|
+
source_names = sources.collect do |source|
|
261
|
+
if source.is_a?(table.class)
|
262
|
+
"_key".dump
|
263
|
+
else
|
264
|
+
source.local_name.dump
|
265
|
+
end
|
266
|
+
end.join(", ")
|
267
|
+
arguments = [target_table_name.dump,
|
268
|
+
sources.size == 1 ? source_names : "[#{source_names}]",
|
269
|
+
":name => #{column.local_name.dump}"]
|
270
|
+
write(" table.index(#{arguments.join(', ')})\n")
|
271
|
+
end
|
272
|
+
|
273
|
+
def column_method(column)
|
274
|
+
range = column.range
|
275
|
+
case range.name
|
276
|
+
when "Int32"
|
277
|
+
"integer32"
|
278
|
+
when "Int64"
|
279
|
+
"integer64"
|
280
|
+
when "UInt32"
|
281
|
+
"unsigned_integer32"
|
282
|
+
when "UInt64"
|
283
|
+
"unsigned_integer64"
|
284
|
+
when "Float"
|
285
|
+
"float"
|
286
|
+
when "Time"
|
287
|
+
"time"
|
288
|
+
when "ShortText"
|
289
|
+
"short_text"
|
290
|
+
when "Text"
|
291
|
+
"text"
|
292
|
+
when "LongText"
|
293
|
+
"long_text"
|
294
|
+
else
|
295
|
+
raise ArgumentError, "unsupported column: #{column.inspect}"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
class CommandSyntax < BaseSyntax # :nodoc:
|
301
|
+
private
|
302
|
+
def create_table_header(table)
|
303
|
+
parameters = []
|
304
|
+
flags = []
|
305
|
+
case table
|
306
|
+
when Groonga::Array
|
307
|
+
flags << "TABLE_NO_KEY"
|
308
|
+
when Groonga::Hash
|
309
|
+
flags << "TABLE_HASH_KEY"
|
310
|
+
when Groonga::PatriciaTrie
|
311
|
+
flags << "TABLE_PAT_KEY"
|
312
|
+
end
|
313
|
+
if table.domain
|
314
|
+
flags << "KEY_NORMALIZE" if table.normalize_key?
|
315
|
+
if table.is_a?(Groonga::PatriciaTrie) and table.register_key_with_sis?
|
316
|
+
flags << "KEY_WITH_SIS"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
parameters << "#{flags.join('|')}"
|
320
|
+
if table.domain
|
321
|
+
parameters << "--key_type #{table.domain.name}"
|
322
|
+
end
|
323
|
+
if table.range
|
324
|
+
parameters << "--value_type #{table.range.name}"
|
325
|
+
end
|
326
|
+
if table.domain
|
327
|
+
default_tokenizer = table.default_tokenizer
|
328
|
+
if default_tokenizer
|
329
|
+
parameters << "--default_tokenizer #{default_tokenizer.name}"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
write("table_create #{table.name} #{parameters.join(' ')}\n")
|
333
|
+
end
|
334
|
+
|
335
|
+
def create_table_footer(table)
|
336
|
+
end
|
337
|
+
|
338
|
+
def change_table_header(table)
|
339
|
+
end
|
340
|
+
|
341
|
+
def change_table_footer(table)
|
342
|
+
end
|
343
|
+
|
344
|
+
def define_column(table, column)
|
345
|
+
parameters = []
|
346
|
+
parameters << table.name
|
347
|
+
parameters << column.local_name
|
348
|
+
flags = []
|
349
|
+
if column.scalar?
|
350
|
+
flags << "COLUMN_SCALAR"
|
351
|
+
elsif column.vector?
|
352
|
+
flags << "COLUMN_VECTOR"
|
353
|
+
end
|
354
|
+
# TODO: support COMPRESS_ZLIB and COMPRESS_LZO?
|
355
|
+
parameters << "#{flags.join('|')}"
|
356
|
+
parameters << "#{column.range.name}"
|
357
|
+
write("column_create #{parameters.join(' ')}\n")
|
358
|
+
end
|
359
|
+
|
360
|
+
def define_reference_column(table, column)
|
361
|
+
define_column(table, column)
|
362
|
+
end
|
363
|
+
|
364
|
+
def define_index_column(table, column)
|
365
|
+
parameters = []
|
366
|
+
parameters << table.name
|
367
|
+
parameters << column.local_name
|
368
|
+
flags = []
|
369
|
+
flags << "COLUMN_INDEX"
|
370
|
+
flags << "WITH_SECTION" if column.with_section?
|
371
|
+
flags << "WITH_WEIGHT" if column.with_weight?
|
372
|
+
flags << "WITH_POSITION" if column.with_position?
|
373
|
+
parameters << "#{flags.join('|')}"
|
374
|
+
parameters << "#{column.range.name}"
|
375
|
+
source_names = column.sources.collect do |source|
|
376
|
+
if source.is_a?(table.class)
|
377
|
+
"_key"
|
378
|
+
else
|
379
|
+
source.local_name
|
380
|
+
end
|
381
|
+
end
|
382
|
+
parameters << "#{source_names.join(',')}" unless source_names.empty?
|
383
|
+
write("column_create #{parameters.join(' ')}\n")
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
class TableDumper
|
389
|
+
def initialize(table, options={})
|
390
|
+
@table = table
|
391
|
+
@options = options
|
392
|
+
@output = @options[:output]
|
393
|
+
@have_output = !@output.nil?
|
394
|
+
@output ||= StringIO.new
|
395
|
+
end
|
396
|
+
|
397
|
+
def dump
|
398
|
+
dump_load_command
|
399
|
+
if @have_output
|
400
|
+
nil
|
401
|
+
else
|
402
|
+
@output.string
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
private
|
407
|
+
def write(content)
|
408
|
+
@output.write(content)
|
409
|
+
end
|
410
|
+
|
411
|
+
def dump_load_command
|
412
|
+
write("load --table #{@table.name}\n")
|
413
|
+
write("[\n")
|
414
|
+
columns = available_columns
|
415
|
+
dump_columns(columns)
|
416
|
+
dump_records(columns)
|
417
|
+
write("\n]\n")
|
418
|
+
end
|
419
|
+
|
420
|
+
def dump_columns(columns)
|
421
|
+
column_names = columns.collect do |column|
|
422
|
+
column.local_name
|
423
|
+
end
|
424
|
+
write(column_names.to_json)
|
425
|
+
end
|
426
|
+
|
427
|
+
def dump_records(columns)
|
428
|
+
@table.each do |record|
|
429
|
+
write(",\n")
|
430
|
+
values = columns.collect do |column|
|
431
|
+
resolve_value(column[record.id])
|
432
|
+
end
|
433
|
+
write(values.to_json)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def resolve_value(value)
|
438
|
+
case value
|
439
|
+
when ::Array
|
440
|
+
value.collect do |v|
|
441
|
+
resolve_value(v)
|
442
|
+
end
|
443
|
+
when Groonga::Record
|
444
|
+
if value.support_key?
|
445
|
+
value = value.key
|
446
|
+
else
|
447
|
+
value = value.id
|
448
|
+
end
|
449
|
+
resolve_value(value)
|
450
|
+
when Time
|
451
|
+
# TODO: groonga should support UTC format literal
|
452
|
+
# value.utc.strftime("%Y-%m-%d %H:%M:%S.%6N")
|
453
|
+
value.to_f
|
454
|
+
when NilClass
|
455
|
+
# TODO: remove me. nil reference column value
|
456
|
+
# doesn't accept null.
|
457
|
+
""
|
458
|
+
else
|
459
|
+
value
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def available_columns
|
464
|
+
columns = []
|
465
|
+
if @table.support_key?
|
466
|
+
columns << @table.column("_key")
|
467
|
+
else
|
468
|
+
columns << @table.column("_id")
|
469
|
+
end
|
470
|
+
columns << @table.column("_value") unless @table.range.nil?
|
471
|
+
data_columns = @table.columns.reject do |column|
|
472
|
+
column.index?
|
473
|
+
end
|
474
|
+
sorted_columns = data_columns.sort_by do |column|
|
475
|
+
column.local_name
|
476
|
+
end
|
477
|
+
columns.concat(sorted_columns)
|
478
|
+
columns
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|