masamune 0.13.8 → 0.14.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/masamune.rb +8 -5
  3. data/lib/masamune/actions.rb +1 -13
  4. data/lib/masamune/actions/data_flow.rb +2 -1
  5. data/lib/masamune/actions/date_parse.rb +0 -1
  6. data/lib/masamune/actions/elastic_mapreduce.rb +0 -2
  7. data/lib/masamune/actions/filesystem.rb +0 -2
  8. data/lib/masamune/actions/hive.rb +0 -2
  9. data/lib/masamune/actions/invoke_parallel.rb +2 -1
  10. data/lib/masamune/actions/postgres.rb +0 -1
  11. data/lib/masamune/actions/s3cmd.rb +2 -0
  12. data/lib/masamune/actions/transform.rb +0 -2
  13. data/lib/masamune/after_initialize_callbacks.rb +0 -2
  14. data/lib/masamune/commands.rb +1 -11
  15. data/lib/masamune/commands/postgres.rb +1 -0
  16. data/lib/masamune/commands/postgres_admin.rb +2 -0
  17. data/lib/masamune/configuration.rb +2 -0
  18. data/lib/masamune/data_plan/engine.rb +2 -0
  19. data/lib/masamune/filesystem.rb +2 -0
  20. data/lib/masamune/helpers.rb +1 -1
  21. data/lib/masamune/last_element.rb +0 -2
  22. data/lib/masamune/schema/dimension.rb +1 -3
  23. data/lib/masamune/schema/store.rb +2 -0
  24. data/lib/masamune/schema/table.rb +2 -0
  25. data/lib/masamune/template.rb +4 -1
  26. data/lib/masamune/thor.rb +1 -1
  27. data/lib/masamune/transform.rb +1 -21
  28. data/lib/masamune/transform/bulk_upsert.rb +1 -22
  29. data/lib/masamune/transform/common.rb +27 -0
  30. data/lib/masamune/transform/common/denormalize_table.rb +90 -0
  31. data/lib/masamune/transform/deduplicate_dimension.rb +1 -41
  32. data/lib/masamune/transform/define_table.rb +1 -113
  33. data/lib/masamune/transform/denormalize_table.rb +1 -50
  34. data/lib/masamune/transform/hive.rb +27 -0
  35. data/lib/masamune/transform/{define_schema.hql.erb → hive/define_schema.hql.erb} +0 -0
  36. data/lib/masamune/transform/{define_table.hql.erb → hive/define_table.hql.erb} +0 -0
  37. data/lib/masamune/transform/hive/define_table.rb +46 -0
  38. data/lib/masamune/transform/{denormalize_table.hql.erb → hive/denormalize_table.hql.erb} +0 -0
  39. data/lib/masamune/transform/hive/denormalize_table.rb +27 -0
  40. data/lib/masamune/transform/insert_reference_values.rb +1 -30
  41. data/lib/masamune/transform/operator.rb +36 -37
  42. data/lib/masamune/transform/postgres.rb +27 -0
  43. data/lib/masamune/transform/{bulk_upsert.psql.erb → postgres/bulk_upsert.psql.erb} +0 -0
  44. data/lib/masamune/transform/postgres/bulk_upsert.rb +62 -0
  45. data/lib/masamune/transform/{deduplicate_dimension.psql.erb → postgres/deduplicate_dimension.psql.erb} +1 -7
  46. data/lib/masamune/transform/postgres/deduplicate_dimension.rb +79 -0
  47. data/lib/masamune/transform/{define_foreign_key.psql.erb → postgres/define_foreign_key.psql.erb} +0 -0
  48. data/lib/masamune/transform/{define_index.psql.erb → postgres/define_index.psql.erb} +0 -0
  49. data/lib/masamune/transform/{define_inheritance.psql.erb → postgres/define_inheritance.psql.erb} +0 -0
  50. data/lib/masamune/transform/{define_schema.psql.erb → postgres/define_schema.psql.erb} +0 -0
  51. data/lib/masamune/transform/{define_table.psql.erb → postgres/define_table.psql.erb} +0 -0
  52. data/lib/masamune/transform/postgres/define_table.rb +142 -0
  53. data/lib/masamune/transform/{define_unique.psql.erb → postgres/define_unique.psql.erb} +0 -0
  54. data/lib/masamune/transform/{denormalize_table.psql.erb → postgres/denormalize_table.psql.erb} +0 -0
  55. data/lib/masamune/transform/postgres/denormalize_table.rb +27 -0
  56. data/lib/masamune/transform/{insert_reference_values.psql.erb → postgres/insert_reference_values.psql.erb} +1 -1
  57. data/lib/masamune/transform/postgres/insert_reference_values.rb +69 -0
  58. data/lib/masamune/transform/{relabel_dimension.psql.erb → postgres/relabel_dimension.psql.erb} +4 -1
  59. data/lib/masamune/transform/postgres/relabel_dimension.rb +45 -0
  60. data/lib/masamune/transform/{replace_table.psql.erb → postgres/replace_table.psql.erb} +0 -0
  61. data/lib/masamune/transform/{rollup_fact.psql.erb → postgres/rollup_fact.psql.erb} +0 -0
  62. data/lib/masamune/transform/postgres/rollup_fact.rb +123 -0
  63. data/lib/masamune/transform/{snapshot_dimension.psql.erb → postgres/snapshot_dimension.psql.erb} +3 -10
  64. data/lib/masamune/transform/postgres/snapshot_dimension.rb +83 -0
  65. data/lib/masamune/transform/{stage_dimension.psql.erb → postgres/stage_dimension.psql.erb} +0 -0
  66. data/lib/masamune/transform/postgres/stage_dimension.rb +90 -0
  67. data/lib/masamune/transform/{stage_fact.psql.erb → postgres/stage_fact.psql.erb} +0 -0
  68. data/lib/masamune/transform/postgres/stage_fact.rb +134 -0
  69. data/lib/masamune/transform/relabel_dimension.rb +1 -9
  70. data/lib/masamune/transform/rollup_fact.rb +1 -86
  71. data/lib/masamune/transform/snapshot_dimension.rb +1 -44
  72. data/lib/masamune/transform/stage_dimension.rb +1 -53
  73. data/lib/masamune/transform/stage_fact.rb +1 -96
  74. data/lib/masamune/version.rb +1 -1
  75. data/spec/masamune/template_spec.rb +1 -1
  76. data/spec/masamune/transform/bulk_upsert.dimension_spec.rb +1 -3
  77. data/spec/masamune/transform/deduplicate_dimension_spec.rb +1 -7
  78. data/spec/masamune/transform/define_table.dimension_spec.rb +0 -14
  79. data/spec/masamune/transform/denormalize_table_spec.rb +34 -0
  80. data/spec/masamune/transform/relabel_dimension_spec.rb +6 -1
  81. data/spec/masamune/transform/snapshot_dimension_spec.rb +3 -10
  82. metadata +37 -21
