mondrian-olap 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -72,7 +72,8 @@ describe "Cube" do
72
72
  @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
73
73
  end
74
74
 
75
- describe 'cache', unless: MONDRIAN_DRIVER == 'luciddb' do
75
+ # Do not execute tests on analytical databases with slow individual inserts
76
+ describe 'cache', unless: %w(vertica snowflake).include?(MONDRIAN_DRIVER) do
76
77
  def qt(name)
77
78
  @connection.quote_table_name(name.to_s)
78
79
  end
@@ -87,7 +88,7 @@ describe "Cube" do
87
88
  SQL
88
89
 
89
90
  case MONDRIAN_DRIVER
90
- when 'mysql', 'jdbc_mysql', 'postgresql', 'oracle', 'vertica', 'snowflake'
91
+ when 'mysql', 'jdbc_mysql', 'postgresql', 'oracle'
91
92
  @connection.execute 'CREATE TABLE sales_copy AS SELECT * FROM sales'
92
93
  when 'mssql', 'sqlserver'
93
94
  # Use raw_connection.execute to avoid detecting this query as a SELECT query
@@ -97,14 +98,8 @@ describe "Cube" do
97
98
  end
98
99
 
99
100
  after(:each) do
100
- case MONDRIAN_DRIVER
101
- when 'mysql', 'jdbc_mysql', 'postgresql', 'oracle', 'vertica', 'snowflake'
102
- @connection.execute 'TRUNCATE TABLE sales'
103
- @connection.execute 'INSERT INTO sales SELECT * FROM sales_copy'
104
- when 'mssql', 'sqlserver'
105
- @connection.execute 'TRUNCATE TABLE sales'
106
- @connection.execute 'INSERT INTO sales SELECT * FROM sales_copy'
107
- end
101
+ @connection.execute 'TRUNCATE TABLE sales'
102
+ @connection.execute 'INSERT INTO sales SELECT * FROM sales_copy'
108
103
 
109
104
  @olap.flush_schema_cache
110
105
  @olap.close
@@ -112,12 +107,7 @@ describe "Cube" do
112
107
  end
113
108
 
114
109
  after(:all) do
115
- case MONDRIAN_DRIVER
116
- when 'mysql', 'jdbc_mysql', 'postgresql', 'oracle', 'vertica', 'snowflake'
117
- @connection.execute 'DROP TABLE sales_copy'
118
- when 'mssql', 'sqlserver'
119
- @connection.execute 'DROP TABLE sales_copy'
120
- end
110
+ @connection.execute 'DROP TABLE sales_copy'
121
111
  end
122
112
 
123
113
  it 'should clear cache for deleted data at lower level with segments' do
@@ -61,9 +61,6 @@ fname || ' ' || lname
61
61
  </SQL>
62
62
  <SQL dialect="mysql">
63
63
  CONCAT(`customers`.`fname`, ' ', `customers`.`lname`)
64
- </SQL>
65
- <SQL dialect="luciddb">
66
- "fname" || ' ' || "lname"
67
64
  </SQL>
68
65
  <SQL dialect="generic">
69
66
  fullname
@@ -78,9 +75,6 @@ fname || ' ' || lname
78
75
  </SQL>
79
76
  <SQL dialect="mysql">
80
77
  CONCAT(`customers`.`fname`, ' ', `customers`.`lname`)
81
- </SQL>
82
- <SQL dialect="luciddb">
83
- "fname" || ' ' || "lname"
84
78
  </SQL>
85
79
  <SQL dialect="generic">
86
80
  fullname
@@ -61,9 +61,6 @@ fname || ' ' || lname
61
61
  </SQL>
62
62
  <SQL dialect="mysql">
63
63
  CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
64
- </SQL>
65
- <SQL dialect="luciddb">
66
- fname || ' ' || lname
67
64
  </SQL>
68
65
  <SQL dialect="generic">
69
66
  FULLNAME
@@ -78,9 +75,6 @@ fname || ' ' || lname
78
75
  </SQL>
79
76
  <SQL dialect="mysql">
80
77
  CONCAT(`customer`.`fname`, ' ', `customer`.`lname`)
81
- </SQL>
82
- <SQL dialect="luciddb">
83
- fname || ' ' || lname
84
78
  </SQL>
85
79
  <SQL dialect="generic">
86
80
  FULLNAME
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe "Mondrian features" do
@@ -11,13 +13,39 @@ describe "Mondrian features" do
11
13
  level 'Gender', :column => 'gender', :unique_members => true
12
14
  end
13
15
  end
