masamune 0.11.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.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +54 -0
  4. data/Rakefile +15 -0
  5. data/bin/masamune-elastic-mapreduce +4 -0
  6. data/bin/masamune-hive +4 -0
  7. data/bin/masamune-psql +4 -0
  8. data/bin/masamune-shell +4 -0
  9. data/lib/masamune.rb +56 -0
  10. data/lib/masamune/accumulate.rb +60 -0
  11. data/lib/masamune/actions.rb +38 -0
  12. data/lib/masamune/actions/data_flow.rb +131 -0
  13. data/lib/masamune/actions/date_parse.rb +75 -0
  14. data/lib/masamune/actions/elastic_mapreduce.rb +68 -0
  15. data/lib/masamune/actions/execute.rb +52 -0
  16. data/lib/masamune/actions/filesystem.rb +37 -0
  17. data/lib/masamune/actions/hadoop_filesystem.rb +40 -0
  18. data/lib/masamune/actions/hadoop_streaming.rb +41 -0
  19. data/lib/masamune/actions/hive.rb +74 -0
  20. data/lib/masamune/actions/postgres.rb +76 -0
  21. data/lib/masamune/actions/postgres_admin.rb +34 -0
  22. data/lib/masamune/actions/s3cmd.rb +44 -0
  23. data/lib/masamune/actions/transform.rb +89 -0
  24. data/lib/masamune/after_initialize_callbacks.rb +55 -0
  25. data/lib/masamune/cached_filesystem.rb +110 -0
  26. data/lib/masamune/commands.rb +37 -0
  27. data/lib/masamune/commands/elastic_mapreduce.rb +119 -0
  28. data/lib/masamune/commands/hadoop_filesystem.rb +57 -0
  29. data/lib/masamune/commands/hadoop_streaming.rb +116 -0
  30. data/lib/masamune/commands/hive.rb +178 -0
  31. data/lib/masamune/commands/interactive.rb +37 -0
  32. data/lib/masamune/commands/postgres.rb +128 -0
  33. data/lib/masamune/commands/postgres_admin.rb +72 -0
  34. data/lib/masamune/commands/postgres_common.rb +33 -0
  35. data/lib/masamune/commands/retry_with_backoff.rb +60 -0
  36. data/lib/masamune/commands/s3cmd.rb +70 -0
  37. data/lib/masamune/commands/shell.rb +202 -0
  38. data/lib/masamune/configuration.rb +195 -0
  39. data/lib/masamune/data_plan.rb +31 -0
  40. data/lib/masamune/data_plan/builder.rb +66 -0
  41. data/lib/masamune/data_plan/elem.rb +190 -0
  42. data/lib/masamune/data_plan/engine.rb +162 -0
  43. data/lib/masamune/data_plan/rule.rb +292 -0
  44. data/lib/masamune/data_plan/set.rb +176 -0
  45. data/lib/masamune/environment.rb +164 -0
  46. data/lib/masamune/filesystem.rb +567 -0
  47. data/lib/masamune/has_environment.rb +40 -0
  48. data/lib/masamune/helpers.rb +27 -0
  49. data/lib/masamune/helpers/postgres.rb +84 -0
  50. data/lib/masamune/io.rb +33 -0
  51. data/lib/masamune/last_element.rb +53 -0
  52. data/lib/masamune/method_logger.rb +41 -0
  53. data/lib/masamune/multi_io.rb +39 -0
  54. data/lib/masamune/schema.rb +36 -0
  55. data/lib/masamune/schema/catalog.rb +233 -0
  56. data/lib/masamune/schema/column.rb +527 -0
  57. data/lib/masamune/schema/dimension.rb +133 -0
  58. data/lib/masamune/schema/event.rb +121 -0
  59. data/lib/masamune/schema/fact.rb +133 -0
  60. data/lib/masamune/schema/map.rb +265 -0
  61. data/lib/masamune/schema/row.rb +133 -0
  62. data/lib/masamune/schema/store.rb +115 -0
  63. data/lib/masamune/schema/table.rb +308 -0
  64. data/lib/masamune/schema/table_reference.rb +76 -0
  65. data/lib/masamune/spec_helper.rb +23 -0
  66. data/lib/masamune/string_format.rb +34 -0
  67. data/lib/masamune/tasks/elastic_mapreduce_thor.rb +60 -0
  68. data/lib/masamune/tasks/hive_thor.rb +55 -0
  69. data/lib/masamune/tasks/postgres_thor.rb +47 -0
  70. data/lib/masamune/tasks/shell_thor.rb +63 -0
  71. data/lib/masamune/template.rb +77 -0
  72. data/lib/masamune/thor.rb +186 -0
  73. data/lib/masamune/thor_loader.rb +38 -0
  74. data/lib/masamune/topological_hash.rb +34 -0
  75. data/lib/masamune/transform.rb +47 -0
  76. data/lib/masamune/transform/bulk_upsert.psql.erb +64 -0
  77. data/lib/masamune/transform/bulk_upsert.rb +52 -0
  78. data/lib/masamune/transform/consolidate_dimension.rb +54 -0
  79. data/lib/masamune/transform/deduplicate_dimension.psql.erb +52 -0
  80. data/lib/masamune/transform/deduplicate_dimension.rb +53 -0
  81. data/lib/masamune/transform/define_event_view.hql.erb +51 -0
  82. data/lib/masamune/transform/define_event_view.rb +60 -0
  83. data/lib/masamune/transform/define_index.psql.erb +34 -0
  84. data/lib/masamune/transform/define_schema.hql.erb +23 -0
  85. data/lib/masamune/transform/define_schema.psql.erb +79 -0
  86. data/lib/masamune/transform/define_schema.rb +56 -0
  87. data/lib/masamune/transform/define_table.hql.erb +34 -0
  88. data/lib/masamune/transform/define_table.psql.erb +95 -0
  89. data/lib/masamune/transform/define_table.rb +40 -0
  90. data/lib/masamune/transform/define_unique.psql.erb +30 -0
  91. data/lib/masamune/transform/insert_reference_values.psql.erb +43 -0
  92. data/lib/masamune/transform/insert_reference_values.rb +64 -0
  93. data/lib/masamune/transform/load_dimension.rb +47 -0
  94. data/lib/masamune/transform/load_fact.rb +45 -0
  95. data/lib/masamune/transform/operator.rb +96 -0
  96. data/lib/masamune/transform/relabel_dimension.psql.erb +76 -0
  97. data/lib/masamune/transform/relabel_dimension.rb +39 -0
  98. data/lib/masamune/transform/rollup_fact.psql.erb +79 -0
  99. data/lib/masamune/transform/rollup_fact.rb +149 -0
  100. data/lib/masamune/transform/snapshot_dimension.psql.erb +75 -0
  101. data/lib/masamune/transform/snapshot_dimension.rb +74 -0
  102. data/lib/masamune/transform/stage_dimension.psql.erb +39 -0
  103. data/lib/masamune/transform/stage_dimension.rb +83 -0
  104. data/lib/masamune/transform/stage_fact.psql.erb +80 -0
  105. data/lib/masamune/transform/stage_fact.rb +111 -0
  106. data/lib/masamune/version.rb +25 -0
  107. data/spec/fixtures/aggregate.sql.erb +25 -0
  108. data/spec/fixtures/comment.sql.erb +27 -0
  109. data/spec/fixtures/invalid.sql.erb +23 -0
  110. data/spec/fixtures/relative.sql.erb +23 -0
  111. data/spec/fixtures/simple.sql.erb +28 -0
  112. data/spec/fixtures/whitespace.sql.erb +30 -0
  113. data/spec/masamune/actions/elastic_mapreduce_spec.rb +108 -0
  114. data/spec/masamune/actions/execute_spec.rb +50 -0
  115. data/spec/masamune/actions/hadoop_filesystem_spec.rb +44 -0
  116. data/spec/masamune/actions/hadoop_streaming_spec.rb +74 -0
  117. data/spec/masamune/actions/hive_spec.rb +117 -0
  118. data/spec/masamune/actions/postgres_admin_spec.rb +58 -0
  119. data/spec/masamune/actions/postgres_spec.rb +134 -0
  120. data/spec/masamune/actions/s3cmd_spec.rb +44 -0
  121. data/spec/masamune/actions/transform_spec.rb +144 -0
  122. data/spec/masamune/after_initialization_callbacks_spec.rb +61 -0
  123. data/spec/masamune/cached_filesystem_spec.rb +167 -0
  124. data/spec/masamune/commands/hadoop_filesystem_spec.rb +50 -0
  125. data/spec/masamune/commands/hadoop_streaming_spec.rb +106 -0
  126. data/spec/masamune/commands/hive_spec.rb +117 -0
  127. data/spec/masamune/commands/postgres_admin_spec.rb +69 -0
  128. data/spec/masamune/commands/postgres_spec.rb +100 -0
  129. data/spec/masamune/commands/retry_with_backoff_spec.rb +116 -0
  130. data/spec/masamune/commands/s3cmd_spec.rb +50 -0
  131. data/spec/masamune/commands/shell_spec.rb +101 -0
  132. data/spec/masamune/configuration_spec.rb +102 -0
  133. data/spec/masamune/data_plan/builder_spec.rb +91 -0
  134. data/spec/masamune/data_plan/elem_spec.rb +102 -0
  135. data/spec/masamune/data_plan/engine_spec.rb +356 -0
  136. data/spec/masamune/data_plan/rule_spec.rb +407 -0
  137. data/spec/masamune/data_plan/set_spec.rb +517 -0
  138. data/spec/masamune/environment_spec.rb +65 -0
  139. data/spec/masamune/filesystem_spec.rb +1421 -0
  140. data/spec/masamune/helpers/postgres_spec.rb +95 -0
  141. data/spec/masamune/schema/catalog_spec.rb +613 -0
  142. data/spec/masamune/schema/column_spec.rb +696 -0
  143. data/spec/masamune/schema/dimension_spec.rb +137 -0
  144. data/spec/masamune/schema/event_spec.rb +75 -0
  145. data/spec/masamune/schema/fact_spec.rb +117 -0
  146. data/spec/masamune/schema/map_spec.rb +593 -0
  147. data/spec/masamune/schema/row_spec.rb +28 -0
  148. data/spec/masamune/schema/store_spec.rb +49 -0
  149. data/spec/masamune/schema/table_spec.rb +395 -0
  150. data/spec/masamune/string_format_spec.rb +60 -0
  151. data/spec/masamune/tasks/elastic_mapreduce_thor_spec.rb +57 -0
  152. data/spec/masamune/tasks/hive_thor_spec.rb +75 -0
  153. data/spec/masamune/tasks/postgres_thor_spec.rb +42 -0
  154. data/spec/masamune/tasks/shell_thor_spec.rb +51 -0
  155. data/spec/masamune/template_spec.rb +77 -0
  156. data/spec/masamune/thor_spec.rb +238 -0
  157. data/spec/masamune/transform/bulk_upsert.dimension_spec.rb +200 -0
  158. data/spec/masamune/transform/consolidate_dimension_spec.rb +62 -0
  159. data/spec/masamune/transform/deduplicate_dimension_spec.rb +84 -0
  160. data/spec/masamune/transform/define_event_view_spec.rb +84 -0
  161. data/spec/masamune/transform/define_schema_spec.rb +83 -0
  162. data/spec/masamune/transform/define_table.dimension_spec.rb +306 -0
  163. data/spec/masamune/transform/define_table.fact_spec.rb +291 -0
  164. data/spec/masamune/transform/define_table.table_spec.rb +525 -0
  165. data/spec/masamune/transform/insert_reference_values.dimension_spec.rb +111 -0
  166. data/spec/masamune/transform/insert_reference_values.fact_spec.rb +149 -0
  167. data/spec/masamune/transform/load_dimension_spec.rb +76 -0
  168. data/spec/masamune/transform/load_fact_spec.rb +89 -0
  169. data/spec/masamune/transform/relabel_dimension_spec.rb +102 -0
  170. data/spec/masamune/transform/rollup_fact_spec.rb +333 -0
  171. data/spec/masamune/transform/snapshot_dimension_spec.rb +103 -0
  172. data/spec/masamune/transform/stage_dimension_spec.rb +115 -0
  173. data/spec/masamune/transform/stage_fact_spec.rb +204 -0
  174. data/spec/masamune_spec.rb +32 -0
  175. data/spec/spec_helper.rb +41 -0
  176. data/spec/support/masamune/example_group.rb +36 -0
  177. data/spec/support/masamune/mock_command.rb +99 -0
  178. data/spec/support/masamune/mock_delegate.rb +51 -0
  179. data/spec/support/masamune/mock_filesystem.rb +96 -0
  180. data/spec/support/masamune/thor_mute.rb +35 -0
  181. data/spec/support/rspec/example/action_example_group.rb +34 -0
  182. data/spec/support/rspec/example/task_example_group.rb +80 -0
  183. data/spec/support/rspec/example/transform_example_group.rb +36 -0
  184. data/spec/support/shared_examples/postgres_common_examples.rb +53 -0
  185. metadata +462 -0