@@ -25,15 +25,7 @@ module Masamune::Transform
25
25
  extend ActiveSupport::Concern
26
26
 
27
27
  def relabel_dimension(target)
28
- Operator.new(__method__, target: target, presenters: { postgres: Postgres })
29
- end
30
-
31
- private
32
-
33
- class Postgres < SimpleDelegator
34
- def window(*extra)
35
- (columns.values.select { |column| extra.delete(column.name) || column.natural_key || column.auto_reference }.map(&:name) + extra).uniq
36
- end
28
+ Operator.new(__method__, target: target)
37
29
  end
38
30
  end
39
31
  end
@@ -27,92 +27,7 @@ module Masamune::Transform
27
27
  def rollup_fact(source, target, date)
28
28
  raise ArgumentError, "#{source.name} must have date_column to rollup" unless source.date_column
29
29
  raise ArgumentError, "#{target.name} must have date_column to rollup" unless target.date_column
30
- Operator.new __method__, source: source.partition_table(date), target: target.partition_table(date), presenters: { postgres: Postgres }
31
- end
32
-
33
- private
34
-
35
- class Postgres < SimpleDelegator
36
- include Masamune::LastElement
37
-
38
- def insert_columns(source)
39
- values = []
40
- shared_columns(source).values.map do |columns|
41
- column = columns.first
42
- next if column.id == :last_modified_at
43
- next if column.auto_reference
44
- values << column.name
45
- end
46
- measures.each do |_ ,measure|
47
- values << measure.name
48
- end
49
- values << time_key.name
50
- values.compact
51
- end
52
-
53
- def insert_values(source)
54
- values = []
55
- values << calculated_date_key(source)
56
- shared_columns(source).values.map do |columns|
57
- column = columns.first
58
- next unless column.reference
59
- next if column.reference.type == :date
60
- next if column.auto_reference
61
- values << column.qualified_name
62
- end
63
- source.measures.each do |_ ,measure|
64
- values << measure.aggregate_value
65
- end
66
- values << calculated_time_key(source)
67
- values
68
- end
69
- method_with_last_element :insert_values
70
-
71
- def join_conditions(source)
72
- {
73
- source.date_column.reference.name => [
74
- "#{source.date_column.reference.surrogate_key.qualified_name} = #{source.date_column.qualified_name}"
75
- ]
76
- }
77
- end
78
-
79
- def group_by(source)
80
- group_by = []
81
- group_by << calculated_date_key(source)
82
- shared_columns(source).values.map do |columns|
83
- column = columns.first
84
- next unless column.reference
85
- next if column.reference.type == :date
86
- next if column.auto_reference
87
- group_by << column.qualified_name
88
- end
89
- group_by << calculated_time_key(source)
90
- group_by
91
- end
92
- method_with_last_element :group_by
93
-
94
- private
95
-
96
- def calculated_date_key(source)
97
- case grain
98
- when :hourly, :daily
99
- "#{source.date_column.qualified_name}"
100
- when :monthly
101
- "to_char(date_trunc('month',#{source.date_column.qualified_name}::text::date),'YYYYMMDD')::integer"
102
- end
103
- end
104
-
105
- def calculated_time_key(source)
106
- case grain
107
- when :hourly
108
- "(#{source.time_key.qualified_name} - (#{source.time_key.qualified_name} % #{1.hour.seconds}))"
109
- when :daily
110
- "extract(EPOCH from #{source.date_column.qualified_name}::text::date)"
111
- when :monthly
112
- "extract(EPOCH from date_trunc('month',#{source.date_column.qualified_name}::text::date))"
113
- end
114
- end
115
-
30
+ Operator.new __method__, source: source.partition_table(date), target: target.partition_table(date)
116
31
  end