16
+ dimension 'Promotions', :foreign_key => 'promotion_id' do
17
+ hierarchy :has_all => true, :primary_key => 'id' do
18
+ table 'promotions'
19
+ level 'Promotion', :column => 'id', :name_column => 'promotion', :unique_members => true, :ordinal_column => 'sequence', :type => 'Numeric'
20
+ end
21
+ end
22
+ dimension 'Linked Promotions', :foreign_key => 'customer_id' do
23
+ hierarchy :has_all => true, :primary_key => 'id', :primary_key_table => 'customers' do
24
+ join :left_key => 'related_fullname', :right_key => 'fullname' do
25
+ table "customers"
26
+ join :left_key => "promotion_id", :right_key => "id" do
27
+ table "customers", :alias => "customers_bt"
28
+ table "promotions"
29
+ end
30
+ end
31
+ level 'Promotion', :column => 'id', :name_column => 'promotion', :unique_members => true, :table => 'promotions', :ordinal_column => 'sequence', :type => 'Numeric', :approx_row_count => 10
32
+ end
33
+ end
14
34
  dimension 'Customers', :foreign_key => 'customer_id' do
15
35
  hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
16
36
  table 'customers'
17
37
  level 'Country', :column => 'country', :unique_members => true
18
38
  level 'State Province', :column => 'state_province', :unique_members => true
19
39
  level 'City', :column => 'city', :unique_members => false
20
- level 'Name', :column => 'fullname', :unique_members => true
40
+ level 'Name', :column => 'fullname', :unique_members => true do
41
+ property 'Related name', :column => 'related_fullname', :type => "String"
42
+ end
43
+ end
44
+ hierarchy 'ID', :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
45
+ table 'customers'
46
+ level 'ID', :column => 'id', :type => 'Numeric', :internal_type => 'long', :unique_members => true do
47
+ property 'Name', :column => 'fullname'
48
+ end
21
49
  end
22
50
  end
23
51
  dimension 'Time', :foreign_key => 'time_id', :type => 'TimeDimension' do
@@ -50,4 +78,46 @@ describe "Mondrian features" do
50
78
  end.should_not raise_error
51
79
  end
52
80
 
81
+ # test for https://jira.pentaho.com/browse/MONDRIAN-2683
82
+ it "should order crossjoin of rows" do
83
+ lambda do
84
+ @olap.from('Sales').
85
+ columns('[Measures].[Unit Sales]').
86
+ rows('[Customers].[Country].Members').crossjoin('[Gender].[Gender].Members').
87
+ order('[Measures].[Unit Sales]', :bdesc).
88
+ execute
89
+ end.should_not raise_error
90
+ end
91
+
92
+ it "should generate correct member name from large number key" do
93
+ result = @olap.from('Sales').
94
+ columns("Filter([Customers.ID].[ID].Members, [Customers.ID].CurrentMember.Properties('Name') = 'Big Number')").
95
+ execute
96
+ result.column_names.should == ["10000000000"]
97
+ end
98
+
99
+ # test for https://jira.pentaho.com/browse/MONDRIAN-990
100
+ it "should return result when diacritical marks used" do
101
+ full_name = '[Customers].[USA].[CA].[Rīga]'
102
+ result = @olap.from('Sales').columns(full_name).execute
103
+ result.column_full_names.should == [full_name]
104
+ end
105
+
106
+ it "should execute MDX with join tables" do
107
+ # Load dimension members in Mondrian cache as the problem occurred when searching members in the cache
108
+ @olap.from('Sales').columns('CROSSJOIN({[Linked Promotions].[Promotion].[Promotion 2]}, [Customers].[Name].Members)').execute
109
+
110
+ mdx = <<~MDX
111
+ SELECT
112
+ NON EMPTY FILTER(
113
+ CROSSJOIN({[Linked Promotions].[Promotion].[Promotion 2]}, [Customers].[Name].Members),
114
+ (([Measures].[Unit Sales]) <> 0)
115
+ ) ON ROWS,
116
+ [Measures].[Unit Sales] ON COLUMNS
117
+ FROM [Sales]
118
+ MDX
119
+
120
+ expect { @olap.execute mdx }.not_to raise_error
121
+ end
122
+
53
123
  end
data/spec/query_spec.rb CHANGED
@@ -788,7 +788,7 @@ describe "Query" do
788
788
  @drill_through.column_types.should == [
789
789
  :INT, :VARCHAR, :INT, :INT, :INT,
790
790
  :VARCHAR, :VARCHAR, :VARCHAR, :VARCHAR, :VARCHAR, :VARCHAR,
791
- :VARCHAR, :VARCHAR, :VARCHAR, :INT,
791
+ :VARCHAR, :VARCHAR, :VARCHAR, :BIGINT,
792
792
  :VARCHAR,
793
793
  :DECIMAL
794
794
  ]
@@ -912,15 +912,20 @@ describe "Query" do
912
912
  return: [
913
913
  "Name([Customers].[Name])",
914
914
  "Property([Customers].[Name], 'Gender')",
915
- "Property([Customers].[Name], 'Description')"
915
+ "Property([Customers].[Name], 'Description')",
916
+ "Property([Customers].[Name], 'Very long non-existing property name')"
916
917
  ]
917
918
  )
