rroonga 3.0.9 → 3.1.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.
- checksums.yaml +4 -4
- data/README.textile +10 -10
- data/bin/groonga-database-inspect +40 -0
- data/doc/text/news.textile +19 -0
- data/ext/groonga/extconf.rb +3 -1
- data/ext/groonga/rb-grn-expression.c +1 -1
- data/ext/groonga/rb-grn-operator.c +218 -8
- data/ext/groonga/rb-grn.h +4 -2
- data/lib/groonga.rb +4 -1
- data/lib/groonga/column.rb +25 -0
- data/lib/groonga/database-inspector.rb +279 -0
- data/lib/groonga/database.rb +32 -2
- data/lib/groonga/dumper.rb +7 -13
- data/lib/groonga/index-column.rb +11 -1
- data/lib/groonga/statistic-measurer.rb +37 -0
- data/lib/groonga/table.rb +25 -0
- data/rroonga-build.rb +2 -2
- data/test/groonga-test-utils.rb +10 -10
- data/test/test-column.rb +26 -0
- data/test/test-database-inspector.rb +676 -0
- data/test/test-database.rb +25 -1
- data/test/test-expression-builder.rb +1 -0
- data/test/test-expression.rb +73 -0
- data/test/test-index-column.rb +23 -0
- data/test/test-statistic-measurer.rb +55 -0
- data/test/test-table.rb +40 -0
- metadata +17 -8
- data/lib/groonga/view-record.rb +0 -50
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2013 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
|
+
module Groonga
|
19
|
+
class Column
|
20
|
+
def disk_usage
|
21
|
+
measurer = StatisticMeasurer.new
|
22
|
+
measurer.measure_disk_usage(path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2013 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
|
+
module Groonga
|
19
|
+
# It is a class that inspects database. You can know details metadata
|
20
|
+
# of the database.
|
21
|
+
class DatabaseInspector
|
22
|
+
# @param database [Database] The database to be inspected.
|
23
|
+
def initialize(database)
|
24
|
+
@database = database
|
25
|
+
end
|
26
|
+
|
27
|
+
# Report inspected result of the database.
|
28
|
+
#
|
29
|
+
# @param output [#write] (nil) The output of inspected result.
|
30
|
+
# If it is @nil@, @$stdout@ is used.
|
31
|
+
def report(output=nil)
|
32
|
+
output ||= $stdout
|
33
|
+
reporter = Reporter.new(@database, output)
|
34
|
+
reporter.report
|
35
|
+
end
|
36
|
+
|
37
|
+
# @private
|
38
|
+
class Reporter
|
39
|
+
def initialize(database, output)
|
40
|
+
@database = database
|
41
|
+
@context = @database.context
|
42
|
+
@output = output
|
43
|
+
@indent_width = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def report
|
47
|
+
write("Database\n")
|
48
|
+
indent do
|
49
|
+
write("Path: #{inspect_path(@database.path)}\n")
|
50
|
+
write("Total disk usage: " +
|
51
|
+
"#{inspect_disk_usage(total_disk_usage)}\n")
|
52
|
+
write("Disk usage: " +
|
53
|
+
"#{inspect_sub_disk_usage(@database.disk_usage)}\n")
|
54
|
+
write("N records: #{count_total_n_records}\n")
|
55
|
+
write("N tables: #{count_n_tables}\n")
|
56
|
+
write("N columns: #{count_total_n_columns}\n")
|
57
|
+
report_plugins
|
58
|
+
report_tables
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def report_plugins
|
64
|
+
write("Plugins:\n")
|
65
|
+
indent do
|
66
|
+
plugin_paths = @database.plugin_paths
|
67
|
+
if plugin_paths.empty?
|
68
|
+
write("None\n")
|
69
|
+
return
|
70
|
+
end
|
71
|
+
plugin_paths.each do |path|
|
72
|
+
write("* #{path}\n")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def report_tables
|
78
|
+
write("Tables:\n")
|
79
|
+
indent do
|
80
|
+
tables = @database.tables
|
81
|
+
if tables.empty?
|
82
|
+
write("None\n")
|
83
|
+
return
|
84
|
+
end
|
85
|
+
tables.each do |table|
|
86
|
+
report_table(table)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def report_table(table)
|
92
|
+
write("#{table.name}:\n")
|
93
|
+
indent do
|
94
|
+
write("ID: #{table.id}\n")
|
95
|
+
write("Type: #{inspect_table_type(table)}\n")
|
96
|
+
write("Key type: #{inspect_key_type(table)}\n")
|
97
|
+
write("Tokenizer: #{inspect_tokenizer(table)}\n")
|
98
|
+
write("Normalizer: #{inspect_normalizer(table)}\n")
|
99
|
+
write("Path: #{inspect_path(table.path)}\n")
|
100
|
+
total_table_disk_usage = count_total_table_disk_usage(table)
|
101
|
+
write("Total disk usage: " +
|
102
|
+
"#{inspect_sub_disk_usage(total_table_disk_usage)}\n")
|
103
|
+
write("Disk usage: " +
|
104
|
+
"#{inspect_sub_disk_usage(table.disk_usage)}\n")
|
105
|
+
write("N records: #{table.size}\n")
|
106
|
+
write("N columns: #{table.columns.size}\n")
|
107
|
+
report_columns(table)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def report_columns(table)
|
112
|
+
write("Columns:\n")
|
113
|
+
indent do
|
114
|
+
columns = table.columns
|
115
|
+
if columns.empty?
|
116
|
+
write("None\n")
|
117
|
+
return
|
118
|
+
end
|
119
|
+
columns.each do |column|
|
120
|
+
report_column(column)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def report_column(column)
|
126
|
+
write("#{column.local_name}:\n")
|
127
|
+
indent do
|
128
|
+
write("ID: #{column.id}\n")
|
129
|
+
write("Type: #{inspect_column_type(column)}\n")
|
130
|
+
write("Value type: #{inspect_column_value_type(column)}\n")
|
131
|
+
write("Path: #{inspect_path(column.path)}\n")
|
132
|
+
write("Disk usage: #{inspect_sub_disk_usage(column.disk_usage)}\n")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def indent
|
137
|
+
indent_width = @indent_width
|
138
|
+
@indent_width += 2
|
139
|
+
yield
|
140
|
+
ensure
|
141
|
+
@indent_width = indent_width
|
142
|
+
end
|
143
|
+
|
144
|
+
def write(message)
|
145
|
+
indent = " " * @indent_width
|
146
|
+
@output.write("#{indent}#{message}")
|
147
|
+
end
|
148
|
+
|
149
|
+
def inspect_path(path)
|
150
|
+
if path.nil?
|
151
|
+
"(null)"
|
152
|
+
else
|
153
|
+
"<#{path}>"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
KiB = (2 ** 10).to_f
|
158
|
+
MiB = (2 ** 20).to_f
|
159
|
+
GiB = (2 ** 30).to_f
|
160
|
+
TiB = (2 ** 40).to_f
|
161
|
+
PiB = (2 ** 50).to_f
|
162
|
+
def inspect_disk_usage(disk_usage)
|
163
|
+
if disk_usage < KiB
|
164
|
+
"%dB" % disk_usage
|
165
|
+
elsif disk_usage < MiB
|
166
|
+
"%.3fKiB" % (disk_usage / KiB)
|
167
|
+
elsif disk_usage < GiB
|
168
|
+
"%.3fMiB" % (disk_usage / MiB)
|
169
|
+
elsif disk_usage < TiB
|
170
|
+
"%.3fGiB" % (disk_usage / GiB)
|
171
|
+
elsif disk_usage < PiB
|
172
|
+
"%.3fTiB" % (disk_usage / TiB)
|
173
|
+
else
|
174
|
+
"%.3fPiB" % (disk_usage / PiB)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def inspect_sub_disk_usage(disk_usage)
|
179
|
+
percent = disk_usage / total_disk_usage.to_f * 100
|
180
|
+
"%s (%.3f%%)" % [inspect_disk_usage(disk_usage), percent]
|
181
|
+
end
|
182
|
+
|
183
|
+
def count_total_n_records
|
184
|
+
@database.tables.inject(0) do |previous, table|
|
185
|
+
previous + table.size
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def count_n_tables
|
190
|
+
@database.tables.size
|
191
|
+
end
|
192
|
+
|
193
|
+
def count_total_n_columns
|
194
|
+
@database.tables.inject(0) do |previous, table|
|
195
|
+
previous + table.columns.size
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def total_disk_usage
|
200
|
+
@total_disk_usage ||= count_total_disk_usage
|
201
|
+
end
|
202
|
+
|
203
|
+
def count_total_disk_usage
|
204
|
+
@database.tables.inject(@database.disk_usage) do |previous, table|
|
205
|
+
previous + count_total_table_disk_usage(table)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def count_total_table_disk_usage(table)
|
210
|
+
table.columns.inject(table.disk_usage) do |previous, column|
|
211
|
+
previous + column.disk_usage
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def inspect_table_type(table)
|
216
|
+
case table
|
217
|
+
when Groonga::Array
|
218
|
+
"array"
|
219
|
+
when Groonga::Hash
|
220
|
+
"hash"
|
221
|
+
when Groonga::PatriciaTrie
|
222
|
+
"patricia trie"
|
223
|
+
when Groonga::DoubleArrayTrie
|
224
|
+
"double array trie"
|
225
|
+
else
|
226
|
+
"unknown (#{table.class})"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def inspect_key_type(table)
|
231
|
+
if table.support_key?
|
232
|
+
table.domain.name
|
233
|
+
else
|
234
|
+
"(no key)"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def inspect_tokenizer(table)
|
239
|
+
if table.support_key?
|
240
|
+
tokenizer = table.default_tokenizer
|
241
|
+
if tokenizer
|
242
|
+
tokenizer.name
|
243
|
+
else
|
244
|
+
"(no tokenizer)"
|
245
|
+
end
|
246
|
+
else
|
247
|
+
"(no key)"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def inspect_normalizer(table)
|
252
|
+
if table.support_key?
|
253
|
+
normalizer = table.normalizer
|
254
|
+
if normalizer
|
255
|
+
normalizer.name
|
256
|
+
else
|
257
|
+
"(no normalizer)"
|
258
|
+
end
|
259
|
+
else
|
260
|
+
"(no key)"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def inspect_column_type(column)
|
265
|
+
if column.index?
|
266
|
+
"index"
|
267
|
+
elsif column.vector?
|
268
|
+
"vector"
|
269
|
+
else
|
270
|
+
"scalar"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def inspect_column_value_type(column)
|
275
|
+
column.domain.name
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
data/lib/groonga/database.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2013 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
|
@@ -19,11 +19,41 @@ module Groonga
|
|
19
19
|
class Database
|
20
20
|
# @return [Array<Groonga::Table>] tables defined in the database.
|
21
21
|
def tables
|
22
|
-
|
22
|
+
options = {
|
23
|
+
:ignore_missing_object => true,
|
24
|
+
:order_by => :key,
|
25
|
+
}
|
26
|
+
each(options).find_all do |object|
|
23
27
|
object.is_a?(Groonga::Table)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
31
|
+
# @return [Array<String>] registered plugin paths.
|
32
|
+
def plugin_paths
|
33
|
+
processed_paths = {}
|
34
|
+
paths = []
|
35
|
+
each(:ignore_missing_object => true, :order_by => :id) do |object|
|
36
|
+
next unless object.is_a?(Groonga::Procedure)
|
37
|
+
next if object.builtin?
|
38
|
+
path = object.path
|
39
|
+
next if path.nil?
|
40
|
+
next if processed_paths.has_key?(path)
|
41
|
+
processed_paths[path] = true
|
42
|
+
paths << path
|
43
|
+
end
|
44
|
+
paths
|
45
|
+
end
|
46
|
+
|
47
|
+
def disk_usage
|
48
|
+
return 0 if path.nil?
|
49
|
+
|
50
|
+
usage = 0
|
51
|
+
measurer = StatisticMeasurer.new
|
52
|
+
usage += measurer.measure_disk_usage(path)
|
53
|
+
usage += measurer.measure_disk_usage("%s.%07X" % [path, 0])
|
54
|
+
usage
|
55
|
+
end
|
56
|
+
|
27
57
|
def dump_index(output_directory)
|
28
58
|
each do |object|
|
29
59
|
next unless object.is_a?(Groonga::IndexColumn)
|
data/lib/groonga/dumper.rb
CHANGED
@@ -81,15 +81,9 @@ module Groonga
|
|
81
81
|
|
82
82
|
private
|
83
83
|
def dump_plugins(options)
|
84
|
-
plugin_paths =
|
85
|
-
|
86
|
-
|
87
|
-
next if object.builtin?
|
88
|
-
path = object.path
|
89
|
-
next if path.nil?
|
90
|
-
next if plugin_paths.has_key?(path)
|
91
|
-
plugin_paths[path] = true
|
92
|
-
dump_plugin(object, options)
|
84
|
+
plugin_paths = options[:database].plugin_paths
|
85
|
+
plugin_paths.each do |path|
|
86
|
+
dump_plugin(path, options)
|
93
87
|
end
|
94
88
|
options[:output].write("\n") unless plugin_paths.empty?
|
95
89
|
end
|
@@ -112,13 +106,13 @@ module Groonga
|
|
112
106
|
TableDumper.new(table, options).dump
|
113
107
|
end
|
114
108
|
|
115
|
-
def dump_plugin(
|
109
|
+
def dump_plugin(path, options)
|
116
110
|
output = options[:output]
|
117
111
|
plugins_dir_re = Regexp.escape(Groonga::Plugin.system_plugins_dir)
|
118
112
|
suffix_re = Regexp.escape(Groonga::Plugin.suffix)
|
119
|
-
plugin_name =
|
120
|
-
|
121
|
-
|
113
|
+
plugin_name = path.gsub(/(?:\A#{plugins_dir_re}\/|
|
114
|
+
#{suffix_re}\z)/x,
|
115
|
+
'')
|
122
116
|
output.write("register #{plugin_name}\n")
|
123
117
|
end
|
124
118
|
|
data/lib/groonga/index-column.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
|
3
|
+
# Copyright (C) 2012-2013 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
|
@@ -24,6 +24,16 @@ module Groonga
|
|
24
24
|
dumper = IndexColumnDumper.new(self, output_directory)
|
25
25
|
dumper.dump
|
26
26
|
end
|
27
|
+
|
28
|
+
def disk_usage
|
29
|
+
return 0 if path.nil?
|
30
|
+
|
31
|
+
usage = super
|
32
|
+
chunk_path = "#{path}.c"
|
33
|
+
measurer = StatisticMeasurer.new
|
34
|
+
usage += measurer.measure_disk_usage(chunk_path)
|
35
|
+
usage
|
36
|
+
end
|
27
37
|
end
|
28
38
|
|
29
39
|
class IndexColumnDumper
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2013 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
|
+
module Groonga
|
19
|
+
# Measures statistic.
|
20
|
+
class StatisticMeasurer
|
21
|
+
MAX_N_ADDITIONAL_PATHS = 4096
|
22
|
+
|
23
|
+
# @param path [String, nil] Measures disk usage of the path.
|
24
|
+
# @return [Integer] 0 if path is @nil@, disk usage of the path otherwise.
|
25
|
+
def measure_disk_usage(path)
|
26
|
+
return 0 if path.nil?
|
27
|
+
|
28
|
+
usage = File.size(path)
|
29
|
+
1.step(MAX_N_ADDITIONAL_PATHS) do |i|
|
30
|
+
additional_path = "%s.%03X" % [path, i]
|
31
|
+
break unless File.exist?(additional_path)
|
32
|
+
usage += File.size(additional_path)
|
33
|
+
end
|
34
|
+
usage
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|