117
32
  end
118
33
  end
@@ -25,50 +25,7 @@ module Masamune::Transform
25
25
  extend ActiveSupport::Concern
26
26
 
27
27
  def snapshot_dimension(source, target, order = 'DESC')
28
- Operator.new(__method__, source: source, target: target, order: order, presenters: { postgres: Postgres })
29
- end
30
-
31
- private
32
-
33
- class Postgres < SimpleDelegator
34
- include Masamune::LastElement
35
-
36
- def insert_columns(source = nil)
37
- consolidated_columns.map { |_, column| column.name }
38
- end
39
-
40
- def insert_view_values
41
- consolidated_columns.map { |_, column| column.name }
42
- end
43
-
44
- def insert_view_constraints
45
- consolidated_columns.reject { |_, column| !column.default.nil? || column.null }.map { |_, column| "#{column.name} IS NOT NULL" }
46
- end
47
- method_with_last_element :insert_view_constraints
48
-
49
- def window(*extra)
50
- (columns.values.select { |column| extra.delete(column.name) || column.natural_key || column.auto_reference }.map(&:name) + extra).uniq
51
- end
52
-
53
- def insert_values(opts = {})
54
- window = opts[:window]
55
- consolidated_columns.map do |_, column|
56
- if column.natural_key
57
- "#{column.name} AS #{column.name}"
58
- elsif column.type == :key_value
59
- "hstore_merge(#{column.name}) OVER #{window} AS #{column.name}"
60
- else
61
- "coalesce_merge(#{column.name}) OVER #{window} AS #{column.name}"
62
- end
63
- end
64
- end
65
- method_with_last_element :insert_values
66
-
67
- private
68
-
69
- def consolidated_columns
70
- unreserved_columns.reject { |_, column| column.surrogate_key }
71
- end
28
+ Operator.new(__method__, source: source, target: target, order: order)
72
29
  end
