dynamic_migrations 3.6.15 → 3.6.16

Sign up to get free protection for your applications and to get access to all the features.
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