mondrian-olap 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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