xmigra 1.5.1 → 1.6.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
  SHA1:
3
- metadata.gz: b8cc262267c7f2ccdb80a17bb68f76da30ea4bcb
4
- data.tar.gz: d5245127f534c80e96a983e0222f855e872c7e4d
3
+ metadata.gz: 6bd9e09df1d03a0bb23ee61fd21644669c459315
4
+ data.tar.gz: 2467c3c1de5236f6e27f87706b7d1b091c774dbd
5
5
  SHA512:
6
- metadata.gz: dcde456a4403f7ec18a273c27550293e66032445ca62df0bf25c42160e21daacc16e37a56ae0ad8a7dfad44631e27179a1d589188b9c00748525c0c5aabb14b3
7
- data.tar.gz: 8426367f6962b891e919df73cde7ca086b6524a70e10344390f83e53cdaaac0ca36f61c4082d50188d18e6e9a0e0528bbd4cf2ad2cd17deabbc7b818a762212b
6
+ metadata.gz: 4cdf662d14d79137b156324376dcb3725183712354ecbe0a2e3ecd453f3a6b331f322f46204d9e5a5290f356062e9f6125a2571754d3ee75d439ca22c9cb21f6
7
+ data.tar.gz: 75c686b17ad25eb2a117a25d30ced42e501944facef9cb8446461ff9715556a7b99f7049bfea154a0cf83e12ebe9caea5922b4fc239c9c3fdf2e34da5b1d8941
@@ -1153,6 +1153,49 @@ INSERT INTO [xmigra].[branch_upgrade] ([Current]) VALUES (#{branch_id_literal});
1153
1153
  return parts.join("\n")
1154
1154
  end
1155
1155
 
1156
+ def index_template_sql
1157
+ <<-"END_OF_SQL"
1158
+ CREATE INDEX [{filename}]
1159
+ ON <<<table>>> (<<<columns>>>);
1160
+ END_OF_SQL
1161
+ end
1162
+
1163
+ def view_definition_template_sql
1164
+ <<-"END_OF_SQL"
1165
+ CREATE VIEW [{filename}]
1166
+ AS SELECT <<<query>>>;
1167
+ END_OF_SQL
1168
+ end
1169
+
1170
+ def procedure_definition_template_sql
1171
+ <<-"END_OF_SQL"
1172
+ CREATE PROCEDURE [{filename}]
1173
+ <<<parameters>>>
1174
+ AS BEGIN
1175
+ <<<statements>>>
1176
+ END
1177
+ END_OF_SQL
1178
+ end
1179
+
1180
+ def function_definition_template_sql
1181
+ <<-"END_OF_SQL"
1182
+ CREATE FUNCTION [{filename}] (
1183
+ <<<parameters>>>
1184
+ ) RETURNS <<<return-type>>>
1185
+ AS BEGIN
1186
+ <<<statements>>>
1187
+ RETURN <<<return-value-expression>>>;
1188
+ END
1189
+ END_OF_SQL
1190
+ end
1191
+
1192
+ def alter_table_columns_sql_statements(col_pairs)
1193
+ col_pairs.map do |_, col|
1194
+ nullability = (col.nullable? ? "" : "NOT ") + "NULL"
1195
+ "ALTER TABLE #{name} ALTER COLUMN #{col.name} #{col.type} #{nullability};"
1196
+ end
1197
+ end
1198
+
1156
1199
  class << self
1157
1200
  def strip_identifier_quoting(s)
1158
1201
  case
@@ -704,6 +704,51 @@ module XMigra
704
704
  return parts.join("\n")
705
705
  end
706
706
 
707
+ def index_template_sql
708
+ XMigra.dedent(%Q{
709
+ CREATE INDEX [{filename}]
710
+ ON <<<table>>> (<<<columns>>>);
711
+ })
712
+ end
713
+
714
+ def view_definition_template_sql
715
+ XMigra.dedent(%Q{
716
+ CREATE VIEW [{filename}]
717
+ AS SELECT <<<query>>>;
718
+ })
719
+ end
720
+
721
+ def procedure_definition_template_sql
722
+ raise XMigra::NewAccessArtifactAdder::UnsupportedArtifactType.new(:procedure, SYSTEM_NAME)
723
+ end
724
+
725
+ def function_definition_template_sql
726
+ XMigra.dedent(%Q{
727
+ CREATE FUNCTION [{filename}] (
728
+ <<<parameters>>>
729
+ ) RETURNS <<<return-type>>>
730
+ AS $$
731
+ <<<function-body>>>
732
+ $$ LANGUAGE plpgsql;
733
+ })
734
+ end
735
+
736
+ def alter_table_columns_sql_statements(col_pairs)
737
+ col_pairs.flat_map do |old_col, col|
738
+ [].tap do |parts|
739
+ if !old_col.nullable? && col.nullable?
740
+ parts << "ALTER TABLE #{name} ALTER COLUMN #{col.name} DROP NOT NULL;"
741
+ end
742
+ if old_col.type != col.type
743
+ parts << "ALTER TABLE #{name} ALTER COLUMN #{col.name} TYPE #{col.type};"
744
+ end
745
+ if old_col.nullable? && !col.nullable?
746
+ parts << "ALTER TABLE #{name} ALTER COLUMN #{col.name} SET NOT NULL;"
747
+ end
748
+ end
749
+ end
750
+ end
751
+
707
752
  class <<self
708
753
  def in_plpgsql(*args)
709
754
  variables = args[0].kind_of?(Hash) ? args.shift : {}
@@ -0,0 +1,160 @@
1
+
2
+ module XMigra
3
+ module DeclarativeMigration
4
+ VALID_GOALS = %w{creation adoption revision renunciation destruction}
5
+ GOAL_KEY = 'does'
6
+ TARGET_KEY = 'of object'
7
+ DECLARATION_VERSION_KEY = 'to realize'
8
+ QUALIFICATION_KEY = 'implementation qualification'
9
+ SUBDIR = 'declarative'
10
+
11
+ class MissingImplementationError < Error
12
+ COMMAND_LINE_HELP = "The '%prog impdecl' command may help resolve this error."
13
+ end
14
+
15
+ class QuestionableImplementationError < Error; end
16
+
17
+ Missing = Class.new do
18
+ def goal
19
+ :newly_managed_object
20
+ end
21
+
22
+ def declarative_status
23
+ :unimplemented
24
+ end
25
+
26
+ def delta(file_path)
27
+ Pathname(file_path).readlines.map {|l| '+' + l}.join('')
28
+ end
29
+ end.new
30
+
31
+ module ChainSupport
32
+ def latest_declarative_implementations
33
+ @latest_declarative_implementations ||= Hash.new do |h, file_path|
34
+ ext_path = Pathname(file_path).expand_path
35
+ if h.has_key? ext_path
36
+ next h[ext_path]
37
+ end
38
+ raise Error, (
39
+ "Unexpected file path '#{file_path}', known file paths:" +
40
+ h.keys.collect {|kp| " #{kp}\n"}.join('')
41
+ )
42
+ end.tap do |files|
43
+ each do |migration|
44
+ # Skip non-declarative migrations
45
+ next unless migration.is_a? DeclarativeMigration
46
+ if (
47
+ [:renunciation, :destruction].include?(migration.goal) &&
48
+ migration.declarative_status == :missing
49
+ )
50
+ files.delete(migration.declarative_file_path)
51
+ else
52
+ files[migration.declarative_file_path] = migration
53
+ end
54
+ end
55
+
56
+ Dir.glob(Pathname(path).join(SUBDIR, '*.yaml').to_s) do |decl_file|
57
+ decl_file_path = Pathname(decl_file).expand_path
58
+ next if files.has_key?(decl_file_path)
59
+ files[decl_file_path] = DeclarativeMigration::Missing
60
+ end
61
+
62
+ files.freeze
63
+ end
64
+ end
65
+
66
+ def unimplemented_declaratives
67
+ @unimplemented_declaratives ||= latest_declarative_implementations.reject do |file_path, migration|
68
+ [:equal, :older].include? migration.declarative_status
69
+ end.keys
70
+ end
71
+
72
+ def check_declaratives_current!
73
+ unless unimplemented_declaratives.empty?
74
+ raise(
75
+ MissingImplementationError,
76
+ "Declaratives without migration implementing current state:\n" +
77
+ unimplemented_declaratives.collect {|df| " #{df.basename('.yaml')}\n"}.join("")
78
+ )
79
+ end
80
+
81
+ questionable_migrations = latest_declarative_implementations.values.select {|m| m.questionable?}
82
+ unless questionable_migrations.empty?
83
+ raise(
84
+ QuestionableImplementationError,
85
+ "Implementing migrations with questionable SQL:\n" +
86
+ questionable_migrations.collect {|m| " #{m.file_path}\n"}.join("")
87
+ )
88
+ end
89
+
90
+ questionable_migrations = latest_declarative_implementations.values.each do |m|
91
+ next unless m.management_migration?
92
+ raise(
93
+ QuestionableImplementationError,
94
+ "#{m.file_path} cannot execute SQL for a declarative #{m.goal}"
95
+ ) unless m.sql.nil? || m.sql.empty?
96
+ end
97
+ end
98
+ end
99
+
100
+ def goal
101
+ @declarative_goal ||= @all_info[GOAL_KEY].tap do |val|
102
+ raise(ArgumentError, "'#{GOAL_KEY}' must be one of: #{VALID_GOALS.join(', ')}") unless VALID_GOALS.include?(val)
103
+ end.to_sym
104
+ end
105
+
106
+ def affected_object
107
+ @declarative_target ||= @all_info[TARGET_KEY].dup.freeze
108
+ end
109
+
110
+ # Override the way the base class handles changes -- this integrates with
111
+ # the "history" command
112
+ def changes
113
+ if management_migration?
114
+ []
115
+ else
116
+ [affected_object]
117
+ end
118
+ end
119
+
120
+ def sql
121
+ if management_migration?
122
+ ''
123
+ else
124
+ super()
125
+ end
126
+ end
127
+
128
+ def management_migration?
129
+ [:adoption, :renunciation].include? goal
130
+ end
131
+
132
+ # This method is only used when the declarative file has uncommitted
133
+ # modifications and the migration file is uncommitted
134
+ def implemented_version
135
+ @declarative_implemented_ver ||= (@all_info[DECLARATION_VERSION_KEY].dup.freeze if vcs_uncommitted?)
136
+ end
137
+
138
+ def implementation_of?(file_path)
139
+ XMigra.secure_digest(file_path.read) == implemented_version
140
+ end
141
+
142
+ def declarative_file_path
143
+ @declarative_file_path ||= Pathname(file_path).dirname.join(SUBDIR, affected_object + '.yaml')
144
+ end
145
+
146
+ def declarative_status
147
+ @declarative_status ||= begin
148
+ vcs_comparator(:expected_content_method=>:implementation_of?).relative_version(declarative_file_path)
149
+ end
150
+ end
151
+
152
+ def delta(file_path)
153
+ vcs_changes_from(vcs_latest_revision, file_path)
154
+ end
155
+
156
+ def questionable?
157
+ @all_info.has_key? QUALIFICATION_KEY
158
+ end
159
+ end
160
+ end