mondrian-olap 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +33 -0
  3. data/LICENSE-Mondrian.txt +87 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +4 -4
  6. data/VERSION +1 -1
  7. data/lib/mondrian/jars/guava-17.0.jar +0 -0
  8. data/lib/mondrian/jars/log4j-api-2.17.1.jar +0 -0
  9. data/lib/mondrian/jars/log4j-core-2.17.1.jar +0 -0
  10. data/lib/mondrian/jars/log4j2-config.jar +0 -0
  11. data/lib/mondrian/jars/mondrian-9.3.0.0.jar +0 -0
  12. data/lib/mondrian/olap/connection.rb +126 -73
  13. data/lib/mondrian/olap/cube.rb +46 -4
  14. data/lib/mondrian/olap/error.rb +10 -2
  15. data/lib/mondrian/olap/query.rb +1 -0
  16. data/lib/mondrian/olap/result.rb +132 -56
  17. data/lib/mondrian/olap/schema.rb +9 -3
  18. data/lib/mondrian/olap/schema_element.rb +6 -3
  19. data/lib/mondrian/olap/schema_udf.rb +8 -82
  20. data/lib/mondrian/olap.rb +11 -7
  21. data/spec/connection_role_spec.rb +4 -1
  22. data/spec/connection_spec.rb +38 -5
  23. data/spec/cube_cache_control_spec.rb +7 -17
  24. data/spec/cube_spec.rb +36 -2
  25. data/spec/fixtures/MondrianTest.xml +40 -6
  26. data/spec/fixtures/MondrianTestOracle.xml +40 -6
  27. data/spec/mondrian_spec.rb +203 -1
  28. data/spec/query_spec.rb +94 -25
  29. data/spec/rake_tasks.rb +319 -165
  30. data/spec/schema_definition_spec.rb +8 -241
  31. data/spec/spec_helper.rb +330 -112
  32. data/spec/support/data/customers.csv +10902 -0
  33. data/spec/support/data/product_classes.csv +101 -0
  34. data/spec/support/data/products.csv +101 -0
  35. data/spec/support/data/promotions.csv +11 -0
  36. data/spec/support/data/sales.csv +101 -0
  37. data/spec/support/data/time.csv +731 -0
  38. data/spec/support/data/warehouse.csv +101 -0
  39. data/spec/support/matchers/be_like.rb +1 -0
  40. metadata +42 -83
  41. data/LICENSE-Mondrian.html +0 -259
  42. data/lib/mondrian/jars/log4j-1.2.17.jar +0 -0
  43. data/lib/mondrian/jars/log4j.properties +0 -3
  44. data/lib/mondrian/jars/mondrian-8.3.0.5.jar +0 -0
data/spec/rake_tasks.rb CHANGED
@@ -1,12 +1,16 @@
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__)
4
6
  end
5
7
 
8
+ import_data_drivers = %w(vertica snowflake clickhouse mariadb)
9
+
6
10
  desc "Create test database tables"
7
11
  task :create_tables => :require_spec_helper do
8
12
  puts "==> Creating tables for test data"
9
- ActiveRecord::Schema.define do
13
+ ActiveRecord::Schema.instance_eval do
10
14
 
11
15
  create_table :time, :force => true do |t|
12
16
  t.datetime :the_date
@@ -32,7 +36,10 @@ namespace :db do
32
36
  t.string :product_family, :limit => 30
33
37
  end
34
38
 
35
- create_table :customers, :force => true do |t|
39
+ customers_options = {force: true}
40
+ customers_options[:id] = false if import_data_drivers.include?(MONDRIAN_DRIVER)
41
+ create_table :customers, **customers_options do |t|
42
+ t.integer :id, :limit => 8 if import_data_drivers.include?(MONDRIAN_DRIVER)
36
43
  t.string :country, :limit => 30
37
44
  t.string :state_province, :limit => 30
38
45
  t.string :city, :limit => 30
@@ -40,6 +47,9 @@ namespace :db do
40
47
  t.string :lname, :limit => 30
41
48
  t.string :fullname, :limit => 60
42
49
  t.string :gender, :limit => 30
50
+ t.date :birthdate
51
+ t.integer :promotion_id
52
+ t.string :related_fullname, :limit => 60
43
53
  # Mondrian does not support properties with Oracle CLOB type