73
30
  end
74
31
  end
@@ -25,59 +25,7 @@ module Masamune::Transform
25
25
  extend ActiveSupport::Concern
26
26
 
27
27
  def stage_dimension(source, target)
28
- Operator.new(__method__, source: source, target: target, presenters: { postgres: Postgres })
29
- end
30
-
31
- private
32
-
33
- class Postgres < SimpleDelegator
34
- include Masamune::LastElement
35
-
36
- def insert_columns(source)
37
- shared_columns(source).values.map do |columns|
38
- column = columns.first
39
- if reference = column.reference
40
- reference.foreign_key_name
41
- else
42
- column.name
43
- end
44
- end.compact
45
- end
46
-
47
- def insert_values(source)
48
- shared_columns(source).values.map do |columns|
49
- column = columns.first
50
- if reference = column.reference
51
- reference.surrogate_key.qualified_name(reference.label)
52
- elsif column.type == :json || column.type == :yaml || column.type == :key_value
53
- "json_to_hstore(#{column.qualified_name})"
54
- else
55
- column.qualified_name
56
- end
57
- end.compact
58
- end
59
- method_with_last_element :insert_values
60
-
61
- def join_conditions(source)
62
- join_columns = shared_columns(source).values.flatten
63
- join_columns = join_columns.select { |column| column.reference }
64
- join_columns = join_columns.group_by { |column| column.reference }
65
-
66
- conditions = Hash.new { |h,k| h[k] = Set.new }
67
- join_columns.each do |reference, columns|
68
- left_uniq = Set.new
69
- (columns + lateral_references(source, reference)).each do |column|
70
- left = reference.columns[column.id]
71
- next unless left_uniq.add?(left.qualified_name(reference.label))
72
- conditions[[reference.name, reference.alias]] << "#{left.qualified_name(reference.label)} = #{column.qualified_name}"
73
- end
74
- end
75
- conditions
76
- end
77
-
78
- def lateral_references(source, reference)
79
- source.shared_columns(reference).keys.reject { |column| column.auto_reference }
80
- end
28
+ Operator.new(__method__, source: source, target: target)
81
29
  end
82
30
  end
83
31
  end
@@ -25,102 +25,7 @@ module Masamune::Transform
25
25
  extend ActiveSupport::Concern
26
26
 
27
27
  def stage_fact(source, target, date)
28
- Operator.new(__method__, source: source, target: target, date: date, presenters: { postgres: Postgres })
29
- end
30
-
31
- private
32
-
33
- class Postgres < SimpleDelegator
34
- include Masamune::LastElement
35
-
36
- def insert_columns(source)
37
- shared_columns(source).values.map do |columns|
38
- column = columns.first
39
- if reference = column.reference
40
- reference.foreign_key_name
41
- else
42
- column.name
43
- end
44
- end
45
- end
46
-
47
- def insert_values(source)
48
- shared_columns(source).values.map do |columns|
49
- column = columns.first
50
- if !column.degenerate? && reference = column.reference
51
- reference.surrogate_key.qualified_name(column.reference.label)
52
- else
53
- column.qualified_name
54
- end
55
- end
56
- end
57
- method_with_last_element :insert_values
58
-
59
- def join_alias(reference)
60
- reference.label ? "#{reference.name} AS #{[reference.label, reference.name].compact.join('_')}" : reference.name
61
- end
62
-
63
- def join_conditions(source)
64
- join_columns = shared_columns(source).values.flatten
65
- join_columns = join_columns.select { |column| column.reference }
66
- join_columns = join_columns.group_by { |column| column.reference }
67
-
68
- dependencies = Masamune::TopologicalHash.new
69
- conditions = Hash.new { |h,k| h[k] = [] }
70
- join_columns.each do |reference, columns|
71
- reference_name = join_alias(reference)
72
- columns.each do |column|
73
- next if column.degenerate?
74
- dependencies[reference_name] ||= []
75
- cross_references = cross_references(column)
76
-
77
- coalesce_values = []
78
-
79
- if cross_references.any?
80
- dependencies[reference_name] += cross_references.map { |reference, _| join_alias(reference) }
81
- coalesce_values << cross_references.map { |reference, column| column.qualified_name(reference.label) }
82
- end
83
-
84
- column.reference.auto_surrogate_keys.each do |auto_surrogate_key|
85
- next unless auto_surrogate_key.default
86
- conditions[reference_name] << "#{auto_surrogate_key.qualified_name(reference.label)} = #{auto_surrogate_key.default}"
87
- end if column.reference
88
-
89
- if column.reference && !column.reference.default.nil? && column.adjacent.natural_key
90
- coalesce_values << column.reference.default(column.adjacent)
91
- elsif column.adjacent && !column.adjacent.default.nil?
92
- coalesce_values << column.adjacent.sql_value(column.adjacent.default)
93
- end
94
-
95
- conditions[reference_name] << (coalesce_values.any? ?
96
- "#{column.foreign_key_name} = COALESCE(#{column.qualified_name}, #{coalesce_values.join(', ')})" :
97
- "#{column.foreign_key_name} = #{column.qualified_name}")
98
- end
99
-
100
- if reference.type == :two || reference.type == :four
101
- 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')"
102
- 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"
103
- conditions[reference_name] << "((#{join_key_a}) OR (#{join_key_b}))"
104
- end
105
-
106
- conditions[reference_name].uniq!
107
- end
108
- conditions.slice(*dependencies.tsort)
109
- end
110
-
111
- private
112
-
113
- def cross_references(column)
114
- return {} unless column.natural_key || column.adjacent.try(:natural_key)
115
- {}.tap do |result|
116
- column.reference.through.each do |reference_id|
117
- reference = references[reference_id]
118
- if reference.columns[column.id]
119
- result[reference] = reference.columns[column.id]
120
- end
121
- end
122
- end
123
- end
28
+ Operator.new(__method__, source: source, target: target, date: date)
124
29
  end
