masamune 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 77bd1d9e8351f259a7d946ac7b2abd11b4c14408
4
- data.tar.gz: deb47f8efaadd8b90b5825f79970e46833bbb030
3
+ metadata.gz: 49fdc180c59d4179763efd965d946c93fb864dcf
4
+ data.tar.gz: 0765d61a7a7247c7bb5eb0d70d17c7edb5e5fcf5
5
5
  SHA512:
6
- metadata.gz: 784416ce6169e2c3f2a9a14465580417ecfc6bdd73e238643022f608a9a04ede66d0b670dfce06e0956b14d3d96a8edbcbe0018ee76bdd0e1ccd4c2bb9f4b37a
7
- data.tar.gz: 6b1cb22550d392cb6bf6cf36258d21b28e7c323dd6bab5616b4066380e315bcb289c725d52a36f65cd182293c1a28d68f31e928a642d5429ce1f024945f2d81a
6
+ metadata.gz: 7a121d38a8d42a7e0c255ba17e523e1520da7016e0a87d553adcd32c67f6af21eb2ffc81cbcda1123e286fd93cdde8b31177affa30e17314497248d820fea989
7
+ data.tar.gz: 93ab83f1f5804d0b3bf25970a665d63f3e7099c43ec6c817e8a04ee30ad3dde348aa4a2515a1a8518e76aeb0150eed04a77cf1a568710f806dbe4582f1502259
@@ -30,7 +30,7 @@ module Masamune::Actions
30
30
  def parse_datetime_type(key)
31
31
  value = options[key]
32
32
  Chronic.parse(value).tap do |datetime_value|
33
- console("Using '#{datetime_value}' for --#{key}") if value != datetime_value
33
+ logger.debug("Using '#{datetime_value}' for --#{key}") if value != datetime_value
34
34
  end or raise Thor::MalformattedArgumentError, "Expected date time value for '--#{key}'; got #{value}"
35
35
  end
36
36
 
@@ -30,12 +30,14 @@ module Masamune::Commands
30
30
  {
31
31
  :create_db_path => 'createdb',
32
32
  :drop_db_path => 'dropdb',
33
+ :pg_dump_path => 'pg_dump',
33
34
  :options => [],
34
35
  :hostname => 'localhost',
35
36
  :username => 'postgres',
36
37
  :pgpass_file => nil,
37
38
  :action => nil,
38
- :database => nil
39
+ :database => nil,
40
+ :output => nil
39
41
  }
40
42
 
41
43
  def initialize(delegate, attrs = {})
@@ -52,7 +54,9 @@ module Masamune::Commands
52
54
  args << '--host=%s' % @hostname if @hostname
53
55
  args << '--username=%s' % @username if @username
54
56
  args << '--no-password'
55
- args << @database
57
+ args << database
58
+ args << @options
59
+ args << output
56
60
  args.flatten.compact
57
61
  end
58
62
 
@@ -64,9 +68,21 @@ module Masamune::Commands
64
68
  [@create_db_path]
65
69
  when :drop
66
70
  [@drop_db_path, '--if-exists']
71
+ when :dump
72
+ [@pg_dump_path, '--no-owner', '--no-privileges', '--oids', '--schema=public']
67
73
  else
68
- raise ArgumentError, ':action must be :create or :drop'
74
+ raise ArgumentError, ':action must be :create, :drop, or :dump'
69
75
  end
70
76
  end
77
+
78
+ def database
79
+ return @database unless @action == :dump
80
+ '--dbname=%s' % @database
81
+ end
82
+
83
+ def output
84
+ return unless @action == :dump
85
+ '--file=%s' % @output if @output
86
+ end
71
87
  end
72
88
  end
@@ -109,9 +109,18 @@ class Masamune::DataPlan::Rule
109
109
  matched_pattern.present? && matched_pattern[:rest].blank?
110
110
  end
111
111
 