918
- @drill_through.column_labels.should == [ "Name", "Gender", "Description" ]
919
- @drill_through.rows.should == @sql.select_rows(<<-SQL
919
+ @drill_through.column_labels.should == [
920
+ "Name", "Gender", "Description",
921
+ "Very long non-existing property name"[0, MONDRIAN_DRIVER == 'oracle' ? 30 : 9999]
922
+ ]
923
+ @drill_through.rows.should == @sql.select_rows(<<-SQL)
920
924
  SELECT
921
925
  customers.fullname,
922
926
  customers.gender,
923
- customers.description
927
+ customers.description,
928
+ '' as non_existing
924
929
  FROM
925
930
  sales,
926
931
  customers,
@@ -940,7 +945,6 @@ describe "Query" do
940
945
  customers.gender,
941
946
  customers.description
942
947
  SQL
943
- )
944
948
  end
945
949
 
946
950
  it "should group by" do
data/spec/rake_tasks.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  namespace :db do
2
4
  task :require_spec_helper do
3
5
  require File.expand_path("../spec_helper", __FILE__)
@@ -40,6 +42,8 @@ namespace :db do
40
42
  t.string :lname, :limit => 30
41
43
  t.string :fullname, :limit => 60
42
44
  t.string :gender, :limit => 30
45
+ t.integer :promotion_id
46
+ t.string :related_fullname, :limit => 60
43
47
  # Mondrian does not support properties with Oracle CLOB type
44
48
  # as it tries to GROUP BY all columns when loading a dimension table
45
49
  if MONDRIAN_DRIVER == 'oracle'
@@ -47,13 +51,47 @@ namespace :db do
47
51
  else
48
52
  t.text :description
49
53
  end
54
+ end
55
+
56
+ if MONDRIAN_DRIVER == 'oracle'
57
+
58
+ execute "DROP TABLE PROMOTIONS" rescue nil
59
+ execute "DROP SEQUENCE PROMOTIONS_SEQ" rescue nil
60
+
61
+ execute <<~SQL
62
+ CREATE TABLE PROMOTIONS(
63
+ ID NUMBER(*,0) NOT NULL,
64
+ PROMOTION VARCHAR2(30 CHAR),
65
+ SEQUENCE NUMBER(38,0),
66
+ PRIMARY KEY ("ID")
67
+ )
68
+ SQL
69
+ execute "CREATE SEQUENCE PROMOTIONS_SEQ"
70
+ else
71
+ create_table :promotions, :force => true do |t|
72
+ t.string :promotion, :limit => 30
73
+ t.integer :sequence
74
+ end
75
+ end
50
76
 
77
+ case MONDRIAN_DRIVER
78
+ when /mysql/
79
+ execute "ALTER TABLE customers MODIFY COLUMN id BIGINT NOT NULL AUTO_INCREMENT"
80
+ when /postgresql/
81
+ execute "ALTER TABLE customers ALTER COLUMN id SET DATA TYPE bigint"
82
+ when /mssql|sqlserver/
83
+ sql = "SELECT name FROM sysobjects WHERE xtype = 'PK' AND parent_obj=OBJECT_ID('customers')"
84
+ primary_key_constraint = select_value(sql)
85
+ execute "ALTER TABLE customers DROP CONSTRAINT #{primary_key_constraint}"
86
+ execute "ALTER TABLE customers ALTER COLUMN id BIGINT"
87
+ execute "ALTER TABLE customers ADD CONSTRAINT #{primary_key_constraint} PRIMARY KEY (id)"
51
88
  end
52
89
 
53
90
  create_table :sales, :force => true, :id => false do |t|
54
91
  t.integer :product_id
55
92
  t.integer :time_id
56
93
  t.integer :customer_id
94
+ t.integer :promotion_id
57
95
  t.decimal :store_sales, :precision => 10, :scale => 4
58
96
  t.decimal :store_cost, :precision => 10, :scale => 4
59
97
  t.decimal :unit_sales, :precision => 10, :scale => 4
@@ -61,189 +99,236 @@ namespace :db do
61
99
  end
62
100
  end
63
101
 
64
- task :setup_luciddb => :require_spec_helper do
65
- # create link to mysql database to import tables
66
- # see description at http://pub.eigenbase.org/wiki/LucidDbCreateForeignServer
67
- if MONDRIAN_DRIVER == 'luciddb'
68
- conn = ActiveRecord::Base.connection
69
- conn.execute "drop schema mondrian_test_source cascade" rescue nil
70
- conn.execute "drop server mondrian_test_source" rescue nil
71
- conn.execute "create schema mondrian_test_source"
72
- conn.execute <<-SQL
73
- create server mondrian_test_source
74
- foreign data wrapper sys_jdbc
75
- options(
76
- driver_class 'com.mysql.jdbc.Driver',
77
- url 'jdbc:mysql://localhost/mondrian_test?characterEncoding=utf-8&useCursorFetch=true',
78
- user_name 'mondrian_test',
79
- password 'mondrian_test',
80
- login_timeout '10',
81
- fetch_size '1000',
82
- validation_query 'select 1',
83
- schema_name 'MONDRIAN_TEST',
84
- table_types 'TABLE')
85
- SQL
86
- conn.execute "import foreign schema mondrian_test from server mondrian_test_source into mondrian_test_source"
87
- end
88
- end
89
-
90
102
  task :define_models => :require_spec_helper do