@@ -0,0 +1,525 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'spec_helper'
24
+
25
+ describe Masamune::Transform::DefineTable do
26
+ subject { transform.define_table(target).to_s }
27
+
28
+ context 'for postgres table with columns' do
29
+ before do
30
+ catalog.schema :postgres do
31
+ table 'user' do
32
+ column 'tenant_id'
33
+ column 'user_id'
34
+ end
35
+ end
36
+ end
37
+
38
+ let(:target) { catalog.postgres.user_table }
39
+
40
+ it 'should render table template' do
41
+ is_expected.to eq <<-EOS.strip_heredoc
42
+ CREATE TABLE IF NOT EXISTS user_table
43
+ (
44
+ id SERIAL PRIMARY KEY,
45
+ tenant_id INTEGER NOT NULL,
46
+ user_id INTEGER NOT NULL
47
+ );
48
+ EOS
49
+ end
50
+ end
51
+
52
+ context 'for postgres table with index columns' do
53
+ before do
54
+ catalog.schema :postgres do
55
+ table 'user' do
56
+ column 'tenant_id', index: true
57
+ column 'user_id', index: true
58
+ end
59
+ end
60
+ end
61
+
62
+ let(:target) { catalog.postgres.user_table }
63
+
64
+ it 'should render table template' do
65
+ is_expected.to eq <<-EOS.strip_heredoc
66
+ CREATE TABLE IF NOT EXISTS user_table
67
+ (
68
+ id SERIAL PRIMARY KEY,
69
+ tenant_id INTEGER NOT NULL,
70
+ user_id INTEGER NOT NULL
71
+ );
72
+
73
+ DO $$ BEGIN
74
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_3854361_index') THEN
75
+ CREATE INDEX user_table_3854361_index ON user_table (tenant_id);
76
+ END IF; END $$;
77
+
78
+ DO $$ BEGIN
79
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e8701ad_index') THEN
80
+ CREATE INDEX user_table_e8701ad_index ON user_table (user_id);
81
+ END IF; END $$;
82
+ EOS
83
+ end
84
+ end
85
+
86
+ context 'for postgres table with multiple index columns' do
87
+ before do
88
+ catalog.schema :postgres do
89
+ table 'user' do
90
+ column 'tenant_id', index: ['tenant_id', 'shared']
91
+ column 'user_id', index: ['user_id', 'shared']
92
+ end
93
+ end
94
+ end
95
+
96
+ let(:target) { catalog.postgres.user_table }
97
+
98
+ it 'should render table template' do
99
+ is_expected.to eq <<-EOS.strip_heredoc
100
+ CREATE TABLE IF NOT EXISTS user_table
101
+ (
102
+ id SERIAL PRIMARY KEY,
103
+ tenant_id INTEGER NOT NULL,
104
+ user_id INTEGER NOT NULL
105
+ );
106
+
107
+ DO $$ BEGIN
108
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_3854361_index') THEN
109
+ CREATE INDEX user_table_3854361_index ON user_table (tenant_id);
110
+ END IF; END $$;
111
+
112
+ DO $$ BEGIN
113
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e8701ad_index') THEN
114
+ CREATE INDEX user_table_e8701ad_index ON user_table (user_id);
115
+ END IF; END $$;
116
+
117
+ DO $$ BEGIN
118
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e0e4295_index') THEN
119
+ CREATE INDEX user_table_e0e4295_index ON user_table (tenant_id, user_id);
120
+ END IF; END $$;
121
+ EOS
122
+ end
123
+ end
124
+
125
+ context 'for postgres table with multiple unique columns' do
126
+ before do
127
+ catalog.schema :postgres do
128
+ table 'user' do
129
+ column 'tenant_id', unique: ['shared']
130
+ column 'user_id', unique: ['user_id', 'shared']
131
+ end
132
+ end
133
+ end
134
+
135
+ let(:target) { catalog.postgres.user_table }
136
+
137
+ it 'should render table template' do
138
+ is_expected.to eq <<-EOS.strip_heredoc
139
+ CREATE TABLE IF NOT EXISTS user_table
140
+ (
141
+ id SERIAL PRIMARY KEY,
142
+ tenant_id INTEGER NOT NULL,
143
+ user_id INTEGER NOT NULL
144
+ );
145
+
146
+ DO $$ BEGIN
147
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e8701ad_key') THEN
148
+ ALTER TABLE user_table ADD CONSTRAINT user_table_e8701ad_key UNIQUE(user_id);
149
+ END IF; END $$;
150
+
151
+ DO $$ BEGIN
152
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e0e4295_key') THEN
153
+ ALTER TABLE user_table ADD CONSTRAINT user_table_e0e4295_key UNIQUE(tenant_id, user_id);
154
+ END IF; END $$;
155
+ EOS
156
+ end
157
+ end
158
+
159
+ context 'for postgres table with enum column' do
160
+ before do
161
+ catalog.schema :postgres do
162
+ table 'user' do
163
+ column 'tenant_id'
164
+ column 'user_id'
165
+ column 'state', type: :enum, sub_type: :user_state, values: %w(active inactive terminated), default: 'active'
166
+ end
167
+ end
168
+ end
169
+
170
+ let(:target) { catalog.postgres.user_table }
171
+
172
+ it 'should render table template' do
173
+ is_expected.to eq <<-EOS.strip_heredoc
174
+ DO $$ BEGIN
175
+ IF NOT EXISTS (SELECT 1 FROM pg_type t WHERE LOWER(t.typname) = LOWER('USER_STATE_TYPE')) THEN
176
+ CREATE TYPE USER_STATE_TYPE AS ENUM ('active', 'inactive', 'terminated');
177
+ END IF; END $$;
178
+
179
+ CREATE TABLE IF NOT EXISTS user_table
180
+ (
181
+ id SERIAL PRIMARY KEY,
182
+ tenant_id INTEGER NOT NULL,
183
+ user_id INTEGER NOT NULL,
184
+ state USER_STATE_TYPE NOT NULL DEFAULT 'active'::USER_STATE_TYPE
185
+ );
186
+ EOS
187
+ end
188
+ end
189
+
190
+ context 'for postgres table with surrogate_key columns override' do
191
+ before do
192
+ catalog.schema :postgres do
193
+ table 'user' do
194
+ column 'identifier', type: :uuid, surrogate_key: true
195
+ column 'name', type: :string
196
+ end
197
+ end
198
+ end
199
+
200
+ let(:target) { catalog.postgres.user_table }
201
+
202
+ it 'should render table template' do
203
+ is_expected.to eq <<-EOS.strip_heredoc
204
+ CREATE TABLE IF NOT EXISTS user_table
205
+ (
206
+ identifier UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
207
+ name VARCHAR NOT NULL
208
+ );
209
+ EOS
210
+ end
211
+ end
212
+
213
+ context 'for postgres table with partial values' do
214
+ before do
215
+ catalog.schema :postgres do
216
+ table 'user' do
217
+ column 'name', type: :string
218
+ column 'description', type: :string
219
+ row name: 'registered', description: 'Registered'
220
+ row name: 'active'
221
+ end
222
+ end
223
+ end
224
+
225
+ let(:target) { catalog.postgres.user_table }
226
+
227
+ it 'should render table template' do
228
+ is_expected.to eq <<-EOS.strip_heredoc
229
+ CREATE TABLE IF NOT EXISTS user_table
230
+ (
231
+ id SERIAL PRIMARY KEY,
232
+ name VARCHAR NOT NULL,
233
+ description VARCHAR NOT NULL
234
+ );
235
+
236
+ INSERT INTO user_table (name, description)
237
+ SELECT 'registered', 'Registered'
238
+ WHERE NOT EXISTS (SELECT 1 FROM user_table WHERE name = 'registered' AND description = 'Registered');
239
+
240
+ INSERT INTO user_table (name)
241
+ SELECT 'active'
242
+ WHERE NOT EXISTS (SELECT 1 FROM user_table WHERE name = 'active');
243
+ EOS
244
+ end
245
+ end
246
+
247
+ context 'for postgres table with shared unique index' do
248
+ before do
249
+ catalog.schema :postgres do
250
+ table 'user' do
251
+ column 'tenant_id', unique: 'tenant_and_user', index: 'tenant_and_user'
252
+ column 'user_id', unique: 'tenant_and_user', index: 'tenant_and_user'
253
+ end
254
+ end
255
+ end
256
+
257
+ let(:target) { catalog.postgres.user_table }
258
+
259
+ it 'should render table template' do
260
+ is_expected.to eq <<-EOS.strip_heredoc
261
+ CREATE TABLE IF NOT EXISTS user_table
262
+ (
263
+ id SERIAL PRIMARY KEY,
264
+ tenant_id INTEGER NOT NULL,
265
+ user_id INTEGER NOT NULL
266
+ );
267
+
268
+ DO $$ BEGIN
269
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e0e4295_key') THEN
270
+ ALTER TABLE user_table ADD CONSTRAINT user_table_e0e4295_key UNIQUE(tenant_id, user_id);
271
+ END IF; END $$;
272
+
273
+ DO $$ BEGIN
274
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e0e4295_index') THEN
275
+ CREATE UNIQUE INDEX user_table_e0e4295_index ON user_table (tenant_id, user_id);
276
+ END IF; END $$;
277
+ EOS
278
+ end
279
+ end
280
+
281
+ context 'for postgres table with multiple default and named rows' do
282
+ before do
283
+ catalog.schema :postgres do
284
+ table 'user' do
285
+ column 'tenant_id', type: :integer, natural_key: true
286
+ column 'user_id', type: :integer, natural_key: true
287
+ row tenant_id: 'default_tenant_id()', user_id: -1, attributes: {default: true}
288
+ row tenant_id: 'default_tenant_id()', user_id: -2, attributes: {id: 'unknown'}
289
+ end
290
+ end
291
+ end
292
+
293
+ let(:target) { catalog.postgres.user_table }
294
+
295
+ it 'should render table template' do
296
+ is_expected.to eq <<-EOS.strip_heredoc
297
+ CREATE TABLE IF NOT EXISTS user_table
298
+ (
299
+ id SERIAL PRIMARY KEY,
300
+ tenant_id INTEGER NOT NULL,
301
+ user_id INTEGER NOT NULL
302
+ );
303
+
304
+ DO $$ BEGIN
305
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_e0e4295_key') THEN
306
+ ALTER TABLE user_table ADD CONSTRAINT user_table_e0e4295_key UNIQUE(tenant_id, user_id);
307
+ END IF; END $$;
308
+
309
+ INSERT INTO user_table (tenant_id, user_id)
310
+ SELECT default_tenant_id(), -1
311
+ WHERE NOT EXISTS (SELECT 1 FROM user_table WHERE tenant_id = default_tenant_id() AND user_id = -1);
312
+
313
+ INSERT INTO user_table (tenant_id, user_id)
314
+ SELECT default_tenant_id(), -2
315
+ WHERE NOT EXISTS (SELECT 1 FROM user_table WHERE tenant_id = default_tenant_id() AND user_id = -2);
316
+
317
+ CREATE OR REPLACE FUNCTION default_user_id()
318
+ RETURNS INTEGER IMMUTABLE AS $$
319
+ SELECT -1;
320
+ $$ LANGUAGE SQL;
321
+
322
+ CREATE OR REPLACE FUNCTION default_user_table_id()
323
+ RETURNS INTEGER IMMUTABLE AS $$
324
+ SELECT id FROM user_table WHERE tenant_id = default_tenant_id() AND user_id = -1;
325
+ $$ LANGUAGE SQL;
326
+
327
+ CREATE OR REPLACE FUNCTION unknown_user_id()
328
+ RETURNS INTEGER IMMUTABLE AS $$
329
+ SELECT -2;
330
+ $$ LANGUAGE SQL;
331
+
332
+ CREATE OR REPLACE FUNCTION unknown_user_table_id()
333
+ RETURNS INTEGER IMMUTABLE AS $$
334
+ SELECT id FROM user_table WHERE tenant_id = default_tenant_id() AND user_id = -2;
335
+ $$ LANGUAGE SQL;
336
+ EOS
337
+ end
338
+ end
339
+
340
+ context 'for postgres table with referenced tables' do
341
+ before do
342
+ catalog.schema :postgres do
343
+ table 'user_account_state' do
344
+ column 'name', type: :string, unique: true
345
+ column 'description', type: :string
346
+ row name: 'registered', description: 'Registered'
347
+ row name: 'active', description: 'Active', attributes: { default: true }
348
+ row name: 'inactive', description: 'Inactive'
349
+ end
350
+
351
+ table 'user' do
352
+ references :user_account_state
353
+ column 'name', type: :string
354
+ end
355
+ end
356
+ end
357
+
358
+ let(:target) { catalog.postgres.user_table }
359
+
360
+ it 'should render table template' do
361
+ is_expected.to eq <<-EOS.strip_heredoc
362
+ CREATE TABLE IF NOT EXISTS user_table
363
+ (
364
+ id SERIAL PRIMARY KEY,
365
+ user_account_state_table_id INTEGER NOT NULL REFERENCES user_account_state_table(id) DEFAULT default_user_account_state_table_id(),
366
+ name VARCHAR NOT NULL
367
+ );
368
+
369
+ DO $$ BEGIN
370
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_bd2027e_index') THEN
371
+ CREATE INDEX user_table_bd2027e_index ON user_table (user_account_state_table_id);
372
+ END IF; END $$;
373
+ EOS
374
+ end
375
+ end
376
+
377
+ context 'for postgres table with labeled referenced table' do
378
+ before do
379
+ catalog.schema :postgres do
380
+ table 'user_account_state' do
381
+ column 'name', type: :string, unique: true
382
+ column 'description', type: :string
383
+ row name: 'active', description: 'Active', attributes: { default: true }
384
+ end
385
+
386
+ table 'user' do
387
+ references :user_account_state
388
+ references :user_account_state, label: 'hr', null: true, default: :null
389
+ column 'name', type: :string
390
+ end
391
+ end
392
+ end
393
+
394
+ let(:target) { catalog.postgres.user_table }
395
+
396
+ it 'should render table template' do
397
+ is_expected.to eq <<-EOS.strip_heredoc
398
+ CREATE TABLE IF NOT EXISTS user_table
399
+ (
400
+ id SERIAL PRIMARY KEY,
401
+ user_account_state_table_id INTEGER NOT NULL REFERENCES user_account_state_table(id) DEFAULT default_user_account_state_table_id(),
402
+ hr_user_account_state_table_id INTEGER REFERENCES user_account_state_table(id),
403
+ name VARCHAR NOT NULL
404
+ );
405
+
406
+ DO $$ BEGIN
407
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_bd2027e_index') THEN
408
+ CREATE INDEX user_table_bd2027e_index ON user_table (user_account_state_table_id);
409
+ END IF; END $$;
410
+
411
+ DO $$ BEGIN
412
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_074da4a_index') THEN
413
+ CREATE INDEX user_table_074da4a_index ON user_table (hr_user_account_state_table_id);
414
+ END IF; END $$;
415
+ EOS
416
+ end
417
+ end
418
+
419
+ context '#stage_table' do
420
+ before do
421
+ catalog.schema :postgres do
422
+ table 'user_account_state' do
423
+ column 'name', type: :string, unique: true
424
+ column 'description', type: :string
425
+ end
426
+
427
+ table 'user' do
428
+ references :user_account_state
429
+ references :user_account_state, label: 'hr'
430
+ column 'name', type: :string
431
+ end
432
+ end
433
+ end
434
+
435
+ let(:table) { catalog.postgres.user_table }
436
+ let(:target) { table.stage_table(columns: columns) }
437
+
438
+ context 'without specified columns' do
439
+ let(:columns) { [] }
440
+
441
+ it 'should render table template' do
442
+ is_expected.to eq <<-EOS.strip_heredoc
443
+ CREATE TEMPORARY TABLE IF NOT EXISTS user_table_stage
444
+ (
445
+ user_account_state_table_id INTEGER,
446
+ hr_user_account_state_table_id INTEGER,
447
+ name VARCHAR
448
+ );
449
+
450
+ CREATE INDEX user_table_stage_bd2027e_index ON user_table_stage (user_account_state_table_id);
451
+ CREATE INDEX user_table_stage_074da4a_index ON user_table_stage (hr_user_account_state_table_id);
452
+ EOS
453
+ end
454
+ end
455
+
456
+ context 'for postgres table with all specified columns' do
457
+ let(:columns) { %w(hr_user_account_state_table_id user_account_state_table_id name) }
458
+
459
+ it 'should render table template' do
460
+ is_expected.to eq <<-EOS.strip_heredoc
461
+ CREATE TEMPORARY TABLE IF NOT EXISTS user_table_stage
462
+ (
463
+ hr_user_account_state_table_id INTEGER,
464
+ user_account_state_table_id INTEGER,
465
+ name VARCHAR
466
+ );
467
+
468
+ CREATE INDEX user_table_stage_074da4a_index ON user_table_stage (hr_user_account_state_table_id);
469
+ CREATE INDEX user_table_stage_bd2027e_index ON user_table_stage (user_account_state_table_id);
470
+ EOS
471
+ end
472
+ end
473
+
474
+ context 'for postgres table with all specified columns in denormalized form' do
475
+ let(:columns) { %w(hr_user_account_state.name user_account_state.name name) }
476
+
477
+ it 'should render table template' do
478
+ is_expected.to eq <<-EOS.strip_heredoc
479
+ CREATE TEMPORARY TABLE IF NOT EXISTS user_table_stage
480
+ (
481
+ hr_user_account_state_table_name VARCHAR,
482
+ user_account_state_table_name VARCHAR,
483
+ name VARCHAR
484
+ );
485
+ EOS
486
+ end
487
+ end
488
+ end
489
+
490
+ context 'for postgres table with sequence column' do
491
+ before do
492
+ catalog.schema :postgres do
493
+ table 'user' do
494
+ column 'id', type: :sequence, surrogate_key: true, sequence_offset: 1024
495
+ column 'tenant_id'
496
+ column 'user_id'
497
+ end
498
+ end
499
+ end
500
+
501
+ let(:target) { catalog.postgres.user_table }
502
+
503
+ it 'should render table template' do
504
+ is_expected.to eq <<-EOS.strip_heredoc
505
+ DO $$ BEGIN
506
+ IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_table_id_seq') THEN
507
+ CREATE SEQUENCE user_table_id_seq;
508
+ ALTER SEQUENCE user_table_id_seq RESTART 1024;
509
+ END IF; END $$;
510
+
511
+ CREATE TABLE IF NOT EXISTS user_table
512
+ (
513
+ id INTEGER PRIMARY KEY DEFAULT nextval('user_table_id_seq'),
514
+ tenant_id INTEGER NOT NULL,
515
+ user_id INTEGER NOT NULL
516
+ );
517
+
518
+ DO $$ BEGIN
519
+ IF NOT EXISTS (SELECT 1 WHERE sequence_owner('user_table_id_seq') = 'user_table.id') THEN
520
+ ALTER SEQUENCE user_table_id_seq OWNED BY user_table.id;
521
+ END IF; END $$;
522
+ EOS
523
+ end
524
+ end
525
+ end