@malloydata/malloy 0.0.240-dev250311155207 → 0.0.240-dev250311202829

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.
@@ -121,7 +121,7 @@ async function compileModel(request, fetchers) {
121
121
  // eslint-disable-next-line no-constant-condition
122
122
  while (true) {
123
123
  const result = Core.statedCompileModel(state);
124
- if (result.model) {
124
+ if (result.model || Core.hasErrors(result.logs)) {
125
125
  return result;
126
126
  }
127
127
  const needs = await fetchNeeds(result.compiler_needs, fetchers);
@@ -134,7 +134,7 @@ async function compileSource(request, fetchers) {
134
134
  // eslint-disable-next-line no-constant-condition
135
135
  while (true) {
136
136
  const result = Core.statedCompileSource(state, request.name);
137
- if (result.source) {
137
+ if (result.source || Core.hasErrors(result.logs)) {
138
138
  return result;
139
139
  }
140
140
  const needs = await fetchNeeds(result.compiler_needs, fetchers);
@@ -147,7 +147,7 @@ async function compileQuery(request, fetchers) {
147
147
  // eslint-disable-next-line no-constant-condition
148
148
  while (true) {
149
149
  const result = Core.statedCompileQuery(state);
150
- if (result.result) {
150
+ if (result.result || Core.hasErrors(result.logs)) {
151
151
  return result;
152
152
  }
153
153
  const needs = await fetchNeeds(result.compiler_needs, fetchers);
@@ -177,7 +177,7 @@ async function runQuery(request, fetchers) {
177
177
  }
178
178
  try {
179
179
  const connection = await fetchers.connections.lookupConnection(compiled.result.connection_name);
180
- const data = await connection.runSQL(compiled.result.sql);
180
+ const data = await connection.runSQL(compiled.result.sql, compiled.result.schema);
181
181
  return {
182
182
  ...compiled,
183
183
  result: {
@@ -146,13 +146,15 @@ ORDER BY 1 asc NULLS LAST
146
146
  describe('run query', () => {
147
147
  test('run query with table dependency', async () => {
148
148
  const data = {
149
- kind: 'table',
150
- rows: [
149
+ kind: 'array_cell',
150
+ array_value: [
151
151
  {
152
- cells: [{ kind: 'string_cell', string_value: 'WN' }],
152
+ kind: 'record_cell',
153
+ record_value: [{ kind: 'string_cell', string_value: 'WN' }],
153
154
  },
154
155
  {
155
- cells: [{ kind: 'string_cell', string_value: 'AA' }],
156
+ kind: 'record_cell',
157
+ record_value: [{ kind: 'string_cell', string_value: 'AA' }],
156
158
  },
157
159
  ],
158
160
  };
@@ -5,7 +5,7 @@ export interface InfoConnection {
5
5
  get dialectName(): string;
6
6
  }
7
7
  export interface Connection extends InfoConnection {
8
- runSQL(sql: string): Promise<Malloy.Data>;
8
+ runSQL(sql: string, schema: Malloy.Schema): Promise<Malloy.Data>;
9
9
  }
10
10
  export interface LookupConnection<T extends InfoConnection> {
11
11
  lookupConnection(connectionName?: string): Promise<T>;
@@ -33,5 +33,6 @@ export declare function _statedCompileModel(state: CompileModelState): CompileRe
33
33
  export declare const DEFAULT_LOG_RANGE: Malloy.DocumentRange;
34
34
  export declare function compileModel(request: Malloy.CompileModelRequest, state?: CompileModelState): Malloy.CompileModelResponse;
35
35
  export declare function compileSource(request: Malloy.CompileSourceRequest): Malloy.CompileSourceResponse;
36
+ export declare function hasErrors(log: Malloy.LogMessage[] | undefined): boolean;
36
37
  export declare function newCompileQueryState(request: Malloy.CompileQueryRequest): CompileModelState;
37
38
  export declare function statedCompileQuery(state: CompileModelState): Malloy.CompileQueryResponse;
package/dist/api/core.js CHANGED
@@ -29,12 +29,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
29
29
  return result;
30
30
  };
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.statedCompileQuery = exports.newCompileQueryState = exports.compileSource = exports.compileModel = exports.DEFAULT_LOG_RANGE = exports._statedCompileModel = exports.statedCompileSource = exports.statedCompileModel = exports.newCompileSourceState = exports.newCompileModelState = exports.updateCompileModelState = exports.compileQuery = void 0;
32
+ exports.statedCompileQuery = exports.newCompileQueryState = exports.hasErrors = exports.compileSource = exports.compileModel = exports.DEFAULT_LOG_RANGE = exports._statedCompileModel = exports.statedCompileSource = exports.statedCompileModel = exports.newCompileSourceState = exports.newCompileModelState = exports.updateCompileModelState = exports.compileQuery = void 0;
33
33
  const Malloy = __importStar(require("@malloydata/malloy-interfaces"));
34
34
  const lang_1 = require("../lang");
35
35
  const model_1 = require("../model");
36
36
  const to_stable_1 = require("../to_stable");
37
37
  const sql_block_1 = require("../model/sql_block");
38
+ const annotation_1 = require("../annotation");
39
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
38
40
  // TODO find where this should go...
39
41
  function tableKey(connectionName, tablePath) {
40
42
  return `${connectionName}:${tablePath}`;
@@ -371,6 +373,11 @@ function extractSource(result, name, defaultURL) {
371
373
  return { compiler_needs: result.compilerNeeds, logs };
372
374
  }
373
375
  }
376
+ function hasErrors(log) {
377
+ var _a;
378
+ return (_a = log === null || log === void 0 ? void 0 : log.some(m => m.severity === 'error')) !== null && _a !== void 0 ? _a : false;
379
+ }
380
+ exports.hasErrors = hasErrors;
374
381
  // Given a StableQueryDef and the URL to a model, run it and return a StableResult
375
382
  // Given a StableQueryDef and the URL to a model, compile it and return a StableResultDef
376
383
  // Given a StableQueryDef and the URL to a model, validate it and return a list of StableErrors
@@ -393,6 +400,7 @@ function newCompileQueryState(request) {
393
400
  }
394
401
  exports.newCompileQueryState = newCompileQueryState;
395
402
  function statedCompileQuery(state) {
403
+ var _a;
396
404
  const result = _statedCompileModel(state);
397
405
  // TODO this can expose the internal URL... is there a better way to handle URL-less errors from the compiler?
398
406
  const defaultURL = state.translator.sourceURL;
@@ -415,14 +423,33 @@ function statedCompileQuery(state) {
415
423
  const index = queries.length - 1;
416
424
  const query = result.modelDef.queryList[index];
417
425
  const schema = result.model.anonymous_queries[index].schema;
426
+ const annotations = (_a = result.model.anonymous_queries[index].annotations) !== null && _a !== void 0 ? _a : [];
418
427
  try {
419
428
  const queryModel = new model_1.QueryModel(result.modelDef);
420
429
  const translatedQuery = queryModel.compileQuery(query);
430
+ const modelAnnotations = (0, annotation_1.annotationToTaglines)(result.modelDef.annotation).map(l => ({
431
+ value: l,
432
+ }));
433
+ annotations.push({
434
+ value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
435
+ .set(['source_name'], translatedQuery.sourceExplore)
436
+ .toString(),
437
+ });
438
+ if (translatedQuery.queryName) {
439
+ annotations.push({
440
+ value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
441
+ .set(['query_name'], translatedQuery.queryName)
442
+ .toString(),
443
+ });
444
+ }
421
445
  return {
422
446
  result: {
423
447
  sql: translatedQuery.sql,
424
448
  schema,
425
449
  connection_name: translatedQuery.connectionName,
450
+ annotations: annotations.length > 0 ? annotations : undefined,
451
+ model_annotations: modelAnnotations.length > 0 ? modelAnnotations : undefined,
452
+ query_timezone: translatedQuery.queryTimezone,
426
453
  },
427
454
  };
428
455
  }
@@ -1,3 +1,5 @@
1
1
  export * as sessioned from './sessioned';
2
2
  export * as stateless from './stateless';
3
3
  export * as asynchronous from './asynchronous';
4
+ export * from './connection';
5
+ export * as util from './util';
package/dist/api/index.js CHANGED
@@ -22,8 +22,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.asynchronous = exports.stateless = exports.sessioned = void 0;
29
+ exports.util = exports.asynchronous = exports.stateless = exports.sessioned = void 0;
27
30
  /*
28
31
  * Copyright (c) Meta Platforms, Inc. and affiliates.
29
32
  *
@@ -33,4 +36,6 @@ exports.asynchronous = exports.stateless = exports.sessioned = void 0;
33
36
  exports.sessioned = __importStar(require("./sessioned"));
34
37
  exports.stateless = __importStar(require("./stateless"));
35
38
  exports.asynchronous = __importStar(require("./asynchronous"));
39
+ __exportStar(require("./connection"), exports);
40
+ exports.util = __importStar(require("./util"));
36
41
  //# sourceMappingURL=index.js.map
@@ -131,8 +131,7 @@ class SessionManager {
131
131
  this.sessions.delete(sessionId);
132
132
  }
133
133
  hasErrors(log) {
134
- var _a;
135
- return (_a = log === null || log === void 0 ? void 0 : log.some(m => m.severity === 'error')) !== null && _a !== void 0 ? _a : false;
134
+ return Core.hasErrors(log);
136
135
  }
137
136
  compileModel(request, options) {
138
137
  const sessionInfo = {
@@ -269,6 +269,7 @@ describe('api', () => {
269
269
  const result = (0, stateless_1.compileQuery)({
270
270
  model_url: 'file://test.malloy',
271
271
  query: {
272
+ annotations: [{ value: '#(test) hello' }],
272
273
  definition: {
273
274
  kind: 'arrow',
274
275
  source_reference: { name: 'flights' },
@@ -313,6 +314,11 @@ describe('api', () => {
313
314
  const expected = {
314
315
  result: {
315
316
  connection_name: 'connection',
317
+ annotations: [
318
+ { value: '#(test) hello\n' },
319
+ { value: '#(malloy) ordered_by = [{ carrier = asc }]\n' },
320
+ { value: '#(malloy) source_name = flights\n' },
321
+ ],
316
322
  sql: `SELECT \n\
317
323
  base."carrier" as "carrier"
318
324
  FROM flights as base
@@ -0,0 +1,9 @@
1
+ import { InfoConnection as LegacyInfoConnection, Connection as LegacyConnection } from '../connection';
2
+ import { Result } from '../malloy';
3
+ import { QueryData } from '../model';
4
+ import { Connection, InfoConnection } from './connection';
5
+ import * as Malloy from '@malloydata/malloy-interfaces';
6
+ export declare function wrapLegacyInfoConnection(connection: LegacyInfoConnection): InfoConnection;
7
+ export declare function wrapLegacyConnection(connection: LegacyConnection): Connection;
8
+ export declare function mapData(data: QueryData, schema: Malloy.Schema): Malloy.Data;
9
+ export declare function wrapResult(result: Result): Malloy.Result;
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.wrapResult = exports.mapData = exports.wrapLegacyConnection = exports.wrapLegacyInfoConnection = void 0;
10
+ const malloy_tag_1 = require("@malloydata/malloy-tag");
11
+ const annotation_1 = require("../annotation");
12
+ const to_stable_1 = require("../to_stable");
13
+ const luxon_1 = require("luxon");
14
+ function wrapLegacyInfoConnection(connection) {
15
+ return {
16
+ get dialectName() {
17
+ return connection.dialectName;
18
+ },
19
+ async fetchSchemaForSQLQuery(sql) {
20
+ const result = await connection.fetchSchemaForSQLStruct({ connection: connection.name, selectStr: sql }, {});
21
+ const table = result.structDef;
22
+ if (table === undefined) {
23
+ throw new Error(result.error);
24
+ }
25
+ return {
26
+ fields: (0, to_stable_1.convertFieldInfos)(table, table.fields),
27
+ };
28
+ },
29
+ async fetchSchemaForTable(tableName) {
30
+ const key = `${connection.name}:${tableName}`;
31
+ const result = await connection.fetchSchemaForTables({ [key]: tableName }, {});
32
+ const table = result.schemas[key];
33
+ if (table === undefined) {
34
+ throw new Error(result.errors[key]);
35
+ }
36
+ return {
37
+ fields: (0, to_stable_1.convertFieldInfos)(table, table.fields),
38
+ };
39
+ },
40
+ };
41
+ }
42
+ exports.wrapLegacyInfoConnection = wrapLegacyInfoConnection;
43
+ function wrapLegacyConnection(connection) {
44
+ return {
45
+ ...wrapLegacyInfoConnection(connection),
46
+ runSQL: async (sql, schema) => {
47
+ const result = await connection.runSQL(sql);
48
+ return mapData(result.rows, schema);
49
+ },
50
+ };
51
+ }
52
+ exports.wrapLegacyConnection = wrapLegacyConnection;
53
+ function valueToDate(value) {
54
+ // TODO properly map the data from BQ/Postgres types
55
+ if (value instanceof Date) {
56
+ return value;
57
+ }
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ const valAsAny = value;
60
+ if (valAsAny.constructor.name === 'Date') {
61
+ // For some reason duckdb TSTZ values come back as objects which do not
62
+ // pass "instance of" but do seem date like.
63
+ return new Date(value);
64
+ }
65
+ else if (typeof value === 'number') {
66
+ return new Date(value);
67
+ }
68
+ else if (typeof value !== 'string') {
69
+ return new Date(value.value);
70
+ }
71
+ else {
72
+ // Postgres timestamps end up here, and ideally we would know the system
73
+ // timezone of the postgres instance to correctly create a Date() object
74
+ // which represents the same instant in time, but we don't have the data
75
+ // flow to implement that. This may be a problem at some future day,
76
+ // so here is a comment, for that day.
77
+ let parsed = luxon_1.DateTime.fromISO(value, { zone: 'UTC' });
78
+ if (!parsed.isValid) {
79
+ parsed = luxon_1.DateTime.fromSQL(value, { zone: 'UTC' });
80
+ }
81
+ return parsed.toJSDate();
82
+ }
83
+ }
84
+ function mapData(data, schema) {
85
+ function mapValue(value, field) {
86
+ if (value === null) {
87
+ return { kind: 'null_cell' };
88
+ }
89
+ else if (field.type.kind === 'date_type' ||
90
+ field.type.kind === 'timestamp_type') {
91
+ const time_value = valueToDate(value).toISOString();
92
+ if (field.type.kind === 'date_type') {
93
+ return { kind: 'date_cell', date_value: time_value };
94
+ }
95
+ else {
96
+ return { kind: 'timestamp_cell', timestamp_value: time_value };
97
+ }
98
+ }
99
+ else if (field.type.kind === 'boolean_type') {
100
+ if (typeof value === 'number') {
101
+ return { kind: 'boolean_cell', boolean_value: value !== 0 };
102
+ }
103
+ if (typeof value !== 'boolean') {
104
+ throw new Error(`Invalid boolean ${value}`);
105
+ }
106
+ return { kind: 'boolean_cell', boolean_value: value };
107
+ }
108
+ else if (field.type.kind === 'number_type') {
109
+ if (typeof value !== 'number') {
110
+ throw new Error(`Invalid number ${value}`);
111
+ }
112
+ return { kind: 'number_cell', number_value: value };
113
+ }
114
+ else if (field.type.kind === 'string_type') {
115
+ if (typeof value !== 'string') {
116
+ throw new Error(`Invalid string ${value}`);
117
+ }
118
+ return { kind: 'string_cell', string_value: value };
119
+ }
120
+ else if (field.type.kind === 'array_type') {
121
+ if (!Array.isArray(value)) {
122
+ throw new Error(`Invalid array ${value}`);
123
+ }
124
+ return {
125
+ kind: 'array_cell',
126
+ array_value: value.map(value => mapValue(value, {
127
+ name: 'array_element',
128
+ type: field.type.element_type,
129
+ })),
130
+ };
131
+ }
132
+ else if (field.type.kind === 'json_type') {
133
+ return { kind: 'json_cell', json_value: JSON.stringify(value) };
134
+ }
135
+ else if (field.type.kind === 'sql_native_type') {
136
+ return { kind: 'sql_native_cell', sql_native_value: JSON.stringify(value) };
137
+ }
138
+ else {
139
+ const type = field.type;
140
+ if (type.kind !== 'record_type') {
141
+ throw new Error(`Invalid record in result ${JSON.stringify(field)}, ${JSON.stringify(value)}`);
142
+ }
143
+ return mapRow(value, {
144
+ kind: 'join',
145
+ relationship: 'many',
146
+ name: 'array_element',
147
+ schema: {
148
+ fields: type.fields.map(f => ({ kind: 'dimension', ...f })),
149
+ },
150
+ });
151
+ }
152
+ }
153
+ function mapRow(row, field) {
154
+ const cells = [];
155
+ for (const f of field.schema.fields) {
156
+ const value = row[f.name];
157
+ if (f.kind !== 'dimension') {
158
+ throw new Error('Invalid result -- expected all fields to be dimensions');
159
+ }
160
+ const cell = mapValue(value, f);
161
+ cells.push(cell);
162
+ }
163
+ return {
164
+ kind: 'record_cell',
165
+ record_value: cells,
166
+ };
167
+ }
168
+ const rootField = {
169
+ kind: 'join',
170
+ schema,
171
+ name: 'root',
172
+ relationship: 'one',
173
+ };
174
+ return {
175
+ kind: 'array_cell',
176
+ array_value: data.map(row => mapRow(row, rootField)),
177
+ };
178
+ }
179
+ exports.mapData = mapData;
180
+ function wrapResult(result) {
181
+ const structs = result._queryResult.structs;
182
+ const struct = structs[structs.length - 1];
183
+ const schema = { fields: (0, to_stable_1.convertFieldInfos)(struct, struct.fields) };
184
+ const annotations = (0, annotation_1.annotationToTaglines)(result.annotation).map(l => ({
185
+ value: l,
186
+ }));
187
+ const metadataAnnot = struct.resultMetadata
188
+ ? (0, to_stable_1.getResultStructMetadataAnnotation)(struct, struct.resultMetadata)
189
+ : undefined;
190
+ if (metadataAnnot) {
191
+ annotations.push(metadataAnnot);
192
+ }
193
+ annotations.push(...(struct.resultMetadata ? [] : []));
194
+ if (result.sourceExplore) {
195
+ annotations.push({
196
+ value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
197
+ .set(['source_name'], result.sourceExplore.name)
198
+ .toString(),
199
+ });
200
+ }
201
+ annotations.push({
202
+ value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
203
+ .set(['query_name'], result.resultExplore.name)
204
+ .toString(),
205
+ });
206
+ return {
207
+ schema,
208
+ data: mapData(result.data.toObject(), schema),
209
+ connection_name: result.connectionName,
210
+ annotations: annotations.length > 0 ? annotations : undefined,
211
+ query_timezone: result.data.field.queryTimezone,
212
+ };
213
+ }
214
+ exports.wrapResult = wrapResult;
215
+ //# sourceMappingURL=util.js.map
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { isSourceDef, Segment, isLeafAtomic, isJoined, isJoinedSource, isSamplin
5
5
  export { MalloyTranslator, } from './lang';
6
6
  export type { LogMessage, TranslateResponse } from './lang';
7
7
  export { Model, Malloy, Runtime, AtomicFieldType, ConnectionRuntime, SingleConnectionRuntime, EmptyURLReader, InMemoryURLReader, FixedConnectionMap, MalloyError, JoinRelationship, SourceRelationship, DateTimeframe, TimestampTimeframe, PreparedResult, Result, QueryMaterializer, CSVWriter, JSONWriter, Parse, DataWriter, Explore, InMemoryModelCache, CacheManager, } from './malloy';
8
- export type { PreparedQuery, Field, AtomicField, ExploreField, QueryField, SortableField, DataArray, DataRecord, DataColumn, DataArrayOrRecord, Loggable, ModelMaterializer, DocumentTablePath, DocumentSymbol, ResultJSON, PreparedResultMaterializer, ExploreMaterializer, WriteStream, SerializedExplore, ModelCache, CachedModel, DateField, TimestampField, } from './malloy';
8
+ export type { PreparedQuery, Field, AtomicField, ExploreField, QueryField, SortableField, DataArray, DataRecord, DataColumn, DataArrayOrRecord, Loggable, ModelMaterializer, DocumentTablePath, DocumentSymbol, ResultJSON, PreparedResultJSON, PreparedResultMaterializer, ExploreMaterializer, WriteStream, SerializedExplore, ModelCache, CachedModel, DateField, TimestampField, } from './malloy';
9
9
  export type { QueryOptionsReader, RunSQLOptions } from './run_sql_options';
10
10
  export type { EventStream, ModelString, ModelURL, QueryString, QueryURL, URLReader, InvalidationKey, } from './runtime_types';
11
11
  export type { Connection, ConnectionConfig, ConnectionFactory, ConnectionParameter, ConnectionParameterValue, ConnectionConfigSchema, FetchSchemaOptions, InfoConnection, LookupConnection, PersistSQLResults, PooledConnection, TestableConnection, StreamingConnection, } from './connection/types';
package/dist/malloy.d.ts CHANGED
@@ -482,7 +482,7 @@ export declare class PreparedResult implements Taggable {
482
482
  * @return The `Explore` representing the data that will be returned by running this query.
483
483
  */
484
484
  get resultExplore(): Explore;
485
- get sourceExplore(): Explore;
485
+ get sourceExplore(): Explore | undefined;
486
486
  get _sourceExploreName(): string;
487
487
  get _sourceFilters(): FilterCondition[];
488
488
  }
package/dist/malloy.js CHANGED
@@ -864,13 +864,9 @@ class PreparedResult {
864
864
  get sourceExplore() {
865
865
  const name = this.inner.sourceExplore;
866
866
  const explore = this.modelDef.contents[name];
867
- if (explore === undefined) {
868
- throw new Error('Malformed query result.');
869
- }
870
- if ((0, model_1.isSourceDef)(explore)) {
867
+ if (explore && (0, model_1.isSourceDef)(explore)) {
871
868
  return new Explore(explore);
872
869
  }
873
- throw new Error(`'${name} is not an explore`);
874
870
  }
875
871
  get _sourceExploreName() {
876
872
  return this.inner.sourceExplore;
@@ -289,6 +289,7 @@ declare class QueryQuery extends QueryField {
289
289
  generateTurtlePipelineSQL(fi: FieldInstanceResult, stageWriter: StageWriter, sourceSQLExpression: string): {
290
290
  structDef: QueryResultDef;
291
291
  pipeOut: any;
292
+ repeatedResultType: RepeatedResultType;
292
293
  };
293
294
  generateComplexSQL(stageWriter: StageWriter): string;
294
295
  generateSQL(stageWriter: StageWriter): string;
@@ -1934,8 +1934,8 @@ class QueryQuery extends QueryField {
1934
1934
  for (const [name, fi] of resultStruct.allFields) {
1935
1935
  const resultMetadata = this.getResultMetadata(fi);
1936
1936
  if (fi instanceof FieldInstanceResult) {
1937
- const { structDef } = this.generateTurtlePipelineSQL(fi, new StageWriter(true, undefined), '<nosource>');
1938
- if (fi.getRepeatedResultType() === 'nested') {
1937
+ const { structDef, repeatedResultType } = this.generateTurtlePipelineSQL(fi, new StageWriter(true, undefined), '<nosource>');
1938
+ if (repeatedResultType === 'nested') {
1939
1939
  const multiLineNest = {
1940
1940
  ...structDef,
1941
1941
  type: 'array',
@@ -2716,6 +2716,7 @@ class QueryQuery extends QueryField {
2716
2716
  const repeatedResultType = fi.getRepeatedResultType();
2717
2717
  const hasPipeline = fi.turtleDef.pipeline.length > 1;
2718
2718
  let pipeOut;
2719
+ let outputRepeatedResultType = repeatedResultType;
2719
2720
  if (hasPipeline) {
2720
2721
  const pipeline = [...fi.turtleDef.pipeline];
2721
2722
  pipeline.shift();
@@ -2735,6 +2736,7 @@ class QueryQuery extends QueryField {
2735
2736
  const qs = new QueryStruct(inputStruct, undefined, { model: this.parent.getModel() }, this.parent.prepareResultOptions);
2736
2737
  const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter, this.isJoinedSubquery);
2737
2738
  pipeOut = q.generateSQLFromPipeline(stageWriter);
2739
+ outputRepeatedResultType = q.rootResult.getRepeatedResultType();
2738
2740
  // console.log(stageWriter.generateSQLStages());
2739
2741
  structDef = pipeOut.outputStruct;
2740
2742
  }
@@ -2742,6 +2744,7 @@ class QueryQuery extends QueryField {
2742
2744
  return {
2743
2745
  structDef,
2744
2746
  pipeOut,
2747
+ repeatedResultType: outputRepeatedResultType,
2745
2748
  };
2746
2749
  }
2747
2750
  generateComplexSQL(stageWriter) {
@@ -3706,6 +3709,7 @@ class QueryModel {
3706
3709
  queryName: query.name,
3707
3710
  connectionName: ret.connectionName,
3708
3711
  annotation: query.annotation,
3712
+ queryTimezone: ret.structs[0].queryTimezone,
3709
3713
  };
3710
3714
  }
3711
3715
  async searchIndex(connection, explore, searchValue, limit = 1000, searchField = undefined) {
@@ -1,4 +1,5 @@
1
1
  import * as Malloy from '@malloydata/malloy-interfaces';
2
- import { FieldDef, ModelDef, SourceDef } from './model';
2
+ import { FieldDef, ModelDef, ResultStructMetadataDef, SourceDef } from './model';
3
3
  export declare function modelDefToModelInfo(modelDef: ModelDef): Malloy.ModelInfo;
4
4
  export declare function convertFieldInfos(source: SourceDef, fields: FieldDef[]): Malloy.FieldInfo[];
5
+ export declare function getResultStructMetadataAnnotation(field: SourceDef, resultMetadata: ResultStructMetadataDef): Malloy.Annotation | undefined;
package/dist/to_stable.js CHANGED
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
2
8
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.convertFieldInfos = exports.modelDefToModelInfo = void 0;
9
+ exports.getResultStructMetadataAnnotation = exports.convertFieldInfos = exports.modelDefToModelInfo = void 0;
4
10
  const model_1 = require("./model");
5
11
  const malloy_query_1 = require("./model/malloy_query");
6
12
  const annotation_1 = require("./annotation");
@@ -146,6 +152,13 @@ function getResultMetadataAnnotation(field, resultMetadata) {
146
152
  tag.set(['calculation']);
147
153
  hasAny = true;
148
154
  }
155
+ if (resultMetadata.filterList) {
156
+ const drillFilters = resultMetadata.filterList
157
+ .filter(f => f.expressionType === 'scalar')
158
+ .map(f => f.code);
159
+ tag.set(['drill_filters'], drillFilters);
160
+ hasAny = true;
161
+ }
149
162
  if (resultMetadata.fieldKind === 'dimension') {
150
163
  const dot = '.';
151
164
  // If field is joined-in from another table i.e. of type `tableName.columnName`,
@@ -153,7 +166,7 @@ function getResultMetadataAnnotation(field, resultMetadata) {
153
166
  const drillExpression = (resultMetadata === null || resultMetadata === void 0 ? void 0 : resultMetadata.sourceExpression) ||
154
167
  ((resultMetadata === null || resultMetadata === void 0 ? void 0 : resultMetadata.sourceField.includes(dot))
155
168
  ? resultMetadata === null || resultMetadata === void 0 ? void 0 : resultMetadata.sourceField
156
- : field.name);
169
+ : identifierCode(field.name));
157
170
  tag.set(['drill_expression'], drillExpression);
158
171
  hasAny = true;
159
172
  }
@@ -163,6 +176,14 @@ function getResultMetadataAnnotation(field, resultMetadata) {
163
176
  }
164
177
  : undefined;
165
178
  }
179
+ function escapeIdentifier(str) {
180
+ return str.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
181
+ }
182
+ function identifierCode(name) {
183
+ if (name.match(/^[A-Za-z_][0-9A-Za-z_]*$/))
184
+ return name;
185
+ return `\`${escapeIdentifier(name)}\``;
186
+ }
166
187
  function getResultStructMetadataAnnotation(field, resultMetadata) {
167
188
  var _a, _b;
168
189
  const tag = malloy_tag_1.Tag.withPrefix('#(malloy) ');
@@ -171,11 +192,20 @@ function getResultStructMetadataAnnotation(field, resultMetadata) {
171
192
  tag.set(['limit'], resultMetadata.limit);
172
193
  hasAny = true;
173
194
  }
195
+ if (resultMetadata.filterList) {
196
+ const drillFilters = resultMetadata.filterList
197
+ .filter(f => f.expressionType === 'scalar')
198
+ .map(f => f.code);
199
+ if (drillFilters.length > 0) {
200
+ tag.set(['drill_filters'], drillFilters);
201
+ hasAny = true;
202
+ }
203
+ }
174
204
  if (resultMetadata.orderBy) {
175
205
  for (let i = 0; i < resultMetadata.orderBy.length; i++) {
176
206
  const orderBy = resultMetadata.orderBy[i];
177
207
  const orderByField = typeof orderBy.field === 'number'
178
- ? (_a = field.fields[orderBy.field].as) !== null && _a !== void 0 ? _a : field.fields[orderBy.field].name
208
+ ? (_a = field.fields[orderBy.field - 1].as) !== null && _a !== void 0 ? _a : field.fields[orderBy.field - 1].name
179
209
  : orderBy.field;
180
210
  const direction = (_b = orderBy.dir) !== null && _b !== void 0 ? _b : null;
181
211
  tag.set(['ordered_by', i, orderByField], direction);
@@ -188,6 +218,7 @@ function getResultStructMetadataAnnotation(field, resultMetadata) {
188
218
  }
189
219
  : undefined;
190
220
  }
221
+ exports.getResultStructMetadataAnnotation = getResultStructMetadataAnnotation;
191
222
  function typeDefToType(field) {
192
223
  if ((0, model_1.isLeafAtomic)(field)) {
193
224
  switch (field.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.240-dev250311155207",
3
+ "version": "0.0.240-dev250311202829",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -41,8 +41,8 @@
41
41
  "generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
42
42
  },
43
43
  "dependencies": {
44
- "@malloydata/malloy-interfaces": "^0.0.240-dev250311155207",
45
- "@malloydata/malloy-tag": "^0.0.240-dev250311155207",
44
+ "@malloydata/malloy-interfaces": "^0.0.240-dev250311202829",
45
+ "@malloydata/malloy-tag": "^0.0.240-dev250311202829",
46
46
  "antlr4ts": "^0.5.0-alpha.4",
47
47
  "assert": "^2.0.0",
48
48
  "jest-diff": "^29.6.2",