91
- unless MONDRIAN_DRIVER == 'luciddb'
92
- class TimeDimension < ActiveRecord::Base
93
- self.table_name = "time"
94
- validates_presence_of :the_date
95
- before_create do
96
- self.the_day = the_date.strftime("%A")
97
- self.the_month = the_date.strftime("%B")
98
- self.the_year = the_date.strftime("%Y").to_i
99
- self.day_of_month = the_date.strftime("%d").to_i
100
- self.week_of_year = the_date.strftime("%W").to_i
101
- self.month_of_year = the_date.strftime("%m").to_i
102
- self.quarter = "Q#{(month_of_year-1)/3+1}"
103
- end
104
- end
105
- class Product < ActiveRecord::Base
106
- belongs_to :product_class
107
- end
108
- class ProductClass < ActiveRecord::Base
109
- end
110
- class Customer < ActiveRecord::Base
111
- end
112
- class Sales < ActiveRecord::Base
113
- self.table_name = "sales"
114
- belongs_to :time_by_day
115
- belongs_to :product
116
- belongs_to :customer
103
+ class TimeDimension < ActiveRecord::Base
104
+ self.table_name = "time"
105
+ validates_presence_of :the_date
106
+ before_create do
107
+ self.the_day = the_date.strftime("%A")
108
+ self.the_month = the_date.strftime("%B")
109
+ self.the_year = the_date.strftime("%Y").to_i
110
+ self.day_of_month = the_date.strftime("%d").to_i
111
+ self.week_of_year = the_date.strftime("%W").to_i
112
+ self.month_of_year = the_date.strftime("%m").to_i
113
+ self.quarter = "Q#{(month_of_year-1)/3+1}"
117
114
  end
118
115
  end
116
+ class Product < ActiveRecord::Base
117
+ belongs_to :product_class
118
+ end
119
+ class ProductClass < ActiveRecord::Base
120
+ end
121
+ class Customer < ActiveRecord::Base
122
+ end
123
+ class Promotion < ActiveRecord::Base
124
+ end
125
+ class Sales < ActiveRecord::Base
126
+ self.table_name = "sales"
127
+ belongs_to :time_by_day
128
+ belongs_to :product
129
+ belongs_to :customer
130
+ end
119
131
  end
120
132
 
121
133
  desc "Create test data"
122
- task :create_data => [:create_tables, :setup_luciddb, :create_time_data, :create_product_data, :create_customer_data, :create_sales_data]
134
+ task :create_data => [:create_tables] + ( %w(vertica snowflake).include?(ENV['MONDRIAN_DRIVER']) ? [:import_data] :
135
+ [ :create_time_data, :create_product_data, :create_promotion_data, :create_customer_data, :create_sales_data ] )
123
136
 
124
137
  task :create_time_data => :define_models do
125
138
  puts "==> Creating time dimension"
126
- if MONDRIAN_DRIVER == 'luciddb'
127
- ActiveRecord::Base.connection.execute 'truncate table "TIME"'
128
- ActiveRecord::Base.connection.execute 'insert into "TIME" select * from mondrian_test_source."time"'
129
- ActiveRecord::Base.connection.execute 'analyze table "TIME" compute statistics for all columns'
130
- else
131
- TimeDimension.delete_all
132
- start_time = Time.local(2010,1,1)
133
- (2*365).times do |i|
134
- TimeDimension.create!(:the_date => start_time + i.day)
135
- end
139
+ TimeDimension.delete_all
140
+ start_time = Time.utc(2010,1,1)
141
+ (2*365).times do |i|
142
+ TimeDimension.create!(:the_date => start_time + i.day)
136
143
  end
137
144
  end
138
145
 
139
146
  task :create_product_data => :define_models do
140
147
  puts "==> Creating product data"
