db_schema 0.4.1 → 0.5.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 +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.
|