44
54
  # as it tries to GROUP BY all columns when loading a dimension table
45
55
  if MONDRIAN_DRIVER == 'oracle'
@@ -47,211 +57,355 @@ namespace :db do
47
57
  else
48
58
  t.text :description
49
59
  end
60
+ end
61
+
62
+ if MONDRIAN_DRIVER == 'oracle'
63
+
64
+ execute "DROP TABLE PROMOTIONS" rescue nil
65
+ execute "DROP SEQUENCE PROMOTIONS_SEQ" rescue nil
66
+
67
+ execute <<~SQL
68
+ CREATE TABLE PROMOTIONS(
69
+ ID NUMBER(*,0) NOT NULL,
70
+ PROMOTION VARCHAR2(30 CHAR),
71
+ SEQUENCE NUMBER(38,0),
72
+ PRIMARY KEY ("ID")
73
+ )
74
+ SQL
75
+ execute "CREATE SEQUENCE PROMOTIONS_SEQ"
76
+ else
77
+ create_table :promotions, :force => true do |t|
78
+ t.string :promotion, :limit => 30
79
+ t.integer :sequence
80
+ end
81
+ end
50
82
 
83
+ case MONDRIAN_DRIVER
84
+ when /mysql/
85
+ execute "ALTER TABLE customers MODIFY COLUMN id BIGINT NOT NULL AUTO_INCREMENT"
86
+ when /postgresql/
87
+ execute "ALTER TABLE customers ALTER COLUMN id SET DATA TYPE bigint"
88
+ when /sqlserver/
89
+ sql = "SELECT name FROM sysobjects WHERE xtype = 'PK' AND parent_obj=OBJECT_ID('customers')"
90
+ primary_key_constraint = select_value(sql)
91
+ execute "ALTER TABLE customers DROP CONSTRAINT #{primary_key_constraint}"
92
+ execute "ALTER TABLE customers ALTER COLUMN id BIGINT"
93
+ execute "ALTER TABLE customers ADD CONSTRAINT #{primary_key_constraint} PRIMARY KEY (id)"
51
94
  end
52
95
 
53
96
  create_table :sales, :force => true, :id => false do |t|
54
97
  t.integer :product_id
55
98
  t.integer :time_id
56
- t.integer :customer_id
57
- t.decimal :store_sales, :precision => 10, :scale => 4
58
- t.decimal :store_cost, :precision => 10, :scale => 4
59
- t.decimal :unit_sales, :precision => 10, :scale => 4
99
+ t.integer :customer_id, limit: MONDRIAN_DRIVER == 'sqlserver' ? nil : 8
100
+ t.integer :promotion_id
101
+ t.decimal :store_sales, precision: 10, scale: 4
102
+ t.decimal :store_cost, precision: 10, scale: 4
103
+ t.decimal :unit_sales, precision: 10, scale: 4
60
104
  end
61
- end
62
- end
63
105
 
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"
106
+ case MONDRIAN_DRIVER
107
+ when /sqlserver/
108
+ execute "ALTER TABLE sales ALTER COLUMN customer_id BIGINT"
109
+ end
110
+
111
+ create_table :warehouse, :force => true, :id => false do |t|
112
+ t.integer :product_id
113
+ t.integer :time_id
114
+ t.integer :units_shipped
115
+ t.decimal :store_invoice, precision: 10, scale: 4
116
+ end
87
117
  end
88
118
  end
89
119
 
90
120
  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
121
+ class TimeDimension < ActiveRecord::Base
122
+ self.table_name = "time"
123
+ validates_presence_of :the_date
124
+ before_create do
125
+ self.the_day = the_date.strftime("%A")
126
+ self.the_month = the_date.strftime("%B")
127
+ self.the_year = the_date.strftime("%Y").to_i
128
+ self.day_of_month = the_date.strftime("%d").to_i
129
+ self.week_of_year = the_date.strftime("%W").to_i
130
+ self.month_of_year = the_date.strftime("%m").to_i
131
+ self.quarter = "Q#{(month_of_year-1)/3+1}"
117
132
  end
118
133
  end
