@malloydata/malloy 0.0.343 → 0.0.344
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/api/core.js +2 -1
- package/dist/api/foundation/core.d.ts +4 -1
- package/dist/api/foundation/core.js +10 -5
- package/dist/api/foundation/runtime.js +1 -1
- package/dist/dialect/snowflake/snowflake.js +2 -2
- package/dist/dialect/trino/trino.js +2 -2
- package/dist/lang/ast/field-space/dynamic-space.js +2 -1
- package/dist/lang/ast/field-space/index-field-space.js +5 -5
- package/dist/lang/ast/field-space/parameter-space.js +4 -4
- package/dist/lang/ast/field-space/query-spaces.d.ts +1 -1
- package/dist/lang/ast/field-space/query-spaces.js +5 -5
- package/dist/lang/ast/field-space/static-space.js +10 -10
- package/dist/lang/ast/source-elements/named-source.js +9 -5
- package/dist/lang/ast/source-elements/source.js +2 -1
- package/dist/lang/ast/statements/import-statement.js +5 -3
- package/dist/lang/ast/types/malloy-element.d.ts +2 -2
- package/dist/lang/ast/types/malloy-element.js +9 -9
- package/dist/model/expression_compiler.js +4 -4
- package/dist/model/malloy_types.d.ts +21 -10
- package/dist/model/malloy_types.js +11 -0
- package/dist/model/persist_utils.js +1 -1
- package/dist/model/query_query.js +1 -1
- package/dist/model/source_def_utils.js +1 -1
- package/dist/model/sql_compiled.js +1 -1
- package/dist/model/utils.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/CONTEXT.md +0 -58
- package/MALLOY_API.md +0 -129
package/dist/api/core.js
CHANGED
|
@@ -500,7 +500,8 @@ function statedCompileQuery(state) {
|
|
|
500
500
|
source = query.structRef;
|
|
501
501
|
}
|
|
502
502
|
else {
|
|
503
|
-
|
|
503
|
+
// TODO: `as StructDef` cast is pre-existing type-safety issue — contents holds NamedModelObject
|
|
504
|
+
source = (0, model_1.safeRecordGet)(result.modelDef.contents, query.structRef);
|
|
504
505
|
}
|
|
505
506
|
}
|
|
506
507
|
const sourceAnnotations = (0, annotation_1.annotationToTaglines)(source.annotation).map(l => ({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LogMessage } from '../../lang';
|
|
2
|
-
import type { BuildID, CompiledQuery, DocumentLocation, BooleanFieldDef, JSONFieldDef, NumberFieldDef, StringFieldDef, FilterCondition, Query as InternalQuery, ModelDef, DocumentPosition as ModelDocumentPosition, NamedQueryDef, StructDef, TurtleDef, NativeUnsupportedFieldDef, ImportLocation, Annotation, AtomicFieldDef, DateFieldDef, ATimestampFieldDef, SourceDef, Argument, SourceComponentInfo, DocumentReference, PersistableSourceDef } from '../../model';
|
|
2
|
+
import type { BuildID, CompiledQuery, DocumentLocation, BooleanFieldDef, JSONFieldDef, NumberFieldDef, StringFieldDef, FilterCondition, Query as InternalQuery, ModelDef, DocumentPosition as ModelDocumentPosition, NamedQueryDef, StructDef, TurtleDef, NativeUnsupportedFieldDef, ImportLocation, Annotation, NamedModelObject, AtomicFieldDef, DateFieldDef, ATimestampFieldDef, SourceDef, Argument, SourceComponentInfo, DocumentReference, PersistableSourceDef } from '../../model';
|
|
3
3
|
import { QueryModel } from '../../model';
|
|
4
4
|
import type { Dialect } from '../../dialect';
|
|
5
5
|
import type { BuildGraph, CompileQueryOptions } from './types';
|
|
@@ -235,7 +235,10 @@ export declare class Model implements Taggable {
|
|
|
235
235
|
readonly fromSources: string[];
|
|
236
236
|
private readonly references;
|
|
237
237
|
private _queryModel?;
|
|
238
|
+
private readonly contentsMap;
|
|
238
239
|
constructor(modelDef: ModelDef, problems: LogMessage[], fromSources: string[], existingQueryModel?: QueryModel);
|
|
240
|
+
/** Safe lookup in model contents by name. */
|
|
241
|
+
getContent(name: string): NamedModelObject | undefined;
|
|
239
242
|
get queryModel(): QueryModel;
|
|
240
243
|
/**
|
|
241
244
|
* Returns the cached QueryModel if it exists, without creating one.
|
|
@@ -750,6 +750,11 @@ class Model {
|
|
|
750
750
|
this.fromSources = fromSources;
|
|
751
751
|
this.references = new reference_list_1.ReferenceList((_a = fromSources[0]) !== null && _a !== void 0 ? _a : '', (_b = modelDef.references) !== null && _b !== void 0 ? _b : []);
|
|
752
752
|
this._queryModel = existingQueryModel;
|
|
753
|
+
this.contentsMap = new Map(Object.entries(modelDef.contents));
|
|
754
|
+
}
|
|
755
|
+
/** Safe lookup in model contents by name. */
|
|
756
|
+
getContent(name) {
|
|
757
|
+
return this.contentsMap.get(name);
|
|
753
758
|
}
|
|
754
759
|
get queryModel() {
|
|
755
760
|
if (!this._queryModel) {
|
|
@@ -799,7 +804,7 @@ class Model {
|
|
|
799
804
|
* @return A prepared query.
|
|
800
805
|
*/
|
|
801
806
|
getPreparedQueryByName(queryName) {
|
|
802
|
-
const query = this.
|
|
807
|
+
const query = this.getContent(queryName);
|
|
803
808
|
if ((query === null || query === void 0 ? void 0 : query.type) === 'query') {
|
|
804
809
|
return new PreparedQuery(query, this, this.problems, queryName);
|
|
805
810
|
}
|
|
@@ -846,7 +851,7 @@ class Model {
|
|
|
846
851
|
* @return An `Explore`.
|
|
847
852
|
*/
|
|
848
853
|
getExploreByName(name) {
|
|
849
|
-
const struct = this.
|
|
854
|
+
const struct = this.getContent(name);
|
|
850
855
|
if (struct && (0, model_1.isSourceDef)(struct)) {
|
|
851
856
|
return new Explore(struct);
|
|
852
857
|
}
|
|
@@ -1129,9 +1134,9 @@ class PreparedQuery {
|
|
|
1129
1134
|
get dialect() {
|
|
1130
1135
|
const sourceRef = this._query.structRef;
|
|
1131
1136
|
const source = typeof sourceRef === 'string'
|
|
1132
|
-
? this.
|
|
1137
|
+
? this._model.getContent(sourceRef)
|
|
1133
1138
|
: sourceRef;
|
|
1134
|
-
if (!(0, model_1.isSourceDef)(source)) {
|
|
1139
|
+
if (source === undefined || !(0, model_1.isSourceDef)(source)) {
|
|
1135
1140
|
throw new Error('Invalid source for query');
|
|
1136
1141
|
}
|
|
1137
1142
|
return source.dialect;
|
|
@@ -1221,7 +1226,7 @@ class PreparedResult {
|
|
|
1221
1226
|
}
|
|
1222
1227
|
get sourceExplore() {
|
|
1223
1228
|
const name = this.inner.sourceExplore;
|
|
1224
|
-
const explore = this.modelDef.contents
|
|
1229
|
+
const explore = (0, model_1.safeRecordGet)(this.modelDef.contents, name);
|
|
1225
1230
|
if (explore && (0, model_1.isSourceDef)(explore)) {
|
|
1226
1231
|
return new Explore(explore);
|
|
1227
1232
|
}
|
|
@@ -572,7 +572,7 @@ class QueryMaterializer extends FluentState {
|
|
|
572
572
|
}
|
|
573
573
|
const plan = preparedQuery.model.getBuildPlan();
|
|
574
574
|
const connectionNames = new Set(Object.values(plan.sources).map(s => s.connectionName));
|
|
575
|
-
connectionDigests =
|
|
575
|
+
connectionDigests = (0, model_1.mkSafeRecord)();
|
|
576
576
|
for (const connName of connectionNames) {
|
|
577
577
|
const conn = await this.runtime.connections.lookupConnection(connName);
|
|
578
578
|
connectionDigests[connName] = conn.getDigest();
|
|
@@ -515,12 +515,12 @@ ${(0, utils_1.indent)(sql)}
|
|
|
515
515
|
return sqlType.match(/^[A-Za-z\s(),[\]0-9]*$/) !== null;
|
|
516
516
|
}
|
|
517
517
|
sqlLiteralRecord(lit) {
|
|
518
|
-
var _a, _b;
|
|
518
|
+
var _a, _b, _c;
|
|
519
519
|
const rowVals = [];
|
|
520
520
|
for (const f of lit.typeDef.fields) {
|
|
521
521
|
const name = (_a = f.as) !== null && _a !== void 0 ? _a : f.name;
|
|
522
522
|
const propName = `'${name}'`;
|
|
523
|
-
const propVal = (_b = lit.kids
|
|
523
|
+
const propVal = (_c = (_b = (0, malloy_types_1.safeRecordGet)(lit.kids, name)) === null || _b === void 0 ? void 0 : _b.sql) !== null && _c !== void 0 ? _c : 'internal-error-record-literal';
|
|
524
524
|
rowVals.push(`${propName},${propVal}`);
|
|
525
525
|
}
|
|
526
526
|
return `OBJECT_CONSTRUCT_KEEP_NULL(${rowVals.join(',')})`;
|
|
@@ -594,13 +594,13 @@ ${(0, utils_1.indent)(sql)}
|
|
|
594
594
|
return from.units === 'day_of_week' ? `mod(${extracted}+1,7)` : extracted;
|
|
595
595
|
}
|
|
596
596
|
sqlLiteralRecord(lit) {
|
|
597
|
-
var _a, _b;
|
|
597
|
+
var _a, _b, _c;
|
|
598
598
|
const rowVals = [];
|
|
599
599
|
const rowTypes = [];
|
|
600
600
|
for (const f of lit.typeDef.fields) {
|
|
601
601
|
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
602
602
|
const name = (_a = f.as) !== null && _a !== void 0 ? _a : f.name;
|
|
603
|
-
rowVals.push((_b = lit.kids
|
|
603
|
+
rowVals.push((_c = (_b = (0, malloy_types_1.safeRecordGet)(lit.kids, name)) === null || _b === void 0 ? void 0 : _b.sql) !== null && _c !== void 0 ? _c : 'internal-error-record-literal');
|
|
604
604
|
const elType = this.malloyTypeToSQLType(f);
|
|
605
605
|
rowTypes.push(`${this.sqlMaybeQuoteIdentifier(name)} ${elType}`);
|
|
606
606
|
}
|
|
@@ -57,6 +57,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
57
57
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
58
|
exports.DynamicSpace = void 0;
|
|
59
59
|
const model = __importStar(require("../../../model/malloy_types"));
|
|
60
|
+
const malloy_types_1 = require("../../../model/malloy_types");
|
|
60
61
|
const field_utils_1 = require("../../field-utils");
|
|
61
62
|
const error_factory_1 = require("../error-factory");
|
|
62
63
|
const space_field_1 = require("../types/space-field");
|
|
@@ -116,7 +117,7 @@ class DynamicSpace extends static_space_1.StaticSpace {
|
|
|
116
117
|
if (this.sourceDef === undefined) {
|
|
117
118
|
// Grab all the parameters so that we can populate the "final" structDef
|
|
118
119
|
// with parameters immediately so that views can see them when they are translating
|
|
119
|
-
const parameters =
|
|
120
|
+
const parameters = (0, malloy_types_1.mkSafeRecord)();
|
|
120
121
|
for (const [name, entry] of this.entries()) {
|
|
121
122
|
if (entry instanceof space_param_1.SpaceParam) {
|
|
122
123
|
parameters[name] = entry.parameter();
|
|
@@ -76,7 +76,7 @@ class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
|
|
|
76
76
|
for (const [name, field] of this.entries()) {
|
|
77
77
|
if (field instanceof space_field_1.SpaceField) {
|
|
78
78
|
let nextFieldUsage = undefined;
|
|
79
|
-
const wild = this.expandedWild
|
|
79
|
+
const wild = this.expandedWild.get(name);
|
|
80
80
|
if (wild) {
|
|
81
81
|
indexFields.push({ type: 'fieldref', path: wild.path, at: wild.at });
|
|
82
82
|
fieldUsage.push({ path: wild.path });
|
|
@@ -104,7 +104,7 @@ class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
|
|
|
104
104
|
}
|
|
105
105
|
addRefineFromFields(_refineThis) { }
|
|
106
106
|
addWild(wild) {
|
|
107
|
-
var _a;
|
|
107
|
+
var _a, _b;
|
|
108
108
|
let current = this.exprSpace;
|
|
109
109
|
const joinPath = [];
|
|
110
110
|
if (wild.joinPath) {
|
|
@@ -142,7 +142,7 @@ class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
|
|
|
142
142
|
name,
|
|
143
143
|
]);
|
|
144
144
|
if (this.entry(indexName)) {
|
|
145
|
-
const conflict = (_a = this.expandedWild
|
|
145
|
+
const conflict = (_b = (_a = this.expandedWild.get(indexName)) === null || _a === void 0 ? void 0 : _a.path) === null || _b === void 0 ? void 0 : _b.join('.');
|
|
146
146
|
wild.logError('name-conflict-in-wildcard-expansion', `Cannot expand '${name}' in '${wild.refString}' because a field with that name already exists${conflict ? ` (conflicts with ${conflict})` : ''}`);
|
|
147
147
|
}
|
|
148
148
|
else {
|
|
@@ -152,11 +152,11 @@ class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
|
|
|
152
152
|
(0, malloy_types_1.expressionIsScalar)(eTypeDesc.expressionType) &&
|
|
153
153
|
(dialect === undefined || !dialect.ignoreInProject(name))) {
|
|
154
154
|
expandEntries.push({ name: indexName, entry });
|
|
155
|
-
this.expandedWild
|
|
155
|
+
this.expandedWild.set(indexName, {
|
|
156
156
|
path: joinPath.concat(name),
|
|
157
157
|
entry,
|
|
158
158
|
at: wild.location,
|
|
159
|
-
};
|
|
159
|
+
});
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -11,9 +11,9 @@ const space_param_1 = require("../types/space-param");
|
|
|
11
11
|
class ParameterSpace {
|
|
12
12
|
constructor(parameters) {
|
|
13
13
|
this.type = 'fieldSpace';
|
|
14
|
-
this._map =
|
|
14
|
+
this._map = new Map();
|
|
15
15
|
for (const parameter of parameters) {
|
|
16
|
-
this._map
|
|
16
|
+
this._map.set(parameter.name, new space_param_1.AbstractParameter(parameter));
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
structDef() {
|
|
@@ -23,7 +23,7 @@ class ParameterSpace {
|
|
|
23
23
|
throw new Error('Parameter space does not have an emptyStructDef');
|
|
24
24
|
}
|
|
25
25
|
entry(name) {
|
|
26
|
-
return this._map
|
|
26
|
+
return this._map.get(name);
|
|
27
27
|
}
|
|
28
28
|
lookup(symbol) {
|
|
29
29
|
const name = symbol[0];
|
|
@@ -65,7 +65,7 @@ class ParameterSpace {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
entries() {
|
|
68
|
-
return
|
|
68
|
+
return [...this._map.entries()];
|
|
69
69
|
}
|
|
70
70
|
dialectName() {
|
|
71
71
|
return '~parameter-space-unknown-dialect~';
|
|
@@ -24,7 +24,7 @@ export declare abstract class QueryOperationSpace extends RefinedSpace implement
|
|
|
24
24
|
readonly astEl: MalloyElement;
|
|
25
25
|
protected exprSpace: QueryInputSpace;
|
|
26
26
|
abstract readonly segmentType: 'reduce' | 'project' | 'index';
|
|
27
|
-
expandedWild:
|
|
27
|
+
expandedWild: Map<string, {
|
|
28
28
|
path: string[];
|
|
29
29
|
entry: SpaceEntry;
|
|
30
30
|
at: model.DocumentLocation;
|
|
@@ -85,7 +85,7 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
|
|
|
85
85
|
super(queryInputSpace.emptyStructDef());
|
|
86
86
|
this.nestParent = nestParent;
|
|
87
87
|
this.astEl = astEl;
|
|
88
|
-
this.expandedWild =
|
|
88
|
+
this.expandedWild = new Map();
|
|
89
89
|
this.drillDimensions = [];
|
|
90
90
|
this.compositeFieldUsers = [];
|
|
91
91
|
// Composite field usage is not computed until `queryFieldDefs` is called
|
|
@@ -149,7 +149,7 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
|
|
|
149
149
|
continue;
|
|
150
150
|
}
|
|
151
151
|
if (this.entry(name)) {
|
|
152
|
-
const conflict = (_a = this.expandedWild
|
|
152
|
+
const conflict = (_a = this.expandedWild.get(name)) === null || _a === void 0 ? void 0 : _a.path.join('.');
|
|
153
153
|
wild.logError('name-conflict-in-wildcard-expansion', `Cannot expand '${name}' in '${wild.refString}' because a field with that name already exists${conflict ? ` (conflicts with ${conflict})` : ''}`);
|
|
154
154
|
}
|
|
155
155
|
else {
|
|
@@ -158,11 +158,11 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
|
|
|
158
158
|
model.expressionIsScalar(eType.expressionType) &&
|
|
159
159
|
(dialect === undefined || !dialect.ignoreInProject(name))) {
|
|
160
160
|
expandEntries.push({ name, entry });
|
|
161
|
-
this.expandedWild
|
|
161
|
+
this.expandedWild.set(name, {
|
|
162
162
|
path: joinPath.concat(name),
|
|
163
163
|
entry,
|
|
164
164
|
at: wild.location,
|
|
165
|
-
};
|
|
165
|
+
});
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -348,7 +348,7 @@ class QuerySpace extends QueryOperationSpace {
|
|
|
348
348
|
}
|
|
349
349
|
else {
|
|
350
350
|
const { name, field } = user;
|
|
351
|
-
const wildPath = this.expandedWild
|
|
351
|
+
const wildPath = this.expandedWild.get(name);
|
|
352
352
|
if (wildPath) {
|
|
353
353
|
const typeDesc = wildPath.entry.typeDesc();
|
|
354
354
|
fields.push({
|
|
@@ -62,16 +62,16 @@ class StaticSpace {
|
|
|
62
62
|
}
|
|
63
63
|
get map() {
|
|
64
64
|
if (this.memoMap === undefined) {
|
|
65
|
-
this.memoMap =
|
|
65
|
+
this.memoMap = new Map();
|
|
66
66
|
for (const f of this.fromStruct.fields) {
|
|
67
67
|
const name = f.as || f.name;
|
|
68
|
-
this.memoMap
|
|
68
|
+
this.memoMap.set(name, this.defToSpaceField(f));
|
|
69
69
|
}
|
|
70
70
|
if ((0, malloy_types_1.isSourceDef)(this.fromStruct)) {
|
|
71
71
|
if (this.fromStruct.parameters) {
|
|
72
72
|
for (const [paramName, paramDef] of Object.entries(this.fromStruct.parameters)) {
|
|
73
|
-
if (!
|
|
74
|
-
this.memoMap
|
|
73
|
+
if (!this.memoMap.has(paramName)) {
|
|
74
|
+
this.memoMap.set(paramName, new space_param_1.DefinedParameter(paramDef));
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -83,20 +83,20 @@ class StaticSpace {
|
|
|
83
83
|
return 'internal';
|
|
84
84
|
}
|
|
85
85
|
dropEntries() {
|
|
86
|
-
this.memoMap =
|
|
86
|
+
this.memoMap = new Map();
|
|
87
87
|
}
|
|
88
88
|
dropEntry(name) {
|
|
89
|
-
|
|
89
|
+
this.map.delete(name);
|
|
90
90
|
}
|
|
91
91
|
// TODO this was protected
|
|
92
92
|
entry(name) {
|
|
93
|
-
return this.map
|
|
93
|
+
return this.map.get(name);
|
|
94
94
|
}
|
|
95
95
|
setEntry(name, value) {
|
|
96
|
-
this.map
|
|
96
|
+
this.map.set(name, value);
|
|
97
97
|
}
|
|
98
98
|
entries() {
|
|
99
|
-
return
|
|
99
|
+
return [...this.map.entries()];
|
|
100
100
|
}
|
|
101
101
|
structDef() {
|
|
102
102
|
return this.fromStruct;
|
|
@@ -104,7 +104,7 @@ class StaticSpace {
|
|
|
104
104
|
emptyStructDef() {
|
|
105
105
|
const ret = { ...this.fromStruct };
|
|
106
106
|
if ((0, malloy_types_1.isSourceDef)(ret)) {
|
|
107
|
-
ret.parameters =
|
|
107
|
+
ret.parameters = (0, malloy_types_1.mkSafeRecord)();
|
|
108
108
|
}
|
|
109
109
|
ret.fields = [];
|
|
110
110
|
return ret;
|
|
@@ -117,7 +117,7 @@ class NamedSource extends source_1.Source {
|
|
|
117
117
|
}
|
|
118
118
|
evaluateArguments(parameterSpace, parametersIn, parametersOut) {
|
|
119
119
|
var _a, _b, _c;
|
|
120
|
-
const outArguments =
|
|
120
|
+
const outArguments = Object.assign((0, malloy_types_1.mkSafeRecord)(), this.sourceArguments);
|
|
121
121
|
const passedNames = new Set();
|
|
122
122
|
for (const argument of (_a = this.args) !== null && _a !== void 0 ? _a : []) {
|
|
123
123
|
const id = (_b = argument.id) !== null && _b !== void 0 ? _b : (argument.value instanceof expr_id_reference_1.ExprIdReference
|
|
@@ -133,7 +133,9 @@ class NamedSource extends source_1.Source {
|
|
|
133
133
|
continue;
|
|
134
134
|
}
|
|
135
135
|
passedNames.add(name);
|
|
136
|
-
const parameter =
|
|
136
|
+
const parameter = parametersIn
|
|
137
|
+
? (0, malloy_types_1.safeRecordGet)(parametersIn, name)
|
|
138
|
+
: undefined;
|
|
137
139
|
if (!parameter) {
|
|
138
140
|
id.logError('source-parameter-not-found', `\`${this.refName}\` has no declared parameter named \`${id.refString}\``);
|
|
139
141
|
}
|
|
@@ -200,7 +202,7 @@ class NamedSource extends source_1.Source {
|
|
|
200
202
|
notFound.dialect = notFound.dialect + err;
|
|
201
203
|
return notFound;
|
|
202
204
|
}
|
|
203
|
-
const outParameters =
|
|
205
|
+
const outParameters = (0, malloy_types_1.mkSafeRecord)();
|
|
204
206
|
for (const parameter of pList !== null && pList !== void 0 ? pList : []) {
|
|
205
207
|
const compiled = parameter.parameter();
|
|
206
208
|
outParameters[compiled.name] = compiled;
|
|
@@ -208,8 +210,10 @@ class NamedSource extends source_1.Source {
|
|
|
208
210
|
const outArguments = this.evaluateArguments(parameterSpace, base.parameters, pList);
|
|
209
211
|
for (const paramName in base.parameters) {
|
|
210
212
|
if (!(paramName in outArguments) &&
|
|
211
|
-
(0, malloy_types_1.paramHasValue)(base.parameters
|
|
212
|
-
outArguments[paramName] = {
|
|
213
|
+
(0, malloy_types_1.paramHasValue)((0, malloy_types_1.safeRecordGet)(base.parameters, paramName))) {
|
|
214
|
+
outArguments[paramName] = {
|
|
215
|
+
...(0, malloy_types_1.safeRecordGet)(base.parameters, paramName),
|
|
216
|
+
};
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
const ret = { ...base, parameters: outParameters, arguments: outArguments };
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.Source = void 0;
|
|
26
|
+
const malloy_types_1 = require("../../../model/malloy_types");
|
|
26
27
|
const malloy_element_1 = require("../types/malloy-element");
|
|
27
28
|
/**
|
|
28
29
|
* A "Source" is a thing which you can run queries against. The main
|
|
@@ -37,7 +38,7 @@ class Source extends malloy_element_1.MalloyElement {
|
|
|
37
38
|
packParameters(pList) {
|
|
38
39
|
if (pList === undefined)
|
|
39
40
|
return undefined;
|
|
40
|
-
const parameters =
|
|
41
|
+
const parameters = (0, malloy_types_1.mkSafeRecord)();
|
|
41
42
|
for (const hasP of pList) {
|
|
42
43
|
const pVal = hasP.parameter();
|
|
43
44
|
parameters[pVal.name] = pVal;
|
|
@@ -102,7 +102,7 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
102
102
|
for (const importOne of this.list) {
|
|
103
103
|
const dstName = importOne.text;
|
|
104
104
|
const srcName = importOne.from ? importOne.from.text : dstName;
|
|
105
|
-
if (importedModel.contents
|
|
105
|
+
if ((0, malloy_types_1.safeRecordGet)(importedModel.contents, srcName) === undefined) {
|
|
106
106
|
importOne.logError('selective-import-not-found', `Cannot find '${srcName}', not imported`);
|
|
107
107
|
}
|
|
108
108
|
else if (doc.getEntry(dstName)) {
|
|
@@ -117,7 +117,9 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
117
117
|
const picked = explicitImport[srcName];
|
|
118
118
|
const dstName = picked || srcName;
|
|
119
119
|
if (importAll || picked) {
|
|
120
|
-
const importMe = {
|
|
120
|
+
const importMe = {
|
|
121
|
+
...(0, malloy_types_1.safeRecordGet)(importedModel.contents, srcName),
|
|
122
|
+
};
|
|
121
123
|
importMe.as = dstName;
|
|
122
124
|
doc.setEntry(dstName, { entry: importMe, exported: false });
|
|
123
125
|
// Collect dependencies for persistable sources
|
|
@@ -136,7 +138,7 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
136
138
|
// (parent can't resolve by name since it's not in namespace)
|
|
137
139
|
let entry = value.entry;
|
|
138
140
|
if ((0, malloy_types_1.isSourceRegistryReference)(entry)) {
|
|
139
|
-
const resolved = importedModel.contents
|
|
141
|
+
const resolved = (0, malloy_types_1.safeRecordGet)(importedModel.contents, entry.name);
|
|
140
142
|
if (resolved &&
|
|
141
143
|
(0, malloy_types_1.isSourceDef)(resolved) &&
|
|
142
144
|
(0, malloy_types_1.isPersistableSourceDef)(resolved)) {
|
|
@@ -120,7 +120,7 @@ export declare class DocStatementList extends ListOf<DocStatement | DocStatement
|
|
|
120
120
|
export declare class Document extends MalloyElement implements NameSpace {
|
|
121
121
|
elementType: string;
|
|
122
122
|
globalNameSpace: NameSpace;
|
|
123
|
-
documentModel:
|
|
123
|
+
documentModel: Map<string, ModelEntry>;
|
|
124
124
|
documentSrcRegistry: Record<SourceID, SourceRegistryValue>;
|
|
125
125
|
queryList: Query[];
|
|
126
126
|
statements: DocStatementList;
|
|
@@ -136,7 +136,7 @@ export declare class Document extends MalloyElement implements NameSpace {
|
|
|
136
136
|
hasAnnotation(): boolean;
|
|
137
137
|
currentModelAnnotation(): ModelAnnotation | undefined;
|
|
138
138
|
modelDef(): ModelDef;
|
|
139
|
-
getEntry(str: string): ModelEntry;
|
|
139
|
+
getEntry(str: string): ModelEntry | undefined;
|
|
140
140
|
setEntry(str: string, ent: ModelEntry): void;
|
|
141
141
|
/**
|
|
142
142
|
* Return an error message if this dialect is the first reference to this particular
|
|
@@ -437,7 +437,7 @@ class Document extends MalloyElement {
|
|
|
437
437
|
super();
|
|
438
438
|
this.elementType = 'document';
|
|
439
439
|
this.globalNameSpace = new global_name_space_1.GlobalNameSpace();
|
|
440
|
-
this.documentModel =
|
|
440
|
+
this.documentModel = new Map();
|
|
441
441
|
this.documentSrcRegistry = {};
|
|
442
442
|
this.queryList = [];
|
|
443
443
|
this.didInitModel = false;
|
|
@@ -453,7 +453,7 @@ class Document extends MalloyElement {
|
|
|
453
453
|
if (this.didInitModel) {
|
|
454
454
|
return;
|
|
455
455
|
}
|
|
456
|
-
this.documentModel =
|
|
456
|
+
this.documentModel = new Map();
|
|
457
457
|
this.documentSrcRegistry = {};
|
|
458
458
|
this.queryList = [];
|
|
459
459
|
if (extendingModelDef) {
|
|
@@ -516,17 +516,17 @@ class Document extends MalloyElement {
|
|
|
516
516
|
if (this.hasAnnotation()) {
|
|
517
517
|
def.annotation = this.currentModelAnnotation();
|
|
518
518
|
}
|
|
519
|
-
for (const
|
|
520
|
-
const entryDef =
|
|
519
|
+
for (const [name, modelEntry] of this.documentModel) {
|
|
520
|
+
const entryDef = modelEntry.entry;
|
|
521
521
|
if ((0, malloy_types_1.isSourceDef)(entryDef) || entryDef.type === 'query') {
|
|
522
|
-
if (
|
|
523
|
-
def.exports.push(
|
|
522
|
+
if (modelEntry.exported) {
|
|
523
|
+
def.exports.push(name);
|
|
524
524
|
}
|
|
525
525
|
const newEntry = { ...entryDef };
|
|
526
526
|
if (newEntry.modelAnnotation === undefined && def.annotation) {
|
|
527
527
|
newEntry.modelAnnotation = def.annotation;
|
|
528
528
|
}
|
|
529
|
-
def.contents[
|
|
529
|
+
def.contents[name] = newEntry;
|
|
530
530
|
}
|
|
531
531
|
}
|
|
532
532
|
// Copy the accumulated sourceRegistry
|
|
@@ -535,7 +535,7 @@ class Document extends MalloyElement {
|
|
|
535
535
|
}
|
|
536
536
|
getEntry(str) {
|
|
537
537
|
var _a;
|
|
538
|
-
return (_a = this.globalNameSpace.getEntry(str)) !== null && _a !== void 0 ? _a : this.documentModel
|
|
538
|
+
return (_a = this.globalNameSpace.getEntry(str)) !== null && _a !== void 0 ? _a : this.documentModel.get(str);
|
|
539
539
|
}
|
|
540
540
|
setEntry(str, ent) {
|
|
541
541
|
// TODO this error message is going to be in the wrong place everywhere...
|
|
@@ -549,7 +549,7 @@ class Document extends MalloyElement {
|
|
|
549
549
|
if (this.didInitModel) {
|
|
550
550
|
this.modelWasModified = true;
|
|
551
551
|
}
|
|
552
|
-
this.documentModel
|
|
552
|
+
this.documentModel.set(str, ent);
|
|
553
553
|
// Maintain sourceRegistry for persistable sources with sourceID
|
|
554
554
|
if ((0, malloy_types_1.isSourceDef)(ent.entry) &&
|
|
555
555
|
(0, malloy_types_1.isPersistableSourceDef)(ent.entry) &&
|
|
@@ -255,8 +255,8 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
|
|
|
255
255
|
if (filterExpr.node === 'parameter') {
|
|
256
256
|
const name = filterExpr.path[0];
|
|
257
257
|
(_a = context.eventStream) === null || _a === void 0 ? void 0 : _a.emit('source-argument-compiled', { name });
|
|
258
|
-
const argument = context.arguments()
|
|
259
|
-
if (argument.value) {
|
|
258
|
+
const argument = (0, malloy_types_1.safeRecordGet)(context.arguments(), name);
|
|
259
|
+
if (argument === null || argument === void 0 ? void 0 : argument.value) {
|
|
260
260
|
filterExpr = argument.value;
|
|
261
261
|
}
|
|
262
262
|
else {
|
|
@@ -574,8 +574,8 @@ function generateParameterFragment(resultSet, context, expr, state) {
|
|
|
574
574
|
var _a;
|
|
575
575
|
const name = expr.path[0];
|
|
576
576
|
(_a = context.eventStream) === null || _a === void 0 ? void 0 : _a.emit('source-argument-compiled', { name });
|
|
577
|
-
const argument = context.arguments()
|
|
578
|
-
if (argument.value) {
|
|
577
|
+
const argument = (0, malloy_types_1.safeRecordGet)(context.arguments(), name);
|
|
578
|
+
if (argument === null || argument === void 0 ? void 0 : argument.value) {
|
|
579
579
|
return exprToSQL(resultSet, context, argument.value, state);
|
|
580
580
|
}
|
|
581
581
|
throw new Error(`Can't generate SQL, no value for ${expr.path}`);
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type * as Malloy from '@malloydata/malloy-interfaces';
|
|
2
2
|
import type { EventStream } from '../runtime_types';
|
|
3
|
+
/**
|
|
4
|
+
* A Record<string, V> used as a string-keyed map in serializable IR types.
|
|
5
|
+
* Direct bracket access (record[key]) is UNSAFE because Object.prototype
|
|
6
|
+
* property names like 'constructor', 'toString', 'valueOf' return inherited
|
|
7
|
+
* functions instead of undefined. Use safeRecordGet() to read entries.
|
|
8
|
+
*/
|
|
9
|
+
export type SafeRecord<V> = Record<string, V>;
|
|
10
|
+
/** Safely read from a SafeRecord. Returns undefined for non-own properties. */
|
|
11
|
+
export declare function safeRecordGet<V>(record: SafeRecord<V>, key: string): V | undefined;
|
|
12
|
+
/** Create an empty null-prototype SafeRecord (immune to prototype pollution). */
|
|
13
|
+
export declare function mkSafeRecord<V>(): SafeRecord<V>;
|
|
3
14
|
/**
|
|
4
15
|
* Field computations are compiled into an expression tree of "Expr"
|
|
5
16
|
* type nodes. Each node is one of these three interfaces. The
|
|
@@ -229,7 +240,7 @@ export interface BooleanLiteralNode extends ExprLeaf {
|
|
|
229
240
|
}
|
|
230
241
|
export interface RecordLiteralNode extends ExprWithKids {
|
|
231
242
|
node: 'recordLiteral';
|
|
232
|
-
kids:
|
|
243
|
+
kids: SafeRecord<Expr>;
|
|
233
244
|
typeDef: RecordTypeDef;
|
|
234
245
|
}
|
|
235
246
|
export interface ArrayLiteralNode extends ExprWithKids {
|
|
@@ -561,7 +572,7 @@ export type StructRef = string | SourceDef;
|
|
|
561
572
|
export declare function refIsStructDef(ref: StructRef): ref is SourceDef;
|
|
562
573
|
export type InvokedStructRef = {
|
|
563
574
|
structRef: StructRef;
|
|
564
|
-
sourceArguments?:
|
|
575
|
+
sourceArguments?: SafeRecord<Argument>;
|
|
565
576
|
};
|
|
566
577
|
export interface Filtered {
|
|
567
578
|
filterList?: FilterCondition[];
|
|
@@ -579,7 +590,7 @@ export interface Query extends Pipeline, Filtered, HasLocation {
|
|
|
579
590
|
type?: 'query';
|
|
580
591
|
name?: string;
|
|
581
592
|
structRef: StructRef;
|
|
582
|
-
sourceArguments?:
|
|
593
|
+
sourceArguments?: SafeRecord<Argument>;
|
|
583
594
|
annotation?: Annotation;
|
|
584
595
|
modelAnnotation?: Annotation;
|
|
585
596
|
compositeResolvedSourceDef?: SourceDef;
|
|
@@ -699,8 +710,8 @@ export interface PartitionCompositeDesc {
|
|
|
699
710
|
compositeFields: string[];
|
|
700
711
|
}
|
|
701
712
|
interface SourceDefBase extends StructDefBase, Filtered, ResultStructMetadata {
|
|
702
|
-
arguments?:
|
|
703
|
-
parameters?:
|
|
713
|
+
arguments?: SafeRecord<Argument>;
|
|
714
|
+
parameters?: SafeRecord<Parameter>;
|
|
704
715
|
queryTimezone?: string;
|
|
705
716
|
connection: string;
|
|
706
717
|
primaryKey?: PrimaryKeyRef;
|
|
@@ -950,7 +961,7 @@ export interface DependencyTree {
|
|
|
950
961
|
export interface ModelDef {
|
|
951
962
|
name: string;
|
|
952
963
|
exports: string[];
|
|
953
|
-
contents:
|
|
964
|
+
contents: SafeRecord<NamedModelObject>;
|
|
954
965
|
/**
|
|
955
966
|
* Registry mapping sourceID to source definitions for build graph construction.
|
|
956
967
|
* For sources in namespace: maps to SourceRegistryReference (look up in contents)
|
|
@@ -965,8 +976,8 @@ export interface ModelDef {
|
|
|
965
976
|
imports?: ImportLocation[];
|
|
966
977
|
}
|
|
967
978
|
/** Very common record type */
|
|
968
|
-
export type NamedSourceDefs =
|
|
969
|
-
export type NamedModelObjects =
|
|
979
|
+
export type NamedSourceDefs = SafeRecord<SourceDef>;
|
|
980
|
+
export type NamedModelObjects = SafeRecord<NamedModelObject>;
|
|
970
981
|
/** Malloy source annotations attached to objects */
|
|
971
982
|
export interface Annotation {
|
|
972
983
|
inherits?: Annotation;
|
|
@@ -1006,7 +1017,7 @@ export type MalloyQueryData = {
|
|
|
1006
1017
|
export interface DrillSource {
|
|
1007
1018
|
sourceExplore: string;
|
|
1008
1019
|
sourceFilters?: FilterCondition[];
|
|
1009
|
-
sourceArguments?:
|
|
1020
|
+
sourceArguments?: SafeRecord<Argument>;
|
|
1010
1021
|
}
|
|
1011
1022
|
export interface CompiledQuery extends DrillSource {
|
|
1012
1023
|
structs: SourceDef[];
|
|
@@ -1065,7 +1076,7 @@ export interface PrepareResultOptions {
|
|
|
1065
1076
|
/** Manifest of built tables (BuildID → entry), the build cache */
|
|
1066
1077
|
buildManifest?: BuildManifest;
|
|
1067
1078
|
/** Map from connectionName to connectionDigest (from Connection.getDigest()) */
|
|
1068
|
-
connectionDigests?:
|
|
1079
|
+
connectionDigests?: SafeRecord<string>;
|
|
1069
1080
|
/** If true, throw when a persist query's digest is not in the manifest */
|
|
1070
1081
|
strictPersist?: boolean;
|
|
1071
1082
|
}
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.TD = exports.ValueType = void 0;
|
|
27
|
+
exports.safeRecordGet = safeRecordGet;
|
|
28
|
+
exports.mkSafeRecord = mkSafeRecord;
|
|
27
29
|
exports.exprHasKids = exprHasKids;
|
|
28
30
|
exports.exprHasE = exprHasE;
|
|
29
31
|
exports.exprIsLeaf = exprIsLeaf;
|
|
@@ -96,6 +98,15 @@ exports.isValueBoolean = isValueBoolean;
|
|
|
96
98
|
exports.isValueTimestamp = isValueTimestamp;
|
|
97
99
|
exports.isValueDate = isValueDate;
|
|
98
100
|
exports.mergeUniqueKeyRequirement = mergeUniqueKeyRequirement;
|
|
101
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
102
|
+
/** Safely read from a SafeRecord. Returns undefined for non-own properties. */
|
|
103
|
+
function safeRecordGet(record, key) {
|
|
104
|
+
return hasOwn.call(record, key) ? record[key] : undefined;
|
|
105
|
+
}
|
|
106
|
+
/** Create an empty null-prototype SafeRecord (immune to prototype pollution). */
|
|
107
|
+
function mkSafeRecord() {
|
|
108
|
+
return Object.create(null);
|
|
109
|
+
}
|
|
99
110
|
function exprHasKids(e) {
|
|
100
111
|
return 'kids' in e;
|
|
101
112
|
}
|
|
@@ -13,7 +13,7 @@ const annotation_1 = require("../annotation");
|
|
|
13
13
|
* Resolve a source name to its definition from model contents.
|
|
14
14
|
*/
|
|
15
15
|
function resolveSource(modelDef, name) {
|
|
16
|
-
const obj = modelDef.contents
|
|
16
|
+
const obj = (0, malloy_types_1.safeRecordGet)(modelDef.contents, name);
|
|
17
17
|
return obj && (0, malloy_types_1.isSourceDef)(obj) ? obj : undefined;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
@@ -537,7 +537,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
537
537
|
const { buildManifest, connectionDigests, strictPersist } = (_b = qs.prepareResultOptions) !== null && _b !== void 0 ? _b : {};
|
|
538
538
|
// Check manifest for this source
|
|
539
539
|
if (buildManifest && connectionDigests) {
|
|
540
|
-
const connDigest = connectionDigests
|
|
540
|
+
const connDigest = (0, malloy_types_1.safeRecordGet)(connectionDigests, qs.structDef.connection);
|
|
541
541
|
if (connDigest) {
|
|
542
542
|
// Compile with empty opts to get manifest-ignorant SQL for BuildID
|
|
543
543
|
const fullRet = this.compileQueryToStages(qs.structDef.query, {}, undefined, false);
|
|
@@ -127,7 +127,7 @@ function resolveSourceID(modelDef, sourceID) {
|
|
|
127
127
|
if (!value)
|
|
128
128
|
return undefined;
|
|
129
129
|
if ((0, malloy_types_1.isSourceRegistryReference)(value.entry)) {
|
|
130
|
-
const obj = modelDef.contents
|
|
130
|
+
const obj = (0, malloy_types_1.safeRecordGet)(modelDef.contents, value.entry.name);
|
|
131
131
|
return obj && (0, malloy_types_1.isSourceDef)(obj) && (0, malloy_types_1.isPersistableSourceDef)(obj)
|
|
132
132
|
? obj
|
|
133
133
|
: undefined;
|
|
@@ -54,7 +54,7 @@ function expandPersistableSource(source, opts, quoteTablePath, compileQuery) {
|
|
|
54
54
|
const { buildManifest, connectionDigests, strictPersist } = opts;
|
|
55
55
|
// Try manifest lookup if we have the required info
|
|
56
56
|
if (buildManifest && connectionDigests) {
|
|
57
|
-
const connDigest = connectionDigests
|
|
57
|
+
const connDigest = (0, malloy_types_1.safeRecordGet)(connectionDigests, source.connection);
|
|
58
58
|
if (connDigest) {
|
|
59
59
|
// Get the SQL for this source to compute BuildID (no opts = full SQL)
|
|
60
60
|
const sql = getSourceSQL(source, quoteTablePath, compileQuery);
|
package/dist/model/utils.js
CHANGED
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MALLOY_VERSION = "0.0.
|
|
1
|
+
export declare const MALLOY_VERSION = "0.0.344";
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MALLOY_VERSION = void 0;
|
|
4
4
|
// generated with 'generate-version-file' script; do not edit manually
|
|
5
|
-
exports.MALLOY_VERSION = '0.0.
|
|
5
|
+
exports.MALLOY_VERSION = '0.0.344';
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/malloy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.344",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"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"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@malloydata/malloy-filter": "0.0.
|
|
49
|
-
"@malloydata/malloy-interfaces": "0.0.
|
|
50
|
-
"@malloydata/malloy-tag": "0.0.
|
|
48
|
+
"@malloydata/malloy-filter": "0.0.344",
|
|
49
|
+
"@malloydata/malloy-interfaces": "0.0.344",
|
|
50
|
+
"@malloydata/malloy-tag": "0.0.344",
|
|
51
51
|
"@noble/hashes": "^1.8.0",
|
|
52
52
|
"antlr4ts": "^0.5.0-alpha.4",
|
|
53
53
|
"assert": "^2.0.0",
|
package/CONTEXT.md
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# Malloy Core Package
|
|
2
|
-
|
|
3
|
-
The `malloy` package is the heart of the Malloy language implementation. It contains the compiler, translator, and runtime system that powers Malloy's semantic modeling and query capabilities.
|
|
4
|
-
|
|
5
|
-
## Package Structure
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
packages/malloy/
|
|
9
|
-
├── src/
|
|
10
|
-
│ ├── lang/ # Translator: Parse tree → AST → IR (see src/lang/CONTEXT.md)
|
|
11
|
-
│ ├── model/ # Compiler: IR → SQL (see src/model/CONTEXT.md)
|
|
12
|
-
│ ├── dialect/ # Database-specific SQL generation
|
|
13
|
-
│ ├── api/ # API layers (see src/api/CONTEXT.md)
|
|
14
|
-
│ │ └── foundation/ # Public API classes (see MALLOY_API.md)
|
|
15
|
-
│ └── connection/ # Database connection abstractions
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Two-Phase Architecture
|
|
19
|
-
|
|
20
|
-
The Malloy compilation process is split into two distinct phases:
|
|
21
|
-
|
|
22
|
-
### Phase 1: Translation (src/lang/)
|
|
23
|
-
The translator takes Malloy source code and transforms it into an Intermediate Representation (IR).
|
|
24
|
-
|
|
25
|
-
**Process:**
|
|
26
|
-
1. ANTLR parser generates parse tree from source code
|
|
27
|
-
2. Parse tree is transformed into Abstract Syntax Tree (AST)
|
|
28
|
-
3. AST is analyzed and transformed into IR
|
|
29
|
-
|
|
30
|
-
**Key characteristics:**
|
|
31
|
-
- IR is a **serializable data format** (plain objects, not class instances)
|
|
32
|
-
- IR fully describes the semantic model independent of SQL
|
|
33
|
-
- IR can be cached, transmitted, and reused across compilations
|
|
34
|
-
|
|
35
|
-
For detailed information about the translator, see [src/lang/CONTEXT.md](src/lang/CONTEXT.md).
|
|
36
|
-
|
|
37
|
-
### Phase 2: Compilation (src/model/)
|
|
38
|
-
The compiler takes IR and generates SQL queries for specific database dialects.
|
|
39
|
-
|
|
40
|
-
**Process:**
|
|
41
|
-
1. IR is read and analyzed
|
|
42
|
-
2. Query operations are transformed into SQL expressions
|
|
43
|
-
3. Dialect-specific SQL is generated
|
|
44
|
-
4. Metadata is generated for result processing
|
|
45
|
-
|
|
46
|
-
**Key characteristics:**
|
|
47
|
-
- Produces SQL that can be executed on target database
|
|
48
|
-
- Includes metadata to interpret and render results
|
|
49
|
-
- Dialect-agnostic until final SQL generation step
|
|
50
|
-
|
|
51
|
-
For detailed information about the compiler, see [src/model/CONTEXT.md](src/model/CONTEXT.md).
|
|
52
|
-
|
|
53
|
-
## Subsystem Context
|
|
54
|
-
|
|
55
|
-
For deeper details on specific subsystems:
|
|
56
|
-
- [MALLOY_API.md](MALLOY_API.md) - Public API classes (Model, PreparedQuery, Runtime, Materializers)
|
|
57
|
-
- [src/lang/CONTEXT.md](src/lang/CONTEXT.md) - Translator architecture (grammar, AST, IR generation)
|
|
58
|
-
- [src/model/CONTEXT.md](src/model/CONTEXT.md) - Compiler architecture (SQL generation, expression compilation)
|
package/MALLOY_API.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# Foundation API - Public API Classes
|
|
2
|
-
|
|
3
|
-
The directory `src/api/foundation/` is the main public API surface for the Malloy package. It exports classes that wrap internal IR types and provide user-facing functionality. See `src/api/CONTEXT.md` for a complete overview of all API layers.
|
|
4
|
-
|
|
5
|
-
## Core Data Classes
|
|
6
|
-
|
|
7
|
-
### `Model`
|
|
8
|
-
Wraps `ModelDef` (internal IR). Represents a compiled Malloy model.
|
|
9
|
-
|
|
10
|
-
- `explores: Explore[]` - All sources in the model (wrapped)
|
|
11
|
-
- `getPreparedQueryByName(name): PreparedQuery`
|
|
12
|
-
- `getPreparedQueryByIndex(index): PreparedQuery`
|
|
13
|
-
- `getExploreByName(name): Explore`
|
|
14
|
-
- `_modelDef` - Escape hatch to raw IR
|
|
15
|
-
|
|
16
|
-
### `PreparedQuery`
|
|
17
|
-
Wraps `Query` (internal IR) + `ModelDef`. A query that can be compiled to SQL.
|
|
18
|
-
|
|
19
|
-
- `getPreparedResult(options?): PreparedResult` - Compile to SQL
|
|
20
|
-
- `dialect: string`
|
|
21
|
-
- `name?: string`
|
|
22
|
-
- `_query`, `_modelDef` - Escape hatches
|
|
23
|
-
|
|
24
|
-
**Issue:** Each `getPreparedResult()` call creates a new `QueryModel`, reprocessing the ModelDef.
|
|
25
|
-
|
|
26
|
-
### `PreparedResult`
|
|
27
|
-
Wraps `CompiledQuery` (internal). The compiled SQL and metadata.
|
|
28
|
-
|
|
29
|
-
- `sql: string` - The generated SQL
|
|
30
|
-
- `connectionName: string` - Which connection to run against
|
|
31
|
-
- `resultExplore: Explore` - Schema of the result
|
|
32
|
-
|
|
33
|
-
### `Result`
|
|
34
|
-
Extends `PreparedResult`. Adds actual query result data.
|
|
35
|
-
|
|
36
|
-
- `data: DataArray` - The result rows
|
|
37
|
-
- `totalRows: number`
|
|
38
|
-
- `runStats: QueryRunStats`
|
|
39
|
-
|
|
40
|
-
### `Explore`
|
|
41
|
-
Wraps `StructDef` (internal). Represents a source (historical name was "explore").
|
|
42
|
-
|
|
43
|
-
- `name: string`
|
|
44
|
-
- `allFields: Field[]`
|
|
45
|
-
- `getFieldByName(name): Field`
|
|
46
|
-
- `getQueryByName(name): PreparedQuery` - Get a view as a query
|
|
47
|
-
|
|
48
|
-
### `ExploreField`
|
|
49
|
-
Extends `Explore`. A joined source (appears as a field in parent).
|
|
50
|
-
|
|
51
|
-
### `Query`
|
|
52
|
-
Wraps `TurtleDef`. Represents a view definition. **Different from `PreparedQuery`.**
|
|
53
|
-
|
|
54
|
-
### `QueryField`
|
|
55
|
-
Extends `Query`. A view that appears as a field in an explore.
|
|
56
|
-
|
|
57
|
-
### `AtomicField` and subclasses
|
|
58
|
-
Field wrappers: `StringField`, `NumberField`, `DateField`, `TimestampField`, `BooleanField`, `JSONField`, `UnsupportedField`
|
|
59
|
-
|
|
60
|
-
## Runtime & Materializer Classes
|
|
61
|
-
|
|
62
|
-
### `Runtime`
|
|
63
|
-
Entry point for loading and running Malloy. Holds URLReader, connections, event stream.
|
|
64
|
-
|
|
65
|
-
- `loadModel(source): ModelMaterializer`
|
|
66
|
-
- `loadQuery(query): QueryMaterializer`
|
|
67
|
-
- `getModel(source): Promise<Model>`
|
|
68
|
-
- `getQuery(query): Promise<PreparedQuery>`
|
|
69
|
-
|
|
70
|
-
### `SingleConnectionRuntime`
|
|
71
|
-
Extends `Runtime`. For single-connection use cases.
|
|
72
|
-
|
|
73
|
-
### `ConnectionRuntime`
|
|
74
|
-
Extends `Runtime`. Holds array of connections.
|
|
75
|
-
|
|
76
|
-
### `ModelMaterializer`
|
|
77
|
-
Fluent builder for loading models. Returned by `runtime.loadModel()`.
|
|
78
|
-
|
|
79
|
-
- `getModel(): Promise<Model>`
|
|
80
|
-
- `loadQueryByName(name): QueryMaterializer`
|
|
81
|
-
- `loadExploreByName(name): ExploreMaterializer`
|
|
82
|
-
- `extendModel(source): ModelMaterializer`
|
|
83
|
-
|
|
84
|
-
### `QueryMaterializer`
|
|
85
|
-
Fluent builder for queries. Returned by `modelMaterializer.loadQueryByName()`.
|
|
86
|
-
|
|
87
|
-
- `getPreparedQuery(): Promise<PreparedQuery>`
|
|
88
|
-
- `getPreparedResult(): Promise<PreparedResult>`
|
|
89
|
-
- `getSQL(): Promise<string>`
|
|
90
|
-
- `run(options?): Promise<Result>`
|
|
91
|
-
|
|
92
|
-
### `PreparedResultMaterializer`
|
|
93
|
-
Fluent builder for prepared results.
|
|
94
|
-
|
|
95
|
-
### `ExploreMaterializer`
|
|
96
|
-
Fluent builder for explores/sources.
|
|
97
|
-
|
|
98
|
-
## Result Data Classes
|
|
99
|
-
|
|
100
|
-
### `DataArray`
|
|
101
|
-
Iterable array of `DataRecord`. Query result rows.
|
|
102
|
-
|
|
103
|
-
### `DataRecord`
|
|
104
|
-
Single result row. Field values accessed by name.
|
|
105
|
-
|
|
106
|
-
## Utility Classes
|
|
107
|
-
|
|
108
|
-
- `Parse` - Parsed (not compiled) Malloy document
|
|
109
|
-
- `Malloy` - Static methods: `parse()`, `compile()`, `run()`
|
|
110
|
-
- `MalloyError` - Error with structured problems
|
|
111
|
-
- `EmptyURLReader`, `InMemoryURLReader` - URLReader implementations
|
|
112
|
-
- `FixedConnectionMap` - LookupConnection implementation
|
|
113
|
-
- `CacheManager`, `InMemoryModelCache` - Caching
|
|
114
|
-
- `JSONWriter`, `CSVWriter` - Result writers
|
|
115
|
-
- `DocumentSymbol`, `DocumentPosition`, `DocumentRange`, etc. - IDE support
|
|
116
|
-
|
|
117
|
-
## Naming Issues
|
|
118
|
-
|
|
119
|
-
| Current Name | What It Actually Is |
|
|
120
|
-
|--------------|---------------------|
|
|
121
|
-
| `Explore` | A source (historical name) |
|
|
122
|
-
| `PreparedQuery` | Uncompiled query holding IR |
|
|
123
|
-
| `PreparedResult` | Compiled query with SQL (not a "result") |
|
|
124
|
-
| `Query` | A view/turtle definition |
|
|
125
|
-
|
|
126
|
-
## Known Architectural Issues
|
|
127
|
-
|
|
128
|
-
1. **Transient QueryModel**: `PreparedQuery.getPreparedResult()` creates new `QueryModel` each call - expensive.
|
|
129
|
-
3. **Name collision**: `Query` class (view wrapper) vs internal `Query` type vs `PreparedQuery`
|