db_schema 0.4.1 → 0.5.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 +4 -3
- data/Gemfile +3 -0
- data/README.md +2 -2
- data/db_schema.gemspec +2 -2
- data/lib/db_schema/awesome_print.rb +10 -139
- data/lib/db_schema/changes.rb +4 -13
- data/lib/db_schema/dsl.rb +36 -6
- data/lib/db_schema/migrator.rb +12 -1
- data/lib/db_schema/normalizer.rb +99 -110
- data/lib/db_schema/operations.rb +36 -12
- data/lib/db_schema/runner.rb +39 -35
- data/lib/db_schema/validator.rb +2 -2
- data/lib/db_schema/version.rb +1 -1
- data/lib/db_schema.rb +2 -3
- metadata +13 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1609672a54947a93c2953044d09ae26e4e5653105ca76455364e0746d0157372
|
4
|
+
data.tar.gz: 00c069c16b8f8414cacb529e2857c9cbd785e1534786282b6d89bfa966c31cde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22568b1fd6eeda2a067aec451d9c5e8a2e6ad88fa5cc5801bd4d90a9016394baa00e0b821dcb714d6ec8291462f07d3aebdd68525ad4b37b4b1b323cd641e6b7
|
7
|
+
data.tar.gz: d1ad3e5f0d74706bf1aefb41004d8b08cfd9e1d4e61db3a13fde05bdb18c707d52c277f3210fbd0d89f3453a4c9369fca02264ff9b1ed1a94d6aa1a4db5de07a
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
+
gem 'db_schema-definitions', github: 'db-schema/definitions', branch: 'primary_keys'
|
4
|
+
gem 'db_schema-reader-postgres', github: 'db-schema/reader-postgres', branch: 'primary_keys'
|
5
|
+
|
3
6
|
# Specify your gem's dependencies in db_schema.gemspec
|
4
7
|
gemspec
|
data/README.md
CHANGED
@@ -53,8 +53,8 @@ But you would lose it even with manual migrations.
|
|
53
53
|
Add these lines to your application's Gemfile:
|
54
54
|
|
55
55
|
``` ruby
|
56
|
-
gem 'db_schema', '
|
57
|
-
gem 'db_schema-reader-postgres', '
|
56
|
+
gem 'db_schema', '= 0.5.rc1'
|
57
|
+
gem 'db_schema-reader-postgres', '= 0.2.rc1'
|
58
58
|
```
|
59
59
|
|
60
60
|
And then execute:
|
data/db_schema.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_runtime_dependency 'sequel'
|
22
22
|
spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
|
23
|
-
spec.add_runtime_dependency 'db_schema-definitions', '
|
23
|
+
spec.add_runtime_dependency 'db_schema-definitions', '= 0.2.rc1'
|
24
24
|
|
25
25
|
spec.add_development_dependency 'bundler', '~> 1.11'
|
26
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
@@ -32,5 +32,5 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency 'terminal-notifier'
|
33
33
|
spec.add_development_dependency 'terminal-notifier-guard'
|
34
34
|
|
35
|
-
spec.add_development_dependency 'db_schema-reader-postgres', '
|
35
|
+
spec.add_development_dependency 'db_schema-reader-postgres', '= 0.2.rc1'
|
36
36
|
end
|
@@ -13,33 +13,6 @@ if defined?(AwesomePrint)
|
|
13
13
|
|
14
14
|
def cast_with_dbschema(object, type)
|
15
15
|
case object
|
16
|
-
when ::DbSchema::Definitions::Schema
|
17
|
-
:dbschema_schema
|
18
|
-
when ::DbSchema::Definitions::NullTable,
|
19
|
-
::DbSchema::Definitions::NullField,
|
20
|
-
::DbSchema::Definitions::NullIndex,
|
21
|
-
::DbSchema::Definitions::NullCheckConstraint,
|
22
|
-
::DbSchema::Definitions::NullForeignKey,
|
23
|
-
::DbSchema::Definitions::NullEnum
|
24
|
-
:dbschema_null_object
|
25
|
-
when ::DbSchema::Definitions::Table
|
26
|
-
:dbschema_table
|
27
|
-
when ::DbSchema::Definitions::Field::Custom
|
28
|
-
:dbschema_custom_field
|
29
|
-
when ::DbSchema::Definitions::Field::Base
|
30
|
-
:dbschema_field
|
31
|
-
when ::DbSchema::Definitions::Index
|
32
|
-
:dbschema_index
|
33
|
-
when ::DbSchema::Definitions::Index::Column
|
34
|
-
:dbschema_index_column
|
35
|
-
when ::DbSchema::Definitions::CheckConstraint
|
36
|
-
:dbschema_check_constraint
|
37
|
-
when ::DbSchema::Definitions::ForeignKey
|
38
|
-
:dbschema_foreign_key
|
39
|
-
when ::DbSchema::Definitions::Enum
|
40
|
-
:dbschema_enum
|
41
|
-
when ::DbSchema::Definitions::Extension
|
42
|
-
:dbschema_column_operation
|
43
16
|
when ::DbSchema::Operations::CreateTable
|
44
17
|
:dbschema_create_table
|
45
18
|
when ::DbSchema::Operations::DropTable
|
@@ -58,6 +31,8 @@ if defined?(AwesomePrint)
|
|
58
31
|
:dbschema_alter_column_default
|
59
32
|
when ::DbSchema::Operations::CreateIndex
|
60
33
|
:dbschema_create_index
|
34
|
+
when ::DbSchema::Operations::DropIndex
|
35
|
+
:dbschema_drop_index
|
61
36
|
when ::DbSchema::Operations::CreateCheckConstraint
|
62
37
|
:dbschema_create_check_constraint
|
63
38
|
when ::DbSchema::Operations::CreateForeignKey
|
@@ -76,118 +51,6 @@ if defined?(AwesomePrint)
|
|
76
51
|
end
|
77
52
|
|
78
53
|
private
|
79
|
-
def awesome_dbschema_schema(object)
|
80
|
-
data = ["tables: #{object.tables.ai}"]
|
81
|
-
data << "enums: #{object.enums.ai}" if object.enums.any?
|
82
|
-
data << "extensions: #{object.extensions.ai}" if object.extensions.any?
|
83
|
-
|
84
|
-
data_string = data.join(', ')
|
85
|
-
"#<DbSchema::Definitions::Schema #{data_string}>"
|
86
|
-
end
|
87
|
-
|
88
|
-
def awesome_dbschema_table(object)
|
89
|
-
data = ["fields: #{object.fields.ai}"]
|
90
|
-
data << "indexes: #{object.indexes.ai}" if object.indexes.any?
|
91
|
-
data << "checks: #{object.checks.ai}" if object.checks.any?
|
92
|
-
data << "foreign_keys: #{object.foreign_keys.ai}" if object.foreign_keys.any?
|
93
|
-
|
94
|
-
data_string = indent_lines(data.join(', '))
|
95
|
-
"#<DbSchema::Definitions::Table #{object.name.ai} #{data_string}>"
|
96
|
-
end
|
97
|
-
|
98
|
-
def awesome_dbschema_null_object(object)
|
99
|
-
"#<#{object.class}>"
|
100
|
-
end
|
101
|
-
|
102
|
-
def awesome_dbschema_field(object)
|
103
|
-
options = object.options.map do |k, v|
|
104
|
-
key = colorize("#{k}:", :symbol)
|
105
|
-
|
106
|
-
if (k == :default) && v.is_a?(Symbol)
|
107
|
-
"#{key} #{colorize(v.to_s, :string)}"
|
108
|
-
else
|
109
|
-
"#{key} #{v.ai}"
|
110
|
-
end
|
111
|
-
end.unshift(nil).join(', ')
|
112
|
-
|
113
|
-
primary_key = if object.primary_key?
|
114
|
-
', ' + colorize('primary key', :nilclass)
|
115
|
-
else
|
116
|
-
''
|
117
|
-
end
|
118
|
-
|
119
|
-
"#<#{object.class} #{object.name.ai}#{options}#{primary_key}>"
|
120
|
-
end
|
121
|
-
|
122
|
-
def awesome_dbschema_custom_field(object)
|
123
|
-
options = object.options.map do |k, v|
|
124
|
-
key = colorize("#{k}:", :symbol)
|
125
|
-
|
126
|
-
if (k == :default) && v.is_a?(Symbol)
|
127
|
-
"#{key} #{colorize(v.to_s, :string)}"
|
128
|
-
else
|
129
|
-
"#{key} #{v.ai}"
|
130
|
-
end
|
131
|
-
end.unshift(nil).join(', ')
|
132
|
-
|
133
|
-
primary_key = if object.primary_key?
|
134
|
-
', ' + colorize('primary key', :nilclass)
|
135
|
-
else
|
136
|
-
''
|
137
|
-
end
|
138
|
-
|
139
|
-
"#<DbSchema::Definitions::Field::Custom (#{object.type.ai}) #{object.name.ai}#{options}#{primary_key}>"
|
140
|
-
end
|
141
|
-
|
142
|
-
def awesome_dbschema_index(object)
|
143
|
-
columns = format_dbschema_fields(object.columns)
|
144
|
-
using = ' using ' + colorize(object.type.to_s, :symbol) unless object.btree?
|
145
|
-
|
146
|
-
data = [nil]
|
147
|
-
data << colorize('unique', :nilclass) if object.unique?
|
148
|
-
data << colorize('condition: ', :symbol) + object.condition.ai unless object.condition.nil?
|
149
|
-
|
150
|
-
"#<#{object.class} #{object.name.ai} on #{columns}#{using}#{data.join(', ')}>"
|
151
|
-
end
|
152
|
-
|
153
|
-
def awesome_dbschema_index_column(object)
|
154
|
-
data = [object.name.ai]
|
155
|
-
|
156
|
-
if object.desc?
|
157
|
-
data << colorize('desc', :nilclass)
|
158
|
-
data << colorize('nulls last', :symbol) if object.nulls == :last
|
159
|
-
else
|
160
|
-
data << colorize('nulls first', :symbol) if object.nulls == :first
|
161
|
-
end
|
162
|
-
|
163
|
-
data.join(' ')
|
164
|
-
end
|
165
|
-
|
166
|
-
def awesome_dbschema_check_constraint(object)
|
167
|
-
"#<#{object.class} #{object.name.ai} #{object.condition.ai}>"
|
168
|
-
end
|
169
|
-
|
170
|
-
def awesome_dbschema_foreign_key(object)
|
171
|
-
fields = format_dbschema_fields(object.fields)
|
172
|
-
references = "#{colorize('references', :class)} #{object.table.ai}"
|
173
|
-
references << ' ' + format_dbschema_fields(object.keys) unless object.references_primary_key?
|
174
|
-
|
175
|
-
data = [nil]
|
176
|
-
data << colorize("on_update:", :symbol) + " #{object.on_update.ai}" unless object.on_update == :no_action
|
177
|
-
data << colorize("on_delete:", :symbol) + " #{object.on_delete.ai}" unless object.on_delete == :no_action
|
178
|
-
data << colorize('deferrable', :nilclass) if object.deferrable?
|
179
|
-
|
180
|
-
"#<#{object.class} #{object.name.ai} on #{fields} #{references}#{data.join(', ')}>"
|
181
|
-
end
|
182
|
-
|
183
|
-
def awesome_dbschema_enum(object)
|
184
|
-
values = object.values.map do |value|
|
185
|
-
colorize(value.to_s, :string)
|
186
|
-
end.join(', ')
|
187
|
-
|
188
|
-
"#<#{object.class} #{object.name.ai} (#{values})>"
|
189
|
-
end
|
190
|
-
|
191
54
|
def awesome_dbschema_create_table(object)
|
192
55
|
data = ["fields: #{object.table.fields.ai}"]
|
193
56
|
data << "indexes: #{object.table.indexes.ai}" if object.table.indexes.any?
|
@@ -241,12 +104,20 @@ if defined?(AwesomePrint)
|
|
241
104
|
using = ' using ' + colorize(object.index.type.to_s, :symbol) unless object.index.btree?
|
242
105
|
|
243
106
|
data = [nil]
|
107
|
+
data << colorize('primary key', :nilclass) if object.index.primary?
|
244
108
|
data << colorize('unique', :nilclass) if object.index.unique?
|
245
109
|
data << colorize('condition: ', :symbol) + object.index.condition.ai unless object.index.condition.nil?
|
246
110
|
|
247
111
|
"#<#{object.class} #{object.index.name.ai} on #{columns}#{using}#{data.join(', ')}>"
|
248
112
|
end
|
249
113
|
|
114
|
+
def awesome_dbschema_drop_index(object)
|
115
|
+
data = [object.name.ai]
|
116
|
+
data << colorize('primary key', :nilclass) if object.primary?
|
117
|
+
|
118
|
+
"#<#{object.class} #{data.join(' ')}>"
|
119
|
+
end
|
120
|
+
|
250
121
|
def awesome_dbschema_create_check_constraint(object)
|
251
122
|
"#<#{object.class} #{object.check.name.ai} #{object.check.condition.ai}>"
|
252
123
|
end
|
data/lib/db_schema/changes.rb
CHANGED
@@ -67,19 +67,12 @@ module DbSchema
|
|
67
67
|
if (actual.type != desired.type) || (actual.attributes != desired.attributes)
|
68
68
|
operations << Operations::AlterColumnType.new(
|
69
69
|
actual.name,
|
70
|
+
old_type: actual.type,
|
70
71
|
new_type: desired.type,
|
71
72
|
**desired.attributes
|
72
73
|
)
|
73
74
|
end
|
74
75
|
|
75
|
-
if desired.primary_key? && !actual.primary_key?
|
76
|
-
operations << Operations::CreatePrimaryKey.new(actual.name)
|
77
|
-
end
|
78
|
-
|
79
|
-
if actual.primary_key? && !desired.primary_key?
|
80
|
-
operations << Operations::DropPrimaryKey.new(actual.name)
|
81
|
-
end
|
82
|
-
|
83
76
|
if desired.null? && !actual.null?
|
84
77
|
operations << Operations::AllowNull.new(actual.name)
|
85
78
|
end
|
@@ -101,10 +94,10 @@ module DbSchema
|
|
101
94
|
desired_table.indexes,
|
102
95
|
actual_table.indexes,
|
103
96
|
create: -> (index) { Operations::CreateIndex.new(index) },
|
104
|
-
drop: -> (index) { Operations::DropIndex.new(index.name) },
|
97
|
+
drop: -> (index) { Operations::DropIndex.new(index.name, index.primary?) },
|
105
98
|
change: -> (desired, actual) do
|
106
99
|
[
|
107
|
-
Operations::DropIndex.new(actual.name),
|
100
|
+
Operations::DropIndex.new(actual.name, actual.primary?),
|
108
101
|
Operations::CreateIndex.new(desired)
|
109
102
|
]
|
110
103
|
end
|
@@ -220,7 +213,6 @@ module DbSchema
|
|
220
213
|
Utils.sort_by_class(
|
221
214
|
changes,
|
222
215
|
[
|
223
|
-
Operations::DropPrimaryKey,
|
224
216
|
Operations::DropCheckConstraint,
|
225
217
|
Operations::DropIndex,
|
226
218
|
Operations::DropColumn,
|
@@ -230,8 +222,7 @@ module DbSchema
|
|
230
222
|
Operations::AlterColumnDefault,
|
231
223
|
Operations::CreateColumn,
|
232
224
|
Operations::CreateIndex,
|
233
|
-
Operations::CreateCheckConstraint
|
234
|
-
Operations::CreatePrimaryKey
|
225
|
+
Operations::CreateCheckConstraint
|
235
226
|
]
|
236
227
|
)
|
237
228
|
end
|
data/lib/db_schema/dsl.rb
CHANGED
@@ -16,7 +16,7 @@ module DbSchema
|
|
16
16
|
|
17
17
|
@schema.tables << Definitions::Table.new(
|
18
18
|
name,
|
19
|
-
fields: table_yielder
|
19
|
+
fields: prepare_fields(table_yielder),
|
20
20
|
indexes: table_yielder.indexes,
|
21
21
|
checks: table_yielder.checks,
|
22
22
|
foreign_keys: table_yielder.foreign_keys
|
@@ -35,6 +35,20 @@ module DbSchema
|
|
35
35
|
migrations << Migration.new(name, block).migration
|
36
36
|
end
|
37
37
|
|
38
|
+
private
|
39
|
+
def prepare_fields(table_yielder)
|
40
|
+
primary_key = table_yielder.indexes.find(&:primary?)
|
41
|
+
return table_yielder.fields if primary_key.nil?
|
42
|
+
|
43
|
+
table_yielder.fields.map do |field|
|
44
|
+
if primary_key.columns.map(&:name).include?(field.name)
|
45
|
+
field.with_null(false)
|
46
|
+
else
|
47
|
+
field
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
38
52
|
class TableYielder
|
39
53
|
attr_reader :table_name
|
40
54
|
|
@@ -55,12 +69,19 @@ module DbSchema
|
|
55
69
|
field(name, :array, element_type: of, **options)
|
56
70
|
end
|
57
71
|
|
72
|
+
%i(smallserial serial bigserial).each do |serial_type|
|
73
|
+
define_method(serial_type) do |name, **options|
|
74
|
+
allowed_options = Utils.filter_by_keys(options, :primary_key, :unique, :index, :references, :check)
|
75
|
+
field(name, serial_type, null: false, **allowed_options)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
58
79
|
def method_missing(method_name, name, *args, &block)
|
59
80
|
field(name, method_name, args.first || {})
|
60
81
|
end
|
61
82
|
|
62
|
-
def primary_key(name)
|
63
|
-
|
83
|
+
def primary_key(*columns, name: nil)
|
84
|
+
index(*columns, name: name, primary: true, unique: true)
|
64
85
|
end
|
65
86
|
|
66
87
|
def index(*columns, **index_options)
|
@@ -83,9 +104,13 @@ module DbSchema
|
|
83
104
|
)
|
84
105
|
end
|
85
106
|
|
86
|
-
def field(name, type, unique: false, index: false, references: nil, check: nil, **options)
|
107
|
+
def field(name, type, primary_key: false, unique: false, index: false, references: nil, check: nil, **options)
|
87
108
|
fields << Definitions::Field.build(name, type, options)
|
88
109
|
|
110
|
+
if primary_key
|
111
|
+
primary_key(name)
|
112
|
+
end
|
113
|
+
|
89
114
|
if unique
|
90
115
|
index(name, unique: true)
|
91
116
|
elsif index
|
@@ -118,7 +143,7 @@ module DbSchema
|
|
118
143
|
end
|
119
144
|
|
120
145
|
class << self
|
121
|
-
def build_index(columns, table_name:, name: nil, unique: false, using: :btree, where: nil, **ordered_fields)
|
146
|
+
def build_index(columns, table_name:, name: nil, primary: false, unique: false, using: :btree, where: nil, **ordered_fields)
|
122
147
|
if columns.last.is_a?(Hash)
|
123
148
|
*ascending_columns, ordered_expressions = columns
|
124
149
|
else
|
@@ -151,11 +176,16 @@ module DbSchema
|
|
151
176
|
end
|
152
177
|
end
|
153
178
|
|
154
|
-
index_name = name ||
|
179
|
+
index_name = name || if primary
|
180
|
+
"#{table_name}_pkey"
|
181
|
+
else
|
182
|
+
"#{table_name}_#{index_columns.map(&:index_name_segment).join('_')}_index"
|
183
|
+
end
|
155
184
|
|
156
185
|
Definitions::Index.new(
|
157
186
|
name: index_name,
|
158
187
|
columns: index_columns,
|
188
|
+
primary: primary,
|
159
189
|
unique: unique,
|
160
190
|
type: using,
|
161
191
|
condition: where
|
data/lib/db_schema/migrator.rb
CHANGED
@@ -86,6 +86,7 @@ module DbSchema
|
|
86
86
|
def alter_column_type(name, new_type, using: nil, **new_attributes)
|
87
87
|
alter_table.changes << Operations::AlterColumnType.new(
|
88
88
|
name,
|
89
|
+
old_type: nil,
|
89
90
|
new_type: new_type,
|
90
91
|
using: using,
|
91
92
|
**new_attributes
|
@@ -104,6 +105,16 @@ module DbSchema
|
|
104
105
|
alter_table.changes << Operations::AlterColumnDefault.new(name, new_default: new_default)
|
105
106
|
end
|
106
107
|
|
108
|
+
def add_primary_key(*columns)
|
109
|
+
alter_table.changes << Operations::CreateIndex.new(
|
110
|
+
DSL::TableYielder.build_index(columns, table_name: alter_table.table_name, primary: true)
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def drop_primary_key
|
115
|
+
alter_table.changes << Operations::DropIndex.new(:"#{alter_table.table_name}_pkey", true)
|
116
|
+
end
|
117
|
+
|
107
118
|
def add_index(*columns, **index_options)
|
108
119
|
alter_table.changes << Operations::CreateIndex.new(
|
109
120
|
DSL::TableYielder.build_index(
|
@@ -115,7 +126,7 @@ module DbSchema
|
|
115
126
|
end
|
116
127
|
|
117
128
|
def drop_index(name)
|
118
|
-
alter_table.changes << Operations::DropIndex.new(name)
|
129
|
+
alter_table.changes << Operations::DropIndex.new(name, false)
|
119
130
|
end
|
120
131
|
|
121
132
|
def add_check(name, condition)
|
data/lib/db_schema/normalizer.rb
CHANGED
@@ -13,10 +13,12 @@ module DbSchema
|
|
13
13
|
connection.transaction do
|
14
14
|
create_extensions!
|
15
15
|
create_enums!
|
16
|
+
create_temporary_tables!
|
17
|
+
normalized_tables = read_temporary_tables
|
16
18
|
|
17
19
|
schema.tables = schema.tables.map do |table|
|
18
20
|
if table.has_expressions?
|
19
|
-
|
21
|
+
normalized_tables.fetch(table.name)
|
20
22
|
else
|
21
23
|
table
|
22
24
|
end
|
@@ -43,37 +45,10 @@ module DbSchema
|
|
43
45
|
Runner.new(operations, connection).run!
|
44
46
|
end
|
45
47
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def hash
|
51
|
-
@hash ||= begin
|
52
|
-
names = schema.tables.flat_map do |table|
|
53
|
-
[table.name] + table.fields.map(&:name) + table.indexes.map(&:name) + table.checks.map(&:name)
|
54
|
-
end
|
55
|
-
|
56
|
-
Digest::MD5.hexdigest(names.join(','))[0..9]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class Table
|
61
|
-
attr_reader :table, :hash, :enum_names, :connection
|
62
|
-
|
63
|
-
def initialize(table, hash, enum_names, connection)
|
64
|
-
@table = table
|
65
|
-
@hash = hash
|
66
|
-
@enum_names = enum_names
|
67
|
-
@connection = connection
|
68
|
-
end
|
48
|
+
def create_temporary_tables!
|
49
|
+
schema.tables.select(&:has_expressions?).each do |table|
|
50
|
+
temporary_table_name = append_hash(table.name)
|
69
51
|
|
70
|
-
def normalized_table
|
71
|
-
create_temporary_table!
|
72
|
-
read_temporary_table
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
def create_temporary_table!
|
77
52
|
operation = Operations::CreateTable.new(
|
78
53
|
table.with_name(temporary_table_name)
|
79
54
|
.with_fields(rename_types(table.fields))
|
@@ -83,113 +58,127 @@ module DbSchema
|
|
83
58
|
|
84
59
|
Runner.new([operation], connection).run!
|
85
60
|
end
|
61
|
+
end
|
86
62
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
temporary_table.with_name(table.name)
|
91
|
-
.with_fields(rename_types_back(temporary_table.fields))
|
92
|
-
.with_indexes(rename_indexes_back(temporary_table.indexes))
|
93
|
-
.with_checks(rename_types_in_checks_back(temporary_table.checks))
|
94
|
-
.with_foreign_keys(table.foreign_keys)
|
95
|
-
end
|
63
|
+
def read_temporary_tables
|
64
|
+
all_tables = Reader.reader_for(connection).read_tables
|
96
65
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
else
|
102
|
-
field.default
|
103
|
-
end
|
66
|
+
schema.tables.select(&:has_expressions?).reduce({}) do |normalized_tables, table|
|
67
|
+
temporary_table = all_tables.find do |t|
|
68
|
+
t.name == append_hash(table.name).to_sym
|
69
|
+
end || raise
|
104
70
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
71
|
+
normalized_tables.merge(
|
72
|
+
table.name => temporary_table.with_name(table.name)
|
73
|
+
.with_fields(rename_types_back(temporary_table.fields))
|
74
|
+
.with_indexes(rename_indexes_back(temporary_table.indexes))
|
75
|
+
.with_checks(rename_types_in_checks_back(temporary_table.checks))
|
76
|
+
.with_foreign_keys(table.foreign_keys)
|
77
|
+
)
|
113
78
|
end
|
79
|
+
end
|
114
80
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
if field.custom?
|
124
|
-
field.with_type(remove_hash(field.type))
|
125
|
-
elsif field.array? && field.custom_element_type?
|
126
|
-
field.with_attribute(:element_type, remove_hash(field.element_type.type).to_sym)
|
127
|
-
else
|
128
|
-
field
|
129
|
-
end.with_default(new_default)
|
81
|
+
def rename_types(fields)
|
82
|
+
fields.map do |field|
|
83
|
+
new_default = if field.default_is_expression?
|
84
|
+
rename_all_types_in(field.default.to_s).to_sym
|
85
|
+
else
|
86
|
+
field.default
|
130
87
|
end
|
131
|
-
end
|
132
88
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
89
|
+
if field.custom?
|
90
|
+
field.with_type(append_hash(field.type))
|
91
|
+
elsif field.array? && field.custom_element_type?
|
92
|
+
field.with_attribute(:element_type, append_hash(field.element_type.type).to_sym)
|
93
|
+
else
|
94
|
+
field
|
95
|
+
end.with_default(new_default)
|
139
96
|
end
|
97
|
+
end
|
140
98
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
99
|
+
def rename_types_back(fields)
|
100
|
+
fields.map do |field|
|
101
|
+
new_default = if field.default_is_expression?
|
102
|
+
rename_all_types_back_in(field.default.to_s).to_sym
|
103
|
+
else
|
104
|
+
field.default
|
146
105
|
end
|
147
|
-
end
|
148
106
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
107
|
+
if field.custom?
|
108
|
+
field.with_type(remove_hash(field.type))
|
109
|
+
elsif field.array? && field.custom_element_type?
|
110
|
+
field.with_attribute(:element_type, remove_hash(field.element_type.type).to_sym)
|
111
|
+
else
|
112
|
+
field
|
113
|
+
end.with_default(new_default)
|
153
114
|
end
|
115
|
+
end
|
154
116
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
117
|
+
def rename_indexes(indexes)
|
118
|
+
indexes.map do |index|
|
119
|
+
index
|
120
|
+
.with_name(append_hash(index.name))
|
121
|
+
.with_condition(rename_all_types_in(index.condition))
|
159
122
|
end
|
123
|
+
end
|
160
124
|
|
161
|
-
|
162
|
-
|
125
|
+
def rename_indexes_back(indexes)
|
126
|
+
indexes.map do |index|
|
127
|
+
index
|
128
|
+
.with_name(remove_hash(index.name))
|
129
|
+
.with_condition(rename_all_types_back_in(index.condition))
|
163
130
|
end
|
131
|
+
end
|
164
132
|
|
165
|
-
|
166
|
-
|
133
|
+
def rename_types_in_checks(checks)
|
134
|
+
checks.map do |check|
|
135
|
+
check.with_condition(rename_all_types_in(check.condition))
|
167
136
|
end
|
137
|
+
end
|
168
138
|
|
169
|
-
|
170
|
-
|
139
|
+
def rename_types_in_checks_back(checks)
|
140
|
+
checks.map do |check|
|
141
|
+
check.with_condition(rename_all_types_back_in(check.condition))
|
171
142
|
end
|
143
|
+
end
|
172
144
|
|
173
|
-
|
174
|
-
|
145
|
+
def append_hash(name)
|
146
|
+
"#{name}_#{hash}"
|
147
|
+
end
|
175
148
|
|
176
|
-
|
177
|
-
|
178
|
-
|
149
|
+
def remove_hash(name)
|
150
|
+
name.to_s.sub(/_#{Regexp.escape(hash)}$/, '').to_sym
|
151
|
+
end
|
152
|
+
|
153
|
+
def rename_all_types_in(string)
|
154
|
+
return string unless string.is_a?(String)
|
155
|
+
|
156
|
+
enum_renaming.reduce(string) do |new_string, (from, to)|
|
157
|
+
new_string.gsub(from, to)
|
179
158
|
end
|
159
|
+
end
|
180
160
|
|
181
|
-
|
182
|
-
|
161
|
+
def rename_all_types_back_in(string)
|
162
|
+
return string unless string.is_a?(String)
|
183
163
|
|
184
|
-
|
185
|
-
|
186
|
-
end
|
164
|
+
enum_renaming.invert.reduce(string) do |new_string, (from, to)|
|
165
|
+
new_string.gsub(from, to)
|
187
166
|
end
|
167
|
+
end
|
188
168
|
|
189
|
-
|
190
|
-
|
191
|
-
|
169
|
+
def enum_renaming
|
170
|
+
schema.enums.reduce({}) do |hash, enum|
|
171
|
+
hash.merge("::#{enum.name}" => "::#{append_hash(enum.name)}")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def hash
|
176
|
+
@hash ||= begin
|
177
|
+
names = schema.tables.flat_map do |table|
|
178
|
+
[table.name] + table.fields.map(&:name) + table.indexes.map(&:name) + table.checks.map(&:name)
|
192
179
|
end
|
180
|
+
|
181
|
+
Digest::MD5.hexdigest(names.join(','))[0..9]
|
193
182
|
end
|
194
183
|
end
|
195
184
|
end
|
data/lib/db_schema/operations.rb
CHANGED
@@ -68,10 +68,6 @@ module DbSchema
|
|
68
68
|
field.type
|
69
69
|
end
|
70
70
|
|
71
|
-
def primary_key?
|
72
|
-
field.primary_key?
|
73
|
-
end
|
74
|
-
|
75
71
|
def options
|
76
72
|
field.options
|
77
73
|
end
|
@@ -84,21 +80,26 @@ module DbSchema
|
|
84
80
|
end
|
85
81
|
|
86
82
|
class AlterColumnType
|
87
|
-
|
88
|
-
|
83
|
+
SERIAL_TYPES = [:smallserial, :serial, :bigserial].freeze
|
84
|
+
|
85
|
+
include Dry::Equalizer(:name, :old_type, :new_type, :using, :new_attributes)
|
86
|
+
attr_reader :name, :old_type, :new_type, :using, :new_attributes
|
89
87
|
|
90
|
-
def initialize(name, new_type:, using: nil, **new_attributes)
|
88
|
+
def initialize(name, old_type:, new_type:, using: nil, **new_attributes)
|
91
89
|
@name = name
|
90
|
+
@old_type = old_type
|
92
91
|
@new_type = new_type
|
93
92
|
@using = using
|
94
93
|
@new_attributes = new_attributes
|
95
94
|
end
|
96
|
-
end
|
97
95
|
|
98
|
-
|
99
|
-
|
96
|
+
def from_serial?
|
97
|
+
SERIAL_TYPES.include?(old_type)
|
98
|
+
end
|
100
99
|
|
101
|
-
|
100
|
+
def to_serial?
|
101
|
+
SERIAL_TYPES.include?(new_type)
|
102
|
+
end
|
102
103
|
end
|
103
104
|
|
104
105
|
class AllowNull < ColumnOperation
|
@@ -124,9 +125,32 @@ module DbSchema
|
|
124
125
|
def initialize(index)
|
125
126
|
@index = index
|
126
127
|
end
|
128
|
+
|
129
|
+
def primary?
|
130
|
+
index.primary?
|
131
|
+
end
|
132
|
+
|
133
|
+
def name
|
134
|
+
index.name
|
135
|
+
end
|
136
|
+
|
137
|
+
def columns
|
138
|
+
index.columns
|
139
|
+
end
|
127
140
|
end
|
128
141
|
|
129
|
-
class DropIndex
|
142
|
+
class DropIndex
|
143
|
+
include Dry::Equalizer(:name, :primary?)
|
144
|
+
attr_reader :name
|
145
|
+
|
146
|
+
def initialize(name, primary)
|
147
|
+
@name = name
|
148
|
+
@primary = primary
|
149
|
+
end
|
150
|
+
|
151
|
+
def primary?
|
152
|
+
@primary
|
153
|
+
end
|
130
154
|
end
|
131
155
|
|
132
156
|
class CreateCheckConstraint
|
data/lib/db_schema/runner.rb
CHANGED
@@ -44,24 +44,22 @@ module DbSchema
|
|
44
44
|
def create_table(change)
|
45
45
|
connection.create_table(change.table.name) do
|
46
46
|
change.table.fields.each do |field|
|
47
|
-
|
48
|
-
|
49
|
-
else
|
50
|
-
options = Runner.map_options(field.class.type, field.options)
|
51
|
-
column(field.name, field.type.capitalize, options)
|
52
|
-
|
53
|
-
primary_key([field.name]) if field.primary_key?
|
54
|
-
end
|
47
|
+
options = Runner.map_options(field.class.type, field.options)
|
48
|
+
column(field.name, field.type.capitalize, options)
|
55
49
|
end
|
56
50
|
|
57
51
|
change.table.indexes.each do |index|
|
58
|
-
index
|
59
|
-
index.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
52
|
+
if index.primary?
|
53
|
+
primary_key(index.columns.map(&:name), name: index.name)
|
54
|
+
else
|
55
|
+
index(
|
56
|
+
index.columns_to_sequel,
|
57
|
+
name: index.name,
|
58
|
+
unique: index.unique?,
|
59
|
+
type: index.type,
|
60
|
+
where: index.condition
|
61
|
+
)
|
62
|
+
end
|
65
63
|
end
|
66
64
|
|
67
65
|
change.table.checks.each do |check|
|
@@ -83,25 +81,23 @@ module DbSchema
|
|
83
81
|
change.changes.each do |element|
|
84
82
|
case element
|
85
83
|
when Operations::CreateColumn
|
86
|
-
|
87
|
-
|
88
|
-
else
|
89
|
-
options = Runner.map_options(element.type, element.options)
|
90
|
-
add_column(element.name, element.type.capitalize, options)
|
91
|
-
|
92
|
-
add_primary_key([element.name]) if element.primary_key?
|
93
|
-
end
|
84
|
+
options = Runner.map_options(element.type, element.options)
|
85
|
+
add_column(element.name, element.type.capitalize, options)
|
94
86
|
when Operations::DropColumn
|
95
87
|
drop_column(element.name)
|
96
88
|
when Operations::RenameColumn
|
97
89
|
rename_column(element.old_name, element.new_name)
|
98
90
|
when Operations::AlterColumnType
|
91
|
+
if element.from_serial?
|
92
|
+
raise NotImplementedError, 'Changing a SERIAL column to another type is not supported'
|
93
|
+
end
|
94
|
+
|
95
|
+
if element.to_serial?
|
96
|
+
raise NotImplementedError, 'Changing a column type to SERIAL is not supported'
|
97
|
+
end
|
98
|
+
|
99
99
|
attributes = Runner.map_options(element.new_type, element.new_attributes)
|
100
100
|
set_column_type(element.name, element.new_type.capitalize, using: element.using, **attributes)
|
101
|
-
when Operations::CreatePrimaryKey
|
102
|
-
raise NotImplementedError, 'Converting an existing column to primary key is currently unsupported'
|
103
|
-
when Operations::DropPrimaryKey
|
104
|
-
raise NotImplementedError, 'Removing a primary key while leaving the column is currently unsupported'
|
105
101
|
when Operations::AllowNull
|
106
102
|
set_column_allow_null(element.name)
|
107
103
|
when Operations::DisallowNull
|
@@ -109,15 +105,23 @@ module DbSchema
|
|
109
105
|
when Operations::AlterColumnDefault
|
110
106
|
set_column_default(element.name, Runner.default_to_sequel(element.new_default))
|
111
107
|
when Operations::CreateIndex
|
112
|
-
|
113
|
-
element.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
108
|
+
if element.primary?
|
109
|
+
add_primary_key(element.columns.map(&:name), name: element.name)
|
110
|
+
else
|
111
|
+
add_index(
|
112
|
+
element.index.columns_to_sequel,
|
113
|
+
name: element.index.name,
|
114
|
+
unique: element.index.unique?,
|
115
|
+
type: element.index.type,
|
116
|
+
where: element.index.condition
|
117
|
+
)
|
118
|
+
end
|
119
119
|
when Operations::DropIndex
|
120
|
-
|
120
|
+
if element.primary?
|
121
|
+
drop_constraint(element.name)
|
122
|
+
else
|
123
|
+
drop_index([], name: element.name)
|
124
|
+
end
|
121
125
|
when Operations::CreateCheckConstraint
|
122
126
|
add_constraint(element.check.name, element.check.condition)
|
123
127
|
when Operations::DropCheckConstraint
|
data/lib/db_schema/validator.rb
CHANGED
@@ -5,7 +5,7 @@ module DbSchema
|
|
5
5
|
class << self
|
6
6
|
def validate(schema)
|
7
7
|
table_errors = schema.tables.each_with_object([]) do |table, errors|
|
8
|
-
primary_keys_count = table.
|
8
|
+
primary_keys_count = table.indexes.select(&:primary?).count
|
9
9
|
if primary_keys_count > 1
|
10
10
|
error_message = %(Table "#{table.name}" has #{primary_keys_count} primary keys)
|
11
11
|
errors << error_message
|
@@ -60,7 +60,7 @@ module DbSchema
|
|
60
60
|
|
61
61
|
if referenced_table = schema.tables.find { |table| table.name == fkey.table }
|
62
62
|
if fkey.references_primary_key?
|
63
|
-
unless referenced_table.
|
63
|
+
unless referenced_table.indexes.any?(&:primary?)
|
64
64
|
error_message = %(Foreign key "#{fkey.name}" refers to primary key of table "#{fkey.table}" which does not have a primary key)
|
65
65
|
errors << error_message
|
66
66
|
end
|
data/lib/db_schema/version.rb
CHANGED
data/lib/db_schema.rb
CHANGED
@@ -114,8 +114,7 @@ module DbSchema
|
|
114
114
|
end
|
115
115
|
|
116
116
|
def run_migrations(migrations, connection)
|
117
|
-
|
118
|
-
@current_schema = reader.read_schema
|
117
|
+
@current_schema = Reader.reader_for(connection).read_schema
|
119
118
|
|
120
119
|
migrations.reduce(@current_schema) do |schema, migration|
|
121
120
|
migrator = Migrator.new(migration)
|
@@ -123,7 +122,7 @@ module DbSchema
|
|
123
122
|
if migrator.applicable?(schema)
|
124
123
|
log_migration(migration) if configuration.log_changes?
|
125
124
|
migrator.run!(connection)
|
126
|
-
|
125
|
+
Reader.reader_for(connection).read_schema
|
127
126
|
else
|
128
127
|
schema
|
129
128
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: db_schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vsevolod Romashov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: db_schema-definitions
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.2.rc1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.2.rc1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -168,16 +168,16 @@ dependencies:
|
|
168
168
|
name: db_schema-reader-postgres
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
|
-
- -
|
171
|
+
- - '='
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: 0.
|
173
|
+
version: 0.2.rc1
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
|
-
- -
|
178
|
+
- - '='
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: 0.
|
180
|
+
version: 0.2.rc1
|
181
181
|
description: A database schema management tool that reads a "single-source-of-truth"
|
182
182
|
schema definition from a ruby file and auto-migrates the database to conform to
|
183
183
|
it.
|
@@ -228,12 +228,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
228
228
|
version: '0'
|
229
229
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
230
230
|
requirements:
|
231
|
-
- - "
|
231
|
+
- - ">"
|
232
232
|
- !ruby/object:Gem::Version
|
233
|
-
version:
|
233
|
+
version: 1.3.1
|
234
234
|
requirements: []
|
235
|
-
|
236
|
-
rubygems_version: 2.7.6
|
235
|
+
rubygems_version: 3.0.1
|
237
236
|
signing_key:
|
238
237
|
specification_version: 4
|
239
238
|
summary: Declarative database schema definition.
|