@cubejs-backend/schema-compiler 0.35.10 → 0.35.13
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.
- package/dist/src/adapter/BaseDimension.d.ts +1 -0
- package/dist/src/adapter/BaseDimension.d.ts.map +1 -1
- package/dist/src/adapter/BaseDimension.js +6 -0
- package/dist/src/adapter/BaseDimension.js.map +1 -1
- package/dist/src/adapter/BaseMeasure.d.ts +1 -0
- package/dist/src/adapter/BaseMeasure.d.ts.map +1 -1
- package/dist/src/adapter/BaseMeasure.js +6 -0
- package/dist/src/adapter/BaseMeasure.js.map +1 -1
- package/dist/src/adapter/BaseQuery.d.ts +21 -3
- package/dist/src/adapter/BaseQuery.d.ts.map +1 -1
- package/dist/src/adapter/BaseQuery.js +316 -83
- package/dist/src/adapter/BaseQuery.js.map +1 -1
- package/dist/src/compiler/CubeEvaluator.d.ts +10 -0
- package/dist/src/compiler/CubeEvaluator.d.ts.map +1 -1
- package/dist/src/compiler/CubeEvaluator.js +21 -0
- package/dist/src/compiler/CubeEvaluator.js.map +1 -1
- package/dist/src/compiler/CubeValidator.d.ts.map +1 -1
- package/dist/src/compiler/CubeValidator.js +25 -2
- package/dist/src/compiler/CubeValidator.js.map +1 -1
- package/dist/src/compiler/transpilers/CubePropContextTranspiler.d.ts.map +1 -1
- package/dist/src/compiler/transpilers/CubePropContextTranspiler.js +3 -0
- package/dist/src/compiler/transpilers/CubePropContextTranspiler.js.map +1 -1
- package/package.json +4 -4
|
@@ -195,13 +195,19 @@ class BaseQuery {
|
|
|
195
195
|
memberToAlias: this.options.memberToAlias,
|
|
196
196
|
expressionParams: this.options.expressionParams,
|
|
197
197
|
convertTzForRawTimeDimension: this.options.convertTzForRawTimeDimension,
|
|
198
|
+
from: this.options.from,
|
|
199
|
+
postAggregateQuery: this.options.postAggregateQuery,
|
|
200
|
+
postAggregateDimensions: this.options.postAggregateDimensions,
|
|
198
201
|
});
|
|
202
|
+
this.from = this.options.from;
|
|
203
|
+
this.postAggregateQuery = this.options.postAggregateQuery;
|
|
199
204
|
this.timezone = this.options.timezone;
|
|
200
205
|
this.rowLimit = this.options.rowLimit;
|
|
201
206
|
this.offset = this.options.offset;
|
|
202
207
|
this.preAggregations = this.newPreAggregations();
|
|
203
208
|
this.measures = (this.options.measures || []).map(this.newMeasure.bind(this));
|
|
204
209
|
this.dimensions = (this.options.dimensions || []).map(this.newDimension.bind(this));
|
|
210
|
+
this.postAggregateDimensions = (this.options.postAggregateDimensions || []).map(this.newDimension.bind(this));
|
|
205
211
|
this.segments = (this.options.segments || []).map(this.newSegment.bind(this));
|
|
206
212
|
this.order = this.options.order || [];
|
|
207
213
|
const filters = this.extractFiltersAsTree(this.options.filters || []);
|
|
@@ -405,6 +411,10 @@ class BaseQuery {
|
|
|
405
411
|
buildParamAnnotatedSql() {
|
|
406
412
|
let sql;
|
|
407
413
|
let preAggForQuery;
|
|
414
|
+
// TODO Most probably should be called later than here but avoids errors during pre-aggregation match for now
|
|
415
|
+
if (this.from) {
|
|
416
|
+
return this.simpleQuery();
|
|
417
|
+
}
|
|
408
418
|
if (!this.options.preAggregationQuery) {
|
|
409
419
|
preAggForQuery =
|
|
410
420
|
this.preAggregations.findPreAggregationForQuery();
|
|
@@ -413,7 +423,7 @@ class BaseQuery {
|
|
|
413
423
|
}
|
|
414
424
|
}
|
|
415
425
|
if (preAggForQuery) {
|
|
416
|
-
const { multipliedMeasures, regularMeasures, cumulativeMeasures, } = this.fullKeyQueryAggregateMeasures();
|
|
426
|
+
const { multipliedMeasures, regularMeasures, cumulativeMeasures, withQueries, postAggregateMembers, } = this.fullKeyQueryAggregateMeasures();
|
|
417
427
|
if (cumulativeMeasures.length === 0) {
|
|
418
428
|
sql = this.preAggregations.rollupPreAggregation(preAggForQuery, this.measures, true);
|
|
419
429
|
}
|
|
@@ -564,6 +574,9 @@ class BaseQuery {
|
|
|
564
574
|
// eslint-disable-next-line prefer-template
|
|
565
575
|
const inlineWhereConditions = [];
|
|
566
576
|
const commonQuery = this.rewriteInlineWhere(() => this.commonQuery(), inlineWhereConditions);
|
|
577
|
+
if (this.postAggregateQuery) {
|
|
578
|
+
return `${commonQuery} ${this.baseWhere(this.allFilters.concat(inlineWhereConditions))}`;
|
|
579
|
+
}
|
|
567
580
|
return `${commonQuery} ${this.baseWhere(this.allFilters.concat(inlineWhereConditions))}` +
|
|
568
581
|
this.groupByClause() +
|
|
569
582
|
this.baseHaving(this.measureFilters) +
|
|
@@ -575,10 +588,14 @@ class BaseQuery {
|
|
|
575
588
|
* @returns {string}
|
|
576
589
|
*/
|
|
577
590
|
fullKeyQueryAggregate() {
|
|
578
|
-
|
|
579
|
-
|
|
591
|
+
if (this.from) {
|
|
592
|
+
return this.simpleQuery();
|
|
593
|
+
}
|
|
594
|
+
const { multipliedMeasures, regularMeasures, cumulativeMeasures, withQueries, postAggregateMembers, } = this.fullKeyQueryAggregateMeasures();
|
|
595
|
+
if (!multipliedMeasures.length && !cumulativeMeasures.length && !postAggregateMembers.length) {
|
|
580
596
|
return this.simpleQuery();
|
|
581
597
|
}
|
|
598
|
+
const renderedWithQueries = withQueries.map(q => this.renderWithQuery(q));
|
|
582
599
|
let toJoin;
|
|
583
600
|
if (this.options.preAggregationQuery) {
|
|
584
601
|
const allRegular = regularMeasures.concat(cumulativeMeasures
|
|
@@ -600,7 +617,9 @@ class BaseQuery {
|
|
|
600
617
|
.concat(ramda_1.default.pipe(ramda_1.default.groupBy(m => m.cube().name), ramda_1.default.toPairs, ramda_1.default.map(([keyCubeName, measures]) => this
|
|
601
618
|
.withCubeAliasPrefix(`${this.aliasName(keyCubeName)}_key`, () => this.aggregateSubQuery(keyCubeName, measures))))(multipliedMeasures)).concat(ramda_1.default.map(([multiplied, measure]) => this.withCubeAliasPrefix(`${this.aliasName(measure.measure.replace('.', '_'))}_cumulative`, () => this.overTimeSeriesQuery(multiplied
|
|
602
619
|
? (measures, filters) => this.aggregateSubQuery(measures[0].cube().name, measures, filters)
|
|
603
|
-
: this.regularMeasuresSubQuery.bind(this), measure, false)))(cumulativeMeasures)
|
|
620
|
+
: this.regularMeasuresSubQuery.bind(this), measure, false)))(cumulativeMeasures)
|
|
621
|
+
// TODO SELECT *
|
|
622
|
+
).concat(postAggregateMembers.map(m => `SELECT * FROM ${m.alias}`));
|
|
604
623
|
}
|
|
605
624
|
// Move regular measures to multiplied ones if there're same
|
|
606
625
|
// cubes to calculate. Most of the times it'll be much faster to
|
|
@@ -631,36 +650,196 @@ class BaseQuery {
|
|
|
631
650
|
toJoin = ramda_1.default.pipe(ramda_1.default.groupBy(m => m.cube().name), ramda_1.default.toPairs, ramda_1.default.map(([keyCubeName, measures]) => this.withCubeAliasPrefix(`${keyCubeName}_key`, () => this.aggregateSubQuery(keyCubeName, measures))))(measuresList);
|
|
632
651
|
}
|
|
633
652
|
}
|
|
634
|
-
|
|
653
|
+
const postAggregateMeasures = ramda_1.default.flatten(postAggregateMembers.map(m => m.measures)).map(m => this.newMeasure(m));
|
|
654
|
+
return this.withQueries(this.joinFullKeyQueryAggregate(
|
|
655
|
+
// TODO separate param?
|
|
656
|
+
multipliedMeasures.concat(postAggregateMeasures), regularMeasures, cumulativeMeasures, toJoin), renderedWithQueries);
|
|
635
657
|
}
|
|
636
658
|
joinFullKeyQueryAggregate(multipliedMeasures, regularMeasures, cumulativeMeasures, toJoin) {
|
|
659
|
+
return this.outerMeasuresJoinFullKeyQueryAggregate(multipliedMeasures.concat(regularMeasures).concat(cumulativeMeasures.map(([multiplied, measure]) => measure)), this.measures, toJoin);
|
|
660
|
+
}
|
|
661
|
+
outerMeasuresJoinFullKeyQueryAggregate(innerMembers, outerMembers, toJoin) {
|
|
637
662
|
const renderedReferenceContext = {
|
|
638
|
-
renderedReference: ramda_1.default.pipe(ramda_1.default.map(m => [m.measure, m.aliasName()]), ramda_1.default.fromPairs)(
|
|
663
|
+
renderedReference: ramda_1.default.pipe(ramda_1.default.map(m => [m.measure || m.dimension, m.aliasName()]), ramda_1.default.fromPairs)(innerMembers),
|
|
639
664
|
};
|
|
640
665
|
const join = ramda_1.default.drop(1, toJoin)
|
|
641
666
|
.map((q, i) => (this.dimensionAliasNames().length ?
|
|
642
|
-
`INNER JOIN
|
|
643
|
-
`,
|
|
644
|
-
const columnsToSelect = this.evaluateSymbolSqlWithContext(() => this.dimensionColumns('q_0').concat(
|
|
645
|
-
const queryHasNoRemapping = this.evaluateSymbolSqlWithContext(() => this.dimensionsForSelect().concat(
|
|
667
|
+
`INNER JOIN ${this.wrapInParenthesis((q))} as q_${i + 1} ON ${this.dimensionsJoinCondition(`q_${i}`, `q_${i + 1}`)}` :
|
|
668
|
+
`, ${this.wrapInParenthesis(q)} as q_${i + 1}`)).join('\n');
|
|
669
|
+
const columnsToSelect = this.evaluateSymbolSqlWithContext(() => this.dimensionColumns('q_0').concat(outerMembers.map(m => m.selectColumns())).join(', '), renderedReferenceContext);
|
|
670
|
+
const queryHasNoRemapping = this.evaluateSymbolSqlWithContext(() => this.dimensionsForSelect().concat(outerMembers).every(r => r.hasNoRemapping()), renderedReferenceContext);
|
|
646
671
|
const havingFilters = this.evaluateSymbolSqlWithContext(() => this.baseWhere(this.measureFilters), renderedReferenceContext);
|
|
647
672
|
// TODO all having filters should be pushed down
|
|
648
673
|
// subQuery dimensions can introduce projection remapping
|
|
649
674
|
if (toJoin.length === 1 &&
|
|
650
675
|
this.measureFilters.length === 0 &&
|
|
651
|
-
|
|
676
|
+
outerMembers.filter(m => m.expression).length === 0 &&
|
|
652
677
|
queryHasNoRemapping) {
|
|
653
678
|
return `${toJoin[0].replace(/^SELECT/, `SELECT ${this.topLimit()}`)} ${this.orderBy()}${this.groupByDimensionLimit()}`;
|
|
654
679
|
}
|
|
655
|
-
return `SELECT ${this.topLimit()}${columnsToSelect} FROM
|
|
680
|
+
return `SELECT ${this.topLimit()}${columnsToSelect} FROM ${this.wrapInParenthesis(toJoin[0])} as q_0 ${join}${havingFilters}${this.orderBy()}${this.groupByDimensionLimit()}`;
|
|
681
|
+
}
|
|
682
|
+
wrapInParenthesis(select) {
|
|
683
|
+
return select.trim().match(/^SELECT/ig) ? `(${select})` : select;
|
|
684
|
+
}
|
|
685
|
+
withQueries(select, withQueries) {
|
|
686
|
+
if (!withQueries || !withQueries.length) {
|
|
687
|
+
return select;
|
|
688
|
+
}
|
|
689
|
+
// TODO escape alias
|
|
690
|
+
return `WITH\n${withQueries.map(q => `${q.alias} AS (${q.query})`).join(',\n')}\n${select}`;
|
|
656
691
|
}
|
|
657
692
|
fullKeyQueryAggregateMeasures(context) {
|
|
658
693
|
const measureToHierarchy = this.collectRootMeasureToHieararchy(context);
|
|
659
|
-
const
|
|
694
|
+
const allMemberChildren = this.collectAllMemberChildren(context);
|
|
695
|
+
const measuresToRender = (multiplied, cumulative) => ramda_1.default.pipe(ramda_1.default.values, ramda_1.default.flatten, ramda_1.default.filter(m => m.multiplied === multiplied && this.newMeasure(m.measure).isCumulative() === cumulative && !m.postAggregate), ramda_1.default.map(m => m.measure), ramda_1.default.uniq, ramda_1.default.map(m => this.newMeasure(m)));
|
|
660
696
|
const multipliedMeasures = measuresToRender(true, false)(measureToHierarchy);
|
|
661
697
|
const regularMeasures = measuresToRender(false, false)(measureToHierarchy);
|
|
662
698
|
const cumulativeMeasures = ramda_1.default.pipe(ramda_1.default.map(multiplied => ramda_1.default.xprod([multiplied], measuresToRender(multiplied, true)(measureToHierarchy))), ramda_1.default.unnest)([false, true]);
|
|
663
|
-
|
|
699
|
+
const withQueries = [];
|
|
700
|
+
const postAggregateMembers = (this.allMembersConcat(false))
|
|
701
|
+
.filter(m => m.definition && m.definition()?.postAggregate)
|
|
702
|
+
.map(m => m.measure || m.dimension)
|
|
703
|
+
.map(m => this.postAggregateWithQueries(m, { dimensions: this.dimensions.map(d => d.dimension), postAggregateDimensions: this.dimensions.map(d => d.dimension), filters: this.filters }, allMemberChildren, withQueries));
|
|
704
|
+
const usedWithQueries = {};
|
|
705
|
+
postAggregateMembers.forEach(m => this.collectUsedWithQueries(usedWithQueries, m));
|
|
706
|
+
return {
|
|
707
|
+
multipliedMeasures,
|
|
708
|
+
regularMeasures,
|
|
709
|
+
cumulativeMeasures,
|
|
710
|
+
postAggregateMembers,
|
|
711
|
+
withQueries: withQueries.filter(q => usedWithQueries[q.alias])
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
collectAllMemberChildren(context) {
|
|
715
|
+
return this.collectFromMembers(false, (fn) => {
|
|
716
|
+
const memberChildren = {};
|
|
717
|
+
this.evaluateSymbolSqlWithContext(fn, { ...context, memberChildren });
|
|
718
|
+
return memberChildren;
|
|
719
|
+
}, context ? ['collectAllMemberChildren', JSON.stringify(context)] : 'collectAllMemberChildren').reduce((a, b) => ({ ...a, ...b }), {});
|
|
720
|
+
}
|
|
721
|
+
postAggregateWithQueries(member, queryContext, memberChildren, withQueries) {
|
|
722
|
+
let memberFrom = memberChildren[member]
|
|
723
|
+
?.map(child => this.postAggregateWithQueries(child, this.childrenPostAggregateContext(member, queryContext), memberChildren, withQueries));
|
|
724
|
+
const unionFromDimensions = memberFrom ? ramda_1.default.uniq(ramda_1.default.flatten(memberFrom.map(f => f.dimensions))) : queryContext.dimensions;
|
|
725
|
+
const unionDimensionsContext = { ...queryContext, dimensions: unionFromDimensions.filter(d => !this.newDimension(d).isPostAggregate()) };
|
|
726
|
+
// TODO is calling postAggregateWithQueries twice optimal? If so make sure to keep only used CTE
|
|
727
|
+
memberFrom = memberChildren[member] &&
|
|
728
|
+
ramda_1.default.uniqBy(f => f.alias, memberChildren[member].map(child => this.postAggregateWithQueries(child, this.childrenPostAggregateContext(member, unionDimensionsContext), memberChildren, withQueries)));
|
|
729
|
+
const selfContext = this.selfPostAggregateContext(member, queryContext, unionDimensionsContext);
|
|
730
|
+
const subQuery = {
|
|
731
|
+
...selfContext,
|
|
732
|
+
...(this.cubeEvaluator.isMeasure(member) ? { measures: [member] } : { measures: [], dimensions: ramda_1.default.uniq(selfContext.dimensions.concat(member)) }),
|
|
733
|
+
memberFrom,
|
|
734
|
+
};
|
|
735
|
+
if (!memberFrom) {
|
|
736
|
+
const postAggregateMember = subQuery.measures.find(m => this.newMeasure(m).isPostAggregate()) || subQuery.dimensions.find(m => this.newDimension(m).isPostAggregate());
|
|
737
|
+
if (postAggregateMember) {
|
|
738
|
+
throw new Error(`Post aggregate member '${postAggregateMember}' lacks FROM clause in sub query: ${JSON.stringify(subQuery)}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const foundWith = withQueries.find(({ alias, ...q }) => ramda_1.default.equals(subQuery, q));
|
|
742
|
+
if (foundWith) {
|
|
743
|
+
return foundWith;
|
|
744
|
+
}
|
|
745
|
+
subQuery.alias = `cte_${withQueries.length}`;
|
|
746
|
+
withQueries.push(subQuery);
|
|
747
|
+
return subQuery;
|
|
748
|
+
}
|
|
749
|
+
collectUsedWithQueries(usedQueries, member) {
|
|
750
|
+
usedQueries[member.alias] = true;
|
|
751
|
+
member.memberFrom?.forEach(m => this.collectUsedWithQueries(usedQueries, m));
|
|
752
|
+
}
|
|
753
|
+
childrenPostAggregateContext(memberPath, queryContext) {
|
|
754
|
+
let member;
|
|
755
|
+
if (this.cubeEvaluator.isMeasure(memberPath)) {
|
|
756
|
+
member = this.newMeasure(memberPath);
|
|
757
|
+
}
|
|
758
|
+
else if (this.cubeEvaluator.isDimension(memberPath)) {
|
|
759
|
+
member = this.newDimension(memberPath);
|
|
760
|
+
}
|
|
761
|
+
const memberDef = member.definition();
|
|
762
|
+
// TODO can addGroupBy replaced by something else?
|
|
763
|
+
if (memberDef.addGroupByReferences) {
|
|
764
|
+
queryContext = { ...queryContext, dimensions: ramda_1.default.uniq(queryContext.dimensions.concat(memberDef.addGroupByReferences)) };
|
|
765
|
+
}
|
|
766
|
+
return queryContext;
|
|
767
|
+
}
|
|
768
|
+
selfPostAggregateContext(memberPath, queryContext, unionDimensionsContext) {
|
|
769
|
+
let member;
|
|
770
|
+
if (this.cubeEvaluator.isMeasure(memberPath)) {
|
|
771
|
+
member = this.newMeasure(memberPath);
|
|
772
|
+
}
|
|
773
|
+
else if (this.cubeEvaluator.isDimension(memberPath)) {
|
|
774
|
+
member = this.newDimension(memberPath);
|
|
775
|
+
// TODO is it right place to replace context?
|
|
776
|
+
// if (member.definition().type === 'rank') {
|
|
777
|
+
// queryContext = unionDimensionsContext;
|
|
778
|
+
// }
|
|
779
|
+
}
|
|
780
|
+
const memberDef = member.definition();
|
|
781
|
+
if (memberDef.reduceByReferences) {
|
|
782
|
+
queryContext = {
|
|
783
|
+
...queryContext,
|
|
784
|
+
postAggregateDimensions: ramda_1.default.difference(queryContext.postAggregateDimensions, memberDef.reduceByReferences),
|
|
785
|
+
// dimensions: R.uniq(queryContext.dimensions.concat(memberDef.reduceByReferences))
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
if (memberDef.groupByReferences) {
|
|
789
|
+
queryContext = {
|
|
790
|
+
...queryContext,
|
|
791
|
+
postAggregateDimensions: ramda_1.default.intersection(queryContext.postAggregateDimensions, memberDef.groupByReferences)
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
return queryContext;
|
|
795
|
+
}
|
|
796
|
+
renderWithQuery(withQuery) {
|
|
797
|
+
const fromMeasures = withQuery.memberFrom && ramda_1.default.uniq(ramda_1.default.flatten(withQuery.memberFrom.map(f => f.measures)));
|
|
798
|
+
// TODO get rid of this postAggregate filter
|
|
799
|
+
const fromDimensions = withQuery.memberFrom && ramda_1.default.uniq(ramda_1.default.flatten(withQuery.memberFrom.map(f => f.dimensions)));
|
|
800
|
+
const renderedReferenceContext = {
|
|
801
|
+
renderedReference: withQuery.memberFrom && ramda_1.default.fromPairs(ramda_1.default.unnest(withQuery.memberFrom.map(from => from.measures.map(m => {
|
|
802
|
+
const measure = this.newMeasure(m);
|
|
803
|
+
return [m, measure.aliasName()];
|
|
804
|
+
}).concat(from.dimensions.map(m => {
|
|
805
|
+
const member = this.newDimension(m);
|
|
806
|
+
return [m, member.aliasName()];
|
|
807
|
+
})))))
|
|
808
|
+
};
|
|
809
|
+
const fromSubQuery = fromMeasures && this.newSubQuery({
|
|
810
|
+
measures: fromMeasures,
|
|
811
|
+
// TODO get rid of this postAggregate filter
|
|
812
|
+
dimensions: fromDimensions,
|
|
813
|
+
postAggregateDimensions: withQuery.postAggregateDimensions,
|
|
814
|
+
filters: withQuery.filters,
|
|
815
|
+
// TODO do we need it?
|
|
816
|
+
postAggregateQuery: true // !!fromDimensions.find(d => this.newDimension(d).isPostAggregate())
|
|
817
|
+
});
|
|
818
|
+
const measures = fromSubQuery && fromMeasures.map(m => fromSubQuery.newMeasure(m));
|
|
819
|
+
// TODO get rid of this postAggregate filter
|
|
820
|
+
const postAggregateDimensions = fromSubQuery && fromDimensions.map(m => fromSubQuery.newDimension(m)).filter(d => d.isPostAggregate());
|
|
821
|
+
const membersToSelect = measures?.concat(postAggregateDimensions);
|
|
822
|
+
const select = fromSubQuery && fromSubQuery.outerMeasuresJoinFullKeyQueryAggregate(membersToSelect, membersToSelect, withQuery.memberFrom.map(f => f.alias));
|
|
823
|
+
const fromSql = select && this.wrapInParenthesis(select);
|
|
824
|
+
const subQuery = this.newSubQuery({
|
|
825
|
+
measures: withQuery.measures,
|
|
826
|
+
dimensions: withQuery.dimensions,
|
|
827
|
+
postAggregateDimensions: withQuery.postAggregateDimensions,
|
|
828
|
+
filters: withQuery.filters,
|
|
829
|
+
from: fromSql && {
|
|
830
|
+
sql: fromSql,
|
|
831
|
+
alias: `${withQuery.alias}_join`,
|
|
832
|
+
},
|
|
833
|
+
// TODO condition should something else instead of rank
|
|
834
|
+
postAggregateQuery: !!withQuery.measures.find(d => {
|
|
835
|
+
const { type } = this.newMeasure(d).definition();
|
|
836
|
+
return type === 'rank' || type === 'number';
|
|
837
|
+
}),
|
|
838
|
+
});
|
|
839
|
+
return {
|
|
840
|
+
query: subQuery.evaluateSymbolSqlWithContext(() => subQuery.buildParamAnnotatedSql(), renderedReferenceContext),
|
|
841
|
+
alias: withQuery.alias
|
|
842
|
+
};
|
|
664
843
|
}
|
|
665
844
|
dimensionsJoinCondition(leftAlias, rightAlias) {
|
|
666
845
|
const dimensionAliases = this.dimensionAliasNames();
|
|
@@ -807,7 +986,7 @@ class BaseQuery {
|
|
|
807
986
|
}));
|
|
808
987
|
}
|
|
809
988
|
query() {
|
|
810
|
-
return this.joinQuery(this.join, this.collectFromMembers(false, this.collectSubQueryDimensionsFor.bind(this), 'collectSubQueryDimensionsFor'));
|
|
989
|
+
return this.from && this.joinSql([this.from]) || this.joinQuery(this.join, this.collectFromMembers(false, this.collectSubQueryDimensionsFor.bind(this), 'collectSubQueryDimensionsFor'));
|
|
811
990
|
}
|
|
812
991
|
rewriteInlineCubeSql(cube, isLeftJoinCondition) {
|
|
813
992
|
const sql = this.cubeSql(cube);
|
|
@@ -1048,12 +1227,7 @@ class BaseQuery {
|
|
|
1048
1227
|
return this.collectFromMembers(excludeTimeDimensions, this.collectJoinHintsFor.bind(this), 'collectJoinHintsFor');
|
|
1049
1228
|
}
|
|
1050
1229
|
collectFromMembers(excludeTimeDimensions, fn, methodName) {
|
|
1051
|
-
const membersToCollectFrom = this.
|
|
1052
|
-
.concat(this.dimensions)
|
|
1053
|
-
.concat(this.segments)
|
|
1054
|
-
.concat(this.filters)
|
|
1055
|
-
.concat(this.measureFilters)
|
|
1056
|
-
.concat(excludeTimeDimensions ? [] : this.timeDimensions)
|
|
1230
|
+
const membersToCollectFrom = this.allMembersConcat(excludeTimeDimensions)
|
|
1057
1231
|
.concat(this.join ? this.join.joins.map(j => ({
|
|
1058
1232
|
getMembers: () => [{
|
|
1059
1233
|
path: () => null,
|
|
@@ -1063,6 +1237,14 @@ class BaseQuery {
|
|
|
1063
1237
|
})) : []);
|
|
1064
1238
|
return this.collectFrom(membersToCollectFrom, fn, methodName);
|
|
1065
1239
|
}
|
|
1240
|
+
allMembersConcat(excludeTimeDimensions) {
|
|
1241
|
+
return this.measures
|
|
1242
|
+
.concat(this.dimensions)
|
|
1243
|
+
.concat(this.segments)
|
|
1244
|
+
.concat(this.filters)
|
|
1245
|
+
.concat(this.measureFilters)
|
|
1246
|
+
.concat(excludeTimeDimensions ? [] : this.timeDimensions);
|
|
1247
|
+
}
|
|
1066
1248
|
collectFrom(membersToCollectFrom, fn, methodName, cache) {
|
|
1067
1249
|
const methodCacheKey = Array.isArray(methodName) ? methodName : [methodName];
|
|
1068
1250
|
return ramda_1.default.pipe(ramda_1.default.map(f => f.getMembers()), ramda_1.default.flatten, ramda_1.default.map(s => ((cache || this.compilerCache).cache(['collectFrom'].concat(methodCacheKey).concat(s.path() ? [s.path().join('.')] : [s.cube().name, s.expression?.toString() || s.expressionName || s.definition().sql]), () => fn(() => this.traverseSymbol(s))))), ramda_1.default.unnest, ramda_1.default.uniq, ramda_1.default.filter(ramda_1.default.identity))(membersToCollectFrom);
|
|
@@ -1249,75 +1431,100 @@ class BaseQuery {
|
|
|
1249
1431
|
if (!type && this.cubeEvaluator.isSegment(memberPathArray)) {
|
|
1250
1432
|
type = 'segment';
|
|
1251
1433
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1434
|
+
const parentMember = this.safeEvaluateSymbolContext().currentMember;
|
|
1435
|
+
if (this.safeEvaluateSymbolContext().memberChildren && parentMember) {
|
|
1436
|
+
this.safeEvaluateSymbolContext().memberChildren[parentMember] = this.safeEvaluateSymbolContext().memberChildren[parentMember] || [];
|
|
1437
|
+
if (this.safeEvaluateSymbolContext().memberChildren[parentMember].indexOf(memberPath) === -1) {
|
|
1438
|
+
this.safeEvaluateSymbolContext().memberChildren[parentMember].push(memberPath);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
this.safeEvaluateSymbolContext().currentMember = memberPath;
|
|
1442
|
+
try {
|
|
1443
|
+
if (type === 'measure') {
|
|
1444
|
+
let parentMeasure;
|
|
1445
|
+
if (this.safeEvaluateSymbolContext().compositeCubeMeasures ||
|
|
1446
|
+
this.safeEvaluateSymbolContext().leafMeasures) {
|
|
1447
|
+
parentMeasure = this.safeEvaluateSymbolContext().currentMeasure;
|
|
1448
|
+
if (this.safeEvaluateSymbolContext().compositeCubeMeasures) {
|
|
1449
|
+
if (parentMeasure && !memberExpressionType &&
|
|
1450
|
+
(this.cubeEvaluator.cubeNameFromPath(parentMeasure) !== cubeName ||
|
|
1451
|
+
this.newMeasure(memberPath).isCumulative())) {
|
|
1452
|
+
this.safeEvaluateSymbolContext().compositeCubeMeasures[parentMeasure] = true;
|
|
1453
|
+
}
|
|
1262
1454
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1455
|
+
this.safeEvaluateSymbolContext().currentMeasure = memberPath;
|
|
1456
|
+
if (this.safeEvaluateSymbolContext().leafMeasures) {
|
|
1457
|
+
if (parentMeasure) {
|
|
1458
|
+
this.safeEvaluateSymbolContext().leafMeasures[parentMeasure] = false;
|
|
1459
|
+
}
|
|
1460
|
+
this.safeEvaluateSymbolContext().leafMeasures[this.safeEvaluateSymbolContext().currentMeasure] = true;
|
|
1268
1461
|
}
|
|
1269
|
-
this.safeEvaluateSymbolContext().leafMeasures[this.safeEvaluateSymbolContext().currentMeasure] = true;
|
|
1270
1462
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
this.safeEvaluateSymbolContext().currentMeasure = parentMeasure;
|
|
1280
|
-
}
|
|
1281
|
-
return result;
|
|
1282
|
-
}
|
|
1283
|
-
else if (type === 'dimension') {
|
|
1284
|
-
if ((this.safeEvaluateSymbolContext().renderedReference || {})[memberPath]) {
|
|
1285
|
-
return this.evaluateSymbolContext.renderedReference[memberPath];
|
|
1286
|
-
}
|
|
1287
|
-
if (symbol.subQuery) {
|
|
1288
|
-
if (this.safeEvaluateSymbolContext().subQueryDimensions) {
|
|
1289
|
-
this.safeEvaluateSymbolContext().subQueryDimensions.push(memberPath);
|
|
1463
|
+
const primaryKeys = this.cubeEvaluator.primaryKeys[cubeName];
|
|
1464
|
+
const orderBySql = (symbol.orderBy || []).map(o => ({ sql: this.evaluateSql(cubeName, o.sql), dir: o.dir }));
|
|
1465
|
+
let sql;
|
|
1466
|
+
if (symbol.type !== 'rank') {
|
|
1467
|
+
sql = symbol.sql && this.evaluateSql(cubeName, symbol.sql) ||
|
|
1468
|
+
primaryKeys.length && (primaryKeys.length > 1 ?
|
|
1469
|
+
this.concatStringsSql(primaryKeys.map((pk) => this.castToString(this.primaryKeySql(pk, cubeName))))
|
|
1470
|
+
: this.primaryKeySql(primaryKeys[0], cubeName)) || '*';
|
|
1290
1471
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1472
|
+
const result = this.renderSqlMeasure(name, sql && this.applyMeasureFilters(this.autoPrefixWithCubeName(cubeName, sql), symbol, cubeName), symbol, cubeName, parentMeasure, orderBySql);
|
|
1473
|
+
if (this.safeEvaluateSymbolContext().compositeCubeMeasures ||
|
|
1474
|
+
this.safeEvaluateSymbolContext().leafMeasures) {
|
|
1475
|
+
this.safeEvaluateSymbolContext().currentMeasure = parentMeasure;
|
|
1476
|
+
}
|
|
1477
|
+
return result;
|
|
1295
1478
|
}
|
|
1296
|
-
else if (
|
|
1297
|
-
|
|
1298
|
-
this.
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
]);
|
|
1479
|
+
else if (type === 'dimension') {
|
|
1480
|
+
if ((this.safeEvaluateSymbolContext().renderedReference || {})[memberPath]) {
|
|
1481
|
+
return this.evaluateSymbolContext.renderedReference[memberPath];
|
|
1482
|
+
}
|
|
1483
|
+
// if (symbol.postAggregate) {
|
|
1484
|
+
// const orderBySql = (symbol.orderBy || []).map(o => ({ sql: this.evaluateSql(cubeName, o.sql), dir: o.dir }));
|
|
1485
|
+
// const partitionBy = this.postAggregateDimensions.length ? `PARTITION BY ${this.postAggregateDimensions.map(d => d.dimensionSql()).join(', ')} ` : '';
|
|
1486
|
+
// if (symbol.type === 'rank') {
|
|
1487
|
+
// return `${symbol.type}() OVER (${partitionBy}ORDER BY ${orderBySql.map(o => `${o.sql} ${o.dir}`).join(', ')})`;
|
|
1488
|
+
// }
|
|
1489
|
+
// }
|
|
1490
|
+
if (symbol.subQuery) {
|
|
1491
|
+
if (this.safeEvaluateSymbolContext().subQueryDimensions) {
|
|
1492
|
+
this.safeEvaluateSymbolContext().subQueryDimensions.push(memberPath);
|
|
1493
|
+
}
|
|
1494
|
+
return this.escapeColumnName(this.aliasName(memberPath));
|
|
1495
|
+
}
|
|
1496
|
+
if (symbol.case) {
|
|
1497
|
+
return this.renderDimensionCase(symbol, cubeName);
|
|
1498
|
+
}
|
|
1499
|
+
else if (symbol.type === 'geo') {
|
|
1500
|
+
return this.concatStringsSql([
|
|
1501
|
+
this.autoPrefixAndEvaluateSql(cubeName, symbol.latitude.sql),
|
|
1502
|
+
'\',\'',
|
|
1503
|
+
this.autoPrefixAndEvaluateSql(cubeName, symbol.longitude.sql)
|
|
1504
|
+
]);
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
let res = this.autoPrefixAndEvaluateSql(cubeName, symbol.sql);
|
|
1508
|
+
if (this.safeEvaluateSymbolContext().convertTzForRawTimeDimension &&
|
|
1509
|
+
!memberExpressionType &&
|
|
1510
|
+
symbol.type === 'time' &&
|
|
1511
|
+
this.cubeEvaluator.byPathAnyType(memberPathArray).ownedByCube) {
|
|
1512
|
+
res = this.convertTz(res);
|
|
1513
|
+
}
|
|
1514
|
+
return res;
|
|
1515
|
+
}
|
|
1302
1516
|
}
|
|
1303
|
-
else {
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
!memberExpressionType &&
|
|
1307
|
-
symbol.type === 'time' &&
|
|
1308
|
-
this.cubeEvaluator.byPathAnyType(memberPathArray).ownedByCube) {
|
|
1309
|
-
res = this.convertTz(res);
|
|
1517
|
+
else if (type === 'segment') {
|
|
1518
|
+
if ((this.safeEvaluateSymbolContext().renderedReference || {})[memberPath]) {
|
|
1519
|
+
return this.evaluateSymbolContext.renderedReference[memberPath];
|
|
1310
1520
|
}
|
|
1311
|
-
return
|
|
1521
|
+
return this.autoPrefixWithCubeName(cubeName, this.evaluateSql(cubeName, symbol.sql));
|
|
1312
1522
|
}
|
|
1523
|
+
return this.evaluateSql(cubeName, symbol.sql);
|
|
1313
1524
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
return this.evaluateSymbolContext.renderedReference[memberPath];
|
|
1317
|
-
}
|
|
1318
|
-
return this.autoPrefixWithCubeName(cubeName, this.evaluateSql(cubeName, symbol.sql));
|
|
1525
|
+
finally {
|
|
1526
|
+
this.safeEvaluateSymbolContext().currentMember = parentMember;
|
|
1319
1527
|
}
|
|
1320
|
-
return this.evaluateSql(cubeName, symbol.sql);
|
|
1321
1528
|
}
|
|
1322
1529
|
autoPrefixAndEvaluateSql(cubeName, sql) {
|
|
1323
1530
|
return this.autoPrefixWithCubeName(cubeName, this.evaluateSql(cubeName, sql));
|
|
@@ -1415,7 +1622,7 @@ class BaseQuery {
|
|
|
1415
1622
|
this.evaluateSymbolContext = oldContext;
|
|
1416
1623
|
}
|
|
1417
1624
|
}
|
|
1418
|
-
renderSqlMeasure(name, evaluateSql, symbol, cubeName, parentMeasure) {
|
|
1625
|
+
renderSqlMeasure(name, evaluateSql, symbol, cubeName, parentMeasure, orderBySql) {
|
|
1419
1626
|
const multiplied = this.multipliedJoinRowResult(cubeName);
|
|
1420
1627
|
const measurePath = `${cubeName}.${name}`;
|
|
1421
1628
|
let resultMultiplied = multiplied;
|
|
@@ -1427,10 +1634,10 @@ class BaseQuery {
|
|
|
1427
1634
|
if (parentMeasure &&
|
|
1428
1635
|
(this.safeEvaluateSymbolContext().foundCompositeCubeMeasures || {})[parentMeasure] &&
|
|
1429
1636
|
!(this.safeEvaluateSymbolContext().foundCompositeCubeMeasures || {})[measurePath]) {
|
|
1430
|
-
this.safeEvaluateSymbolContext().measuresToRender.push({ multiplied: resultMultiplied, measure: measurePath });
|
|
1637
|
+
this.safeEvaluateSymbolContext().measuresToRender.push({ multiplied: resultMultiplied, measure: measurePath, postAggregate: symbol.postAggregate });
|
|
1431
1638
|
}
|
|
1432
1639
|
if (this.safeEvaluateSymbolContext().foundCompositeCubeMeasures && !parentMeasure) {
|
|
1433
|
-
this.safeEvaluateSymbolContext().rootMeasure.value = { multiplied: resultMultiplied, measure: measurePath };
|
|
1640
|
+
this.safeEvaluateSymbolContext().rootMeasure.value = { multiplied: resultMultiplied, measure: measurePath, postAggregate: symbol.postAggregate };
|
|
1434
1641
|
}
|
|
1435
1642
|
if (((this.evaluateSymbolContext || {}).renderedReference || {})[measurePath]) {
|
|
1436
1643
|
return this.evaluateSymbolContext.renderedReference[measurePath];
|
|
@@ -1459,6 +1666,32 @@ class BaseQuery {
|
|
|
1459
1666
|
return onGroupedColumn;
|
|
1460
1667
|
}
|
|
1461
1668
|
}
|
|
1669
|
+
if (symbol.postAggregate) {
|
|
1670
|
+
const partitionBy = this.postAggregateDimensions.length ? `PARTITION BY ${this.postAggregateDimensions.map(d => d.dimensionSql()).join(', ')} ` : '';
|
|
1671
|
+
if (symbol.type === 'rank') {
|
|
1672
|
+
return `${symbol.type}() OVER (${partitionBy}ORDER BY ${orderBySql.map(o => `${o.sql} ${o.dir}`).join(', ')})`;
|
|
1673
|
+
}
|
|
1674
|
+
if (!ramda_1.default.equals(this.postAggregateDimensions.map(d => d.expressionPath()), this.dimensions.map(d => d.expressionPath()))) {
|
|
1675
|
+
let funDef;
|
|
1676
|
+
if (symbol.type === 'countDistinctApprox') {
|
|
1677
|
+
funDef = this.countDistinctApprox(evaluateSql);
|
|
1678
|
+
}
|
|
1679
|
+
else if (symbol.type === 'countDistinct' || symbol.type === 'count' && !symbol.sql && multiplied) {
|
|
1680
|
+
funDef = `count(distinct ${evaluateSql})`;
|
|
1681
|
+
}
|
|
1682
|
+
else if (BaseQuery.isCalculatedMeasureType(symbol.type)) {
|
|
1683
|
+
// TODO calculated measure type will be ungrouped
|
|
1684
|
+
// if (this.postAggregateDimensions.length !== this.dimensions.length) {
|
|
1685
|
+
// throw new UserError(`Calculated measure '${measurePath}' uses group_by or reduce_by context modifiers while it isn't allowed`);
|
|
1686
|
+
// }
|
|
1687
|
+
return evaluateSql;
|
|
1688
|
+
}
|
|
1689
|
+
else {
|
|
1690
|
+
funDef = `${symbol.type}(${symbol.type}(${evaluateSql}))`;
|
|
1691
|
+
}
|
|
1692
|
+
return `${funDef} OVER(${partitionBy})`;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1462
1695
|
if (symbol.type === 'countDistinctApprox') {
|
|
1463
1696
|
return this.safeEvaluateSymbolContext().overTimeSeriesAggregate || this.options.preAggregationQuery ?
|
|
1464
1697
|
this.hllInit(evaluateSql) :
|