141
- if MONDRIAN_DRIVER == 'luciddb'
142
- ActiveRecord::Base.connection.execute 'truncate table product_classes'
143
- ActiveRecord::Base.connection.execute 'truncate table products'
144
- ActiveRecord::Base.connection.execute 'insert into product_classes select * from mondrian_test_source."product_classes"'
145
- ActiveRecord::Base.connection.execute 'insert into products select * from mondrian_test_source."products"'
146
- ActiveRecord::Base.connection.execute 'analyze table product_classes compute statistics for all columns'
147
- ActiveRecord::Base.connection.execute 'analyze table products compute statistics for all columns'
148
- else
149
- Product.delete_all
150
- ProductClass.delete_all
151
- families = ["Drink", "Food", "Non-Consumable"]
152
- (1..100).each do |i|
153
- product_class = ProductClass.create!(
154
- :product_family => families[i % 3],
155
- :product_department => "Department #{i}",
156
- :product_category => "Category #{i}",
157
- :product_subcategory => "Subcategory #{i}"
158
- )
159
- Product.create!(
160
- # LucidDB is not returning inserted ID therefore doing it hard way
161
- :product_class_id => ProductClass.where(:product_category => "Category #{i}").to_a.first.id,
162
- :brand_name => "Brand #{i}",
163
- :product_name => "Product #{i}"
164
- )
165
- end
148
+ Product.delete_all
149
+ ProductClass.delete_all
150
+ families = ["Drink", "Food", "Non-Consumable"]
151
+ (1..100).each do |i|
152
+ product_class = ProductClass.create!(
153
+ :product_family => families[i % 3],
154
+ :product_department => "Department #{i}",
155
+ :product_category => "Category #{i}",
156
+ :product_subcategory => "Subcategory #{i}"
157
+ )
158
+ Product.create!(
159
+ :product_class_id => ProductClass.where(:product_category => "Category #{i}").to_a.first.id,
160
+ :brand_name => "Brand #{i}",
161
+ :product_name => "Product #{i}"
162
+ )
163
+ end
164
+ end
165
+
166
+ task :create_promotion_data => :define_models do
167
+ puts "==> Creating promotion data"
168
+ Promotion.delete_all
169
+ (1..10).each do |i|
170
+ Promotion.create!(promotion: "Promotion #{i}", sequence: i)
166
171
  end
167
172
  end
168
173
 
169
174
  task :create_customer_data => :define_models do
170
175
  puts "==> Creating customer data"