125
30
  end
126
31
  end
@@ -21,5 +21,5 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  module Masamune
24
- VERSION = '0.13.8'
24
+ VERSION = '0.14.0'
25
25
  end
@@ -75,7 +75,7 @@ describe Masamune::Template do
75
75
  end
76
76
 
77
77
  context 'with packaged template' do
78
- let(:template) { 'define_schema.hql.erb' }
78
+ let(:template) { 'hive/define_schema.hql.erb' }
79
79
  it { is_expected.to_not be_nil }
80
80
  end
81
81
  end
@@ -101,7 +101,7 @@ describe Masamune::Transform::BulkUpsert do
101
101
  ;
102
102
 
103
103
  INSERT INTO
104
- user_dimension (department_type_id, user_account_state_type_id, hr_user_account_state_type_id, tenant_id, user_id, name, preferences, parent_id, record_id, start_at, end_at, version, last_modified_at)
104
+ user_dimension (department_type_id, user_account_state_type_id, hr_user_account_state_type_id, tenant_id, user_id, name, preferences, start_at, end_at, version, last_modified_at)
105
105
  SELECT
106
106
  user_dimension_stage.department_type_id,
107
107
  user_dimension_stage.user_account_state_type_id,
@@ -110,8 +110,6 @@ describe Masamune::Transform::BulkUpsert do
110
110
  user_dimension_stage.user_id,
111
111
  user_dimension_stage.name,
112
112
  user_dimension_stage.preferences,
113
- user_dimension_stage.parent_id,
114
- user_dimension_stage.record_id,
115
113
  user_dimension_stage.start_at,
116
114
  user_dimension_stage.end_at,
117
115
  user_dimension_stage.version,
@@ -52,21 +52,17 @@ describe Masamune::Transform::DeduplicateDimension do
52
52
  tenant_id,
53
53
  user_id,
54
54
  preferences,
55
- parent_id,
56
- record_id,
57
55
  start_at
58
56
  FROM
59
57
  user_consolidated_dimension_stage
60
58
  )
61
59
  INSERT INTO
62
- user_deduplicated_dimension_stage (user_account_state_type_id, tenant_id, user_id, preferences, parent_id, record_id, start_at)
60
+ user_deduplicated_dimension_stage (user_account_state_type_id, tenant_id, user_id, preferences, start_at)
63
61
  SELECT DISTINCT
64
62
  user_account_state_type_id,
