@malloydata/malloy 0.0.218-dev241122201503 → 0.0.218-dev241127170002
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/dialect/dialect.d.ts +2 -2
- package/dist/dialect/duckdb/duckdb.d.ts +2 -2
- package/dist/dialect/duckdb/duckdb.js +17 -2
- package/dist/lang/ast/index.d.ts +0 -1
- package/dist/lang/ast/index.js +0 -1
- package/dist/lang/ast/query-builders/reduce-builder.d.ts +1 -2
- package/dist/lang/ast/query-builders/reduce-builder.js +67 -33
- package/dist/lang/ast/view-elements/refine-utils.js +3 -8
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +0 -1
- package/dist/lang/lib/Malloy/MalloyParser.js +977 -992
- package/dist/lang/malloy-to-ast.d.ts +1 -1
- package/dist/lang/malloy-to-ast.js +1 -18
- package/dist/lang/test/query.spec.js +63 -26
- package/dist/model/malloy_query.d.ts +5 -5
- package/dist/model/malloy_query.js +6 -4
- package/dist/model/malloy_types.d.ts +7 -5
- package/dist/model/malloy_types.js +6 -2
- package/package.json +1 -1
- package/dist/lang/ast/query-properties/top.d.ts +0 -16
- package/dist/lang/ast/query-properties/top.js +0 -76
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Expr, Sampling, AtomicTypeDef, MeasureTimeExpr, TimeTruncExpr, TimeExtractExpr, TimeDeltaExpr, TypecastExpr, RegexMatchExpr, TimeLiteralNode, RecordLiteralNode, ArrayLiteralNode, LeafAtomicTypeDef } from '../model/malloy_types';
|
|
1
|
+
import { Expr, Sampling, AtomicTypeDef, MeasureTimeExpr, TimeTruncExpr, TimeExtractExpr, TimeDeltaExpr, TypecastExpr, RegexMatchExpr, TimeLiteralNode, RecordLiteralNode, ArrayLiteralNode, LeafAtomicTypeDef, OrderBy } from '../model/malloy_types';
|
|
2
2
|
import { DialectFunctionOverloadDef } from './functions';
|
|
3
3
|
type DialectFieldTypes = string | 'struct';
|
|
4
4
|
interface DialectField {
|
|
@@ -80,7 +80,7 @@ export declare abstract class Dialect {
|
|
|
80
80
|
abstract sqlFieldReference(alias: string, fieldName: string, fieldType: string, isNested: boolean, isArray: boolean): string;
|
|
81
81
|
abstract sqlUnnestPipelineHead(isSingleton: boolean, sourceSQLExpression: string, fieldList?: DialectFieldList): string;
|
|
82
82
|
abstract sqlCreateFunction(id: string, funcText: string): string;
|
|
83
|
-
abstract sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList): string;
|
|
83
|
+
abstract sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList, orderBy: OrderBy[] | undefined): string;
|
|
84
84
|
abstract sqlCreateTableAsSelect(tableName: string, sql: string): string;
|
|
85
85
|
abstract sqlSelectAliasAsStruct(alias: string, fieldList: DialectFieldList): string;
|
|
86
86
|
sqlFinalStage(_lastStageName: string, _fields: string[]): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Sampling, AtomicTypeDef, TimeDeltaExpr, RegexMatchExpr, MeasureTimeExpr, LeafAtomicTypeDef } from '../../model/malloy_types';
|
|
1
|
+
import { Sampling, AtomicTypeDef, TimeDeltaExpr, RegexMatchExpr, MeasureTimeExpr, LeafAtomicTypeDef, OrderBy } from '../../model/malloy_types';
|
|
2
2
|
import { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import { DialectFieldList } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
@@ -39,7 +39,7 @@ export declare class DuckDBDialect extends PostgresBase {
|
|
|
39
39
|
sqlFieldReference(alias: string, fieldName: string, _fieldType: string, _isNested: boolean, isArray: boolean): string;
|
|
40
40
|
sqlUnnestPipelineHead(isSingleton: boolean, sourceSQLExpression: string): string;
|
|
41
41
|
sqlCreateFunction(id: string, funcText: string): string;
|
|
42
|
-
sqlCreateFunctionCombineLastStage(lastStageName: string, dialectFieldList: DialectFieldList): string;
|
|
42
|
+
sqlCreateFunctionCombineLastStage(lastStageName: string, dialectFieldList: DialectFieldList, orderBy: OrderBy[] | undefined): string;
|
|
43
43
|
sqlSelectAliasAsStruct(alias: string, dialectFieldList: DialectFieldList): string;
|
|
44
44
|
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
45
45
|
sqlCreateTableAsSelect(_tableName: string, _sql: string): string;
|
|
@@ -185,10 +185,25 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
185
185
|
sqlCreateFunction(id, funcText) {
|
|
186
186
|
return `DROP MACRO IF EXISTS ${id}; \n${hackSplitComment}\n CREATE MACRO ${id}(_param) AS (\n${(0, utils_1.indent)(funcText)}\n);\n${hackSplitComment}\n`;
|
|
187
187
|
}
|
|
188
|
-
sqlCreateFunctionCombineLastStage(lastStageName, dialectFieldList) {
|
|
188
|
+
sqlCreateFunctionCombineLastStage(lastStageName, dialectFieldList, orderBy) {
|
|
189
|
+
let o = '';
|
|
190
|
+
if (orderBy) {
|
|
191
|
+
const clauses = [];
|
|
192
|
+
for (const c of orderBy) {
|
|
193
|
+
if (typeof c.field === 'string') {
|
|
194
|
+
clauses.push(`${c.field} ${c.dir || 'asc'}`);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
clauses.push(`${dialectFieldList[c.field].sqlOutputName} ${c.dir || 'asc'}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (clauses.length > 0) {
|
|
201
|
+
o = ` ORDER BY ${clauses.join(', ')}`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
189
204
|
return `SELECT LIST(STRUCT_PACK(${dialectFieldList
|
|
190
205
|
.map(d => this.sqlMaybeQuoteIdentifier(d.sqlOutputName))
|
|
191
|
-
.join(',')})) FROM ${lastStageName}\n`;
|
|
206
|
+
.join(',')})${o}) FROM ${lastStageName}\n`;
|
|
192
207
|
}
|
|
193
208
|
sqlSelectAliasAsStruct(alias, dialectFieldList) {
|
|
194
209
|
return `STRUCT_PACK(${dialectFieldList
|
package/dist/lang/ast/index.d.ts
CHANGED
|
@@ -91,7 +91,6 @@ export * from './query-properties/ordering';
|
|
|
91
91
|
export * from './query-properties/project-statement';
|
|
92
92
|
export * from './query-properties/qop-desc';
|
|
93
93
|
export * from './query-properties/sampling';
|
|
94
|
-
export * from './query-properties/top';
|
|
95
94
|
export * from './source-elements/named-source';
|
|
96
95
|
export * from './source-elements/query-source';
|
|
97
96
|
export * from './source-elements/sql-source';
|
package/dist/lang/ast/index.js
CHANGED
|
@@ -129,7 +129,6 @@ __exportStar(require("./query-properties/ordering"), exports);
|
|
|
129
129
|
__exportStar(require("./query-properties/project-statement"), exports);
|
|
130
130
|
__exportStar(require("./query-properties/qop-desc"), exports);
|
|
131
131
|
__exportStar(require("./query-properties/sampling"), exports);
|
|
132
|
-
__exportStar(require("./query-properties/top"), exports);
|
|
133
132
|
__exportStar(require("./source-elements/named-source"), exports);
|
|
134
133
|
__exportStar(require("./source-elements/query-source"), exports);
|
|
135
134
|
__exportStar(require("./source-elements/sql-source"), exports);
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { CompositeFieldUsage, FilterCondition, PipeSegment, QuerySegment } from '../../../model/malloy_types';
|
|
2
2
|
import { SourceFieldSpace } from '../types/field-space';
|
|
3
3
|
import { Ordering } from '../query-properties/ordering';
|
|
4
|
-
import { Top } from '../query-properties/top';
|
|
5
4
|
import { QueryProperty } from '../types/query-property';
|
|
6
5
|
import { QueryBuilder } from '../types/query-builder';
|
|
7
6
|
import { QueryOperationSpace, ReduceFieldSpace } from '../field-space/query-spaces';
|
|
8
7
|
import { QueryInputSpace } from '../field-space/query-input-space';
|
|
9
8
|
import { MalloyElement } from '../types/malloy-element';
|
|
10
9
|
export declare abstract class QuerySegmentBuilder implements QueryBuilder {
|
|
11
|
-
order?:
|
|
10
|
+
order?: Ordering;
|
|
12
11
|
limit?: number;
|
|
13
12
|
alwaysJoins: string[];
|
|
14
13
|
abstract inputFS: QueryInputSpace;
|
|
@@ -25,12 +25,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
25
25
|
exports.ReduceBuilder = exports.QuerySegmentBuilder = void 0;
|
|
26
26
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
27
27
|
const error_factory_1 = require("../error-factory");
|
|
28
|
+
const field_space_1 = require("../types/field-space");
|
|
28
29
|
const limit_1 = require("../query-properties/limit");
|
|
29
30
|
const ordering_1 = require("../query-properties/ordering");
|
|
30
|
-
const top_1 = require("../query-properties/top");
|
|
31
31
|
const query_spaces_1 = require("../field-space/query-spaces");
|
|
32
32
|
const definition_list_1 = require("../types/definition-list");
|
|
33
33
|
const composite_source_utils_1 = require("../../../model/composite_source_utils");
|
|
34
|
+
function queryFieldName(qf) {
|
|
35
|
+
if (qf.type === 'fieldref') {
|
|
36
|
+
return qf.path[qf.path.length - 1];
|
|
37
|
+
}
|
|
38
|
+
return qf.name;
|
|
39
|
+
}
|
|
34
40
|
class QuerySegmentBuilder {
|
|
35
41
|
constructor() {
|
|
36
42
|
this.alwaysJoins = [];
|
|
@@ -44,22 +50,6 @@ class QuerySegmentBuilder {
|
|
|
44
50
|
if (qp instanceof definition_list_1.DefinitionList) {
|
|
45
51
|
this.resultFS.pushFields(...qp.list);
|
|
46
52
|
}
|
|
47
|
-
else if (qp instanceof top_1.Top) {
|
|
48
|
-
if (this.limit) {
|
|
49
|
-
qp.logError('limit-already-specified', 'Query operation already limited');
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
this.limit = qp.limit;
|
|
53
|
-
}
|
|
54
|
-
if (qp.by) {
|
|
55
|
-
if (this.order) {
|
|
56
|
-
qp.logError('ordering-already-specified', 'Query operation is already sorted');
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
this.order = qp;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
53
|
else if (qp instanceof limit_1.Limit) {
|
|
64
54
|
if (this.limit) {
|
|
65
55
|
qp.logError('limit-already-specified', 'Query operation already limited');
|
|
@@ -83,30 +73,20 @@ class QuerySegmentBuilder {
|
|
|
83
73
|
refineFrom(from, to) {
|
|
84
74
|
var _a;
|
|
85
75
|
if (from && from.type !== 'index' && from.type !== 'raw') {
|
|
86
|
-
if (!this.
|
|
87
|
-
|
|
88
|
-
to.orderBy = from.orderBy;
|
|
89
|
-
}
|
|
90
|
-
else if (from.by) {
|
|
91
|
-
to.by = from.by;
|
|
92
|
-
}
|
|
76
|
+
if (!this.limit && from.orderBy && !from.defaultOrderBy) {
|
|
77
|
+
to.orderBy = from.orderBy;
|
|
93
78
|
}
|
|
94
79
|
if (!this.limit && from.limit) {
|
|
95
80
|
to.limit = from.limit;
|
|
96
81
|
}
|
|
97
82
|
}
|
|
83
|
+
if (this.order) {
|
|
84
|
+
to.orderBy = this.order.getOrderBy(this.inputFS);
|
|
85
|
+
delete to.defaultOrderBy;
|
|
86
|
+
}
|
|
98
87
|
if (this.limit) {
|
|
99
88
|
to.limit = this.limit;
|
|
100
89
|
}
|
|
101
|
-
if (this.order instanceof top_1.Top) {
|
|
102
|
-
const topBy = this.order.getBy(this.inputFS);
|
|
103
|
-
if (topBy) {
|
|
104
|
-
to.by = topBy;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (this.order instanceof ordering_1.Ordering) {
|
|
108
|
-
to.orderBy = this.order.getOrderBy(this.inputFS);
|
|
109
|
-
}
|
|
110
90
|
const oldFilters = (from === null || from === void 0 ? void 0 : from.filterList) || [];
|
|
111
91
|
if (this.filters.length > 0 && !oldFilters) {
|
|
112
92
|
to.filterList = this.filters;
|
|
@@ -144,6 +124,60 @@ class ReduceBuilder extends QuerySegmentBuilder {
|
|
|
144
124
|
}
|
|
145
125
|
const reduceSegment = this.resultFS.getQuerySegment(from);
|
|
146
126
|
this.refineFrom(from, reduceSegment);
|
|
127
|
+
if (reduceSegment.orderBy) {
|
|
128
|
+
// In the modern world, we will ONLY allow names and not numbers in order by lists
|
|
129
|
+
for (const by of reduceSegment.orderBy) {
|
|
130
|
+
if (typeof by.field === 'number') {
|
|
131
|
+
const by_field = reduceSegment.queryFields[by.field - 1];
|
|
132
|
+
by.field = queryFieldName(by_field);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (reduceSegment.orderBy === undefined || reduceSegment.defaultOrderBy) {
|
|
137
|
+
// In the modern world, we will order all reduce segments with the default ordering
|
|
138
|
+
let usableDefaultOrderField;
|
|
139
|
+
for (const field of reduceSegment.queryFields) {
|
|
140
|
+
let fieldAggregate = false;
|
|
141
|
+
let fieldAnalytic = false;
|
|
142
|
+
let fieldType;
|
|
143
|
+
const fieldName = queryFieldName(field);
|
|
144
|
+
if (field.type === 'fieldref') {
|
|
145
|
+
const lookupPath = field.path.map(el => new field_space_1.FieldName(el));
|
|
146
|
+
const refField = this.inputFS.lookup(lookupPath).found;
|
|
147
|
+
if (refField) {
|
|
148
|
+
const typeDesc = refField.typeDesc();
|
|
149
|
+
fieldType = typeDesc.type;
|
|
150
|
+
fieldAggregate = (0, malloy_types_1.expressionIsAggregate)(typeDesc.expressionType);
|
|
151
|
+
fieldAnalytic = (0, malloy_types_1.expressionIsAnalytic)(typeDesc.expressionType);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
fieldType = field.type;
|
|
159
|
+
fieldAggregate =
|
|
160
|
+
(0, malloy_types_1.hasExpression)(field) && (0, malloy_types_1.expressionIsAggregate)(field.expressionType);
|
|
161
|
+
fieldAnalytic =
|
|
162
|
+
(0, malloy_types_1.hasExpression)(field) && (0, malloy_types_1.expressionIsAnalytic)(field.expressionType);
|
|
163
|
+
}
|
|
164
|
+
if ((0, malloy_types_1.isTemporalField)(fieldType) || fieldAggregate) {
|
|
165
|
+
reduceSegment.defaultOrderBy = true;
|
|
166
|
+
reduceSegment.orderBy = [{ field: fieldName, dir: 'desc' }];
|
|
167
|
+
usableDefaultOrderField = undefined;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
if ((0, malloy_types_1.canOrderBy)(fieldType) &&
|
|
171
|
+
!fieldAnalytic &&
|
|
172
|
+
!usableDefaultOrderField) {
|
|
173
|
+
usableDefaultOrderField = fieldName;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (usableDefaultOrderField) {
|
|
177
|
+
reduceSegment.defaultOrderBy = true;
|
|
178
|
+
reduceSegment.orderBy = [{ field: usableDefaultOrderField, dir: 'asc' }];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
147
181
|
return reduceSegment;
|
|
148
182
|
}
|
|
149
183
|
}
|
|
@@ -48,14 +48,9 @@ function refine(logTo, refineTo, refineFrom) {
|
|
|
48
48
|
logTo.logError('mismatched-view-types-for-refinement', `cannot refine ${to.type} view with ${from.type} view`);
|
|
49
49
|
}
|
|
50
50
|
if (from.type !== 'index' && to.type !== 'index' && from.type !== 'raw') {
|
|
51
|
-
if (from.orderBy !== undefined
|
|
52
|
-
if (to.orderBy === undefined
|
|
53
|
-
|
|
54
|
-
to.orderBy = from.orderBy;
|
|
55
|
-
}
|
|
56
|
-
else if (from.by) {
|
|
57
|
-
to.by = from.by;
|
|
58
|
-
}
|
|
51
|
+
if (from.orderBy !== undefined && !from.defaultOrderBy) {
|
|
52
|
+
if (to.orderBy === undefined || to.defaultOrderBy) {
|
|
53
|
+
to.orderBy = from.orderBy;
|
|
59
54
|
}
|
|
60
55
|
else {
|
|
61
56
|
logTo.logError('ordering-overridden-in-refinement', 'refinement cannot override existing ordering');
|
|
@@ -1712,7 +1712,6 @@ export declare class BySpecContext extends ParserRuleContext {
|
|
|
1712
1712
|
export declare class TopStatementContext extends ParserRuleContext {
|
|
1713
1713
|
TOP(): TerminalNode;
|
|
1714
1714
|
INTEGER_LITERAL(): TerminalNode;
|
|
1715
|
-
bySpec(): BySpecContext | undefined;
|
|
1716
1715
|
constructor(parent: ParserRuleContext | undefined, invokingState: number);
|
|
1717
1716
|
get ruleIndex(): number;
|
|
1718
1717
|
enterRule(listener: MalloyParserListener): void;
|