171
- if MONDRIAN_DRIVER == 'luciddb'
172
- ActiveRecord::Base.connection.execute 'truncate table customers'
173
- ActiveRecord::Base.connection.execute 'insert into customers select * from mondrian_test_source."customers"'
174
- ActiveRecord::Base.connection.execute 'analyze table customers compute statistics for all columns'
175
- else
176
- Customer.delete_all
177
- i = 0
178
- [
179
- ["Canada", "BC", "Burnaby"],["Canada", "BC", "Cliffside"],["Canada", "BC", "Haney"],["Canada", "BC", "Ladner"],
180
- ["Canada", "BC", "Langford"],["Canada", "BC", "Langley"],["Canada", "BC", "Metchosin"],["Canada", "BC", "N. Vancouver"],
181
- ["Canada", "BC", "Newton"],["Canada", "BC", "Oak Bay"],["Canada", "BC", "Port Hammond"],["Canada", "BC", "Richmond"],
182
- ["Canada", "BC", "Royal Oak"],["Canada", "BC", "Shawnee"],["Canada", "BC", "Sooke"],["Canada", "BC", "Vancouver"],
183
- ["Canada", "BC", "Victoria"],["Canada", "BC", "Westminster"],
184
- ["Mexico", "DF", "San Andres"],["Mexico", "DF", "Santa Anita"],["Mexico", "DF", "Santa Fe"],["Mexico", "DF", "Tixapan"],
185
- ["Mexico", "Guerrero", "Acapulco"],["Mexico", "Jalisco", "Guadalajara"],["Mexico", "Mexico", "Mexico City"],
186
- ["Mexico", "Oaxaca", "Tlaxiaco"],["Mexico", "Sinaloa", "La Cruz"],["Mexico", "Veracruz", "Orizaba"],
187
- ["Mexico", "Yucatan", "Merida"],["Mexico", "Zacatecas", "Camacho"],["Mexico", "Zacatecas", "Hidalgo"],
188
- ["USA", "CA", "Altadena"],["USA", "CA", "Arcadia"],["USA", "CA", "Bellflower"],["USA", "CA", "Berkeley"],
189
- ["USA", "CA", "Beverly Hills"],["USA", "CA", "Burbank"],["USA", "CA", "Burlingame"],["USA", "CA", "Chula Vista"],
190
- ["USA", "CA", "Colma"],["USA", "CA", "Concord"],["USA", "CA", "Coronado"],["USA", "CA", "Daly City"],
191
- ["USA", "CA", "Downey"],["USA", "CA", "El Cajon"],["USA", "CA", "Fremont"],["USA", "CA", "Glendale"],
192
- ["USA", "CA", "Grossmont"],["USA", "CA", "Imperial Beach"],["USA", "CA", "La Jolla"],["USA", "CA", "La Mesa"],
193
- ["USA", "CA", "Lakewood"],["USA", "CA", "Lemon Grove"],["USA", "CA", "Lincoln Acres"],["USA", "CA", "Long Beach"],
194
- ["USA", "CA", "Los Angeles"],["USA", "CA", "Mill Valley"],["USA", "CA", "National City"],["USA", "CA", "Newport Beach"],
195
- ["USA", "CA", "Novato"],["USA", "CA", "Oakland"],["USA", "CA", "Palo Alto"],["USA", "CA", "Pomona"],
196
- ["USA", "CA", "Redwood City"],["USA", "CA", "Richmond"],["USA", "CA", "San Carlos"],["USA", "CA", "San Diego"],
197
- ["USA", "CA", "San Francisco"],["USA", "CA", "San Gabriel"],["USA", "CA", "San Jose"],["USA", "CA", "Santa Cruz"],
198
- ["USA", "CA", "Santa Monica"],["USA", "CA", "Spring Valley"],["USA", "CA", "Torrance"],["USA", "CA", "West Covina"],
199
- ["USA", "CA", "Woodland Hills"],
200
- ["USA", "OR", "Albany"],["USA", "OR", "Beaverton"],["USA", "OR", "Corvallis"],["USA", "OR", "Lake Oswego"],
201
- ["USA", "OR", "Lebanon"],["USA", "OR", "Milwaukie"],["USA", "OR", "Oregon City"],["USA", "OR", "Portland"],
202
- ["USA", "OR", "Salem"],["USA", "OR", "W. Linn"],["USA", "OR", "Woodburn"],
203
- ["USA", "WA", "Anacortes"],["USA", "WA", "Ballard"],["USA", "WA", "Bellingham"],["USA", "WA", "Bremerton"],
204
- ["USA", "WA", "Burien"],["USA", "WA", "Edmonds"],["USA", "WA", "Everett"],["USA", "WA", "Issaquah"],
205
- ["USA", "WA", "Kirkland"],["USA", "WA", "Lynnwood"],["USA", "WA", "Marysville"],["USA", "WA", "Olympia"],
206
- ["USA", "WA", "Port Orchard"],["USA", "WA", "Puyallup"],["USA", "WA", "Redmond"],["USA", "WA", "Renton"],
207
- ["USA", "WA", "Seattle"],["USA", "WA", "Sedro Woolley"],["USA", "WA", "Spokane"],["USA", "WA", "Tacoma"],
208
- ["USA", "WA", "Walla Walla"],["USA", "WA", "Yakima"]
209
- ].each do |country, state, city|
210
- i += 1
211
- Customer.create!(
212
- :country => country,
213
- :state_province => state,
214
- :city => city,
215
- :fname => "First#{i}",
216
- :lname => "Last#{i}",
217
- :fullname => "First#{i} Last#{i}",
218
- :gender => i % 2 == 0 ? "M" : "F",
219
- :description => 100.times.map{"1234567890"}.join("\n")
220
- )
176
+ Customer.delete_all
177
+ promotions = Promotion.order("id").to_a
178
+ i = 0
179
+ [
180
+ ["Canada", "BC", "Burnaby"],["Canada", "BC", "Cliffside"],["Canada", "BC", "Haney"],["Canada", "BC", "Ladner"],
181
+ ["Canada", "BC", "Langford"],["Canada", "BC", "Langley"],["Canada", "BC", "Metchosin"],["Canada", "BC", "N. Vancouver"],
182
+ ["Canada", "BC", "Newton"],["Canada", "BC", "Oak Bay"],["Canada", "BC", "Port Hammond"],["Canada", "BC", "Richmond"],
183
+ ["Canada", "BC", "Royal Oak"],["Canada", "BC", "Shawnee"],["Canada", "BC", "Sooke"],["Canada", "BC", "Vancouver"],
184
+ ["Canada", "BC", "Victoria"],["Canada", "BC", "Westminster"],
185
+ ["Mexico", "DF", "San Andres"],["Mexico", "DF", "Santa Anita"],["Mexico", "DF", "Santa Fe"],["Mexico", "DF", "Tixapan"],
186
+ ["Mexico", "Guerrero", "Acapulco"],["Mexico", "Jalisco", "Guadalajara"],["Mexico", "Mexico", "Mexico City"],
187
+ ["Mexico", "Oaxaca", "Tlaxiaco"],["Mexico", "Sinaloa", "La Cruz"],["Mexico", "Veracruz", "Orizaba"],
188
+ ["Mexico", "Yucatan", "Merida"],["Mexico", "Zacatecas", "Camacho"],["Mexico", "Zacatecas", "Hidalgo"],
189
+ ["USA", "CA", "Altadena"],["USA", "CA", "Arcadia"],["USA", "CA", "Bellflower"],["USA", "CA", "Berkeley"],
190
+ ["USA", "CA", "Beverly Hills"],["USA", "CA", "Burbank"],["USA", "CA", "Burlingame"],["USA", "CA", "Chula Vista"],
191
+ ["USA", "CA", "Colma"],["USA", "CA", "Concord"],["USA", "CA", "Coronado"],["USA", "CA", "Daly City"],
192
+ ["USA", "CA", "Downey"],["USA", "CA", "El Cajon"],["USA", "CA", "Fremont"],["USA", "CA", "Glendale"],
193
+ ["USA", "CA", "Grossmont"],["USA", "CA", "Imperial Beach"],["USA", "CA", "La Jolla"],["USA", "CA", "La Mesa"],
194
+ ["USA", "CA", "Lakewood"],["USA", "CA", "Lemon Grove"],["USA", "CA", "Lincoln Acres"],["USA", "CA", "Long Beach"],
195
+ ["USA", "CA", "Los Angeles"],["USA", "CA", "Mill Valley"],["USA", "CA", "National City"],["USA", "CA", "Newport Beach"],
196
+ ["USA", "CA", "Novato"],["USA", "CA", "Oakland"],["USA", "CA", "Palo Alto"],["USA", "CA", "Pomona"],
197
+ ["USA", "CA", "Redwood City"],["USA", "CA", "Richmond"],["USA", "CA", "San Carlos"],["USA", "CA", "San Diego"],
198
+ ["USA", "CA", "San Francisco"],["USA", "CA", "San Gabriel"],["USA", "CA", "San Jose"],["USA", "CA", "Santa Cruz"],
199
+ ["USA", "CA", "Santa Monica"],["USA", "CA", "Spring Valley"],["USA", "CA", "Torrance"],["USA", "CA", "West Covina"],
200
+ ["USA", "CA", "Woodland Hills"],
201
+ ["USA", "OR", "Albany"],["USA", "OR", "Beaverton"],["USA", "OR", "Corvallis"],["USA", "OR", "Lake Oswego"],
202
+ ["USA", "OR", "Lebanon"],["USA", "OR", "Milwaukie"],["USA", "OR", "Oregon City"],["USA", "OR", "Portland"],
203
+ ["USA", "OR", "Salem"],["USA", "OR", "W. Linn"],["USA", "OR", "Woodburn"],
204
+ ["USA", "WA", "Anacortes"],["USA", "WA", "Ballard"],["USA", "WA", "Bellingham"],["USA", "WA", "Bremerton"],
205
+ ["USA", "WA", "Burien"],["USA", "WA", "Edmonds"],["USA", "WA", "Everett"],["USA", "WA", "Issaquah"],
206
+ ["USA", "WA", "Kirkland"],["USA", "WA", "Lynnwood"],["USA", "WA", "Marysville"],["USA", "WA", "Olympia"],
207
+ ["USA", "WA", "Port Orchard"],["USA", "WA", "Puyallup"],["USA", "WA", "Redmond"],["USA", "WA", "Renton"],
208
+ ["USA", "WA", "Seattle"],["USA", "WA", "Sedro Woolley"],["USA", "WA", "Spokane"],["USA", "WA", "Tacoma"],
209
+ ["USA", "WA", "Walla Walla"],["USA", "WA", "Yakima"]
210
+ ].each do |country, state, city|
211
+ i += 1
212
+ Customer.create!(
213
+ :country => country,
214
+ :state_province => state,
215
+ :city => city,
216
+ :fname => "First#{i}",
217
+ :lname => "Last#{i}",
218
+ :fullname => "First#{i} Last#{i}",
219
+ :gender => i % 2 == 0 ? "M" : "F",
220
+ :promotion_id => promotions[i % 10].id,
221
+ :related_fullname => "First#{i} Last#{i}",
222
+ :description => 100.times.map{"1234567890"}.join("\n")
223
+ )
224
+ end
225
+ # Create additional customer with large ID
226
+ attributes = {
227
+ :id => 10_000_000_000,
228
+ :country => "USA",
229
+ :state_province => "CA",
230
+ :city => "Rīga", # For testing UTF-8 characters
231
+ :fname => "Big",
232
+ :lname => "Number",
233
+ :fullname => "Big Number",
234
+ :gender => "M",
235
+ :promotion_id => promotions.first.id,
236
+ :related_fullname => "Big Number"
237
+ }
238
+ case MONDRIAN_DRIVER
239
+ when /mssql|sqlserver/
240
+ Customer.connection.with_identity_insert_enabled("customers") do
241
+ Customer.create!(attributes)
221
242
  end
