db_schema 0.1.3 → 0.2
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/README.md +4 -16
- data/lib/db_schema/awesome_print.rb +30 -3
- data/lib/db_schema/changes.rb +8 -29
- data/lib/db_schema/definitions/field/base.rb +5 -9
- data/lib/db_schema/definitions/field/datetime.rb +1 -1
- data/lib/db_schema/definitions.rb +35 -13
- data/lib/db_schema/dsl.rb +4 -4
- data/lib/db_schema/normalizer.rb +89 -0
- data/lib/db_schema/reader.rb +53 -29
- data/lib/db_schema/runner.rb +15 -1
- data/lib/db_schema/validator.rb +4 -7
- data/lib/db_schema/version.rb +1 -1
- data/lib/db_schema.rb +14 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3851833bd5308ba174d2d70c217b6fc73c7d583b
|
4
|
+
data.tar.gz: 95071a258ce6202862e38a64d0d57fee8a0cfea9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 424334e2045a9cf08bdf6fedddbc8710c05f1d101ac2bd22350191e71e8dccc201118bb3511590ff56d67610c80494e6c7aed98d2d2e263b582343f910179567
|
7
|
+
data.tar.gz: cac4af5351c520d8610a98c54e57d7c93a884bcc0f0b33e953eedb45291e43e9cfd5fccfef47ee791e3ab07dcde72a546c6c3f0d22959c3acdf8f9b24fe1f1c6
|
data/README.md
CHANGED
@@ -53,7 +53,7 @@ But you would lose it even with manual migrations.
|
|
53
53
|
Add this line to your application's Gemfile:
|
54
54
|
|
55
55
|
``` ruby
|
56
|
-
gem 'db_schema', '~> 0.
|
56
|
+
gem 'db_schema', '~> 0.2.0'
|
57
57
|
```
|
58
58
|
|
59
59
|
And then execute:
|
@@ -207,7 +207,7 @@ db.table :people do |t|
|
|
207
207
|
end
|
208
208
|
```
|
209
209
|
|
210
|
-
Passing `null: false` to the field definition makes it `NOT NULL`; passing some value under the `:default` key makes it the default value. A symbol passed as a default is interpreted as
|
210
|
+
Passing `null: false` to the field definition makes it `NOT NULL`; passing some value under the `:default` key makes it the default value. You can use `String`s as SQL strings, `Fixnum`s as integers, `Float`s as floating point numbers, `true` & `false` as their SQL counterparts, `Date`s as SQL dates and `Time`s as timestamps. A symbol passed as a default is a special case: it is interpreted as an SQL expression so `t.timestamp :created_at, default: :'now()'` defines a field with a default value of `NOW()`.
|
211
211
|
|
212
212
|
Other attributes are type specific, like `:length` for varchars; the following table lists them all (values in parentheses are default attribute values).
|
213
213
|
|
@@ -354,8 +354,6 @@ db.table :users do |t|
|
|
354
354
|
end
|
355
355
|
```
|
356
356
|
|
357
|
-
Be warned though that you have to specify the condition exactly as PostgreSQL outputs it in `psql` with `\d table_name` command; otherwise your index will be recreated on each DbSchema run. This will be fixed in a later DbSchema version.
|
358
|
-
|
359
357
|
If you need an index on expression you can use the same syntax replacing column names with SQL strings containing the expressions:
|
360
358
|
|
361
359
|
``` ruby
|
@@ -367,8 +365,6 @@ end
|
|
367
365
|
|
368
366
|
Expression indexes syntax allows specifying an order exactly like in a common index on table fields - just use a hash form like `t.index 'date(created_at)' => :desc`. You can also use an expression in a multiple index.
|
369
367
|
|
370
|
-
As with partial index condition (and all other SQL segments in `db_schema`), you must write the expression in a way `psql` outputs it, so instead of `lower(email)` you should use `lower(email::text)` (assuming that `email` is a varchar field).
|
371
|
-
|
372
368
|
#### Foreign keys
|
373
369
|
|
374
370
|
The `#foreign_key` method defines a foreign key. In it's minimal form it takes a referencing field name and referenced table name:
|
@@ -439,8 +435,6 @@ db.table :users do |t|
|
|
439
435
|
end
|
440
436
|
```
|
441
437
|
|
442
|
-
As with partial index conditions, for now you have to specify the SQL exactly as `psql` outputs it (otherwise the constraint will be recreated on each run).
|
443
|
-
|
444
438
|
### Enum types
|
445
439
|
|
446
440
|
PostgreSQL allows developers to create custom enum types; value of enum type is one of a fixed set of values stored in the type definition.
|
@@ -532,9 +526,7 @@ All configuration options are described in the following table:
|
|
532
526
|
|
533
527
|
By default DbSchema logs the changes it applies to your database; you can disable that by setting `log_changes` to false.
|
534
528
|
|
535
|
-
DbSchema provides an opt-out post-run schema check; it ensures that there are no remaining differences between your `schema.rb` and the actual database schema.
|
536
|
-
|
537
|
-
The `post_check` option is likely to become off by default when DbSchema becomes more stable and battle-tested, and when the partial index problem will be solved.
|
529
|
+
DbSchema provides an opt-out post-run schema check; it ensures that the schema was applied correctly and there are no remaining differences between your `schema.rb` and the actual database schema. The corresponding `post_check` option is likely to become off by default when DbSchema becomes more stable and battle-tested.
|
538
530
|
|
539
531
|
There is also a dry run mode which does not apply the changes to your database - it just logs the necessary changes (if you leave `log_changes` set to `true`). Post check is also skipped in that case.
|
540
532
|
|
@@ -543,12 +535,8 @@ Dry run may be useful while you are building your schema definition for an exist
|
|
543
535
|
## Known problems and limitations
|
544
536
|
|
545
537
|
* primary keys are hardcoded to a single NOT NULL integer field with a postgres sequence attached
|
546
|
-
* "partial index problem": some conditions of partial indexes and check constraints can cause
|
547
|
-
a false positive result of checking for differences between `schema.rb` and actual database schema,
|
548
|
-
resulting in unwanted operations on each run (the worst of them being the recreation of an index
|
549
|
-
on a large table)
|
550
538
|
* array element type attributes are not supported
|
551
|
-
* precision in time
|
539
|
+
* precision in all date/time types isn't supported
|
552
540
|
* no support for databases other than PostgreSQL
|
553
541
|
* no support for renaming tables & columns
|
554
542
|
|
@@ -13,6 +13,8 @@ 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
|
16
18
|
when ::DbSchema::Definitions::Table
|
17
19
|
:dbschema_table
|
18
20
|
when ::DbSchema::Definitions::Field::Custom
|
@@ -79,6 +81,15 @@ if defined?(AwesomePrint)
|
|
79
81
|
end
|
80
82
|
|
81
83
|
private
|
84
|
+
def awesome_dbschema_schema(object)
|
85
|
+
data = ["tables: #{object.tables.ai}"]
|
86
|
+
data << "enums: #{object.enums.ai}" if object.enums.any?
|
87
|
+
data << "extensions: #{object.extensions.ai}" if object.extensions.any?
|
88
|
+
|
89
|
+
data_string = data.join(', ')
|
90
|
+
"#<DbSchema::Definitions::Schema #{data_string}>"
|
91
|
+
end
|
92
|
+
|
82
93
|
def awesome_dbschema_table(object)
|
83
94
|
data = ["fields: #{object.fields.ai}"]
|
84
95
|
data << "indices: #{object.indices.ai}" if object.indices.any?
|
@@ -92,7 +103,12 @@ if defined?(AwesomePrint)
|
|
92
103
|
def awesome_dbschema_field(object)
|
93
104
|
options = object.options.map do |k, v|
|
94
105
|
key = colorize("#{k}:", :symbol)
|
95
|
-
|
106
|
+
|
107
|
+
if (k == :default) && v.is_a?(Symbol)
|
108
|
+
"#{key} #{colorize(v.to_s, :string)}"
|
109
|
+
else
|
110
|
+
"#{key} #{v.ai}"
|
111
|
+
end
|
96
112
|
end.unshift(nil).join(', ')
|
97
113
|
|
98
114
|
primary_key = if object.primary_key?
|
@@ -107,7 +123,12 @@ if defined?(AwesomePrint)
|
|
107
123
|
def awesome_dbschema_custom_field(object)
|
108
124
|
options = object.options.map do |k, v|
|
109
125
|
key = colorize("#{k}:", :symbol)
|
110
|
-
|
126
|
+
|
127
|
+
if (k == :default) && v.is_a?(Symbol)
|
128
|
+
"#{key} #{colorize(v.to_s, :string)}"
|
129
|
+
else
|
130
|
+
"#{key} #{v.ai}"
|
131
|
+
end
|
111
132
|
end.unshift(nil).join(', ')
|
112
133
|
|
113
134
|
primary_key = if object.primary_key?
|
@@ -212,7 +233,13 @@ if defined?(AwesomePrint)
|
|
212
233
|
end
|
213
234
|
|
214
235
|
def awesome_dbschema_alter_column_default(object)
|
215
|
-
|
236
|
+
new_default = if object.new_default.is_a?(Symbol)
|
237
|
+
colorize(object.new_default.to_s, :string)
|
238
|
+
else
|
239
|
+
object.new_default.ai
|
240
|
+
end
|
241
|
+
|
242
|
+
"#<DbSchema::Changes::AlterColumnDefault #{object.name.ai}, #{new_default}>"
|
216
243
|
end
|
217
244
|
|
218
245
|
def awesome_dbschema_create_foreign_key(object)
|
data/lib/db_schema/changes.rb
CHANGED
@@ -5,14 +5,11 @@ module DbSchema
|
|
5
5
|
module Changes
|
6
6
|
class << self
|
7
7
|
def between(desired_schema, actual_schema)
|
8
|
-
|
9
|
-
actual_tables = extract_tables(actual_schema)
|
10
|
-
|
11
|
-
table_names = [desired_tables, actual_tables].flatten.map(&:name).uniq
|
8
|
+
table_names = [desired_schema.tables, actual_schema.tables].flatten.map(&:name).uniq
|
12
9
|
|
13
10
|
table_changes = table_names.each.with_object([]) do |table_name, changes|
|
14
|
-
desired =
|
15
|
-
actual =
|
11
|
+
desired = desired_schema.tables.find { |table| table.name == table_name }
|
12
|
+
actual = actual_schema.tables.find { |table| table.name == table_name }
|
16
13
|
|
17
14
|
if desired && !actual
|
18
15
|
changes << CreateTable.new(
|
@@ -51,14 +48,11 @@ module DbSchema
|
|
51
48
|
end
|
52
49
|
end
|
53
50
|
|
54
|
-
|
55
|
-
actual_enums = extract_enums(actual_schema)
|
56
|
-
|
57
|
-
enum_names = [desired_enums, actual_enums].flatten.map(&:name).uniq
|
51
|
+
enum_names = [desired_schema.enums, actual_schema.enums].flatten.map(&:name).uniq
|
58
52
|
|
59
53
|
enum_changes = enum_names.each_with_object([]) do |enum_name, changes|
|
60
|
-
desired =
|
61
|
-
actual =
|
54
|
+
desired = desired_schema.enums.find { |enum| enum.name == enum_name }
|
55
|
+
actual = actual_schema.enums.find { |enum| enum.name == enum_name }
|
62
56
|
|
63
57
|
if desired && !actual
|
64
58
|
changes << CreateEnum.new(enum_name, desired.values)
|
@@ -89,12 +83,9 @@ module DbSchema
|
|
89
83
|
end
|
90
84
|
end
|
91
85
|
|
92
|
-
|
93
|
-
actual_extensions = extract_extensions(actual_schema)
|
94
|
-
|
95
|
-
extension_changes = (desired_extensions - actual_extensions).map do |extension|
|
86
|
+
extension_changes = (desired_schema.extensions - actual_schema.extensions).map do |extension|
|
96
87
|
CreateExtension.new(extension.name)
|
97
|
-
end + (
|
88
|
+
end + (actual_schema.extensions - desired_schema.extensions).map do |extension|
|
98
89
|
DropExtension.new(extension.name)
|
99
90
|
end
|
100
91
|
|
@@ -226,18 +217,6 @@ module DbSchema
|
|
226
217
|
end
|
227
218
|
end
|
228
219
|
end
|
229
|
-
|
230
|
-
def extract_tables(schema)
|
231
|
-
Utils.filter_by_class(schema, Definitions::Table)
|
232
|
-
end
|
233
|
-
|
234
|
-
def extract_enums(schema)
|
235
|
-
Utils.filter_by_class(schema, Definitions::Enum)
|
236
|
-
end
|
237
|
-
|
238
|
-
def extract_extensions(schema)
|
239
|
-
Utils.filter_by_class(schema, Definitions::Extension)
|
240
|
-
end
|
241
220
|
end
|
242
221
|
|
243
222
|
class CreateTable
|
@@ -9,7 +9,7 @@ module DbSchema
|
|
9
9
|
@name = name
|
10
10
|
@primary_key = primary_key
|
11
11
|
@null = null
|
12
|
-
@default =
|
12
|
+
@default = default
|
13
13
|
@attributes = attributes
|
14
14
|
end
|
15
15
|
|
@@ -21,6 +21,10 @@ module DbSchema
|
|
21
21
|
!primary_key? && @null
|
22
22
|
end
|
23
23
|
|
24
|
+
def default_is_expression?
|
25
|
+
default.is_a?(Symbol)
|
26
|
+
end
|
27
|
+
|
24
28
|
def options
|
25
29
|
attributes.tap do |options|
|
26
30
|
options[:null] = false unless null?
|
@@ -48,14 +52,6 @@ module DbSchema
|
|
48
52
|
self.class.type
|
49
53
|
end
|
50
54
|
|
51
|
-
def process_default(default)
|
52
|
-
if default.is_a?(Symbol)
|
53
|
-
Sequel.function(default)
|
54
|
-
else
|
55
|
-
default
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
55
|
class << self
|
60
56
|
def register(*types)
|
61
57
|
types.each do |type|
|
@@ -2,6 +2,37 @@ require 'dry/equalizer'
|
|
2
2
|
|
3
3
|
module DbSchema
|
4
4
|
module Definitions
|
5
|
+
class Schema
|
6
|
+
include Dry::Equalizer(:tables, :enums, :extensions)
|
7
|
+
attr_reader :tables, :enums, :extensions
|
8
|
+
attr_writer :tables
|
9
|
+
|
10
|
+
def initialize(tables: [], enums: [], extensions: [])
|
11
|
+
@tables = tables
|
12
|
+
@enums = enums
|
13
|
+
@extensions = extensions
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Table
|
18
|
+
include Dry::Equalizer(:name, :fields, :indices, :checks, :foreign_keys)
|
19
|
+
attr_reader :name, :fields, :indices, :checks, :foreign_keys
|
20
|
+
|
21
|
+
def initialize(name, fields: [], indices: [], checks: [], foreign_keys: [])
|
22
|
+
@name = name.to_sym
|
23
|
+
@fields = fields
|
24
|
+
@indices = indices
|
25
|
+
@checks = checks
|
26
|
+
@foreign_keys = foreign_keys
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_expressions?
|
30
|
+
fields.any?(&:default_is_expression?) ||
|
31
|
+
indices.any?(&:has_expressions?) ||
|
32
|
+
checks.any?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
5
36
|
class Index
|
6
37
|
include Dry::Equalizer(:name, :columns, :unique?, :type, :condition)
|
7
38
|
attr_reader :name, :columns, :type, :condition
|
@@ -30,6 +61,10 @@ module DbSchema
|
|
30
61
|
end
|
31
62
|
end
|
32
63
|
|
64
|
+
def has_expressions?
|
65
|
+
!condition.nil? || columns.any?(&:expression?)
|
66
|
+
end
|
67
|
+
|
33
68
|
class Column
|
34
69
|
include Dry::Equalizer(:name, :order, :nulls)
|
35
70
|
attr_reader :name, :order, :nulls
|
@@ -130,19 +165,6 @@ module DbSchema
|
|
130
165
|
end
|
131
166
|
end
|
132
167
|
|
133
|
-
class Table
|
134
|
-
include Dry::Equalizer(:name, :fields, :indices, :checks, :foreign_keys)
|
135
|
-
attr_reader :name, :fields, :indices, :checks, :foreign_keys
|
136
|
-
|
137
|
-
def initialize(name, fields: [], indices: [], checks: [], foreign_keys: [])
|
138
|
-
@name = name.to_sym
|
139
|
-
@fields = fields
|
140
|
-
@indices = indices
|
141
|
-
@checks = checks
|
142
|
-
@foreign_keys = foreign_keys
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
168
|
class Enum
|
147
169
|
include Dry::Equalizer(:name, :values)
|
148
170
|
attr_reader :name, :values
|
data/lib/db_schema/dsl.rb
CHANGED
@@ -4,7 +4,7 @@ module DbSchema
|
|
4
4
|
|
5
5
|
def initialize(block)
|
6
6
|
@block = block
|
7
|
-
@schema =
|
7
|
+
@schema = Definitions::Schema.new
|
8
8
|
end
|
9
9
|
|
10
10
|
def schema
|
@@ -16,7 +16,7 @@ module DbSchema
|
|
16
16
|
def table(name, &block)
|
17
17
|
table_yielder = TableYielder.new(name, block)
|
18
18
|
|
19
|
-
@schema << Definitions::Table.new(
|
19
|
+
@schema.tables << Definitions::Table.new(
|
20
20
|
name,
|
21
21
|
fields: table_yielder.fields,
|
22
22
|
indices: table_yielder.indices,
|
@@ -26,11 +26,11 @@ module DbSchema
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def enum(name, values)
|
29
|
-
@schema << Definitions::Enum.new(name.to_sym, values.map(&:to_sym))
|
29
|
+
@schema.enums << Definitions::Enum.new(name.to_sym, values.map(&:to_sym))
|
30
30
|
end
|
31
31
|
|
32
32
|
def extension(name)
|
33
|
-
@schema << Definitions::Extension.new(name.to_sym)
|
33
|
+
@schema.extensions << Definitions::Extension.new(name.to_sym)
|
34
34
|
end
|
35
35
|
|
36
36
|
class TableYielder
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module DbSchema
|
4
|
+
class Normalizer
|
5
|
+
attr_reader :table
|
6
|
+
|
7
|
+
def initialize(table)
|
8
|
+
@table = table
|
9
|
+
end
|
10
|
+
|
11
|
+
def normalized_table
|
12
|
+
create_temporary_table!
|
13
|
+
read_temporary_table
|
14
|
+
ensure
|
15
|
+
cleanup!
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def create_temporary_table!
|
20
|
+
operation = Changes::CreateTable.new(
|
21
|
+
temporary_table_name,
|
22
|
+
fields: table.fields,
|
23
|
+
indices: rename_indices(table.indices),
|
24
|
+
checks: table.checks
|
25
|
+
)
|
26
|
+
|
27
|
+
Runner.new([operation]).run!
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_temporary_table
|
31
|
+
temporary_table = Reader.read_table(temporary_table_name)
|
32
|
+
|
33
|
+
Definitions::Table.new(
|
34
|
+
remove_hash(temporary_table.name),
|
35
|
+
fields: temporary_table.fields,
|
36
|
+
indices: rename_indices_back(temporary_table.indices),
|
37
|
+
checks: temporary_table.checks,
|
38
|
+
foreign_keys: table.foreign_keys
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def cleanup!
|
43
|
+
DbSchema.connection.drop_table(temporary_table_name, if_exists: true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def rename_indices(indices)
|
47
|
+
indices.map do |index|
|
48
|
+
Definitions::Index.new(
|
49
|
+
name: append_hash(index.name),
|
50
|
+
columns: index.columns,
|
51
|
+
unique: index.unique?,
|
52
|
+
type: index.type,
|
53
|
+
condition: index.condition
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def rename_indices_back(indices)
|
59
|
+
indices.map do |index|
|
60
|
+
Definitions::Index.new(
|
61
|
+
name: remove_hash(index.name),
|
62
|
+
columns: index.columns,
|
63
|
+
unique: index.unique?,
|
64
|
+
type: index.type,
|
65
|
+
condition: index.condition
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def temporary_table_name
|
71
|
+
append_hash(table.name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def append_hash(name)
|
75
|
+
"#{name}_#{hash}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_hash(name)
|
79
|
+
name.to_s.sub(/_#{Regexp.escape(hash)}$/, '').to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
def hash
|
83
|
+
@hash ||= begin
|
84
|
+
names = [table.name] + table.fields.map(&:name) + table.indices.map(&:name) + table.checks.map(&:name)
|
85
|
+
Digest::MD5.hexdigest(names.join(','))[0..9]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/db_schema/reader.rb
CHANGED
@@ -5,6 +5,10 @@ module DbSchema
|
|
5
5
|
adapter.read_schema
|
6
6
|
end
|
7
7
|
|
8
|
+
def read_table(table_name)
|
9
|
+
adapter.read_table(table_name)
|
10
|
+
end
|
11
|
+
|
8
12
|
def adapter
|
9
13
|
adapter_name = DbSchema.configuration.adapter
|
10
14
|
registry.fetch(adapter_name) do |adapter_name|
|
@@ -19,7 +23,19 @@ module DbSchema
|
|
19
23
|
end
|
20
24
|
|
21
25
|
module Postgres
|
22
|
-
DEFAULT_VALUE = /\A(
|
26
|
+
DEFAULT_VALUE = /\A(
|
27
|
+
('(?<date>\d{4}-\d{2}-\d{2})'::date)
|
28
|
+
|
|
29
|
+
('(?<time>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}([+-]\d{2})?)'::timestamp)
|
30
|
+
|
|
31
|
+
('(?<string>.*)')
|
32
|
+
|
|
33
|
+
(?<float>\d+\.\d+)
|
34
|
+
|
|
35
|
+
(?<integer>\d+)
|
36
|
+
|
|
37
|
+
(?<boolean>true|false)
|
38
|
+
)/x
|
23
39
|
|
24
40
|
COLUMN_NAMES_QUERY = <<-SQL.freeze
|
25
41
|
SELECT c.column_name AS name,
|
@@ -104,6 +120,10 @@ SELECT extname
|
|
104
120
|
|
105
121
|
class << self
|
106
122
|
def read_schema
|
123
|
+
tables = DbSchema.connection.tables.map do |table_name|
|
124
|
+
read_table(table_name)
|
125
|
+
end
|
126
|
+
|
107
127
|
enums = DbSchema.connection[ENUMS_QUERY].map do |enum_data|
|
108
128
|
Definitions::Enum.new(enum_data[:name].to_sym, enum_data[:values].map(&:to_sym))
|
109
129
|
end
|
@@ -112,38 +132,38 @@ SELECT extname
|
|
112
132
|
Definitions::Extension.new(extension_data[:extname].to_sym)
|
113
133
|
end
|
114
134
|
|
115
|
-
tables
|
116
|
-
|
135
|
+
Definitions::Schema.new(tables: tables, enums: enums, extensions: extensions)
|
136
|
+
end
|
117
137
|
|
118
|
-
|
119
|
-
|
120
|
-
end
|
138
|
+
def read_table(table_name)
|
139
|
+
primary_key_name = DbSchema.connection.primary_key(table_name)
|
121
140
|
|
122
|
-
|
123
|
-
|
124
|
-
|
141
|
+
fields = DbSchema.connection[COLUMN_NAMES_QUERY, table_name.to_s].map do |column_data|
|
142
|
+
build_field(column_data, primary_key: column_data[:name] == primary_key_name)
|
143
|
+
end
|
125
144
|
|
126
|
-
|
127
|
-
|
128
|
-
|
145
|
+
indices = indices_data_for(table_name).map do |index_data|
|
146
|
+
Definitions::Index.new(index_data)
|
147
|
+
end.sort_by(&:name)
|
129
148
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
condition: check_data[:condition]
|
134
|
-
)
|
135
|
-
end
|
149
|
+
foreign_keys = DbSchema.connection.foreign_key_list(table_name).map do |foreign_key_data|
|
150
|
+
build_foreign_key(foreign_key_data)
|
151
|
+
end
|
136
152
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
checks: checks,
|
142
|
-
foreign_keys: foreign_keys
|
153
|
+
checks = DbSchema.connection[CONSTRAINTS_QUERY, table_name.to_s].map do |check_data|
|
154
|
+
Definitions::CheckConstraint.new(
|
155
|
+
name: check_data[:name].to_sym,
|
156
|
+
condition: check_data[:condition]
|
143
157
|
)
|
144
158
|
end
|
145
159
|
|
146
|
-
|
160
|
+
Definitions::Table.new(
|
161
|
+
table_name,
|
162
|
+
fields: fields,
|
163
|
+
indices: indices,
|
164
|
+
checks: checks,
|
165
|
+
foreign_keys: foreign_keys
|
166
|
+
)
|
147
167
|
end
|
148
168
|
|
149
169
|
def indices_data_for(table_name)
|
@@ -227,8 +247,12 @@ SELECT extname
|
|
227
247
|
nullable = (data[:null] != 'NO')
|
228
248
|
|
229
249
|
unless primary_key || data[:default].nil?
|
230
|
-
if match = DEFAULT_VALUE.match(data[:default])
|
231
|
-
|
250
|
+
default = if match = DEFAULT_VALUE.match(data[:default])
|
251
|
+
if match[:date]
|
252
|
+
Date.parse(match[:date])
|
253
|
+
elsif match[:time]
|
254
|
+
Time.parse(match[:time])
|
255
|
+
elsif match[:string]
|
232
256
|
match[:string]
|
233
257
|
elsif match[:integer]
|
234
258
|
match[:integer].to_i
|
@@ -236,9 +260,9 @@ SELECT extname
|
|
236
260
|
match[:float].to_f
|
237
261
|
elsif match[:boolean]
|
238
262
|
match[:boolean] == 'true'
|
239
|
-
elsif match[:function]
|
240
|
-
match[:function].to_sym
|
241
263
|
end
|
264
|
+
else
|
265
|
+
data[:default].to_sym
|
242
266
|
end
|
243
267
|
end
|
244
268
|
|
data/lib/db_schema/runner.rb
CHANGED
@@ -133,7 +133,7 @@ module DbSchema
|
|
133
133
|
when Changes::DisallowNull
|
134
134
|
set_column_not_null(element.name)
|
135
135
|
when Changes::AlterColumnDefault
|
136
|
-
set_column_default(element.name, element.new_default)
|
136
|
+
set_column_default(element.name, Runner.default_to_sequel(element.new_default))
|
137
137
|
when Changes::CreateIndex
|
138
138
|
add_index(
|
139
139
|
element.columns_to_sequel,
|
@@ -216,6 +216,20 @@ module DbSchema
|
|
216
216
|
else
|
217
217
|
options
|
218
218
|
end
|
219
|
+
|
220
|
+
if mapping.key?(:default)
|
221
|
+
mapping.merge(default: default_to_sequel(mapping[:default]))
|
222
|
+
else
|
223
|
+
mapping
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def default_to_sequel(default)
|
228
|
+
if default.is_a?(Symbol)
|
229
|
+
Sequel.lit(default.to_s)
|
230
|
+
else
|
231
|
+
default
|
232
|
+
end
|
219
233
|
end
|
220
234
|
end
|
221
235
|
end
|
data/lib/db_schema/validator.rb
CHANGED
@@ -2,10 +2,7 @@ module DbSchema
|
|
2
2
|
module Validator
|
3
3
|
class << self
|
4
4
|
def validate(schema)
|
5
|
-
|
6
|
-
enums = Utils.filter_by_class(schema, Definitions::Enum)
|
7
|
-
|
8
|
-
table_errors = tables.each_with_object([]) do |table, errors|
|
5
|
+
table_errors = schema.tables.each_with_object([]) do |table, errors|
|
9
6
|
primary_keys_count = table.fields.select(&:primary_key?).count
|
10
7
|
if primary_keys_count > 1
|
11
8
|
error_message = %(Table "#{table.name}" has #{primary_keys_count} primary keys)
|
@@ -14,7 +11,7 @@ module DbSchema
|
|
14
11
|
|
15
12
|
table.fields.each do |field|
|
16
13
|
if field.is_a?(Definitions::Field::Custom)
|
17
|
-
unless enums.map(&:name).include?(field.type_name)
|
14
|
+
unless schema.enums.map(&:name).include?(field.type_name)
|
18
15
|
error_message = %(Field "#{table.name}.#{field.name}" has unknown type "#{field.type_name}")
|
19
16
|
errors << error_message
|
20
17
|
end
|
@@ -40,7 +37,7 @@ module DbSchema
|
|
40
37
|
end
|
41
38
|
end
|
42
39
|
|
43
|
-
if referenced_table = schema.find { |table| table.name == fkey.table }
|
40
|
+
if referenced_table = schema.tables.find { |table| table.name == fkey.table }
|
44
41
|
if fkey.references_primary_key?
|
45
42
|
unless referenced_table.fields.any?(&:primary_key?)
|
46
43
|
error_message = %(Foreign key "#{fkey.name}" refers to primary key of table "#{fkey.table}" which does not have a primary key)
|
@@ -63,7 +60,7 @@ module DbSchema
|
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
66
|
-
enum_errors = enums.each_with_object([]) do |enum, errors|
|
63
|
+
enum_errors = schema.enums.each_with_object([]) do |enum, errors|
|
67
64
|
if enum.values.empty?
|
68
65
|
error_message = %(Enum "#{enum.name}" contains no values)
|
69
66
|
errors << error_message
|
data/lib/db_schema/version.rb
CHANGED
data/lib/db_schema.rb
CHANGED
@@ -7,6 +7,7 @@ require 'db_schema/definitions'
|
|
7
7
|
require 'db_schema/awesome_print'
|
8
8
|
require 'db_schema/dsl'
|
9
9
|
require 'db_schema/validator'
|
10
|
+
require 'db_schema/normalizer'
|
10
11
|
require 'db_schema/reader'
|
11
12
|
require 'db_schema/changes'
|
12
13
|
require 'db_schema/runner'
|
@@ -17,6 +18,7 @@ module DbSchema
|
|
17
18
|
def describe(&block)
|
18
19
|
desired_schema = DSL.new(block).schema
|
19
20
|
validate(desired_schema)
|
21
|
+
normalize(desired_schema)
|
20
22
|
|
21
23
|
actual_schema = Reader.read_schema
|
22
24
|
changes = Changes.between(desired_schema, actual_schema)
|
@@ -88,6 +90,18 @@ module DbSchema
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
93
|
+
def normalize(schema)
|
94
|
+
normalized_tables = schema.tables.map do |table|
|
95
|
+
if table.has_expressions?
|
96
|
+
Normalizer.new(table).normalized_table
|
97
|
+
else
|
98
|
+
table
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
schema.tables = normalized_tables
|
103
|
+
end
|
104
|
+
|
91
105
|
def log_changes(changes)
|
92
106
|
return if changes.empty?
|
93
107
|
|
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.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vsevolod Romashov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -214,6 +214,7 @@ files:
|
|
214
214
|
- lib/db_schema/definitions/field/text_search.rb
|
215
215
|
- lib/db_schema/definitions/field/uuid.rb
|
216
216
|
- lib/db_schema/dsl.rb
|
217
|
+
- lib/db_schema/normalizer.rb
|
217
218
|
- lib/db_schema/reader.rb
|
218
219
|
- lib/db_schema/runner.rb
|
219
220
|
- lib/db_schema/utils.rb
|