dynamic_migrations 2.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/lib/dynamic_migrations/active_record/migrators/column.rb +21 -0
- data/lib/dynamic_migrations/active_record/migrators/foreign_key_constraint.rb +112 -0
- data/lib/dynamic_migrations/active_record/migrators/function.rb +108 -0
- data/lib/dynamic_migrations/active_record/migrators/index.rb +27 -0
- data/lib/dynamic_migrations/active_record/migrators/schema.rb +21 -0
- data/lib/dynamic_migrations/active_record/migrators/table.rb +21 -0
- data/lib/dynamic_migrations/active_record/migrators/trigger.rb +109 -0
- data/lib/dynamic_migrations/active_record/migrators/unique_constraint.rb +63 -0
- data/lib/dynamic_migrations/active_record/migrators/validation.rb +67 -0
- data/lib/dynamic_migrations/active_record/migrators.rb +64 -0
- data/lib/dynamic_migrations/name_helper.rb +13 -0
- data/lib/dynamic_migrations/postgres/generator/column.rb +92 -0
- data/lib/dynamic_migrations/postgres/generator/foreign_key_constraint.rb +84 -0
- data/lib/dynamic_migrations/postgres/generator/fragment.rb +30 -0
- data/lib/dynamic_migrations/postgres/generator/function.rb +77 -0
- data/lib/dynamic_migrations/postgres/generator/index.rb +101 -0
- data/lib/dynamic_migrations/postgres/generator/primary_key.rb +55 -0
- data/lib/dynamic_migrations/postgres/generator/schema.rb +19 -0
- data/lib/dynamic_migrations/postgres/generator/schema_migrations/section.rb +37 -0
- data/lib/dynamic_migrations/postgres/generator/schema_migrations.rb +92 -0
- data/lib/dynamic_migrations/postgres/generator/table.rb +122 -0
- data/lib/dynamic_migrations/postgres/generator/trigger.rb +101 -0
- data/lib/dynamic_migrations/postgres/generator/unique_constraint.rb +79 -0
- data/lib/dynamic_migrations/postgres/generator/validation.rb +87 -0
- data/lib/dynamic_migrations/postgres/generator.rb +359 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/functions.rb +68 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/columns.rb +72 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/foreign_key_constraints.rb +73 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/indexes.rb +73 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/primary_key.rb +49 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/triggers.rb +73 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/unique_constraints.rb +73 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables/validations.rb +73 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas/tables.rb +80 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations/schemas.rb +48 -0
- data/lib/dynamic_migrations/postgres/server/database/differences/to_migrations.rb +59 -0
- data/lib/dynamic_migrations/postgres/server/database/differences.rb +75 -6
- data/lib/dynamic_migrations/postgres/server/database/keys_and_unique_constraints_loader.rb +35 -9
- data/lib/dynamic_migrations/postgres/server/database/loaded_schemas_builder.rb +49 -8
- data/lib/dynamic_migrations/postgres/server/database/schema/function.rb +69 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/functions.rb +63 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/column.rb +4 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/columns.rb +1 -1
- data/lib/dynamic_migrations/postgres/server/database/schema/table/foreign_key_constraint.rb +40 -5
- data/lib/dynamic_migrations/postgres/server/database/schema/table/index.rb +23 -9
- data/lib/dynamic_migrations/postgres/server/database/schema/table/primary_key.rb +21 -6
- data/lib/dynamic_migrations/postgres/server/database/schema/table/trigger.rb +151 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/triggers.rb +66 -0
- data/lib/dynamic_migrations/postgres/server/database/schema/table/unique_constraint.rb +19 -9
- data/lib/dynamic_migrations/postgres/server/database/schema/table/validation.rb +20 -1
- data/lib/dynamic_migrations/postgres/server/database/schema/table.rb +15 -5
- data/lib/dynamic_migrations/postgres/server/database/schema/tables.rb +63 -0
- data/lib/dynamic_migrations/postgres/server/database/schema.rb +3 -49
- data/lib/dynamic_migrations/postgres/server/database/source.rb +21 -0
- data/lib/dynamic_migrations/postgres/server/database/structure_loader.rb +6 -6
- data/lib/dynamic_migrations/postgres/server/database/triggers_and_functions_loader.rb +131 -0
- data/lib/dynamic_migrations/postgres/server/database/validations_loader.rb +10 -4
- data/lib/dynamic_migrations/postgres/server/database.rb +2 -1
- data/lib/dynamic_migrations/postgres/server.rb +6 -0
- data/lib/dynamic_migrations/postgres.rb +1 -1
- data/lib/dynamic_migrations/version.rb +1 -1
- data/lib/dynamic_migrations.rb +47 -3
- metadata +44 -2
@@ -0,0 +1,92 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
module Column
|
5
|
+
class NoColumnCommentError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_column column, code_comment = nil
|
9
|
+
if column.description.nil?
|
10
|
+
raise NoColumnCommentError, "Refusing to generate add_column migration, no description was provided for `#{column.table.schema.name}`.`#{column.table.name}` column `#{column.name}`"
|
11
|
+
end
|
12
|
+
|
13
|
+
options = {}
|
14
|
+
options[:null] = column.null
|
15
|
+
|
16
|
+
unless column.default.nil?
|
17
|
+
options[:default] = "\"#{column.default}\""
|
18
|
+
end
|
19
|
+
|
20
|
+
if column.array?
|
21
|
+
options[:array] = true
|
22
|
+
end
|
23
|
+
|
24
|
+
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
25
|
+
|
26
|
+
data_type = column.data_type
|
27
|
+
if column.array?
|
28
|
+
data_type = "\"#{data_type}\""
|
29
|
+
end
|
30
|
+
|
31
|
+
add_migration column.table.schema.name, column.table.name, :add_column, column.name, code_comment, <<~RUBY
|
32
|
+
add_column :#{column.table.name}, :#{column.name}, :#{data_type}, #{options_syntax}, comment: <<~COMMENT
|
33
|
+
#{indent column.description}
|
34
|
+
COMMENT
|
35
|
+
RUBY
|
36
|
+
end
|
37
|
+
|
38
|
+
def change_column column, code_comment = nil
|
39
|
+
options = {}
|
40
|
+
options[:null] = column.null
|
41
|
+
|
42
|
+
unless column.default.nil?
|
43
|
+
options[:default] = "\"#{column.default}\""
|
44
|
+
end
|
45
|
+
|
46
|
+
if column.array?
|
47
|
+
options[:array] = true
|
48
|
+
end
|
49
|
+
|
50
|
+
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
51
|
+
|
52
|
+
data_type = column.data_type
|
53
|
+
if column.array?
|
54
|
+
data_type = ":\"#{data_type}\""
|
55
|
+
end
|
56
|
+
|
57
|
+
add_migration column.table.schema.name, column.table.name, :change_column, column.name, code_comment, <<~RUBY
|
58
|
+
change_column :#{column.table.name}, :#{column.name}, :#{data_type}, #{options_syntax}
|
59
|
+
RUBY
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_column column, code_comment = nil
|
63
|
+
add_migration column.table.schema.name, column.table.name, :remove_column, column.name, code_comment, <<~RUBY
|
64
|
+
remove_column :#{column.table.name}, :#{column.name}
|
65
|
+
RUBY
|
66
|
+
end
|
67
|
+
|
68
|
+
# add a comment to a column
|
69
|
+
def set_column_comment column, code_comment = nil
|
70
|
+
description = column.description
|
71
|
+
|
72
|
+
if description.nil?
|
73
|
+
raise MissingDescriptionError
|
74
|
+
end
|
75
|
+
|
76
|
+
add_migration column.table.schema.name, column.table.name, :set_column_comment, column.name, code_comment, <<~RUBY
|
77
|
+
set_column_comment :#{column.table.name}, :#{column.name}, <<~COMMENT
|
78
|
+
#{indent description}
|
79
|
+
COMMENT
|
80
|
+
RUBY
|
81
|
+
end
|
82
|
+
|
83
|
+
# remove the comment from a column
|
84
|
+
def remove_column_comment column, code_comment = nil
|
85
|
+
add_migration column.table.schema.name, column.table.name, :remove_column_comment, column.name, code_comment, <<~RUBY
|
86
|
+
remove_column_comment :#{column.table.name}, :#{column.name}
|
87
|
+
RUBY
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
module ForeignKeyConstraint
|
5
|
+
def add_foreign_key_constraint foreign_key_constraint, code_comment = nil
|
6
|
+
# the migration accepts either a single column name or an array of column names
|
7
|
+
# we use the appropriate syntax just to make the migration prettier and easier
|
8
|
+
# to understand
|
9
|
+
p_c_names = foreign_key_constraint.column_names
|
10
|
+
column_names = (p_c_names.count == 1) ? ":#{p_c_names.first}" : "[:#{p_c_names.join(", :")}]"
|
11
|
+
|
12
|
+
f_c_names = foreign_key_constraint.foreign_column_names
|
13
|
+
foreign_column_names = (f_c_names.count == 1) ? ":#{f_c_names.first}" : "[:#{f_c_names.join(", :")}]"
|
14
|
+
|
15
|
+
options = {
|
16
|
+
name: ":#{foreign_key_constraint.name}",
|
17
|
+
initially_deferred: foreign_key_constraint.initially_deferred,
|
18
|
+
deferrable: foreign_key_constraint.deferrable,
|
19
|
+
on_delete: ":#{foreign_key_constraint.on_delete}",
|
20
|
+
on_update: ":#{foreign_key_constraint.on_update}"
|
21
|
+
}
|
22
|
+
unless foreign_key_constraint.description.nil?
|
23
|
+
options[:comment] = <<~RUBY
|
24
|
+
<<~COMMENT
|
25
|
+
#{indent foreign_key_constraint.description}
|
26
|
+
COMMENT
|
27
|
+
RUBY
|
28
|
+
end
|
29
|
+
|
30
|
+
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
31
|
+
|
32
|
+
add_migration foreign_key_constraint.table.schema.name, foreign_key_constraint.table.name, :add_foreign_key, foreign_key_constraint.name, code_comment, <<~RUBY
|
33
|
+
add_foreign_key :#{foreign_key_constraint.table.name}, #{column_names}, :#{foreign_key_constraint.foreign_table.name}, #{foreign_column_names}, #{options_syntax}
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_foreign_key_constraint foreign_key_constraint, code_comment = nil
|
38
|
+
add_migration foreign_key_constraint.table.schema.name, foreign_key_constraint.table.name, :remove_foreign_key, foreign_key_constraint.name, code_comment, <<~RUBY
|
39
|
+
remove_foreign_key :#{foreign_key_constraint.table.name}, :#{foreign_key_constraint.name}
|
40
|
+
RUBY
|
41
|
+
end
|
42
|
+
|
43
|
+
def recreate_foreign_key_constraint original_foreign_key_constraint, updated_foreign_key_constraint
|
44
|
+
# remove the original foreign_key_constraint
|
45
|
+
removal_fragment = remove_foreign_key_constraint original_foreign_key_constraint, <<~CODE_COMMENT
|
46
|
+
Removing original foreign key constraint because it has changed (it is recreated below)
|
47
|
+
Changes:
|
48
|
+
#{indent original_foreign_key_constraint.differences_descriptions(updated_foreign_key_constraint).join("\n")}
|
49
|
+
CODE_COMMENT
|
50
|
+
|
51
|
+
# recrete the foreign_key_constraint with the new options
|
52
|
+
recreation_fragment = add_foreign_key_constraint updated_foreign_key_constraint, <<~CODE_COMMENT
|
53
|
+
Recreating this foreign key constraint
|
54
|
+
CODE_COMMENT
|
55
|
+
|
56
|
+
# return the new fragments (the main reason to return them here is for the specs)
|
57
|
+
[removal_fragment, recreation_fragment]
|
58
|
+
end
|
59
|
+
|
60
|
+
# add a comment to a foreign_key_constraint
|
61
|
+
def set_foreign_key_constraint_comment foreign_key_constraint, code_comment = nil
|
62
|
+
description = foreign_key_constraint.description
|
63
|
+
|
64
|
+
if description.nil?
|
65
|
+
raise MissingDescriptionError
|
66
|
+
end
|
67
|
+
|
68
|
+
add_migration foreign_key_constraint.table.schema.name, foreign_key_constraint.table.name, :set_foreign_key_constraint_comment, foreign_key_constraint.name, code_comment, <<~RUBY
|
69
|
+
set_foreign_key_comment :#{foreign_key_constraint.table.name}, :#{foreign_key_constraint.name}, <<~COMMENT
|
70
|
+
#{indent description}
|
71
|
+
COMMENT
|
72
|
+
RUBY
|
73
|
+
end
|
74
|
+
|
75
|
+
# remove the comment from a foreign_key_constraint
|
76
|
+
def remove_foreign_key_constraint_comment foreign_key_constraint, code_comment = nil
|
77
|
+
add_migration foreign_key_constraint.table.schema.name, foreign_key_constraint.table.name, :remove_foreign_key_constraint_comment, foreign_key_constraint.name, code_comment, <<~RUBY
|
78
|
+
remove_foreign_key_comment :#{foreign_key_constraint.table.name}, :#{foreign_key_constraint.name}
|
79
|
+
RUBY
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
class Fragment
|
5
|
+
attr_reader :object_name
|
6
|
+
attr_reader :code_comment
|
7
|
+
|
8
|
+
def initialize object_name, code_comment, content
|
9
|
+
@object_name = object_name
|
10
|
+
@code_comment = code_comment
|
11
|
+
@content = content
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
strings = []
|
16
|
+
comment = @code_comment
|
17
|
+
unless comment.nil?
|
18
|
+
strings << "# " + comment.split("\n").join("\n# ")
|
19
|
+
end
|
20
|
+
strings << @content
|
21
|
+
strings.join("\n").strip
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_code_comment?
|
25
|
+
!@code_comment.nil?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
module Function
|
5
|
+
def create_function function, code_comment = nil
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
if function.description.nil?
|
9
|
+
comment_sql = ""
|
10
|
+
else
|
11
|
+
comment_sql = <<~RUBY
|
12
|
+
#{function.name}_comment = <<~COMMENT
|
13
|
+
#{indent function.description || ""}
|
14
|
+
COMMENT
|
15
|
+
RUBY
|
16
|
+
options[:comment] = "#{function.name}_comment"
|
17
|
+
end
|
18
|
+
|
19
|
+
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
20
|
+
optional_options_syntax = (options_syntax == "") ? "" : ", #{options_syntax}"
|
21
|
+
|
22
|
+
fn_sql = function.definition.strip
|
23
|
+
# ensure that the function ends with a semicolon
|
24
|
+
unless fn_sql.end_with? ";"
|
25
|
+
fn_sql << ";"
|
26
|
+
end
|
27
|
+
|
28
|
+
add_migration function.schema.name, function.triggers.first&.table&.name, :create_function, function.name, code_comment, (comment_sql + <<~RUBY)
|
29
|
+
create_function :#{function.name}#{optional_options_syntax} do
|
30
|
+
<<~SQL
|
31
|
+
#{indent fn_sql}
|
32
|
+
SQL
|
33
|
+
end
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_function function, code_comment = nil
|
38
|
+
fn_sql = function.definition.strip
|
39
|
+
# ensure that the function ends with a semicolon
|
40
|
+
unless fn_sql.end_with? ";"
|
41
|
+
fn_sql << ";"
|
42
|
+
end
|
43
|
+
|
44
|
+
add_migration function.schema.name, function.triggers.first&.table&.name, :update_function, function.name, code_comment, <<~RUBY
|
45
|
+
update_function :#{function.name} do
|
46
|
+
<<~SQL
|
47
|
+
#{indent fn_sql}
|
48
|
+
SQL
|
49
|
+
end
|
50
|
+
RUBY
|
51
|
+
end
|
52
|
+
|
53
|
+
def drop_function function, code_comment = nil
|
54
|
+
add_migration function.schema.name, function.triggers.first&.table&.name, :drop_function, function.name, code_comment, <<~RUBY
|
55
|
+
drop_function :#{function.name}
|
56
|
+
RUBY
|
57
|
+
end
|
58
|
+
|
59
|
+
# add a comment to a function
|
60
|
+
def set_function_comment function, code_comment = nil
|
61
|
+
add_migration function.schema.name, function.triggers.first&.table&.name, :set_function_comment, function.name, code_comment, <<~RUBY
|
62
|
+
set_function_comment :#{function.name}, <<~COMMENT
|
63
|
+
#{indent function.description || ""}
|
64
|
+
COMMENT
|
65
|
+
RUBY
|
66
|
+
end
|
67
|
+
|
68
|
+
# remove the comment from a function
|
69
|
+
def remove_function_comment function, code_comment = nil
|
70
|
+
add_migration function.schema.name, function.triggers.first&.table&.name, :remove_function_comment, function.name, code_comment, <<~RUBY
|
71
|
+
remove_function_comment :#{function.name}
|
72
|
+
RUBY
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
module Index
|
5
|
+
def add_index index, code_comment = nil
|
6
|
+
# the migration accepts either a single column name or an array of column names
|
7
|
+
# we use the appropriate syntax just to make the migration prettier and easier
|
8
|
+
# to understand
|
9
|
+
column_names = (index.column_names.count == 1) ? ":#{index.column_names.first}" : "[:#{index.column_names.join(", :")}]"
|
10
|
+
|
11
|
+
options = {
|
12
|
+
name: ":#{index.name}",
|
13
|
+
unique: index.unique,
|
14
|
+
using: ":#{index.type}",
|
15
|
+
# todo: support custom sorting, it requires refactoring the index class because the ordering is actually on a column by column basis, not the index itself
|
16
|
+
sort: ":#{index.order}"
|
17
|
+
}
|
18
|
+
|
19
|
+
# :first is the default when :desc is specified, :last is the default when :asc is specified
|
20
|
+
if (index.order == :desc && index.nulls_position == :last) || (index.order == :asc && index.nulls_position == :first)
|
21
|
+
# todo: support nulls_position, it requires writing our own migrator because rails does not provide this option
|
22
|
+
raise "custom nulls_position is not currently supported"
|
23
|
+
end
|
24
|
+
|
25
|
+
unless index.where.nil?
|
26
|
+
options[:where] = "\"#{index.where}\""
|
27
|
+
end
|
28
|
+
|
29
|
+
unless index.description.nil?
|
30
|
+
options[:comment] = <<~RUBY
|
31
|
+
<<~COMMENT
|
32
|
+
#{indent index.description}
|
33
|
+
COMMENT
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
|
37
|
+
where_sql = ""
|
38
|
+
unless index.where.nil?
|
39
|
+
options[:where] = "#{index.name}_where_sql"
|
40
|
+
where_sql = <<~RUBY
|
41
|
+
#{index.name}_where_sql = <<~SQL
|
42
|
+
#{indent index.where}
|
43
|
+
SQL
|
44
|
+
RUBY
|
45
|
+
end
|
46
|
+
|
47
|
+
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
48
|
+
|
49
|
+
add_migration index.table.schema.name, index.table.name, :add_index, index.name, code_comment, (where_sql + <<~RUBY)
|
50
|
+
add_index :#{index.table.name}, #{column_names}, #{options_syntax}
|
51
|
+
RUBY
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_index index, code_comment = nil
|
55
|
+
add_migration index.table.schema.name, index.table.name, :remove_index, index.name, code_comment, <<~RUBY
|
56
|
+
remove_index :#{index.table.name}, :#{index.name}
|
57
|
+
RUBY
|
58
|
+
end
|
59
|
+
|
60
|
+
def recreate_index original_index, updated_index
|
61
|
+
# remove the original index
|
62
|
+
removal_fragment = remove_index original_index, <<~CODE_COMMENT
|
63
|
+
Removing original index because it has changed (it is recreated below)
|
64
|
+
Changes:
|
65
|
+
#{indent original_index.differences_descriptions(updated_index).join("\n")}
|
66
|
+
CODE_COMMENT
|
67
|
+
|
68
|
+
# recrete the index with the new options
|
69
|
+
recreation_fragment = add_index updated_index, <<~CODE_COMMENT
|
70
|
+
Recreating this index
|
71
|
+
CODE_COMMENT
|
72
|
+
|
73
|
+
# return the new fragments (the main reason to return them here is for the specs)
|
74
|
+
[removal_fragment, recreation_fragment]
|
75
|
+
end
|
76
|
+
|
77
|
+
# add a comment to a index
|
78
|
+
def set_index_comment index, code_comment = nil
|
79
|
+
description = index.description
|
80
|
+
|
81
|
+
if description.nil?
|
82
|
+
raise MissingDescriptionError
|
83
|
+
end
|
84
|
+
|
85
|
+
add_migration index.table.schema.name, index.table.name, :set_index_comment, index.name, code_comment, <<~RUBY
|
86
|
+
set_index_comment :#{index.table.name}, :#{index.name}, <<~COMMENT
|
87
|
+
#{indent description}
|
88
|
+
COMMENT
|
89
|
+
RUBY
|
90
|
+
end
|
91
|
+
|
92
|
+
# remove the comment from a index
|
93
|
+
def remove_index_comment index, code_comment = nil
|
94
|
+
add_migration index.table.schema.name, index.table.name, :remove_index_comment, index.name, code_comment, <<~RUBY
|
95
|
+
remove_index_comment :#{index.table.name}, :#{index.name}
|
96
|
+
RUBY
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
module PrimaryKey
|
5
|
+
def add_primary_key primary_key, code_comment = nil
|
6
|
+
# the migration accepts either a single column name or an array of column names
|
7
|
+
# we use the appropriate syntax just to make the migration prettier and easier
|
8
|
+
# to understand
|
9
|
+
column_names = (primary_key.column_names.count == 1) ? ":#{primary_key.column_names.first}" : "[:#{primary_key.column_names.join(", :")}]"
|
10
|
+
|
11
|
+
options = {
|
12
|
+
name: ":#{primary_key.name}"
|
13
|
+
}
|
14
|
+
|
15
|
+
unless primary_key.description.nil?
|
16
|
+
options[:comment] = <<~RUBY
|
17
|
+
<<~COMMENT
|
18
|
+
#{indent primary_key.description}
|
19
|
+
COMMENT
|
20
|
+
RUBY
|
21
|
+
end
|
22
|
+
|
23
|
+
options_syntax = options.map { |k, v| "#{k}: #{v}" }.join(", ")
|
24
|
+
|
25
|
+
add_migration primary_key.table.schema.name, primary_key.table.name, :add_primary_key, primary_key.name, code_comment, <<~RUBY
|
26
|
+
add_primary_key :#{primary_key.table.name}, #{column_names}, #{options_syntax}
|
27
|
+
RUBY
|
28
|
+
end
|
29
|
+
|
30
|
+
def remove_primary_key primary_key, code_comment = nil
|
31
|
+
add_migration primary_key.table.schema.name, primary_key.table.name, :remove_primary_key, primary_key.name, code_comment, <<~RUBY
|
32
|
+
remove_primary_key :#{primary_key.table.name}, :#{primary_key.name}
|
33
|
+
RUBY
|
34
|
+
end
|
35
|
+
|
36
|
+
def recreate_primary_key original_primary_key, updated_primary_key
|
37
|
+
# remove the original primary_key
|
38
|
+
removal_fragment = remove_primary_key original_primary_key, <<~CODE_COMMENT
|
39
|
+
Removing original primary key because it has changed (it is recreated below)
|
40
|
+
Changes:
|
41
|
+
#{indent original_primary_key.differences_descriptions(updated_primary_key).join("\n")}
|
42
|
+
CODE_COMMENT
|
43
|
+
|
44
|
+
# recrete the primary_key with the new options
|
45
|
+
recreation_fragment = add_primary_key updated_primary_key, <<~CODE_COMMENT
|
46
|
+
Recreating this primary key
|
47
|
+
CODE_COMMENT
|
48
|
+
|
49
|
+
# return the new fragments (the main reason to return them here is for the specs)
|
50
|
+
[removal_fragment, recreation_fragment]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
module Schema
|
5
|
+
def create_schema schema, code_comment = nil
|
6
|
+
add_migration schema.name, nil, :create_schema, schema.name, code_comment, <<~RUBY
|
7
|
+
create_schema :#{schema.name}
|
8
|
+
RUBY
|
9
|
+
end
|
10
|
+
|
11
|
+
def drop_schema schema, code_comment = nil
|
12
|
+
add_migration schema.name, nil, :drop_schema, schema.name, code_comment, <<~RUBY
|
13
|
+
drop_schema :#{schema.name}
|
14
|
+
RUBY
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
class SchemaMigrations
|
5
|
+
class Section
|
6
|
+
attr_reader :schema_name
|
7
|
+
attr_reader :table_name
|
8
|
+
attr_reader :content_type
|
9
|
+
attr_reader :fragment
|
10
|
+
|
11
|
+
def initialize schema_name, table_name, content_type, fragment
|
12
|
+
@schema_name = schema_name
|
13
|
+
@table_name = table_name
|
14
|
+
@content_type = content_type
|
15
|
+
@fragment = fragment
|
16
|
+
end
|
17
|
+
|
18
|
+
def object_name
|
19
|
+
@fragment.object_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@fragment.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_comment?
|
27
|
+
content_type? :comment
|
28
|
+
end
|
29
|
+
|
30
|
+
def content_type? content_type
|
31
|
+
@content_type == content_type
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module DynamicMigrations
|
2
|
+
module Postgres
|
3
|
+
class Generator
|
4
|
+
class SchemaMigrations
|
5
|
+
class SectionNotFoundError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :current_migration_sections
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@migrations = []
|
12
|
+
@current_migration_sections = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_fragment schema_name, table_name, content_type, fragment
|
16
|
+
@current_migration_sections << Section.new(schema_name, table_name, content_type, fragment)
|
17
|
+
end
|
18
|
+
|
19
|
+
def finalize
|
20
|
+
if @current_migration_sections.any?
|
21
|
+
|
22
|
+
contents = []
|
23
|
+
@current_migration_sections.each do |section|
|
24
|
+
contents << section.to_s
|
25
|
+
# add an empty line between sections (unless this is a comment section)
|
26
|
+
unless section.is_comment?
|
27
|
+
contents << ""
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@migrations << {
|
32
|
+
name: generate_current_migration_name,
|
33
|
+
content: contents.join("\n").strip
|
34
|
+
}
|
35
|
+
|
36
|
+
@current_migration_sections = []
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_a
|
41
|
+
@migrations
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def current_migration_has_content_type? content_type
|
47
|
+
@current_migration_sections.map(&:content_type).include? content_type
|
48
|
+
end
|
49
|
+
|
50
|
+
def current_migration_section_of_content_type content_type
|
51
|
+
section = @current_migration_sections.find(&:content_type)
|
52
|
+
if section.nil?
|
53
|
+
raise SectionNotFoundError, "No section of type #{content_type} found"
|
54
|
+
end
|
55
|
+
section
|
56
|
+
end
|
57
|
+
|
58
|
+
# return true if the current migration only has the provided content types and comments
|
59
|
+
def current_migration_only_content_types? content_types
|
60
|
+
(@current_migration_sections.map(&:content_type) - content_types - [:comment]).empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_current_migration_name
|
64
|
+
if current_migration_has_content_type? :create_schema
|
65
|
+
"create_#{current_migration_section_of_content_type(:create_schema).schema_name}_schema".to_sym
|
66
|
+
|
67
|
+
elsif current_migration_has_content_type? :drop_schema
|
68
|
+
"drop_#{current_migration_section_of_content_type(:drop_schema).schema_name}_schema".to_sym
|
69
|
+
|
70
|
+
elsif current_migration_has_content_type? :create_table
|
71
|
+
"create_#{current_migration_section_of_content_type(:create_table).table_name}".to_sym
|
72
|
+
|
73
|
+
elsif current_migration_has_content_type? :drop_table
|
74
|
+
"drop_#{current_migration_section_of_content_type(:drop_table).table_name}".to_sym
|
75
|
+
|
76
|
+
elsif current_migration_only_content_types? [:create_function]
|
77
|
+
"create_function_#{@current_migration_sections.find { |s| s.content_type == :create_function }&.object_name}".to_sym
|
78
|
+
|
79
|
+
elsif current_migration_only_content_types? [:create_function, :update_function, :drop_function, :set_function_comment, :remove_function_comment]
|
80
|
+
:schema_functions
|
81
|
+
|
82
|
+
elsif @current_migration_sections.first&.table_name
|
83
|
+
"changes_for_#{@current_migration_sections.first&.table_name}".to_sym
|
84
|
+
|
85
|
+
else
|
86
|
+
:changes
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|