@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.
@@ -140,7 +140,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
140
140
  visitAggregateOrdering(pcx: parse.AggregateOrderingContext): ast.FunctionOrdering;
141
141
  visitOrderBySpec(pcx: parse.OrderBySpecContext): ast.OrderBy;
142
142
  visitOrdering(pcx: parse.OrderingContext): ast.Ordering;
143
- visitTopStatement(pcx: parse.TopStatementContext): ast.Top;
143
+ visitTopStatement(pcx: parse.TopStatementContext): ast.Limit;
144
144
  visitTopLevelQueryDefs(pcx: parse.TopLevelQueryDefsContext): ast.DefineQueryList;
145
145
  visitTopLevelQueryDef(pcx: parse.TopLevelQueryDefContext): ast.DefineQuery;
146
146
  visitAnonymousQuery(pcx: parse.AnonymousQueryContext): ast.AnonymousQuery;
@@ -744,25 +744,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
744
744
  return this.astAt(new ast.Ordering(orderList), pcx);
745
745
  }
746
746
  visitTopStatement(pcx) {
747
- const byCx = pcx.bySpec();
748
747
  const topN = this.getNumber(pcx.INTEGER_LITERAL());
749
- let top;
750
- if (byCx) {
751
- this.m4advisory(byCx, 'top-by', 'by clause of top statement unupported. Use order_by instead');
752
- const nameCx = byCx.fieldName();
753
- if (nameCx) {
754
- const name = this.getFieldName(nameCx);
755
- top = new ast.Top(topN, name);
756
- }
757
- const exprCx = byCx.fieldExpr();
758
- if (exprCx) {
759
- top = new ast.Top(topN, this.getFieldExpr(exprCx));
760
- }
761
- }
762
- if (!top) {
763
- top = new ast.Top(topN, undefined);
764
- }
765
- return this.astAt(top, pcx);
748
+ return this.astAt(new ast.Limit(topN), pcx);
766
749
  }