134
+ class Product < ActiveRecord::Base
135
+ belongs_to :product_class
136
+ end
137
+ class ProductClass < ActiveRecord::Base
138
+ end
139
+ class Customer < ActiveRecord::Base
140
+ end
141
+ class Promotion < ActiveRecord::Base
142
+ end
143
+ class Sales < ActiveRecord::Base
144
+ self.table_name = "sales"
145
+ belongs_to :time_by_day
146
+ belongs_to :product
147
+ belongs_to :customer
148
+ end
149
+ class Warehouse < ActiveRecord::Base
150
+ self.table_name = "warehouse"
151
+ belongs_to :time_by_day
152
+ belongs_to :product
153
+ end
119
154
  end
120
155
 
121
156
  desc "Create test data"
122
- task :create_data => [:create_tables, :setup_luciddb, :create_time_data, :create_product_data, :create_customer_data, :create_sales_data]
157
+ task :create_data => [:create_tables] + (import_data_drivers.include?(ENV['MONDRIAN_DRIVER']) ? [:import_data] :
158
+ [ :create_time_data, :create_product_data, :create_promotion_data, :create_customer_data, :create_sales_data,
159
+ :create_warehouse_data ] )
123
160
 
124
161
  task :create_time_data => :define_models do
125
162
  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
163
+ TimeDimension.delete_all
164
+ start_time = Time.utc(2010,1,1)
165
+ (2*365).times do |i|
166
+ TimeDimension.create!(:the_date => start_time + i.day)
136
167
  end
137
168
  end
138
169
 
139
170
  task :create_product_data => :define_models do
140
171
  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
172
+ Product.delete_all
173
+ ProductClass.delete_all
174
+ families = ["Drink", "Food", "Non-Consumable"]
175
+ (1..100).each do |i|
176
+ product_class = ProductClass.create!(
177
+ :product_family => families[i % 3],
178
+ :product_department => "Department #{i}",
179
+ :product_category => "Category #{i}",
180
+ :product_subcategory => "Subcategory #{i}"
181
+ )
182
+ Product.create!(
183
+ :product_class_id => ProductClass.where(:product_category => "Category #{i}").to_a.first.id,
184
+ :brand_name => "Brand #{i}",
185
+ :product_name => "Product #{i}"
186
+ )
187
+ end
188
+ end
189
+
190
+ task :create_promotion_data => :define_models do
191
+ puts "==> Creating promotion data"
192
+ Promotion.delete_all
193
+ (1..10).each do |i|
194
+ Promotion.create!(promotion: "Promotion #{i}", sequence: i)
166
195
  end
167
196
  end
168
197
 
169
198
  task :create_customer_data => :define_models do
170
199
  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'
