dynamic_migrations 3.6.16 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/function.rb +1 -1
- data/lib/dynamic_migrations/postgres/server/database/schema/table/column.rb +6 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraint.rb +4 -7
- data/lib/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraints.rb +5 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/trigger.rb +2 -2
- data/lib/dynamic_migrations/postgres/server/database/schema/table/validation.rb +52 -24
- data/lib/dynamic_migrations/postgres/server/database/schema/table/validations.rb +1 -1
- data/lib/dynamic_migrations/postgres/server/database/schema/table.rb +2 -0
- data/lib/dynamic_migrations/postgres/server/database/structure_loader.rb +1 -1
- data/lib/dynamic_migrations/version.rb +1 -1
- data/sig/dynamic_migrations/postgres/server/database/schema/table/column.rbs +2 -0
- data/sig/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraint.rbs +0 -3
- data/sig/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraints.rbs +1 -0
- data/sig/dynamic_migrations/postgres/server/database/schema/table/validation.rbs +5 -3
- data/sig/dynamic_migrations/postgres/server/database/schema/table/validations.rbs +1 -1
- data/sig/dynamic_migrations/postgres/server/database/schema/table.rbs +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22fd29f59924e820da26858d1cf6caf04b03985ecc2fafd5f49205f5a0c2b34e
|
4
|
+
data.tar.gz: a8164cec22385ede8954df04e86c866bdb9d1ae4d420ba9107353f96e1a525e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec4eb2339d1ce110393eae8747c9e2aabee426d58a4e3caca8e01b7dd48ddfe99291ab903c9c7ba2e474a8b509a7185850132c1934f512c8ebe710e763243ba9
|
7
|
+
data.tar.gz: 50076bbed1405f301476ac49f0886207f9fde401c47af062fafa4095139cd3287699b8054cac4583b97422180f8fb3be935d94ac87b1ccd071a293972811eec7
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.7.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.6.16...v3.7.0) (2023-09-27)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* providing access to foreign key constraints from both sides of the association ([48dcf1c](https://github.com/craigulliott/dynamic_migrations/commit/48dcf1cd4cdb23bc37da3e47b00f8007b8bc0f8a))
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* allowing foreign keys to the same table because they are valid and sometimes useful ([0566384](https://github.com/craigulliott/dynamic_migrations/commit/0566384a4cfeade757d9806059035477f7c41ee2))
|
14
|
+
* lazy loading column names for validations when they were configured with a nil value for columns ([439200e](https://github.com/craigulliott/dynamic_migrations/commit/439200efdedf74dddc852bf9bd35e81c1f6b4336))
|
15
|
+
* providing convenience method to retrieve columns base data type from array columns ([cd3d9bf](https://github.com/craigulliott/dynamic_migrations/commit/cd3d9bf06682335a890d9b11133d397d7bcd50af))
|
16
|
+
* semi colon at the end of function definitions is now optional ([ca9b3aa](https://github.com/craigulliott/dynamic_migrations/commit/ca9b3aa04da23605ff61b2ec14baed927145d2c3))
|
17
|
+
* structure loader was not identifying enums properly ([95276d3](https://github.com/craigulliott/dynamic_migrations/commit/95276d33f98f41eca9a9467bb6abeb458a37f16b))
|
18
|
+
|
3
19
|
## [3.6.16](https://github.com/craigulliott/dynamic_migrations/compare/v3.6.15...v3.6.16) (2023-09-16)
|
4
20
|
|
5
21
|
|
@@ -34,7 +34,7 @@ module DynamicMigrations
|
|
34
34
|
raise ExpectedSymbolError, name unless name.is_a? Symbol
|
35
35
|
@name = name
|
36
36
|
|
37
|
-
unless definition.is_a?(String) && definition.strip != "" && definition.strip.end_with?("END;")
|
37
|
+
unless definition.is_a?(String) && definition.strip != "" && definition.strip.end_with?("END;", "END")
|
38
38
|
raise ExpectedDefinitionError, "Definition must be a string, and end with `END;`. Definition provided:\n#{definition}"
|
39
39
|
end
|
40
40
|
@definition = definition.strip
|
@@ -77,6 +77,12 @@ module DynamicMigrations
|
|
77
77
|
!@enum.nil?
|
78
78
|
end
|
79
79
|
|
80
|
+
# for arrays returns the column type without the array brackets, for non arrays
|
81
|
+
# jsut returnms the column type
|
82
|
+
def base_data_type
|
83
|
+
array? ? @data_type[0..-3]&.to_sym : @data_type
|
84
|
+
end
|
85
|
+
|
80
86
|
# sometimes this system makes temporary tables in order to fetch the normalized
|
81
87
|
# version of constraint check clauses, function definitions or trigger action conditions
|
82
88
|
# because certain data types might not yet exist, we need to use alternative types
|
@@ -14,9 +14,6 @@ module DynamicMigrations
|
|
14
14
|
class ExpectedArrayOfColumnsError < StandardError
|
15
15
|
end
|
16
16
|
|
17
|
-
class ExpectedDifferentTablesError < StandardError
|
18
|
-
end
|
19
|
-
|
20
17
|
class DuplicateColumnError < StandardError
|
21
18
|
end
|
22
19
|
|
@@ -49,14 +46,14 @@ module DynamicMigrations
|
|
49
46
|
raise ExpectedArrayOfColumnsError
|
50
47
|
end
|
51
48
|
|
52
|
-
if table.name == foreign_table.name && table.schema.name == foreign_table.schema.name
|
53
|
-
raise ExpectedDifferentTablesError
|
54
|
-
end
|
55
|
-
|
56
49
|
# tables must be set before the columns are added
|
57
50
|
@table = table
|
58
51
|
@foreign_table = foreign_table
|
59
52
|
|
53
|
+
# add this foreign_key_constraint to the remote table (so we can always find
|
54
|
+
# these from both sides of the association)
|
55
|
+
@foreign_table.add_remote_foreign_key_constraint self
|
56
|
+
|
60
57
|
@columns = {}
|
61
58
|
columns.each do |column|
|
62
59
|
add_column column
|
data/lib/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraints.rb
CHANGED
@@ -61,6 +61,11 @@ module DynamicMigrations
|
|
61
61
|
# return the new foreign_key_constraint
|
62
62
|
new_foreign_key_constraint
|
63
63
|
end
|
64
|
+
|
65
|
+
# called automatically from the other side of the foreign key constraint to keep track of the foreign key from both sides
|
66
|
+
def add_remote_foreign_key_constraint foreign_key_constraint
|
67
|
+
@remote_foreign_key_constraints << foreign_key_constraint
|
68
|
+
end
|
64
69
|
end
|
65
70
|
end
|
66
71
|
end
|
@@ -54,7 +54,7 @@ module DynamicMigrations
|
|
54
54
|
attr_reader :description
|
55
55
|
attr_reader :template
|
56
56
|
|
57
|
-
# initialize a new object to represent a
|
57
|
+
# initialize a new object to represent a trigger in a postgres table
|
58
58
|
def initialize source, table, name, action_timing:, event_manipulation:, parameters:, action_orientation:, function:, action_order: nil, action_condition: nil, action_reference_old_table: nil, action_reference_new_table: nil, description: nil, template: nil
|
59
59
|
super source
|
60
60
|
|
@@ -201,7 +201,7 @@ module DynamicMigrations
|
|
201
201
|
descriptions
|
202
202
|
end
|
203
203
|
|
204
|
-
# create a temporary table in postgres to represent this
|
204
|
+
# create a temporary table in postgres to represent this trigger and fetch
|
205
205
|
# the actual normalized check constraint directly from the database
|
206
206
|
def normalized_action_condition
|
207
207
|
if action_condition.nil?
|
@@ -37,22 +37,25 @@ module DynamicMigrations
|
|
37
37
|
raise ExpectedTableError, table unless table.is_a? Table
|
38
38
|
@table = table
|
39
39
|
|
40
|
-
# assert that the provided columns is an array
|
41
|
-
unless columns.is_a?(Array) && columns.count > 0
|
42
|
-
raise ExpectedArrayOfColumnsError
|
43
|
-
end
|
44
|
-
|
45
|
-
@columns = {}
|
46
|
-
columns.each do |column|
|
47
|
-
add_column column
|
48
|
-
end
|
49
|
-
|
50
40
|
raise ExpectedSymbolError, name unless name.is_a? Symbol
|
51
41
|
@name = name
|
52
42
|
|
53
43
|
raise ExpectedStringError, check_clause unless check_clause.is_a? String
|
54
44
|
@check_clause = check_clause.strip
|
55
45
|
|
46
|
+
# if this validation is created via configuration (apposed to being loaded) then they can be lazy loaded
|
47
|
+
unless from_configuration? && columns.nil?
|
48
|
+
# assert that the provided columns is an array
|
49
|
+
unless columns.is_a?(Array) && columns.count > 0
|
50
|
+
raise ExpectedArrayOfColumnsError
|
51
|
+
end
|
52
|
+
|
53
|
+
@columns = {}
|
54
|
+
columns.each do |column|
|
55
|
+
add_column column
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
56
59
|
unless description.nil?
|
57
60
|
raise ExpectedStringError, description unless description.is_a? String
|
58
61
|
@description = description.strip
|
@@ -80,11 +83,17 @@ module DynamicMigrations
|
|
80
83
|
|
81
84
|
# return an array of this validations columns
|
82
85
|
def columns
|
86
|
+
if @columns.nil?
|
87
|
+
@columns = {}
|
88
|
+
normalized_check_clause_and_column_names[:column_names].each do |column_name|
|
89
|
+
add_column table.column(column_name)
|
90
|
+
end
|
91
|
+
end
|
83
92
|
@columns.values
|
84
93
|
end
|
85
94
|
|
86
95
|
def column_names
|
87
|
-
|
96
|
+
columns.map(&:name)
|
88
97
|
end
|
89
98
|
|
90
99
|
def differences_descriptions other_validation
|
@@ -102,15 +111,19 @@ module DynamicMigrations
|
|
102
111
|
if from_database?
|
103
112
|
check_clause
|
104
113
|
else
|
105
|
-
|
114
|
+
normalized_check_clause_and_column_names[:check_clause]
|
106
115
|
end
|
107
116
|
end
|
108
117
|
|
109
118
|
private
|
110
119
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
120
|
+
def normalized_check_clause_and_column_names
|
121
|
+
@normalized_check_clause_and_column_names ||= fetch_normalized_check_clause_and_column_names
|
122
|
+
end
|
123
|
+
|
124
|
+
def fetch_normalized_check_clause_and_column_names
|
125
|
+
result = table.schema.database.with_connection do |connection|
|
126
|
+
# wrapped in a transaction just in case something here fails, because
|
114
127
|
# we don't want the temporary table to be persisted
|
115
128
|
connection.exec("BEGIN")
|
116
129
|
|
@@ -122,31 +135,46 @@ module DynamicMigrations
|
|
122
135
|
);
|
123
136
|
SQL
|
124
137
|
|
125
|
-
# get the
|
138
|
+
# get the normalized version of the constraint
|
126
139
|
rows = connection.exec(<<~SQL)
|
127
|
-
SELECT
|
140
|
+
SELECT
|
141
|
+
pg_get_constraintdef(pg_constraint.oid) AS check_clause,
|
142
|
+
ARRAY_AGG(col.attname ORDER BY u.attposition) AS column_names
|
128
143
|
FROM pg_constraint
|
129
|
-
|
144
|
+
LEFT JOIN LATERAL UNNEST(pg_constraint.conkey)
|
145
|
+
WITH ORDINALITY AS u(attnum, attposition)
|
146
|
+
ON TRUE
|
147
|
+
LEFT JOIN pg_attribute col
|
148
|
+
ON
|
149
|
+
(col.attrelid = pg_constraint.conrelid
|
150
|
+
AND col.attnum = u.attnum)
|
151
|
+
WHERE conrelid = 'validation_normalized_check_clause_temp_table'::regclass
|
152
|
+
GROUP BY pg_constraint.oid;
|
130
153
|
SQL
|
131
154
|
|
132
155
|
# delete the temp table and close the transaction
|
133
156
|
connection.exec("ROLLBACK")
|
134
157
|
|
135
|
-
|
136
|
-
rows.first["check_clause"]
|
158
|
+
rows.first
|
137
159
|
end
|
138
160
|
|
139
|
-
if
|
161
|
+
if result["check_clause"].nil?
|
140
162
|
raise UnnormalizableCheckClauseError, "Failed to nomalize check clause `#{check_clause}`"
|
141
163
|
end
|
142
164
|
|
143
165
|
# extract the check clause from the result "CHECK(%check_clause%)"
|
144
|
-
matches =
|
166
|
+
matches = result["check_clause"].match(/\ACHECK \((?<inner_clause>.*)\)\z/)
|
145
167
|
if matches.nil?
|
146
|
-
raise UnnormalizableCheckClauseError, "Unparsable normalized check_clause #{
|
168
|
+
raise UnnormalizableCheckClauseError, "Unparsable normalized check_clause #{result["check_clause"]}"
|
147
169
|
end
|
148
170
|
|
149
|
-
|
171
|
+
normalized_column_names = result["column_names"].gsub(/\A\{/, "").gsub(/\}\Z/, "").split(",").map { |column_name| column_name.to_sym }
|
172
|
+
|
173
|
+
# return the normalized check clause
|
174
|
+
{
|
175
|
+
check_clause: matches[:inner_clause],
|
176
|
+
column_names: normalized_column_names
|
177
|
+
}
|
150
178
|
end
|
151
179
|
|
152
180
|
# used internally to set the columns from this objects initialize method
|
@@ -42,7 +42,7 @@ module DynamicMigrations
|
|
42
42
|
if has_validation? name
|
43
43
|
raise(ValidationAlreadyExistsError, "Validation #{name} already exists")
|
44
44
|
end
|
45
|
-
columns = column_names
|
45
|
+
columns = column_names&.map { |column_name| column column_name }
|
46
46
|
included_target = self
|
47
47
|
if included_target.is_a? Table
|
48
48
|
new_validation = @validations[name] = Validation.new source, included_target, columns, name, check_clause, **validation_options
|
@@ -26,6 +26,7 @@ module DynamicMigrations
|
|
26
26
|
attr_reader :schema
|
27
27
|
attr_reader :name
|
28
28
|
attr_reader :description
|
29
|
+
attr_reader :remote_foreign_key_constraints
|
29
30
|
|
30
31
|
# initialize a new object to represent a postgres table
|
31
32
|
def initialize source, schema, name, description: nil
|
@@ -47,6 +48,7 @@ module DynamicMigrations
|
|
47
48
|
@validations = {}
|
48
49
|
@indexes = {}
|
49
50
|
@foreign_key_constraints = {}
|
51
|
+
@remote_foreign_key_constraints = []
|
50
52
|
@triggers = {}
|
51
53
|
@unique_constraints = {}
|
52
54
|
end
|
@@ -108,7 +108,7 @@ module DynamicMigrations
|
|
108
108
|
|
109
109
|
column[:data_type] = row["data_type"].to_sym
|
110
110
|
column[:null] = row["is_nullable"] == "YES"
|
111
|
-
column[:is_enum] = row["is_enum"] == "
|
111
|
+
column[:is_enum] = row["is_enum"] == "t"
|
112
112
|
column[:default] = row["column_default"]
|
113
113
|
column[:description] = row["column_description"]
|
114
114
|
column[:interval_type] = row["interval_type"].nil? ? nil : row["interval_type"].to_sym
|
@@ -20,6 +20,8 @@ module DynamicMigrations
|
|
20
20
|
def array?: -> bool
|
21
21
|
def enum?: -> bool
|
22
22
|
def temp_table_data_type: -> Symbol
|
23
|
+
# untyped because we cant specify this logic in rbs yet (compiler is concerned this might be nil)
|
24
|
+
def base_data_type: -> untyped
|
23
25
|
|
24
26
|
class ExpectedTableError < StandardError
|
25
27
|
end
|
data/sig/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraints.rbs
CHANGED
@@ -12,6 +12,7 @@ module DynamicMigrations
|
|
12
12
|
def foreign_key_constraints: -> Array[ForeignKeyConstraint]
|
13
13
|
def foreign_key_constraints_hash: -> Hash[Symbol, ForeignKeyConstraint]
|
14
14
|
def add_foreign_key_constraint: (Symbol name, Array[Symbol] column_names, Symbol foreign_schema_name, Symbol foreign_table_name, Array[Symbol] foreign_column_names, **untyped) -> untyped
|
15
|
+
def add_remote_foreign_key_constraint: (ForeignKeyConstraint foreign_key_constraint) -> untyped
|
15
16
|
|
16
17
|
# these come from the table object (which this module is included into)
|
17
18
|
def source: -> database_or_configuration
|
@@ -6,7 +6,7 @@ module DynamicMigrations
|
|
6
6
|
class Table
|
7
7
|
class Validation < Source
|
8
8
|
@columns: Hash[Symbol, Column]
|
9
|
-
@
|
9
|
+
@normalized_check_clause_and_column_names: {check_clause: String, column_names: Array[Symbol]}?
|
10
10
|
|
11
11
|
attr_reader table: Table
|
12
12
|
attr_reader name: Symbol
|
@@ -16,7 +16,7 @@ module DynamicMigrations
|
|
16
16
|
attr_reader description: String?
|
17
17
|
attr_reader template: Symbol?
|
18
18
|
|
19
|
-
def initialize: (database_or_configuration source, Table table, Array[Column] columns, Symbol name, String check_clause, ?deferrable: bool, ?initially_deferred: bool, ?description: String?, ?template: Symbol?) -> void
|
19
|
+
def initialize: (database_or_configuration source, Table table, Array[Column]? columns, Symbol name, String check_clause, ?deferrable: bool, ?initially_deferred: bool, ?description: String?, ?template: Symbol?) -> void
|
20
20
|
def columns: -> Array[Column]
|
21
21
|
def column_names: -> Array[Symbol]
|
22
22
|
def has_description?: -> bool
|
@@ -24,7 +24,9 @@ module DynamicMigrations
|
|
24
24
|
def normalized_check_clause: -> String
|
25
25
|
|
26
26
|
private
|
27
|
-
|
27
|
+
|
28
|
+
def normalized_check_clause_and_column_names: -> {check_clause: String, column_names: Array[Symbol]}
|
29
|
+
def fetch_normalized_check_clause_and_column_names: -> {check_clause: String, column_names: Array[Symbol]}
|
28
30
|
|
29
31
|
def add_column: (Column column) -> void
|
30
32
|
|
@@ -11,7 +11,7 @@ module DynamicMigrations
|
|
11
11
|
def has_validation?: (Symbol name) -> bool
|
12
12
|
def validations: -> Array[Validation]
|
13
13
|
def validations_hash: -> Hash[Symbol, Validation]
|
14
|
-
def add_validation: (Symbol name, Array[Symbol] column_names, String check_clause, **untyped) -> untyped
|
14
|
+
def add_validation: (Symbol name, Array[Symbol]? column_names, String check_clause, **untyped) -> untyped
|
15
15
|
|
16
16
|
# these come from the table object (which this module is included into)
|
17
17
|
def source: -> database_or_configuration
|
@@ -16,6 +16,7 @@ module DynamicMigrations
|
|
16
16
|
attr_reader schema: Schema
|
17
17
|
attr_reader name: Symbol
|
18
18
|
attr_reader description: String?
|
19
|
+
attr_reader remote_foreign_key_constraints: Array[ForeignKeyConstraint]
|
19
20
|
def initialize: (database_or_configuration source, Schema schema, Symbol name, ?description: String?) -> void
|
20
21
|
def has_description?: -> bool
|
21
22
|
def add_primary_key: (Symbol name, Array[Symbol] column_names, **untyped) -> untyped
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamic_migrations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Craig Ulliott
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|