mondrian-olap 1.1.0 → 1.2.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.
@@ -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