200
+ Customer.delete_all
201
+ promotions = Promotion.order("id").to_a
202
+ i = 0
203
+ [
204
+ ["Canada", "BC", "Burnaby"],["Canada", "BC", "Cliffside"],["Canada", "BC", "Haney"],["Canada", "BC", "Ladner"],
205
+ ["Canada", "BC", "Langford"],["Canada", "BC", "Langley"],["Canada", "BC", "Metchosin"],["Canada", "BC", "N. Vancouver"],
206
+ ["Canada", "BC", "Newton"],["Canada", "BC", "Oak Bay"],["Canada", "BC", "Port Hammond"],["Canada", "BC", "Richmond"],
207
+ ["Canada", "BC", "Royal Oak"],["Canada", "BC", "Shawnee"],["Canada", "BC", "Sooke"],["Canada", "BC", "Vancouver"],
208
+ ["Canada", "BC", "Victoria"],["Canada", "BC", "Westminster"],
209
+ ["Mexico", "DF", "San Andres"],["Mexico", "DF", "Santa Anita"],["Mexico", "DF", "Santa Fe"],["Mexico", "DF", "Tixapan"],
210
+ ["Mexico", "Guerrero", "Acapulco"],["Mexico", "Jalisco", "Guadalajara"],["Mexico", "Mexico", "Mexico City"],
211
+ ["Mexico", "Oaxaca", "Tlaxiaco"],["Mexico", "Sinaloa", "La Cruz"],["Mexico", "Veracruz", "Orizaba"],
212
+ ["Mexico", "Yucatan", "Merida"],["Mexico", "Zacatecas", "Camacho"],["Mexico", "Zacatecas", "Hidalgo"],
213
+ ["USA", "CA", "Altadena"],["USA", "CA", "Arcadia"],["USA", "CA", "Bellflower"],["USA", "CA", "Berkeley"],
214
+ ["USA", "CA", "Beverly Hills"],["USA", "CA", "Burbank"],["USA", "CA", "Burlingame"],["USA", "CA", "Chula Vista"],
215
+ ["USA", "CA", "Colma"],["USA", "CA", "Concord"],["USA", "CA", "Coronado"],["USA", "CA", "Daly City"],
216
+ ["USA", "CA", "Downey"],["USA", "CA", "El Cajon"],["USA", "CA", "Fremont"],["USA", "CA", "Glendale"],
217
+ ["USA", "CA", "Grossmont"],["USA", "CA", "Imperial Beach"],["USA", "CA", "La Jolla"],["USA", "CA", "La Mesa"],
218
+ ["USA", "CA", "Lakewood"],["USA", "CA", "Lemon Grove"],["USA", "CA", "Lincoln Acres"],["USA", "CA", "Long Beach"],
219
+ ["USA", "CA", "Los Angeles"],["USA", "CA", "Mill Valley"],["USA", "CA", "National City"],["USA", "CA", "Newport Beach"],
220
+ ["USA", "CA", "Novato"],["USA", "CA", "Oakland"],["USA", "CA", "Palo Alto"],["USA", "CA", "Pomona"],
221
+ ["USA", "CA", "Redwood City"],["USA", "CA", "Richmond"],["USA", "CA", "San Carlos"],["USA", "CA", "San Diego"],
222
+ ["USA", "CA", "San Francisco"],["USA", "CA", "San Gabriel"],["USA", "CA", "San Jose"],["USA", "CA", "Santa Cruz"],
223
+ ["USA", "CA", "Santa Monica"],["USA", "CA", "Spring Valley"],["USA", "CA", "Torrance"],["USA", "CA", "West Covina"],
224
+ ["USA", "CA", "Woodland Hills"],
225
+ ["USA", "OR", "Albany"],["USA", "OR", "Beaverton"],["USA", "OR", "Corvallis"],["USA", "OR", "Lake Oswego"],
226
+ ["USA", "OR", "Lebanon"],["USA", "OR", "Milwaukie"],["USA", "OR", "Oregon City"],["USA", "OR", "Portland"],
227
+ ["USA", "OR", "Salem"],["USA", "OR", "W. Linn"],["USA", "OR", "Woodburn"],
228
+ ["USA", "WA", "Anacortes"],["USA", "WA", "Ballard"],["USA", "WA", "Bellingham"],["USA", "WA", "Bremerton"],
229
+ ["USA", "WA", "Burien"],["USA", "WA", "Edmonds"],["USA", "WA", "Everett"],["USA", "WA", "Issaquah"],
230
+ ["USA", "WA", "Kirkland"],["USA", "WA", "Lynnwood"],["USA", "WA", "Marysville"],["USA", "WA", "Olympia"],
231
+ ["USA", "WA", "Port Orchard"],["USA", "WA", "Puyallup"],["USA", "WA", "Redmond"],["USA", "WA", "Renton"],
232
+ ["USA", "WA", "Seattle"],["USA", "WA", "Sedro Woolley"],["USA", "WA", "Spokane"],["USA", "WA", "Tacoma"],
233
+ ["USA", "WA", "Walla Walla"],["USA", "WA", "Yakima"]
234
+ ].each do |country, state, city|
235
+ i += 1
236
+ Customer.create!(
237
+ :country => country,
238
+ :state_province => state,
239
+ :city => city,
240
+ :fname => "First#{i}",
241
+ :lname => "Last#{i}",
242
+ :fullname => "First#{i} Last#{i}",
243
+ :gender => i % 2 == 0 ? "M" : "F",
244
+ :birthdate => Date.new(1970, 1, 1) + i,
245
+ :promotion_id => promotions[i % 10].id,
246
+ :related_fullname => "First#{i} Last#{i}",
247
+ :description => 100.times.map{"1234567890"}.join("\n")
248
+ )
249
+ end
250
+ # Create additional customer with large ID
251
+ attributes = {
252
+ :id => 10_000_000_000,
253
+ :country => "USA",
254
+ :state_province => "CA",
255
+ :city => "Rīga", # For testing UTF-8 characters
256
+ :fname => "Big",
257
+ :lname => "Number",
258
+ :fullname => "Big Number",
259
+ :gender => "M",
260
+ :promotion_id => promotions.first.id,
261
+ :related_fullname => "Big Number"
262
+ }
263
+ case MONDRIAN_DRIVER
264
+ when 'sqlserver'
265
+ Customer.connection.execute "SET IDENTITY_INSERT customers ON"
266
+ Customer.create!(attributes)
267
+ Customer.connection.execute "SET IDENTITY_INSERT customers OFF"
175
268
  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
- )
221
- end
269
+ Customer.create!(attributes)
222
270
  end
223
271
  end
224
272
 
225
273
  task :create_sales_data => :define_models do
226
274
  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
- )
275
+ Sales.delete_all
276
+ count = 100
277
+ products = Product.order("id").to_a[0...count]
278
+ times = TimeDimension.order("id").to_a[0...count]
279
+ customers = Customer.order("id").to_a[0...count]
280
+ promotions = Promotion.order("id").to_a[0...count]
281
+ count.times do |i|
282
+ Sales.create!(
283
+ :product_id => products[i].id,
284
+ :time_id => times[i].id,
285
+ :customer_id => customers[i].id,
286
+ :promotion_id => promotions[i % 10].id,
287
+ :store_sales => BigDecimal("2#{i}.12"),
288
+ :store_cost => BigDecimal("1#{i}.1234"),
289
+ :unit_sales => i+1
290
+ )
291
+ end
292
+ end
293
+
294
+ task :create_warehouse_data => :define_models do
295
+ puts "==> Creating warehouse data"
296
+ Warehouse.delete_all
297
+ count = 100
298
+ products = Product.order("id").to_a[0...count]
299
+ times = TimeDimension.order("id").to_a[0...count]
300
+ count.times do |i|
301
+ Warehouse.create!(
302
+ :product_id => products[i].id,
303
+ :time_id => times[i].id,
304
+ :units_shipped => i+1,
305
+ :store_invoice => BigDecimal("1#{i}.1234")
306
+ )
307
+ end
308
+ end
309
+
310
+ export_data_dir = File.expand_path("spec/support/data")
311
+ table_names = %w(time product_classes products customers promotions sales warehouse)
312
+
313
+ desc "Export test data"
314
+ task :export_data => :create_data do
315
+ require "csv"
316
+ puts "==> Exporting data"
317
+ conn = ActiveRecord::Base.connection
318
+ table_names.each do |table_name|
319
+ column_names = conn.columns(table_name).map(&:name)
320
+ csv_content = conn.select_rows("SELECT #{column_names.join(',')} FROM #{table_name}").map do |row|
321
+ row.map do |value|
322
+ case value
323
+ when Time
324
+ value.utc.to_s(:db)
325
+ else
326
+ value
327
+ end
328
+ end.to_csv
329
+ end.join
330
+ file_path = File.expand_path("#{table_name}.csv", export_data_dir)
331
+ File.open(file_path, "w") do |file|
332
+ file.write column_names.to_csv
333
+ file.write csv_content
247
334
  end
248
335
  end
249
336
  end
250
337
 
