groonga-schema 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|