243
+ else
244
+ Customer.create!(attributes)
222
245
  end
223
246
  end
224
247
 
225
248
  task :create_sales_data => :define_models do
226
249
  puts "==> Creating sales data"
227
- if MONDRIAN_DRIVER == 'luciddb'
228
- ActiveRecord::Base.connection.execute 'truncate table sales'
229
- ActiveRecord::Base.connection.execute 'insert into sales select * from mondrian_test_source."sales"'
230
- ActiveRecord::Base.connection.execute 'analyze table sales compute statistics for all columns'
231
- else
232
- Sales.delete_all
233
- count = 100
234
- # LucidDB does not support LIMIT therefore select all and limit in Ruby
235
- products = Product.order("id").to_a[0...count]
236
- times = TimeDimension.order("id").to_a[0...count]
237
- customers = Customer.order("id").to_a[0...count]
238
- count.times do |i|
239
- Sales.create!(
240
- :product_id => products[i].id,
241
- :time_id => times[i].id,
242
- :customer_id => customers[i].id,
243
- :store_sales => BigDecimal("2#{i}.12"),
244
- :store_cost => BigDecimal("1#{i}.1234"),
245
- :unit_sales => i+1
246
- )
250
+ Sales.delete_all
251
+ count = 100
252
+ products = Product.order("id").to_a[0...count]
253
+ times = TimeDimension.order("id").to_a[0...count]
254
+ customers = Customer.order("id").to_a[0...count]
255
+ promotions = Promotion.order("id").to_a[0...count]
256
+ count.times do |i|
257
+ Sales.create!(
258
+ :product_id => products[i].id,
259
+ :time_id => times[i].id,
260
+ :customer_id => customers[i].id,
261
+ :promotion_id => promotions[i % 10].id,
262
+ :store_sales => BigDecimal("2#{i}.12"),
263
+ :store_cost => BigDecimal("1#{i}.1234"),
264
+ :unit_sales => i+1
265
+ )
266
+ end
267
+ end
268
+
269
+ export_data_dir = File.expand_path("spec/support/data")
270
+ table_names = %w(time product_classes products customers sales)
271
+
272
+ desc "Export test data"
273
+ task :export_data => :create_data do
274
+ require "csv"
275
+ puts "==> Exporting data"
276
+ conn = ActiveRecord::Base.connection
277
+ table_names.each do |table_name|
278
+ column_names = conn.columns(table_name).map(&:name)
279
+ csv_content = conn.select_rows("SELECT #{column_names.join(',')} FROM #{table_name}").map do |row|
280
+ row.map do |value|
281
+ case value
282
+ when Time
283
+ value.utc.to_s(:db)
284
+ else
285
+ value
286
+ end
287
+ end.to_csv
288
+ end.join
289
+ file_path = File.expand_path("#{table_name}.csv", export_data_dir)
290
+ File.open(file_path, "w") do |file|
291
+ file.write column_names.to_csv
292
+ file.write csv_content
293
+ end
294
+ end
295
+ end
296
+
297
+ task :import_data => :require_spec_helper do
298
+ puts "==> Importing data"
299
+ conn = ActiveRecord::Base.connection
300
+
301
+ case MONDRIAN_DRIVER
302
+ when 'vertica'
303
+ table_names.each do |table_name|
304
+ puts "==> Truncate #{table_name}"
305
+ conn.execute "TRUNCATE TABLE #{table_name}"
306
+ puts "==> Copy into #{table_name}"
307
+ file_path = "#{export_data_dir}/#{table_name}.csv"
308
+ columns_string = File.open(file_path) { |f| f.gets }.chomp
309
+ count = conn.execute "COPY #{table_name}(#{columns_string}) FROM LOCAL '#{file_path}' " \
310
+ "PARSER public.fcsvparser(header='true') ABORT ON ERROR REJECTMAX 0"
311
+ puts "==> Loaded #{count} records"
312
+ end
313
+
314
+ when 'snowflake'
315
+ conn.execute <<-SQL
316
+ CREATE OR REPLACE FILE FORMAT csv
317
+ TYPE = 'CSV' COMPRESSION = 'AUTO' FIELD_DELIMITER = ',' RECORD_DELIMITER = '\\n' SKIP_HEADER = 1
318
+ FIELD_OPTIONALLY_ENCLOSED_BY = '\\042' TRIM_SPACE = FALSE ERROR_ON_COLUMN_COUNT_MISMATCH = TRUE ESCAPE = 'NONE'
319
+ ESCAPE_UNENCLOSED_FIELD = 'NONE' DATE_FORMAT = 'AUTO' TIMESTAMP_FORMAT = 'AUTO' NULL_IF = ('')
320
+ SQL
321
+ conn.execute "CREATE OR REPLACE STAGE csv_stage FILE_FORMAT = csv"
322
+ conn.execute "PUT file://#{export_data_dir}/*.csv @csv_stage AUTO_COMPRESS = TRUE"
323
+ table_names.each do |table_name|
324
+ puts "==> Truncate #{table_name}"
325
+ conn.execute "TRUNCATE TABLE #{table_name}"
326
+ puts "==> Copy into #{table_name}"
327
+ file_path = "#{export_data_dir}/#{table_name}.csv"
328
+ columns_string = File.open(file_path) { |f| f.gets }.chomp
329
+ count = conn.execute "COPY INTO #{table_name}(#{columns_string}) FROM @csv_stage/#{table_name}.csv.gz " \
330
+ "FILE_FORMAT = (FORMAT_NAME = csv)"
331
+ puts "==> Loaded #{count} records"
247
332
  end
248
333
  end
249
334
  end
@@ -251,7 +336,7 @@ namespace :db do
251
336
  end
252
337
 
253
338
  namespace :spec do
254
- %w(mysql jdbc_mysql postgresql oracle luciddb mssql sqlserver).each do |driver|
339
+ %w(mysql jdbc_mysql postgresql oracle mssql sqlserver vertica snowflake).each do |driver|
255
340
  desc "Run specs with #{driver} driver"
256
341
  task driver do
257
342
  ENV['MONDRIAN_DRIVER'] = driver