db_schema 0.2.5 → 0.3.rc1
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/.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
|