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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 166ee1f91202640e9ad50fd80c0cb4fade2993ba6ca873ff4bdbf207d939ce81
4
- data.tar.gz: e23bc180aa98886aecd575a4b869d595d08f4d90136a59e7de46b7f42b6cbe11
3
+ metadata.gz: 22fd29f59924e820da26858d1cf6caf04b03985ecc2fafd5f49205f5a0c2b34e
4
+ data.tar.gz: a8164cec22385ede8954df04e86c866bdb9d1ae4d420ba9107353f96e1a525e8
5
5
  SHA512:
6
- metadata.gz: e3b9df7db4f4eed952f7144c52eda7a46447d66cdb83e44fff32f97f335cfb78e03ebd87d0e000f781b8c596a21615ccff085933032c8e2534d900864b242f36
7
- data.tar.gz: e0ce6d30f2d346dbbb69ae506674aef00dc49eefe04c48d7cf7c8e81e06fea11fe5afb33e7822e87302c1d514d8be5da16070ecbc622829e51bfecc87f650885
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
@@ -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 validation in a postgres table
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 validation and fetch
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
- @columns.keys.sort
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
- @normalized_check_clause ||= fetch_normalized_check_clause
114
+ normalized_check_clause_and_column_names[:check_clause]
106
115
  end
107
116
  end
108
117
 
109
118
  private
110
119
 
111
- def fetch_normalized_check_clause
112
- ncc = table.schema.database.with_connection do |connection|
113
- # wrapped in a transaction jsut in case something here fails, because
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 normalzed version of the constraint
138
+ # get the normalized version of the constraint
126
139
  rows = connection.exec(<<~SQL)
127
- SELECT pg_get_constraintdef(pg_constraint.oid) AS check_clause
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
- WHERE conrelid = 'validation_normalized_check_clause_temp_table'::regclass;
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
- # return the normalized check clause
136
- rows.first["check_clause"]
158
+ rows.first
137
159
  end
138
160
 
139
- if ncc.nil?
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 = ncc.match(/\ACHECK \((?<inner_clause>.*)\)\z/)
166
+ matches = result["check_clause"].match(/\ACHECK \((?<inner_clause>.*)\)\z/)
145
167
  if matches.nil?
146
- raise UnnormalizableCheckClauseError, "Unparsable normalized check_clause #{ncc}"
168
+ raise UnnormalizableCheckClauseError, "Unparsable normalized check_clause #{result["check_clause"]}"
147
169
  end
148
170
 
149
- matches[:inner_clause]
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.map { |column_name| column column_name }
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"] == "TRUE"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamicMigrations
4
- VERSION = "3.6.16"
4
+ VERSION = "3.7.0"
5
5
  end
@@ -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
@@ -38,9 +38,6 @@ module DynamicMigrations
38
38
  class ExpectedArrayOfColumnsError < StandardError
39
39
  end
40
40
 
41
- class ExpectedDifferentTablesError < StandardError
42
- end
43
-
44
41
  class DuplicateColumnError < StandardError
45
42
  end
46
43
 
@@ -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
- @normalized_check_clause: String?
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
- def fetch_normalized_check_clause: -> String
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.6.16
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-16 00:00:00.000000000 Z
11
+ date: 2023-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg