mondrian-olap 0.4.0-java
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.
- data/.rspec +2 -0
- data/Changelog.md +60 -0
- data/Gemfile +21 -0
- data/LICENSE-Mondrian.html +259 -0
- data/LICENSE.txt +22 -0
- data/README.md +302 -0
- data/RUNNING_TESTS.rdoc +66 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/mondrian-olap.rb +1 -0
- data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
- data/lib/mondrian/jars/commons-dbcp-1.2.1.jar +0 -0
- data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
- data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
- data/lib/mondrian/jars/commons-pool-1.2.jar +0 -0
- data/lib/mondrian/jars/commons-vfs-1.0.jar +0 -0
- data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
- data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
- data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
- data/lib/mondrian/jars/javacup.jar +0 -0
- data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
- data/lib/mondrian/jars/log4j.properties +5 -0
- data/lib/mondrian/jars/mondrian.jar +0 -0
- data/lib/mondrian/jars/olap4j.jar +0 -0
- data/lib/mondrian/olap.rb +17 -0
- data/lib/mondrian/olap/connection.rb +201 -0
- data/lib/mondrian/olap/cube.rb +297 -0
- data/lib/mondrian/olap/error.rb +57 -0
- data/lib/mondrian/olap/query.rb +342 -0
- data/lib/mondrian/olap/result.rb +264 -0
- data/lib/mondrian/olap/schema.rb +378 -0
- data/lib/mondrian/olap/schema_element.rb +153 -0
- data/lib/mondrian/olap/schema_udf.rb +282 -0
- data/mondrian-olap.gemspec +128 -0
- data/spec/connection_role_spec.rb +130 -0
- data/spec/connection_spec.rb +72 -0
- data/spec/cube_spec.rb +318 -0
- data/spec/fixtures/MondrianTest.xml +134 -0
- data/spec/fixtures/MondrianTestOracle.xml +134 -0
- data/spec/mondrian_spec.rb +53 -0
- data/spec/query_spec.rb +807 -0
- data/spec/rake_tasks.rb +260 -0
- data/spec/schema_definition_spec.rb +1249 -0
- data/spec/spec_helper.rb +134 -0
- data/spec/support/matchers/be_like.rb +24 -0
- metadata +278 -0
data/spec/rake_tasks.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
namespace :db do
|
2
|
+
task :require_spec_helper do
|
3
|
+
require File.expand_path("../spec_helper", __FILE__)
|
4
|
+
end
|
5
|
+
|
6
|
+
desc "Create test database tables"
|
7
|
+
task :create_tables => :require_spec_helper do
|
8
|
+
puts "==> Creating tables for test data"
|
9
|
+
ActiveRecord::Schema.define do
|
10
|
+
|
11
|
+
create_table :time, :force => true do |t|
|
12
|
+
t.datetime :the_date
|
13
|
+
t.string :the_day, :limit => 30
|
14
|
+
t.string :the_month, :limit => 30
|
15
|
+
t.integer :the_year
|
16
|
+
t.integer :day_of_month
|
17
|
+
t.integer :week_of_year
|
18
|
+
t.integer :month_of_year
|
19
|
+
t.string :quarter, :limit => 30
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :products, :force => true do |t|
|
23
|
+
t.integer :product_class_id
|
24
|
+
t.string :brand_name, :limit => 60
|
25
|
+
t.string :product_name, :limit => 60
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table :product_classes, :force => true do |t|
|
29
|
+
t.string :product_subcategory, :limit => 30
|
30
|
+
t.string :product_category, :limit => 30
|
31
|
+
t.string :product_department, :limit => 30
|
32
|
+
t.string :product_family, :limit => 30
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table :customers, :force => true do |t|
|
36
|
+
t.string :country, :limit => 30
|
37
|
+
t.string :state_province, :limit => 30
|
38
|
+
t.string :city, :limit => 30
|
39
|
+
t.string :fname, :limit => 30
|
40
|
+
t.string :lname, :limit => 30
|
41
|
+
t.string :fullname, :limit => 60
|
42
|
+
t.string :gender, :limit => 30
|
43
|
+
end
|
44
|
+
|
45
|
+
create_table :sales, :force => true, :id => false do |t|
|
46
|
+
t.integer :product_id
|
47
|
+
t.integer :time_id
|
48
|
+
t.integer :customer_id
|
49
|
+
t.decimal :store_sales, :precision => 10, :scale => 4
|
50
|
+
t.decimal :store_cost, :precision => 10, :scale => 4
|
51
|
+
t.decimal :unit_sales, :precision => 10, :scale => 4
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
task :setup_luciddb => :require_spec_helper do
|
57
|
+
# create link to mysql database to import tables
|
58
|
+
# see description at http://pub.eigenbase.org/wiki/LucidDbCreateForeignServer
|
59
|
+
if MONDRIAN_DRIVER == 'luciddb'
|
60
|
+
conn = ActiveRecord::Base.connection
|
61
|
+
conn.execute "drop schema mondrian_test_source cascade" rescue nil
|
62
|
+
conn.execute "drop server mondrian_test_source" rescue nil
|
63
|
+
conn.execute "create schema mondrian_test_source"
|
64
|
+
conn.execute <<-SQL
|
65
|
+
create server mondrian_test_source
|
66
|
+
foreign data wrapper sys_jdbc
|
67
|
+
options(
|
68
|
+
driver_class 'com.mysql.jdbc.Driver',
|
69
|
+
url 'jdbc:mysql://localhost/mondrian_test?characterEncoding=utf-8&useCursorFetch=true',
|
70
|
+
user_name 'mondrian_test',
|
71
|
+
password 'mondrian_test',
|
72
|
+
login_timeout '10',
|
73
|
+
fetch_size '1000',
|
74
|
+
validation_query 'select 1',
|
75
|
+
schema_name 'MONDRIAN_TEST',
|
76
|
+
table_types 'TABLE')
|
77
|
+
SQL
|
78
|
+
conn.execute "import foreign schema mondrian_test from server mondrian_test_source into mondrian_test_source"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
task :define_models => :require_spec_helper do
|
83
|
+
unless MONDRIAN_DRIVER == 'luciddb'
|
84
|
+
class TimeDimension < ActiveRecord::Base
|
85
|
+
self.table_name = "time"
|
86
|
+
validates_presence_of :the_date
|
87
|
+
before_create do
|
88
|
+
self.the_day = the_date.strftime("%A")
|
89
|
+
self.the_month = the_date.strftime("%B")
|
90
|
+
self.the_year = the_date.strftime("%Y").to_i
|
91
|
+
self.day_of_month = the_date.strftime("%d").to_i
|
92
|
+
self.week_of_year = the_date.strftime("%W").to_i
|
93
|
+
self.month_of_year = the_date.strftime("%m").to_i
|
94
|
+
self.quarter = "Q#{(month_of_year-1)/3+1}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
class Product < ActiveRecord::Base
|
98
|
+
belongs_to :product_class
|
99
|
+
end
|
100
|
+
class ProductClass < ActiveRecord::Base
|
101
|
+
end
|
102
|
+
class Customer < ActiveRecord::Base
|
103
|
+
end
|
104
|
+
class Sales < ActiveRecord::Base
|
105
|
+
self.table_name = "sales"
|
106
|
+
belongs_to :time_by_day
|
107
|
+
belongs_to :product
|
108
|
+
belongs_to :customer
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
desc "Create test data"
|
114
|
+
task :create_data => [:create_tables, :setup_luciddb, :create_time_data, :create_product_data, :create_customer_data, :create_sales_data]
|
115
|
+
|
116
|
+
task :create_time_data => :define_models do
|
117
|
+
puts "==> Creating time dimension"
|
118
|
+
if MONDRIAN_DRIVER == 'luciddb'
|
119
|
+
ActiveRecord::Base.connection.execute 'truncate table "TIME"'
|
120
|
+
ActiveRecord::Base.connection.execute 'insert into "TIME" select * from mondrian_test_source."time"'
|
121
|
+
ActiveRecord::Base.connection.execute 'analyze table "TIME" compute statistics for all columns'
|
122
|
+
else
|
123
|
+
TimeDimension.delete_all
|
124
|
+
start_time = Time.local(2010,1,1)
|
125
|
+
(2*365).times do |i|
|
126
|
+
TimeDimension.create!(:the_date => start_time + i.day)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
task :create_product_data => :define_models do
|
132
|
+
puts "==> Creating product data"
|
133
|
+
if MONDRIAN_DRIVER == 'luciddb'
|
134
|
+
ActiveRecord::Base.connection.execute 'truncate table product_classes'
|
135
|
+
ActiveRecord::Base.connection.execute 'truncate table products'
|
136
|
+
ActiveRecord::Base.connection.execute 'insert into product_classes select * from mondrian_test_source."product_classes"'
|
137
|
+
ActiveRecord::Base.connection.execute 'insert into products select * from mondrian_test_source."products"'
|
138
|
+
ActiveRecord::Base.connection.execute 'analyze table product_classes compute statistics for all columns'
|
139
|
+
ActiveRecord::Base.connection.execute 'analyze table products compute statistics for all columns'
|
140
|
+
else
|
141
|
+
Product.delete_all
|
142
|
+
ProductClass.delete_all
|
143
|
+
families = ["Drink", "Food", "Non-Consumable"]
|
144
|
+
(1..100).each do |i|
|
145
|
+
product_class = ProductClass.create!(
|
146
|
+
:product_family => families[i % 3],
|
147
|
+
:product_department => "Department #{i}",
|
148
|
+
:product_category => "Category #{i}",
|
149
|
+
:product_subcategory => "Subcategory #{i}"
|
150
|
+
)
|
151
|
+
Product.create!(
|
152
|
+
# LucidDB is not returning inserted ID therefore doing it hard way
|
153
|
+
:product_class_id => ProductClass.find_all_by_product_category("Category #{i}").first.id,
|
154
|
+
:brand_name => "Brand #{i}",
|
155
|
+
:product_name => "Product #{i}"
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
task :create_customer_data => :define_models do
|
162
|
+
puts "==> Creating customer data"
|
163
|
+
if MONDRIAN_DRIVER == 'luciddb'
|
164
|
+
ActiveRecord::Base.connection.execute 'truncate table customers'
|
165
|
+
ActiveRecord::Base.connection.execute 'insert into customers select * from mondrian_test_source."customers"'
|
166
|
+
ActiveRecord::Base.connection.execute 'analyze table customers compute statistics for all columns'
|
167
|
+
else
|
168
|
+
Customer.delete_all
|
169
|
+
i = 0
|
170
|
+
[
|
171
|
+
["Canada", "BC", "Burnaby"],["Canada", "BC", "Cliffside"],["Canada", "BC", "Haney"],["Canada", "BC", "Ladner"],
|
172
|
+
["Canada", "BC", "Langford"],["Canada", "BC", "Langley"],["Canada", "BC", "Metchosin"],["Canada", "BC", "N. Vancouver"],
|
173
|
+
["Canada", "BC", "Newton"],["Canada", "BC", "Oak Bay"],["Canada", "BC", "Port Hammond"],["Canada", "BC", "Richmond"],
|
174
|
+
["Canada", "BC", "Royal Oak"],["Canada", "BC", "Shawnee"],["Canada", "BC", "Sooke"],["Canada", "BC", "Vancouver"],
|
175
|
+
["Canada", "BC", "Victoria"],["Canada", "BC", "Westminster"],
|
176
|
+
["Mexico", "DF", "San Andres"],["Mexico", "DF", "Santa Anita"],["Mexico", "DF", "Santa Fe"],["Mexico", "DF", "Tixapan"],
|
177
|
+
["Mexico", "Guerrero", "Acapulco"],["Mexico", "Jalisco", "Guadalajara"],["Mexico", "Mexico", "Mexico City"],
|
178
|
+
["Mexico", "Oaxaca", "Tlaxiaco"],["Mexico", "Sinaloa", "La Cruz"],["Mexico", "Veracruz", "Orizaba"],
|
179
|
+
["Mexico", "Yucatan", "Merida"],["Mexico", "Zacatecas", "Camacho"],["Mexico", "Zacatecas", "Hidalgo"],
|
180
|
+
["USA", "CA", "Altadena"],["USA", "CA", "Arcadia"],["USA", "CA", "Bellflower"],["USA", "CA", "Berkeley"],
|
181
|
+
["USA", "CA", "Beverly Hills"],["USA", "CA", "Burbank"],["USA", "CA", "Burlingame"],["USA", "CA", "Chula Vista"],
|
182
|
+
["USA", "CA", "Colma"],["USA", "CA", "Concord"],["USA", "CA", "Coronado"],["USA", "CA", "Daly City"],
|
183
|
+
["USA", "CA", "Downey"],["USA", "CA", "El Cajon"],["USA", "CA", "Fremont"],["USA", "CA", "Glendale"],
|
184
|
+
["USA", "CA", "Grossmont"],["USA", "CA", "Imperial Beach"],["USA", "CA", "La Jolla"],["USA", "CA", "La Mesa"],
|
185
|
+
["USA", "CA", "Lakewood"],["USA", "CA", "Lemon Grove"],["USA", "CA", "Lincoln Acres"],["USA", "CA", "Long Beach"],
|
186
|
+
["USA", "CA", "Los Angeles"],["USA", "CA", "Mill Valley"],["USA", "CA", "National City"],["USA", "CA", "Newport Beach"],
|
187
|
+
["USA", "CA", "Novato"],["USA", "CA", "Oakland"],["USA", "CA", "Palo Alto"],["USA", "CA", "Pomona"],
|
188
|
+
["USA", "CA", "Redwood City"],["USA", "CA", "Richmond"],["USA", "CA", "San Carlos"],["USA", "CA", "San Diego"],
|
189
|
+
["USA", "CA", "San Francisco"],["USA", "CA", "San Gabriel"],["USA", "CA", "San Jose"],["USA", "CA", "Santa Cruz"],
|
190
|
+
["USA", "CA", "Santa Monica"],["USA", "CA", "Spring Valley"],["USA", "CA", "Torrance"],["USA", "CA", "West Covina"],
|
191
|
+
["USA", "CA", "Woodland Hills"],
|
192
|
+
["USA", "OR", "Albany"],["USA", "OR", "Beaverton"],["USA", "OR", "Corvallis"],["USA", "OR", "Lake Oswego"],
|
193
|
+
["USA", "OR", "Lebanon"],["USA", "OR", "Milwaukie"],["USA", "OR", "Oregon City"],["USA", "OR", "Portland"],
|
194
|
+
["USA", "OR", "Salem"],["USA", "OR", "W. Linn"],["USA", "OR", "Woodburn"],
|
195
|
+
["USA", "WA", "Anacortes"],["USA", "WA", "Ballard"],["USA", "WA", "Bellingham"],["USA", "WA", "Bremerton"],
|
196
|
+
["USA", "WA", "Burien"],["USA", "WA", "Edmonds"],["USA", "WA", "Everett"],["USA", "WA", "Issaquah"],
|
197
|
+
["USA", "WA", "Kirkland"],["USA", "WA", "Lynnwood"],["USA", "WA", "Marysville"],["USA", "WA", "Olympia"],
|
198
|
+
["USA", "WA", "Port Orchard"],["USA", "WA", "Puyallup"],["USA", "WA", "Redmond"],["USA", "WA", "Renton"],
|
199
|
+
["USA", "WA", "Seattle"],["USA", "WA", "Sedro Woolley"],["USA", "WA", "Spokane"],["USA", "WA", "Tacoma"],
|
200
|
+
["USA", "WA", "Walla Walla"],["USA", "WA", "Yakima"]
|
201
|
+
].each do |country, state, city|
|
202
|
+
i += 1
|
203
|
+
Customer.create!(
|
204
|
+
:country => country,
|
205
|
+
:state_province => state,
|
206
|
+
:city => city,
|
207
|
+
:fname => "First#{i}",
|
208
|
+
:lname => "Last#{i}",
|
209
|
+
:fullname => "First#{i} Last#{i}",
|
210
|
+
:gender => i % 2 == 0 ? "M" : "F"
|
211
|
+
)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
task :create_sales_data => :define_models do
|
217
|
+
puts "==> Creating sales data"
|
218
|
+
if MONDRIAN_DRIVER == 'luciddb'
|
219
|
+
ActiveRecord::Base.connection.execute 'truncate table sales'
|
220
|
+
ActiveRecord::Base.connection.execute 'insert into sales select * from mondrian_test_source."sales"'
|
221
|
+
ActiveRecord::Base.connection.execute 'analyze table sales compute statistics for all columns'
|
222
|
+
else
|
223
|
+
Sales.delete_all
|
224
|
+
count = 100
|
225
|
+
# LucidDB does not support LIMIT therefore select all and limit in Ruby
|
226
|
+
products = Product.order("id").all[0...count]
|
227
|
+
times = TimeDimension.order("id").all[0...count]
|
228
|
+
customers = Customer.order("id").all[0...count]
|
229
|
+
count.times do |i|
|
230
|
+
Sales.create!(
|
231
|
+
:product_id => products[i].id,
|
232
|
+
:time_id => times[i].id,
|
233
|
+
:customer_id => customers[i].id,
|
234
|
+
:store_sales => BigDecimal("2#{i}.12"),
|
235
|
+
:store_cost => BigDecimal("1#{i}.1234"),
|
236
|
+
:unit_sales => i+1
|
237
|
+
)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
namespace :spec do
|
245
|
+
%w(mysql postgresql oracle luciddb mssql sqlserver).each do |driver|
|
246
|
+
desc "Run specs with #{driver} driver"
|
247
|
+
task driver do
|
248
|
+
ENV['MONDRIAN_DRIVER'] = driver
|
249
|
+
Rake::Task['spec'].reenable
|
250
|
+
Rake::Task['spec'].invoke
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
desc "Run specs with all database drivers"
|
255
|
+
task :all do
|
256
|
+
%w(mysql postgresql oracle luciddb mssql sqlserver).each do |driver|
|
257
|
+
Rake::Task["spec:#{driver}"].invoke
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,1249 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "coffee-script"
|
3
|
+
|
4
|
+
describe "Schema definition" do
|
5
|
+
|
6
|
+
describe "elements" do
|
7
|
+
before(:each) do
|
8
|
+
@schema = Mondrian::OLAP::Schema.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "root element" do
|
12
|
+
it "should render to XML" do
|
13
|
+
@schema.to_xml.should be_like <<-XML
|
14
|
+
<?xml version="1.0"?>
|
15
|
+
<Schema/>
|
16
|
+
XML
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should render to XML with attributes" do
|
20
|
+
@schema.define('FoodMart') do
|
21
|
+
description 'Demo "FoodMart" schema'
|
22
|
+
end
|
23
|
+
@schema.to_xml.should be_like <<-XML
|
24
|
+
<?xml version="1.0"?>
|
25
|
+
<Schema description="Demo "FoodMart" schema" name="FoodMart"/>
|
26
|
+
XML
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should render to XML using class method" do
|
30
|
+
schema = Mondrian::OLAP::Schema.define('FoodMart')
|
31
|
+
schema.to_xml.should be_like <<-XML
|
32
|
+
<?xml version="1.0"?>
|
33
|
+
<Schema name="FoodMart"/>
|
34
|
+
XML
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "Cube" do
|
39
|
+
it "should render to XML" do
|
40
|
+
@schema.define do
|
41
|
+
cube 'Sales' do
|
42
|
+
default_measure 'Unit Sales'
|
43
|
+
description 'Sales cube'
|
44
|
+
cache false
|
45
|
+
enabled true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@schema.to_xml.should be_like <<-XML
|
49
|
+
<?xml version="1.0"?>
|
50
|
+
<Schema name="default">
|
51
|
+
<Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
|
52
|
+
</Schema>
|
53
|
+
XML
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should render to XML using options hash" do
|
57
|
+
@schema.define do
|
58
|
+
cube 'Sales', :default_measure => 'Unit Sales',
|
59
|
+
:description => 'Sales cube', :cache => false, :enabled => true
|
60
|
+
end
|
61
|
+
@schema.to_xml.should be_like <<-XML
|
62
|
+
<?xml version="1.0"?>
|
63
|
+
<Schema name="default">
|
64
|
+
<Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
|
65
|
+
</Schema>
|
66
|
+
XML
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "Table" do
|
71
|
+
it "should render to XML" do
|
72
|
+
@schema.define do
|
73
|
+
cube 'Sales' do
|
74
|
+
table 'sales_fact', :alias => 'sales'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@schema.to_xml.should be_like <<-XML
|
78
|
+
<?xml version="1.0"?>
|
79
|
+
<Schema name="default">
|
80
|
+
<Cube name="Sales">
|
81
|
+
<Table alias="sales" name="sales_fact"/>
|
82
|
+
</Cube>
|
83
|
+
</Schema>
|
84
|
+
XML
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should render table name in uppercase when using Oracle or LucidDB driver" do
|
88
|
+
@schema.define do
|
89
|
+
cube 'Sales' do
|
90
|
+
table 'sales_fact', :alias => 'sales', :schema => 'facts'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
%w(oracle luciddb).each do |driver|
|
94
|
+
@schema.to_xml(:driver => driver).should be_like <<-XML
|
95
|
+
<?xml version="1.0"?>
|
96
|
+
<Schema name="default">
|
97
|
+
<Cube name="Sales">
|
98
|
+
<Table alias="SALES" name="SALES_FACT" schema="FACTS"/>
|
99
|
+
</Cube>
|
100
|
+
</Schema>
|
101
|
+
XML
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should render table name in uppercase when :upcase_data_dictionary option is set to true" do
|
106
|
+
@schema.define :upcase_data_dictionary => true do
|
107
|
+
cube 'Sales' do
|
108
|
+
table 'sales_fact', :alias => 'sales', :schema => 'facts'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@schema.to_xml.should be_like <<-XML
|
112
|
+
<?xml version="1.0"?>
|
113
|
+
<Schema name="default">
|
114
|
+
<Cube name="Sales">
|
115
|
+
<Table alias="SALES" name="SALES_FACT" schema="FACTS"/>
|
116
|
+
</Cube>
|
117
|
+
</Schema>
|
118
|
+
XML
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should render table name in lowercase when using Oracle or LucidDB driver but with :upcase_data_dictionary set to false" do
|
122
|
+
@schema.define :upcase_data_dictionary => false do
|
123
|
+
cube 'Sales' do
|
124
|
+
table 'sales_fact', :alias => 'sales', :schema => 'facts'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
%w(oracle luciddb).each do |driver|
|
128
|
+
@schema.to_xml(:driver => driver).should be_like <<-XML
|
129
|
+
<?xml version="1.0"?>
|
130
|
+
<Schema name="default">
|
131
|
+
<Cube name="Sales">
|
132
|
+
<Table alias="sales" name="sales_fact" schema="facts"/>
|
133
|
+
</Cube>
|
134
|
+
</Schema>
|
135
|
+
XML
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should render table with where condition" do
|
140
|
+
@schema.define do
|
141
|
+
cube 'Sales' do
|
142
|
+
table 'sales_fact', :alias => 'sales' do
|
143
|
+
sql 'customer_id IS NOT NULL'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
@schema.to_xml.should be_like <<-XML
|
148
|
+
<?xml version="1.0"?>
|
149
|
+
<Schema name="default">
|
150
|
+
<Cube name="Sales">
|
151
|
+
<Table alias="sales" name="sales_fact">
|
152
|
+
<SQL>customer_id IS NOT NULL</SQL>
|
153
|
+
</Table>
|
154
|
+
</Cube>
|
155
|
+
</Schema>
|
156
|
+
XML
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "View" do
|
161
|
+
it "should render to XML" do
|
162
|
+
@schema.define do
|
163
|
+
cube 'Sales' do
|
164
|
+
view :alias => 'sales' do
|
165
|
+
sql 'select * from sales_fact'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
@schema.to_xml.should be_like <<-XML
|
170
|
+
<?xml version="1.0"?>
|
171
|
+
<Schema name="default">
|
172
|
+
<Cube name="Sales">
|
173
|
+
<View alias="sales">
|
174
|
+
<SQL>select * from sales_fact</SQL>
|
175
|
+
</View>
|
176
|
+
</Cube>
|
177
|
+
</Schema>
|
178
|
+
XML
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "Dimension" do
|
183
|
+
it "should render to XML" do
|
184
|
+
@schema.define do
|
185
|
+
cube 'Sales' do
|
186
|
+
dimension 'Gender' do
|
187
|
+
foreign_key 'customer_id'
|
188
|
+
hierarchy do
|
189
|
+
has_all true
|
190
|
+
all_member_name 'All Genders'
|
191
|
+
primary_key 'customer_id'
|
192
|
+
table 'customer'
|
193
|
+
level 'Gender', :column => 'gender', :unique_members => true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
@schema.to_xml.should be_like <<-XML
|
199
|
+
<?xml version="1.0"?>
|
200
|
+
<Schema name="default">
|
201
|
+
<Cube name="Sales">
|
202
|
+
<Dimension foreignKey="customer_id" name="Gender">
|
203
|
+
<Hierarchy allMemberName="All Genders" hasAll="true" primaryKey="customer_id">
|
204
|
+
<Table name="customer"/>
|
205
|
+
<Level column="gender" name="Gender" uniqueMembers="true"/>
|
206
|
+
</Hierarchy>
|
207
|
+
</Dimension>
|
208
|
+
</Cube>
|
209
|
+
</Schema>
|
210
|
+
XML
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should render time dimension" do
|
214
|
+
@schema.define do
|
215
|
+
cube 'Sales' do
|
216
|
+
dimension 'Time' do
|
217
|
+
foreign_key 'time_id'
|
218
|
+
hierarchy do
|
219
|
+
has_all false
|
220
|
+
primary_key 'time_id'
|
221
|
+
table 'time_by_day'
|
222
|
+
level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
|
223
|
+
level 'Quarter', :column => 'quarter', :unique_members => false
|
224
|
+
level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
@schema.to_xml.should be_like <<-XML
|
230
|
+
<?xml version="1.0"?>
|
231
|
+
<Schema name="default">
|
232
|
+
<Cube name="Sales">
|
233
|
+
<Dimension foreignKey="time_id" name="Time">
|
234
|
+
<Hierarchy hasAll="false" primaryKey="time_id">
|
235
|
+
<Table name="time_by_day"/>
|
236
|
+
<Level column="the_year" name="Year" type="Numeric" uniqueMembers="true"/>
|
237
|
+
<Level column="quarter" name="Quarter" uniqueMembers="false"/>
|
238
|
+
<Level column="month_of_year" name="Month" type="Numeric" uniqueMembers="false"/>
|
239
|
+
</Hierarchy>
|
240
|
+
</Dimension>
|
241
|
+
</Cube>
|
242
|
+
</Schema>
|
243
|
+
XML
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should render dimension hierarchy with join" do
|
247
|
+
@schema.define do
|
248
|
+
cube 'Sales' do
|
249
|
+
dimension 'Products', :foreign_key => 'product_id' do
|
250
|
+
hierarchy :has_all => true, :all_member_name => 'All Products',
|
251
|
+
:primary_key => 'product_id', :primary_key_table => 'product' do
|
252
|
+
join :left_key => 'product_class_id', :right_key => 'product_class_id' do
|
253
|
+
table 'product'
|
254
|
+
table 'product_class'
|
255
|
+
end
|
256
|
+
level 'Product Family', :table => 'product_class', :column => 'product_family', :unique_members => true
|
257
|
+
level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
|
258
|
+
level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
@schema.to_xml.should be_like <<-XML
|
264
|
+
<?xml version="1.0"?>
|
265
|
+
<Schema name="default">
|
266
|
+
<Cube name="Sales">
|
267
|
+
<Dimension foreignKey="product_id" name="Products">
|
268
|
+
<Hierarchy allMemberName="All Products" hasAll="true" primaryKey="product_id" primaryKeyTable="product">
|
269
|
+
<Join leftKey="product_class_id" rightKey="product_class_id">
|
270
|
+
<Table name="product"/>
|
271
|
+
<Table name="product_class"/>
|
272
|
+
</Join>
|
273
|
+
<Level column="product_family" name="Product Family" table="product_class" uniqueMembers="true"/>
|
274
|
+
<Level column="brand_name" name="Brand Name" table="product" uniqueMembers="false"/>
|
275
|
+
<Level column="product_name" name="Product Name" table="product" uniqueMembers="true"/>
|
276
|
+
</Hierarchy>
|
277
|
+
</Dimension>
|
278
|
+
</Cube>
|
279
|
+
</Schema>
|
280
|
+
XML
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should render table and column names in uppercase when using Oracle driver" do
|
284
|
+
@schema.define do
|
285
|
+
cube 'Sales' do
|
286
|
+
dimension 'Products', :foreign_key => 'product_id' do
|
287
|
+
hierarchy :has_all => true, :all_member_name => 'All Products',
|
288
|
+
:primary_key => 'product_id', :primary_key_table => 'product' do
|
289
|
+
join :left_key => 'product_class_id', :right_key => 'product_class_id' do
|
290
|
+
table 'product'
|
291
|
+
table 'product_class'
|
292
|
+
end
|
293
|
+
level 'Product Family', :table => 'product_class', :column => 'product_family', :unique_members => true
|
294
|
+
level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
|
295
|
+
level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
@schema.to_xml(:driver => 'oracle').should be_like <<-XML
|
301
|
+
<?xml version="1.0"?>
|
302
|
+
<Schema name="default">
|
303
|
+
<Cube name="Sales">
|
304
|
+
<Dimension foreignKey="PRODUCT_ID" name="Products">
|
305
|
+
<Hierarchy allMemberName="All Products" hasAll="true" primaryKey="PRODUCT_ID" primaryKeyTable="PRODUCT">
|
306
|
+
<Join leftKey="PRODUCT_CLASS_ID" rightKey="PRODUCT_CLASS_ID">
|
307
|
+
<Table name="PRODUCT"/>
|
308
|
+
<Table name="PRODUCT_CLASS"/>
|
309
|
+
</Join>
|
310
|
+
<Level column="PRODUCT_FAMILY" name="Product Family" table="PRODUCT_CLASS" uniqueMembers="true"/>
|
311
|
+
<Level column="BRAND_NAME" name="Brand Name" table="PRODUCT" uniqueMembers="false"/>
|
312
|
+
<Level column="PRODUCT_NAME" name="Product Name" table="PRODUCT" uniqueMembers="true"/>
|
313
|
+
</Hierarchy>
|
314
|
+
</Dimension>
|
315
|
+
</Cube>
|
316
|
+
</Schema>
|
317
|
+
XML
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should render dimension hierarchy with nested joins" do
|
321
|
+
@schema.define do
|
322
|
+
cube 'Sales' do
|
323
|
+
dimension 'Products', :foreign_key => 'product_id' do
|
324
|
+
hierarchy :has_all => true, :all_member_name => 'All Products',
|
325
|
+
:primary_key => 'product_id', :primary_key_table => 'product' do
|
326
|
+
join :left_key => 'product_class_id', :right_alias => 'product_class', :right_key => 'product_class_id' do
|
327
|
+
table 'product'
|
328
|
+
join :left_key => 'product_type_id', :right_key => 'product_type_id' do
|
329
|
+
table 'product_class'
|
330
|
+
table 'product_type'
|
331
|
+
end
|
332
|
+
end
|
333
|
+
level 'Product Family', :table => 'product_type', :column => 'product_family', :unique_members => true
|
334
|
+
level 'Product Category', :table => 'product_class', :column => 'product_category', :unique_members => false
|
335
|
+
level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
|
336
|
+
level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
@schema.to_xml.should be_like <<-XML
|
342
|
+
<?xml version="1.0"?>
|
343
|
+
<Schema name="default">
|
344
|
+
<Cube name="Sales">
|
345
|
+
<Dimension foreignKey="product_id" name="Products">
|
346
|
+
<Hierarchy allMemberName="All Products" hasAll="true" primaryKey="product_id" primaryKeyTable="product">
|
347
|
+
<Join leftKey="product_class_id" rightAlias="product_class" rightKey="product_class_id">
|
348
|
+
<Table name="product"/>
|
349
|
+
<Join leftKey="product_type_id" rightKey="product_type_id">
|
350
|
+
<Table name="product_class"/>
|
351
|
+
<Table name="product_type"/>
|
352
|
+
</Join>
|
353
|
+
</Join>
|
354
|
+
<Level column="product_family" name="Product Family" table="product_type" uniqueMembers="true"/>
|
355
|
+
<Level column="product_category" name="Product Category" table="product_class" uniqueMembers="false"/>
|
356
|
+
<Level column="brand_name" name="Brand Name" table="product" uniqueMembers="false"/>
|
357
|
+
<Level column="product_name" name="Product Name" table="product" uniqueMembers="true"/>
|
358
|
+
</Hierarchy>
|
359
|
+
</Dimension>
|
360
|
+
</Cube>
|
361
|
+
</Schema>
|
362
|
+
XML
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
describe "Measure" do
|
368
|
+
it "should render XML" do
|
369
|
+
@schema.define do
|
370
|
+
cube 'Sales' do
|
371
|
+
measure 'Unit Sales' do
|
372
|
+
column 'unit_sales'
|
373
|
+
aggregator 'sum'
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
@schema.to_xml.should be_like <<-XML
|
378
|
+
<?xml version="1.0"?>
|
379
|
+
<Schema name="default">
|
380
|
+
<Cube name="Sales">
|
381
|
+
<Measure aggregator="sum" column="unit_sales" name="Unit Sales"/>
|
382
|
+
</Cube>
|
383
|
+
</Schema>
|
384
|
+
XML
|
385
|
+
end
|
386
|
+
|
387
|
+
it "should render column name in uppercase when using Oracle driver" do
|
388
|
+
@schema.define do
|
389
|
+
cube 'Sales' do
|
390
|
+
measure 'Unit Sales' do
|
391
|
+
column 'unit_sales'
|
392
|
+
aggregator 'sum'
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
@schema.to_xml(:driver => 'oracle').should be_like <<-XML
|
397
|
+
<?xml version="1.0"?>
|
398
|
+
<Schema name="default">
|
399
|
+
<Cube name="Sales">
|
400
|
+
<Measure aggregator="sum" column="UNIT_SALES" name="Unit Sales"/>
|
401
|
+
</Cube>
|
402
|
+
</Schema>
|
403
|
+
XML
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should render with measure expression" do
|
407
|
+
@schema.define do
|
408
|
+
cube 'Sales' do
|
409
|
+
measure 'Double Unit Sales', :aggregator => 'sum' do
|
410
|
+
measure_expression do
|
411
|
+
sql 'unit_sales * 2'
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
@schema.to_xml.should be_like <<-XML
|
417
|
+
<?xml version="1.0"?>
|
418
|
+
<Schema name="default">
|
419
|
+
<Cube name="Sales">
|
420
|
+
<Measure aggregator="sum" name="Double Unit Sales">
|
421
|
+
<MeasureExpression>
|
422
|
+
<SQL>unit_sales * 2</SQL>
|
423
|
+
</MeasureExpression>
|
424
|
+
</Measure>
|
425
|
+
</Cube>
|
426
|
+
</Schema>
|
427
|
+
XML
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
describe "Calculated Member" do
|
432
|
+
it "should render XML" do
|
433
|
+
@schema.define do
|
434
|
+
cube 'Sales' do
|
435
|
+
calculated_member 'Profit' do
|
436
|
+
dimension 'Measures'
|
437
|
+
formula '[Measures].[Store Sales] - [Measures].[Store Cost]'
|
438
|
+
format_string '#,##0.00'
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
@schema.to_xml.should be_like <<-XML
|
443
|
+
<?xml version="1.0"?>
|
444
|
+
<Schema name="default">
|
445
|
+
<Cube name="Sales">
|
446
|
+
<CalculatedMember dimension="Measures" formatString="#,##0.00" name="Profit">
|
447
|
+
<Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
|
448
|
+
</CalculatedMember>
|
449
|
+
</Cube>
|
450
|
+
</Schema>
|
451
|
+
XML
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
describe "Aggregates" do
|
456
|
+
it "should render named aggregate to XML" do
|
457
|
+
@schema.define do
|
458
|
+
cube 'Sales' do
|
459
|
+
table 'sales_fact_1997' do
|
460
|
+
agg_name 'agg_c_special_sales_fact_1997' do
|
461
|
+
agg_fact_count :column => 'fact_count'
|
462
|
+
agg_measure '[Measures].[Store Cost]', :column => 'store_cost_sum'
|
463
|
+
agg_measure '[Measures].[Store Sales]', :column => 'store_sales_sum'
|
464
|
+
agg_level '[Product].[Product Family]', :column => 'product_family'
|
465
|
+
agg_level '[Time].[Quarter]', :column => 'time_quarter'
|
466
|
+
agg_level '[Time].[Year]', :column => 'time_year'
|
467
|
+
agg_level '[Time].[Quarter]', :column => 'time_quarter'
|
468
|
+
agg_level '[Time].[Month]', :column => 'time_month'
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
@schema.to_xml.should be_like <<-XML
|
474
|
+
<?xml version="1.0"?>
|
475
|
+
<Schema name="default">
|
476
|
+
<Cube name="Sales">
|
477
|
+
<Table name="sales_fact_1997">
|
478
|
+
<AggName name="agg_c_special_sales_fact_1997">
|
479
|
+
<AggFactCount column="fact_count"/>
|
480
|
+
<AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
|
481
|
+
<AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
|
482
|
+
<AggLevel column="product_family" name="[Product].[Product Family]"/>
|
483
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
484
|
+
<AggLevel column="time_year" name="[Time].[Year]"/>
|
485
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
486
|
+
<AggLevel column="time_month" name="[Time].[Month]"/>
|
487
|
+
</AggName>
|
488
|
+
</Table>
|
489
|
+
</Cube>
|
490
|
+
</Schema>
|
491
|
+
XML
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should render aggregate pattern to XML" do
|
495
|
+
@schema.define do
|
496
|
+
cube 'Sales' do
|
497
|
+
table 'sales_fact_1997' do
|
498
|
+
agg_pattern :pattern => 'agg_.*_sales_fact_1997' do
|
499
|
+
agg_fact_count :column => 'fact_count'
|
500
|
+
agg_measure '[Measures].[Store Cost]', :column => 'store_cost_sum'
|
501
|
+
agg_measure '[Measures].[Store Sales]', :column => 'store_sales_sum'
|
502
|
+
agg_level '[Product].[Product Family]', :column => 'product_family'
|
503
|
+
agg_level '[Time].[Quarter]', :column => 'time_quarter'
|
504
|
+
agg_level '[Time].[Year]', :column => 'time_year'
|
505
|
+
agg_level '[Time].[Quarter]', :column => 'time_quarter'
|
506
|
+
agg_level '[Time].[Month]', :column => 'time_month'
|
507
|
+
agg_exclude 'agg_c_14_sales_fact_1997'
|
508
|
+
agg_exclude 'agg_lc_100_sales_fact_1997'
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
@schema.to_xml.should be_like <<-XML
|
514
|
+
<?xml version="1.0"?>
|
515
|
+
<Schema name="default">
|
516
|
+
<Cube name="Sales">
|
517
|
+
<Table name="sales_fact_1997">
|
518
|
+
<AggPattern pattern="agg_.*_sales_fact_1997">
|
519
|
+
<AggFactCount column="fact_count"/>
|
520
|
+
<AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
|
521
|
+
<AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
|
522
|
+
<AggLevel column="product_family" name="[Product].[Product Family]"/>
|
523
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
524
|
+
<AggLevel column="time_year" name="[Time].[Year]"/>
|
525
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
526
|
+
<AggLevel column="time_month" name="[Time].[Month]"/>
|
527
|
+
<AggExclude name="agg_c_14_sales_fact_1997"/>
|
528
|
+
<AggExclude name="agg_lc_100_sales_fact_1997"/>
|
529
|
+
</AggPattern>
|
530
|
+
</Table>
|
531
|
+
</Cube>
|
532
|
+
</Schema>
|
533
|
+
XML
|
534
|
+
end
|
535
|
+
|
536
|
+
it "should render embedded aggregate XML defintion to XML" do
|
537
|
+
@schema.define do
|
538
|
+
cube 'Sales' do
|
539
|
+
table 'sales_fact_1997' do
|
540
|
+
xml <<-XML
|
541
|
+
<AggName name="agg_c_special_sales_fact_1997">
|
542
|
+
<AggFactCount column="fact_count"/>
|
543
|
+
<AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
|
544
|
+
<AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
|
545
|
+
<AggLevel column="product_family" name="[Product].[Product Family]"/>
|
546
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
547
|
+
<AggLevel column="time_year" name="[Time].[Year]"/>
|
548
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
549
|
+
<AggLevel column="time_month" name="[Time].[Month]"/>
|
550
|
+
</AggName>
|
551
|
+
<AggPattern pattern="agg_.*_sales_fact_1997">
|
552
|
+
<AggFactCount column="fact_count"/>
|
553
|
+
<AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
|
554
|
+
<AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
|
555
|
+
<AggLevel column="product_family" name="[Product].[Product Family]"/>
|
556
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
557
|
+
<AggLevel column="time_year" name="[Time].[Year]"/>
|
558
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
559
|
+
<AggLevel column="time_month" name="[Time].[Month]"/>
|
560
|
+
<AggExclude name="agg_c_14_sales_fact_1997"/>
|
561
|
+
<AggExclude name="agg_lc_100_sales_fact_1997"/>
|
562
|
+
</AggPattern>
|
563
|
+
XML
|
564
|
+
end
|
565
|
+
end
|
566
|
+
end
|
567
|
+
@schema.to_xml.should be_like <<-XML
|
568
|
+
<?xml version="1.0"?>
|
569
|
+
<Schema name="default">
|
570
|
+
<Cube name="Sales">
|
571
|
+
<Table name="sales_fact_1997">
|
572
|
+
<AggName name="agg_c_special_sales_fact_1997">
|
573
|
+
<AggFactCount column="fact_count"/>
|
574
|
+
<AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
|
575
|
+
<AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
|
576
|
+
<AggLevel column="product_family" name="[Product].[Product Family]"/>
|
577
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
578
|
+
<AggLevel column="time_year" name="[Time].[Year]"/>
|
579
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
580
|
+
<AggLevel column="time_month" name="[Time].[Month]"/>
|
581
|
+
</AggName>
|
582
|
+
<AggPattern pattern="agg_.*_sales_fact_1997">
|
583
|
+
<AggFactCount column="fact_count"/>
|
584
|
+
<AggMeasure column="store_cost_sum" name="[Measures].[Store Cost]"/>
|
585
|
+
<AggMeasure column="store_sales_sum" name="[Measures].[Store Sales]"/>
|
586
|
+
<AggLevel column="product_family" name="[Product].[Product Family]"/>
|
587
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
588
|
+
<AggLevel column="time_year" name="[Time].[Year]"/>
|
589
|
+
<AggLevel column="time_quarter" name="[Time].[Quarter]"/>
|
590
|
+
<AggLevel column="time_month" name="[Time].[Month]"/>
|
591
|
+
<AggExclude name="agg_c_14_sales_fact_1997"/>
|
592
|
+
<AggExclude name="agg_lc_100_sales_fact_1997"/>
|
593
|
+
</AggPattern>
|
594
|
+
</Table>
|
595
|
+
</Cube>
|
596
|
+
</Schema>
|
597
|
+
XML
|
598
|
+
end
|
599
|
+
|
600
|
+
end
|
601
|
+
|
602
|
+
describe "Member properties" do
|
603
|
+
it "should render XML" do
|
604
|
+
@schema.define do
|
605
|
+
cube 'Sales' do
|
606
|
+
dimension 'Employees', :foreign_key => 'employee_id' do
|
607
|
+
hierarchy :has_all => true, :all_member_name => 'All Employees', :primary_key => 'employee_id' do
|
608
|
+
table 'employee'
|
609
|
+
level 'Employee Id', :unique_members => true, :type => 'Numeric', :column => 'employee_id', :name_column => 'full_name',
|
610
|
+
:parent_column => 'supervisor_id', :null_parent_value => 0 do
|
611
|
+
property 'Marital Status', :column => 'marital_status'
|
612
|
+
property 'Position Title', :column => 'position_title'
|
613
|
+
property 'Gender', :column => 'gender'
|
614
|
+
property 'Salary', :column => 'salary'
|
615
|
+
property 'Education Level', :column => 'education_level'
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
621
|
+
@schema.to_xml.should be_like <<-XML
|
622
|
+
<?xml version="1.0"?>
|
623
|
+
<Schema name="default">
|
624
|
+
<Cube name="Sales">
|
625
|
+
<Dimension foreignKey="employee_id" name="Employees">
|
626
|
+
<Hierarchy allMemberName="All Employees" hasAll="true" primaryKey="employee_id">
|
627
|
+
<Table name="employee"/>
|
628
|
+
<Level column="employee_id" name="Employee Id" nameColumn="full_name" nullParentValue="0" parentColumn="supervisor_id" type="Numeric" uniqueMembers="true">
|
629
|
+
<Property column="marital_status" name="Marital Status"/>
|
630
|
+
<Property column="position_title" name="Position Title"/>
|
631
|
+
<Property column="gender" name="Gender"/>
|
632
|
+
<Property column="salary" name="Salary"/>
|
633
|
+
<Property column="education_level" name="Education Level"/>
|
634
|
+
</Level>
|
635
|
+
</Hierarchy>
|
636
|
+
</Dimension>
|
637
|
+
</Cube>
|
638
|
+
</Schema>
|
639
|
+
XML
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
describe "User defined functions and formatters in JavaScript" do
|
644
|
+
before(:each) do
|
645
|
+
@schema.define do
|
646
|
+
cube 'Sales' do
|
647
|
+
table 'sales'
|
648
|
+
dimension 'Customers', :foreign_key => 'customer_id' do
|
649
|
+
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
650
|
+
table 'customers'
|
651
|
+
level 'Name', :column => 'fullname' do
|
652
|
+
member_formatter { javascript "return member.getName().toUpperCase();" }
|
653
|
+
property 'City', :column => 'city' do
|
654
|
+
property_formatter { javascript "return propertyValue.toUpperCase();" }
|
655
|
+
end
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
calculated_member 'Factorial' do
|
660
|
+
dimension 'Measures'
|
661
|
+
formula 'Factorial(6)'
|
662
|
+
cell_formatter do
|
663
|
+
javascript <<-JS
|
664
|
+
var s = value.toString();
|
665
|
+
while (s.length < 20) {
|
666
|
+
s = "0" + s;
|
667
|
+
}
|
668
|
+
return s;
|
669
|
+
JS
|
670
|
+
end
|
671
|
+
end
|
672
|
+
calculated_member 'City' do
|
673
|
+
dimension 'Measures'
|
674
|
+
formula "[Customers].CurrentMember.Properties('City')"
|
675
|
+
end
|
676
|
+
end
|
677
|
+
user_defined_function 'Factorial' do
|
678
|
+
javascript <<-JS
|
679
|
+
function getParameterTypes() {
|
680
|
+
return new Array(
|
681
|
+
new mondrian.olap.type.NumericType());
|
682
|
+
}
|
683
|
+
function getReturnType(parameterTypes) {
|
684
|
+
return new mondrian.olap.type.NumericType();
|
685
|
+
}
|
686
|
+
function execute(evaluator, arguments) {
|
687
|
+
var n = arguments[0].evaluateScalar(evaluator);
|
688
|
+
return factorial(n);
|
689
|
+
}
|
690
|
+
function factorial(n) {
|
691
|
+
return n <= 1 ? 1 : n * factorial(n - 1);
|
692
|
+
}
|
693
|
+
JS
|
694
|
+
end
|
695
|
+
end
|
696
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
697
|
+
end
|
698
|
+
|
699
|
+
it "should render XML" do
|
700
|
+
@schema.to_xml.should be_like <<-XML
|
701
|
+
<?xml version="1.0"?>
|
702
|
+
<Schema name="default">
|
703
|
+
<Cube name="Sales">
|
704
|
+
<Table name="sales"/>
|
705
|
+
<Dimension foreignKey="customer_id" name="Customers">
|
706
|
+
<Hierarchy allMemberName="All Customers" hasAll="true" primaryKey="id">
|
707
|
+
<Table name="customers"/>
|
708
|
+
<Level column="fullname" name="Name">
|
709
|
+
<MemberFormatter>
|
710
|
+
<Script language="JavaScript">return member.getName().toUpperCase();</Script>
|
711
|
+
</MemberFormatter>
|
712
|
+
<Property column="city" name="City">
|
713
|
+
<PropertyFormatter>
|
714
|
+
<Script language="JavaScript">return propertyValue.toUpperCase();</Script>
|
715
|
+
</PropertyFormatter>
|
716
|
+
</Property>
|
717
|
+
</Level>
|
718
|
+
</Hierarchy>
|
719
|
+
</Dimension>
|
720
|
+
<CalculatedMember dimension="Measures" name="Factorial">
|
721
|
+
<Formula>Factorial(6)</Formula>
|
722
|
+
<CellFormatter>
|
723
|
+
<Script language="JavaScript">
|
724
|
+
var s = value.toString();
|
725
|
+
while (s.length < 20) {
|
726
|
+
s = "0" + s;
|
727
|
+
}
|
728
|
+
return s;
|
729
|
+
</Script>
|
730
|
+
</CellFormatter>
|
731
|
+
</CalculatedMember>
|
732
|
+
<CalculatedMember dimension="Measures" name="City">
|
733
|
+
<Formula>[Customers].CurrentMember.Properties('City')</Formula>
|
734
|
+
</CalculatedMember>
|
735
|
+
</Cube>
|
736
|
+
<UserDefinedFunction name="Factorial">
|
737
|
+
<Script language="JavaScript">
|
738
|
+
function getParameterTypes() {
|
739
|
+
return new Array(
|
740
|
+
new mondrian.olap.type.NumericType());
|
741
|
+
}
|
742
|
+
function getReturnType(parameterTypes) {
|
743
|
+
return new mondrian.olap.type.NumericType();
|
744
|
+
}
|
745
|
+
function execute(evaluator, arguments) {
|
746
|
+
var n = arguments[0].evaluateScalar(evaluator);
|
747
|
+
return factorial(n);
|
748
|
+
}
|
749
|
+
function factorial(n) {
|
750
|
+
return n <= 1 ? 1 : n * factorial(n - 1);
|
751
|
+
}
|
752
|
+
</Script>
|
753
|
+
</UserDefinedFunction>
|
754
|
+
</Schema>
|
755
|
+
XML
|
756
|
+
end
|
757
|
+
|
758
|
+
it "should execute user defined function" do
|
759
|
+
result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
|
760
|
+
value = 1*2*3*4*5*6
|
761
|
+
result.values.should == [value]
|
762
|
+
result.formatted_values.should == ["%020d" % value]
|
763
|
+
end
|
764
|
+
|
765
|
+
it "should format members and properties" do
|
766
|
+
result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
|
767
|
+
result.row_members.each_with_index do |member, i|
|
768
|
+
member.caption.should == member.name.upcase
|
769
|
+
city = member.property_value('City')
|
770
|
+
result.formatted_values[i].first.should == city
|
771
|
+
member.property_formatted_value('City').should == city.upcase
|
772
|
+
end
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
describe "User defined functions and formatters in CoffeeScript" do
|
777
|
+
before(:each) do
|
778
|
+
@schema.define do
|
779
|
+
cube 'Sales' do
|
780
|
+
table 'sales'
|
781
|
+
dimension 'Customers', :foreign_key => 'customer_id' do
|
782
|
+
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
783
|
+
table 'customers'
|
784
|
+
level 'Name', :column => 'fullname' do
|
785
|
+
member_formatter { coffeescript "member.getName().toUpperCase()" }
|
786
|
+
property 'City', :column => 'city' do
|
787
|
+
property_formatter { coffeescript "propertyValue.toUpperCase()" }
|
788
|
+
end
|
789
|
+
end
|
790
|
+
end
|
791
|
+
end
|
792
|
+
calculated_member 'Factorial' do
|
793
|
+
dimension 'Measures'
|
794
|
+
formula 'Factorial(6)'
|
795
|
+
cell_formatter do
|
796
|
+
coffeescript <<-JS
|
797
|
+
s = value.toString()
|
798
|
+
s = "0" + s while s.length < 20
|
799
|
+
s
|
800
|
+
JS
|
801
|
+
end
|
802
|
+
end
|
803
|
+
calculated_member 'City' do
|
804
|
+
dimension 'Measures'
|
805
|
+
formula "[Customers].CurrentMember.Properties('City')"
|
806
|
+
end
|
807
|
+
end
|
808
|
+
user_defined_function 'Factorial' do
|
809
|
+
coffeescript <<-JS
|
810
|
+
parameters: ["Numeric"]
|
811
|
+
returns: "Numeric"
|
812
|
+
execute: (n) ->
|
813
|
+
if n <= 1 then 1 else n * @execute(n - 1)
|
814
|
+
JS
|
815
|
+
end
|
816
|
+
user_defined_function 'UpperName' do
|
817
|
+
coffeescript <<-JS
|
818
|
+
parameters: ["Member"]
|
819
|
+
returns: "String"
|
820
|
+
syntax: "Property"
|
821
|
+
execute: (member) ->
|
822
|
+
member.getName().toUpperCase()
|
823
|
+
JS
|
824
|
+
end
|
825
|
+
user_defined_function 'toUpperName' do
|
826
|
+
coffeescript <<-JS
|
827
|
+
parameters: ["Member", "String"]
|
828
|
+
returns: "String"
|
829
|
+
syntax: "Method"
|
830
|
+
execute: (member, dummy) ->
|
831
|
+
member.getName().toUpperCase()
|
832
|
+
JS
|
833
|
+
end
|
834
|
+
end
|
835
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
836
|
+
end
|
837
|
+
|
838
|
+
it "should execute user defined function" do
|
839
|
+
result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
|
840
|
+
value = 1*2*3*4*5*6
|
841
|
+
result.values.should == [value]
|
842
|
+
result.formatted_values.should == ["%020d" % value]
|
843
|
+
end
|
844
|
+
|
845
|
+
it "should format members and properties" do
|
846
|
+
result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
|
847
|
+
result.row_members.each_with_index do |member, i|
|
848
|
+
member.caption.should == member.name.upcase
|
849
|
+
city = member.property_value('City')
|
850
|
+
result.formatted_values[i].first.should == city
|
851
|
+
member.property_formatted_value('City').should == city.upcase
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
it "should execute user defined property on member" do
|
856
|
+
result = @olap.from('Sales').
|
857
|
+
with_member('[Measures].[Upper Name]').as('[Customers].CurrentMember.UpperName').
|
858
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
859
|
+
result.row_members.each_with_index do |member, i|
|
860
|
+
result.values[i].should == [member.name.upcase]
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
it "should execute user defined method on member" do
|
865
|
+
result = @olap.from('Sales').
|
866
|
+
with_member('[Measures].[Upper Name]').as("[Customers].CurrentMember.toUpperName('dummy')").
|
867
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
868
|
+
result.row_members.each_with_index do |member, i|
|
869
|
+
result.values[i].should == [member.name.upcase]
|
870
|
+
end
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
874
|
+
describe "User defined functions and formatters in Ruby" do
|
875
|
+
before(:each) do
|
876
|
+
@schema.define do
|
877
|
+
cube 'Sales' do
|
878
|
+
table 'sales'
|
879
|
+
dimension 'Customers', :foreign_key => 'customer_id' do
|
880
|
+
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
881
|
+
table 'customers'
|
882
|
+
level 'Name', :column => 'fullname' do
|
883
|
+
member_formatter { ruby {|member| member.getName().upcase } }
|
884
|
+
property 'City', :column => 'city' do
|
885
|
+
property_formatter { ruby {|member, property_name, property_value| property_value.upcase} }
|
886
|
+
end
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end
|
890
|
+
calculated_member 'Factorial' do
|
891
|
+
dimension 'Measures'
|
892
|
+
formula 'Factorial(6)'
|
893
|
+
cell_formatter { ruby {|value| "%020d" % value} }
|
894
|
+
end
|
895
|
+
calculated_member 'City' do
|
896
|
+
dimension 'Measures'
|
897
|
+
formula "[Customers].CurrentMember.Properties('City')"
|
898
|
+
end
|
899
|
+
end
|
900
|
+
user_defined_function 'Factorial' do
|
901
|
+
ruby do
|
902
|
+
parameters :numeric
|
903
|
+
returns :numeric
|
904
|
+
def call(n)
|
905
|
+
n <= 1 ? 1 : n * call(n - 1)
|
906
|
+
end
|
907
|
+
end
|
908
|
+
end
|
909
|
+
user_defined_function 'UpperName' do
|
910
|
+
ruby do
|
911
|
+
parameters :member
|
912
|
+
returns :string
|
913
|
+
syntax :property
|
914
|
+
def call(member)
|
915
|
+
member.getName.upcase
|
916
|
+
end
|
917
|
+
end
|
918
|
+
end
|
919
|
+
user_defined_function 'toUpperName' do
|
920
|
+
ruby do
|
921
|
+
parameters :member, :string
|
922
|
+
returns :string
|
923
|
+
syntax :method
|
924
|
+
def call(member, dummy)
|
925
|
+
member.getName.upcase
|
926
|
+
end
|
927
|
+
end
|
928
|
+
end
|
929
|
+
user_defined_function 'firstUpperName' do
|
930
|
+
ruby do
|
931
|
+
parameters :set
|
932
|
+
returns :string
|
933
|
+
syntax :property
|
934
|
+
def call(set)
|
935
|
+
set.first.getName.upcase
|
936
|
+
end
|
937
|
+
end
|
938
|
+
end
|
939
|
+
user_defined_function 'firstToUpperName' do
|
940
|
+
ruby do
|
941
|
+
parameters :set, :string
|
942
|
+
returns :string
|
943
|
+
syntax :method
|
944
|
+
def call(set, dummy)
|
945
|
+
set.first.getName.upcase
|
946
|
+
end
|
947
|
+
end
|
948
|
+
end
|
949
|
+
user_defined_function 'firstChildUpperName' do
|
950
|
+
ruby do
|
951
|
+
parameters :hierarchy
|
952
|
+
returns :string
|
953
|
+
syntax :property
|
954
|
+
def call_with_evaluator(evaluator, hierarchy)
|
955
|
+
evaluator.getSchemaReader.getMemberChildren(hierarchy.getDefaultMember).first.getName.upcase
|
956
|
+
end
|
957
|
+
end
|
958
|
+
end
|
959
|
+
user_defined_function 'firstLevelChildUpperName' do
|
960
|
+
ruby do
|
961
|
+
parameters :level
|
962
|
+
returns :string
|
963
|
+
syntax :property
|
964
|
+
def call_with_evaluator(evaluator, level)
|
965
|
+
evaluator.getSchemaReader.getLevelMembers(level, false).first.getName.upcase
|
966
|
+
end
|
967
|
+
end
|
968
|
+
end
|
969
|
+
end
|
970
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
971
|
+
end
|
972
|
+
|
973
|
+
it "should execute user defined function" do
|
974
|
+
result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
|
975
|
+
value = 1*2*3*4*5*6
|
976
|
+
result.values.should == [value]
|
977
|
+
result.formatted_values.should == ["%020d" % value]
|
978
|
+
end
|
979
|
+
|
980
|
+
it "should format members and properties" do
|
981
|
+
result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
|
982
|
+
result.row_members.each_with_index do |member, i|
|
983
|
+
member.caption.should == member.name.upcase
|
984
|
+
city = member.property_value('City')
|
985
|
+
result.formatted_values[i].first.should == city
|
986
|
+
member.property_formatted_value('City').should == city.upcase
|
987
|
+
end
|
988
|
+
end
|
989
|
+
|
990
|
+
it "should execute user defined property on member" do
|
991
|
+
result = @olap.from('Sales').
|
992
|
+
with_member('[Measures].[Upper Name]').as('[Customers].CurrentMember.UpperName').
|
993
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
994
|
+
result.row_members.each_with_index do |member, i|
|
995
|
+
result.values[i].should == [member.name.upcase]
|
996
|
+
end
|
997
|
+
end
|
998
|
+
|
999
|
+
it "should execute user defined method on member" do
|
1000
|
+
result = @olap.from('Sales').
|
1001
|
+
with_member('[Measures].[Upper Name]').as("[Customers].CurrentMember.toUpperName('dummy')").
|
1002
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1003
|
+
result.row_members.each_with_index do |member, i|
|
1004
|
+
result.values[i].should == [member.name.upcase]
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
it "should execute user defined property on set" do
|
1009
|
+
result = @olap.from('Sales').
|
1010
|
+
with_member('[Measures].[Upper Name]').as("{[Customers].CurrentMember}.firstUpperName").
|
1011
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1012
|
+
result.row_members.each_with_index do |member, i|
|
1013
|
+
result.values[i].should == [member.name.upcase]
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
it "should execute user defined method on set" do
|
1018
|
+
result = @olap.from('Sales').
|
1019
|
+
with_member('[Measures].[Upper Name]').as("{[Customers].CurrentMember}.firstToUpperName('dummy')").
|
1020
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1021
|
+
result.row_members.each_with_index do |member, i|
|
1022
|
+
result.values[i].should == [member.name.upcase]
|
1023
|
+
end
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
it "should execute user defined property on hierarchy" do
|
1027
|
+
result = @olap.from('Sales').
|
1028
|
+
with_member('[Measures].[Upper Name]').as("[Customers].firstChildUpperName").
|
1029
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1030
|
+
first_member = result.row_members.first
|
1031
|
+
result.row_members.each_with_index do |member, i|
|
1032
|
+
result.values[i].should == [first_member.name.upcase]
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
it "should execute user defined property on level" do
|
1037
|
+
result = @olap.from('Sales').
|
1038
|
+
with_member('[Measures].[Upper Name]').as("[Customers].[Name].firstLevelChildUpperName").
|
1039
|
+
columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
|
1040
|
+
first_member = result.row_members.first
|
1041
|
+
result.row_members.each_with_index do |member, i|
|
1042
|
+
result.values[i].should == [first_member.name.upcase]
|
1043
|
+
end
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
describe "Shared user defined functions in Ruby" do
|
1048
|
+
before(:each) do
|
1049
|
+
shared_schema = Mondrian::OLAP::Schema.define do
|
1050
|
+
user_defined_function 'Factorial' do
|
1051
|
+
ruby :shared do
|
1052
|
+
parameters :numeric
|
1053
|
+
returns :numeric
|
1054
|
+
def call(n)
|
1055
|
+
n <= 1 ? 1 : n * call(n - 1)
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
user_defined_function 'UpperName' do
|
1060
|
+
ruby :shared do
|
1061
|
+
parameters :member
|
1062
|
+
returns :string
|
1063
|
+
syntax :property
|
1064
|
+
def call(member)
|
1065
|
+
member.getName.upcase
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
user_defined_function 'toUpperName' do
|
1070
|
+
ruby :shared do
|
1071
|
+
parameters :member, :string
|
1072
|
+
returns :string
|
1073
|
+
syntax :method
|
1074
|
+
def call(member, dummy)
|
1075
|
+
member.getName.upcase
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
@schema.define do
|
1082
|
+
include_schema shared_schema
|
1083
|
+
|
1084
|
+
cube 'Sales' do
|
1085
|
+
table 'sales'
|
1086
|
+
dimension 'Customers', :foreign_key => 'customer_id' do
|
1087
|
+
hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
|
1088
|
+
table 'customers'
|
1089
|
+
level 'Name', :column => 'fullname'
|
1090
|
+
end
|
1091
|
+
end
|
1092
|
+
calculated_member 'Factorial' do
|
1093
|
+
dimension 'Measures'
|
1094
|
+
formula 'Factorial(6)'
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
it "should render XML" do
|
1102
|
+
@schema.to_xml.should be_like <<-XML
|
1103
|
+
<?xml version="1.0"?>
|
1104
|
+
<Schema name="default">
|
1105
|
+
<Cube name="Sales">
|
1106
|
+
<Table name="sales"/>
|
1107
|
+
<Dimension foreignKey="customer_id" name="Customers">
|
1108
|
+
<Hierarchy allMemberName="All Customers" hasAll="true" primaryKey="id">
|
1109
|
+
<Table name="customers"/>
|
1110
|
+
<Level column="fullname" name="Name"/>
|
1111
|
+
</Hierarchy>
|
1112
|
+
</Dimension>
|
1113
|
+
<CalculatedMember dimension="Measures" name="Factorial">
|
1114
|
+
<Formula>Factorial(6)</Formula>
|
1115
|
+
</CalculatedMember>
|
1116
|
+
</Cube>
|
1117
|
+
<UserDefinedFunction className="rubyobj.Mondrian.OLAP.Schema.UserDefinedFunction.FactorialUdf" name="Factorial"/>
|
1118
|
+
<UserDefinedFunction className="rubyobj.Mondrian.OLAP.Schema.UserDefinedFunction.UppernameUdf" name="UpperName"/>
|
1119
|
+
<UserDefinedFunction className="rubyobj.Mondrian.OLAP.Schema.UserDefinedFunction.TouppernameUdf" name="toUpperName"/>
|
1120
|
+
</Schema>
|
1121
|
+
XML
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
it "should execute user defined function" do
|
1125
|
+
result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
|
1126
|
+
value = 1*2*3*4*5*6
|
1127
|
+
result.values.should == [value]
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
describe "Roles" do
|
1133
|
+
it "should render XML" do
|
1134
|
+
@schema.define do
|
1135
|
+
role 'California manager' do
|
1136
|
+
schema_grant :access => 'none' do
|
1137
|
+
cube_grant :cube => 'Sales', :access => 'all' do
|
1138
|
+
dimension_grant :dimension => '[Measures]', :access => 'all'
|
1139
|
+
hierarchy_grant :hierarchy => '[Customers]', :access => 'custom',
|
1140
|
+
:top_level => '[Customers].[State Province]', :bottom_level => '[Customers].[City]' do
|
1141
|
+
member_grant :member => '[Customers].[USA].[CA]', :access => 'all'
|
1142
|
+
member_grant :member => '[Customers].[USA].[CA].[Los Angeles]', :access => 'none'
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
@schema.to_xml.should be_like <<-XML
|
1149
|
+
<?xml version="1.0"?>
|
1150
|
+
<Schema name="default">
|
1151
|
+
<Role name="California manager">
|
1152
|
+
<SchemaGrant access="none">
|
1153
|
+
<CubeGrant access="all" cube="Sales">
|
1154
|
+
<DimensionGrant access="all" dimension="[Measures]"/>
|
1155
|
+
<HierarchyGrant access="custom" bottomLevel="[Customers].[City]" hierarchy="[Customers]" topLevel="[Customers].[State Province]">
|
1156
|
+
<MemberGrant access="all" member="[Customers].[USA].[CA]"/>
|
1157
|
+
<MemberGrant access="none" member="[Customers].[USA].[CA].[Los Angeles]"/>
|
1158
|
+
</HierarchyGrant>
|
1159
|
+
</CubeGrant>
|
1160
|
+
</SchemaGrant>
|
1161
|
+
</Role>
|
1162
|
+
</Schema>
|
1163
|
+
XML
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
describe "connection with schema" do
|
1170
|
+
before(:all) do
|
1171
|
+
@schema = Mondrian::OLAP::Schema.define do
|
1172
|
+
cube 'Sales' do
|
1173
|
+
table 'sales'
|
1174
|
+
dimension 'Gender', :foreign_key => 'customer_id' do
|
1175
|
+
hierarchy :has_all => true, :primary_key => 'id' do
|
1176
|
+
table 'customers'
|
1177
|
+
level 'Gender', :column => 'gender', :unique_members => true
|
1178
|
+
end
|
1179
|
+
end
|
1180
|
+
dimension 'Time', :foreign_key => 'time_id' do
|
1181
|
+
hierarchy :has_all => false, :primary_key => 'id' do
|
1182
|
+
table 'time'
|
1183
|
+
level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true
|
1184
|
+
level 'Quarter', :column => 'quarter', :unique_members => false
|
1185
|
+
level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
|
1189
|
+
measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
|
1190
|
+
end
|
1191
|
+
end
|
1192
|
+
@olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
it "should connect" do
|
1196
|
+
@olap.should be_connected
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
it "should execute query" do
|
1200
|
+
@olap.from('Sales').
|
1201
|
+
columns('[Measures].[Unit Sales]', '[Measures].[Store Sales]').
|
1202
|
+
rows('descendants([Time].[2010].[Q1])').
|
1203
|
+
where('[Gender].[F]').
|
1204
|
+
execute.should be_a(Mondrian::OLAP::Result)
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
describe "errors" do
|
1210
|
+
before(:each) do
|
1211
|
+
@schema = Mondrian::OLAP::Schema.new
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
it "should raise error when invalid schema" do
|
1215
|
+
@schema.define do
|
1216
|
+
cube 'Sales' do
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
expect {
|
1220
|
+
Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
1221
|
+
}.to raise_error {|e|
|
1222
|
+
e.should be_kind_of(Mondrian::OLAP::Error)
|
1223
|
+
e.message.should == "mondrian.olap.MondrianException: Mondrian Error:Internal error: Must specify fact table of cube 'Sales'"
|
1224
|
+
e.root_cause_message.should == "Internal error: Must specify fact table of cube 'Sales'"
|
1225
|
+
}
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
it "should raise error when invalid calculated member formula" do
|
1229
|
+
@schema.define do
|
1230
|
+
cube 'Sales' do
|
1231
|
+
table 'sales'
|
1232
|
+
calculated_member 'Dummy' do
|
1233
|
+
dimension 'Measures'
|
1234
|
+
formula 'Dummy(123)'
|
1235
|
+
end
|
1236
|
+
end
|
1237
|
+
end
|
1238
|
+
expect {
|
1239
|
+
Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
|
1240
|
+
}.to raise_error {|e|
|
1241
|
+
e.should be_kind_of(Mondrian::OLAP::Error)
|
1242
|
+
e.message.should == "mondrian.olap.MondrianException: Mondrian Error:Named set in cube 'Sales' has bad formula"
|
1243
|
+
e.root_cause_message.should == "No function matches signature 'Dummy(<Numeric Expression>)'"
|
1244
|
+
}
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
end
|