dynamic_migrations 3.7.0 → 3.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/lib/dynamic_migrations/postgres/server/database/connection.rb +16 -4
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/extensions.rb +3 -1
- data/lib/dynamic_migrations/postgres/server/database/keys_and_unique_constraints_loader.rb +6 -6
- data/lib/dynamic_migrations/postgres/server/database/loaded_schemas_builder.rb +3 -1
- data/lib/dynamic_migrations/postgres/server/database/schema/enums.rb +3 -1
- data/lib/dynamic_migrations/postgres/server/database/structure_loader.rb +35 -12
- data/lib/dynamic_migrations/postgres/server/database/validations_loader.rb +6 -6
- data/lib/dynamic_migrations/postgres.rb +27 -2
- data/lib/dynamic_migrations/version.rb +1 -1
- data/lib/dynamic_migrations.rb +2 -0
- data/sig/dynamic_migrations/postgres/server/database/connection.rbs +1 -0
- data/sig/dynamic_migrations/postgres.rbs +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1235cefb0bc31fd54a095fbef1111be30cb13f89bb6c4358364f35594ffe95ff
|
4
|
+
data.tar.gz: e9050d22292034176525471025a8981fba83c8945b8a81f754c3c2a222cc4538
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2be636e7faf4eb9d05d8f6fb507603aa362f341982a44f99e7e35dd365481fe7042b04e1dcd743981ea321082af61be38e8a1b73c31109e09252f3736c51afb3
|
7
|
+
data.tar.gz: 9db574a68a9009bee00bb5e8d16604b577e0d33a999ece3df62144516a15ba8f0187e624346ab87913f3c04805de4140eb0dd5b68c5d0f1fd30dff7010efef97
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.8.1](https://github.com/craigulliott/dynamic_migrations/compare/v3.8.0...v3.8.1) (2023-10-06)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* reusing exiting connection when with_connection is called (it was trying to open a new connection and caused an error) ([840a5cd](https://github.com/craigulliott/dynamic_migrations/commit/840a5cda04d6bc49d73160d2bad7271780027c84))
|
9
|
+
|
10
|
+
## [3.8.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.7.0...v3.8.0) (2023-10-04)
|
11
|
+
|
12
|
+
|
13
|
+
### Features
|
14
|
+
|
15
|
+
* configuration option to skip removing unused extensions ([5d27e36](https://github.com/craigulliott/dynamic_migrations/commit/5d27e36363dcec552ed015a196fbcf660e6b038c))
|
16
|
+
|
17
|
+
|
18
|
+
### Bug Fixes
|
19
|
+
|
20
|
+
* 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))
|
21
|
+
* handling arrays of enums properly ([5d27e36](https://github.com/craigulliott/dynamic_migrations/commit/5d27e36363dcec552ed015a196fbcf660e6b038c))
|
22
|
+
* skipping views when loading database tables ([5d27e36](https://github.com/craigulliott/dynamic_migrations/commit/5d27e36363dcec552ed015a196fbcf660e6b038c))
|
23
|
+
|
3
24
|
## [3.7.0](https://github.com/craigulliott/dynamic_migrations/compare/v3.6.16...v3.7.0) (2023-09-27)
|
4
25
|
|
5
26
|
|
@@ -33,19 +33,31 @@ module DynamicMigrations
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
def connected?
|
37
|
+
!@connection.nil?
|
38
|
+
end
|
39
|
+
|
36
40
|
# Opens a connection to the database server, and yields the provided block
|
37
41
|
# before automatically closing the connection again. This is useful for
|
38
42
|
# executing one time queries against the database server.
|
39
43
|
def with_connection &block
|
40
|
-
# create a temporary connection to the server
|
41
|
-
|
44
|
+
# create a temporary connection to the server (unless we are already connected)
|
45
|
+
already_connected = connected?
|
46
|
+
unless already_connected
|
47
|
+
connect
|
48
|
+
end
|
49
|
+
|
42
50
|
# perform work with the connection
|
43
51
|
# todo: `yield connection` would have been preferred, but rbs/steep doesnt understand that syntax
|
44
52
|
if block.is_a? Proc
|
45
53
|
result = block.call connection
|
46
54
|
end
|
47
|
-
|
48
|
-
|
55
|
+
|
56
|
+
# close the connection (unless we were already connected)
|
57
|
+
if already_connected
|
58
|
+
disconnect
|
59
|
+
end
|
60
|
+
|
49
61
|
# return whever was returned from within the block
|
50
62
|
result
|
51
63
|
end
|
data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/extensions.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
data/lib/dynamic_migrations.rb
CHANGED
@@ -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.
|
4
|
+
version: 3.8.1
|
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-
|
11
|
+
date: 2023-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|