dynamic_migrations 3.7.0 → 3.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 22fd29f59924e820da26858d1cf6caf04b03985ecc2fafd5f49205f5a0c2b34e
4
- data.tar.gz: a8164cec22385ede8954df04e86c866bdb9d1ae4d420ba9107353f96e1a525e8
3
+ metadata.gz: 437a84189a939e185f89db2de8395bc1fb8dc9f159b3948a97dcc5b04c4fedb7
4
+ data.tar.gz: 6e1df320ac06c0b0ca9d6aeb3abf684e47b9daac4ee1def93180de801f73125b
5
5
  SHA512:
6
- metadata.gz: ec4eb2339d1ce110393eae8747c9e2aabee426d58a4e3caca8e01b7dd48ddfe99291ab903c9c7ba2e474a8b509a7185850132c1934f512c8ebe710e763243ba9
7
- data.tar.gz: 50076bbed1405f301476ac49f0886207f9fde401c47af062fafa4095139cd3287699b8054cac4583b97422180f8fb3be935d94ac87b1ccd071a293972811eec7
6
+ metadata.gz: c2ce93a8d273abfb0c2ede80b7564216a8409bcd8a43b8fe6c85043eed83d750f8a2b2e53996afdc241e13db9f6cdf2c3f52034a5e93965663684bc121237b54
7
+ data.tar.gz: bb3378cc475e8e3c16dd2d3a9a5754484da539d3ba1feb4839d2312c1f24cb147ea11534ba3a9bfbcda827bee4bc5ae18cc2c7a2b23bdd67c84a23503fc4fcab
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.8.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.7.0...v3.8.0) (2023-10-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * configuration option to skip removing unused extensions ([5d27e36](https://github.com/craigulliott/dynamic_migrations/commit/5d27e36363dcec552ed015a196fbcf660e6b038c))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * adding a setting to allow customizing where the schema where materialized view structure caches are created ([f3a81f8](https://github.com/craigulliott/dynamic_migrations/commit/f3a81f850dacc40ee434de77bb1d3f3c14ea0872))
14
+ * handling arrays of enums properly ([5d27e36](https://github.com/craigulliott/dynamic_migrations/commit/5d27e36363dcec552ed015a196fbcf660e6b038c))
15
+ * skipping views when loading database tables ([5d27e36](https://github.com/craigulliott/dynamic_migrations/commit/5d27e36363dcec552ed015a196fbcf660e6b038c))
16
+
3
17
  ## [3.7.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.6.16...v3.7.0) (2023-09-27)
4
18
 
5
19
 
@@ -18,7 +18,9 @@ module DynamicMigrations
18
18
  # then we need to delete it
19
19
  elsif database_extension[:exists] == true && !configuration_extension[:exists]
20
20
  # a migration to drop the extension
21
- @generator.disable_extension extension_name
21
+ if Postgres.remove_unused_extensions?
22
+ @generator.disable_extension extension_name
23
+ end
22
24
  end
23
25
  end
24
26
  end
@@ -7,7 +7,7 @@ module DynamicMigrations
7
7
  module KeysAndUniqueConstraintsLoader
8
8
  def create_database_keys_and_unique_constraints_cache
9
9
  connection.exec(<<~SQL)
10
- CREATE MATERIALIZED VIEW public.dynamic_migrations_keys_and_unique_constraints_cache as
10
+ CREATE MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_keys_and_unique_constraints_cache as
11
11
  SELECT
12
12
  c.conname AS constraint_name,
13
13
  pg_get_constraintdef(c.oid, true) as constraint_definition,
@@ -92,16 +92,16 @@ module DynamicMigrations
92
92
  ORDER BY schema_name, table_name;
93
93
  SQL
94
94
  connection.exec(<<~SQL)
95
- CREATE UNIQUE INDEX dynamic_migrations_keys_and_unique_constraints_cache_index ON public.dynamic_migrations_keys_and_unique_constraints_cache (schema_name, table_name, constraint_name);
95
+ CREATE UNIQUE INDEX dynamic_migrations_keys_and_unique_constraints_cache_index ON #{Postgres.cache_schema_name}.dynamic_migrations_keys_and_unique_constraints_cache (schema_name, table_name, constraint_name);
96
96
  SQL
97
97
  connection.exec(<<~SQL)
98
- COMMENT ON MATERIALIZED VIEW public.dynamic_migrations_keys_and_unique_constraints_cache IS 'A cached representation of the database constraints. This is used by the dynamic migrations library and is created automatically and updated automatically after migrations have run.';
98
+ COMMENT ON MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_keys_and_unique_constraints_cache IS 'A cached representation of the database constraints. This is used by the dynamic migrations library and is created automatically and updated automatically after migrations have run.';
99
99
  SQL
100
100
  end
101
101
 
102
102
  def refresh_database_keys_and_unique_constraints_cache
103
103
  connection.exec(<<~SQL)
104
- REFRESH MATERIALIZED VIEW public.dynamic_migrations_keys_and_unique_constraints_cache
104
+ REFRESH MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_keys_and_unique_constraints_cache
105
105
  SQL
106
106
  rescue PG::UndefinedTable
107
107
  create_database_keys_and_unique_constraints_cache
@@ -112,12 +112,12 @@ module DynamicMigrations
112
112
  def fetch_keys_and_unique_constraints
113
113
  begin
114
114
  rows = connection.exec(<<~SQL)
115
- SELECT * FROM public.dynamic_migrations_keys_and_unique_constraints_cache
115
+ SELECT * FROM #{Postgres.cache_schema_name}.dynamic_migrations_keys_and_unique_constraints_cache
116
116
  SQL
117
117
  rescue PG::UndefinedTable
118
118
  create_database_keys_and_unique_constraints_cache
119
119
  rows = connection.exec(<<~SQL)
120
- SELECT * FROM public.dynamic_migrations_keys_and_unique_constraints_cache
120
+ SELECT * FROM #{Postgres.cache_schema_name}.dynamic_migrations_keys_and_unique_constraints_cache
121
121
  SQL
122
122
  end
123
123
 
@@ -42,7 +42,9 @@ module DynamicMigrations
42
42
  # add each table column
43
43
  table_definition[:columns].each do |column_name, column_definition|
44
44
  if column_definition[:is_enum]
45
- enum_schema, enum_name = column_definition[:data_type].to_s.split(".")
45
+ data_type = column_definition[:data_type].to_s
46
+ enum_full_name = column_definition[:is_array] ? data_type[0..-3] : data_type
47
+ enum_schema, enum_name = enum_full_name.split(".")
46
48
  enum = table.schema.database.loaded_schema(enum_schema.to_sym).enum(enum_name.to_sym)
47
49
  else
48
50
  enum = nil
@@ -37,7 +37,9 @@ module DynamicMigrations
37
37
  # return a enum by its name, raises an error if the enum does not exist
38
38
  def enum enum_name
39
39
  raise ExpectedSymbolError, enum_name unless enum_name.is_a? Symbol
40
- raise EnumDoesNotExistError unless has_enum? enum_name
40
+ unless has_enum? enum_name
41
+ raise EnumDoesNotExistError, "Enum `#{enum_name}` does not exist"
42
+ end
41
43
  @enums[enum_name]
42
44
  end
43
45
 
@@ -7,12 +7,14 @@ module DynamicMigrations
7
7
  module StructureLoader
8
8
  def create_database_structure_cache
9
9
  connection.exec(<<~SQL)
10
- CREATE MATERIALIZED VIEW public.dynamic_migrations_structure_cache AS
10
+ CREATE MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_structure_cache AS
11
11
  SELECT
12
12
  -- Name of the schema containing the table
13
13
  schemata.schema_name,
14
14
  -- Name of the table
15
15
  tables.table_name,
16
+ -- is this a real table or a view
17
+ tables.table_type,
16
18
  -- The comment which has been added to the table (if any)
17
19
  table_description.description AS table_description,
18
20
  -- Name of the column
@@ -24,6 +26,8 @@ module DynamicMigrations
24
26
  -- YES if the column is possibly nullable, NO if
25
27
  -- it is known not nullable
26
28
  columns.is_nullable,
29
+ -- Is this column an array
30
+ columns.data_type = 'ARRAY' AS is_array,
27
31
  -- The formatted data type (such as integer, char(5) or numeric(12,2)[])
28
32
  CASE
29
33
  WHEN tables.table_name IS NOT NULL THEN
@@ -34,12 +38,26 @@ module DynamicMigrations
34
38
  )
35
39
  END AS data_type,
36
40
  -- is this an emum
37
- EXISTS (
38
- SELECT 1
39
- FROM pg_type typ
40
- INNER JOIN pg_enum enu ON typ.oid = enu.enumtypid
41
- WHERE typ.typname = columns.udt_name
42
- ) AS is_enum,
41
+ CASE
42
+ WHEN columns.data_type = 'ARRAY' OR columns.data_type = 'USER-DEFINED' THEN
43
+ (
44
+ SELECT EXISTS (
45
+ SELECT 1
46
+ FROM pg_type
47
+ INNER JOIN pg_enum
48
+ ON pg_type.oid = pg_enum.enumtypid
49
+ INNER JOIN pg_namespace
50
+ ON pg_namespace.oid = pg_type.typnamespace
51
+ WHERE
52
+ -- when the column is an array, the udt_name is the name of the enum prefixed with an underscore
53
+ (columns.data_type = 'ARRAY' AND concat('_', pg_type.typname) = columns.udt_name AND pg_namespace.nspname = columns.udt_schema)
54
+ -- when the column is not an array, the udt_name is the same name as the enum
55
+ OR (columns.data_type = 'USER-DEFINED' AND pg_type.typname = columns.udt_name AND pg_namespace.nspname = columns.udt_schema)
56
+ )
57
+ )
58
+ ELSE FALSE
59
+ END
60
+ AS is_enum,
43
61
  -- If data_type identifies an interval type, this column contains
44
62
  -- the specification which fields the intervals include for this
45
63
  -- column, e.g., YEAR TO MONTH, DAY TO SECOND, etc. If no field
@@ -55,20 +73,24 @@ module DynamicMigrations
55
73
  LEFT JOIN pg_catalog.pg_description table_description ON table_description.objoid = pg_statio_all_tables.relid AND table_description.objsubid = 0
56
74
  -- required for the column description/comment
57
75
  LEFT JOIN pg_catalog.pg_description column_description ON column_description.objoid = pg_statio_all_tables.relid AND column_description.objsubid = columns.ordinal_position
58
- WHERE schemata.schema_name != 'information_schema'
76
+ WHERE
77
+ -- skip internal postgres schemas
78
+ schemata.schema_name != 'information_schema'
59
79
  AND schemata.schema_name != 'postgis'
60
80
  AND left(schemata.schema_name, 3) != 'pg_'
81
+ -- only base tables (skip views), the null check is required for the left join as the schema might be empty
82
+ AND (tables IS NULL OR tables.table_type = 'BASE TABLE')
61
83
  -- order by the schema and table names alphabetically, then by the column position in the table
62
84
  ORDER BY schemata.schema_name, tables.table_schema, columns.ordinal_position
63
85
  SQL
64
86
  connection.exec(<<~SQL)
65
- COMMENT ON MATERIALIZED VIEW public.dynamic_migrations_structure_cache IS 'A cached representation of the database structure. This is used by the dynamic migrations library and is created automatically and updated automatically after migrations have run.';
87
+ COMMENT ON MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_structure_cache IS 'A cached representation of the database structure. This is used by the dynamic migrations library and is created automatically and updated automatically after migrations have run.';
66
88
  SQL
67
89
  end
68
90
 
69
91
  def refresh_database_structure_cache
70
92
  connection.exec(<<~SQL)
71
- REFRESH MATERIALIZED VIEW public.dynamic_migrations_structure_cache
93
+ REFRESH MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_structure_cache
72
94
  SQL
73
95
  rescue PG::UndefinedTable
74
96
  create_database_structure_cache
@@ -79,12 +101,12 @@ module DynamicMigrations
79
101
  def fetch_structure
80
102
  begin
81
103
  rows = connection.exec(<<~SQL)
82
- SELECT * FROM public.dynamic_migrations_structure_cache
104
+ SELECT * FROM #{Postgres.cache_schema_name}.dynamic_migrations_structure_cache
83
105
  SQL
84
106
  rescue PG::UndefinedTable
85
107
  create_database_structure_cache
86
108
  rows = connection.exec(<<~SQL)
87
- SELECT * FROM public.dynamic_migrations_structure_cache
109
+ SELECT * FROM #{Postgres.cache_schema_name}.dynamic_migrations_structure_cache
88
110
  SQL
89
111
  end
90
112
 
@@ -109,6 +131,7 @@ module DynamicMigrations
109
131
  column[:data_type] = row["data_type"].to_sym
110
132
  column[:null] = row["is_nullable"] == "YES"
111
133
  column[:is_enum] = row["is_enum"] == "t"
134
+ column[:is_array] = row["is_array"] == "t"
112
135
  column[:default] = row["column_default"]
113
136
  column[:description] = row["column_description"]
114
137
  column[:interval_type] = row["interval_type"].nil? ? nil : row["interval_type"].to_sym
@@ -7,7 +7,7 @@ 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
10
+ CREATE MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_validations_cache AS
11
11
  SELECT
12
12
  nspname AS schema_name,
13
13
  pg_constraint_class.relname AS table_name,
@@ -38,16 +38,16 @@ module DynamicMigrations
38
38
  conname;
39
39
  SQL
40
40
  connection.exec(<<~SQL)
41
- CREATE UNIQUE INDEX dynamic_migrations_validations_cache_index ON public.dynamic_migrations_validations_cache (schema_name, table_name, validation_name);
41
+ CREATE UNIQUE INDEX dynamic_migrations_validations_cache_index ON #{Postgres.cache_schema_name}.dynamic_migrations_validations_cache (schema_name, table_name, validation_name);
42
42
  SQL
43
43
  connection.exec(<<~SQL)
44
- COMMENT ON MATERIALIZED VIEW public.dynamic_migrations_validations_cache IS 'A cached representation of the database validations. This is used by the dynamic migrations library and is created automatically and updated automatically after migrations have run.';
44
+ COMMENT ON MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_validations_cache IS 'A cached representation of the database validations. This is used by the dynamic migrations library and is created automatically and updated automatically after migrations have run.';
45
45
  SQL
46
46
  end
47
47
 
48
48
  def refresh_database_validations_cache
49
49
  connection.exec(<<~SQL)
50
- REFRESH MATERIALIZED VIEW public.dynamic_migrations_validations_cache
50
+ REFRESH MATERIALIZED VIEW #{Postgres.cache_schema_name}.dynamic_migrations_validations_cache
51
51
  SQL
52
52
  rescue PG::UndefinedTable
53
53
  create_database_validations_cache
@@ -58,12 +58,12 @@ module DynamicMigrations
58
58
  def fetch_validations
59
59
  begin
60
60
  rows = connection.exec(<<~SQL)
61
- SELECT * FROM public.dynamic_migrations_validations_cache
61
+ SELECT * FROM #{Postgres.cache_schema_name}.dynamic_migrations_validations_cache
62
62
  SQL
63
63
  rescue PG::UndefinedTable
64
64
  create_database_validations_cache
65
65
  rows = connection.exec(<<~SQL)
66
- SELECT * FROM public.dynamic_migrations_validations_cache
66
+ SELECT * FROM #{Postgres.cache_schema_name}.dynamic_migrations_validations_cache
67
67
  SQL
68
68
  end
69
69
 
@@ -1,8 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamicMigrations
4
- # This module exists only to namespace Postgres functionality and
5
- # make it possible to add other database/storage types in the future.
6
4
  module Postgres
5
+ # The default behaviour of dynamic migrations is to generate migrations
6
+ # which remove any unused extensions.
7
+ # People don't always have control over which extensions are running on the
8
+ # database, so this behaviour can be disabled by setting
9
+ # `DynamicMigrations::Postgres.remove_unused_extensions = false`
10
+ def self.remove_unused_extensions= value
11
+ @remove_unused_extensions = value
12
+ end
13
+
14
+ # defaults to true, but can be set to false to disable the removal of unused
15
+ # extensions
16
+ def self.remove_unused_extensions?
17
+ (@remove_unused_extensions.nil? || @remove_unused_extensions) ? true : false
18
+ end
19
+
20
+ # Dynamic Migrations creates a materialized view to store a cache representation
21
+ # of various parts of the database structure, by default this is created in the
22
+ # public schema, but this can be changed by setting the otion below.
23
+ def self.cache_schema_name= value
24
+ @cache_schema_name = value
25
+ end
26
+
27
+ # defaults to true, but can be set to false to disable the removal of unused
28
+ # extensions
29
+ def self.cache_schema_name
30
+ @cache_schema_name || :public
31
+ end
7
32
  end
8
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamicMigrations
4
- VERSION = "3.7.0"
4
+ VERSION = "3.8.0"
5
5
  end
@@ -10,6 +10,8 @@ require "dynamic_migrations/expected_integer_error"
10
10
  require "dynamic_migrations/expected_boolean_error"
11
11
  require "dynamic_migrations/module_included_into_unexpected_target_error"
12
12
 
13
+ require "dynamic_migrations/postgres"
14
+
13
15
  require "dynamic_migrations/postgres/server/database/connection"
14
16
  require "dynamic_migrations/postgres/server/database/structure_loader"
15
17
  require "dynamic_migrations/postgres/server/database/validations_loader"
@@ -1,4 +1,12 @@
1
1
  module DynamicMigrations
2
2
  module Postgres
3
+ self.@remove_unused_extensions: bool?
4
+ self.@cache_schema_name: Symbol?
5
+
6
+ def self.remove_unused_extensions=: (bool value) -> bool
7
+ def self.remove_unused_extensions?: -> bool
8
+
9
+ def self.cache_schema_name=: (Symbol value) -> Symbol
10
+ def self.cache_schema_name: -> Symbol
3
11
  end
4
12
  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.7.0
4
+ version: 3.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Craig Ulliott
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-27 00:00:00.000000000 Z
11
+ date: 2023-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg