@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.
@@ -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
@@ -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';
@@ -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?: Top | Ordering;
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.order) {
87
- if (from.orderBy) {
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 || from.by !== undefined) {
52
- if (to.orderBy === undefined && to.by === undefined) {
53
- if (from.orderBy) {
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;