masamune 0.12.3 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/masamune-dump +4 -0
- data/lib/masamune/schema/column.rb +2 -20
- data/lib/masamune/schema/dimension.rb +12 -11
- data/lib/masamune/schema/fact.rb +10 -1
- data/lib/masamune/schema/map.rb +3 -3
- data/lib/masamune/schema/row.rb +1 -1
- data/lib/masamune/schema/table.rb +55 -23
- data/lib/masamune/schema/table_reference.rb +5 -0
- data/lib/masamune/tasks/dump_thor.rb +58 -0
- data/lib/masamune/tasks/shell_thor.rb +0 -19
- data/lib/masamune/template.rb +1 -2
- data/lib/masamune/transform/define_foreign_key.psql.erb +39 -0
- data/lib/masamune/transform/define_index.psql.erb +7 -3
- data/lib/masamune/transform/define_schema.rb +3 -3
- data/lib/masamune/transform/define_table.psql.erb +24 -3
- data/lib/masamune/transform/define_table.rb +16 -2
- data/lib/masamune/transform/define_unique.psql.erb +1 -1
- data/lib/masamune/transform/denormalize_table.rb +5 -1
- data/lib/masamune/transform/replace_table.psql.erb +9 -13
- data/lib/masamune/transform/stage_fact.rb +16 -10
- data/lib/masamune/version.rb +1 -1
- data/spec/masamune/schema/map_spec.rb +1 -1
- data/spec/masamune/tasks/dump_thor_spec.rb +42 -0
- data/spec/masamune/tasks/shell_thor_spec.rb +0 -11
- data/spec/masamune/template_spec.rb +5 -0
- data/spec/masamune/transform/define_table.dimension_spec.rb +81 -52
- data/spec/masamune/transform/define_table.fact_spec.rb +27 -63
- data/spec/masamune/transform/define_table.table_spec.rb +397 -32
- data/spec/masamune/transform/denormalize_table_spec.rb +20 -0
- data/spec/masamune/transform/rollup_fact_spec.rb +54 -54
- data/spec/masamune/transform/stage_fact_spec.rb +57 -34
- metadata +9 -3
data/lib/masamune/template.rb
CHANGED
@@ -27,6 +27,7 @@ module Masamune
|
|
27
27
|
class Template
|
28
28
|
def initialize(paths = [])
|
29
29
|
@paths = Array.wrap(paths)
|
30
|
+
@paths << File.join(File.dirname(__FILE__), 'transform')
|
30
31
|
end
|
31
32
|
|
32
33
|
def render(template, parameters = {})
|
@@ -48,7 +49,6 @@ module Masamune
|
|
48
49
|
|
49
50
|
class << self
|
50
51
|
def render_to_file(template, parameters = {})
|
51
|
-
raise IOError, "File not found: #{template}" unless File.exists?(template)
|
52
52
|
Tempfile.new('masamune').tap do |file|
|
53
53
|
file.write(render_to_string(template, parameters))
|
54
54
|
file.close
|
@@ -56,7 +56,6 @@ module Masamune
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def render_to_string(template, parameters = {})
|
59
|
-
raise IOError, "File not found: #{template}" unless File.exists?(template)
|
60
59
|
instance = Template.new(File.dirname(template))
|
61
60
|
combine instance.render(template, parameters)
|
62
61
|
end
|
@@ -0,0 +1,39 @@
|
|
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
|
+
<%
|
24
|
+
skip_check_exist ||= false
|
25
|
+
skip_check_valid ||= false
|
26
|
+
%>
|
27
|
+
|
28
|
+
<%- target.foreign_key_constraints.each do |id, column_names, reference_name, reference_column_names| -%>
|
29
|
+
<%- foreign_key_name = "#{target.name}_#{id}_fkey" -%>
|
30
|
+
<%- unless target.temporary? || skip_check_exist -%>
|
31
|
+
DO $$ BEGIN
|
32
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = '<%= foreign_key_name %>') THEN
|
33
|
+
<%- end -%>
|
34
|
+
ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= foreign_key_name %> FOREIGN KEY (<%= column_names.join(', ') %>) REFERENCES <%= reference_name %>(<%= reference_column_names.join(', ') %>)<%= ' NOT VALID DEFERRABLE INITIALLY DEFERRED' if skip_check_valid %>;
|
35
|
+
<%- unless target.temporary? || skip_check_exist -%>
|
36
|
+
END IF; END $$;
|
37
|
+
|
38
|
+
<%- end -%>
|
39
|
+
<%- end -%>
|
@@ -20,14 +20,18 @@
|
|
20
20
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
-- THE SOFTWARE.
|
22
22
|
|
23
|
+
<%
|
24
|
+
skip_check_exist ||= false
|
25
|
+
%>
|
26
|
+
|
23
27
|
<%- target.index_columns.each do |column_names, unique, id| -%>
|
24
28
|
<%- index_name = "#{target.name}_#{id}_index" -%>
|
25
|
-
<%- unless target.temporary? -%>
|
29
|
+
<%- unless target.temporary? || skip_check_exist -%>
|
26
30
|
DO $$ BEGIN
|
27
31
|
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '<%= index_name %>') THEN
|
28
32
|
<%- end -%>
|
29
|
-
CREATE <%= unique ? 'UNIQUE INDEX' : 'INDEX' %> <%= index_name %> ON <%= target.name %> (<%= column_names.join(', ') %>);
|
30
|
-
<%- unless target.temporary? -%>
|
33
|
+
CREATE <%= unique ? 'UNIQUE INDEX' : 'INDEX' %> <%= index_name %> ON <%= target.name %> (<%= column_names.to_a.join(', ') %>);
|
34
|
+
<%- unless target.temporary? || skip_check_exist -%>
|
31
35
|
END IF; END $$;
|
32
36
|
|
33
37
|
<%- end -%>
|
@@ -28,18 +28,18 @@ module Masamune::Transform
|
|
28
28
|
|
29
29
|
extend ActiveSupport::Concern
|
30
30
|
|
31
|
-
def define_schema(catalog, store_id)
|
31
|
+
def define_schema(catalog, store_id, options = {})
|
32
32
|
context = catalog[store_id]
|
33
33
|
operators = []
|
34
34
|
|
35
35
|
operators += context.extra(:pre)
|
36
36
|
|
37
37
|
context.dimensions.each do |_, dimension|
|
38
|
-
operators << define_table(dimension)
|
38
|
+
operators << define_table(dimension, [], options)
|
39
39
|
end
|
40
40
|
|
41
41
|
context.facts.each do |_, fact|
|
42
|
-
operators << define_table(fact)
|
42
|
+
operators << define_table(fact, [], options)
|
43
43
|
end
|
44
44
|
|
45
45
|
operators += context.extra(:post)
|
@@ -20,10 +20,15 @@
|
|
20
20
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
-- THE SOFTWARE.
|
22
22
|
|
23
|
-
<%
|
23
|
+
<%
|
24
|
+
files ||= []
|
25
|
+
with_index = locals.fetch(:with_index, true)
|
26
|
+
with_foreign_key = locals.fetch(:with_foreign_key, true)
|
27
|
+
with_unique_constraint = locals.fetch(:with_unique_constraint, true)
|
28
|
+
%>
|
24
29
|
|
25
30
|
<%- target.children.each do |child| -%>
|
26
|
-
<%= render 'define_table.psql.erb', target: child %>
|
31
|
+
<%= render 'define_table.psql.erb', target: child, **locals.except(:target, :files) %>
|
27
32
|
<%- end -%>
|
28
33
|
|
29
34
|
<%- target.enum_columns.each do |_, column| -%>
|
@@ -52,6 +57,17 @@ CREATE TABLE IF NOT EXISTS <%= target.name %>
|
|
52
57
|
<%- end -%>
|
53
58
|
);
|
54
59
|
|
60
|
+
<%- unless target.temporary? || target.primary_keys.empty? -%>
|
61
|
+
DO $$ BEGIN
|
62
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '<%= target.name %>_pkey') THEN
|
63
|
+
ALTER TABLE <%= target.name %> ADD PRIMARY KEY (<%= target.primary_keys.map(&:name).join(', ') %>);
|
64
|
+
END IF; END $$;
|
65
|
+
<%- end -%>
|
66
|
+
|
67
|
+
<%- if with_foreign_key && !target.delay_constraints? -%>
|
68
|
+
<%= render 'define_foreign_key.psql.erb', target: target %>
|
69
|
+
<%- end -%>
|
70
|
+
|
55
71
|
<%- target.sequence_columns.each do |_, column| -%>
|
56
72
|
DO $$ BEGIN
|
57
73
|
IF NOT EXISTS (SELECT 1 WHERE sequence_owner('<%= column.sequence_id %>') = '<%= column.qualified_name %>') THEN
|
@@ -69,8 +85,13 @@ END IF; END $$;
|
|
69
85
|
COPY <%= target.name %> FROM '<%= file %>' WITH (<%= copy_options.join(", ") %>);
|
70
86
|
<%- end -%>
|
71
87
|
|
88
|
+
<%- if with_unique_constraint && !target.delay_constraints? -%>
|
72
89
|
<%= render 'define_unique.psql.erb', target: target %>
|
90
|
+
<%- end -%>
|
91
|
+
|
92
|
+
<%- if with_index && !target.delay_index? -%>
|
73
93
|
<%= render 'define_index.psql.erb', target: target %>
|
94
|
+
<%- end -%>
|
74
95
|
|
75
96
|
<% target.insert_rows.each do |row| %>
|
76
97
|
INSERT INTO <%= target.name %> (<%= row.insert_columns.join(', ') %>)
|
@@ -86,7 +107,7 @@ ANALYZE <%= target.name %>;
|
|
86
107
|
<%- row.natural_keys.each do |column| -%>
|
87
108
|
CREATE OR REPLACE FUNCTION <%= row.name(column) %>
|
88
109
|
RETURNS <%= column.sql_type %> IMMUTABLE AS $$
|
89
|
-
SELECT <%= row.sql_value(column)
|
110
|
+
SELECT CAST(<%= row.sql_value(column) %> AS <%= column.sql_type %>);
|
90
111
|
$$ LANGUAGE SQL;
|
91
112
|
|
92
113
|
<%- end -%>
|
@@ -24,13 +24,27 @@ module Masamune::Transform
|
|
24
24
|
module DefineTable
|
25
25
|
extend ActiveSupport::Concern
|
26
26
|
|
27
|
-
def define_table(target, files = [])
|
27
|
+
def define_table(target, files = [], options = {})
|
28
28
|
return if target.implicit
|
29
|
-
Operator.new(__method__, target: target, files: Masamune::Schema::Map.convert_files(files), presenters: { hive: Hive }).tap do |operator|
|
29
|
+
Operator.new(__method__, target: target, files: Masamune::Schema::Map.convert_files(files), **options.slice(:with_index, :with_foreign_key, :with_unique_constraint), presenters: { postgres: Postgres, hive: Hive }).tap do |operator|
|
30
30
|
logger.debug("#{target.id}\n" + operator.to_s) if target.debug
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
class Postgres < SimpleDelegator
|
35
|
+
def children
|
36
|
+
super.map { |child| self.class.new(child) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def delay_index?
|
40
|
+
type == :fact
|
41
|
+
end
|
42
|
+
|
43
|
+
def delay_constraints?
|
44
|
+
type == :fact
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
34
48
|
class Hive < SimpleDelegator
|
35
49
|
def partition_by
|
36
50
|
return unless partitions.any?
|
@@ -24,7 +24,7 @@
|
|
24
24
|
<%- constraint_name = "#{target.name}_#{id}_key" -%>
|
25
25
|
DO $$ BEGIN
|
26
26
|
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '<%= constraint_name %>') THEN
|
27
|
-
ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= constraint_name %> UNIQUE(<%= column_names.join(', ') %>);
|
27
|
+
ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= constraint_name %> UNIQUE(<%= column_names.to_a.join(', ') %>);
|
28
28
|
END IF; END $$;
|
29
29
|
|
30
30
|
<%- end -%>
|
@@ -54,6 +54,10 @@ module Masamune::Transform
|
|
54
54
|
end
|
55
55
|
method_with_last_element :select_columns
|
56
56
|
|
57
|
+
def join_alias(reference)
|
58
|
+
reference.label ? "#{reference.name} AS #{[reference.label, reference.name].compact.join('_')}" : reference.name
|
59
|
+
end
|
60
|
+
|
57
61
|
def join_conditions(column_names)
|
58
62
|
{}.tap do |conditions|
|
59
63
|
column_names.each do |column_name|
|
@@ -63,7 +67,7 @@ module Masamune::Transform
|
|
63
67
|
next unless adjacent_reference
|
64
68
|
adjacent_column = columns[adjacent_reference.foreign_key_name]
|
65
69
|
next unless adjacent_column
|
66
|
-
conditions[column.reference
|
70
|
+
conditions[join_alias(column.reference)] = "#{column.reference.surrogate_key.qualified_name(column.reference.label)} = #{adjacent_column.qualified_name}"
|
67
71
|
end
|
68
72
|
end
|
69
73
|
end
|
@@ -30,13 +30,12 @@ BEGIN;
|
|
30
30
|
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
31
31
|
|
32
32
|
ALTER TABLE <%= target.name %> DROP CONSTRAINT IF EXISTS <%= target.name %>_time_key_check;
|
33
|
-
<%- target.
|
34
|
-
<%-
|
35
|
-
ALTER TABLE <%= target.name %> DROP CONSTRAINT IF EXISTS <%=
|
36
|
-
<%- end -%>
|
33
|
+
<%- target.foreign_key_constraints.each do |id, _, _, _| -%>
|
34
|
+
<%- foreign_key_name = "#{target.name}_#{id}_fkey" -%>
|
35
|
+
ALTER TABLE <%= target.name %> DROP CONSTRAINT IF EXISTS <%= foreign_key_name %> CASCADE;
|
37
36
|
<%- end -%>
|
38
37
|
|
39
|
-
<%- target.index_columns.each do |
|
38
|
+
<%- target.index_columns.each do |_, _, id| -%>
|
40
39
|
<%- index_name = "#{target.name}_#{id}_index" -%>
|
41
40
|
DROP INDEX IF EXISTS <%= index_name %>;
|
42
41
|
<%- end -%>
|
@@ -44,18 +43,15 @@ DROP INDEX IF EXISTS <%= index_name %>;
|
|
44
43
|
ALTER TABLE <%= target.name %> RENAME TO <%= target_tmp.name %>;
|
45
44
|
ALTER TABLE <%= source.name %> RENAME TO <%= target.name %>;
|
46
45
|
|
46
|
+
<%- if target.parent -%>
|
47
47
|
ALTER TABLE <%= target.name %> INHERIT <%= target.parent.name %>;
|
48
|
-
ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= target.name %>_time_key_check <%= target.constraints %>;
|
49
|
-
<%- target.foreign_key_columns.each do |column| -%>
|
50
|
-
<%- if column.reference_constraint -%>
|
51
|
-
ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= target.name %>_<%= column.name %>_fkey FOREIGN KEY (<%= column.name %>) <%= column.reference_constraint %> NOT VALID DEFERRABLE INITIALLY DEFERRED;
|
52
48
|
<%- end -%>
|
49
|
+
<%- if target.constraints -%>
|
50
|
+
ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= target.name %>_time_key_check <%= target.constraints %>;
|
53
51
|
<%- end -%>
|
52
|
+
<%= render 'define_foreign_key.psql.erb', target: target, skip_check_exist: true, skip_check_valid: true %>
|
54
53
|
|
55
|
-
|
56
|
-
<%- index_name = "#{target.name}_#{id}_index" -%>
|
57
|
-
CREATE <%= unique ? 'UNIQUE INDEX' : 'INDEX' %> <%= index_name %> ON <%= target.name %> (<%= column_names.join(', ') %>);
|
58
|
-
<%- end -%>
|
54
|
+
<%= render 'define_index.psql.erb', target: target, skip_check_exist: true %>
|
59
55
|
|
60
56
|
ANALYZE <%= target.name %>;
|
61
57
|
|
@@ -48,7 +48,7 @@ module Masamune::Transform
|
|
48
48
|
shared_columns(source).values.map do |columns|
|
49
49
|
column = columns.first
|
50
50
|
if !column.degenerate? && reference = column.reference
|
51
|
-
reference.surrogate_key.qualified_name
|
51
|
+
reference.surrogate_key.qualified_name(column.reference.label)
|
52
52
|
else
|
53
53
|
column.qualified_name
|
54
54
|
end
|
@@ -56,6 +56,10 @@ module Masamune::Transform
|
|
56
56
|
end
|
57
57
|
method_with_last_element :insert_values
|
58
58
|
|
59
|
+
def join_alias(reference)
|
60
|
+
reference.label ? "#{reference.name} AS #{[reference.label, reference.name].compact.join('_')}" : reference.name
|
61
|
+
end
|
62
|
+
|
59
63
|
def join_conditions(source)
|
60
64
|
join_columns = shared_columns(source).values.flatten
|
61
65
|
join_columns = join_columns.select { |column| column.reference }
|
@@ -64,15 +68,16 @@ module Masamune::Transform
|
|
64
68
|
dependencies = Masamune::TopologicalHash.new
|
65
69
|
conditions = Hash.new { |h,k| h[k] = [] }
|
66
70
|
join_columns.each do |reference, columns|
|
71
|
+
reference_name = join_alias(reference)
|
67
72
|
columns.each do |column|
|
68
73
|
next if column.degenerate?
|
69
|
-
dependencies[
|
74
|
+
dependencies[reference_name] ||= []
|
70
75
|
cross_references = cross_references(column)
|
71
76
|
coalesce_values = []
|
72
77
|
|
73
78
|
if cross_references.any?
|
74
|
-
dependencies[
|
75
|
-
coalesce_values << cross_references.map { |
|
79
|
+
dependencies[reference_name] += cross_references.map { |reference, _| join_alias(reference) }
|
80
|
+
coalesce_values << cross_references.map { |reference, column| column.qualified_name(reference.label) }
|
76
81
|
end
|
77
82
|
|
78
83
|
if column.reference && !column.reference.default.nil?
|
@@ -81,14 +86,14 @@ module Masamune::Transform
|
|
81
86
|
coalesce_values << column.adjacent.sql_value(column.adjacent.default)
|
82
87
|
end
|
83
88
|
|
84
|
-
conditions[
|
89
|
+
conditions[reference_name] << (coalesce_values.any? ?
|
85
90
|
"#{column.foreign_key_name} = COALESCE(#{column.qualified_name}, #{coalesce_values.join(', ')})" :
|
86
91
|
"#{column.foreign_key_name} = #{column.qualified_name}")
|
87
92
|
end
|
88
93
|
if reference.type == :two || reference.type == :four
|
89
|
-
join_key_a = "TO_TIMESTAMP(#{source.time_key.qualified_name}) BETWEEN #{reference.start_key.qualified_name} AND COALESCE(#{reference.end_key.qualified_name}, 'INFINITY')"
|
90
|
-
join_key_b = "TO_TIMESTAMP(#{source.time_key.qualified_name}) < #{reference.start_key.qualified_name} AND #{reference.version_key.qualified_name} = 1"
|
91
|
-
conditions[
|
94
|
+
join_key_a = "TO_TIMESTAMP(#{source.time_key.qualified_name}) BETWEEN #{reference.start_key.qualified_name(reference.label)} AND COALESCE(#{reference.end_key.qualified_name(reference.label)}, 'INFINITY')"
|
95
|
+
join_key_b = "TO_TIMESTAMP(#{source.time_key.qualified_name}) < #{reference.start_key.qualified_name(reference.label)} AND #{reference.version_key.qualified_name(reference.label)} = 1"
|
96
|
+
conditions[reference_name] << "((#{join_key_a}) OR (#{join_key_b}))"
|
92
97
|
end
|
93
98
|
end
|
94
99
|
conditions.slice(*dependencies.tsort)
|
@@ -99,8 +104,9 @@ module Masamune::Transform
|
|
99
104
|
def cross_references(column)
|
100
105
|
return {} unless column.natural_key || column.adjacent.try(:natural_key)
|
101
106
|
{}.tap do |result|
|
102
|
-
|
103
|
-
|
107
|
+
column.reference.through.each do |reference_id|
|
108
|
+
reference = references[reference_id]
|
109
|
+
if reference.columns[column.id]
|
104
110
|
result[reference] = reference.columns[column.id]
|
105
111
|
end
|
106
112
|
end
|
data/lib/masamune/version.rb
CHANGED
@@ -223,7 +223,7 @@ describe Masamune::Schema::Map do
|
|
223
223
|
end
|
224
224
|
|
225
225
|
before do
|
226
|
-
expect(environment.logger).to receive(:warn).with(/failed to process
|
226
|
+
expect(environment.logger).to receive(:warn).with(/failed to process row for #{target.name}/).ordered
|
227
227
|
expect(environment.logger).to receive(:warn).with(/failed to parse '{.*}' for #{source.name}/).ordered
|
228
228
|
end
|
229
229
|
|
@@ -0,0 +1,42 @@
|
|
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
|
+
require 'thor'
|
25
|
+
|
26
|
+
require 'masamune/tasks/dump_thor'
|
27
|
+
|
28
|
+
describe Masamune::Tasks::DumpThor do
|
29
|
+
context 'with help command ' do
|
30
|
+
let(:command) { 'help' }
|
31
|
+
it_behaves_like 'command usage'
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with no arguments' do
|
35
|
+
it 'exits with status code 0 and prints catalog' do
|
36
|
+
expect { cli_invocation }.to raise_error { |e|
|
37
|
+
expect(e).to be_a(SystemExit)
|
38
|
+
expect(e.status).to eq(0)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -37,15 +37,4 @@ describe Masamune::Tasks::ShellThor do
|
|
37
37
|
cli_invocation
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
41
|
-
context 'with --dump' do
|
42
|
-
let(:options) { ['--dump'] }
|
43
|
-
|
44
|
-
it 'exits with status code 0 and prints catalog' do
|
45
|
-
expect { cli_invocation }.to raise_error { |e|
|
46
|
-
expect(e).to be_a(SystemExit)
|
47
|
-
expect(e.status).to eq(0)
|
48
|
-
}
|
49
|
-
end
|
50
|
-
end
|
51
40
|
end
|
@@ -73,5 +73,10 @@ describe Masamune::Template do
|
|
73
73
|
let(:template) { File.join(File.dirname(__FILE__), '..', 'fixtures', 'relative.sql.erb') }
|
74
74
|
it { is_expected.to eq("SELECT * FROM relative;\n") }
|
75
75
|
end
|
76
|
+
|
77
|
+
context 'with packaged template' do
|
78
|
+
let(:template) { 'define_schema.hql.erb' }
|
79
|
+
it { is_expected.to_not be_nil }
|
80
|
+
end
|
76
81
|
end
|
77
82
|
end
|
@@ -157,11 +157,16 @@ describe Masamune::Transform::DefineTable do
|
|
157
157
|
is_expected.to eq <<-EOS.strip_heredoc
|
158
158
|
CREATE TABLE IF NOT EXISTS user_dimension
|
159
159
|
(
|
160
|
-
id SERIAL
|
160
|
+
id SERIAL,
|
161
161
|
tenant_id INTEGER NOT NULL,
|
162
162
|
user_id INTEGER NOT NULL,
|
163
163
|
last_modified_at TIMESTAMP NOT NULL DEFAULT NOW()
|
164
164
|
);
|
165
|
+
|
166
|
+
DO $$ BEGIN
|
167
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_pkey') THEN
|
168
|
+
ALTER TABLE user_dimension ADD PRIMARY KEY (id);
|
169
|
+
END IF; END $$;
|
165
170
|
EOS
|
166
171
|
end
|
167
172
|
end
|
@@ -170,8 +175,8 @@ describe Masamune::Transform::DefineTable do
|
|
170
175
|
before do
|
171
176
|
catalog.schema :postgres do
|
172
177
|
dimension 'user', type: :two do
|
173
|
-
column 'tenant_id',
|
174
|
-
column 'user_id',
|
178
|
+
column 'tenant_id', natural_key: true
|
179
|
+
column 'user_id', natural_key: true
|
175
180
|
end
|
176
181
|
end
|
177
182
|
end
|
@@ -182,7 +187,7 @@ describe Masamune::Transform::DefineTable do
|
|
182
187
|
is_expected.to eq <<-EOS.strip_heredoc
|
183
188
|
CREATE TABLE IF NOT EXISTS user_dimension
|
184
189
|
(
|
185
|
-
id SERIAL
|
190
|
+
id SERIAL,
|
186
191
|
tenant_id INTEGER NOT NULL,
|
187
192
|
user_id INTEGER NOT NULL,
|
188
193
|
start_at TIMESTAMP NOT NULL DEFAULT TO_TIMESTAMP(0),
|
@@ -192,18 +197,18 @@ describe Masamune::Transform::DefineTable do
|
|
192
197
|
);
|
193
198
|
|
194
199
|
DO $$ BEGIN
|
195
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
196
|
-
ALTER TABLE user_dimension ADD
|
200
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_pkey') THEN
|
201
|
+
ALTER TABLE user_dimension ADD PRIMARY KEY (id);
|
197
202
|
END IF; END $$;
|
198
203
|
|
199
204
|
DO $$ BEGIN
|
200
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
201
|
-
|
205
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_e6c3d91_key') THEN
|
206
|
+
ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_e6c3d91_key UNIQUE(tenant_id, user_id, start_at);
|
202
207
|
END IF; END $$;
|
203
208
|
|
204
209
|
DO $$ BEGIN
|
205
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
206
|
-
CREATE INDEX
|
210
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_2c8e908_index') THEN
|
211
|
+
CREATE INDEX user_dimension_2c8e908_index ON user_dimension (end_at);
|
207
212
|
END IF; END $$;
|
208
213
|
|
209
214
|
DO $$ BEGIN
|
@@ -212,13 +217,18 @@ describe Masamune::Transform::DefineTable do
|
|
212
217
|
END IF; END $$;
|
213
218
|
|
214
219
|
DO $$ BEGIN
|
215
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
216
|
-
CREATE INDEX
|
220
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_3854361_index') THEN
|
221
|
+
CREATE INDEX user_dimension_3854361_index ON user_dimension (tenant_id);
|
222
|
+
END IF; END $$;
|
223
|
+
|
224
|
+
DO $$ BEGIN
|
225
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_e8701ad_index') THEN
|
226
|
+
CREATE INDEX user_dimension_e8701ad_index ON user_dimension (user_id);
|
217
227
|
END IF; END $$;
|
218
228
|
|
219
229
|
DO $$ BEGIN
|
220
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
221
|
-
CREATE INDEX
|
230
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_e6c3d91_index') THEN
|
231
|
+
CREATE UNIQUE INDEX user_dimension_e6c3d91_index ON user_dimension (tenant_id, user_id, start_at);
|
222
232
|
END IF; END $$;
|
223
233
|
EOS
|
224
234
|
end
|
@@ -242,8 +252,8 @@ describe Masamune::Transform::DefineTable do
|
|
242
252
|
dimension 'user', type: :four do
|
243
253
|
references :cluster
|
244
254
|
references :user_account_state
|
245
|
-
column 'tenant_id',
|
246
|
-
column 'user_id',
|
255
|
+
column 'tenant_id', natural_key: true
|
256
|
+
column 'user_id', natural_key: true
|
247
257
|
column 'preferences', type: :key_value, null: true
|
248
258
|
end
|
249
259
|
end
|
@@ -255,9 +265,9 @@ describe Masamune::Transform::DefineTable do
|
|
255
265
|
is_expected.to eq <<-EOS.strip_heredoc
|
256
266
|
CREATE TABLE IF NOT EXISTS user_dimension_ledger
|
257
267
|
(
|
258
|
-
id SERIAL
|
259
|
-
cluster_type_id INTEGER NOT NULL
|
260
|
-
user_account_state_type_id INTEGER
|
268
|
+
id SERIAL,
|
269
|
+
cluster_type_id INTEGER NOT NULL DEFAULT default_cluster_type_id(),
|
270
|
+
user_account_state_type_id INTEGER,
|
261
271
|
tenant_id INTEGER NOT NULL,
|
262
272
|
user_id INTEGER NOT NULL,
|
263
273
|
preferences HSTORE,
|
@@ -269,18 +279,23 @@ describe Masamune::Transform::DefineTable do
|
|
269
279
|
);
|
270
280
|
|
271
281
|
DO $$ BEGIN
|
272
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
273
|
-
ALTER TABLE user_dimension_ledger ADD
|
282
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_ledger_pkey') THEN
|
283
|
+
ALTER TABLE user_dimension_ledger ADD PRIMARY KEY (cluster_type_id, id);
|
274
284
|
END IF; END $$;
|
275
285
|
|
276
286
|
DO $$ BEGIN
|
277
|
-
IF NOT EXISTS (SELECT 1 FROM
|
278
|
-
|
287
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = 'user_dimension_ledger_d6b9b38_fkey') THEN
|
288
|
+
ALTER TABLE user_dimension_ledger ADD CONSTRAINT user_dimension_ledger_d6b9b38_fkey FOREIGN KEY (cluster_type_id) REFERENCES cluster_type(id);
|
279
289
|
END IF; END $$;
|
280
290
|
|
281
291
|
DO $$ BEGIN
|
282
|
-
IF NOT EXISTS (SELECT 1 FROM
|
283
|
-
|
292
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = 'user_dimension_ledger_7988187_fkey') THEN
|
293
|
+
ALTER TABLE user_dimension_ledger ADD CONSTRAINT user_dimension_ledger_7988187_fkey FOREIGN KEY (user_account_state_type_id) REFERENCES user_account_state_type(id);
|
294
|
+
END IF; END $$;
|
295
|
+
|
296
|
+
DO $$ BEGIN
|
297
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_ledger_ff54dba_key') THEN
|
298
|
+
ALTER TABLE user_dimension_ledger ADD CONSTRAINT user_dimension_ledger_ff54dba_key UNIQUE(cluster_type_id, tenant_id, user_id, source_kind, source_uuid, start_at);
|
284
299
|
END IF; END $$;
|
285
300
|
|
286
301
|
DO $$ BEGIN
|
@@ -294,20 +309,20 @@ describe Masamune::Transform::DefineTable do
|
|
294
309
|
END IF; END $$;
|
295
310
|
|
296
311
|
DO $$ BEGIN
|
297
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
298
|
-
CREATE INDEX
|
312
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_ledger_370d6dd_index') THEN
|
313
|
+
CREATE INDEX user_dimension_ledger_370d6dd_index ON user_dimension_ledger (tenant_id, user_id, source_kind, source_uuid, start_at);
|
299
314
|
END IF; END $$;
|
300
315
|
|
301
316
|
CREATE TABLE IF NOT EXISTS user_dimension
|
302
317
|
(
|
303
|
-
id SERIAL
|
304
|
-
cluster_type_id INTEGER NOT NULL
|
305
|
-
user_account_state_type_id INTEGER NOT NULL
|
318
|
+
id SERIAL,
|
319
|
+
cluster_type_id INTEGER NOT NULL DEFAULT default_cluster_type_id(),
|
320
|
+
user_account_state_type_id INTEGER NOT NULL DEFAULT default_user_account_state_type_id(),
|
306
321
|
tenant_id INTEGER NOT NULL,
|
307
322
|
user_id INTEGER NOT NULL,
|
308
323
|
preferences HSTORE,
|
309
|
-
parent_id INTEGER
|
310
|
-
record_id INTEGER
|
324
|
+
parent_id INTEGER,
|
325
|
+
record_id INTEGER,
|
311
326
|
start_at TIMESTAMP NOT NULL DEFAULT TO_TIMESTAMP(0),
|
312
327
|
end_at TIMESTAMP,
|
313
328
|
version INTEGER DEFAULT 1,
|
@@ -315,33 +330,33 @@ describe Masamune::Transform::DefineTable do
|
|
315
330
|
);
|
316
331
|
|
317
332
|
DO $$ BEGIN
|
318
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
319
|
-
ALTER TABLE user_dimension ADD
|
333
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_pkey') THEN
|
334
|
+
ALTER TABLE user_dimension ADD PRIMARY KEY (cluster_type_id, id);
|
320
335
|
END IF; END $$;
|
321
336
|
|
322
337
|
DO $$ BEGIN
|
323
|
-
IF NOT EXISTS (SELECT 1 FROM
|
324
|
-
|
338
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = 'user_dimension_d6b9b38_fkey') THEN
|
339
|
+
ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_d6b9b38_fkey FOREIGN KEY (cluster_type_id) REFERENCES cluster_type(id);
|
325
340
|
END IF; END $$;
|
326
341
|
|
327
342
|
DO $$ BEGIN
|
328
|
-
IF NOT EXISTS (SELECT 1 FROM
|
329
|
-
|
343
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = 'user_dimension_7988187_fkey') THEN
|
344
|
+
ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_7988187_fkey FOREIGN KEY (user_account_state_type_id) REFERENCES user_account_state_type(id);
|
330
345
|
END IF; END $$;
|
331
346
|
|
332
347
|
DO $$ BEGIN
|
333
|
-
IF NOT EXISTS (SELECT 1 FROM
|
334
|
-
|
348
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = 'user_dimension_e0538bc_fkey') THEN
|
349
|
+
ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_e0538bc_fkey FOREIGN KEY (cluster_type_id, parent_id) REFERENCES user_dimension_ledger(cluster_type_id, id);
|
335
350
|
END IF; END $$;
|
336
351
|
|
337
352
|
DO $$ BEGIN
|
338
|
-
IF NOT EXISTS (SELECT 1 FROM
|
339
|
-
|
353
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint c WHERE c.conname = 'user_dimension_824002d_fkey') THEN
|
354
|
+
ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_824002d_fkey FOREIGN KEY (cluster_type_id, record_id) REFERENCES user_dimension_ledger(cluster_type_id, id);
|
340
355
|
END IF; END $$;
|
341
356
|
|
342
357
|
DO $$ BEGIN
|
343
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
344
|
-
|
358
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_3fcebfa_key') THEN
|
359
|
+
ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_3fcebfa_key UNIQUE(cluster_type_id, tenant_id, user_id, start_at);
|
345
360
|
END IF; END $$;
|
346
361
|
|
347
362
|
DO $$ BEGIN
|
@@ -350,8 +365,23 @@ describe Masamune::Transform::DefineTable do
|
|
350
365
|
END IF; END $$;
|
351
366
|
|
352
367
|
DO $$ BEGIN
|
353
|
-
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '
|
354
|
-
CREATE INDEX
|
368
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_23563d3_index') THEN
|
369
|
+
CREATE INDEX user_dimension_23563d3_index ON user_dimension (start_at);
|
370
|
+
END IF; END $$;
|
371
|
+
|
372
|
+
DO $$ BEGIN
|
373
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_3854361_index') THEN
|
374
|
+
CREATE INDEX user_dimension_3854361_index ON user_dimension (tenant_id);
|
375
|
+
END IF; END $$;
|
376
|
+
|
377
|
+
DO $$ BEGIN
|
378
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_e8701ad_index') THEN
|
379
|
+
CREATE INDEX user_dimension_e8701ad_index ON user_dimension (user_id);
|
380
|
+
END IF; END $$;
|
381
|
+
|
382
|
+
DO $$ BEGIN
|
383
|
+
IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_e6c3d91_index') THEN
|
384
|
+
CREATE INDEX user_dimension_e6c3d91_index ON user_dimension (tenant_id, user_id, start_at);
|
355
385
|
END IF; END $$;
|
356
386
|
EOS
|
357
387
|
end
|
@@ -368,8 +398,8 @@ describe Masamune::Transform::DefineTable do
|
|
368
398
|
|
369
399
|
dimension 'user', type: :four do
|
370
400
|
references :user_account_state
|
371
|
-
column 'tenant_id',
|
372
|
-
column 'user_id',
|
401
|
+
column 'tenant_id', natural_key: true
|
402
|
+
column 'user_id', natural_key: true
|
373
403
|
column 'preferences', type: :key_value, null: true
|
374
404
|
end
|
375
405
|
end
|
@@ -393,12 +423,11 @@ describe Masamune::Transform::DefineTable do
|
|
393
423
|
last_modified_at TIMESTAMP DEFAULT NOW()
|
394
424
|
);
|
395
425
|
|
396
|
-
CREATE INDEX
|
426
|
+
CREATE INDEX user_consolidated_forward_dimension_stage_2c8e908_index ON user_consolidated_forward_dimension_stage (end_at);
|
427
|
+
CREATE INDEX user_consolidated_forward_dimension_stage_23563d3_index ON user_consolidated_forward_dimension_stage (start_at);
|
397
428
|
CREATE INDEX user_consolidated_forward_dimension_stage_3854361_index ON user_consolidated_forward_dimension_stage (tenant_id);
|
398
429
|
CREATE INDEX user_consolidated_forward_dimension_stage_e8701ad_index ON user_consolidated_forward_dimension_stage (user_id);
|
399
|
-
CREATE INDEX
|
400
|
-
CREATE INDEX user_consolidated_forward_dimension_stage_2c8e908_index ON user_consolidated_forward_dimension_stage (end_at);
|
401
|
-
CREATE INDEX user_consolidated_forward_dimension_stage_2af72f1_index ON user_consolidated_forward_dimension_stage (version);
|
430
|
+
CREATE INDEX user_consolidated_forward_dimension_stage_e6c3d91_index ON user_consolidated_forward_dimension_stage (tenant_id, user_id, start_at);
|
402
431
|
EOS
|
403
432
|
end
|
404
433
|
end
|