xmigra 1.5.1 → 1.6.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
  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