rroonga 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|