dynamic_migrations 3.8.4 → 3.8.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/enum.rb +3 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/column.rb +0 -13
- data/lib/dynamic_migrations/postgres/server/database/schema/table/columns.rb +4 -2
- data/lib/dynamic_migrations/postgres/server/database/schema/table/trigger.rb +12 -8
- data/lib/dynamic_migrations/postgres/server/database/schema/table/validation.rb +31 -25
- data/lib/dynamic_migrations/postgres/server/database/schema/table.rb +50 -0
- data/lib/dynamic_migrations/version.rb +1 -1
- data/sig/dynamic_migrations/postgres/server/database/schema/table/column.rbs +0 -1
- data/sig/dynamic_migrations/postgres/server/database/schema/table/columns.rbs +1 -0
- data/sig/dynamic_migrations/postgres/server/database/schema/table.rbs +1 -1
- 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: 0237e61916864ca25f8f7ba1c8882ee7cd8ba687eb2ead948f7dc602cc11e58d
|
4
|
+
data.tar.gz: 8e883bcf30a9b2bf549b7e52062e1cd0576aecaedbe24eb20314522b7e6a6ac8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c026e79bd2af5e73e7cc994fa0a3a28ed480c99f79e151377ae1f01a4953696875d9b3b3df8e53e8e4b6d0e463cadb784ceeed49e0e37b6b6fe9929261f030c5
|
7
|
+
data.tar.gz: 4de30c0cdc9586519da3790fd073eaa4e039214fff6448a064b1fc603a694f3264885ffa3258f542f588e22523173eec55b26c9596753d03a72bbebc5145e7a6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.8.5](https://github.com/craigulliott/dynamic_migrations/compare/v3.8.4...v3.8.5) (2023-10-08)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* missing error class ([761e478](https://github.com/craigulliott/dynamic_migrations/commit/761e478fbd9234014751d853da3dde35d2043a7e))
|
9
|
+
* normalized validation check constraint and trigger action condition now work with enum columns ([a657dd6](https://github.com/craigulliott/dynamic_migrations/commit/a657dd66f3bb3e140672db89224b0b562bb91ef0))
|
10
|
+
|
3
11
|
## [3.8.4](https://github.com/craigulliott/dynamic_migrations/compare/v3.8.3...v3.8.4) (2023-10-06)
|
4
12
|
|
5
13
|
|
@@ -82,19 +82,6 @@ module DynamicMigrations
|
|
82
82
|
def base_data_type
|
83
83
|
array? ? @data_type[0..-3]&.to_sym : @data_type
|
84
84
|
end
|
85
|
-
|
86
|
-
# sometimes this system makes temporary tables in order to fetch the normalized
|
87
|
-
# version of constraint check clauses, function definitions or trigger action conditions
|
88
|
-
# because certain data types might not yet exist, we need to use alternative types
|
89
|
-
def temp_table_data_type
|
90
|
-
if enum
|
91
|
-
:text
|
92
|
-
elsif @data_type == :citext || @data_type == :"citext[]"
|
93
|
-
:text
|
94
|
-
else
|
95
|
-
@data_type
|
96
|
-
end
|
97
|
-
end
|
98
85
|
end
|
99
86
|
end
|
100
87
|
end
|
@@ -18,7 +18,9 @@ module DynamicMigrations
|
|
18
18
|
# error if the column does not exist
|
19
19
|
def column name
|
20
20
|
raise ExpectedSymbolError, name unless name.is_a? Symbol
|
21
|
-
|
21
|
+
unless has_column? name
|
22
|
+
raise ColumnDoesNotExistError, "column `#{name}` does not exist within table `#{self.name}`"
|
23
|
+
end
|
22
24
|
@columns[name]
|
23
25
|
end
|
24
26
|
|
@@ -40,7 +42,7 @@ module DynamicMigrations
|
|
40
42
|
# adds a new column to this table, and returns it
|
41
43
|
def add_column name, data_type, **column_options
|
42
44
|
if has_column? name
|
43
|
-
raise
|
45
|
+
raise DuplicateColumnError, "Column `#{name}` already exists"
|
44
46
|
end
|
45
47
|
included_target = self
|
46
48
|
if included_target.is_a? Table
|
@@ -202,7 +202,7 @@ module DynamicMigrations
|
|
202
202
|
end
|
203
203
|
|
204
204
|
# create a temporary table in postgres to represent this trigger and fetch
|
205
|
-
# the actual normalized
|
205
|
+
# the actual normalized action_condition directly from the database
|
206
206
|
def normalized_action_condition
|
207
207
|
if action_condition.nil?
|
208
208
|
nil
|
@@ -215,12 +215,8 @@ module DynamicMigrations
|
|
215
215
|
# we don't want the function, temporary table or trigger to be persisted
|
216
216
|
connection.exec("BEGIN")
|
217
217
|
|
218
|
-
# create the temp table and add the expected columns
|
219
|
-
|
220
|
-
CREATE TEMP TABLE trigger_normalized_action_condition_temp_table (
|
221
|
-
#{table.columns.map { |column| '"' + column.name.to_s + '" ' + column.temp_table_data_type.to_s }.join(", ")}
|
222
|
-
);
|
223
|
-
SQL
|
218
|
+
# create the temp table and add the expected columns
|
219
|
+
temp_enums = table.create_temp_table(connection, "trigger_normalized_action_condition_temp_table")
|
224
220
|
|
225
221
|
# create a temporary function to trigger (triggers require a function)
|
226
222
|
connection.exec(<<~SQL)
|
@@ -253,7 +249,15 @@ module DynamicMigrations
|
|
253
249
|
connection.exec("ROLLBACK")
|
254
250
|
|
255
251
|
# return the normalized action condition
|
256
|
-
rows.first["action_condition"]
|
252
|
+
action_condition_result = rows.first["action_condition"]
|
253
|
+
|
254
|
+
# string replace any enum names with their real enum names
|
255
|
+
temp_enums.each do |temp_enum_name, enum|
|
256
|
+
real_enum_name = (enum.schema == table.schema) ? enum.name : enum.full_name
|
257
|
+
action_condition_result.gsub!("::#{temp_enum_name}", "::#{real_enum_name}")
|
258
|
+
end
|
259
|
+
|
260
|
+
action_condition_result
|
257
261
|
end
|
258
262
|
|
259
263
|
if ac.nil?
|
@@ -128,17 +128,15 @@ module DynamicMigrations
|
|
128
128
|
if table.columns.empty?
|
129
129
|
raise ExpectedTableColumnsError, "Can not normalize check clause or validation columnns because the table has no columns"
|
130
130
|
end
|
131
|
-
|
132
|
-
# wrapped in a transaction
|
133
|
-
# we don't want the temporary table to be persisted
|
131
|
+
table.schema.database.with_connection do |connection|
|
132
|
+
# wrapped in a transaction so we can rollback the creation of the table and any enums
|
134
133
|
connection.exec("BEGIN")
|
135
134
|
|
136
|
-
|
135
|
+
temp_enums = table.create_temp_table(connection, "validation_normalized_check_clause_temp_table")
|
136
|
+
|
137
137
|
connection.exec(<<~SQL)
|
138
|
-
|
139
|
-
#{
|
140
|
-
CONSTRAINT #{name} CHECK (#{check_clause})
|
141
|
-
);
|
138
|
+
ALTER TABLE validation_normalized_check_clause_temp_table
|
139
|
+
ADD CONSTRAINT #{name} CHECK (#{check_clause})
|
142
140
|
SQL
|
143
141
|
|
144
142
|
# get the normalized version of the constraint
|
@@ -158,29 +156,37 @@ module DynamicMigrations
|
|
158
156
|
GROUP BY pg_constraint.oid;
|
159
157
|
SQL
|
160
158
|
|
161
|
-
# delete the
|
159
|
+
# delete the table and any temporary enums
|
162
160
|
connection.exec("ROLLBACK")
|
163
161
|
|
164
|
-
rows.first
|
165
|
-
|
162
|
+
check_clause_result = rows.first["check_clause"]
|
163
|
+
column_names_string = rows.first["column_names"]
|
166
164
|
|
167
|
-
|
168
|
-
|
169
|
-
|
165
|
+
if check_clause_result.nil?
|
166
|
+
raise UnnormalizableCheckClauseError, "Failed to nomalize check clause `#{check_clause_result}`"
|
167
|
+
end
|
170
168
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
169
|
+
# extract the check clause from the result "CHECK(%check_clause%)"
|
170
|
+
matches = check_clause_result.match(/\ACHECK \((?<inner_clause>.*)\)\z/)
|
171
|
+
if matches.nil?
|
172
|
+
raise UnnormalizableCheckClauseError, "Unparsable normalized check_clause #{check_clause_result}"
|
173
|
+
end
|
174
|
+
check_clause_result = matches[:inner_clause]
|
175
|
+
|
176
|
+
# string replace any enum names with their real enum names
|
177
|
+
temp_enums.each do |temp_enum_name, enum|
|
178
|
+
real_enum_name = (enum.schema == table.schema) ? enum.name : enum.full_name
|
179
|
+
check_clause_result.gsub!("::#{temp_enum_name}", "::#{real_enum_name}")
|
180
|
+
end
|
176
181
|
|
177
|
-
|
182
|
+
column_names_result = column_names_string.gsub(/\A\{/, "").gsub(/\}\Z/, "").split(",").map { |column_name| column_name.to_sym }
|
178
183
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
+
# return the normalized check clause
|
185
|
+
{
|
186
|
+
check_clause: check_clause_result,
|
187
|
+
column_names: column_names_result
|
188
|
+
}
|
189
|
+
end
|
184
190
|
end
|
185
191
|
|
186
192
|
# used internally to set the columns from this objects initialize method
|
@@ -78,6 +78,56 @@ module DynamicMigrations
|
|
78
78
|
end
|
79
79
|
pk
|
80
80
|
end
|
81
|
+
|
82
|
+
# Used within validations and triggers when normalizing check clauses and other
|
83
|
+
# SQL statements which require a table to process the SQL.
|
84
|
+
#
|
85
|
+
# This method returns a hash representation of any temporary enums created to satisfy
|
86
|
+
# the columns in the table
|
87
|
+
def create_temp_table connection, temp_table_name
|
88
|
+
# create the temp table and add the expected columns
|
89
|
+
|
90
|
+
# if any of the columns are enums, then we need to create a temporary enum type for them.
|
91
|
+
# we cant just create temporary columns as text fields because postgres may automatically
|
92
|
+
# add casts to those columns, which would result in a different normalized check clause
|
93
|
+
temp_enums = {}
|
94
|
+
|
95
|
+
# an array of sql column definitions for within the create table SQL
|
96
|
+
# we process each column individually like this so that we can create temporary enums for
|
97
|
+
# any enum columns
|
98
|
+
columns_sql = columns.map do |column|
|
99
|
+
enum = column.enum
|
100
|
+
if enum
|
101
|
+
# create the temporary enum type
|
102
|
+
temp_enum_name = "#{temp_table_name}_enum_#{temp_enums.count}"
|
103
|
+
connection.exec(<<~SQL)
|
104
|
+
CREATE TYPE #{temp_enum_name} as ENUM ('#{enum.values.join("','")}');
|
105
|
+
SQL
|
106
|
+
temp_enums[temp_enum_name] = enum
|
107
|
+
|
108
|
+
# return the column definition used within the CREATE TABLE SQL
|
109
|
+
data_type = column.array? ? "#{temp_enum_name}[]" : temp_enum_name
|
110
|
+
"\"#{column.name}\" #{data_type}"
|
111
|
+
|
112
|
+
else
|
113
|
+
# return the column definition used within the CREATE TABLE SQL
|
114
|
+
"\"#{column.name}\" #{column.data_type}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# in case any of the columnbs are citext columns
|
119
|
+
connection.exec("CREATE EXTENSION IF NOT EXISTS citext;")
|
120
|
+
|
121
|
+
# note, this is not actually a TEMP TABLE, it is created within a transaction
|
122
|
+
# and rolled back.
|
123
|
+
connection.exec(<<~SQL)
|
124
|
+
CREATE TABLE #{temp_table_name} (
|
125
|
+
#{columns_sql.join(", ")}
|
126
|
+
);
|
127
|
+
SQL
|
128
|
+
|
129
|
+
temp_enums
|
130
|
+
end
|
81
131
|
end
|
82
132
|
end
|
83
133
|
end
|
@@ -19,7 +19,6 @@ module DynamicMigrations
|
|
19
19
|
def has_description?: -> bool
|
20
20
|
def array?: -> bool
|
21
21
|
def enum?: -> bool
|
22
|
-
def temp_table_data_type: -> Symbol
|
23
22
|
# untyped because we cant specify this logic in rbs yet (compiler is concerned this might be nil)
|
24
23
|
def base_data_type: -> untyped
|
25
24
|
|
@@ -22,7 +22,7 @@ module DynamicMigrations
|
|
22
22
|
def add_primary_key: (Symbol name, Array[Symbol] column_names, **untyped) -> untyped
|
23
23
|
def has_primary_key?: -> bool
|
24
24
|
def primary_key: -> PrimaryKey
|
25
|
-
|
25
|
+
def create_temp_table: (PG::Connection connection, String table_name) -> Hash[String, Enum]
|
26
26
|
class ExpectedSchemaError < StandardError
|
27
27
|
end
|
28
28
|
|
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.8.
|
4
|
+
version: 3.8.5
|
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-10-
|
11
|
+
date: 2023-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|