@malloydata/malloy 0.0.432 → 0.0.434
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/foundation/compile.d.ts +2 -1
- package/dist/api/foundation/compile.js +8 -6
- package/dist/api/foundation/config.js +6 -15
- package/dist/api/foundation/runtime.d.ts +2 -7
- package/dist/api/foundation/runtime.js +10 -16
- package/dist/api/foundation/types.d.ts +1 -0
- package/dist/connection/validate_table_path.d.ts +1 -2
- package/dist/connection/validate_table_path.js +1 -2
- package/dist/dialect/databricks/databricks.js +6 -6
- package/dist/dialect/dialect.d.ts +21 -2
- package/dist/dialect/dialect.js +33 -3
- package/dist/dialect/duckdb/duckdb.js +7 -21
- package/dist/dialect/mysql/mysql.js +70 -29
- package/dist/dialect/pg_impl.js +12 -12
- package/dist/dialect/postgres/postgres.js +3 -3
- package/dist/dialect/snowflake/snowflake.js +12 -12
- package/dist/dialect/standardsql/standardsql.d.ts +1 -1
- package/dist/dialect/standardsql/standardsql.js +16 -13
- package/dist/dialect/trino/trino.d.ts +3 -3
- package/dist/dialect/trino/trino.js +32 -28
- package/dist/lang/ast/expressions/expr-aggregate-function.js +20 -26
- package/dist/lang/ast/field-space/def-space.js +1 -0
- package/dist/lang/ast/field-space/index-field-space.js +7 -33
- package/dist/lang/ast/field-space/parameter-space.js +2 -0
- package/dist/lang/ast/field-space/query-spaces.d.ts +10 -0
- package/dist/lang/ast/field-space/query-spaces.js +33 -32
- package/dist/lang/ast/field-space/static-space.d.ts +34 -1
- package/dist/lang/ast/field-space/static-space.js +118 -75
- package/dist/lang/ast/source-elements/refined-source.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +2 -1
- package/dist/lang/ast/source-properties/timezone-statement.d.ts +4 -3
- package/dist/lang/ast/source-properties/timezone-statement.js +14 -12
- package/dist/lang/ast/types/lookup-result.d.ts +2 -0
- package/dist/lang/composite-source-utils.js +10 -3
- package/dist/lang/malloy-to-ast.js +2 -8
- package/dist/lang/parse-log.d.ts +1 -3
- package/dist/lang/parse-malloy.d.ts +3 -2
- package/dist/lang/parse-malloy.js +2 -1
- package/dist/lang/test/test-translator.d.ts +2 -1
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/model/join_instance.d.ts +1 -0
- package/dist/model/join_instance.js +9 -1
- package/dist/model/query_query.d.ts +25 -0
- package/dist/model/query_query.js +142 -17
- package/dist/model/sql_block.d.ts +2 -2
- package/dist/model/sql_block.js +11 -47
- package/dist/model/sql_compiled.d.ts +3 -1
- package/dist/model/sql_compiled.js +8 -6
- package/dist/model/stage_writer.js +3 -3
- package/dist/model/utils.d.ts +2 -2
- package/dist/model/utils.js +2 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/femto-config.motly +2 -0
- package/package.json +4 -4
|
@@ -12,7 +12,6 @@ const field_references_1 = require("../query-items/field-references");
|
|
|
12
12
|
const space_field_1 = require("../types/space-field");
|
|
13
13
|
const query_spaces_1 = require("./query-spaces");
|
|
14
14
|
const reference_field_1 = require("./reference-field");
|
|
15
|
-
const static_space_1 = require("./static-space");
|
|
16
15
|
class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
|
|
17
16
|
constructor() {
|
|
18
17
|
super(...arguments);
|
|
@@ -81,46 +80,21 @@ class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
|
|
|
81
80
|
}
|
|
82
81
|
addRefineFromFields(_refineThis) { }
|
|
83
82
|
addWild(wild) {
|
|
84
|
-
var _a, _b;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// walk path to determine namespace for *
|
|
89
|
-
for (const pathPart of wild.joinPath.list) {
|
|
90
|
-
const part = pathPart.refString;
|
|
91
|
-
joinPath.push(part);
|
|
92
|
-
const ent = current.entry(part);
|
|
93
|
-
if (ent) {
|
|
94
|
-
if (ent instanceof static_space_1.StructSpaceField) {
|
|
95
|
-
current = ent.fieldSpace;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
pathPart.logError('invalid-wildcard-source', `Field '${part}' does not contain rows and cannot be expanded with '*'`);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
pathPart.logError('wildcard-source-not-found', `No such field as '${part}'`);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
83
|
+
var _a, _b, _c, _d;
|
|
84
|
+
const entries = this.wildcardExpansion(wild);
|
|
85
|
+
if (entries === undefined) {
|
|
86
|
+
return;
|
|
107
87
|
}
|
|
88
|
+
const joinPath = (_b = (_a = wild.joinPath) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : [];
|
|
108
89
|
const dialect = this.dialectObj();
|
|
109
90
|
const expandEntries = [];
|
|
110
|
-
for (const [name, entry] of
|
|
111
|
-
if (wild.except.has(name)) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
if (entry.refType === 'parameter') {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
91
|
+
for (const [name, entry] of entries) {
|
|
117
92
|
const indexName = field_references_1.IndexFieldReference.indexOutputName([
|
|
118
93
|
...joinPath,
|
|
119
94
|
name,
|
|
120
95
|
]);
|
|
121
96
|
if (this.entry(indexName)) {
|
|
122
|
-
|
|
123
|
-
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})` : ''}`);
|
|
97
|
+
this.logWildcardConflict(wild, name, (_d = (_c = this.expandedWild.get(indexName)) === null || _c === void 0 ? void 0 : _c.path) === null || _d === void 0 ? void 0 : _d.join('.'));
|
|
124
98
|
}
|
|
125
99
|
else {
|
|
126
100
|
const eTypeDesc = entry.typeDesc();
|
|
@@ -41,6 +41,7 @@ class ParameterSpace {
|
|
|
41
41
|
error: {
|
|
42
42
|
message: `\`${name}\` is not defined`,
|
|
43
43
|
code: 'parameter-not-found',
|
|
44
|
+
at: name,
|
|
44
45
|
},
|
|
45
46
|
found: undefined,
|
|
46
47
|
};
|
|
@@ -52,6 +53,7 @@ class ParameterSpace {
|
|
|
52
53
|
.slice(1)
|
|
53
54
|
.join('.')}\``,
|
|
54
55
|
code: 'invalid-parameter-reference',
|
|
56
|
+
at: name,
|
|
55
57
|
},
|
|
56
58
|
found: undefined,
|
|
57
59
|
};
|
|
@@ -57,6 +57,16 @@ export declare abstract class QueryOperationSpace extends RefinedSpace implement
|
|
|
57
57
|
inputSpace(): QueryInputSpace;
|
|
58
58
|
outputSpace(): QueryOperationSpace;
|
|
59
59
|
isQueryOutputSpace(): boolean;
|
|
60
|
+
protected logWildcardConflict(wild: WildcardFieldReference, name: string, conflict: string | undefined): void;
|
|
61
|
+
/**
|
|
62
|
+
* A `*` expands the fields a user of the finished source will see: it
|
|
63
|
+
* walks its path and reads the namespace at the end as a public reader,
|
|
64
|
+
* with the same walk and the same per-entry rule as a lookup by name.
|
|
65
|
+
* A disallowed path is an error, since the user wrote it; a disallowed
|
|
66
|
+
* field is dropped, since naming it would disclose it. Returns undefined,
|
|
67
|
+
* having logged, when the path fails or nothing is left to expand.
|
|
68
|
+
*/
|
|
69
|
+
protected wildcardExpansion(wild: WildcardFieldReference): [string, SpaceEntry][] | undefined;
|
|
60
70
|
protected addWild(wild: WildcardFieldReference): void;
|
|
61
71
|
protected addValidatedCompositeFieldUserFromEntry(name: string, entry: SpaceEntry): void;
|
|
62
72
|
addFieldUserFromFilter(filter: model.FilterCondition): void;
|
|
@@ -114,43 +114,44 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
|
|
|
114
114
|
isQueryOutputSpace() {
|
|
115
115
|
return true;
|
|
116
116
|
}
|
|
117
|
+
logWildcardConflict(wild, name, conflict) {
|
|
118
|
+
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})` : ''}`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A `*` expands the fields a user of the finished source will see: it
|
|
122
|
+
* walks its path and reads the namespace at the end as a public reader,
|
|
123
|
+
* with the same walk and the same per-entry rule as a lookup by name.
|
|
124
|
+
* A disallowed path is an error, since the user wrote it; a disallowed
|
|
125
|
+
* field is dropped, since naming it would disclose it. Returns undefined,
|
|
126
|
+
* having logged, when the path fails or nothing is left to expand.
|
|
127
|
+
*/
|
|
128
|
+
wildcardExpansion(wild) {
|
|
129
|
+
var _a, _b, _c;
|
|
130
|
+
const ns = (0, static_space_1.resolveNamespace)(this.exprSpace, (_b = (_a = wild.joinPath) === null || _a === void 0 ? void 0 : _a.list) !== null && _b !== void 0 ? _b : [], 'public', '*');
|
|
131
|
+
if (ns.error) {
|
|
132
|
+
const at = (_c = ns.error.at) !== null && _c !== void 0 ? _c : wild;
|
|
133
|
+
at.logError(ns.error.code, ns.error.message);
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const entries = (0, static_space_1.accessibleEntries)(ns.space, ns.accessLevel).filter(([name, entry]) => !wild.except.has(name) && entry.refType !== 'parameter');
|
|
137
|
+
if (entries.length === 0) {
|
|
138
|
+
wild.logError('wildcard-matched-no-fields', `'${wild.refString}' did not match any fields`);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return entries;
|
|
142
|
+
}
|
|
117
143
|
addWild(wild) {
|
|
118
|
-
var _a;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// walk path to determine namespace for *
|
|
123
|
-
for (const pathPart of wild.joinPath.list) {
|
|
124
|
-
const part = pathPart.refString;
|
|
125
|
-
joinPath.push(part);
|
|
126
|
-
const ent = current.entry(part);
|
|
127
|
-
if (ent) {
|
|
128
|
-
if (ent instanceof static_space_1.StructSpaceField) {
|
|
129
|
-
current = ent.fieldSpace;
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
pathPart.logError('invalid-wildcard-source', `Field '${part}' does not contain rows and cannot be expanded with '*'`);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
pathPart.logError('wildcard-source-not-defined', `No such field as '${part}'`);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
144
|
+
var _a, _b, _c;
|
|
145
|
+
const entries = this.wildcardExpansion(wild);
|
|
146
|
+
if (entries === undefined) {
|
|
147
|
+
return;
|
|
141
148
|
}
|
|
149
|
+
const joinPath = (_b = (_a = wild.joinPath) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : [];
|
|
142
150
|
const dialect = this.dialectObj();
|
|
143
151
|
const expandEntries = [];
|
|
144
|
-
for (const [name, entry] of
|
|
145
|
-
if (wild.except.has(name)) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
if (entry.refType === 'parameter') {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
152
|
+
for (const [name, entry] of entries) {
|
|
151
153
|
if (this.entry(name)) {
|
|
152
|
-
|
|
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
|
+
this.logWildcardConflict(wild, name, (_c = this.expandedWild.get(name)) === null || _c === void 0 ? void 0 : _c.path.join('.'));
|
|
154
155
|
}
|
|
155
156
|
else {
|
|
156
157
|
const eType = entry.typeDesc();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Dialect } from '../../../dialect/dialect';
|
|
2
2
|
import type { FieldDef, StructDef, SourceDef, JoinFieldDef, AccessModifierLabel } from '../../../model/malloy_types';
|
|
3
3
|
import type { SpaceEntry } from '../types/space-entry';
|
|
4
|
-
import type { LookupResult } from '../types/lookup-result';
|
|
4
|
+
import type { JoinPath, LookupError, LookupResult } from '../types/lookup-result';
|
|
5
5
|
import type { FieldName, FieldSpace, QueryFieldSpace, SourceFieldSpace } from '../types/field-space';
|
|
6
6
|
import { SpaceField } from '../types/space-field';
|
|
7
7
|
import { StructSpaceFieldBase } from './struct-space-field-base';
|
|
@@ -50,3 +50,36 @@ export declare class StaticSourceSpace extends StaticSpace implements SourceFiel
|
|
|
50
50
|
emptyStructDef(): SourceDef;
|
|
51
51
|
accessProtectionLevel(): AccessModifierLabel;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* A namespace reached by walking a join path, and the access level a reader
|
|
55
|
+
* who started at `accessLevel` has once they arrive there.
|
|
56
|
+
*/
|
|
57
|
+
export interface NamespaceRead {
|
|
58
|
+
space: FieldSpace;
|
|
59
|
+
accessLevel: AccessModifierLabel;
|
|
60
|
+
joinPath: JoinPath;
|
|
61
|
+
error: undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Walk `path` from `from`, one `readEntry` per hop, narrowing the access
|
|
65
|
+
* level at each join. `lookup()` walks the path before a name with it and
|
|
66
|
+
* `*` walks the path before a star. `member` is what is about to be read from the namespace at the end
|
|
67
|
+
* of the path, a field name or `'*'`, named in the error when a hop is not a
|
|
68
|
+
* namespace.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveNamespace(from: FieldSpace, path: FieldName[], accessLevel: AccessModifierLabel, member: string): NamespaceRead | LookupError;
|
|
71
|
+
interface EntryRead {
|
|
72
|
+
found: SpaceEntry;
|
|
73
|
+
error: undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read one name from a namespace on behalf of a reader at `accessLevel`.
|
|
77
|
+
* Records the reference, and refuses the entry if the reader may not see it.
|
|
78
|
+
*/
|
|
79
|
+
export declare function readEntry(space: FieldSpace, name: FieldName, accessLevel: AccessModifierLabel): EntryRead | LookupError;
|
|
80
|
+
/**
|
|
81
|
+
* Every entry of `space` a reader at `accessLevel` may see, by the same
|
|
82
|
+
* rule `readEntry` applies to one name.
|
|
83
|
+
*/
|
|
84
|
+
export declare function accessibleEntries(space: FieldSpace, accessLevel: AccessModifierLabel): [string, SpaceEntry][];
|
|
85
|
+
export {};
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.StaticSourceSpace = exports.StructSpaceField = exports.StaticSpace = void 0;
|
|
8
|
+
exports.resolveNamespace = resolveNamespace;
|
|
9
|
+
exports.readEntry = readEntry;
|
|
10
|
+
exports.accessibleEntries = accessibleEntries;
|
|
8
11
|
const dialect_map_1 = require("../../../dialect/dialect_map");
|
|
9
12
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
10
13
|
const field_space_1 = require("../types/field-space");
|
|
@@ -107,81 +110,19 @@ class StaticSpace {
|
|
|
107
110
|
}
|
|
108
111
|
lookup(path, accessLevel) {
|
|
109
112
|
accessLevel !== null && accessLevel !== void 0 ? accessLevel : (accessLevel = this.accessProtectionLevel());
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
error: {
|
|
116
|
-
message: `'${head}' is not defined`,
|
|
117
|
-
code: 'field-not-found',
|
|
118
|
-
},
|
|
119
|
-
found,
|
|
120
|
-
};
|
|
113
|
+
const last = path[path.length - 1];
|
|
114
|
+
const ns = resolveNamespace(this, path.slice(0, -1), accessLevel, last.refString);
|
|
115
|
+
if (ns.error) {
|
|
116
|
+
return ns;
|
|
121
117
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!(found instanceof struct_space_field_base_1.StructSpaceFieldBase) && (0, malloy_types_1.isJoined)(definition)) {
|
|
126
|
-
// We have looked up a field which is a join, but not a StructSpaceField
|
|
127
|
-
// because it is someting like "dimension: joinedArray is arrayComputation"
|
|
128
|
-
// which wasn't known to be a join when the fieldspace was constructed.
|
|
129
|
-
// TODO don't make one of these every time you do a lookup
|
|
130
|
-
found = new StructSpaceField(definition, this.structDialect, this.structConnection);
|
|
131
|
-
}
|
|
132
|
-
// cswenson review todo I don't know how to count the reference properly now
|
|
133
|
-
// i tried only writing it as a join reference if there was more in the path
|
|
134
|
-
// but that failed because lookup([JOINNAME]) is called when translating JOINNAME.AGGREGATE(...)
|
|
135
|
-
// with a 1-length-path but that IS a join reference and there is a test
|
|
136
|
-
head.addReference({
|
|
137
|
-
type: found instanceof struct_space_field_base_1.StructSpaceFieldBase
|
|
138
|
-
? 'joinReference'
|
|
139
|
-
: 'fieldReference',
|
|
140
|
-
definition: {
|
|
141
|
-
type: definition.type,
|
|
142
|
-
annotations: definition.annotations,
|
|
143
|
-
location: definition.location,
|
|
144
|
-
},
|
|
145
|
-
location: head.location,
|
|
146
|
-
text: head.refString,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
if (definition === null || definition === void 0 ? void 0 : definition.accessModifier) {
|
|
150
|
-
if (!accessAllowed(accessLevel, definition.accessModifier)) {
|
|
151
|
-
return {
|
|
152
|
-
error: {
|
|
153
|
-
message: `'${head}' is ${definition === null || definition === void 0 ? void 0 : definition.accessModifier}`,
|
|
154
|
-
code: 'field-not-accessible',
|
|
155
|
-
},
|
|
156
|
-
found: undefined,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
} // cswenson review todo { else this is SpaceEntry not a field which can only be a param and what is going on? }
|
|
161
|
-
const joinPath = found instanceof struct_space_field_base_1.StructSpaceFieldBase
|
|
162
|
-
? [{ ...found.joinPathElement, name: head.refString }]
|
|
163
|
-
: [];
|
|
164
|
-
if (rest.length) {
|
|
165
|
-
if (found instanceof struct_space_field_base_1.StructSpaceFieldBase) {
|
|
166
|
-
const restResult = found.fieldSpace.lookup(rest, lessPermissiveAccessLevel(accessLevel, found.fieldSpace.accessProtectionLevel()));
|
|
167
|
-
if (restResult.found) {
|
|
168
|
-
return {
|
|
169
|
-
...restResult,
|
|
170
|
-
joinPath: [...joinPath, ...restResult.joinPath],
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
return restResult;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
error: {
|
|
179
|
-
message: `'${head}' cannot contain a '${rest[0]}'`,
|
|
180
|
-
code: 'invalid-property-access-in-field-reference',
|
|
181
|
-
},
|
|
182
|
-
found: undefined,
|
|
183
|
-
};
|
|
118
|
+
const read = readEntry(ns.space, last, ns.accessLevel);
|
|
119
|
+
if (read.error) {
|
|
120
|
+
return read;
|
|
184
121
|
}
|
|
122
|
+
const found = read.found;
|
|
123
|
+
const joinPath = found instanceof struct_space_field_base_1.StructSpaceFieldBase
|
|
124
|
+
? [...ns.joinPath, { ...found.joinPathElement, name: last.refString }]
|
|
125
|
+
: ns.joinPath;
|
|
185
126
|
return { found, error: undefined, joinPath, isOutputField: false };
|
|
186
127
|
}
|
|
187
128
|
isQueryFieldSpace() {
|
|
@@ -225,9 +166,111 @@ class StaticSourceSpace extends StaticSpace {
|
|
|
225
166
|
}
|
|
226
167
|
}
|
|
227
168
|
exports.StaticSourceSpace = StaticSourceSpace;
|
|
169
|
+
/**
|
|
170
|
+
* Walk `path` from `from`, one `readEntry` per hop, narrowing the access
|
|
171
|
+
* level at each join. `lookup()` walks the path before a name with it and
|
|
172
|
+
* `*` walks the path before a star. `member` is what is about to be read from the namespace at the end
|
|
173
|
+
* of the path, a field name or `'*'`, named in the error when a hop is not a
|
|
174
|
+
* namespace.
|
|
175
|
+
*/
|
|
176
|
+
function resolveNamespace(from, path, accessLevel, member) {
|
|
177
|
+
let space = from;
|
|
178
|
+
const joinPath = [];
|
|
179
|
+
for (let i = 0; i < path.length; i++) {
|
|
180
|
+
const hop = path[i];
|
|
181
|
+
const read = readEntry(space, hop, accessLevel);
|
|
182
|
+
if (read.error) {
|
|
183
|
+
return read;
|
|
184
|
+
}
|
|
185
|
+
if (!(read.found instanceof struct_space_field_base_1.StructSpaceFieldBase)) {
|
|
186
|
+
const next = i + 1 < path.length ? path[i + 1].refString : member;
|
|
187
|
+
const message = next === '*'
|
|
188
|
+
? `'${hop}' does not contain fields and cannot be expanded with '*'`
|
|
189
|
+
: `'${hop}' cannot contain a '${next}'`;
|
|
190
|
+
return {
|
|
191
|
+
error: {
|
|
192
|
+
message,
|
|
193
|
+
code: 'invalid-property-access-in-field-reference',
|
|
194
|
+
at: hop,
|
|
195
|
+
},
|
|
196
|
+
found: undefined,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
joinPath.push({ ...read.found.joinPathElement, name: hop.refString });
|
|
200
|
+
space = read.found.fieldSpace;
|
|
201
|
+
accessLevel = lessPermissiveAccessLevel(accessLevel, space.accessProtectionLevel());
|
|
202
|
+
}
|
|
203
|
+
return { space, accessLevel, joinPath, error: undefined };
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Read one name from a namespace on behalf of a reader at `accessLevel`.
|
|
207
|
+
* Records the reference, and refuses the entry if the reader may not see it.
|
|
208
|
+
*/
|
|
209
|
+
function readEntry(space, name, accessLevel) {
|
|
210
|
+
let found = space.entry(name.refString);
|
|
211
|
+
let restriction;
|
|
212
|
+
if (!found) {
|
|
213
|
+
return {
|
|
214
|
+
error: {
|
|
215
|
+
message: `'${name}' is not defined`,
|
|
216
|
+
code: 'field-not-found',
|
|
217
|
+
at: name,
|
|
218
|
+
},
|
|
219
|
+
found: undefined,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (found instanceof space_field_1.SpaceField) {
|
|
223
|
+
const definition = found.fieldDef();
|
|
224
|
+
restriction = definition === null || definition === void 0 ? void 0 : definition.accessModifier;
|
|
225
|
+
if (definition) {
|
|
226
|
+
if (!(found instanceof struct_space_field_base_1.StructSpaceFieldBase) && (0, malloy_types_1.isJoined)(definition)) {
|
|
227
|
+
// A field which turned out to be a join after the space was built,
|
|
228
|
+
// e.g. "dimension: joinedArray is arrayComputation", so the entry
|
|
229
|
+
// is not a StructSpaceField; promote it so the path can continue.
|
|
230
|
+
found = new StructSpaceField(definition, space.dialectName(), space.connectionName());
|
|
231
|
+
}
|
|
232
|
+
// A one-element path to a join is still a join reference:
|
|
233
|
+
// JOIN.aggregate() looks the join up on its own.
|
|
234
|
+
name.addReference({
|
|
235
|
+
type: found instanceof struct_space_field_base_1.StructSpaceFieldBase
|
|
236
|
+
? 'joinReference'
|
|
237
|
+
: 'fieldReference',
|
|
238
|
+
definition: {
|
|
239
|
+
type: definition.type,
|
|
240
|
+
annotations: definition.annotations,
|
|
241
|
+
location: definition.location,
|
|
242
|
+
},
|
|
243
|
+
location: name.location,
|
|
244
|
+
text: name.refString,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (restriction && !accessAllowed(accessLevel, restriction)) {
|
|
249
|
+
return {
|
|
250
|
+
error: {
|
|
251
|
+
message: `'${name}' is ${restriction}`,
|
|
252
|
+
code: 'field-not-accessible',
|
|
253
|
+
at: name,
|
|
254
|
+
},
|
|
255
|
+
found: undefined,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return { found, error: undefined };
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Every entry of `space` a reader at `accessLevel` may see, by the same
|
|
262
|
+
* rule `readEntry` applies to one name.
|
|
263
|
+
*/
|
|
264
|
+
function accessibleEntries(space, accessLevel) {
|
|
265
|
+
return space.entries().filter(([, entry]) => {
|
|
266
|
+
var _a;
|
|
267
|
+
const restriction = entry instanceof space_field_1.SpaceField
|
|
268
|
+
? (_a = entry.fieldDef()) === null || _a === void 0 ? void 0 : _a.accessModifier
|
|
269
|
+
: undefined;
|
|
270
|
+
return restriction === undefined || accessAllowed(accessLevel, restriction);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
228
273
|
function accessAllowed(accessLevel, accessModifier) {
|
|
229
|
-
if (accessModifier === 'public')
|
|
230
|
-
return true;
|
|
231
274
|
if (accessLevel === 'internal')
|
|
232
275
|
return accessModifier === 'internal';
|
|
233
276
|
if (accessLevel === 'private')
|
|
@@ -81,7 +81,7 @@ class RefinedSource extends source_1.Source {
|
|
|
81
81
|
filters.push(el);
|
|
82
82
|
}
|
|
83
83
|
else if (el instanceof timezone_statement_1.TimezoneStatement) {
|
|
84
|
-
newTimezone = el.
|
|
84
|
+
newTimezone = el.timezoneName();
|
|
85
85
|
}
|
|
86
86
|
else {
|
|
87
87
|
errTo.logError('unexpected-source-property', `Unexpected source property: '${errTo.elementType}'`);
|
|
@@ -19,12 +19,13 @@ class SQLSource extends source_1.Source {
|
|
|
19
19
|
this.connectionNameInvalid = false;
|
|
20
20
|
}
|
|
21
21
|
sqlSourceRequest(doc) {
|
|
22
|
+
var _a;
|
|
22
23
|
const partialModel = this.select.containsQueries
|
|
23
24
|
? doc.modelDef()
|
|
24
25
|
: undefined;
|
|
25
26
|
const [valid, phrases] = this.select.sqlPhrases(this.connectionName.refString);
|
|
26
27
|
if (valid) {
|
|
27
|
-
return (0, sql_block_1.getSourceRequest)(phrases, this.connectionName.refString, partialModel);
|
|
28
|
+
return (0, sql_block_1.getSourceRequest)(phrases, this.connectionName.refString, partialModel, (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.virtualMap);
|
|
28
29
|
}
|
|
29
30
|
return undefined;
|
|
30
31
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { ExprString } from '../expressions/expr-string';
|
|
1
2
|
import type { QueryBuilder } from '../types/query-builder';
|
|
2
3
|
import { MalloyElement } from '../types/malloy-element';
|
|
3
4
|
import type { QueryPropertyInterface } from '../types/query-property-interface';
|
|
4
5
|
export declare class TimezoneStatement extends MalloyElement implements QueryPropertyInterface {
|
|
5
|
-
readonly
|
|
6
|
+
private readonly timezone;
|
|
6
7
|
elementType: string;
|
|
7
8
|
forceQueryClass: undefined;
|
|
8
9
|
queryRefinementStage: undefined;
|
|
9
|
-
constructor(
|
|
10
|
-
|
|
10
|
+
constructor(timezone: ExprString);
|
|
11
|
+
timezoneName(): string | undefined;
|
|
11
12
|
queryExecute(executeFor: QueryBuilder): void;
|
|
12
13
|
}
|
|
@@ -5,27 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.TimezoneStatement = void 0;
|
|
8
|
+
const dialect_1 = require("../../../dialect/dialect");
|
|
8
9
|
const malloy_element_1 = require("../types/malloy-element");
|
|
9
|
-
const luxon_1 = require("luxon");
|
|
10
10
|
class TimezoneStatement extends malloy_element_1.MalloyElement {
|
|
11
|
-
constructor(
|
|
12
|
-
super();
|
|
13
|
-
this.
|
|
11
|
+
constructor(timezone) {
|
|
12
|
+
super({ timezone });
|
|
13
|
+
this.timezone = timezone;
|
|
14
14
|
this.elementType = 'timezone';
|
|
15
15
|
this.forceQueryClass = undefined;
|
|
16
16
|
this.queryRefinementStage = undefined;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
catch {
|
|
24
|
-
return false;
|
|
18
|
+
timezoneName() {
|
|
19
|
+
const name = this.timezone.value;
|
|
20
|
+
if (!dialect_1.Dialect.isSafeTimezoneName(name)) {
|
|
21
|
+
this.logError('invalid-timezone', { timezone: name }, { at: this.timezone.location });
|
|
22
|
+
return undefined;
|
|
25
23
|
}
|
|
24
|
+
return name;
|
|
26
25
|
}
|
|
27
26
|
queryExecute(executeFor) {
|
|
28
|
-
|
|
27
|
+
const timezone = this.timezoneName();
|
|
28
|
+
if (timezone !== undefined) {
|
|
29
|
+
executeFor.resultFS.setTimezone(timezone);
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
exports.TimezoneStatement = TimezoneStatement;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { JoinElementType, JoinType } from '../../../model';
|
|
2
2
|
import type { MessageCode } from '../../parse-log';
|
|
3
3
|
import type { SpaceEntry } from './space-entry';
|
|
4
|
+
import type { FieldName } from './field-space';
|
|
4
5
|
export interface JoinPathElement {
|
|
5
6
|
name: string;
|
|
6
7
|
joinElementType: JoinElementType;
|
|
@@ -17,6 +18,7 @@ export interface LookupError {
|
|
|
17
18
|
error: {
|
|
18
19
|
message: string;
|
|
19
20
|
code: MessageCode;
|
|
21
|
+
at?: FieldName | undefined;
|
|
20
22
|
};
|
|
21
23
|
found: undefined;
|
|
22
24
|
}
|
|
@@ -407,9 +407,11 @@ function expandRefUsage(seed, fields, alwaysJoinNames = []) {
|
|
|
407
407
|
// given usage into the accumulator. Used both in-loop (joins reached
|
|
408
408
|
// via reference paths) and upfront (always-on segment-level joins).
|
|
409
409
|
function activateJoin(joinDef, joinPath) {
|
|
410
|
+
if (!(0, malloy_types_1.isJoined)(joinDef))
|
|
411
|
+
return;
|
|
410
412
|
const joinKey = (0, utils_2.pathToKey)('join', joinPath);
|
|
411
413
|
const thisDep = getJoin(activeJoinGraph, joinKey, joinPath);
|
|
412
|
-
if (
|
|
414
|
+
if (thisDep.checked)
|
|
413
415
|
return;
|
|
414
416
|
thisDep.checked = true;
|
|
415
417
|
const joinFieldUsage = getJoinFieldUsage(joinDef, joinPath);
|
|
@@ -462,8 +464,13 @@ function expandRefUsage(seed, fields, alwaysJoinNames = []) {
|
|
|
462
464
|
ungroupings.push(...joinedUngroupings(refPath, def.ungroupings));
|
|
463
465
|
}
|
|
464
466
|
}
|
|
465
|
-
//
|
|
466
|
-
|
|
467
|
+
// Track the joins along the path. A path which ends at a join and carries
|
|
468
|
+
// a unique key requirement is an aggregate computed over that join's rows,
|
|
469
|
+
// which should then include the join's on-clause fields in the field usage.
|
|
470
|
+
// A path which merely names a join-typed value does not.
|
|
471
|
+
const endsInTraversedJoin = reference.uniqueKeyRequirement !== undefined;
|
|
472
|
+
const joinDepth = reference.path.length - (endsInTraversedJoin ? 0 : 1);
|
|
473
|
+
for (let joinLen = 1; joinLen <= joinDepth; joinLen++) {
|
|
467
474
|
const joinPath = reference.path.slice(0, joinLen);
|
|
468
475
|
const joinDef = inNamespace(joinPath, fieldNameSpace);
|
|
469
476
|
if (!joinDef)
|
|
@@ -803,14 +803,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
803
803
|
return this.visitTimezoneStatement(cx.timezoneStatement());
|
|
804
804
|
}
|
|
805
805
|
visitTimezoneStatement(cx) {
|
|
806
|
-
const timezone = this.getPlainStringFrom(cx);
|
|
807
|
-
|
|
808
|
-
if (!timezoneStatement.isValid) {
|
|
809
|
-
this.astError(timezoneStatement, 'invalid-timezone', {
|
|
810
|
-
timezone: timezoneStatement.tz,
|
|
811
|
-
});
|
|
812
|
-
}
|
|
813
|
-
return this.astAt(timezoneStatement, cx);
|
|
806
|
+
const timezone = this.astAt(new ast.ExprString(this.getPlainStringFrom(cx)), cx.string());
|
|
807
|
+
return this.astAt(new ast.TimezoneStatement(timezone), cx);
|
|
814
808
|
}
|
|
815
809
|
visitQueryProperties(pcx) {
|
|
816
810
|
const qProps = this.only(pcx.queryStatement().map(qcx => this.astAt(this.visit(qcx), qcx)), x => ast.isQueryProperty(x) && x, 'query statement');
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -154,12 +154,10 @@ type MessageParameterTypes = {
|
|
|
154
154
|
'top-by-non-aggregate': string;
|
|
155
155
|
'definition-name-conflict': string;
|
|
156
156
|
'invalid-field-in-index-query': string;
|
|
157
|
-
'invalid-wildcard-source': string;
|
|
158
|
-
'wildcard-source-not-found': string;
|
|
159
157
|
'name-conflict-in-wildcard-expansion': string;
|
|
160
158
|
'invalid-parameter-reference': string;
|
|
161
159
|
'parameter-not-found': string;
|
|
162
|
-
'wildcard-
|
|
160
|
+
'wildcard-matched-no-fields': string;
|
|
163
161
|
'unexpected-index-segment': string;
|
|
164
162
|
'accept-parameter': string;
|
|
165
163
|
'except-parameter': string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DocumentLocation, DocumentPosition, DocumentReference, ImportLocation, ModelDef, SourceDef, SQLSourceDef, DependencyTree, DocumentRange } from '../model/malloy_types';
|
|
1
|
+
import type { DocumentLocation, DocumentPosition, DocumentReference, ImportLocation, ModelDef, SourceDef, SQLSourceDef, DependencyTree, DocumentRange, VirtualMap } from '../model/malloy_types';
|
|
2
2
|
import type { LogMessage, LogMessageOptions, MessageCode, MessageParameterType } from './parse-log';
|
|
3
3
|
import { BaseMessageLogger } from './parse-log';
|
|
4
4
|
import type { ZoneData } from './zone';
|
|
@@ -186,6 +186,7 @@ export declare class MalloyChildTranslator extends MalloyTranslation {
|
|
|
186
186
|
export declare class MalloyTranslator extends MalloyTranslation {
|
|
187
187
|
private readonly eventStream;
|
|
188
188
|
readonly restrictedMode: boolean;
|
|
189
|
+
virtualMap?: VirtualMap | undefined;
|
|
189
190
|
schemaZone: Zone<SourceDef>;
|
|
190
191
|
importZone: Zone<string>;
|
|
191
192
|
pretranslatedModels: Map<string, ModelDef>;
|
|
@@ -193,7 +194,7 @@ export declare class MalloyTranslator extends MalloyTranslation {
|
|
|
193
194
|
connectionDialectZone: Zone<string>;
|
|
194
195
|
logger: BaseMessageLogger;
|
|
195
196
|
readonly root: MalloyTranslator;
|
|
196
|
-
constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null, restrictedMode?: boolean);
|
|
197
|
+
constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null, restrictedMode?: boolean, virtualMap?: VirtualMap | undefined);
|
|
197
198
|
update(dd: ParseUpdate): void;
|
|
198
199
|
private lockZonesIfRestricted;
|
|
199
200
|
translate(extendingModel?: ModelDef): TranslateResponse;
|
|
@@ -780,10 +780,11 @@ exports.MalloyChildTranslator = MalloyChildTranslator;
|
|
|
780
780
|
* no need to call again, the translation is finished or error'd.
|
|
781
781
|
*/
|
|
782
782
|
class MalloyTranslator extends MalloyTranslation {
|
|
783
|
-
constructor(rootURL, importURL = null, preload = null, eventStream = null, restrictedMode = false) {
|
|
783
|
+
constructor(rootURL, importURL = null, preload = null, eventStream = null, restrictedMode = false, virtualMap) {
|
|
784
784
|
super(rootURL, importURL);
|
|
785
785
|
this.eventStream = eventStream;
|
|
786
786
|
this.restrictedMode = restrictedMode;
|
|
787
|
+
this.virtualMap = virtualMap;
|
|
787
788
|
this.schemaZone = new zone_1.Zone();
|
|
788
789
|
this.importZone = new zone_1.Zone();
|
|
789
790
|
this.pretranslatedModels = new Map();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DocumentLocation, FieldDef, ModelDef, NamedModelObject, PipeSegment, Query, QueryFieldDef, StructDef, TurtleDef, SourceDef, TableSourceDef, SQLSourceDef, UserTypeDef } from '../../model/malloy_types';
|
|
1
|
+
import type { DocumentLocation, FieldDef, ModelDef, NamedModelObject, PipeSegment, Query, QueryFieldDef, StructDef, TurtleDef, SourceDef, TableSourceDef, SQLSourceDef, UserTypeDef, VirtualMap } from '../../model/malloy_types';
|
|
2
2
|
import { MalloyElement } from '../ast';
|
|
3
3
|
import type { NameSpace } from '../ast/types/name-space';
|
|
4
4
|
import type { ModelEntry } from '../ast/types/model-entry';
|
|
@@ -56,6 +56,7 @@ export interface TestTranslatorOptions {
|
|
|
56
56
|
eventStream?: EventStream | null;
|
|
57
57
|
internalModel?: ModelDef;
|
|
58
58
|
restrictedMode?: boolean;
|
|
59
|
+
virtualMap?: VirtualMap;
|
|
59
60
|
/**
|
|
60
61
|
* Each entry is a compiler-flag tag fragment — the content that
|
|
61
62
|
* would follow `##! ` in a Malloy source annotation. Rendered to
|