767
750
  visitTopLevelQueryDefs(pcx) {
768
751
  const stmts = pcx
@@ -784,35 +784,72 @@ describe('query:', () => {
784
784
  test('top N', () => {
785
785
  expect('run: a->{ top: 5; group_by: astr }').toTranslate();
786
786
  });
787
- test('top N by field', () => {
788
- expect(`##! m4warnings=warn
789
- run: a->{top: 5 ${'by astr'}; group_by: astr}`).toLog((0, test_translator_1.warningMessage)('by clause of top statement unupported. Use order_by instead'));
790
- });
791
- test('top N by expression', () => {
792
- expect(`##! m4warnings=warn
793
- run: ab->{top: 5 by ai + 1; group_by: ai}`).toLog((0, test_translator_1.warningMessage)('by clause of top statement unupported. Use order_by instead'));
794
- });
795
787
  test('limit N', () => {
796
788
  expect('run: a->{ limit: 5; group_by: astr }').toTranslate();
797
789
  });
798
- test('order by', () => {
799
- expect('run: a->{ order_by: astr; group_by: astr }').toTranslate();
800
- });
801
- test('order by preserved over refinement', () => {
802
- expect(`
803
- query: a1 is a -> { group_by: astr }
804
- run: a1 + { order_by: astr }
805
- `).toTranslate();
806
- });
807
- test('order by must be in the output space', () => expect('run: a -> { order_by: af; group_by: astr }').toLog((0, test_translator_1.errorMessage)('Unknown field af in output space')));
808
- test('order by asc', () => {
809
- expect('run: a->{ order_by: astr asc; group_by: astr }').toTranslate();
810
- });
811
- test('order by desc', () => {
812
- expect('run: a->{ order_by: astr desc; group_by: astr }').toTranslate();
813
- });
814
- test('order by N', () => {
815
- expect('run: a->{ order_by: 1 asc; group_by: astr }').toTranslate();
790
+ describe('order by variations', () => {
791
+ test('order by', () => {
792
+ expect('run: a->{ order_by: astr; group_by: astr }').toTranslate();
793
+ });
794
+ test('order by preserved over refinement', () => {
795
+ expect(`
796
+ query: a1 is a -> { group_by: astr }
797
+ run: a1 + { order_by: astr }
798
+ `).toTranslate();
799
+ });
800
+ test('order by must be in the output space', () => expect('run: a -> { order_by: af; group_by: astr }').toLog((0, test_translator_1.errorMessage)('Unknown field af in output space')));
801
+ test('order by asc', () => {
802
+ expect('run: a->{ order_by: astr asc; group_by: astr }').toTranslate();
803
+ });
804
+ test('order by desc', () => {
805
+ expect('run: a->{ order_by: astr desc; group_by: astr }').toTranslate();
806
+ });
807
+ test('order by N', () => {
808
+ expect('run: a->{ order_by: 1 asc; group_by: astr }').toTranslate();
809
+ });
810
+ test('first aggregate used for default ordering', () => {
811
+ const m = (0, test_translator_1.model) `run: a->{
812
+ group_by: astr
813
+ aggregate: t is ai.sum()
814
+ }`;
815
+ expect(m).toTranslate();
816
+ const runStmt = m.translator.getQuery(0);
817
+ expect(runStmt).toBeDefined();
818
+ const reduce = runStmt.pipeline[0];
819
+ expect(reduce.type).toEqual('reduce');
820
+ if (reduce.type === 'reduce') {
821
+ expect(reduce.defaultOrderBy).toBeTruthy();
822
+ expect(reduce.orderBy).toEqual([{ field: 't', dir: 'desc' }]);
823
+ }
824
+ });
825
+ test('first temporal used for default ordering', () => {
826
+ const m = (0, test_translator_1.model) `run: a->{
827
+ group_by: astr, ats
828
+ }`;
829
+ expect(m).toTranslate();
830
+ const runStmt = m.translator.getQuery(0);
831
+ expect(runStmt).toBeDefined();
832
+ const reduce = runStmt.pipeline[0];
833
+ expect(reduce.type).toEqual('reduce');
834
+ if (reduce.type === 'reduce') {
835
+ expect(reduce.defaultOrderBy).toBeTruthy();
836
+ expect(reduce.orderBy).toEqual([{ field: 'ats', dir: 'desc' }]);
837
+ }
838
+ });
839
+ test('first used for ordering when appropriate', () => {
840
+ const m = (0, test_translator_1.model) `run: a->{
841
+ group_by: astr, big is upper(astr)
842
+ }`;
843
+ expect(m).toTranslate();
844
+ const runStmt = m.translator.getQuery(0);
845
+ expect(runStmt).toBeDefined();
846
+ const reduce = runStmt.pipeline[0];
847
+ expect(reduce.type).toEqual('reduce');
848
+ if (reduce.type === 'reduce') {
849
+ expect(reduce.defaultOrderBy).toBeTruthy();
850
+ expect(reduce.orderBy).toEqual([{ field: 'astr', dir: 'asc' }]);
851
+ }
852
+ });
816
853
  });
817
854
  test('order by multiple', () => {
818
855
  expect(`
@@ -1,5 +1,5 @@
1
1
  import { Dialect, DialectFieldList } from '../dialect';
2
- import { AggregateFunctionType, Annotation, CompiledQuery, Expr, FieldDef, Filtered, FunctionOverloadDef, FunctionParameterDef, JoinRelationship, ModelDef, OrderBy, OutputFieldNode, ParameterNode, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SegmentFieldDef, StructDef, StructRef, TurtleDef, FunctionOrderBy, Argument, AggregateExpr, FilterCondition, GenericSQLExpr, FieldnameNode, FunctionCallNode, UngroupNode, SourceReferenceNode, SpreadExpr, FilteredExpr, SourceDef, AtomicFieldDef, BooleanFieldDef, QueryToMaterialize, PrepareResultOptions, CaseExpr } from './malloy_types';
2
+ import { AggregateFunctionType, Annotation, CompiledQuery, Expr, FieldDef, Filtered, FunctionOverloadDef, FunctionParameterDef, JoinRelationship, ModelDef, OrderBy, OutputFieldNode, ParameterNode, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SegmentFieldDef, StructDef, StructRef, TurtleDef, FunctionOrderBy, Argument, AggregateExpr, FilterCondition, GenericSQLExpr, FieldnameNode, FunctionCallNode, UngroupNode, SourceReferenceNode, SpreadExpr, FilteredExpr, SourceDef, AtomicFieldDef, BooleanFieldDef, QueryResultDef, QueryToMaterialize, PrepareResultOptions, CaseExpr } from './malloy_types';
3
3
  import { Connection } from '../connection/types';
4
4
  import { AndChain } from './utils';
5
5
  import { QueryInfo } from '../dialect/dialect';
@@ -224,7 +224,7 @@ declare class QueryTurtle extends QueryField {
224
224
  * half translated to the new world of types ..
225
225
  */
226
226
  export declare class Segment {
227
- static nextStructDef(structDef: SourceDef, segment: PipeSegment): SourceDef;
227
+ static nextStructDef(structDef: SourceDef, segment: PipeSegment): QueryResultDef;
228
228
  }
229
229
  type StageGroupMaping = {
230
230
  fromGroup: number;
@@ -267,7 +267,7 @@ declare class QueryQuery extends QueryField {
267
267
  addAlwaysJoins(rootResult: FieldInstanceResultRoot): void;
268
268
  getResultMetadata(fi: FieldInstance): ResultStructMetadataDef | ResultMetadataDef | undefined;
269
269
  /** returns a fields and primary key of a struct for this query */
270
- getResultStructDef(resultStruct?: FieldInstanceResult, isRoot?: boolean): SourceDef;
270
+ getResultStructDef(resultStruct?: FieldInstanceResult, isRoot?: boolean): QueryResultDef;
271
271
  generateSQLJoinBlock(stageWriter: StageWriter, ji: JoinInstance): string;
272
272
  generateSQLPassthroughKeys(qs: QueryStruct): string;
273
273
  generateSQLJoins(stageWriter: StageWriter): string;
@@ -285,14 +285,14 @@ declare class QueryQuery extends QueryField {
285
285
  buildDialectFieldList(resultStruct: FieldInstanceResult): DialectFieldList;
286
286
  generateTurtleSQL(resultStruct: FieldInstanceResult, stageWriter: StageWriter, sqlFieldName: string, outputPipelinedSQL: OutputPipelinedSQL[]): string;
287
287
  generateTurtlePipelineSQL(fi: FieldInstanceResult, stageWriter: StageWriter, sourceSQLExpression: string): {
288
- structDef: SourceDef;
288
+ structDef: QueryResultDef;
289
289
  pipeOut: any;
290
290
  };
291
291
  generateComplexSQL(stageWriter: StageWriter): string;
292
292
  generateSQL(stageWriter: StageWriter): string;
293
293
  generateSQLFromPipeline(stageWriter: StageWriter): {
294
294
  lastStageName: string;
295
- outputStruct: SourceDef;
295
+ outputStruct: QueryResultDef;
296
296
  };
297
297
  }
298
298
  /** Structure object as it is used to build a query */
@@ -96,12 +96,13 @@ class StageWriter {
96
96
  }
97
97
  }
98
98
  addUDF(stageWriter, dialect, structDef) {
99
+ var _a;
99
100
  // eslint-disable-next-line prefer-const
100
101
  let { sql, lastStageName } = stageWriter.combineStages(true);
101
102
  if (lastStageName === undefined) {
102
103
  throw new Error('Internal Error: no stage to combine');
103
104
  }
104
- sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, getDialectFieldList(structDef));
105
+ sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, getDialectFieldList(structDef), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
105
106
  const id = `${dialect.udfPrefix}${this.root().udfs.length}`;
106
107
  sql = dialect.sqlCreateFunction(id, sql);
107
108
  this.root().udfs.push(sql);
@@ -162,12 +163,13 @@ class StageWriter {
162
163
  return udfs + pdts + sql + this.withs[lastStageNum];
163
164
  }
164
165
  generateCoorelatedSubQuery(dialect, structDef) {
166
+ var _a, _b;
165
167
  if (!this.useCTE) {
166
- return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, getDialectFieldList(structDef));
168
+ return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, getDialectFieldList(structDef), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
167
169
  }
168
170
  else {
169
171
  return (this.combineStages(true).sql +
170
- dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), getDialectFieldList(structDef)));
172
+ dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), getDialectFieldList(structDef), (_b = structDef.resultMetadata) === null || _b === void 0 ? void 0 : _b.orderBy));
171
173
  }
172
174
  }
173
175
  }
@@ -2876,7 +2878,7 @@ class QueryQueryRaw extends QueryQuery {
2876
2878
  if (!(0, malloy_types_1.isSourceDef)(this.parent.structDef)) {
2877
2879
  throw new Error(`Result cannot by type ${this.parent.structDef.type}`);
2878
2880
  }
2879
- return this.parent.structDef;
2881
+ return { ...this.parent.structDef, type: 'query_result' };
2880
2882
  }
2881
2883
  getResultMetadata(_fi) {
2882
2884
  return undefined;
@@ -317,9 +317,12 @@ export interface ResultMetadataDef {
317
317
  fieldKind: 'measure' | 'dimension' | 'struct';
318
318
  referenceId?: string;
319
319
  }
320
- export interface ResultStructMetadataDef extends ResultMetadataDef {
321
- limit?: number;
320
+ export interface Ordered {
322
321
  orderBy?: OrderBy[];
322
+ defaultOrderBy?: boolean;
323
+ }
324
+ export interface ResultStructMetadataDef extends ResultMetadataDef, Ordered {
325
+ limit?: number;
323
326
  }
324
327
  export interface ResultMetadata {
325
328
  resultMetadata?: ResultMetadataDef;
@@ -345,6 +348,7 @@ export declare function isTemporalField(s: string): s is TemporalFieldType;
345
348
  export type CastType = 'string' | 'number' | TemporalFieldType | 'boolean' | 'json';
346
349
  export type AtomicFieldType = CastType | 'sql native' | 'record' | 'array' | 'error';
347
350
  export declare function isAtomicFieldType(s: string): s is AtomicFieldType;
351
+ export declare function canOrderBy(s: string): boolean;
348
352
  export declare function isCastType(s: string): s is CastType;
349
353
  /**
350
354
  * Fields which contain scalar data all inherit from this. The field
@@ -550,13 +554,11 @@ export interface CompositeFieldUsage {
550
554
  fields: string[];
551
555
  joinedUsage: Record<string, CompositeFieldUsage>;
552
556
  }
553
- export interface QuerySegment extends Filtered {
557
+ export interface QuerySegment extends Filtered, Ordered {
554
558
  type: 'reduce' | 'project' | 'partial';
555
559
  queryFields: QueryFieldDef[];
556
560
  extendSource?: FieldDef[];
557
561
  limit?: number;
558
- by?: By;
559
- orderBy?: OrderBy[];
560
562
  queryTimezone?: string;
561
563
  alwaysJoins?: string[];
562
564
  compositeFieldUsage?: CompositeFieldUsage;
@@ -22,8 +22,8 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.isScalarArray = exports.isBaseTable = exports.isSourceDef = exports.sourceBase = exports.isSegmentSQL = exports.isIndexSegment = exports.isRawSegment = exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isPartialSegment = exports.isReduceSegment = exports.structHasErrors = exports.segmentHasErrors = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.isJoinedSource = exports.isJoined = exports.isJoinable = exports.isMatrixOperation = exports.isRepeatedRecord = exports.arrayEachFields = exports.fieldIsIntrinsic = exports.isCastType = exports.isAtomicFieldType = exports.isTemporalField = exports.hasExpression = exports.maxOfExpressionTypes = exports.maxExpressionType = exports.isExpressionTypeLEQ = exports.expressionIsAnalytic = exports.expressionIsCalculation = exports.expressionInvolvesAggregate = exports.expressionIsUngroupedAggregate = exports.expressionIsAggregate = exports.expressionIsScalar = exports.paramHasValue = exports.isRawCast = exports.mkTemporal = exports.isAsymmetricExpr = exports.exprIsLeaf = exports.exprHasE = exports.exprHasKids = void 0;
26
- exports.TD = exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = exports.getAtomicFields = exports.isAtomic = exports.isTurtleDef = exports.getIdentifier = exports.isLeafAtomic = exports.mergeEvalSpaces = exports.isLiteral = void 0;
25
+ exports.isBaseTable = exports.isSourceDef = exports.sourceBase = exports.isSegmentSQL = exports.isIndexSegment = exports.isRawSegment = exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isPartialSegment = exports.isReduceSegment = exports.structHasErrors = exports.segmentHasErrors = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.isJoinedSource = exports.isJoined = exports.isJoinable = exports.isMatrixOperation = exports.isRepeatedRecord = exports.arrayEachFields = exports.fieldIsIntrinsic = exports.isCastType = exports.canOrderBy = exports.isAtomicFieldType = exports.isTemporalField = exports.hasExpression = exports.maxOfExpressionTypes = exports.maxExpressionType = exports.isExpressionTypeLEQ = exports.expressionIsAnalytic = exports.expressionIsCalculation = exports.expressionInvolvesAggregate = exports.expressionIsUngroupedAggregate = exports.expressionIsAggregate = exports.expressionIsScalar = exports.paramHasValue = exports.isRawCast = exports.mkTemporal = exports.isAsymmetricExpr = exports.exprIsLeaf = exports.exprHasE = exports.exprHasKids = void 0;
26
+ exports.TD = exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = exports.getAtomicFields = exports.isAtomic = exports.isTurtleDef = exports.getIdentifier = exports.isLeafAtomic = exports.mergeEvalSpaces = exports.isLiteral = exports.isScalarArray = void 0;
27
27
  function exprHasKids(e) {
28
28
  return 'kids' in e;
29
29
  }
@@ -167,6 +167,10 @@ function isAtomicFieldType(s) {
167
167
  ].includes(s);
168
168
  }
169
169
  exports.isAtomicFieldType = isAtomicFieldType;
170
+ function canOrderBy(s) {
171
+ return ['string', 'number', 'date', 'boolean', 'date', 'timestamp'].includes(s);
172
+ }
173
+ exports.canOrderBy = canOrderBy;
170
174
  function isCastType(s) {
171
175
  return ['string', 'number', 'date', 'timestamp', 'boolean', 'json'].includes(s);
172
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.218-dev241122201503",
3
+ "version": "0.0.218-dev241127170002",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -1,16 +0,0 @@
1
- import { By as ModelBy } from '../../../model/malloy_types';
2
- import { ExpressionDef } from '../types/expression-def';
3
- import { FieldName, FieldSpace } from '../types/field-space';
4
- import { MalloyElement } from '../types/malloy-element';
5
- import { LegalRefinementStage, QueryPropertyInterface } from '../types/query-property-interface';
6
- type TopInit = FieldName | ExpressionDef;
7
- export declare class Top extends MalloyElement implements QueryPropertyInterface {
8
- readonly limit: number;
9
- readonly by?: TopInit | undefined;
10
- elementType: string;
11
- queryRefinementStage: LegalRefinementStage;
12
- forceQueryClass: undefined;
13
- constructor(limit: number, by?: TopInit | undefined);
14
- getBy(fs: FieldSpace): ModelBy | undefined;
15
- }
16
- export {};
@@ -1,76 +0,0 @@
1
- "use strict";
2
- /*
3
- * Copyright 2023 Google LLC
4
- *
5
- * Permission is hereby granted, free of charge, to any person obtaining
6
- * a copy of this software and associated documentation files
7
- * (the "Software"), to deal in the Software without restriction,
8
- * including without limitation the rights to use, copy, modify, merge,
9
- * publish, distribute, sublicense, and/or sell copies of the Software,
10
- * and to permit persons to whom the Software is furnished to do so,
11
- * subject to the following conditions:
12
- *
13
- * The above copyright notice and this permission notice shall be
14
- * included in all copies or substantial portions of the Software.
15
- *
16
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
- * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
- */
24
- Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.Top = void 0;
26
- const malloy_types_1 = require("../../../model/malloy_types");
27
- const field_space_1 = require("../types/field-space");
28
- const malloy_element_1 = require("../types/malloy-element");
29
- const query_property_interface_1 = require("../types/query-property-interface");
30
- class Top extends malloy_element_1.MalloyElement {
31
- constructor(limit, by) {
32
- super();
33
- this.limit = limit;
34
- this.by = by;
35
- this.elementType = 'top';
36
- this.queryRefinementStage = query_property_interface_1.LegalRefinementStage.Tail;
37
- this.forceQueryClass = undefined;
38
- this.has({ by: by });
39
- }
40
- getBy(fs) {
41
- var _a;
42
- if (this.by) {
43
- if (this.by instanceof field_space_1.FieldName) {
44
- if (fs.isQueryFieldSpace()) {
45
- // TODO jump-to-definition now that we can lookup fields in the output space,
46
- // we need to actually add the reference when we do so.
47
- const output = fs.outputSpace();
48
- const entry = this.by.getField(output);
49
- if (entry.error) {
50
- this.by.logError(entry.error.code, entry.error.message);
51
- }
52
- if (!entry.found || !entry.isOutputField) {
53
- this.by.logError('top-by-not-found-in-output', `Unknown field ${this.by.refString} in output space`);
54
- }
55
- if ((0, malloy_types_1.expressionIsAnalytic)((_a = entry.found) === null || _a === void 0 ? void 0 : _a.typeDesc().expressionType)) {
56
- this.by.logError('top-by-analytic', `Illegal order by of analytic field ${this.by.refString}`);
57
- }
58
- }
59
- return { by: 'name', name: this.by.refString };
60
- }
61
- else {
62
- const byExpr = this.by.getExpression(fs);
63
- if ((0, malloy_types_1.expressionIsAggregate)(byExpr.expressionType)) {
64
- this.by.logError('top-by-aggregate', 'top by expression must not be an aggregate');
65
- }
66
- if (byExpr.evalSpace === 'output') {
67
- this.by.logError('top-by-not-in-output', 'top by expression must be an output expression');
68
- }
69
- return { by: 'expression', e: byExpr.value };
70
- }
71
- }
72
- return undefined;
73
- }
74
- }
75
- exports.Top = Top;
76
- //# sourceMappingURL=top.js.map