dynamic_migrations 3.7.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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