groonga-schema 1.0.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 +7 -0
- data/.yardopts +9 -0
- data/Gemfile +21 -0
- data/README.md +164 -0
- data/Rakefile +45 -0
- data/bin/groonga-schema-diff +21 -0
- data/doc/text/lgpl-2.1.txt +502 -0
- data/doc/text/news.md +5 -0
- data/groonga-schema.gemspec +65 -0
- data/lib/groonga-schema/column.rb +211 -0
- data/lib/groonga-schema/command-line/groonga-schema-diff.rb +112 -0
- data/lib/groonga-schema/diff.rb +268 -0
- data/lib/groonga-schema/differ.rb +120 -0
- data/lib/groonga-schema/plugin.rb +40 -0
- data/lib/groonga-schema/schema.rb +87 -0
- data/lib/groonga-schema/table.rb +258 -0
- data/lib/groonga-schema/version.rb +19 -0
- data/lib/groonga-schema.rb +20 -0
- data/test/run-test.rb +32 -0
- data/test/test-column.rb +52 -0
- data/test/test-diff.rb +549 -0
- data/test/test-differ.rb +222 -0
- data/test/test-plugin.rb +43 -0
- data/test/test-schema.rb +138 -0
- data/test/test-table.rb +92 -0
- metadata +190 -0
@@ -0,0 +1,211 @@
|
|
1
|
+
# Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
module GroongaSchema
|
18
|
+
class Column
|
19
|
+
attr_reader :table_name
|
20
|
+
attr_reader :name
|
21
|
+
attr_accessor :type
|
22
|
+
attr_accessor :flags
|
23
|
+
attr_accessor :value_type
|
24
|
+
attr_accessor :sources
|
25
|
+
attr_writer :reference_value_type
|
26
|
+
attr_accessor :related_columns
|
27
|
+
def initialize(table_name, name)
|
28
|
+
@table_name = table_name
|
29
|
+
@name = name
|
30
|
+
@type = :scalar
|
31
|
+
@flags = []
|
32
|
+
@value_type = "ShortText"
|
33
|
+
@sources = []
|
34
|
+
@reference_value_type = false
|
35
|
+
@related_columns = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def reference_value_type?
|
39
|
+
@reference_value_type
|
40
|
+
end
|
41
|
+
|
42
|
+
def apply_command(command)
|
43
|
+
applier = CommandApplier.new(self, command)
|
44
|
+
applier.apply
|
45
|
+
end
|
46
|
+
|
47
|
+
def apply_column(column)
|
48
|
+
self.type = column.type
|
49
|
+
self.flags = column.flags
|
50
|
+
self.value_type = column.value_type
|
51
|
+
self.sources = column.sources
|
52
|
+
self.reference_value_type = column.reference_value_type?
|
53
|
+
end
|
54
|
+
|
55
|
+
def ==(other)
|
56
|
+
return false unless other.is_a?(self.class)
|
57
|
+
|
58
|
+
@table_name == other.table_name and
|
59
|
+
@name == other.name and
|
60
|
+
@type == other.type and
|
61
|
+
@flags.sort == other.flags.sort and
|
62
|
+
@value_type == other.value_type and
|
63
|
+
@sources == other.sources
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_create_groonga_command
|
67
|
+
column_create_command(@name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_remove_groonga_command
|
71
|
+
column_remove_command(@name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_copy_groonga_command(to_table_name, to_name)
|
75
|
+
column_copy_command(to_table_name, to_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_migrate_start_groonga_commands
|
79
|
+
commands = []
|
80
|
+
commands << column_create_command(new_name)
|
81
|
+
if type != :index
|
82
|
+
commands << column_copy_command(@table_name, new_name)
|
83
|
+
end
|
84
|
+
commands << column_rename_command(@name, old_name)
|
85
|
+
commands << column_rename_command(new_name, @name)
|
86
|
+
commands
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_migrate_finish_groonga_commands
|
90
|
+
[
|
91
|
+
column_remove_command(old_name),
|
92
|
+
]
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def old_name
|
97
|
+
"#{@name}_old"
|
98
|
+
end
|
99
|
+
|
100
|
+
def new_name
|
101
|
+
"#{@name}_new"
|
102
|
+
end
|
103
|
+
|
104
|
+
def column_create_command(name)
|
105
|
+
flags_value = [type_flag, *flags].join("|")
|
106
|
+
sources_value = @sources.join(",")
|
107
|
+
sources_value = nil if sources_value.empty?
|
108
|
+
arguments = {
|
109
|
+
"table" => @table_name,
|
110
|
+
"name" => name,
|
111
|
+
"flags" => flags_value,
|
112
|
+
"type" => @value_type,
|
113
|
+
"source" => sources_value,
|
114
|
+
}
|
115
|
+
Groonga::Command::ColumnCreate.new(arguments)
|
116
|
+
end
|
117
|
+
|
118
|
+
def column_remove_command(name)
|
119
|
+
arguments = {
|
120
|
+
"table" => @table_name,
|
121
|
+
"name" => name,
|
122
|
+
}
|
123
|
+
Groonga::Command::ColumnRemove.new(arguments)
|
124
|
+
end
|
125
|
+
|
126
|
+
def column_copy_command(to_table_name, to_name)
|
127
|
+
arguments = {
|
128
|
+
"from_table" => @table_name,
|
129
|
+
"from_name" => @name,
|
130
|
+
"to_table" => to_table_name,
|
131
|
+
"to_name" => to_name,
|
132
|
+
}
|
133
|
+
Groonga::Command::ColumnCopy.new(arguments)
|
134
|
+
end
|
135
|
+
|
136
|
+
def column_rename_command(name, new_name)
|
137
|
+
arguments = {
|
138
|
+
"table" => @table_name,
|
139
|
+
"name" => name,
|
140
|
+
"new_name" => new_name,
|
141
|
+
}
|
142
|
+
Groonga::Command::ColumnRename.new(arguments)
|
143
|
+
end
|
144
|
+
|
145
|
+
def type_flag
|
146
|
+
case @type
|
147
|
+
when :scalar
|
148
|
+
"COLUMN_SCALAR"
|
149
|
+
when :vector
|
150
|
+
"COLUMN_VECTOR"
|
151
|
+
when :index
|
152
|
+
"COLUMN_INDEX"
|
153
|
+
else
|
154
|
+
"COLUMN_SCALAR"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class CommandApplier
|
159
|
+
def initialize(column, command)
|
160
|
+
@column = column
|
161
|
+
@command = command
|
162
|
+
end
|
163
|
+
|
164
|
+
def apply
|
165
|
+
apply_flags
|
166
|
+
apply_value_type
|
167
|
+
apply_sources
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
def apply_flags
|
172
|
+
@type = :scalar
|
173
|
+
@flags = []
|
174
|
+
@command.flags.each do |flag|
|
175
|
+
parse_flag(flag)
|
176
|
+
end
|
177
|
+
|
178
|
+
@column.type = @type
|
179
|
+
@column.flags = @flags
|
180
|
+
end
|
181
|
+
|
182
|
+
def parse_flag(flag)
|
183
|
+
case flag
|
184
|
+
when "COLUMN_SCALAR"
|
185
|
+
@type = :scalar
|
186
|
+
when "COLUMN_VECTOR"
|
187
|
+
@type = :vector
|
188
|
+
when "COLUMN_INDEX"
|
189
|
+
@type = :index
|
190
|
+
else
|
191
|
+
@flags << flag
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def apply_value_type
|
196
|
+
# TODO: Validate for index column. Index column must have table as
|
197
|
+
# value type.
|
198
|
+
@column.value_type = @command.type
|
199
|
+
end
|
200
|
+
|
201
|
+
def apply_sources
|
202
|
+
case @type
|
203
|
+
when :index
|
204
|
+
@column.sources = @command.sources
|
205
|
+
else
|
206
|
+
@column.sources = []
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "optparse"
|
18
|
+
require "uri"
|
19
|
+
require "open-uri"
|
20
|
+
|
21
|
+
require "groonga/command/parser"
|
22
|
+
|
23
|
+
require "groonga-schema/differ"
|
24
|
+
|
25
|
+
module GroongaSchema
|
26
|
+
module CommandLine
|
27
|
+
class GroongaSchemaDiff
|
28
|
+
class << self
|
29
|
+
def run(args=ARGV)
|
30
|
+
new(args).run
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(args)
|
35
|
+
@args = args
|
36
|
+
@format = :command
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
parse_arguments
|
41
|
+
|
42
|
+
from_schema = parse_schema(@from)
|
43
|
+
to_schema = parse_schema(@to)
|
44
|
+
differ = GroongaSchema::Differ.new(from_schema, to_schema)
|
45
|
+
diff = differ.diff
|
46
|
+
$stdout.print(diff.to_groonga_command_list(:format => @format))
|
47
|
+
|
48
|
+
if diff.same?
|
49
|
+
0
|
50
|
+
else
|
51
|
+
1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def parse_arguments
|
57
|
+
parser = OptionParser.new
|
58
|
+
parser.banner += " FROM_SCHEMA TO_SCHEMA"
|
59
|
+
|
60
|
+
available_formats = [:command, :uri]
|
61
|
+
parser.on("--format=FORMAT", available_formats,
|
62
|
+
"Specify output Groonga command format.",
|
63
|
+
"Available formats: #{available_formats.join(", ")}",
|
64
|
+
"(#{@format})") do |format|
|
65
|
+
@format = format
|
66
|
+
end
|
67
|
+
|
68
|
+
rest_args = parser.parse(@args)
|
69
|
+
|
70
|
+
if rest_args.size != 2
|
71
|
+
$stderr.puts("Error: Both FROM_SCHEMA and TO_SCHEMA are required.")
|
72
|
+
$stderr.puts(parser.help)
|
73
|
+
exit(false)
|
74
|
+
end
|
75
|
+
@from, @to = rest_args
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_schema(resource_path)
|
79
|
+
open_resource(resource_path) do |resource|
|
80
|
+
schema = GroongaSchema::Schema.new
|
81
|
+
parser = Groonga::Command::Parser.new
|
82
|
+
parser.on_command do |command|
|
83
|
+
schema.apply_command(command)
|
84
|
+
end
|
85
|
+
resource.each_line do |line|
|
86
|
+
parser << line
|
87
|
+
end
|
88
|
+
parser.finish
|
89
|
+
schema
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def open_resource(resource_path)
|
94
|
+
uri = nil
|
95
|
+
begin
|
96
|
+
uri = URI.parse(resource_path)
|
97
|
+
rescue URI::InvalidURIError
|
98
|
+
end
|
99
|
+
|
100
|
+
if uri and uri.respond_to?(:open)
|
101
|
+
uri.open do |response|
|
102
|
+
yield(response)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
File.open(resource_path) do |file|
|
106
|
+
yield(file)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
module GroongaSchema
|
18
|
+
class Diff
|
19
|
+
attr_reader :removed_plugins
|
20
|
+
attr_reader :added_plugins
|
21
|
+
|
22
|
+
attr_reader :removed_tables
|
23
|
+
attr_reader :added_tables
|
24
|
+
attr_reader :changed_tables
|
25
|
+
|
26
|
+
attr_reader :removed_columns
|
27
|
+
attr_reader :added_columns
|
28
|
+
attr_reader :changed_columns
|
29
|
+
def initialize
|
30
|
+
@removed_plugins = []
|
31
|
+
@added_plugins = []
|
32
|
+
|
33
|
+
@removed_tables = {}
|
34
|
+
@added_tables = {}
|
35
|
+
@changed_tables = {}
|
36
|
+
|
37
|
+
@removed_columns = {}
|
38
|
+
@added_columns = {}
|
39
|
+
@changed_columns = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
return false unless other.is_a?(self.class)
|
44
|
+
|
45
|
+
@removed_plugins == other.removed_plugins and
|
46
|
+
@added_plugins == other.added_plugins and
|
47
|
+
@removed_tables == other.removed_tables and
|
48
|
+
@added_tables == other.added_tables and
|
49
|
+
@changed_tables == other.changed_tables and
|
50
|
+
@removed_columns == other.removed_columns and
|
51
|
+
@added_columns == other.added_columns and
|
52
|
+
@changed_columns == other.changed_columns
|
53
|
+
end
|
54
|
+
|
55
|
+
def same?
|
56
|
+
@removed_plugins.empty? and
|
57
|
+
@added_plugins.empty? and
|
58
|
+
@removed_tables.empty? and
|
59
|
+
@added_tables.empty? and
|
60
|
+
@changed_tables.empty? and
|
61
|
+
@removed_columns.empty? and
|
62
|
+
@added_columns.empty? and
|
63
|
+
@changed_columns.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_groonga_command_list(options={})
|
67
|
+
converter = GroongaCommandListConverter.new(self, options)
|
68
|
+
converter.convert
|
69
|
+
end
|
70
|
+
|
71
|
+
class GroongaCommandListConverter
|
72
|
+
def initialize(diff, options={})
|
73
|
+
@diff = diff
|
74
|
+
@options = options
|
75
|
+
@grouped_list = []
|
76
|
+
end
|
77
|
+
|
78
|
+
def convert
|
79
|
+
@grouped_list.clear
|
80
|
+
|
81
|
+
convert_added_plugins
|
82
|
+
convert_added_tables
|
83
|
+
convert_removed_columns
|
84
|
+
convert_removed_tables
|
85
|
+
convert_removed_plugins
|
86
|
+
convert_changed_tables
|
87
|
+
|
88
|
+
meaningful_grouped_list = @grouped_list.reject do |group|
|
89
|
+
group.empty?
|
90
|
+
end
|
91
|
+
formatted_grouped_list = meaningful_grouped_list.collect do |group|
|
92
|
+
command_list = ""
|
93
|
+
group.each do |command|
|
94
|
+
command_list << "#{format_command(command)}\n"
|
95
|
+
end
|
96
|
+
command_list
|
97
|
+
end
|
98
|
+
formatted_grouped_list.join("\n")
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def convert_added_plugins
|
103
|
+
sorted_plugins = @diff.added_plugins.sort_by(&:name)
|
104
|
+
@grouped_list << sorted_plugins.collect(&:to_register_groonga_command)
|
105
|
+
end
|
106
|
+
|
107
|
+
def convert_added_tables
|
108
|
+
reference_table_names = []
|
109
|
+
no_reference_table_names = []
|
110
|
+
@diff.added_tables.each do |name, table|
|
111
|
+
if table.reference_key_type?
|
112
|
+
reference_table_names << name
|
113
|
+
else
|
114
|
+
no_reference_table_names << name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
no_reference_table_names |=
|
118
|
+
(@diff.added_columns.keys - reference_table_names)
|
119
|
+
|
120
|
+
sorted_reference_table_names = reference_table_names.sort
|
121
|
+
sorted_no_reference_table_names = no_reference_table_names.sort
|
122
|
+
|
123
|
+
sorted_table_names =
|
124
|
+
sorted_no_reference_table_names +
|
125
|
+
sorted_reference_table_names
|
126
|
+
|
127
|
+
sorted_table_names.each do |name|
|
128
|
+
group = []
|
129
|
+
table = @diff.added_tables[name]
|
130
|
+
group << table.to_create_groonga_command if table
|
131
|
+
group.concat(convert_added_columns(name, false))
|
132
|
+
@grouped_list << group
|
133
|
+
end
|
134
|
+
|
135
|
+
sorted_table_names.each do |name|
|
136
|
+
@grouped_list << convert_added_columns(name, true)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def convert_added_columns(name, target_is_reference_type)
|
141
|
+
columns = @diff.added_columns[name]
|
142
|
+
return [] if columns.nil?
|
143
|
+
|
144
|
+
sorted_columns = columns.sort_by do |column_name,|
|
145
|
+
column_name
|
146
|
+
end
|
147
|
+
|
148
|
+
group = []
|
149
|
+
sorted_columns.each do |column_name, column|
|
150
|
+
if target_is_reference_type
|
151
|
+
next unless column.reference_value_type?
|
152
|
+
else
|
153
|
+
next if column.reference_value_type?
|
154
|
+
end
|
155
|
+
group << column.to_create_groonga_command
|
156
|
+
end
|
157
|
+
group
|
158
|
+
end
|
159
|
+
|
160
|
+
def convert_removed_columns
|
161
|
+
sorted_removed_columns = @diff.removed_columns.sort_by do |table_name,|
|
162
|
+
table_name
|
163
|
+
end
|
164
|
+
|
165
|
+
column_groups = []
|
166
|
+
sorted_removed_columns.each do |table_name, columns|
|
167
|
+
group = []
|
168
|
+
columns.each do |column_name, column|
|
169
|
+
group << column unless column.sources.empty?
|
170
|
+
end
|
171
|
+
next if group.empty?
|
172
|
+
column_groups << group
|
173
|
+
end
|
174
|
+
sorted_removed_columns.each do |table_name, columns|
|
175
|
+
group = []
|
176
|
+
columns.each do |column_name, column|
|
177
|
+
group << column if column.sources.empty?
|
178
|
+
end
|
179
|
+
next if group.empty?
|
180
|
+
column_groups << group
|
181
|
+
end
|
182
|
+
|
183
|
+
column_groups.each do |columns|
|
184
|
+
sorted_columns = columns.sort_by do |column|
|
185
|
+
column.name
|
186
|
+
end
|
187
|
+
group = sorted_columns.collect do |column|
|
188
|
+
column.to_remove_groonga_command
|
189
|
+
end
|
190
|
+
@grouped_list << group
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def convert_removed_tables
|
195
|
+
sorted_tables = @diff.removed_tables.sort_by do |name, table|
|
196
|
+
[
|
197
|
+
table.reference_key_type? ? 0 : 1,
|
198
|
+
table.name,
|
199
|
+
]
|
200
|
+
end
|
201
|
+
|
202
|
+
sorted_tables.each do |name, table|
|
203
|
+
@grouped_list << [table.to_remove_groonga_command]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def convert_removed_plugins
|
208
|
+
sorted_plugins = @diff.removed_plugins.sort_by(&:name)
|
209
|
+
@grouped_list << sorted_plugins.collect(&:to_unregister_groonga_command)
|
210
|
+
end
|
211
|
+
|
212
|
+
def convert_changed_tables
|
213
|
+
sorted_tables = @diff.changed_tables.sort_by do |name, table|
|
214
|
+
[
|
215
|
+
table.reference_key_type? ? 1 : 0,
|
216
|
+
table.name,
|
217
|
+
]
|
218
|
+
end
|
219
|
+
|
220
|
+
sorted_tables.each do |name, table|
|
221
|
+
@grouped_list << table.to_migrate_start_groonga_commands
|
222
|
+
end
|
223
|
+
convert_changed_columns
|
224
|
+
sorted_tables.each do |name, table|
|
225
|
+
@grouped_list << table.to_migrate_finish_groonga_commands
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def convert_changed_columns
|
230
|
+
all_columns = []
|
231
|
+
@diff.changed_columns.each do |table_name, columns|
|
232
|
+
all_columns.concat(columns.values)
|
233
|
+
end
|
234
|
+
|
235
|
+
sorted_columns = all_columns.sort_by do |column|
|
236
|
+
[
|
237
|
+
(column.type == :index) ? 1 : 0,
|
238
|
+
column.table_name,
|
239
|
+
column.name,
|
240
|
+
]
|
241
|
+
end
|
242
|
+
sorted_columns.each do |column|
|
243
|
+
@grouped_list << column.to_migrate_start_groonga_commands
|
244
|
+
end
|
245
|
+
|
246
|
+
sorted_columns = all_columns.sort_by do |column|
|
247
|
+
[
|
248
|
+
(column.type == :index) ? 0 : 1,
|
249
|
+
column.table_name,
|
250
|
+
column.name,
|
251
|
+
]
|
252
|
+
end
|
253
|
+
sorted_columns.each do |column|
|
254
|
+
@grouped_list << column.to_migrate_finish_groonga_commands
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def format_command(command)
|
259
|
+
case @options[:format]
|
260
|
+
when :uri
|
261
|
+
command.to_uri_format
|
262
|
+
else
|
263
|
+
command.to_command_format
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# Copyright (C) 2016 Kouhei Sutou <kou@clear-code.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "groonga-schema/diff"
|
18
|
+
|
19
|
+
module GroongaSchema
|
20
|
+
class Differ
|
21
|
+
# @param from [Schema] The original schema.
|
22
|
+
# @param to [Schema] The changed schema.
|
23
|
+
def initialize(from, to)
|
24
|
+
@from = from
|
25
|
+
@to = to
|
26
|
+
end
|
27
|
+
|
28
|
+
def diff
|
29
|
+
diff = Diff.new
|
30
|
+
diff_plugins(diff)
|
31
|
+
diff_tables(diff)
|
32
|
+
diff_columns(diff)
|
33
|
+
diff
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def diff_plugins(diff)
|
38
|
+
diff.removed_plugins.concat(@from.plugins - @to.plugins)
|
39
|
+
diff.added_plugins.concat(@to.plugins - @from.plugins)
|
40
|
+
end
|
41
|
+
|
42
|
+
def diff_tables(diff)
|
43
|
+
from_tables = @from.tables
|
44
|
+
to_tables = @to.tables
|
45
|
+
|
46
|
+
from_tables.each do |name, from_table|
|
47
|
+
to_table = to_tables[name]
|
48
|
+
if to_table.nil?
|
49
|
+
diff.removed_tables[from_table.name] = from_table
|
50
|
+
elsif from_table != to_table
|
51
|
+
diff_changed_table(diff, to_table)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
to_tables.each do |name, to_table|
|
56
|
+
from_table = from_tables[name]
|
57
|
+
if from_table.nil?
|
58
|
+
diff.added_tables[name] = to_table
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def diff_changed_table(diff, to_table)
|
64
|
+
diff.changed_tables[to_table.name] = to_table
|
65
|
+
|
66
|
+
to_table.related_tables.each do |table|
|
67
|
+
diff.changed_tables[table.name] = table
|
68
|
+
end
|
69
|
+
|
70
|
+
to_table.related_columns.each do |column|
|
71
|
+
table_name = column.table_name
|
72
|
+
diff.changed_columns[table_name] ||= {}
|
73
|
+
diff.changed_columns[table_name][column.name] = column
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def diff_columns(diff)
|
78
|
+
@from.columns.each do |table_name, from_columns|
|
79
|
+
to_columns = @to.columns[table_name]
|
80
|
+
next if to_columns.nil?
|
81
|
+
|
82
|
+
from_columns.each do |name, from_column|
|
83
|
+
to_column = to_columns[name]
|
84
|
+
if to_column.nil?
|
85
|
+
diff.removed_columns[table_name] ||= {}
|
86
|
+
diff.removed_columns[table_name][name] = from_column
|
87
|
+
elsif from_column != to_column
|
88
|
+
diff_changed_column(diff, to_column)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@to.columns.each do |table_name, to_columns|
|
94
|
+
from_columns = @from.columns[table_name] || {}
|
95
|
+
to_columns.each do |name, to_column|
|
96
|
+
from_column = from_columns[name]
|
97
|
+
if from_column.nil?
|
98
|
+
diff.added_columns[table_name] ||= {}
|
99
|
+
diff.added_columns[table_name][name] = to_column
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def diff_changed_column(diff, to_column)
|
106
|
+
table_name = to_column.table_name
|
107
|
+
name = to_column.name
|
108
|
+
|
109
|
+
diff.changed_columns[table_name] ||= {}
|
110
|
+
diff.changed_columns[table_name][name] = to_column
|
111
|
+
|
112
|
+
to_column.related_columns.each do |related_column|
|
113
|
+
related_table_name = related_column.table_name
|
114
|
+
related_name = related_column.name
|
115
|
+
diff.changed_columns[related_table_name] ||= {}
|
116
|
+
diff.changed_columns[related_table_name][related_name] = related_column
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|