dynamic_migrations 3.6.15 → 3.6.16

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/lib/dynamic_migrations/active_record/migrators/primary_key.rb +21 -0
  4. data/lib/dynamic_migrations/active_record/migrators.rb +1 -0
  5. data/lib/dynamic_migrations/postgres/generator/primary_key.rb +32 -0
  6. data/lib/dynamic_migrations/postgres/generator/table.rb +20 -4
  7. data/lib/dynamic_migrations/postgres/generator/table_migration.rb +1 -1
  8. data/lib/dynamic_migrations/postgres/generator/validation.rb +0 -4
  9. data/lib/dynamic_migrations/postgres/generator/validation_template_base.rb +5 -3
  10. data/lib/dynamic_migrations/postgres/generator.rb +4 -5
  11. data/lib/dynamic_migrations/postgres/server/database/connection.rb +3 -1
  12. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/functions.rb +1 -1
  13. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/foreign_key_constraints.rb +2 -2
  14. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/indexes.rb +2 -2
  15. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/triggers.rb +2 -2
  16. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/unique_constraints.rb +2 -2
  17. data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/validations.rb +2 -2
  18. data/lib/dynamic_migrations/postgres/server/database/differences.rb +3 -3
  19. data/lib/dynamic_migrations/postgres/server/database/enums_loader.rb +1 -1
  20. data/lib/dynamic_migrations/postgres/server/database/keys_and_unique_constraints_loader.rb +8 -0
  21. data/lib/dynamic_migrations/postgres/server/database/schema/function.rb +50 -1
  22. data/lib/dynamic_migrations/postgres/server/database/schema/table/column.rb +23 -2
  23. data/lib/dynamic_migrations/postgres/server/database/schema/table/trigger.rb +71 -5
  24. data/lib/dynamic_migrations/postgres/server/database/schema/table/validation.rb +57 -2
  25. data/lib/dynamic_migrations/postgres/server/database/structure_loader.rb +8 -0
  26. data/lib/dynamic_migrations/postgres/server/database/triggers_and_functions_loader.rb +4 -3
  27. data/lib/dynamic_migrations/postgres/server/database/validations_loader.rb +42 -29
  28. data/lib/dynamic_migrations/postgres/server/database.rb +6 -0
  29. data/lib/dynamic_migrations/version.rb +1 -1
  30. data/lib/dynamic_migrations.rb +1 -0
  31. data/sig/dynamic_migrations/active_record/migrators/primary_key.rbs +18 -0
  32. data/sig/dynamic_migrations/postgres/generator/primary_key.rbs +2 -0
  33. data/sig/dynamic_migrations/postgres/generator/table.rbs +2 -0
  34. data/sig/dynamic_migrations/postgres/generator/validation_template_base.rbs +7 -7
  35. data/sig/dynamic_migrations/postgres/generator.rbs +1 -1
  36. data/sig/dynamic_migrations/postgres/server/database/connection.rbs +1 -1
  37. data/sig/dynamic_migrations/postgres/server/database/keys_and_unique_constraints_loader.rbs +1 -0
  38. data/sig/dynamic_migrations/postgres/server/database/schema/enum.rbs +2 -2
  39. data/sig/dynamic_migrations/postgres/server/database/schema/enums.rbs +1 -1
  40. data/sig/dynamic_migrations/postgres/server/database/schema/function.rbs +9 -0
  41. data/sig/dynamic_migrations/postgres/server/database/schema/table/column.rbs +2 -0
  42. data/sig/dynamic_migrations/postgres/server/database/schema/table/trigger.rbs +9 -0
  43. data/sig/dynamic_migrations/postgres/server/database/schema/table/validation.rbs +6 -0
  44. data/sig/dynamic_migrations/postgres/server/database/structure_loader.rbs +1 -0
  45. data/sig/dynamic_migrations/postgres/server/database/validations_loader.rbs +1 -0
  46. data/sig/dynamic_migrations/postgres/server/database.rbs +1 -0
  47. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b1f731ee879fc2da123605b8cb10c5dee47ff16a065dd043be4ec4424ceba90
4
- data.tar.gz: ee8925da339476c44a54440240d4736b443ae671075d6f9791033052ef542da3
3
+ metadata.gz: 166ee1f91202640e9ad50fd80c0cb4fade2993ba6ca873ff4bdbf207d939ce81
4
+ data.tar.gz: e23bc180aa98886aecd575a4b869d595d08f4d90136a59e7de46b7f42b6cbe11
5
5
  SHA512:
