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.
- checksums.yaml +4 -4
- data/Changelog.md +33 -0
- data/LICENSE-Mondrian.txt +87 -0
- data/LICENSE.txt +1 -1
- data/README.md +4 -4
- data/VERSION +1 -1
- data/lib/mondrian/jars/guava-17.0.jar +0 -0
- data/lib/mondrian/jars/log4j-api-2.17.1.jar +0 -0
- data/lib/mondrian/jars/log4j-core-2.17.1.jar +0 -0
- data/lib/mondrian/jars/log4j2-config.jar +0 -0
- data/lib/mondrian/jars/mondrian-9.3.0.0.jar +0 -0
- data/lib/mondrian/olap/connection.rb +126 -73
- data/lib/mondrian/olap/cube.rb +46 -4
- data/lib/mondrian/olap/error.rb +10 -2
- data/lib/mondrian/olap/query.rb +1 -0
- data/lib/mondrian/olap/result.rb +132 -56
- data/lib/mondrian/olap/schema.rb +9 -3
- data/lib/mondrian/olap/schema_element.rb +6 -3
- data/lib/mondrian/olap/schema_udf.rb +8 -82
- data/lib/mondrian/olap.rb +11 -7
- data/spec/connection_role_spec.rb +4 -1
- data/spec/connection_spec.rb +38 -5
- data/spec/cube_cache_control_spec.rb +7 -17
- data/spec/cube_spec.rb +36 -2
- data/spec/fixtures/MondrianTest.xml +40 -6
- data/spec/fixtures/MondrianTestOracle.xml +40 -6
- data/spec/mondrian_spec.rb +203 -1
- data/spec/query_spec.rb +94 -25
- data/spec/rake_tasks.rb +319 -165
- data/spec/schema_definition_spec.rb +8 -241
- data/spec/spec_helper.rb +330 -112
- data/spec/support/data/customers.csv +10902 -0
- data/spec/support/data/product_classes.csv +101 -0
- data/spec/support/data/products.csv +101 -0
- data/spec/support/data/promotions.csv +11 -0
- data/spec/support/data/sales.csv +101 -0
- data/spec/support/data/time.csv +731 -0
- data/spec/support/data/warehouse.csv +101 -0
- data/spec/support/matchers/be_like.rb +1 -0
- metadata +42 -83
- data/LICENSE-Mondrian.html +0 -259
- data/lib/mondrian/jars/log4j-1.2.17.jar +0 -0
- data/lib/mondrian/jars/log4j.properties +0 -3
- 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.
|
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
|
-
|
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.
|
58
|
-
t.decimal :
|
59
|
-
t.decimal :
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
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
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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.
|
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
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
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
|
419
|
+
%w(mysql jdbc_mysql postgresql oracle sqlserver).each do |driver|
|
266
420
|
Rake::Task["spec:#{driver}"].invoke
|
267
421
|
end
|
268
422
|
end
|