@malloydata/malloy 0.0.105-dev231122194042 → 0.0.105-dev231127164722
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/malloy.d.ts +2 -0
- package/dist/malloy.js +46 -31
- package/dist/model/malloy_query.d.ts +4 -3
- package/dist/model/malloy_query.js +39 -21
- package/package.json +1 -1
package/dist/malloy.d.ts
CHANGED
|
@@ -1234,6 +1234,7 @@ declare class DataNull extends Data<null> {
|
|
|
1234
1234
|
export declare class DataArray extends Data<QueryData> implements Iterable<DataRecord> {
|
|
1235
1235
|
private queryData;
|
|
1236
1236
|
protected _field: Explore;
|
|
1237
|
+
private rowCache;
|
|
1237
1238
|
constructor(queryData: QueryData, field: Explore, parent: DataArrayOrRecord | undefined, parentRecord: DataRecord | undefined);
|
|
1238
1239
|
/**
|
|
1239
1240
|
* @return The `Explore` that describes the structure of this data.
|
|
@@ -1256,6 +1257,7 @@ export declare class DataRecord extends Data<{
|
|
|
1256
1257
|
private queryDataRow;
|
|
1257
1258
|
protected _field: Explore;
|
|
1258
1259
|
readonly index: number | undefined;
|
|
1260
|
+
private cellCache;
|
|
1259
1261
|
constructor(queryDataRow: QueryDataRow, index: number | undefined, field: Explore, parent: DataArrayOrRecord | undefined, parentRecord: DataRecord | undefined);
|
|
1260
1262
|
toObject(): QueryDataRow;
|
|
1261
1263
|
path(...path: (number | string)[]): DataColumn;
|
package/dist/malloy.js
CHANGED
|
@@ -2611,6 +2611,7 @@ class DataNull extends Data {
|
|
|
2611
2611
|
class DataArray extends Data {
|
|
2612
2612
|
constructor(queryData, field, parent, parentRecord) {
|
|
2613
2613
|
super(field, parent, parentRecord);
|
|
2614
|
+
this.rowCache = new Map();
|
|
2614
2615
|
this.queryData = queryData;
|
|
2615
2616
|
this._field = field;
|
|
2616
2617
|
}
|
|
@@ -2630,6 +2631,12 @@ class DataArray extends Data {
|
|
|
2630
2631
|
return getPath(this, path);
|
|
2631
2632
|
}
|
|
2632
2633
|
row(index) {
|
|
2634
|
+
let record = this.rowCache.get(index);
|
|
2635
|
+
if (!record) {
|
|
2636
|
+
record = new DataRecord(this.queryData[index], index, this.field, this, this.parentRecord);
|
|
2637
|
+
this.rowCache.set(index, record);
|
|
2638
|
+
}
|
|
2639
|
+
return record;
|
|
2633
2640
|
return new DataRecord(this.queryData[index], index, this.field, this, this.parentRecord);
|
|
2634
2641
|
}
|
|
2635
2642
|
get rowCount() {
|
|
@@ -2674,6 +2681,7 @@ function getPath(data, path) {
|
|
|
2674
2681
|
class DataRecord extends Data {
|
|
2675
2682
|
constructor(queryDataRow, index, field, parent, parentRecord) {
|
|
2676
2683
|
super(field, parent, parentRecord);
|
|
2684
|
+
this.cellCache = new Map();
|
|
2677
2685
|
this.queryDataRow = queryDataRow;
|
|
2678
2686
|
this._field = field;
|
|
2679
2687
|
this.index = index;
|
|
@@ -2687,41 +2695,48 @@ class DataRecord extends Data {
|
|
|
2687
2695
|
cell(fieldOrName) {
|
|
2688
2696
|
const fieldName = typeof fieldOrName === 'string' ? fieldOrName : fieldOrName.name;
|
|
2689
2697
|
const field = this._field.getFieldByName(fieldName);
|
|
2690
|
-
|
|
2691
|
-
if (
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
if (field.isBoolean()) {
|
|
2696
|
-
return new DataBoolean(value, field, this, this);
|
|
2697
|
-
}
|
|
2698
|
-
else if (field.isDate()) {
|
|
2699
|
-
return new DataDate(value, field, this, this);
|
|
2700
|
-
}
|
|
2701
|
-
else if (field.isJSON()) {
|
|
2702
|
-
return new DataJSON(value, field, this, this);
|
|
2703
|
-
}
|
|
2704
|
-
else if (field.isTimestamp()) {
|
|
2705
|
-
return new DataTimestamp(value, field, this, this);
|
|
2698
|
+
let column = this.cellCache.get(fieldName);
|
|
2699
|
+
if (!column) {
|
|
2700
|
+
const value = this.queryDataRow[fieldName];
|
|
2701
|
+
if (value === null) {
|
|
2702
|
+
column = new DataNull(field, this, this);
|
|
2706
2703
|
}
|
|
2707
|
-
else if (field.
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2704
|
+
else if (field.isAtomicField()) {
|
|
2705
|
+
if (field.isBoolean()) {
|
|
2706
|
+
column = new DataBoolean(value, field, this, this);
|
|
2707
|
+
}
|
|
2708
|
+
else if (field.isDate()) {
|
|
2709
|
+
column = new DataDate(value, field, this, this);
|
|
2710
|
+
}
|
|
2711
|
+
else if (field.isJSON()) {
|
|
2712
|
+
column = new DataJSON(value, field, this, this);
|
|
2713
|
+
}
|
|
2714
|
+
else if (field.isTimestamp()) {
|
|
2715
|
+
column = new DataTimestamp(value, field, this, this);
|
|
2716
|
+
}
|
|
2717
|
+
else if (field.isNumber()) {
|
|
2718
|
+
column = new DataNumber(value, field, this, this);
|
|
2719
|
+
}
|
|
2720
|
+
else if (field.isString()) {
|
|
2721
|
+
column = new DataString(value, field, this, this);
|
|
2722
|
+
}
|
|
2723
|
+
else if (field.isUnsupported()) {
|
|
2724
|
+
column = new DataUnsupported(value, field, this, this);
|
|
2725
|
+
}
|
|
2720
2726
|
}
|
|
2721
|
-
else {
|
|
2722
|
-
|
|
2727
|
+
else if (field.isExploreField()) {
|
|
2728
|
+
if (Array.isArray(value)) {
|
|
2729
|
+
column = new DataArray(value, field, this, this);
|
|
2730
|
+
}
|
|
2731
|
+
else {
|
|
2732
|
+
column = new DataRecord(value, undefined, field, this, this);
|
|
2733
|
+
}
|
|
2723
2734
|
}
|
|
2735
|
+
if (column)
|
|
2736
|
+
this.cellCache.set(fieldName, column);
|
|
2724
2737
|
}
|
|
2738
|
+
if (column)
|
|
2739
|
+
return column;
|
|
2725
2740
|
throw new Error(`Internal Error: could not construct data column for field '${fieldName}'.`);
|
|
2726
2741
|
}
|
|
2727
2742
|
get value() {
|
|
@@ -230,8 +230,9 @@ declare class QueryQuery extends QueryField {
|
|
|
230
230
|
rootResult: FieldInstanceResultRoot;
|
|
231
231
|
resultStage: string | undefined;
|
|
232
232
|
stageWriter: StageWriter | undefined;
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
isJoinedSubquery: boolean;
|
|
234
|
+
constructor(fieldDef: TurtleDef, parent: QueryStruct, stageWriter: StageWriter | undefined, isJoinedSubquery: boolean);
|
|
235
|
+
static makeQuery(fieldDef: TurtleDef, parentStruct: QueryStruct, stageWriter: StageWriter | undefined, isJoinedSubquery: boolean): QueryQuery;
|
|
235
236
|
inNestedPipeline(): boolean;
|
|
236
237
|
getFieldList(): QueryFieldDef[];
|
|
237
238
|
expandField(f: QueryFieldDef): {
|
|
@@ -355,7 +356,7 @@ export declare class QueryModel {
|
|
|
355
356
|
};
|
|
356
357
|
getStructByName(name: string): QueryStruct;
|
|
357
358
|
getStructFromRef(structRef: StructRef): QueryStruct;
|
|
358
|
-
loadQuery(query: Query, stageWriter: StageWriter | undefined, emitFinalStage?: boolean): QueryResults;
|
|
359
|
+
loadQuery(query: Query, stageWriter: StageWriter | undefined, emitFinalStage?: boolean, isJoinedSubquery?: boolean): QueryResults;
|
|
359
360
|
compileQuery(query: Query, finalize?: boolean): CompiledQuery;
|
|
360
361
|
exploreSearchSQLMap: Map<any, any>;
|
|
361
362
|
searchIndex(connection: Connection, explore: string, searchValue: string, limit?: number, searchField?: string | undefined): Promise<SearchIndexResult[] | undefined>;
|
|
@@ -1262,15 +1262,15 @@ class Segment {
|
|
|
1262
1262
|
name: 'ignoreme',
|
|
1263
1263
|
pipeline: [segment],
|
|
1264
1264
|
};
|
|
1265
|
-
const queryQueryQuery = QueryQuery.makeQuery(turtleDef, qs, new StageWriter(true, undefined) // stage write indicates we want to get a result.
|
|
1266
|
-
);
|
|
1265
|
+
const queryQueryQuery = QueryQuery.makeQuery(turtleDef, qs, new StageWriter(true, undefined), // stage write indicates we want to get a result.
|
|
1266
|
+
false);
|
|
1267
1267
|
return queryQueryQuery.getResultStructDef();
|
|
1268
1268
|
}
|
|
1269
1269
|
}
|
|
1270
1270
|
exports.Segment = Segment;
|
|
1271
1271
|
/** Query builder object. */
|
|
1272
1272
|
class QueryQuery extends QueryField {
|
|
1273
|
-
constructor(fieldDef, parent, stageWriter) {
|
|
1273
|
+
constructor(fieldDef, parent, stageWriter, isJoinedSubquery) {
|
|
1274
1274
|
super(fieldDef, parent);
|
|
1275
1275
|
this.prepared = false;
|
|
1276
1276
|
this.maxDepth = 0;
|
|
@@ -1280,8 +1280,9 @@ class QueryQuery extends QueryField {
|
|
|
1280
1280
|
this.stageWriter = stageWriter;
|
|
1281
1281
|
// do some magic here to get the first segment.
|
|
1282
1282
|
this.firstSegment = fieldDef.pipeline[0];
|
|
1283
|
+
this.isJoinedSubquery = isJoinedSubquery;
|
|
1283
1284
|
}
|
|
1284
|
-
static makeQuery(fieldDef, parentStruct, stageWriter = undefined) {
|
|
1285
|
+
static makeQuery(fieldDef, parentStruct, stageWriter = undefined, isJoinedSubquery) {
|
|
1285
1286
|
let flatTurtleDef = parentStruct.flattenTurtleDef(fieldDef);
|
|
1286
1287
|
let parent = parentStruct;
|
|
1287
1288
|
const firstStage = flatTurtleDef.pipeline[0];
|
|
@@ -1315,11 +1316,11 @@ class QueryQuery extends QueryField {
|
|
|
1315
1316
|
}
|
|
1316
1317
|
switch (firstStage.type) {
|
|
1317
1318
|
case 'reduce':
|
|
1318
|
-
return new QueryQueryReduce(flatTurtleDef, parent, stageWriter);
|
|
1319
|
+
return new QueryQueryReduce(flatTurtleDef, parent, stageWriter, isJoinedSubquery);
|
|
1319
1320
|
case 'project':
|
|
1320
|
-
return new QueryQueryProject(flatTurtleDef, parent, stageWriter);
|
|
1321
|
+
return new QueryQueryProject(flatTurtleDef, parent, stageWriter, isJoinedSubquery);
|
|
1321
1322
|
case 'index':
|
|
1322
|
-
return new QueryQueryIndex(flatTurtleDef, parent, stageWriter);
|
|
1323
|
+
return new QueryQueryIndex(flatTurtleDef, parent, stageWriter, isJoinedSubquery);
|
|
1323
1324
|
case 'partial':
|
|
1324
1325
|
throw new Error('Attempt to make query out of partial stage');
|
|
1325
1326
|
}
|
|
@@ -1370,7 +1371,7 @@ class QueryQuery extends QueryField {
|
|
|
1370
1371
|
const newFieldDef = (0, cloneDeep_1.default)(field.fieldDef);
|
|
1371
1372
|
newFieldDef.as = f.name;
|
|
1372
1373
|
newFieldDef.filterList = f.filterList;
|
|
1373
|
-
field = QueryQuery.makeQuery(newFieldDef, this.parent);
|
|
1374
|
+
field = QueryQuery.makeQuery(newFieldDef, this.parent, undefined, this.isJoinedSubquery);
|
|
1374
1375
|
}
|
|
1375
1376
|
else if (!(field instanceof QueryFieldTimestamp ||
|
|
1376
1377
|
field instanceof QueryFieldDate)) {
|
|
@@ -1984,6 +1985,22 @@ class QueryQuery extends QueryField {
|
|
|
1984
1985
|
if (this.firstSegment.type === 'project' && !queryDef.orderBy) {
|
|
1985
1986
|
return ''; // No default ordering for project.
|
|
1986
1987
|
}
|
|
1988
|
+
// Intermediate results (in a pipeline or join) that have no limit, don't need an orderby
|
|
1989
|
+
// Some database don't have this optimization.
|
|
1990
|
+
if (this.fieldDef.pipeline.length > 1 && queryDef.limit === undefined) {
|
|
1991
|
+
return '';
|
|
1992
|
+
}
|
|
1993
|
+
// ignore orderby if all aggregates.
|
|
1994
|
+
if (resultStruct.getRepeatedResultType() === 'inline_all_numbers') {
|
|
1995
|
+
return '';
|
|
1996
|
+
}
|
|
1997
|
+
// if we are in the last stage of a query and the query is a subquery
|
|
1998
|
+
// and has no limit, ORDER BY is superfluous
|
|
1999
|
+
if (this.isJoinedSubquery &&
|
|
2000
|
+
this.fieldDef.pipeline.length === 1 &&
|
|
2001
|
+
queryDef.limit === undefined) {
|
|
2002
|
+
return '';
|
|
2003
|
+
}
|
|
1987
2004
|
const orderBy = queryDef.orderBy || resultStruct.calculateDefaultOrderBy();
|
|
1988
2005
|
const o = [];
|
|
1989
2006
|
for (const f of orderBy) {
|
|
@@ -2439,7 +2456,7 @@ class QueryQuery extends QueryField {
|
|
|
2439
2456
|
const qs = new QueryStruct(structDef, {
|
|
2440
2457
|
model: this.parent.getModel(),
|
|
2441
2458
|
});
|
|
2442
|
-
const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter);
|
|
2459
|
+
const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter, this.isJoinedSubquery);
|
|
2443
2460
|
pipeOut = q.generateSQLFromPipeline(stageWriter);
|
|
2444
2461
|
// console.log(stageWriter.generateSQLStages());
|
|
2445
2462
|
structDef = pipeOut.outputStruct;
|
|
@@ -2497,7 +2514,7 @@ class QueryQuery extends QueryField {
|
|
|
2497
2514
|
const s = new QueryStruct(structDef, {
|
|
2498
2515
|
model: this.parent.getModel(),
|
|
2499
2516
|
});
|
|
2500
|
-
const q = QueryQuery.makeQuery({ type: 'turtle', name: 'ignoreme', pipeline: [transform] }, s, stageWriter);
|
|
2517
|
+
const q = QueryQuery.makeQuery({ type: 'turtle', name: 'ignoreme', pipeline: [transform] }, s, stageWriter, this.isJoinedSubquery);
|
|
2501
2518
|
q.prepare(stageWriter);
|
|
2502
2519
|
lastStageName = q.generateSQL(stageWriter);
|
|
2503
2520
|
outputStruct = q.getResultStructDef();
|
|
@@ -2518,8 +2535,8 @@ class QueryQueryProject extends QueryQuery {
|
|
|
2518
2535
|
// wildcards have been expanded
|
|
2519
2536
|
// nested repeated fields are safe to use.
|
|
2520
2537
|
class QueryQueryIndexStage extends QueryQuery {
|
|
2521
|
-
constructor(fieldDef, parent, stageWriter) {
|
|
2522
|
-
super(fieldDef, parent, stageWriter);
|
|
2538
|
+
constructor(fieldDef, parent, stageWriter, isJoinedSubquery) {
|
|
2539
|
+
super(fieldDef, parent, stageWriter, isJoinedSubquery);
|
|
2523
2540
|
this.fieldDef = fieldDef;
|
|
2524
2541
|
}
|
|
2525
2542
|
// get a field ref and expand it.
|
|
@@ -2632,8 +2649,8 @@ FROM ${resultStage}\n`);
|
|
|
2632
2649
|
}
|
|
2633
2650
|
}
|
|
2634
2651
|
class QueryQueryIndex extends QueryQuery {
|
|
2635
|
-
constructor(fieldDef, parent, stageWriter) {
|
|
2636
|
-
super(fieldDef, parent, stageWriter);
|
|
2652
|
+
constructor(fieldDef, parent, stageWriter, isJoinedSubquery) {
|
|
2653
|
+
super(fieldDef, parent, stageWriter, isJoinedSubquery);
|
|
2637
2654
|
this.rootFields = [];
|
|
2638
2655
|
this.fanPrefixMap = {};
|
|
2639
2656
|
this.fieldDef = fieldDef;
|
|
@@ -2720,7 +2737,7 @@ class QueryQueryIndex extends QueryQuery {
|
|
|
2720
2737
|
fields: fields,
|
|
2721
2738
|
},
|
|
2722
2739
|
],
|
|
2723
|
-
}, this.parent, stageWriter);
|
|
2740
|
+
}, this.parent, stageWriter, this.isJoinedSubquery);
|
|
2724
2741
|
q.prepare(stageWriter);
|
|
2725
2742
|
const lastStageName = q.generateSQL(stageWriter);
|
|
2726
2743
|
outputStageNames.push(lastStageName);
|
|
@@ -2799,7 +2816,7 @@ class QueryStruct extends QueryNode {
|
|
|
2799
2816
|
// case "reduce" || "project" || "index": {
|
|
2800
2817
|
case 'turtle': {
|
|
2801
2818
|
// not sure why we need to cast here...
|
|
2802
|
-
this.addFieldToNameMap(as, QueryQuery.makeQuery(field, this));
|
|
2819
|
+
this.addFieldToNameMap(as, QueryQuery.makeQuery(field, this, undefined, false));
|
|
2803
2820
|
break;
|
|
2804
2821
|
}
|
|
2805
2822
|
default: {
|
|
@@ -3077,12 +3094,13 @@ class QueryStruct extends QueryNode {
|
|
|
3077
3094
|
// should be cached.
|
|
3078
3095
|
if (name.includes('cache')) {
|
|
3079
3096
|
const dtStageWriter = new StageWriter(true, stageWriter);
|
|
3080
|
-
this.model.loadQuery(this.fieldDef.structSource.query, dtStageWriter);
|
|
3097
|
+
this.model.loadQuery(this.fieldDef.structSource.query, dtStageWriter, false, false);
|
|
3081
3098
|
return dtStageWriter.addPDT(name, this.dialect);
|
|
3082
3099
|
}
|
|
3083
3100
|
else {
|
|
3084
3101
|
// returns the stage name.
|
|
3085
|
-
return this.model.loadQuery(this.fieldDef.structSource.query, stageWriter
|
|
3102
|
+
return this.model.loadQuery(this.fieldDef.structSource.query, stageWriter, false, true // this is an intermediate stage.
|
|
3103
|
+
).lastStageName;
|
|
3086
3104
|
}
|
|
3087
3105
|
}
|
|
3088
3106
|
default:
|
|
@@ -3269,7 +3287,7 @@ class QueryModel {
|
|
|
3269
3287
|
}
|
|
3270
3288
|
return new QueryStruct(structDef, { model: this });
|
|
3271
3289
|
}
|
|
3272
|
-
loadQuery(query, stageWriter, emitFinalStage = false) {
|
|
3290
|
+
loadQuery(query, stageWriter, emitFinalStage = false, isJoinedSubquery = false) {
|
|
3273
3291
|
const malloy = '';
|
|
3274
3292
|
if (!stageWriter) {
|
|
3275
3293
|
stageWriter = new StageWriter(true, undefined);
|
|
@@ -3281,7 +3299,7 @@ class QueryModel {
|
|
|
3281
3299
|
pipeline: query.pipeline,
|
|
3282
3300
|
filterList: query.filterList,
|
|
3283
3301
|
};
|
|
3284
|
-
const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef), stageWriter);
|
|
3302
|
+
const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef), stageWriter, isJoinedSubquery);
|
|
3285
3303
|
const ret = q.generateSQLFromPipeline(stageWriter);
|
|
3286
3304
|
if (emitFinalStage && q.parent.dialect.hasFinalStage) {
|
|
3287
3305
|
// const fieldNames: string[] = [];
|
|
@@ -3302,7 +3320,7 @@ class QueryModel {
|
|
|
3302
3320
|
compileQuery(query, finalize = true) {
|
|
3303
3321
|
let newModel;
|
|
3304
3322
|
const m = newModel || this;
|
|
3305
|
-
const ret = m.loadQuery(query, undefined, finalize);
|
|
3323
|
+
const ret = m.loadQuery(query, undefined, finalize, false);
|
|
3306
3324
|
const sourceExplore = typeof query.structRef === 'string'
|
|
3307
3325
|
? query.structRef
|
|
3308
3326
|
: // LTNOTE: the parser needs to capture the query before the |. This will work
|