338
+ task :import_data => :require_spec_helper do
339
+ puts "==> Importing data"
340
+ conn = ActiveRecord::Base.connection
341
+
342
+ case MONDRIAN_DRIVER
343
+ when 'vertica'
344
+ table_names.each do |table_name|
345
+ puts "==> Truncate #{table_name}"
346
+ conn.execute "TRUNCATE TABLE #{table_name}"
347
+ puts "==> Copy into #{table_name}"
348
+ file_path = "#{export_data_dir}/#{table_name}.csv"
349
+ columns_string = File.open(file_path) { |f| f.gets }.chomp
350
+ count = conn.execute "COPY #{table_name}(#{columns_string}) FROM LOCAL '#{file_path}' " \
351
+ "PARSER public.fcsvparser(header='true') ABORT ON ERROR REJECTMAX 0"
352
+ puts "==> Loaded #{count} records"
353
+ end
354
+
355
+ when 'snowflake'
356
+ conn.execute <<-SQL
357
+ CREATE OR REPLACE FILE FORMAT csv
358
+ TYPE = 'CSV' COMPRESSION = 'AUTO' FIELD_DELIMITER = ',' RECORD_DELIMITER = '\\n' SKIP_HEADER = 1
359
+ FIELD_OPTIONALLY_ENCLOSED_BY = '\\042' TRIM_SPACE = FALSE ERROR_ON_COLUMN_COUNT_MISMATCH = TRUE ESCAPE = 'NONE'
360
+ ESCAPE_UNENCLOSED_FIELD = 'NONE' DATE_FORMAT = 'AUTO' TIMESTAMP_FORMAT = 'AUTO' NULL_IF = ('')
361
+ SQL
362
+ conn.execute "CREATE OR REPLACE STAGE csv_stage FILE_FORMAT = csv"
363
+ conn.execute "PUT file://#{export_data_dir}/*.csv @csv_stage AUTO_COMPRESS = TRUE"
364
+ table_names.each do |table_name|
365
+ puts "==> Truncate #{table_name}"
366
+ conn.execute "TRUNCATE TABLE #{table_name}"
367
+ puts "==> Copy into #{table_name}"
368
+ file_path = "#{export_data_dir}/#{table_name}.csv"
369
+ columns_string = File.open(file_path) { |f| f.gets }.chomp
370
+ count = conn.execute "COPY INTO #{table_name}(#{columns_string}) FROM @csv_stage/#{table_name}.csv.gz " \
371
+ "FILE_FORMAT = (FORMAT_NAME = csv)"
372
+ puts "==> Loaded #{count} records"
373
+ end
374
+
375
+ when 'clickhouse'
376
+ table_names.each do |table_name|
377
+ puts "==> Truncate #{table_name}"
378
+ conn.execute "TRUNCATE TABLE #{table_name}"
379
+ puts "==> Copy into #{table_name}"
380
+ file_path = "#{export_data_dir}/#{table_name}.csv"
381
+ columns_string = File.open(file_path) { |f| f.gets }.chomp
382
+ clickhouse_format_class = Java::com.clickhouse.data.ClickHouseFormat rescue Java::com.clickhouse.client.ClickHouseFormat
383
+ conn.jdbc_connection.createStatement.write.
384
+ query("INSERT INTO #{table_name}(#{columns_string})").
385
+ data(file_path).format(clickhouse_format_class::CSVWithNames).execute
386
+ count = conn.select_value("SELECT COUNT(*) FROM #{table_name}").to_i
387
+ puts "==> Loaded #{count} records"
388
+ end
389
+
390
+ when 'mariadb'
391
+ table_names.each do |table_name|
392
+ puts "==> Truncate #{table_name}"
393
+ conn.execute "TRUNCATE TABLE `#{table_name}`"
394
+ puts "==> Copy into #{table_name}"
395
+ file_path = "#{export_data_dir}/#{table_name}.csv"
396
+ columns_string = File.open(file_path) { |f| f.gets }.chomp
397
+ count = conn.execute "LOAD DATA LOCAL INFILE '#{file_path}' INTO TABLE `#{table_name}` CHARACTER SET UTF8 " \
398
+ "FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES (#{columns_string})"
399
+ puts "==> Loaded #{count} records"
400
+ end
401
+
402
+ end
403
+ end
404
+
251
405
  end
252
406
 
253
407
  namespace :spec do
254
- %w(mysql jdbc_mysql postgresql oracle luciddb mssql sqlserver).each do |driver|
408
+ %w(mysql jdbc_mysql postgresql oracle sqlserver vertica snowflake clickhouse mariadb).each do |driver|
255
409
  desc "Run specs with #{driver} driver"
256
410
  task driver do
257
411
  ENV['MONDRIAN_DRIVER'] = driver
@@ -262,7 +416,7 @@ namespace :spec do
262
416
 
263
417
  desc "Run specs with all primary database drivers"
264
418
  task :all do
265
- %w(mysql jdbc_mysql postgresql oracle mssql).each do |driver|
419
+ %w(mysql jdbc_mysql postgresql oracle sqlserver).each do |driver|
266
420
  Rake::Task["spec:#{driver}"].invoke
267
421
  end
268
422
  end