Skip to content

Commit dfeccca

Browse files
feat(mssql-driver): add readonly aggregation for mssql sources (#920) Thanks to @JoshMentzer!
Co-authored-by: Joshua D. Mentzer <mentzerj@trinity-health.org>
1 parent 92cd0b3 commit dfeccca

File tree

3 files changed

+181
-141
lines changed

3 files changed

+181
-141
lines changed

packages/cubejs-mssql-driver/driver/MSSqlDriver.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class MSSqlDriver extends BaseDriver {
1414
process.env.CUBEJS_DB_DOMAIN : undefined,
1515
requestTimeout: 10 * 60 * 1000, // 10 minutes
1616
options: {
17-
encrypt: !!process.env.CUBEJS_DB_SSL || false
17+
encrypt: !!process.env.CUBEJS_DB_SSL || false,
18+
useUTC: false
1819
},
1920
pool: {
2021
max: 8,
@@ -72,6 +73,23 @@ class MSSqlDriver extends BaseDriver {
7273
return null;
7374
});
7475
}
76+
77+
async downloadQueryResults(query, values) {
78+
const result = await this.query(query, values);
79+
const types = Object.keys(result.columns).map((key) => ({
80+
name: result.columns[key].name,
81+
type: this.toGenericType(result.columns[key].type.declaration),
82+
}));
83+
84+
return {
85+
rows: result,
86+
types,
87+
};
88+
}
89+
90+
readOnly() {
91+
return !!this.config.readOnly;
92+
}
7593
}
7694

7795
module.exports = MSSqlDriver;

packages/cubejs-schema-compiler/adapter/MssqlQuery.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ class MssqlParamAllocator extends ParamAllocator {
2929
}
3030

3131
const GRANULARITY_TO_INTERVAL = {
32-
day: (date) => `FORMAT(${date}, 'yyyy-MM-ddT00:00:00.000')`,
33-
week: (date) => `FORMAT(dateadd(week, DATEDIFF(week, '1900-01-01', ${date}), '1900-01-01'), 'yyyy-MM-ddT00:00:00.000')`,
34-
hour: (date) => `FORMAT(${date}, 'yyyy-MM-ddTHH:00:00.000')`,
35-
minute: (date) => `FORMAT(${date}, 'yyyy-MM-ddTHH:mm:00.000')`,
36-
second: (date) => `FORMAT(${date}, 'yyyy-MM-ddTHH:mm:ss.000')`,
37-
month: (date) => `FORMAT(${date}, 'yyyy-MM-01T00:00:00.000')`,
38-
year: (date) => `FORMAT(${date}, 'yyyy-01-01T00:00:00.000')`
32+
day: (date) => `dateadd(day, DATEDIFF(day, 0, ${date}), 0)`,
33+
week: (date) => `dateadd(week, DATEDIFF(week, 0, ${date}), 0)`,
34+
hour: (date) => `dateadd(hour, DATEDIFF(hour, 0, ${date}), 0)`,
35+
minute: (date) => `dateadd(minute, DATEDIFF(minute, 0, ${date}), 0)`,
36+
second: (date) => `CAST(FORMAT(${date}, 'yyyy-MM-ddTHH:mm:ss.000') AS datetime)`, // until SQL 2016, this causes an int overflow; in SQL 2016 these calls can be changed to DATEDIFF_BIG
37+
month: (date) => `dateadd(month, DATEDIFF(month, 0, ${date}), 0)`,
38+
year: (date) => `dateadd(year, DATEDIFF(year, 0, ${date}), 0)`,
3939
};
4040