112
- def bind_date(input_date)
113
- output_date = tz.utc_to_local(input_date)
114
- Masamune::DataPlan::Elem.new(self, output_date, options_for_elem)
112
+ def bind_date_or_time(input = nil)
113
+ input_time =
114
+ case input
115
+ when Time, DateTime
116
+ input
117
+ when Date
118
+ input.to_time
119
+ else
120
+ raise ArgumentError, "Cannot bind_date_or_time with type #{input.class}"
121
+ end
122
+ output_time = tz.utc_to_local(input_time)
123
+ Masamune::DataPlan::Elem.new(self, output_time, options_for_elem)
115
124
  end
116
125
 
117
126
  def bind_input(input)
@@ -123,12 +132,12 @@ class Masamune::DataPlan::Rule
123
132
  end
124
133
 
125
134
  def unify(elem, rule)
126
- rule.bind_date(elem.start_time)
135
+ rule.bind_date_or_time(elem.start_time)
127
136
  end
128
137
 
129
138
  def generate(start_time, stop_time)
130
139
  return Set.new(to_enum(:generate, start_time, stop_time)) unless block_given?
131
- instance = bind_date(start_time)
140
+ instance = bind_date_or_time(start_time)
132
141
 
133
142
  begin
134
143
  yield instance
@@ -85,17 +85,28 @@ module Masamune::Schema
85
85
  end
86
86
  end
87
87
 
88
- def partition_table(date)
89
- partition_range = partition_rule.bind_date(date)
88
+ def partition_table(date = nil)
89
+ return unless partition
90
+ return unless date
91
+ partition_range = partition_rule.bind_date_or_time(date)
90
92
  @partition_tables ||= {}
91
93
  @partition_tables[partition_range] ||= self.class.new(id: @id, store: store, columns: partition_table_columns, parent: self, range: partition_range, grain: grain, inherit: true)
92
94
  end
93
95
 
96
+ def partition_tables(start_date = nil, stop_date = nil)
97
+ return unless partition
98
+ return unless start_date && stop_date
99
+ (start_date .. stop_date).each do |date|
100
+ next unless date.day == 1
101
+ yield partition_table(date)
102
+ end
103
+ end
104
+
94
105
  def measures
95
106
  columns.select { |_, column| column.measure }
96
107
  end
97
108
 
98
- def constraints
109
+ def inheritance_constraints
99
110
  return unless range
100
111
  "CHECK (time_key >= #{range.start_time.to_i} AND time_key < #{range.stop_time.to_i})"
101
112
  end
@@ -183,8 +183,13 @@ module Masamune::Schema
183
183
  return to_enum(__method__).to_a.flatten.compact unless block_given?
184
184
  columns.map do |_, column|
185
185
  next if column.surrogate_key || column.ignore
186
- if column.reference
187
- (column.reference.natural_keys.any? ? column.reference.natural_keys : column.reference.denormalized_columns).each do |join_column|
186
+ if column.reference && column.reference.natural_keys.any?
187
+ column.reference.natural_keys.each do |join_column|
188
+ next if join_column.reference && join_column.natural_key
189
+ yield [column.reference, join_column]
190
+ end
191
+ elsif column.reference && column.reference.denormalized_columns.any?
192
+ column.reference.denormalized_columns.each do |join_column|
188
193
  yield [column.reference, join_column]
189
194
  end
190
195
  else
@@ -26,6 +26,7 @@ require 'thor'
26
26
  module Masamune::Tasks
27
27
  class DumpThor < Thor
28
28
  include Masamune::Thor
29
+ include Masamune::Actions::DateParse
29
30
  include Masamune::Transform::DefineSchema
30
31
 
31
32
  # FIXME need to add an unnecessary namespace until this issue is fixed:
@@ -35,9 +36,7 @@ module Masamune::Tasks
35
36
 
36
37
  desc 'dump', 'Dump schema'
