masamune 0.12.3 → 0.13.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 +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
|