@malloydata/malloy 0.0.119-dev240116200529 → 0.0.119-dev240118211537
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.
|
@@ -193,16 +193,16 @@ class PostgresDialect extends dialect_1.Dialect {
|
|
|
193
193
|
return 'GEN_RANDOM_UUID()';
|
|
194
194
|
}
|
|
195
195
|
sqlFieldReference(alias, fieldName, fieldType, isNested, _isArray) {
|
|
196
|
-
let ret =
|
|
196
|
+
let ret = `(${alias}->>'${fieldName}')`;
|
|
197
197
|
if (isNested) {
|
|
198
198
|
switch (fieldType) {
|
|
199
199
|
case 'string':
|
|
200
200
|
break;
|
|
201
201
|
case 'number':
|
|
202
|
-
ret =
|
|
202
|
+
ret = `${ret}::double precision`;
|
|
203
203
|
break;
|
|
204
204
|
case 'struct':
|
|
205
|
-
ret =
|
|
205
|
+
ret = `${ret}::jsonb`;
|
|
206
206
|
break;
|
|
207
207
|
}
|
|
208
208
|
return ret;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { FieldValueType, StructRelationship } from '../../../model/malloy_types';
|
|
1
|
+
import { AggregateFunctionType, FieldValueType, StructRelationship } from '../../../model/malloy_types';
|
|
2
2
|
import { FieldReference } from '../query-items/field-references';
|
|
3
3
|
import { ExprValue } from '../types/expr-value';
|
|
4
4
|
import { ExpressionDef } from '../types/expression-def';
|
|
5
5
|
import { FieldSpace } from '../types/field-space';
|
|
6
6
|
export declare abstract class ExprAggregateFunction extends ExpressionDef {
|
|
7
|
-
readonly func:
|
|
7
|
+
readonly func: AggregateFunctionType;
|
|
8
8
|
elementType: string;
|
|
9
9
|
source?: FieldReference;
|
|
10
10
|
expr?: ExpressionDef;
|
|
11
11
|
explicitSource?: boolean;
|
|
12
12
|
legalChildTypes: import("../../../model/malloy_types").TypeDesc[];
|
|
13
|
-
constructor(func:
|
|
13
|
+
constructor(func: AggregateFunctionType, expr?: ExpressionDef, explicitSource?: boolean);
|
|
14
14
|
returns(_forExpression: ExprValue): FieldValueType;
|
|
15
15
|
getExpression(fs: FieldSpace): ExprValue;
|
|
16
16
|
isSymmetricFunction(): boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Dialect, DialectFieldList } from '../dialect';
|
|
2
|
-
import { AggregateFragment, CompiledQuery, DialectFragment, Expr, FieldDef, FieldFragment, FieldRef, Filtered, FilterExpression, FilterFragment, FunctionCallFragment, FunctionOverloadDef, JoinRelationship, ModelDef, OrderBy, OutputFieldFragment, Parameter, ParameterFragment, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SpreadFragment, SQLExpressionFragment, StructDef, StructRef, TurtleDef, UngroupFragment } from './malloy_types';
|
|
2
|
+
import { AggregateFragment, AggregateFunctionType, CompiledQuery, DialectFragment, Expr, FieldDef, FieldFragment, FieldRef, Filtered, FilterExpression, FilterFragment, FunctionCallFragment, FunctionOverloadDef, JoinRelationship, ModelDef, OrderBy, OutputFieldFragment, Parameter, ParameterFragment, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SpreadFragment, SQLExpressionFragment, StructDef, StructRef, TurtleDef, UngroupFragment } from './malloy_types';
|
|
3
3
|
import { Connection } from '../runtime_types';
|
|
4
4
|
import { AndChain } from './utils';
|
|
5
5
|
import { QueryInfo } from '../dialect/dialect';
|
|
@@ -17,6 +17,11 @@ interface OutputPipelinedSQL {
|
|
|
17
17
|
sqlFieldName: string;
|
|
18
18
|
pipelineSQL: string;
|
|
19
19
|
}
|
|
20
|
+
type UniqueKeyPossibleUse = AggregateFunctionType | 'generic_aggregate';
|
|
21
|
+
declare class UniqueKeyUse extends Set<UniqueKeyPossibleUse> {
|
|
22
|
+
add_use(k: UniqueKeyPossibleUse | undefined): this | undefined;
|
|
23
|
+
hasAsymetricFunctions(): boolean;
|
|
24
|
+
}
|
|
20
25
|
declare class StageWriter {
|
|
21
26
|
parent: StageWriter | undefined;
|
|
22
27
|
withs: string[];
|
|
@@ -57,7 +62,7 @@ declare class QueryField extends QueryNode {
|
|
|
57
62
|
fieldDef: FieldDef;
|
|
58
63
|
parent: QueryStruct;
|
|
59
64
|
constructor(fieldDef: FieldDef, parent: QueryStruct);
|
|
60
|
-
|
|
65
|
+
uniqueKeyPossibleUse(): UniqueKeyPossibleUse | undefined;
|
|
61
66
|
getJoinableParent(): QueryStruct;
|
|
62
67
|
caseGroup(groupSets: number[], s: string): string;
|
|
63
68
|
getFullOutputName(): string;
|
|
@@ -166,7 +171,7 @@ declare class FieldInstanceResult implements FieldInstance {
|
|
|
166
171
|
structs(): FieldInstanceResult[];
|
|
167
172
|
selectStructs(result: FieldInstanceResult[], fn: (result: FieldInstanceResult) => boolean): FieldInstanceResult[];
|
|
168
173
|
calculateDefaultOrderBy(): OrderBy[];
|
|
169
|
-
addStructToJoin(qs: QueryStruct, query: QueryQuery,
|
|
174
|
+
addStructToJoin(qs: QueryStruct, query: QueryQuery, uniqueKeyPossibleUse: UniqueKeyPossibleUse | undefined, joinStack: string[]): void;
|
|
170
175
|
findJoins(query: QueryQuery): void;
|
|
171
176
|
root(): FieldInstanceResultRoot;
|
|
172
177
|
getUngroupPartitions(ungroupSet: UngroupSet | undefined): FieldInstanceField[];
|
|
@@ -188,7 +193,7 @@ declare class JoinInstance {
|
|
|
188
193
|
queryStruct: QueryStruct;
|
|
189
194
|
alias: string;
|
|
190
195
|
parent: JoinInstance | undefined;
|
|
191
|
-
|
|
196
|
+
uniqueKeyPossibleUses: UniqueKeyUse;
|
|
192
197
|
makeUniqueKey: boolean;
|
|
193
198
|
leafiest: boolean;
|
|
194
199
|
joinFilterConditions?: QueryFieldBoolean[];
|
|
@@ -243,7 +248,7 @@ declare class QueryQuery extends QueryField {
|
|
|
243
248
|
expandDependantField(resultStruct: FieldInstanceResult, fieldRef: FieldRef): void;
|
|
244
249
|
expandWildCardStruct(struct: QueryStruct, expandChildren: boolean, filter?: ((qf: QueryNode) => boolean) | undefined): string[];
|
|
245
250
|
expandWildCards(fields: QueryFieldDef[], filter?: ((qf: QueryNode) => boolean) | undefined): QueryFieldDef[];
|
|
246
|
-
addDependantPath(resultStruct: FieldInstanceResult, context: QueryStruct, path: string,
|
|
251
|
+
addDependantPath(resultStruct: FieldInstanceResult, context: QueryStruct, path: string, uniqueKeyPossibleUse: UniqueKeyPossibleUse | undefined, joinStack: string[]): void;
|
|
247
252
|
addDependantExpr(resultStruct: FieldInstanceResult, context: QueryStruct, e: Expr, joinStack: string[]): void;
|
|
248
253
|
addDependancies(resultStruct: FieldInstanceResult, field: QueryField): void;
|
|
249
254
|
expandFields(resultStruct: FieldInstanceResult): void;
|
|
@@ -34,6 +34,16 @@ function generateSQLStringLiteral(sourceString) {
|
|
|
34
34
|
function identifierNormalize(s) {
|
|
35
35
|
return s.replace(/[^a-zA-Z0-9_]/g, '_o_');
|
|
36
36
|
}
|
|
37
|
+
class UniqueKeyUse extends Set {
|
|
38
|
+
add_use(k) {
|
|
39
|
+
if (k !== undefined) {
|
|
40
|
+
return this.add(k);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
hasAsymetricFunctions() {
|
|
44
|
+
return this.has('sum') || this.has('avg') || this.has('count');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
37
47
|
class StageWriter {
|
|
38
48
|
constructor(useCTE = true, parent) {
|
|
39
49
|
this.parent = parent;
|
|
@@ -173,8 +183,8 @@ class QueryField extends QueryNode {
|
|
|
173
183
|
this.parent = parent;
|
|
174
184
|
this.fieldDef = fieldDef;
|
|
175
185
|
}
|
|
176
|
-
|
|
177
|
-
return
|
|
186
|
+
uniqueKeyPossibleUse() {
|
|
187
|
+
return undefined;
|
|
178
188
|
}
|
|
179
189
|
getJoinableParent() {
|
|
180
190
|
// if it is inline it should always have a parent
|
|
@@ -436,11 +446,28 @@ class QueryField extends QueryNode {
|
|
|
436
446
|
generateCountFragment(resultSet, context, expr, state) {
|
|
437
447
|
let func = 'COUNT(';
|
|
438
448
|
let thing = '1';
|
|
439
|
-
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
thing = distinctKeySQL;
|
|
449
|
+
let struct = context;
|
|
450
|
+
if (expr.structPath) {
|
|
451
|
+
struct = this.parent.root().getStructByName(expr.structPath);
|
|
443
452
|
}
|
|
453
|
+
const joinName = struct.getJoinableParent().getIdentifier();
|
|
454
|
+
const join = resultSet.root().joins.get(joinName);
|
|
455
|
+
if (!join) {
|
|
456
|
+
throw new Error(`Join ${joinName} not found in result set`);
|
|
457
|
+
}
|
|
458
|
+
if (!join.leafiest || join.makeUniqueKey) {
|
|
459
|
+
func = 'COUNT(DISTINCT';
|
|
460
|
+
thing = struct.getDistinctKey().generateExpression(resultSet);
|
|
461
|
+
}
|
|
462
|
+
// const distinctKeySQL = this.generateDistinctKeyIfNecessary(
|
|
463
|
+
// resultSet,
|
|
464
|
+
// context,
|
|
465
|
+
// expr.structPath
|
|
466
|
+
// );
|
|
467
|
+
// if (distinctKeySQL) {
|
|
468
|
+
// func = 'COUNT(DISTINCT';
|
|
469
|
+
// thing = distinctKeySQL;
|
|
470
|
+
// }
|
|
444
471
|
// find the structDef and return the path to the field...
|
|
445
472
|
if (state.whereSQL) {
|
|
446
473
|
return `${func} CASE WHEN ${state.whereSQL} THEN ${thing} END)`;
|
|
@@ -746,7 +773,8 @@ class QueryFieldDistinctKey extends QueryAtomicField {
|
|
|
746
773
|
}
|
|
747
774
|
else if (this.parent.fieldDef.structSource.type === 'nested') {
|
|
748
775
|
const parentKey = (_a = this.parent.parent) === null || _a === void 0 ? void 0 : _a.getDistinctKey().generateExpression(resultSet);
|
|
749
|
-
return
|
|
776
|
+
return this.parent.dialect.concat(parentKey || '', // shouldn't have to do this...
|
|
777
|
+
"'x'", this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(), '__row_id', 'string', true, false));
|
|
750
778
|
}
|
|
751
779
|
else {
|
|
752
780
|
// return this.parent.getIdentifier() + "." + "__distinct_key";
|
|
@@ -1003,7 +1031,7 @@ class FieldInstanceResult {
|
|
|
1003
1031
|
}
|
|
1004
1032
|
return [];
|
|
1005
1033
|
}
|
|
1006
|
-
addStructToJoin(qs, query,
|
|
1034
|
+
addStructToJoin(qs, query, uniqueKeyPossibleUse, joinStack) {
|
|
1007
1035
|
var _a;
|
|
1008
1036
|
const name = qs.getIdentifier();
|
|
1009
1037
|
// we're already chasing the dependency for this join.
|
|
@@ -1012,7 +1040,7 @@ class FieldInstanceResult {
|
|
|
1012
1040
|
}
|
|
1013
1041
|
let join;
|
|
1014
1042
|
if ((join = this.root().joins.get(name))) {
|
|
1015
|
-
join.
|
|
1043
|
+
join.uniqueKeyPossibleUses.add_use(uniqueKeyPossibleUse);
|
|
1016
1044
|
return;
|
|
1017
1045
|
}
|
|
1018
1046
|
// if we have a parent, join it first.
|
|
@@ -1020,7 +1048,7 @@ class FieldInstanceResult {
|
|
|
1020
1048
|
const parentStruct = (_a = qs.parent) === null || _a === void 0 ? void 0 : _a.getJoinableParent();
|
|
1021
1049
|
if (parentStruct) {
|
|
1022
1050
|
// add dependant expressions first...
|
|
1023
|
-
this.addStructToJoin(parentStruct, query,
|
|
1051
|
+
this.addStructToJoin(parentStruct, query, undefined, joinStack);
|
|
1024
1052
|
parent = this.root().joins.get(parentStruct.getIdentifier());
|
|
1025
1053
|
}
|
|
1026
1054
|
// add any dependant joins based on the ON
|
|
@@ -1038,11 +1066,11 @@ class FieldInstanceResult {
|
|
|
1038
1066
|
join = new JoinInstance(qs, name, parent);
|
|
1039
1067
|
this.root().joins.set(name, join);
|
|
1040
1068
|
}
|
|
1041
|
-
join.
|
|
1069
|
+
join.uniqueKeyPossibleUses.add_use(uniqueKeyPossibleUse);
|
|
1042
1070
|
}
|
|
1043
1071
|
findJoins(query) {
|
|
1044
1072
|
for (const dim of this.fields()) {
|
|
1045
|
-
this.addStructToJoin(dim.f.getJoinableParent(), query, dim.f.
|
|
1073
|
+
this.addStructToJoin(dim.f.getJoinableParent(), query, dim.f.uniqueKeyPossibleUse(), []);
|
|
1046
1074
|
}
|
|
1047
1075
|
for (const s of this.structs()) {
|
|
1048
1076
|
s.findJoins(query);
|
|
@@ -1167,8 +1195,20 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
1167
1195
|
// Nested Unique keys are dependant on the primary key of the parent
|
|
1168
1196
|
// and the table.
|
|
1169
1197
|
for (const [_name, join] of this.joins) {
|
|
1170
|
-
//
|
|
1171
|
-
|
|
1198
|
+
// in a one_to_many join we need a key to count there may be a failed
|
|
1199
|
+
// match in a left join.
|
|
1200
|
+
// users -> {
|
|
1201
|
+
// group_by: user_id
|
|
1202
|
+
// aggregate: order_count is orders.count()
|
|
1203
|
+
if (join.leafiest) {
|
|
1204
|
+
if (join.parent !== null &&
|
|
1205
|
+
join.uniqueKeyPossibleUses.has('count') &&
|
|
1206
|
+
!join.queryStruct.primaryKey()) {
|
|
1207
|
+
join.makeUniqueKey = true;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
else if (!join.leafiest &&
|
|
1211
|
+
join.uniqueKeyPossibleUses.hasAsymetricFunctions()) {
|
|
1172
1212
|
let j = join;
|
|
1173
1213
|
while (j) {
|
|
1174
1214
|
if (!j.queryStruct.primaryKey()) {
|
|
@@ -1190,7 +1230,7 @@ class JoinInstance {
|
|
|
1190
1230
|
this.queryStruct = queryStruct;
|
|
1191
1231
|
this.alias = alias;
|
|
1192
1232
|
this.parent = parent;
|
|
1193
|
-
this.
|
|
1233
|
+
this.uniqueKeyPossibleUses = new UniqueKeyUse();
|
|
1194
1234
|
this.makeUniqueKey = false;
|
|
1195
1235
|
this.leafiest = false;
|
|
1196
1236
|
this.children = [];
|
|
@@ -1474,7 +1514,7 @@ class QueryQuery extends QueryField {
|
|
|
1474
1514
|
}
|
|
1475
1515
|
return ret;
|
|
1476
1516
|
}
|
|
1477
|
-
addDependantPath(resultStruct, context, path,
|
|
1517
|
+
addDependantPath(resultStruct, context, path, uniqueKeyPossibleUse, joinStack) {
|
|
1478
1518
|
const node = context.getFieldByName(path);
|
|
1479
1519
|
let struct;
|
|
1480
1520
|
if (node instanceof QueryField) {
|
|
@@ -1488,7 +1528,7 @@ class QueryQuery extends QueryField {
|
|
|
1488
1528
|
}
|
|
1489
1529
|
resultStruct
|
|
1490
1530
|
.root()
|
|
1491
|
-
.addStructToJoin(struct.getJoinableParent(), this,
|
|
1531
|
+
.addStructToJoin(struct.getJoinableParent(), this, uniqueKeyPossibleUse, joinStack);
|
|
1492
1532
|
}
|
|
1493
1533
|
addDependantExpr(resultStruct, context, e, joinStack) {
|
|
1494
1534
|
for (const expr of e) {
|
|
@@ -1523,7 +1563,7 @@ class QueryQuery extends QueryField {
|
|
|
1523
1563
|
else {
|
|
1524
1564
|
resultStruct
|
|
1525
1565
|
.root()
|
|
1526
|
-
.addStructToJoin(field.parent.getJoinableParent(), this,
|
|
1566
|
+
.addStructToJoin(field.parent.getJoinableParent(), this, undefined, joinStack);
|
|
1527
1567
|
// this.addDependantPath(resultStruct, field.parent, expr.path, false);
|
|
1528
1568
|
}
|
|
1529
1569
|
}
|
|
@@ -1572,18 +1612,18 @@ class QueryQuery extends QueryField {
|
|
|
1572
1612
|
else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
|
|
1573
1613
|
if ((0, malloy_types_1.isAsymmetricFragment)(expr)) {
|
|
1574
1614
|
if (expr.structPath) {
|
|
1575
|
-
this.addDependantPath(resultStruct, context, expr.structPath,
|
|
1615
|
+
this.addDependantPath(resultStruct, context, expr.structPath, expr.function, joinStack);
|
|
1576
1616
|
}
|
|
1577
1617
|
else {
|
|
1578
1618
|
// we are doing a sum in the root. It may need symetric aggregates
|
|
1579
|
-
resultStruct.addStructToJoin(context, this,
|
|
1619
|
+
resultStruct.addStructToJoin(context, this, expr.function, joinStack);
|
|
1580
1620
|
}
|
|
1581
1621
|
}
|
|
1582
1622
|
this.addDependantExpr(resultStruct, context, expr.e, joinStack);
|
|
1583
1623
|
}
|
|
1584
1624
|
else if ((0, malloy_types_1.isFunctionCallFragment)(expr)) {
|
|
1585
1625
|
if (expr.structPath) {
|
|
1586
|
-
this.addDependantPath(resultStruct, context, expr.structPath,
|
|
1626
|
+
this.addDependantPath(resultStruct, context, expr.structPath, 'generic_aggregate', joinStack);
|
|
1587
1627
|
}
|
|
1588
1628
|
// TODO Do we need to call `addStructToJoin` here in the case when there is no `structPath`
|
|
1589
1629
|
// and the function is an aggregate function?
|
|
@@ -1688,7 +1728,7 @@ class QueryQuery extends QueryField {
|
|
|
1688
1728
|
prepare(_stageWriter) {
|
|
1689
1729
|
if (!this.prepared) {
|
|
1690
1730
|
this.expandFields(this.rootResult);
|
|
1691
|
-
this.rootResult.addStructToJoin(this.parent, this,
|
|
1731
|
+
this.rootResult.addStructToJoin(this.parent, this, undefined, []);
|
|
1692
1732
|
this.rootResult.findJoins(this);
|
|
1693
1733
|
this.rootResult.calculateSymmetricAggregates();
|
|
1694
1734
|
this.prepared = true;
|
|
@@ -108,9 +108,10 @@ export interface FilterFragment {
|
|
|
108
108
|
}
|
|
109
109
|
export declare function isFilterFragment(f: Fragment): f is FilterFragment;
|
|
110
110
|
export declare function isDialectFragment(f: Fragment): f is DialectFragment;
|
|
111
|
+
export type AggregateFunctionType = 'sum' | 'avg' | 'count' | 'count_distinct' | 'max' | 'min';
|
|
111
112
|
export interface AggregateFragment {
|
|
112
113
|
type: 'aggregate';
|
|
113
|
-
function:
|
|
114
|
+
function: AggregateFunctionType;
|
|
114
115
|
e: Expr;
|
|
115
116
|
structPath?: string;
|
|
116
117
|
}
|