37
38
  method_option :type, :enum => ['psql', 'hql'], :desc => 'Schema type', :default => 'psql'
38
- method_option :with_index, :type => :boolean, :desc => 'Dump schema with indexes', :default => true
39
- method_option :with_foreign_key, :type => :boolean, :desc => 'Dump schema with foreign key constraints', :default => true
40
- method_option :with_unique_constraint, :type => :boolean, :desc => 'Dump schema with uniqueness constraints', :default => true
39
+ method_option :section, :enum => ['pre', 'post', 'all'], :desc => 'Schema section', :default => 'all'
41
40
  def dump_exec
42
41
  print_catalog
43
42
  exit
@@ -49,10 +48,18 @@ module Masamune::Tasks
49
48
  def print_catalog
50
49
  case options[:type]
51
50
  when 'psql'
52
- puts define_schema(catalog, :postgres, options.slice(:with_index, :with_foreign_key, :with_unique_constraint).to_h.symbolize_keys)
51
+ puts define_schema(catalog, :postgres, define_schema_options)
53
52
  when 'hql'
54
- puts define_schema(catalog, :hive)
53
+ puts define_schema(catalog, :hive, define_schema_options)
55
54
  end
56
55
  end
56
+
57
+ def define_schema_options
58
+ {
59
+ section: options[:section].to_sym,
60
+ start_date: start_date,
61
+ stop_date: stop_date
62
+ }.reject { |_, v| v.blank? }
63
+ end
57
64
  end
58
65
  end
@@ -56,7 +56,7 @@ module Masamune
56
56
  end
57
57
 
58
58
  def render_to_string(template, parameters = {})
59
- instance = Template.new(File.dirname(template))
59
+ instance = self.new(File.dirname(template))
60
60
  combine instance.render(template, parameters)
61
61
  end
62
62
 
@@ -0,0 +1,28 @@
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
+ <%- if target.parent -%>
24
+ ALTER TABLE <%= target.name %> INHERIT <%= target.parent.name %>;
25
+ <%- end -%>
26
+ <%- if target.inheritance_constraints -%>
27
+ ALTER TABLE <%= target.name %> ADD CONSTRAINT <%= target.name %>_time_key_check <%= target.inheritance_constraints %>;
28
+ <%- end -%>
@@ -35,11 +35,14 @@ module Masamune::Transform
35
35
  operators += context.extra(:pre)
36
36
 
37
37
  context.dimensions.each do |_, dimension|
38
- operators << define_table(dimension, [], options)
38
+ operators << define_table(dimension, [], options[:section])
39
39
  end
40
40
 
41
41
  context.facts.each do |_, fact|
42
- operators << define_table(fact, [], options)
42
+ operators << define_table(fact, [], options[:section])
43
+ fact.partition_tables(options[:start_date], options[:stop_date]) do |fact_partition_table|
44
+ operators << define_table(fact_partition_table, [], options[:section])
45
+ end
43
46
  end
44
47
 
45
48
  operators += context.extra(:post)
@@ -22,22 +22,22 @@
22
22
 
23
23
  <%
24
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
25
  %>
29
26
 
30
27
  <%- target.children.each do |child| -%>
31
28
  <%= render 'define_table.psql.erb', target: child, **locals.except(:target, :files) %>
32
29
  <%- end -%>
33
30
 
31
+ <%- if helper.define_types? %>
34
32
  <%- target.enum_columns.each do |_, column| -%>
35
33
  DO $$ BEGIN
36
34
  IF NOT EXISTS (SELECT 1 FROM pg_type t WHERE LOWER(t.typname) = LOWER('<%= column.sql_type %>')) THEN
37
35
  CREATE TYPE <%= column.sql_type %> AS ENUM (<%= column.values.map { |value| "'#{value}'" }.join(', ') %>);
38
36
  END IF; END $$;
39
37
  <%- end -%>
38
+ <%- end -%>
40
39
 