6
- metadata.gz: 841a16d865fc252e0c7ea85b589d0de86703c1d718ba05fd695852daff12117e6dbbfd7ccb22e7f29659253e8193054dccca635423ccd9ea0dc7875482606cb8
7
- data.tar.gz: 16ffa1cffc44cf979247738466a5884db216042f82b945b4936e0f1beca4d04b4544f71b4912e65a1b2e5581d8ffb5c4b2d288a55198eaf6dfdcad04372b10b6
6
+ metadata.gz: e3b9df7db4f4eed952f7144c52eda7a46447d66cdb83e44fff32f97f335cfb78e03ebd87d0e000f781b8c596a21615ccff085933032c8e2534d900864b242f36
7
+ data.tar.gz: e0ce6d30f2d346dbbb69ae506674aef00dc49eefe04c48d7cf7c8e81e06fea11fe5afb33e7822e87302c1d514d8be5da16070ecbc622829e51bfecc87f650885
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.6.16](https://github.com/craigulliott/dynamic_migrations/compare/v3.6.15...v3.6.16) (2023-09-16)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * adding convenience method to call all three cache rebuild methods at once ([287a73d](https://github.com/craigulliott/dynamic_migrations/commit/287a73d96c4bfa303150e3c1f65cb514569c03b6))
9
+ * allowing functions and triggers to be in different schemas ([6df3377](https://github.com/craigulliott/dynamic_migrations/commit/6df337780a9a6b6815776a1f65afd3c8325a00fd))
10
+ * always sorting column names on validations so that they match the structure loader ([3e7e646](https://github.com/craigulliott/dynamic_migrations/commit/3e7e646e834140bcce74e357e6e84c3996b9525e))
11
+ * corrected bug where we passed nil as trigger for updating trigger comment ([c7a3677](https://github.com/craigulliott/dynamic_migrations/commit/c7a3677694130125d581f8f68e56865644d5c442))
12
+ * enum values should be strings not symbols ([080e5da](https://github.com/craigulliott/dynamic_migrations/commit/080e5daaeefa1504912e2cd4f3482af82890ee08))
13
+ * fixed bug where constraint was duplicated due to pg check_constraints table allowing duplicates ([471b6f8](https://github.com/craigulliott/dynamic_migrations/commit/471b6f82fc01770e63f657a12d01d18011c69f07))
14
+ * fixing whitespace with create table migrator syntax ([5bf1a7f](https://github.com/craigulliott/dynamic_migrations/commit/5bf1a7f9f337883ca677b1a2015dd0c41a27442c))
15
+ * generating materialized views automatically when refreshing them but they don't yet exist ([471b6f8](https://github.com/craigulliott/dynamic_migrations/commit/471b6f82fc01770e63f657a12d01d18011c69f07))
16
+ * handling default comments via templates ([3e7e646](https://github.com/craigulliott/dynamic_migrations/commit/3e7e646e834140bcce74e357e6e84c3996b9525e))
17
+ * methods to refresh caches because this needs to be performed before and after migrations are generated and run ([ab72670](https://github.com/craigulliott/dynamic_migrations/commit/ab72670e5fc7d379aed86cf72be956cdc39ed620))
18
+ * more strictly validating column types data types which use enums ([3e7e646](https://github.com/craigulliott/dynamic_migrations/commit/3e7e646e834140bcce74e357e6e84c3996b9525e))
19
+ * removed code which was stripping empty lines from migrations, but inadvertently removing empty lines from within SQL statements ([3e7e646](https://github.com/craigulliott/dynamic_migrations/commit/3e7e646e834140bcce74e357e6e84c3996b9525e))
20
+ * updating action order so that its sequential based on the event manipulation type (update, insert etc.) ([3e7e646](https://github.com/craigulliott/dynamic_migrations/commit/3e7e646e834140bcce74e357e6e84c3996b9525e))
21
+ * validations should not end with a semicolon ([9e2cc8e](https://github.com/craigulliott/dynamic_migrations/commit/9e2cc8ebf1c6da8c4741c5257c6567fc9f3668a7))
22
+ * various fixes after running a variety of real migrations from platformer ([301db01](https://github.com/craigulliott/dynamic_migrations/commit/301db01397f1cb16c6c0e08e9e8c69e1f3ec799e))
23
+
3
24
  ## [3.6.15](https://github.com/craigulliott/dynamic_migrations/compare/v3.6.14...v3.6.15) (2023-09-14)
4
25
 
5
26
 
@@ -0,0 +1,21 @@
1
+ module DynamicMigrations
2
+ module ActiveRecord
3
+ module Migrators
4
+ module PrimaryKey
5
+ # add a comment to the primary_key
6
+ def set_primary_key_comment table_name, primary_key_name, comment
7
+ execute <<~SQL
8
+ COMMENT ON CONSTRAINT #{primary_key_name} ON #{schema_name}.#{table_name} IS #{quote comment};
9
+ SQL
10
+ end
11
+
12
+ # remove a primary_key comment
13
+ def remove_primary_key_comment table_name, primary_key_name
14
+ execute <<~SQL
15
+ COMMENT ON CONSTRAINT #{primary_key_name} ON #{schema_name}.#{table_name} IS NULL;
16
+ SQL
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -30,6 +30,7 @@ module DynamicMigrations
30
30
  include UniqueConstraint
31
31
  include Trigger
32
32
  include Enum
33
+ include PrimaryKey
33
34
 
34
35
  def self.included(base)
35
36
  base.extend(ClassMethods)
@@ -59,6 +59,38 @@ module DynamicMigrations
59
59
  # return the new fragments (the main reason to return them here is for the specs)
60
60
  [removal_fragment, recreation_fragment]
61
61
  end
62
+
63
+ # add a comment to a primary_key
64
+ def set_primary_key_comment primary_key, code_comment = nil
65
+ description = primary_key.description
66
+
67
+ if description.nil?
68
+ raise MissingDescriptionError, "Missing required description for primary_key `#{primary_key.name}` in table `#{primary_key.table.schema.name}.#{primary_key.table.name}`"
69
+ end
70
+
71
+ add_fragment schema: primary_key.table.schema,
72
+ table: primary_key.table,
73
+ migration_method: :set_primary_key_comment,
74
+ object: primary_key,
75
+ code_comment: code_comment,
76
+ migration: <<~RUBY
77
+ set_primary_key_comment :#{primary_key.table.name}, :#{primary_key.name}, <<~COMMENT
78
+ #{indent description}
79
+ COMMENT
80
+ RUBY
81
+ end
82
+
83
+ # remove the comment from a primary_key
84
+ def remove_primary_key_comment primary_key, code_comment = nil
85
+ add_fragment schema: primary_key.table.schema,
86
+ table: primary_key.table,
87
+ migration_method: :remove_primary_key_comment,
88
+ object: primary_key,
89
+ code_comment: code_comment,
90
+ migration: <<~RUBY
91
+ remove_primary_key_comment :#{primary_key.table.name}, :#{primary_key.name}
92
+ RUBY
93
+ end
62
94
  end
63
95
  end
64
96
  end
@@ -93,10 +93,20 @@ module DynamicMigrations
93
93
  lines = []
94
94
  timestamps = []
95
95
  columns.each do |column|
96
- # skip the :id, as it is handled by the table_options method
97
- next if column.name == :id
98
- # skip the :created_at and :updated_at as they are handled below
96
+ # skip creating the :id column as it is handled by the table_options
97
+ # method, but add the comment if there is one
98
+ if column.name == :id
99
+ unless column.description.nil?
100
+ set_column_comment column
101
+ end
102
+ next
103
+ end
104
+ # skip creating the :created_at and :updated_at column as it is handled
105
+ # by the table_options method, but add the comments
99
106
  if column.name == :created_at || column.name == :updated_at
107
+ unless column.description.nil?
108
+ set_column_comment column
109
+ end
100
110
  timestamps << column.name
101
111
  next
102
112
  end
@@ -115,7 +125,7 @@ module DynamicMigrations
115
125
  if column.description.nil?
116
126
  raise NoTableColumnCommentError, "Refusing to generate create_table migration, no description was provided for `#{column.table.schema.name}`.`#{column.table.name}` column `#{column.name}`"
117
127
  end
118
- options[:comment] = <<~RUBY
128
+ options[:comment] = <<~RUBY.strip
119
129
  <<~COMMENT
120
130
  #{indent column.description}
121
131
  COMMENT
@@ -163,6 +173,12 @@ module DynamicMigrations
163
173
  elsif pk_column_names.count > 1
164
174
  options << "primary_key: [:#{pk_column_names.join(", :")}]"
165
175
  end
176
+
177
+ # if the primary key has a description, then add it seperately
178
+ if table.primary_key.description
179
+ set_primary_key_comment table.primary_key
180
+ end
181
+
166
182
  end
167
183
 
168
184
  options << "comment: table_comment"
@@ -17,7 +17,7 @@ module DynamicMigrations
17
17
  add_structure_template [:remove_table_comment, :set_table_comment], "Tables"
18
18
  add_structure_template [:add_column], "Additional Columns"
19
19
  add_structure_template [:change_column, :remove_column_comment, :set_column_comment], "Update Columns"
20
- add_structure_template [:add_primary_key], "Primary Key"
20
+ add_structure_template [:add_primary_key, :set_primary_key_comment, :remove_primary_key_comment], "Primary Key"
21
21
  add_structure_template [:add_index, :set_index_comment], "Indexes"
22
22
  add_structure_template [:add_foreign_key, :set_foreign_key_constraint_comment, :remove_foreign_key_constraint_comment], "Foreign Keys"
23
23
  add_structure_template [:add_validation, :add_unique_constraint, :set_validation_comment, :remove_validation_comment, :set_unique_constraint_comment, :remove_unique_constraint_comment], "Validations"
@@ -57,10 +57,6 @@ module DynamicMigrations
57
57
  options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
58
58
 
59
59
  validation_sql = validation.check_clause.strip
60
- # ensure that the validation ends with a semicolon
61
- unless validation_sql.end_with? ";"
62
- validation_sql << ";"
63
- end
64
60
 
65
61
  add_fragment schema: validation.table.schema,
66
62
  table: validation.table,
@@ -46,14 +46,16 @@ module DynamicMigrations
46
46
  matches[:value]
47
47
  end
48
48
 
49
- def name_and_description_options_string default_name
49
+ def name_and_description_options_string default_name, default_comment = nil
50
50
  options = {}
51
51
  # we only need to provide a name if it is different than the default
52
52
  unless @validation.name == default_name
53
53
  options[:name] = @validation.name
54
54
  end
55
- # only provide a comment if it is not nil
56
- unless @validation.description.nil?
55
+ # only provide a comment if it is not nil and not equal to the provided
56
+ # default_comment, if it is the same as the default then we wont want to show
57
+ # it in the migration files
58
+ unless @validation.description.nil? || @validation.description == default_comment
57
59
  options[:comment] = <<~RUBY
58
60
  <<~COMMENT
59
61
  #{indent @validation.description || ""}
@@ -243,9 +243,8 @@ module DynamicMigrations
243
243
  # This method is called from within the various modules which are included to this class.
244
244
  # It locally stores all the fragments which will later be organized into different migrations.
245
245
  def add_fragment migration_method:, object:, migration:, schema: nil, table: nil, code_comment: nil, dependent_table: nil, dependent_function: nil, dependent_enum: nil
246
- # Remove any empty lines and whitespace from the beginning or the end of the migration and then
247
- # strip any empty lines witin the migration (remove the whitespace from them, not delete them).
248
- final_migration = strip_empty_lines(migration).strip
246
+ # Remove any empty lines and whitespace from the beginning or the end of the migration
247
+ final_migration = trim_lines migration
249
248
  fragment = Fragment.new(schema&.name, table&.name, migration_method, object.name, code_comment, final_migration)
250
249
 
251
250
  if dependent_table
@@ -272,8 +271,8 @@ module DynamicMigrations
272
271
  multi_line_string.gsub("\n", "\n#{spaces}")
273
272
  end
274
273
 
275
- def strip_empty_lines multi_line_string
276
- multi_line_string.gsub(/^\s*\n/, "")
274
+ def trim_lines string
275
+ string.split("\n").map(&:rstrip).join("\n")
277
276
  end
278
277
  end
279
278
  end
@@ -42,10 +42,12 @@ module DynamicMigrations
42
42
  # perform work with the connection
43
43
  # todo: `yield connection` would have been preferred, but rbs/steep doesnt understand that syntax
44
44
  if block.is_a? Proc
45
- block.call connection
45
+ result = block.call connection
46
46
  end
47
47
  # close the connection
48
48
  disconnect
49
+ # return whever was returned from within the block
50
+ result
49
51
  end
50
52
  end
51
53
  end
@@ -33,7 +33,7 @@ module DynamicMigrations
33
33
 
34
34
  # If the function exists in both the configuration and database representations
35
35
  # but the definition is different then we need to update the definition.
36
- elsif configuration_function[:definition][:matches] == false
36
+ elsif configuration_function[:normalized_definition][:matches] == false
37
37
  function = @database.configured_schema(schema_name).function(function_name)
38
38
  @generator.update_function function
39
39
  # does the description also need to be updated
@@ -44,9 +44,9 @@ module DynamicMigrations
44
44
  if configuration_foreign_key_constraint[:description][:matches] == false
45
45
  # if the description was removed
46
46
  if configuration_foreign_key_constraint[:description].nil?
47
- @generator.remove_foreign_key_constraint_comment foreign_key_constraint
47
+ @generator.remove_foreign_key_constraint_comment updated_foreign_key_constraint
48
48
  else
49
- @generator.set_foreign_key_constraint_comment foreign_key_constraint
49
+ @generator.set_foreign_key_constraint_comment updated_foreign_key_constraint
50
50
  end
51
51
  end
52
52
 
@@ -44,9 +44,9 @@ module DynamicMigrations
44
44
  if configuration_index[:description][:matches] == false
45
45
  # if the description was removed
46
46
  if configuration_index[:description].nil?
47
- @generator.remove_index_comment index
47
+ @generator.remove_index_comment updated_index
48
48
  else
49
- @generator.set_index_comment index
49
+ @generator.set_index_comment updated_index
50
50
  end
51
51
  end
52
52
 
@@ -44,9 +44,9 @@ module DynamicMigrations
44
44
  if configuration_trigger[:description][:matches] == false
45
45
  # if the description was removed
46
46
  if configuration_trigger[:description].nil?
47
- @generator.remove_trigger_comment trigger
47
+ @generator.remove_trigger_comment updated_trigger
48
48
  else
49
- @generator.set_trigger_comment trigger
49
+ @generator.set_trigger_comment updated_trigger
50
50
  end
51
51
  end
52
52
 
@@ -44,9 +44,9 @@ module DynamicMigrations
44
44
  if configuration_unique_constraint[:description][:matches] == false
45
45
  # if the description was removed
46
46
  if configuration_unique_constraint[:description].nil?
47
- @generator.remove_unique_constraint_comment unique_constraint
47
+ @generator.remove_unique_constraint_comment updated_unique_constraint
48
48
  else
49
- @generator.set_unique_constraint_comment unique_constraint
49
+ @generator.set_unique_constraint_comment updated_unique_constraint
50
50
  end
51
51
  end
52
52
 
@@ -44,9 +44,9 @@ module DynamicMigrations
44
44
  if configuration_validation[:description][:matches] == false
45
45
  # if the description was removed
46
46
  if configuration_validation[:description].nil?
47
- @generator.remove_validation_comment validation
47
+ @generator.remove_validation_comment updated_validation
48
48
  else
49
- @generator.set_validation_comment validation
49
+ @generator.set_validation_comment updated_validation
50
50
  end
51
51
  end
52
52
 
@@ -172,7 +172,7 @@ module DynamicMigrations
172
172
  # the base functions
173
173
  functions.each do |function_name, function|
174
174
  result[function_name] = compare_record function, comparison_functions[function_name], [
175
- :definition,
175
+ :normalized_definition,
176
176
  :description
177
177
  ]
178
178
  end
@@ -249,7 +249,7 @@ module DynamicMigrations
249
249
  :action_timing,
250
250
  :event_manipulation,
251
251
  :action_order,
252
- :action_condition,
252
+ :normalized_action_condition,
253
253
  :parameters,
254
254
  :action_orientation,
255
255
  :action_reference_old_table,
@@ -332,7 +332,7 @@ module DynamicMigrations
332
332
  validations.each do |name, validation|
333
333
  # compare this validation to the equivilent in the comparison list
334
334
  result[name] = compare_record validation, comparison_validations[name], [
335
- :check_clause,
335
+ :normalized_check_clause,
336
336
  :column_names,
337
337
  :description,
338
338
  :deferrable,
@@ -22,7 +22,7 @@ module DynamicMigrations
22
22
  rows.each do |row|
23
23
  schema_name = row["schema_name"].to_sym
24
24
  enum_name = row["enum_name"].to_sym
25
- enum_value = row["enum_value"].to_sym
25
+ enum_value = row["enum_value"]
26
26
 
27
27
  schema = schemas[schema_name] ||= {}
28
28
  enum = schema[enum_name] ||= {
@@ -99,6 +99,14 @@ module DynamicMigrations
99
99
  SQL
100
100
  end
101
101
 
102
+ def refresh_database_keys_and_unique_constraints_cache
103
+ connection.exec(<<~SQL)
104
+ REFRESH MATERIALIZED VIEW public.dynamic_migrations_keys_and_unique_constraints_cache
105
+ SQL
106
+ rescue PG::UndefinedTable
107
+ create_database_keys_and_unique_constraints_cache
108
+ end
109
+
102
110
  # fetch all required data from the database and build and return a
103
111
  # useful hash representing the keys and indexes of your database
104
112
  def fetch_keys_and_unique_constraints
@@ -13,6 +13,9 @@ module DynamicMigrations
13
13
  class ExpectedDefinitionError < StandardError
14
14
  end
15
15
 
16
+ class UnnormalizableDefinitionError < StandardError
17
+ end
18
+
16
19
  attr_reader :schema
17
20
  attr_reader :name
18
21
  attr_reader :definition
@@ -59,9 +62,55 @@ module DynamicMigrations
59
62
 
60
63
  def differences_descriptions other_function
61
64
  method_differences_descriptions other_function, [
62
- :definition
65
+ :normalized_definition
63
66
  ]
64
67
  end
68
+
69
+ # temporarily create a function in postgres and fetch the actual
70
+ # normalized definition directly from the database
71
+ def normalized_definition
72
+ # no need to normalize definitions which originated from the database
73
+ if from_database?
74
+ definition
75
+ else
76
+ @normalized_definition ||= fetch_normalized_definition
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def fetch_normalized_definition
83
+ fn_def = schema.database.with_connection do |connection|
84
+ # wrapped in a transaction just in case something here fails, because
85
+ # we don't want the function to be persisted
86
+ connection.exec("BEGIN")
87
+
88
+ # create a temporary function, from which we will fetch the normalized definition
89
+ connection.exec(<<~SQL)
90
+ CREATE OR REPLACE FUNCTION normalized_definition_temp_fn() returns trigger language plpgsql AS
91
+ $$#{definition}$$;
92
+ SQL
93
+
94
+ # get the normalzed version of the definition
95
+ rows = connection.exec(<<~SQL)
96
+ SELECT prosrc AS function_definition
97
+ FROM pg_proc
98
+ WHERE proname = 'normalized_definition_temp_fn';
99
+ SQL
100
+
101
+ # delete the temp table and close the transaction
102
+ connection.exec("ROLLBACK")
103
+
104
+ # return the normalized function definition
105
+ rows.first["function_definition"]
106
+ end
107
+
108
+ if fn_def.nil?
109
+ raise UnnormalizableDefinitionError, "Failed to nomalize action condition `#{definition}`"
110
+ end
111
+
112
+ fn_def
113
+ end
65
114
  end
66
115
  end
67
116
  end
@@ -36,7 +36,11 @@ module DynamicMigrations
36
36
  @data_type = data_type
37
37
 
38
38
  @null = null
39
- @default = default
39
+
40
+ unless default.nil?
41
+ raise ExpectedStringError, default unless default.is_a? String
42
+ @default = default
43
+ end
40
44
 
41
45
  unless description.nil?
42
46
  raise ExpectedStringError, description unless description.is_a? String
@@ -50,7 +54,7 @@ module DynamicMigrations
50
54
  unless enum.is_a? Enum
51
55
  raise UnexpectedEnumError, "#{enum} is not a valid enum"
52
56
  end
53
- unless @data_type == enum.full_name || @data_type == "#{enum.full_name}[]"
57
+ if (array? && @data_type != :"#{enum.full_name}[]") || (!array? && @data_type != enum.full_name)
54
58
  raise UnexpectedEnumError, "enum `#{enum.full_name}` does not match this column's data type `#{@data_type}`"
55
59
  end
56
60
  @enum = enum
@@ -68,6 +72,23 @@ module DynamicMigrations
68
72
  def array?
69
73
  @data_type.end_with? "[]"
70
74
  end
75
+
76
+ def enum?
77
+ !@enum.nil?
78
+ end
79
+
80
+ # sometimes this system makes temporary tables in order to fetch the normalized
81
+ # version of constraint check clauses, function definitions or trigger action conditions
82
+ # because certain data types might not yet exist, we need to use alternative types
83
+ def temp_table_data_type
84
+ if enum
85
+ :text
86
+ elsif @data_type == :citext || @data_type == :"citext[]"
87
+ :text
88
+ else
89
+ @data_type
90
+ end
91
+ end
71
92
  end
72
93
  end
73
94
  end
@@ -38,6 +38,9 @@ module DynamicMigrations
38
38
  class UnexpectedTemplateError < StandardError
39
39
  end
40
40
 
41
+ class UnnormalizableActionConditionError < StandardError
42
+ end
43
+
41
44
  attr_reader :table
42
45
  attr_reader :name
43
46
  attr_reader :event_manipulation
@@ -147,11 +150,11 @@ module DynamicMigrations
147
150
  end
148
151
  action_order
149
152
 
150
- # otherwise return the dynamically calculated action order, this is calculated
151
- # by returning this triggers index in the list of alphabetically sorted triggers
152
- # for this triggers table
153
+ # otherwise is is computed by finding the index of the trigger within a list of
154
+ # triggers that are alphabetically sorted, all of which pertain to the same event
155
+ # manipulation (such as update, insert, etc.) for this triggers table
153
156
  else
154
- pos = @table.triggers.sort_by(&:name).index(self)
157
+ pos = @table.triggers.select { |t| t.event_manipulation == event_manipulation }.sort_by(&:name).index(self)
155
158
  if pos.nil?
156
159
  raise "Trigger not found in table triggers list. This should be impossible."
157
160
  end
@@ -184,7 +187,7 @@ module DynamicMigrations
184
187
  :event_manipulation,
185
188
  :action_timing,
186
189
  :action_order,
187
- :action_condition,
190
+ :normalized_action_condition,
188
191
  :parameters,
189
192
  :action_orientation,
190
193
  :action_reference_old_table,
@@ -197,6 +200,69 @@ module DynamicMigrations
197
200
  # return the combined differences
198
201
  descriptions
199
202
  end
203
+
204
+ # create a temporary table in postgres to represent this validation and fetch
205
+ # the actual normalized check constraint directly from the database
206
+ def normalized_action_condition
207
+ if action_condition.nil?
208
+ nil
209
+ # no need to normalize action_conditions which originated from the database
210
+ elsif from_database?
211
+ action_condition
212
+ else
213
+ ac = table.schema.database.with_connection do |connection|
214
+ # wrapped in a transaction just in case something here fails, because
215
+ # we don't want the function, temporary table or trigger to be persisted
216
+ connection.exec("BEGIN")
217
+
218
+ # create the temp table and add the expected columns and constraint
219
+ connection.exec(<<~SQL)
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
224
+
225
+ # create a temporary function to trigger (triggers require a function)
226
+ connection.exec(<<~SQL)
227
+ CREATE OR REPLACE FUNCTION trigger_normalized_action_condition_temp_fn() returns trigger language plpgsql AS
228
+ $$ BEGIN END $$;
229
+ SQL
230
+
231
+ # create a temporary trigger, from which we will fetch the normalized action condition
232
+ connection.exec(<<~SQL)
233
+ CREATE TRIGGER trigger_normalized_action_condition_temp_trigger
234
+ BEFORE UPDATE ON trigger_normalized_action_condition_temp_table
235
+ FOR EACH ROW
236
+ WHEN (#{action_condition})
237
+ EXECUTE FUNCTION trigger_normalized_action_condition_temp_fn();
238
+ SQL
239
+
240
+ # get the normalzed version of the action condition
241
+ rows = connection.exec(<<~SQL)
242
+ SELECT (
243
+ regexp_match(
244
+ pg_get_triggerdef(oid),
245
+ '.{35,} WHEN ((.+)) EXECUTE FUNCTION')
246
+ )[1] as action_condition
247
+ FROM pg_trigger
248
+ WHERE tgname = 'trigger_normalized_action_condition_temp_trigger'
249
+ ;
250
+ SQL
251
+
252
+ # delete the temp table and close the transaction
253
+ connection.exec("ROLLBACK")
254
+
255
+ # return the normalized action condition
256
+ rows.first["action_condition"]
257
+ end
258
+
259
+ if ac.nil?
260
+ raise UnnormalizableActionConditionError, "Failed to nomalize action condition `#{action_condition}`"
261
+ end
262
+
263
+ ac
264
+ end
265
+ end
200
266
  end
201
267
  end
202
268
  end
@@ -20,6 +20,9 @@ module DynamicMigrations
20
20
  class UnexpectedTemplateError < StandardError
21
21
  end
22
22
 
23
+ class UnnormalizableCheckClauseError < StandardError
24
+ end
25
+
23
26
  attr_reader :table
24
27
  attr_reader :name
25
28
  attr_reader :check_clause
@@ -81,19 +84,71 @@ module DynamicMigrations
81
84
  end
82
85
 
83
86
  def column_names
84
- @columns.keys
87
+ @columns.keys.sort
85
88
  end
86
89
 
87
90
  def differences_descriptions other_validation
88
91
  method_differences_descriptions other_validation, [
89
- :check_clause,
92
+ :normalized_check_clause,
90
93
  :deferrable,
91
94
  :initially_deferred
92
95
  ]
93
96
  end
94
97
 
98
+ # create a temporary table in postgres to represent this validation and fetch
99
+ # the actual normalized check constraint directly from the database
100
+ def normalized_check_clause
101
+ # no need to normalize check_clauses which originated from the database
102
+ if from_database?
103
+ check_clause
104
+ else
105
+ @normalized_check_clause ||= fetch_normalized_check_clause
106
+ end
107
+ end
108
+
95
109
  private
96
110
 
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
114
+ # we don't want the temporary table to be persisted
115
+ connection.exec("BEGIN")
116
+
117
+ # create the temp table and add the expected columns and constraint
118
+ connection.exec(<<~SQL)
119
+ CREATE TEMP TABLE validation_normalized_check_clause_temp_table (
120
+ #{columns.map { |column| '"' + column.name.to_s + '" ' + column.temp_table_data_type.to_s }.join(", ")},
121
+ CONSTRAINT #{name} CHECK (#{check_clause})
122
+ );
123
+ SQL
124
+
125
+ # get the normalzed version of the constraint
126
+ rows = connection.exec(<<~SQL)
127
+ SELECT pg_get_constraintdef(pg_constraint.oid) AS check_clause
128
+ FROM pg_constraint
129
+ WHERE conrelid = 'validation_normalized_check_clause_temp_table'::regclass;
130
+ SQL
131
+
132
+ # delete the temp table and close the transaction
133
+ connection.exec("ROLLBACK")
134
+
135
+ # return the normalized check clause
136
+ rows.first["check_clause"]
137
+ end
138
+
139
+ if ncc.nil?
140
+ raise UnnormalizableCheckClauseError, "Failed to nomalize check clause `#{check_clause}`"
141
+ end
142
+
143
+ # extract the check clause from the result "CHECK(%check_clause%)"
144
+ matches = ncc.match(/\ACHECK \((?<inner_clause>.*)\)\z/)
145
+ if matches.nil?
146
+ raise UnnormalizableCheckClauseError, "Unparsable normalized check_clause #{ncc}"
147
+ end
148
+
149
+ matches[:inner_clause]
150
+ end
151
+
97
152
  # used internally to set the columns from this objects initialize method
98
153
  def add_column column
99
154
  # assert that the provided dsl name is an array of Columns
@@ -66,6 +66,14 @@ module DynamicMigrations
66
66
  SQL
67
67
  end
68
68
 
69
+ def refresh_database_structure_cache
70
+ connection.exec(<<~SQL)
71
+ REFRESH MATERIALIZED VIEW public.dynamic_migrations_structure_cache
72
+ SQL
73
+ rescue PG::UndefinedTable
74
+ create_database_structure_cache
75
+ end
76
+
69
77
  # fetch all columns from the database and build and return a
70
78
  # useful hash representing the structure of your database
71
79
  def fetch_structure
@@ -91,9 +91,10 @@ module DynamicMigrations
91
91
  schema = schemas[event_object_schema] ||= {}
92
92
  table = schema[event_object_table] ||= {}
93
93
 
94
- # by convention (and to simplify things) we place these all in the same schema
95
- unless row["trigger_schema"] == row["function_schema"] && row["function_schema"] == row["event_object_schema"]
96
- raise EventTriggerProcedureSchemaMismatchError, "Expected trigger, procedure and event_object to be in the same schema for trigger '#{trigger_name}'"
94
+ # By convention (and to simplify things) we place the trigger and the triggers table in the same schema
95
+ # The function can be in a different schema (and often is, expecially for shared functions)
96
+ unless row["trigger_schema"] == row["event_object_schema"]
97
+ raise EventTriggerProcedureSchemaMismatchError, "Expected trigger and event_object to be in the same schema for trigger '#{trigger_name}'"
97
98
  end
98
99
 
99
100
  # turn the parameters into an array of strings
@@ -7,36 +7,35 @@ module DynamicMigrations
7
7
  module ValidationsLoader
8
8
  def create_database_validations_cache
9
9
  connection.exec(<<~SQL)
10
- CREATE MATERIALIZED VIEW public.dynamic_migrations_validations_cache as
11
- SELECT table_constraints.table_schema as schema_name,
12
- table_constraints.table_name,
13
- array_agg(col.column_name ORDER BY col.column_name) AS columns,
14
- table_constraints.constraint_name as validation_name,
15
- pg_get_expr(conbin, conrelid, true) as check_clause,
16
- obj_description(pgc.oid, 'pg_constraint') as description,
10
+ CREATE MATERIALIZED VIEW public.dynamic_migrations_validations_cache AS
11
+ SELECT
12
+ nspname AS schema_name,
13
+ pg_constraint_class.relname AS table_name,
14
+ array_agg(columns.column_name ORDER BY columns.column_name) AS columns,
15
+ pg_get_constraintdef(pg_constraint.oid) AS check_clause,
16
+ conname AS validation_name,
17
+ obj_description(pg_constraint.oid, 'pg_constraint') AS description,
17
18
  -- in case we need to update this query in a later version of DynamicMigrations
18
19
  1 as table_version
19
- FROM information_schema.table_constraints
20
- JOIN information_schema.check_constraints
21
- ON table_constraints.constraint_schema = check_constraints.constraint_schema
22
- AND table_constraints.constraint_name = check_constraints.constraint_name
23
- JOIN pg_namespace nsp ON nsp.nspname = check_constraints.constraint_schema
24
- JOIN pg_constraint pgc ON pgc.conname = check_constraints.constraint_name
25
- AND pgc.connamespace = nsp.oid
26
- AND pgc.contype = 'c'
27
- JOIN information_schema.columns col
28
- ON col.table_schema = table_constraints.table_schema
29
- AND col.table_name = table_constraints.table_name
30
- AND col.ordinal_position = ANY(pgc.conkey)
31
- WHERE table_constraints.constraint_schema != 'information_schema'
32
- AND table_constraints.constraint_schema != 'postgis'
33
- AND left(table_constraints.constraint_schema, 3) != 'pg_'
34
- GROUP BY
35
- pgc.oid,
36
- table_constraints.table_schema,
37
- table_constraints.table_name,
38
- table_constraints.constraint_name,
39
- check_constraints.check_clause;
20
+ FROM pg_catalog.pg_constraint
21
+ INNER JOIN pg_catalog.pg_class pg_constraint_class
22
+ ON pg_constraint_class.oid = pg_constraint.conrelid
23
+ INNER JOIN pg_catalog.pg_namespace pg_constraint_namespace
24
+ ON pg_constraint_namespace.oid = connamespace
25
+ JOIN information_schema.columns
26
+ ON columns.table_schema = nspname
27
+ AND columns.table_name = pg_constraint_class.relname
28
+ AND columns.ordinal_position = ANY(pg_constraint.conkey)
29
+ WHERE
30
+ contype = 'c'
31
+ AND nspname != 'information_schema'
32
+ AND nspname != 'postgis'
33
+ AND left(nspname, 3) != 'pg_'
34
+ GROUP BY
35
+ pg_constraint.oid,
36
+ nspname,
37
+ pg_constraint_class.relname,
38
+ conname;
40
39
  SQL
41
40
  connection.exec(<<~SQL)
42
41
  CREATE UNIQUE INDEX dynamic_migrations_validations_cache_index ON public.dynamic_migrations_validations_cache (schema_name, table_name, validation_name);
@@ -46,6 +45,14 @@ module DynamicMigrations
46
45
  SQL
47
46
  end
48
47
 
48
+ def refresh_database_validations_cache
49
+ connection.exec(<<~SQL)
50
+ REFRESH MATERIALIZED VIEW public.dynamic_migrations_validations_cache
51
+ SQL
52
+ rescue PG::UndefinedTable
53
+ create_database_validations_cache
54
+ end
55
+
49
56
  # fetch all columns from the database and build and return a
50
57
  # useful hash representing the validations of your database
51
58
  def fetch_validations
@@ -70,9 +77,15 @@ module DynamicMigrations
70
77
 
71
78
  validation_name = row["validation_name"].to_sym
72
79
 
80
+ matches = row["check_clause"].match(/\ACHECK \((?<inner_clause>.*)\)\z/)
81
+ if matches.nil?
82
+ raise StandardError, "Unparsable check_clause #{row["check_clause"]}"
83
+ end
84
+ check_clause = matches[:inner_clause]
85
+
73
86
  table[validation_name] = {
74
87
  columns: row["columns"].gsub(/\A\{/, "").gsub(/\}\Z/, "").split(",").map { |column_name| column_name.to_sym },
75
- check_clause: row["check_clause"],
88
+ check_clause: check_clause,
76
89
  description: row["description"],
77
90
  deferrable: row["deferrable"] == "TRUE",
78
91
  initially_deferred: row["initially_deferred"] == "TRUE"
@@ -55,6 +55,12 @@ module DynamicMigrations
55
55
  def differences
56
56
  Differences.new(self)
57
57
  end
58
+
59
+ def refresh_caches
60
+ refresh_database_structure_cache
61
+ refresh_database_keys_and_unique_constraints_cache
62
+ refresh_database_validations_cache
63
+ end
58
64
  end
59
65
  end
60
66
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamicMigrations
4
- VERSION = "3.6.15"
4
+ VERSION = "3.6.16"
5
5
  end
@@ -95,6 +95,7 @@ require "dynamic_migrations/active_record/migrators/trigger"
95
95
  require "dynamic_migrations/active_record/migrators/table"
96
96
  require "dynamic_migrations/active_record/migrators/index"
97
97
  require "dynamic_migrations/active_record/migrators/enum"
98
+ require "dynamic_migrations/active_record/migrators/primary_key"
98
99
  require "dynamic_migrations/active_record/migrators/column"
99
100
  require "dynamic_migrations/active_record/migrators"
100
101
 
@@ -0,0 +1,18 @@
1
+ # TypeProf 0.21.7
2
+
3
+ # Classes
4
+ module DynamicMigrations
5
+ module ActiveRecord
6
+ module Migrators
7
+ module PrimaryKey
8
+ def set_primary_key_comment: (Symbol table_name, Symbol primary_key_name, String comment) -> void
9
+ def remove_primary_key_comment: (Symbol table_name, Symbol primary_key_name) -> void
10
+
11
+ # stubbing these out, as they are available on the module which includes this module
12
+ def execute: (String sql) -> void
13
+ def schema_name: () -> Symbol
14
+ def quote: (String str) -> String
15
+ end
16
+ end
17
+ end
18
+ end
@@ -8,6 +8,8 @@ module DynamicMigrations
8
8
  def add_primary_key: (Postgres::Server::Database::Schema::Table::PrimaryKey primary_key, ?String? code_comment) -> Fragment
9
9
  def remove_primary_key: (Postgres::Server::Database::Schema::Table::PrimaryKey primary_key, ?String? code_comment) -> Fragment
10
10
  def recreate_primary_key: (Postgres::Server::Database::Schema::Table::PrimaryKey original_primary_key, Postgres::Server::Database::Schema::Table::PrimaryKey updated_primary_key) -> Array[Fragment]
11
+ def set_primary_key_comment: (Postgres::Server::Database::Schema::Table::PrimaryKey primary_key, ?String? code_comment) -> Fragment
12
+ def remove_primary_key_comment: (Postgres::Server::Database::Schema::Table::PrimaryKey primary_key, ?String? code_comment) -> Fragment
11
13
 
12
14
  # these come from the generator object (which this module is included into)
13
15
  def add_fragment: (migration_method: Symbol, object: untyped, migration: String, ?schema: Server::Database::Schema?, ?table: Server::Database::Schema::Table?, ?code_comment: String?, ?dependent_table: Server::Database::Schema::Table?, ?dependent_function: Server::Database::Schema::Function?, ?dependent_enum: Server::Database::Schema::Enum?) -> Fragment
@@ -13,6 +13,8 @@ module DynamicMigrations
13
13
  # these come from the generator object (which this module is included into)
14
14
  def add_fragment: (migration_method: Symbol, object: untyped, migration: String, ?schema: Server::Database::Schema?, ?table: Server::Database::Schema::Table?, ?code_comment: String?, ?dependent_table: Server::Database::Schema::Table?, ?dependent_function: Server::Database::Schema::Function?, ?dependent_enum: Server::Database::Schema::Enum?) -> Fragment
15
15
  def add_column: (Postgres::Server::Database::Schema::Table::Column column, ?String? code_comment) -> Fragment
16
+ def set_column_comment: (Postgres::Server::Database::Schema::Table::Column column, ?String? code_comment) -> Fragment
17
+ def set_primary_key_comment: (Postgres::Server::Database::Schema::Table::PrimaryKey primary_key, ?String? code_comment) -> Fragment
16
18
 
17
19
  def indent: (String migration, ?Integer levels) -> String
18
20
 
@@ -6,20 +6,20 @@ module DynamicMigrations
6
6
  class Generator
7
7
  class ValidationTemplateBase
8
8
 
9
- attr_reader validation: untyped
9
+ attr_reader validation: Postgres::Server::Database::Schema::Table::Validation
10
10
  attr_reader code_comment: String?
11
11
 
12
- def initialize: (untyped validation, untyped code_comment) -> void
12
+ def initialize: (Postgres::Server::Database::Schema::Table::Validation validation, String? code_comment) -> void
13
13
 
14
14
  # abstract method (should actually be added to child classes)
15
15
  def fragment_arguments: -> {schema: Postgres::Server::Database::Schema, table: Postgres::Server::Database::Schema::Table, migration_method: Symbol, object: untyped, code_comment: String?, migration: String, dependent_function: Postgres::Server::Database::Schema::Function?}
16
16
 
17
17
  private
18
- def assert_not_deferred!: -> nil
19
- def assert_column_count!: (?Integer count) -> nil
20
- def first_column: -> untyped
21
- def value_from_check_clause: (untyped regex) -> untyped
22
- def name_and_description_options_string: (untyped default_name) -> String?
18
+ def assert_not_deferred!: -> void
19
+ def assert_column_count!: (?Integer count) -> void
20
+ def first_column: -> Postgres::Server::Database::Schema::Table::Column
21
+ def value_from_check_clause: (Regexp regex) -> untyped
22
+ def name_and_description_options_string: (Symbol default_name, ?String? default_comment) -> String?
23
23
  def indent: (String multi_line_string, ?Integer levels) -> String
24
24
 
25
25
  class TemplateError < StandardError
@@ -31,9 +31,9 @@ module DynamicMigrations
31
31
  def supported_migration_method?: (Symbol migration_method) -> bool
32
32
  def add_fragment: (migration_method: Symbol, object: untyped, migration: String, ?schema: Server::Database::Schema?, ?table: Server::Database::Schema::Table?, ?code_comment: String?, ?dependent_table: Server::Database::Schema::Table?, ?dependent_function: Server::Database::Schema::Function?, ?dependent_enum: Server::Database::Schema::Enum?) -> Fragment
33
33
  def indent: (String migration, ?Integer levels) -> String
34
- def strip_empty_lines: (String migration) -> String
35
34
  def tsort_each_node: -> Enumerator[untyped, untyped]
36
35
  def tsort_each_child: (untyped node) -> untyped
36
+ def trim_lines: (String migration) -> String
37
37
 
38
38
  class ExpectedSymbolError < StandardError
39
39
  end
@@ -8,7 +8,7 @@ module DynamicMigrations
8
8
  def connect: -> PG::Connection
9
9
  def connection: -> PG::Connection
10
10
  def disconnect: -> void
11
- def with_connection: -> void
11
+ def with_connection: -> untyped
12
12
 
13
13
  # these come from the database object (which this module is included into)
14
14
  def name: -> Symbol
@@ -5,6 +5,7 @@ module DynamicMigrations
5
5
  module KeysAndUniqueConstraintsLoader
6
6
  def create_database_keys_and_unique_constraints_cache: -> void
7
7
  def fetch_keys_and_unique_constraints: -> Hash[untyped, untyped]
8
+ def refresh_database_keys_and_unique_constraints_cache: -> void
8
9
 
9
10
  # these come from the database object (which this module is included into)
10
11
  def connection: -> PG::Connection
@@ -9,10 +9,10 @@ module DynamicMigrations
9
9
  class Enum < Source
10
10
  attr_reader schema: Schema
11
11
  attr_reader name: Symbol
12
- attr_reader values: Array[Symbol]
12
+ attr_reader values: Array[String]
13
13
  attr_reader description: String?
14
14
  attr_reader columns: Array[Table::Column]
15
- def initialize: (database_or_configuration source, Schema schema, Symbol name, Array[Symbol] values, ?description: String?) -> void
15
+ def initialize: (database_or_configuration source, Schema schema, Symbol name, Array[String] values, ?description: String?) -> void
16
16
  def full_name: -> Symbol
17
17
  def has_description?: -> bool
18
18
  def add_column: (Schema::Table::Column column) -> void
@@ -9,7 +9,7 @@ module DynamicMigrations
9
9
  module Enums
10
10
  @enums: Hash[Symbol, Enum]
11
11
 
12
- def add_enum: (Symbol enum_name, Array[Symbol] values, ?description: String?) -> nil
12
+ def add_enum: (Symbol enum_name, Array[String] values, ?description: String?) -> nil
13
13
  def enum: (Symbol enum_name) -> Enum
14
14
  def has_enum?: (Symbol enum_name) -> bool
15
15
  def enums: -> Array[Enum]
@@ -7,6 +7,8 @@ module DynamicMigrations
7
7
  class Database
8
8
  class Schema
9
9
  class Function < Source
10
+ @normalized_definition: String?
11
+
10
12
  attr_reader schema: Schema
11
13
  attr_reader name: Symbol
12
14
  attr_reader definition: String
@@ -16,12 +18,19 @@ module DynamicMigrations
16
18
  def has_description?: -> bool
17
19
  def add_trigger: (Schema::Table::Trigger trigger) -> void
18
20
  def differences_descriptions: (Function other_function) -> Array[String]
21
+ def normalized_definition: -> String
22
+
23
+ private
24
+ def fetch_normalized_definition: -> String
19
25
 
20
26
  class ExpectedSchemaError < StandardError
21
27
  end
22
28
 
23
29
  class ExpectedDefinitionError < StandardError
24
30
  end
31
+
32
+ class UnnormalizableDefinitionError < StandardError
33
+ end
25
34
  end
26
35
  end
27
36
  end
@@ -18,6 +18,8 @@ module DynamicMigrations
18
18
  def initialize: (database_or_configuration source, Table table, Symbol name, Symbol data_type, ?null: bool, ?default: untyped, ?description: String?, ?interval_type: Symbol?, ?enum: Enum?) -> void
19
19
  def has_description?: -> bool
20
20
  def array?: -> bool
21
+ def enum?: -> bool
22
+ def temp_table_data_type: -> Symbol
21
23
 
22
24
  class ExpectedTableError < StandardError
23
25
  end
@@ -9,6 +9,8 @@ module DynamicMigrations
9
9
  class Table
10
10
  class Trigger < Source
11
11
  @action_order: Integer?
12
+ @normalized_action_condition: String?
13
+
12
14
  attr_reader table: Table
13
15
  attr_reader name: Symbol
14
16
  attr_reader event_manipulation: Symbol
@@ -26,6 +28,10 @@ module DynamicMigrations
26
28
  def action_order: -> Integer
27
29
  def has_description?: -> false
28
30
  def differences_descriptions: (Trigger other_trigger) -> Array[String]
31
+ def normalized_action_condition: -> String?
32
+
33
+ private
34
+ def fetch_normalized_action_condition: -> String
29
35
 
30
36
  class ExpectedTableError < StandardError
31
37
  end
@@ -56,6 +62,9 @@ module DynamicMigrations
56
62
 
57
63
  class UnexpectedTemplateError < StandardError
58
64
  end
65
+
66
+ class UnnormalizableActionConditionError < StandardError
67
+ end
59
68
  end
60
69
  end
61
70
  end
@@ -6,6 +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
10
 
10
11
  attr_reader table: Table
11
12
  attr_reader name: Symbol
@@ -20,8 +21,11 @@ module DynamicMigrations
20
21
  def column_names: -> Array[Symbol]
21
22
  def has_description?: -> bool
22
23
  def differences_descriptions: (Validation other_validation) -> Array[String]
24
+ def normalized_check_clause: -> String
23
25
 
24
26
  private
27
+ def fetch_normalized_check_clause: -> String
28
+
25
29
  def add_column: (Column column) -> void
26
30
 
27
31
  class ExpectedTableError < StandardError
@@ -39,6 +43,8 @@ module DynamicMigrations
39
43
  class UnexpectedTemplateError < StandardError
40
44
  end
41
45
 
46
+ class UnnormalizableCheckClauseError < StandardError
47
+ end
42
48
  end
43
49
  end
44
50
  end
@@ -8,6 +8,7 @@ module DynamicMigrations
8
8
  def recursively_load_database_structure: -> void
9
9
  def fetch_schema_names: -> Array[String]
10
10
  def fetch_table_names: (Symbol schema_name) -> Array[String]
11
+ def refresh_database_structure_cache: -> void
11
12
 
12
13
  # these come from the database object (which this module is included into)
13
14
  def connection: -> PG::Connection
@@ -5,6 +5,7 @@ module DynamicMigrations
5
5
  module ValidationsLoader
6
6
  def create_database_validations_cache: -> void
7
7
  def fetch_validations: -> Hash[untyped, untyped]
8
+ def refresh_database_validations_cache: -> void
8
9
 
9
10
  # these come from the database object (which this module is included into)
10
11
  def connection: -> PG::Connection
@@ -25,6 +25,7 @@ module DynamicMigrations
25
25
  def initialize: (Server server, Symbol name) -> void
26
26
  def schema: (Symbol schema_name, database_or_configuration source) -> untyped
27
27
  def differences: -> Differences
28
+ def refresh_caches: -> void
28
29
 
29
30
  class ExpectedServerError < StandardError
30
31
  end
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.15
4
+ version: 3.6.16
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-14 00:00:00.000000000 Z
11
+ date: 2023-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -71,6 +71,7 @@ files:
71
71
  - lib/dynamic_migrations/active_record/migrators/foreign_key_constraint.rb
72
72
  - lib/dynamic_migrations/active_record/migrators/function.rb
73
73
  - lib/dynamic_migrations/active_record/migrators/index.rb
74
+ - lib/dynamic_migrations/active_record/migrators/primary_key.rb
74
75
  - lib/dynamic_migrations/active_record/migrators/table.rb
75
76
  - lib/dynamic_migrations/active_record/migrators/trigger.rb
76
77
  - lib/dynamic_migrations/active_record/migrators/unique_constraint.rb
@@ -162,6 +163,7 @@ files:
162
163
  - sig/dynamic_migrations/active_record/migrators/foreign_key_constraint.rbs
163
164
  - sig/dynamic_migrations/active_record/migrators/function.rbs
164
165
  - sig/dynamic_migrations/active_record/migrators/index.rbs
166
+ - sig/dynamic_migrations/active_record/migrators/primary_key.rbs
165
167
  - sig/dynamic_migrations/active_record/migrators/table.rbs
166
168
  - sig/dynamic_migrations/active_record/migrators/trigger.rbs
167
169
  - sig/dynamic_migrations/active_record/migrators/unique_constraint.rbs