4141
class MssqlFilter extends BaseFilter {

packages/cubejs-schema-compiler/test/MSSqlPreAggregationsTest.js

Lines changed: 155 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -178,155 +178,177 @@ describe('MSSqlPreAggregations', function test() {
178178
preAggregation = Array.isArray(preAggregation) ? preAggregation : [preAggregation];
179179
return [
180180
preAggregation.reduce(
181-
(replacedQuery, desc) => replacedQuery.replace(new RegExp(desc.tableName, 'g'), `##${desc.tableName}_${suffix}`), toReplace
181+
(replacedQuery, desc) =>
182+
replacedQuery.replace(new RegExp(desc.tableName, 'g'), `##${desc.tableName}_${suffix}`),
183+
toReplace
182184
),
183-
params
185+
params,
184186
];
185187
}
186188

187189
function tempTablePreAggregations(preAggregationsDescriptions) {
188-
return R.unnest(preAggregationsDescriptions.map(
189-
desc => desc.invalidateKeyQueries.concat([[desc.loadSql[0], desc.loadSql[1]]])
190-
));
190+
return R.unnest(
191+
preAggregationsDescriptions.map((desc) => desc.invalidateKeyQueries.concat([[desc.loadSql[0], desc.loadSql[1]]]))
192+
);
191193
}
192194

193-
it('simple pre-aggregation', () => compiler.compile().then(() => {
194-
const query = new MSSqlQuery({ joinGraph, cubeEvaluator, compiler }, {
195-
measures: [
196-
'visitors.count'
197-
],
198-
timeDimensions: [{
199-
dimension: 'visitors.createdAt',
200-
granularity: 'day',
201-
dateRange: ['2017-01-01', '2017-01-30']
202-
}],
203-
timezone: 'UTC',
204-
order: [{
205-
id: 'visitors.createdAt'
206-
}],
207-
preAggregationsSchema: ''
208-
});
195+
it('simple pre-aggregation', () =>
196+
compiler.compile().then(() => {
197+
const query = new MSSqlQuery(
198+
{ joinGraph, cubeEvaluator, compiler },
199+
{
200+
measures: ['visitors.count'],
201+
timeDimensions: [
202+
{
203+
dimension: 'visitors.createdAt',
204+
granularity: 'day',
205+
dateRange: ['2017-01-01', '2017-01-30'],
206+
},
207+
],
208+
timezone: 'UTC',
209+
order: [
210+
{
211+
id: 'visitors.createdAt',
212+
},
213+
],
214+
preAggregationsSchema: '',
215+
}
216+
);
209217

210-
const queryAndParams = query.buildSqlAndParams();
211-
console.log(queryAndParams);
212-
const preAggregationsDescription = query.preAggregations.preAggregationsDescription();
213-
console.log(preAggregationsDescription);
218+
const queryAndParams = query.buildSqlAndParams();
219+
console.log(queryAndParams);
220+
const preAggregationsDescription = query.preAggregations.preAggregationsDescription();
221+
console.log(preAggregationsDescription);
214222

215-
return dbRunner.testQueries(tempTablePreAggregations(preAggregationsDescription).concat([
216-
query.buildSqlAndParams()
217-
]).map(q => replaceTableName(q, preAggregationsDescription, 1))).then(res => {
218-
res.should.be.deepEqual(
219-
[
220-
{
221-
"visitors__created_at_day": "2017-01-03T00:00:00.000",
222-
"visitors__count": 1
223-
},
224-
{
225-
"visitors__created_at_day": "2017-01-05T00:00:00.000",
226-
"visitors__count": 1
227-
},
228-
{
229-
"visitors__created_at_day": "2017-01-06T00:00:00.000",
230-
"visitors__count": 1
231-
},
232-
{
233-
"visitors__created_at_day": "2017-01-07T00:00:00.000",
234-
"visitors__count": 2
235-
}
236-
]
223+
return dbRunner
224+
.testQueries(
225+
tempTablePreAggregations(preAggregationsDescription)
226+
.concat([query.buildSqlAndParams()])
227+
.map((q) => replaceTableName(q, preAggregationsDescription, 1))
228+
)
229+
.then((res) => {
230+
res.should.be.deepEqual([
231+
{
232+
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z'),
233+
visitors__count: 1,
234+
},
235+
{
236+
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
237+
visitors__count: 1,
238+
},
239+
{
240+
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
241+
visitors__count: 1,
242+
},
243+
{
244+
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z'),
245+
visitors__count: 2,
246+
},
247+
]);
248+
});
249+
}));
250+
251+
it('leaf measure pre-aggregation', () =>
252+
compiler.compile().then(() => {
253+
const query = new MSSqlQuery(
254+
{ joinGraph, cubeEvaluator, compiler },
255+
{
256+
measures: ['visitors.ratio'],
257+
timeDimensions: [
258+
{
259+
dimension: 'visitors.createdAt',
260+
granularity: 'day',
261+
dateRange: ['2017-01-01', '2017-01-30'],
262+
},
263+
],
264+
timezone: 'UTC',
265+
order: [
266+
{
267+
id: 'visitors.createdAt',
268+
},
269+
],
270+
preAggregationsSchema: '',
271+
}
237272
);
238-
});
239-
}));
240273

241-
it('leaf measure pre-aggregation', () => compiler.compile().then(() => {
242-
const query = new MSSqlQuery({ joinGraph, cubeEvaluator, compiler }, {
243-
measures: [
244-
'visitors.ratio'
245-
],
246-
timeDimensions: [{
247-
dimension: 'visitors.createdAt',
248-
granularity: 'day',
249-
dateRange: ['2017-01-01', '2017-01-30']
250-
}],
251-
timezone: 'UTC',
252-
order: [{
253-
id: 'visitors.createdAt'
254-
}],
255-
preAggregationsSchema: ''
256-
});
274+
const queryAndParams = query.buildSqlAndParams();
275+
console.log(queryAndParams);
276+
const preAggregationsDescription = query.preAggregations.preAggregationsDescription();
277+
console.log(preAggregationsDescription);
278+
preAggregationsDescription[0].loadSql[0].should.match(/visitors_ratio/);
257279

258-
const queryAndParams = query.buildSqlAndParams();
259-
console.log(queryAndParams);
260-
const preAggregationsDescription = query.preAggregations.preAggregationsDescription();
261-
console.log(preAggregationsDescription);
262-
preAggregationsDescription[0].loadSql[0].should.match(/visitors_ratio/);
280+
return dbRunner
281+
.testQueries(
282+
tempTablePreAggregations(preAggregationsDescription)
283+
.concat([query.buildSqlAndParams()])
284+
.map((q) => replaceTableName(q, preAggregationsDescription, 10))
285+
)
286+
.then((res) => {
287+
res.should.be.deepEqual([
288+
{
289+
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z'),
290+
visitors__ratio: 0.333333333333,
291+
},
292+
{
293+
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
294+
visitors__ratio: 0.5,
295+
},
296+
{
297+
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
298+
visitors__ratio: 1,
299+
},
300+
{
301+
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z'),
302+
visitors__ratio: null,
303+
},
304+
]);
305+
});
306+
}));
263307

264-
return dbRunner.testQueries(tempTablePreAggregations(preAggregationsDescription).concat([
265-
query.buildSqlAndParams()
266-
]).map(q => replaceTableName(q, preAggregationsDescription, 10))).then(res => {
267-
res.should.be.deepEqual(
268-
[
269-
{
270-
"visitors__created_at_day": "2017-01-03T00:00:00.000",
271-
"visitors__ratio": 0.333333333333
272-
},
273-
{
274-
"visitors__created_at_day": "2017-01-05T00:00:00.000",
275-
"visitors__ratio": 0.5
276-
},
277-
{
278-
"visitors__created_at_day": "2017-01-06T00:00:00.000",
279-
"visitors__ratio": 1
280-
},
281-
{
282-
"visitors__created_at_day": "2017-01-07T00:00:00.000",
283-
"visitors__ratio": null
284-
}
285-
]
308+
it('segment', () =>
309+
compiler.compile().then(() => {
310+
const query = new MSSqlQuery(
311+
{ joinGraph, cubeEvaluator, compiler },
312+
{
313+
measures: ['visitors.checkinsTotal'],
314+
dimensions: [],
315+
segments: ['visitors.google'],
316+
timezone: 'UTC',
317+
preAggregationsSchema: '',
318+
timeDimensions: [
319+
{
320+
dimension: 'visitors.createdAt',
321+
granularity: 'day',
322+
dateRange: ['2016-12-30', '2017-01-06'],
323+
},
324+
],
325+
order: [
326+
{
327+
id: 'visitors.createdAt',
328+
},
329+
],
330+
}
286331
);
287-
});
288-
}));
289-
290-
it('segment', () => compiler.compile().then(() => {
291-
const query = new MSSqlQuery({ joinGraph, cubeEvaluator, compiler }, {
292-
measures: [
293-
'visitors.checkinsTotal'
294-
],
295-
dimensions: [],
296-
segments: ['visitors.google'],
297-
timezone: 'UTC',
298-
preAggregationsSchema: '',
299-
timeDimensions: [{
300-
dimension: 'visitors.createdAt',
301-
granularity: 'day',
302-
dateRange: ['2016-12-30', '2017-01-06']
303-
}],
304-
order: [{
305-
id: 'visitors.createdAt'
306-
}],
307-
});
308332

309-
const queryAndParams = query.buildSqlAndParams();
310-
console.log(queryAndParams);
311-
const preAggregationsDescription = query.preAggregations.preAggregationsDescription();
312-
console.log(preAggregationsDescription);
333+
const queryAndParams = query.buildSqlAndParams();
334+
console.log(queryAndParams);
335+
const preAggregationsDescription = query.preAggregations.preAggregationsDescription();
336+
console.log(preAggregationsDescription);
313337

314-
const queries = tempTablePreAggregations(preAggregationsDescription);
338+
const queries = tempTablePreAggregations(preAggregationsDescription);
315339

316-
console.log(JSON.stringify(queries.concat(queryAndParams)));
340+
console.log(JSON.stringify(queries.concat(queryAndParams)));
317341

318-
return dbRunner.testQueries(
319-
queries.concat([queryAndParams]).map(q => replaceTableName(q, preAggregationsDescription, 142))
320-
).then(res => {
321-
console.log(JSON.stringify(res));
322-
res.should.be.deepEqual(
323-
[
324-
{
325-
"visitors__created_at_day": "2017-01-06T00:00:00.000",
326-
"visitors__checkins_total": 1
327-
}
328-
]
329-
);
330-
});
331-
}));
342+
return dbRunner
343+
.testQueries(queries.concat([queryAndParams]).map((q) => replaceTableName(q, preAggregationsDescription, 142)))
344+
.then((res) => {
345+
console.log(JSON.stringify(res));
346+
res.should.be.deepEqual([
347+
{
348+
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
349+
visitors__checkins_total: 1,
350+
},
351+
]);
352+
});
353+
}));
332354
});

0 commit comments

Comments
 (0)