40
+ <%- if helper.define_sequences? %>
41
41
  <%- target.sequence_columns.each do |_, column| -%>
42
42
  DO $$ BEGIN
43
43
  IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '<%= column.sequence_id %>') THEN
@@ -45,7 +45,9 @@ CREATE SEQUENCE <%= column.sequence_id %>;
45
45
  ALTER SEQUENCE <%= column.sequence_id %> RESTART <%= column.sequence_offset %>;
46
46
  END IF; END $$;
47
47
  <%- end -%>
48
+ <%- end -%>
48
49
 
50
+ <%- if helper.define_tables? %>
49
51
  <%- if target.temporary? -%>
50
52
  CREATE TEMPORARY TABLE IF NOT EXISTS <%= target.name %>
51
53
  <%- else -%>
@@ -56,25 +58,29 @@ CREATE TABLE IF NOT EXISTS <%= target.name %>
56
58
  <%= column.as_psql %><%= ',' unless last %>
57
59
  <%- end -%>
58
60
  );
61
+ <%- end -%>
59
62
 
60
- <%- unless target.temporary? || target.primary_keys.empty? -%>
63
+ <%- if helper.define_primary_keys? %>
61
64
  DO $$ BEGIN
62
65
  IF NOT EXISTS (SELECT 1 FROM pg_class c WHERE c.relname = '<%= target.name %>_pkey') THEN
63
66
  ALTER TABLE <%= target.name %> ADD PRIMARY KEY (<%= target.primary_keys.map(&:name).join(', ') %>);
64
67
  END IF; END $$;
65
68
  <%- end -%>
66
69
 
67
- <%- if with_foreign_key && !target.delay_constraints? -%>
70
+ <%- if helper.define_foreign_keys? -%>
68
71
  <%= render 'define_foreign_key.psql.erb', target: target %>
69
72
  <%- end -%>
70
73
 
74
+ <%- if helper.define_sequences? -%>
71
75
  <%- target.sequence_columns.each do |_, column| -%>
72
76
  DO $$ BEGIN
73
77
  IF NOT EXISTS (SELECT 1 WHERE sequence_owner('<%= column.sequence_id %>') = '<%= column.qualified_name %>') THEN
74
78
  ALTER SEQUENCE <%= column.sequence_id %> OWNED BY <%= column.qualified_name %>;
75
79
  END IF; END $$;
76
80
  <%- end -%>
81
+ <%- end -%>
77
82
 
83
+ <%- if helper.load_files? -%>
78
84
  <%- files.each do |file| -%>
79
85
  <%-
80
86
  copy_options = []
@@ -84,25 +90,33 @@ END IF; END $$;
84
90
  -%>
85
91
  COPY <%= target.name %> FROM '<%= file %>' WITH (<%= copy_options.join(", ") %>);
86
92
  <%- end -%>
93
+ <%- end -%>
87
94
 
88
- <%- if with_unique_constraint && !target.delay_constraints? -%>
95
+ <%- if helper.define_inheritance? -%>
96
+ <%= render 'define_inheritance.psql.erb', target: target %>
97
+ <%- end -%>
98
+
99
+ <%- if helper.define_unique_constraints? -%>
89
100
  <%= render 'define_unique.psql.erb', target: target %>
90
101
  <%- end -%>
91
102
 
92
- <%- if with_index && !target.delay_index? -%>
103
+ <%- if helper.define_indexes? -%>
93
104
  <%= render 'define_index.psql.erb', target: target %>
94
105
  <%- end -%>
95
106
 
107
+ <%- if helper.insert_rows? -%>
96
108
  <% target.insert_rows.each do |row| %>
97
109
  INSERT INTO <%= target.name %> (<%= row.insert_columns.join(', ') %>)
98
110
  SELECT <%= row.insert_values.join(', ') %>