65
63
  tenant_id,
66
64
  user_id,
67
65
  preferences,
68
- parent_id,
69
- record_id,
70
66
  start_at
71
67
  FROM (
72
68
  SELECT
@@ -74,8 +70,6 @@ describe Masamune::Transform::DeduplicateDimension do
74
70
  tenant_id,
75
71
  user_id,
76
72
  preferences,
77
- parent_id,
78
- record_id,
79
73
  start_at,
80
74
  CASE
81
75
  WHEN (LAG(user_account_state_type_id) OVER w = user_account_state_type_id) AND (LAG(tenant_id) OVER w = tenant_id) AND (LAG(user_id) OVER w = user_id) AND ((LAG(preferences) OVER w = preferences) OR (LAG(preferences) OVER w IS NULL AND preferences IS NULL)) THEN
@@ -321,8 +321,6 @@ describe Masamune::Transform::DefineTable do
321
321
  tenant_id INTEGER NOT NULL,
322
322
  user_id INTEGER NOT NULL,
323
323
  preferences HSTORE,
324
- parent_id INTEGER,
325
- record_id INTEGER,
326
324
  start_at TIMESTAMP NOT NULL DEFAULT TO_TIMESTAMP(0),
327
325
  end_at TIMESTAMP,
328
326
  version INTEGER DEFAULT 1,
@@ -344,16 +342,6 @@ describe Masamune::Transform::DefineTable do
344
342
  ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_7988187_fkey FOREIGN KEY (user_account_state_type_id) REFERENCES user_account_state_type(id);
345
343
  END IF; END $$;
346
344
 
347
- DO $$ BEGIN
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);
350
- END IF; END $$;
351
-
352
- DO $$ BEGIN
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);
355
- END IF; END $$;
356
-
357
345
  DO $$ BEGIN
358
346
  IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = 'user_dimension_3fcebfa_key') THEN
359
347
  ALTER TABLE user_dimension ADD CONSTRAINT user_dimension_3fcebfa_key UNIQUE(cluster_type_id, tenant_id, user_id, start_at);
@@ -415,8 +403,6 @@ describe Masamune::Transform::DefineTable do
415
403
  tenant_id INTEGER,
416
404
  user_id INTEGER,
417
405
  preferences HSTORE,
418
- parent_id INTEGER,
419
- record_id INTEGER,
420
406
  start_at TIMESTAMP DEFAULT TO_TIMESTAMP(0),
421
407
  end_at TIMESTAMP,
422
408
  version INTEGER DEFAULT 1,
@@ -73,6 +73,39 @@ describe Masamune::Transform::DenormalizeTable do
73
73
 
74
74
  subject(:result) { transform.denormalize_table(target, options).to_s }
75
75
 
76
+ context 'with postgres dimension' do
77
+ let(:target) { catalog.postgres.user_dimension }
78
+ let(:options) { { } }
79
+
80
+ it 'should eq render denormalize_table template' do
81
+ is_expected.to eq <<-EOS.strip_heredoc
82
+ SELECT
83
+ cluster_type.name AS cluster_type_name,
84
+ user_dimension.tenant_id,
85
+ user_dimension.user_id,
86
+ user_dimension.name,
87
+ user_dimension.start_at,
88
+ user_dimension.end_at,
89
+ user_dimension.version
90
+ FROM
91
+ user_dimension
92
+ LEFT JOIN
93
+ cluster_type
94
+ ON
95
+ cluster_type.id = user_dimension.cluster_type_id
96
+ ORDER BY
97
+ cluster_type_name,
98
+ tenant_id,
99
+ user_id,
100
+ name,
101
+ start_at,
102
+ end_at,
103
+ version
104
+ ;
105
+ EOS
106
+ end
107
+ end
108
+
76
109
  context 'with postgres fact without :columns' do
77
110
  let(:target) { catalog.postgres.visits_fact }
78
111
  let(:options) { { } }
@@ -137,6 +170,7 @@ describe Masamune::Transform::DenormalizeTable do
137
170
  EOS
138
171
  end
139
172
  end
173
+
140
174
  context 'with postgres fact with :columns' do
141
175
  let(:target) { catalog.postgres.visits_fact }
142
176
  let(:options) do