db_schema 0.2.5 → 0.3.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/README.md +2 -2
- data/lib/db_schema.rb +81 -38
- data/lib/db_schema/awesome_print.rb +37 -36
- data/lib/db_schema/changes.rb +68 -217
- data/lib/db_schema/configuration.rb +34 -15
- data/lib/db_schema/definitions.rb +7 -252
- data/lib/db_schema/definitions/check_constraint.rb +17 -0
- data/lib/db_schema/definitions/enum.rb +21 -0
- data/lib/db_schema/definitions/extension.rb +12 -0
- data/lib/db_schema/definitions/field/base.rb +6 -0
- data/lib/db_schema/definitions/foreign_key.rb +41 -0
- data/lib/db_schema/definitions/index.rb +56 -0
- data/lib/db_schema/definitions/index/column.rb +32 -0
- data/lib/db_schema/definitions/index/expression.rb +19 -0
- data/lib/db_schema/definitions/index/table_field.rb +19 -0
- data/lib/db_schema/definitions/schema.rb +36 -0
- data/lib/db_schema/definitions/table.rb +115 -0
- data/lib/db_schema/dsl.rb +96 -76
- data/lib/db_schema/dsl/migration.rb +24 -0
- data/lib/db_schema/migration.rb +12 -0
- data/lib/db_schema/migrator.rb +177 -0
- data/lib/db_schema/normalizer.rb +19 -17
- data/lib/db_schema/operations.rb +211 -0
- data/lib/db_schema/reader.rb +46 -36
- data/lib/db_schema/runner.rb +147 -171
- data/lib/db_schema/version.rb +1 -1
- metadata +18 -4
data/lib/db_schema/reader.rb
CHANGED
@@ -1,18 +1,29 @@
|
|
1
1
|
module DbSchema
|
2
2
|
module Reader
|
3
3
|
class << self
|
4
|
-
|
4
|
+
def read_schema(connection)
|
5
|
+
adapter(connection).read_schema(connection)
|
6
|
+
end
|
7
|
+
|
8
|
+
def read_table(table_name, connection)
|
9
|
+
adapter(connection).read_table(table_name, connection)
|
10
|
+
end
|
11
|
+
|
12
|
+
def read_enums(connection)
|
13
|
+
adapter(connection).read_enums(connection)
|
14
|
+
end
|
5
15
|
|
6
|
-
|
16
|
+
def read_extensions(connection)
|
17
|
+
adapter(connection).read_extensions(connection)
|
18
|
+
end
|
7
19
|
|
8
|
-
|
9
|
-
|
10
|
-
registry.fetch(
|
20
|
+
private
|
21
|
+
def adapter(connection)
|
22
|
+
registry.fetch(connection.adapter_scheme) do |adapter_name|
|
11
23
|
raise NotImplementedError, "DbSchema::Reader does not support #{adapter_name}."
|
12
24
|
end
|
13
25
|
end
|
14
26
|
|
15
|
-
private
|
16
27
|
def registry
|
17
28
|
@registry ||= {}
|
18
29
|
end
|
@@ -116,30 +127,30 @@ SELECT extname
|
|
116
127
|
SQL
|
117
128
|
|
118
129
|
class << self
|
119
|
-
def read_schema
|
130
|
+
def read_schema(connection)
|
120
131
|
Definitions::Schema.new(
|
121
|
-
tables: read_tables,
|
122
|
-
enums: read_enums,
|
123
|
-
extensions: read_extensions
|
132
|
+
tables: read_tables(connection),
|
133
|
+
enums: read_enums(connection),
|
134
|
+
extensions: read_extensions(connection)
|
124
135
|
)
|
125
136
|
end
|
126
137
|
|
127
|
-
def read_table(table_name)
|
128
|
-
primary_key_name =
|
138
|
+
def read_table(table_name, connection)
|
139
|
+
primary_key_name = connection.primary_key(table_name)
|
129
140
|
|
130
|
-
fields =
|
141
|
+
fields = connection[COLUMN_NAMES_QUERY, table_name.to_s].map do |column_data|
|
131
142
|
build_field(column_data, primary_key: column_data[:name] == primary_key_name)
|
132
143
|
end
|
133
144
|
|
134
|
-
indices = indices_data_for(table_name).map do |index_data|
|
145
|
+
indices = indices_data_for(table_name, connection).map do |index_data|
|
135
146
|
Definitions::Index.new(index_data)
|
136
147
|
end.sort_by(&:name)
|
137
148
|
|
138
|
-
foreign_keys =
|
139
|
-
build_foreign_key(foreign_key_data)
|
149
|
+
foreign_keys = connection.foreign_key_list(table_name).map do |foreign_key_data|
|
150
|
+
build_foreign_key(foreign_key_data, connection)
|
140
151
|
end
|
141
152
|
|
142
|
-
checks =
|
153
|
+
checks = connection[CONSTRAINTS_QUERY, table_name.to_s].map do |check_data|
|
143
154
|
Definitions::CheckConstraint.new(
|
144
155
|
name: check_data[:name].to_sym,
|
145
156
|
condition: check_data[:condition]
|
@@ -155,13 +166,13 @@ SELECT extname
|
|
155
166
|
)
|
156
167
|
end
|
157
168
|
|
158
|
-
def indices_data_for(table_name)
|
159
|
-
column_names =
|
169
|
+
def indices_data_for(table_name, connection)
|
170
|
+
column_names = connection[COLUMN_NAMES_QUERY, table_name.to_s].reduce({}) do |names, column|
|
160
171
|
names.merge(column[:pos] => column[:name].to_sym)
|
161
172
|
end
|
162
173
|
|
163
|
-
indices_data =
|
164
|
-
expressions_data = index_expressions_data(indices_data)
|
174
|
+
indices_data = connection[INDICES_QUERY, table_name.to_s].to_a
|
175
|
+
expressions_data = index_expressions_data(indices_data, connection)
|
165
176
|
|
166
177
|
indices_data.map do |index|
|
167
178
|
positions = index[:column_positions].split(' ').map(&:to_i)
|
@@ -197,26 +208,26 @@ SELECT extname
|
|
197
208
|
end
|
198
209
|
end
|
199
210
|
|
200
|
-
def read_tables
|
201
|
-
|
202
|
-
read_table(table_name)
|
211
|
+
def read_tables(connection)
|
212
|
+
connection.tables.map do |table_name|
|
213
|
+
read_table(table_name, connection)
|
203
214
|
end
|
204
215
|
end
|
205
216
|
|
206
|
-
def read_enums
|
207
|
-
|
217
|
+
def read_enums(connection)
|
218
|
+
connection[ENUMS_QUERY].map do |enum_data|
|
208
219
|
Definitions::Enum.new(enum_data[:name].to_sym, enum_data[:values].map(&:to_sym))
|
209
220
|
end
|
210
221
|
end
|
211
222
|
|
212
|
-
def read_extensions
|
213
|
-
|
223
|
+
def read_extensions(connection)
|
224
|
+
connection[EXTENSIONS_QUERY].map do |extension_data|
|
214
225
|
Definitions::Extension.new(extension_data[:extname].to_sym)
|
215
226
|
end
|
216
227
|
end
|
217
228
|
|
218
229
|
private
|
219
|
-
def index_expressions_data(indices_data)
|
230
|
+
def index_expressions_data(indices_data, connection)
|
220
231
|
all_positions, max_position = {}, 0
|
221
232
|
|
222
233
|
indices_data.each do |index_data|
|
@@ -230,7 +241,7 @@ SELECT extname
|
|
230
241
|
end
|
231
242
|
|
232
243
|
if all_positions.any?
|
233
|
-
|
244
|
+
connection[
|
234
245
|
EXPRESSION_INDICES_QUERY,
|
235
246
|
Sequel.pg_array(all_positions.keys),
|
236
247
|
Sequel.pg_array((1..max_position.succ).to_a)
|
@@ -318,8 +329,8 @@ SELECT extname
|
|
318
329
|
)
|
319
330
|
end
|
320
331
|
|
321
|
-
def build_foreign_key(data)
|
322
|
-
keys = if data[:key] == [primary_key_for(data[:table])]
|
332
|
+
def build_foreign_key(data, connection)
|
333
|
+
keys = if data[:key] == [primary_key_for(data[:table], connection)]
|
323
334
|
[] # this foreign key references a primary key
|
324
335
|
else
|
325
336
|
data[:key]
|
@@ -336,15 +347,14 @@ SELECT extname
|
|
336
347
|
)
|
337
348
|
end
|
338
349
|
|
339
|
-
def primary_key_for(table_name)
|
340
|
-
if pkey =
|
350
|
+
def primary_key_for(table_name, connection)
|
351
|
+
if pkey = connection.primary_key(table_name)
|
341
352
|
pkey.to_sym
|
342
353
|
end
|
343
354
|
end
|
344
355
|
end
|
345
356
|
end
|
346
357
|
|
347
|
-
registry[
|
348
|
-
registry['postgresql'] = Postgres
|
358
|
+
registry[:postgres] = Postgres
|
349
359
|
end
|
350
360
|
end
|
data/lib/db_schema/runner.rb
CHANGED
@@ -1,213 +1,189 @@
|
|
1
1
|
module DbSchema
|
2
2
|
class Runner
|
3
|
-
attr_reader :changes
|
3
|
+
attr_reader :changes, :connection
|
4
4
|
|
5
|
-
def initialize(changes)
|
6
|
-
@changes
|
5
|
+
def initialize(changes, connection)
|
6
|
+
@changes = changes
|
7
|
+
@connection = connection
|
7
8
|
end
|
8
9
|
|
9
10
|
def run!
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
11
|
+
changes.each do |change|
|
12
|
+
case change
|
13
|
+
when Operations::CreateTable
|
14
|
+
create_table(change)
|
15
|
+
when Operations::DropTable
|
16
|
+
drop_table(change)
|
17
|
+
when Operations::RenameTable
|
18
|
+
rename_table(change)
|
19
|
+
when Operations::AlterTable
|
20
|
+
alter_table(change)
|
21
|
+
when Operations::CreateForeignKey
|
22
|
+
create_foreign_key(change)
|
23
|
+
when Operations::DropForeignKey
|
24
|
+
drop_foreign_key(change)
|
25
|
+
when Operations::CreateEnum
|
26
|
+
create_enum(change)
|
27
|
+
when Operations::DropEnum
|
28
|
+
drop_enum(change)
|
29
|
+
when Operations::AlterEnumValues
|
30
|
+
alter_enum_values(change)
|
31
|
+
when Operations::CreateExtension
|
32
|
+
create_extension(change)
|
33
|
+
when Operations::DropExtension
|
34
|
+
drop_extension(change)
|
35
|
+
when Operations::ExecuteQuery
|
36
|
+
execute_query(change)
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
41
|
private
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
Changes::CreateTable,
|
48
|
-
Changes::AlterTable,
|
49
|
-
Changes::DropTable,
|
50
|
-
Changes::DropEnum,
|
51
|
-
Changes::CreateForeignKey,
|
52
|
-
Changes::DropExtension
|
53
|
-
]
|
54
|
-
)
|
55
|
-
end
|
56
|
-
|
57
|
-
class << self
|
58
|
-
def create_table(change)
|
59
|
-
DbSchema.connection.create_table(change.table.name) do
|
60
|
-
change.table.fields.each do |field|
|
61
|
-
if field.primary_key?
|
62
|
-
primary_key(field.name)
|
63
|
-
else
|
64
|
-
options = Runner.map_options(field.class.type, field.options)
|
65
|
-
column(field.name, field.type.capitalize, options)
|
66
|
-
end
|
42
|
+
def create_table(change)
|
43
|
+
connection.create_table(change.table.name) do
|
44
|
+
change.table.fields.each do |field|
|
45
|
+
if field.primary_key?
|
46
|
+
primary_key(field.name)
|
47
|
+
else
|
48
|
+
options = Runner.map_options(field.class.type, field.options)
|
49
|
+
column(field.name, field.type.capitalize, options)
|
67
50
|
end
|
51
|
+
end
|
68
52
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
53
|
+
change.table.indices.each do |index|
|
54
|
+
index(
|
55
|
+
index.columns_to_sequel,
|
56
|
+
name: index.name,
|
57
|
+
unique: index.unique?,
|
58
|
+
type: index.type,
|
59
|
+
where: index.condition
|
60
|
+
)
|
61
|
+
end
|
78
62
|
|
79
|
-
|
80
|
-
|
81
|
-
end
|
63
|
+
change.table.checks.each do |check|
|
64
|
+
constraint(check.name, check.condition)
|
82
65
|
end
|
83
66
|
end
|
67
|
+
end
|
84
68
|
|
85
|
-
|
86
|
-
|
87
|
-
|
69
|
+
def drop_table(change)
|
70
|
+
connection.drop_table(change.name)
|
71
|
+
end
|
88
72
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
DbSchema::Changes::CreateColumn,
|
104
|
-
DbSchema::Changes::CreateIndex,
|
105
|
-
DbSchema::Changes::CreateCheckConstraint,
|
106
|
-
DbSchema::Changes::CreatePrimaryKey
|
107
|
-
]
|
108
|
-
).each do |element|
|
109
|
-
case element
|
110
|
-
when Changes::CreateColumn
|
111
|
-
if element.primary_key?
|
112
|
-
add_primary_key(element.name)
|
113
|
-
else
|
114
|
-
options = Runner.map_options(element.type, element.options)
|
115
|
-
add_column(element.name, element.type.capitalize, options)
|
116
|
-
end
|
117
|
-
when Changes::DropColumn
|
118
|
-
drop_column(element.name)
|
119
|
-
when Changes::RenameColumn
|
120
|
-
rename_column(element.old_name, element.new_name)
|
121
|
-
when Changes::AlterColumnType
|
122
|
-
attributes = Runner.map_options(element.new_type, element.new_attributes)
|
123
|
-
set_column_type(element.name, element.new_type.capitalize, attributes)
|
124
|
-
when Changes::CreatePrimaryKey
|
125
|
-
raise NotImplementedError, 'Converting an existing column to primary key is currently unsupported'
|
126
|
-
when Changes::DropPrimaryKey
|
127
|
-
raise NotImplementedError, 'Removing a primary key while leaving the column is currently unsupported'
|
128
|
-
when Changes::AllowNull
|
129
|
-
set_column_allow_null(element.name)
|
130
|
-
when Changes::DisallowNull
|
131
|
-
set_column_not_null(element.name)
|
132
|
-
when Changes::AlterColumnDefault
|
133
|
-
set_column_default(element.name, Runner.default_to_sequel(element.new_default))
|
134
|
-
when Changes::CreateIndex
|
135
|
-
add_index(
|
136
|
-
element.index.columns_to_sequel,
|
137
|
-
name: element.index.name,
|
138
|
-
unique: element.index.unique?,
|
139
|
-
type: element.index.type,
|
140
|
-
where: element.index.condition
|
141
|
-
)
|
142
|
-
when Changes::DropIndex
|
143
|
-
drop_index([], name: element.name)
|
144
|
-
when Changes::CreateCheckConstraint
|
145
|
-
add_constraint(element.check.name, element.check.condition)
|
146
|
-
when Changes::DropCheckConstraint
|
147
|
-
drop_constraint(element.name)
|
73
|
+
def rename_table(change)
|
74
|
+
connection.rename_table(change.old_name, change.new_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def alter_table(change)
|
78
|
+
connection.alter_table(change.table_name) do
|
79
|
+
change.changes.each do |element|
|
80
|
+
case element
|
81
|
+
when Operations::CreateColumn
|
82
|
+
if element.primary_key?
|
83
|
+
add_primary_key(element.name)
|
84
|
+
else
|
85
|
+
options = Runner.map_options(element.type, element.options)
|
86
|
+
add_column(element.name, element.type.capitalize, options)
|
148
87
|
end
|
88
|
+
when Operations::DropColumn
|
89
|
+
drop_column(element.name)
|
90
|
+
when Operations::RenameColumn
|
91
|
+
rename_column(element.old_name, element.new_name)
|
92
|
+
when Operations::AlterColumnType
|
93
|
+
attributes = Runner.map_options(element.new_type, element.new_attributes)
|
94
|
+
set_column_type(element.name, element.new_type.capitalize, using: element.using, **attributes)
|
95
|
+
when Operations::CreatePrimaryKey
|
96
|
+
raise NotImplementedError, 'Converting an existing column to primary key is currently unsupported'
|
97
|
+
when Operations::DropPrimaryKey
|
98
|
+
raise NotImplementedError, 'Removing a primary key while leaving the column is currently unsupported'
|
99
|
+
when Operations::AllowNull
|
100
|
+
set_column_allow_null(element.name)
|
101
|
+
when Operations::DisallowNull
|
102
|
+
set_column_not_null(element.name)
|
103
|
+
when Operations::AlterColumnDefault
|
104
|
+
set_column_default(element.name, Runner.default_to_sequel(element.new_default))
|
105
|
+
when Operations::CreateIndex
|
106
|
+
add_index(
|
107
|
+
element.index.columns_to_sequel,
|
108
|
+
name: element.index.name,
|
109
|
+
unique: element.index.unique?,
|
110
|
+
type: element.index.type,
|
111
|
+
where: element.index.condition
|
112
|
+
)
|
113
|
+
when Operations::DropIndex
|
114
|
+
drop_index([], name: element.name)
|
115
|
+
when Operations::CreateCheckConstraint
|
116
|
+
add_constraint(element.check.name, element.check.condition)
|
117
|
+
when Operations::DropCheckConstraint
|
118
|
+
drop_constraint(element.name)
|
149
119
|
end
|
150
120
|
end
|
151
121
|
end
|
122
|
+
end
|
152
123
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
end
|
124
|
+
def create_foreign_key(change)
|
125
|
+
connection.alter_table(change.table_name) do
|
126
|
+
add_foreign_key(change.foreign_key.fields, change.foreign_key.table, change.foreign_key.options)
|
157
127
|
end
|
128
|
+
end
|
158
129
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end
|
130
|
+
def drop_foreign_key(change)
|
131
|
+
connection.alter_table(change.table_name) do
|
132
|
+
drop_foreign_key([], name: change.fkey_name)
|
163
133
|
end
|
134
|
+
end
|
164
135
|
|
165
|
-
|
166
|
-
|
167
|
-
|
136
|
+
def create_enum(change)
|
137
|
+
connection.create_enum(change.enum.name, change.enum.values)
|
138
|
+
end
|
168
139
|
|
169
|
-
|
170
|
-
|
171
|
-
|
140
|
+
def drop_enum(change)
|
141
|
+
connection.drop_enum(change.name)
|
142
|
+
end
|
172
143
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
144
|
+
def alter_enum_values(change)
|
145
|
+
change.enum_fields.each do |field_data|
|
146
|
+
connection.alter_table(field_data[:table_name]) do
|
147
|
+
set_column_type(field_data[:field_name], :VARCHAR)
|
148
|
+
set_column_default(field_data[:field_name], nil)
|
179
149
|
end
|
150
|
+
end
|
180
151
|
|
181
|
-
|
182
|
-
|
152
|
+
connection.drop_enum(change.enum_name)
|
153
|
+
connection.create_enum(change.enum_name, change.new_values)
|
183
154
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
155
|
+
change.enum_fields.each do |field_data|
|
156
|
+
connection.alter_table(field_data[:table_name]) do
|
157
|
+
field_type = if field_data[:array]
|
158
|
+
"#{change.enum_name}[]"
|
159
|
+
else
|
160
|
+
change.enum_name
|
161
|
+
end
|
191
162
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
163
|
+
set_column_type(
|
164
|
+
field_data[:field_name],
|
165
|
+
field_type,
|
166
|
+
using: "#{field_data[:field_name]}::#{field_type}"
|
167
|
+
)
|
197
168
|
|
198
|
-
|
199
|
-
end
|
169
|
+
set_column_default(field_data[:field_name], field_data[:new_default]) unless field_data[:new_default].nil?
|
200
170
|
end
|
201
171
|
end
|
172
|
+
end
|
202
173
|
|
203
|
-
|
204
|
-
|
205
|
-
|
174
|
+
def create_extension(change)
|
175
|
+
connection.run(%Q(CREATE EXTENSION "#{change.extension.name}"))
|
176
|
+
end
|
206
177
|
|
207
|
-
|
208
|
-
|
209
|
-
|
178
|
+
def drop_extension(change)
|
179
|
+
connection.run(%Q(DROP EXTENSION "#{change.name}"))
|
180
|
+
end
|
210
181
|
|
182
|
+
def execute_query(change)
|
183
|
+
connection.run(change.query)
|
184
|
+
end
|
185
|
+
|
186
|
+
class << self
|
211
187
|
def map_options(type, options)
|
212
188
|
mapping = case type
|
213
189
|
when :char, :varchar, :bit, :varbit
|