99
111
  WHERE NOT EXISTS (SELECT 1 FROM <%= target.name %> WHERE <%= row.insert_constraints.join(' AND ') %>);
100
112
  <%- end -%>
113
+ <%- end -%>
101
114
 
102
- <%- if files.any? || target.insert_rows.any? -%>
115
+ <%- if helper.perform_analyze? -%>
103
116
  ANALYZE <%= target.name %>;
104
117
  <%- end -%>
105
118
 
119
+ <%- if helper.define_functions? -%>
106
120
  <% target.aliased_rows.each do |row| %>
107
121
  <%- row.natural_keys.each do |column| -%>
108
122
  CREATE OR REPLACE FUNCTION <%= row.name(column) %>
@@ -118,3 +132,4 @@ RETURNS <%= target.surrogate_key.sql_type %> IMMUTABLE AS $$
118
132
  $$ LANGUAGE SQL;
119
133
 
120
134
  <%- end -%>
135
+ <%- end -%>
@@ -24,23 +24,114 @@ module Masamune::Transform
24
24
  module DefineTable
25
25
  extend ActiveSupport::Concern
26
26
 
27
- def define_table(target, files = [], options = {})
27
+ def define_table(target, files = [], section = nil)
28
28
  return if target.implicit
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|
29
+ Operator.new(__method__, target: target, files: Masamune::Schema::Map.convert_files(files), section: section, helper: Helper, 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 Helper < SimpleDelegator
35
+ def files
36
+ locals[:files]
37
+ end
38
+
39
+ def section
40
+ locals[:section] || :all
41
+ end
42
+
43
+ def define_types?
44
+ !post_section?
45
+ end
46
+
47
+ def define_tables?
48
+ !post_section?
49
+ end
50
+
51
+ def define_functions?
52
+ !post_section?
53
+ end
54
+
55
+ def define_sequences?
56
+ !post_section?
57
+ end
58
+
59
+ def define_primary_keys?
60
+ !pre_section? && !(target.temporary? || target.primary_keys.empty?)
61
+ end
62
+
63
+ def define_inheritance?
64
+ return false unless target.inherited?
65
+ return false if pre_section?
66
+ return true if post_section?
67
+ !target.delay_indexes?
68
+ end
69
+
70
+ def define_indexes?
71
+ return false if pre_section?
72
+ return true if post_section?
73
+ !target.delay_indexes?
74
+ end
75
+
76
+ def define_foreign_keys?
77
+ return false if pre_section?
78
+ return true if post_section?
79
+ !target.delay_foreign_keys?
80
+ end
81
+
82
+ def define_unique_constraints?
83
+ return false if pre_section?
84
+ return true if post_section?
85
+ !target.delay_unique_constraints?
86
+ end
87
+
88
+ def insert_rows?
89
+ !post_section?
90
+ end
91
+
92
+ def load_files?
93
+ all_section?
94
+ end
95
+
96
+ def perform_analyze?
97
+ return false if pre_section?
98
+ return true if post_section?
99
+ files.any? || target.insert_rows.any?
100
+ end
101
+
102
+ private
103
+
104
+ def all_section?
105
+ section == :all
106
+ end
107
+
108
+ def pre_section?
109
+ section == :pre
110
+ end
111
+
112
+ def post_section?
113
+ section == :post
114
+ end
115
+ end
116
+
34
117
  class Postgres < SimpleDelegator
35
118
  def children
36
119
  super.map { |child| self.class.new(child) }
37
120
  end
38
121
 
39
- def delay_index?
122
+ def inherited?
123
+ type == :fact && inheritance_constraints
124
+ end
125
+
126
+ def delay_indexes?
127
+ type == :fact
128
+ end
129
+
130
+ def delay_foreign_keys?
40
131
  type == :fact
41
132
  end
42
133
 
43
- def delay_constraints?
134
+ def delay_unique_constraints?
44
135
  type == :fact
45
136
  end
46
137
  end