@malloydata/malloy 0.0.335 → 0.0.336
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/CONTEXT.md +4 -3
- package/MALLOY_API.md +129 -0
- package/dist/annotation.d.ts +0 -2
- package/dist/annotation.js +29 -23
- package/dist/api/asynchronous.d.ts +1 -1
- package/dist/api/foundation/cache.d.ts +32 -0
- package/dist/api/foundation/cache.js +92 -0
- package/dist/api/foundation/compile.d.ts +175 -0
- package/dist/api/foundation/compile.js +391 -0
- package/dist/api/foundation/core.d.ts +493 -0
- package/dist/api/foundation/core.js +1247 -0
- package/dist/api/foundation/document.d.ts +167 -0
- package/dist/api/foundation/document.js +206 -0
- package/dist/api/foundation/index.d.ts +10 -0
- package/dist/api/foundation/index.js +77 -0
- package/dist/api/foundation/readers.d.ts +53 -0
- package/dist/api/foundation/readers.js +134 -0
- package/dist/api/foundation/result.d.ts +185 -0
- package/dist/api/foundation/result.js +704 -0
- package/dist/api/foundation/runtime.d.ts +361 -0
- package/dist/api/foundation/runtime.js +733 -0
- package/dist/api/foundation/types.d.ts +54 -0
- package/dist/api/foundation/types.js +7 -0
- package/dist/api/foundation/writers.d.ts +42 -0
- package/dist/api/foundation/writers.js +230 -0
- package/dist/api/util.d.ts +1 -1
- package/dist/connection/base_connection.d.ts +5 -0
- package/dist/connection/types.d.ts +5 -0
- package/dist/dialect/duckdb/duckdb.js +2 -1
- package/dist/dialect/snowflake/snowflake.js +7 -1
- package/dist/dialect/trino/trino.js +7 -2
- package/dist/index.d.ts +6 -3
- package/dist/index.js +30 -26
- package/dist/lang/ast/error-factory.js +3 -5
- package/dist/lang/ast/source-elements/query-source.js +2 -7
- package/dist/lang/ast/source-elements/refined-source.js +11 -1
- package/dist/lang/ast/source-elements/sql-source.d.ts +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +18 -3
- package/dist/lang/ast/sql-elements/sql-string.d.ts +2 -2
- package/dist/lang/ast/sql-elements/sql-string.js +18 -1
- package/dist/lang/ast/statements/define-source.js +7 -2
- package/dist/lang/ast/statements/import-statement.js +53 -21
- package/dist/lang/ast/types/document-compile-result.d.ts +1 -0
- package/dist/lang/ast/types/malloy-element.d.ts +3 -1
- package/dist/lang/ast/types/malloy-element.js +23 -7
- package/dist/lang/malloy-to-ast.d.ts +1 -1
- package/dist/lang/malloy-to-ast.js +1 -1
- package/dist/lang/parse-malloy.d.ts +3 -2
- package/dist/lang/parse-malloy.js +14 -25
- package/dist/lang/test/test-translator.js +1 -0
- package/dist/lang/translate-response.d.ts +1 -0
- package/dist/model/constant_expression_compiler.js +6 -7
- package/dist/model/index.d.ts +3 -1
- package/dist/model/index.js +15 -9
- package/dist/model/malloy_types.d.ts +89 -15
- package/dist/model/malloy_types.js +12 -0
- package/dist/model/persist_utils.d.ts +47 -0
- package/dist/model/persist_utils.js +257 -0
- package/dist/model/query_model_impl.d.ts +2 -4
- package/dist/model/query_model_impl.js +5 -13
- package/dist/model/query_node.d.ts +1 -2
- package/dist/model/query_node.js +3 -13
- package/dist/model/query_query.d.ts +17 -1
- package/dist/model/query_query.js +81 -36
- package/dist/model/source_def_utils.d.ts +50 -0
- package/dist/model/source_def_utils.js +154 -0
- package/dist/model/sql_block.d.ts +5 -1
- package/dist/model/sql_block.js +29 -4
- package/dist/model/sql_compiled.d.ts +29 -0
- package/dist/model/sql_compiled.js +102 -0
- package/dist/model/stage_writer.d.ts +1 -3
- package/dist/model/stage_writer.js +7 -25
- package/dist/model/utils.d.ts +20 -1
- package/dist/model/utils.js +40 -0
- package/dist/run_sql_options.d.ts +0 -1
- package/dist/taggable.d.ts +10 -0
- package/dist/taggable.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -4
- package/dist/malloy.d.ts +0 -1365
- package/dist/malloy.js +0 -3421
- package/dist/model/materialization/utils.d.ts +0 -3
- package/dist/model/materialization/utils.js +0 -41
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as Malloy from '@malloydata/malloy-interfaces';
|
|
2
|
+
import type { EventStream } from '../runtime_types';
|
|
2
3
|
/**
|
|
3
4
|
* Field computations are compiled into an expression tree of "Expr"
|
|
4
5
|
* type nodes. Each node is one of these three interfaces. The
|
|
@@ -582,7 +583,7 @@ export interface Query extends Pipeline, Filtered, HasLocation {
|
|
|
582
583
|
modelAnnotation?: Annotation;
|
|
583
584
|
compositeResolvedSourceDef?: SourceDef;
|
|
584
585
|
}
|
|
585
|
-
export type
|
|
586
|
+
export type NamedQueryDef = Query & NamedObject;
|
|
586
587
|
export type PipeSegment = QuerySegment | IndexSegment | RawSegment;
|
|
587
588
|
export declare function segmentHasErrors(segment: PipeSegment): boolean;
|
|
588
589
|
export declare function structHasErrors(struct: StructDef): boolean;
|
|
@@ -685,6 +686,8 @@ interface StructDefBase extends HasLocation, NamedObject {
|
|
|
685
686
|
annotation?: Annotation;
|
|
686
687
|
modelAnnotation?: ModelAnnotation;
|
|
687
688
|
fields: FieldDef[];
|
|
689
|
+
/** Marker for error placeholder structs created by ErrorFactory */
|
|
690
|
+
errorFactory?: boolean;
|
|
688
691
|
}
|
|
689
692
|
export interface PartitionCompositeDesc {
|
|
690
693
|
partitionField: string;
|
|
@@ -716,13 +719,46 @@ export interface CompositeSourceDef extends SourceDefBase {
|
|
|
716
719
|
export interface SQLStringSegment {
|
|
717
720
|
sql: string;
|
|
718
721
|
}
|
|
719
|
-
export type SQLPhraseSegment = Query | SQLStringSegment;
|
|
722
|
+
export type SQLPhraseSegment = Query | PersistableSourceDef | SQLStringSegment;
|
|
720
723
|
export declare function isSegmentSQL(f: SQLPhraseSegment): f is SQLStringSegment;
|
|
721
|
-
export
|
|
724
|
+
export declare function isSegmentSource(f: SQLPhraseSegment): f is PersistableSourceDef;
|
|
725
|
+
/** Format: "name@modelUrl" - uniquely identifies a source for persistence */
|
|
726
|
+
export type SourceID = string;
|
|
727
|
+
/** Hash of (connectionDigest, sql) - uniquely identifies a built artifact */
|
|
728
|
+
export type BuildID = string;
|
|
729
|
+
/**
|
|
730
|
+
* Reference to a source in modelDef.contents by name.
|
|
731
|
+
* Used in sourceRegistry to avoid duplicating SourceDefs that are in the namespace.
|
|
732
|
+
*/
|
|
733
|
+
export interface SourceRegistryReference {
|
|
734
|
+
type: 'source_registry_reference';
|
|
735
|
+
name: string;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Inner entry type: either a reference to contents or an actual PersistableSourceDef.
|
|
739
|
+
* - SourceRegistryReference: source is in namespace (contents), look it up by name
|
|
740
|
+
* - PersistableSourceDef: source is not in namespace (hidden dependency), stored directly
|
|
741
|
+
*/
|
|
742
|
+
export type SourceRegistryEntry = SourceRegistryReference | PersistableSourceDef;
|
|
743
|
+
/**
|
|
744
|
+
* Value in the sourceRegistry, wrapping the entry with persistence info.
|
|
745
|
+
* persist is lazily computed: undefined = not checked, true/false = checked
|
|
746
|
+
*/
|
|
747
|
+
export interface SourceRegistryValue {
|
|
748
|
+
entry: SourceRegistryEntry;
|
|
749
|
+
persist?: boolean;
|
|
750
|
+
}
|
|
751
|
+
export declare function isSourceRegistryReference(entry: SourceRegistryEntry): entry is SourceRegistryReference;
|
|
752
|
+
export interface PersistableSourceProperties {
|
|
753
|
+
sourceID?: SourceID;
|
|
754
|
+
extends?: SourceID;
|
|
755
|
+
}
|
|
756
|
+
export interface SQLSourceDef extends SourceDefBase, PersistableSourceProperties {
|
|
722
757
|
type: 'sql_select';
|
|
723
758
|
selectStr: string;
|
|
759
|
+
selectSegments?: SQLPhraseSegment[];
|
|
724
760
|
}
|
|
725
|
-
export interface QuerySourceDef extends SourceDefBase {
|
|
761
|
+
export interface QuerySourceDef extends SourceDefBase, PersistableSourceProperties {
|
|
726
762
|
type: 'query_source';
|
|
727
763
|
query: Query;
|
|
728
764
|
}
|
|
@@ -738,7 +774,21 @@ export interface FinalizeSourceDef extends SourceDefBase {
|
|
|
738
774
|
}
|
|
739
775
|
export declare function sourceBase(sd: SourceDefBase): SourceDefBase;
|
|
740
776
|
export declare function isSourceDef(sd: NamedModelObject | FieldDef): sd is SourceDef;
|
|
777
|
+
/**
|
|
778
|
+
* Union of all source definition types.
|
|
779
|
+
*
|
|
780
|
+
* IMPORTANT: Never use object spread to copy a SourceDef. Use the factory
|
|
781
|
+
* methods in source_def_utils.ts to merge changes into a source def:
|
|
782
|
+
* - mkSQLSourceDef(base, ...) - create SQLSourceDef from base
|
|
783
|
+
* - mkQuerySourceDef(base, ...) - create QuerySourceDef from base
|
|
784
|
+
*
|
|
785
|
+
* These factories explicitly copy only safe fields, preventing accidental
|
|
786
|
+
* propagation of sourceID/extends which must only be set in DefineSource.
|
|
787
|
+
*/
|
|
741
788
|
export type SourceDef = TableSourceDef | SQLSourceDef | QuerySourceDef | QueryResultDef | FinalizeSourceDef | NestSourceDef | CompositeSourceDef;
|
|
789
|
+
/** Sources that can be persisted (materialized to tables) */
|
|
790
|
+
export type PersistableSourceDef = SQLSourceDef | QuerySourceDef;
|
|
791
|
+
export declare function isPersistableSourceDef(sd: SourceDef): sd is PersistableSourceDef;
|
|
742
792
|
/** Is this the "FROM" table of a query tree */
|
|
743
793
|
export declare function isBaseTable(def: StructDef): def is SourceDef;
|
|
744
794
|
export type StructDef = SourceDef | RecordDef | ArrayDef;
|
|
@@ -891,7 +941,7 @@ export type QueryFieldDef = AtomicFieldDef | TurtleDef | RefToField;
|
|
|
891
941
|
export type TypedDef = AtomicTypeDef | JoinFieldDef | TurtleDef | RefToField | StructDef;
|
|
892
942
|
/** Get the output name for a NamedObject */
|
|
893
943
|
export declare function getIdentifier(n: AliasedName): string;
|
|
894
|
-
export type NamedModelObject = SourceDef |
|
|
944
|
+
export type NamedModelObject = SourceDef | NamedQueryDef | FunctionDef | ConnectionDef;
|
|
895
945
|
export interface DependencyTree {
|
|
896
946
|
[url: string]: DependencyTree;
|
|
897
947
|
}
|
|
@@ -900,6 +950,13 @@ export interface ModelDef {
|
|
|
900
950
|
name: string;
|
|
901
951
|
exports: string[];
|
|
902
952
|
contents: Record<string, NamedModelObject>;
|
|
953
|
+
/**
|
|
954
|
+
* Registry mapping sourceID to source definitions for build graph construction.
|
|
955
|
+
* For sources in namespace: maps to SourceRegistryReference (look up in contents)
|
|
956
|
+
* For hidden dependencies: maps to actual PersistableSourceDef (not in namespace)
|
|
957
|
+
* Each entry includes a lazily-computed persist flag.
|
|
958
|
+
*/
|
|
959
|
+
sourceRegistry: Record<SourceID, SourceRegistryValue>;
|
|
903
960
|
annotation?: ModelAnnotation;
|
|
904
961
|
queryList: Query[];
|
|
905
962
|
dependencies: DependencyTree;
|
|
@@ -948,12 +1005,6 @@ export interface DrillSource {
|
|
|
948
1005
|
sourceFilters?: FilterCondition[];
|
|
949
1006
|
sourceArguments?: Record<string, Argument>;
|
|
950
1007
|
}
|
|
951
|
-
export type QueryToMaterialize = {
|
|
952
|
-
id: string;
|
|
953
|
-
path: string;
|
|
954
|
-
source: string | undefined;
|
|
955
|
-
queryName: string;
|
|
956
|
-
};
|
|
957
1008
|
export interface CompiledQuery extends DrillSource {
|
|
958
1009
|
structs: SourceDef[];
|
|
959
1010
|
sql: string;
|
|
@@ -963,8 +1014,6 @@ export interface CompiledQuery extends DrillSource {
|
|
|
963
1014
|
connectionName: string;
|
|
964
1015
|
queryTimezone?: string;
|
|
965
1016
|
annotation?: Annotation;
|
|
966
|
-
dependenciesToMaterialize?: Record<string, QueryToMaterialize>;
|
|
967
|
-
materialization?: QueryToMaterialize;
|
|
968
1017
|
defaultRowLimitAdded?: number;
|
|
969
1018
|
}
|
|
970
1019
|
/** Result type for running a Malloy query. */
|
|
@@ -1007,10 +1056,15 @@ export interface SearchValueMapResult {
|
|
|
1007
1056
|
}[];
|
|
1008
1057
|
}
|
|
1009
1058
|
export interface PrepareResultOptions {
|
|
1010
|
-
replaceMaterializedReferences?: boolean;
|
|
1011
|
-
materializedTablePrefix?: string;
|
|
1012
1059
|
defaultRowLimit?: number;
|
|
1013
1060
|
isPartialQuery?: boolean;
|
|
1061
|
+
eventStream?: EventStream;
|
|
1062
|
+
/** Manifest of built tables (BuildID → entry), the build cache */
|
|
1063
|
+
buildManifest?: BuildManifest;
|
|
1064
|
+
/** Map from connectionName to connectionDigest (from Connection.getDigest()) */
|
|
1065
|
+
connectionDigests?: Record<string, string>;
|
|
1066
|
+
/** If true, throw when a persist query's digest is not in the manifest */
|
|
1067
|
+
strictPersist?: boolean;
|
|
1014
1068
|
}
|
|
1015
1069
|
type UTD = AtomicTypeDef | TypedDef | FunctionParameterTypeDef | FunctionReturnTypeDef | undefined;
|
|
1016
1070
|
/**
|
|
@@ -1048,4 +1102,24 @@ export type UniqueKeyRequirement = undefined | {
|
|
|
1048
1102
|
isCount: boolean;
|
|
1049
1103
|
};
|
|
1050
1104
|
export declare function mergeUniqueKeyRequirement(existing: UniqueKeyRequirement, newInfo: UniqueKeyRequirement): UniqueKeyRequirement;
|
|
1105
|
+
/**
|
|
1106
|
+
* Entry in a BuildManifest for a persisted table.
|
|
1107
|
+
*/
|
|
1108
|
+
export interface BuildManifestEntry {
|
|
1109
|
+
/** Hash of (connectionDigest, sql) - also the key in buildEntries */
|
|
1110
|
+
buildId: BuildID;
|
|
1111
|
+
tableName: string;
|
|
1112
|
+
buildStartedAt: string;
|
|
1113
|
+
buildFinishedAt: string;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Manifest of persisted query results (the build cache).
|
|
1117
|
+
* Used by compileQuery to substitute persist queries with table references.
|
|
1118
|
+
*/
|
|
1119
|
+
export interface BuildManifest {
|
|
1120
|
+
modelUrl: string;
|
|
1121
|
+
buildStartedAt: string;
|
|
1122
|
+
buildFinishedAt: string;
|
|
1123
|
+
buildEntries: Record<BuildID, BuildManifestEntry>;
|
|
1124
|
+
}
|
|
1051
1125
|
export {};
|
|
@@ -76,8 +76,11 @@ exports.isRawSegment = isRawSegment;
|
|
|
76
76
|
exports.isIndexSegment = isIndexSegment;
|
|
77
77
|
exports.bareFieldUsage = bareFieldUsage;
|
|
78
78
|
exports.isSegmentSQL = isSegmentSQL;
|
|
79
|
+
exports.isSegmentSource = isSegmentSource;
|
|
80
|
+
exports.isSourceRegistryReference = isSourceRegistryReference;
|
|
79
81
|
exports.sourceBase = sourceBase;
|
|
80
82
|
exports.isSourceDef = isSourceDef;
|
|
83
|
+
exports.isPersistableSourceDef = isPersistableSourceDef;
|
|
81
84
|
exports.isBaseTable = isBaseTable;
|
|
82
85
|
exports.isLiteral = isLiteral;
|
|
83
86
|
exports.mergeEvalSpaces = mergeEvalSpaces;
|
|
@@ -443,6 +446,12 @@ function bareFieldUsage(fu) {
|
|
|
443
446
|
function isSegmentSQL(f) {
|
|
444
447
|
return 'sql' in f;
|
|
445
448
|
}
|
|
449
|
+
function isSegmentSource(f) {
|
|
450
|
+
return 'type' in f && (f.type === 'sql_select' || f.type === 'query_source');
|
|
451
|
+
}
|
|
452
|
+
function isSourceRegistryReference(entry) {
|
|
453
|
+
return entry.type === 'source_registry_reference';
|
|
454
|
+
}
|
|
446
455
|
// The gesture {...sourceStruct moreProperties} happens everywhere, now that
|
|
447
456
|
// structs aren't all identical, we need a way to make one from any of the
|
|
448
457
|
// exisitng structs
|
|
@@ -458,6 +467,9 @@ function isSourceDef(sd) {
|
|
|
458
467
|
sd.type === 'nest_source' ||
|
|
459
468
|
sd.type === 'composite');
|
|
460
469
|
}
|
|
470
|
+
function isPersistableSourceDef(sd) {
|
|
471
|
+
return sd.type === 'sql_select' || sd.type === 'query_source';
|
|
472
|
+
}
|
|
461
473
|
/** Is this the "FROM" table of a query tree */
|
|
462
474
|
function isBaseTable(def) {
|
|
463
475
|
if (isJoinedSource(def)) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ModelDef, SourceDef, Query } from './malloy_types';
|
|
2
|
+
import type { BuildNode } from '../api/foundation/types';
|
|
3
|
+
/**
|
|
4
|
+
* Find persistent dependencies for a source or query, returning a nested DAG.
|
|
5
|
+
*
|
|
6
|
+
* Walks the full dependency tree but only includes persistent sources in the
|
|
7
|
+
* result. Non-persistent sources are "flattened out" - their persistent
|
|
8
|
+
* dependencies bubble up to become direct dependencies of the caller.
|
|
9
|
+
*
|
|
10
|
+
* Example: source_c (persist) -> source_b (NOT persist) -> source_a (persist)
|
|
11
|
+
* Returns: [{sourceID: source_a, dependsOn: []}]
|
|
12
|
+
* (source_b is flattened out, source_a becomes direct dependency)
|
|
13
|
+
*
|
|
14
|
+
* ## The 6 Dependency Paths in the IR
|
|
15
|
+
*
|
|
16
|
+
* Starting from a Query or SourceDef, these are ALL the ways a SourceDef
|
|
17
|
+
* can be referenced (and thus must be walked for dependency tracking):
|
|
18
|
+
*
|
|
19
|
+
* 1. **Query.structRef** → SourceDef (the FROM clause)
|
|
20
|
+
* 2. **Query.pipeline[].extendSource[]** → JoinFieldDef (joins in extend blocks)
|
|
21
|
+
* 3. **SourceDef.fields[]** → JoinFieldDef (joins defined on a source)
|
|
22
|
+
* 4. **PersistableSourceDef.extends** → SourceID (extend chain reference)
|
|
23
|
+
* 5. **SQLSourceDef.selectSegments[]** → Query | PersistableSourceDef (SQL interpolation)
|
|
24
|
+
* 6. **QuerySourceDef.query** → Query (nested query in query_source)
|
|
25
|
+
*
|
|
26
|
+
* Note: CompositeSourceDef.sources[] is ignored - composite sources and
|
|
27
|
+
* persistence may be incompatible features.
|
|
28
|
+
*
|
|
29
|
+
* @param root The source or query to find dependencies for
|
|
30
|
+
* @param modelDef The model definition containing the source registry
|
|
31
|
+
* @returns Array of BuildNode representing the persistent dependency DAG
|
|
32
|
+
*/
|
|
33
|
+
export declare function findPersistentDependencies(root: SourceDef | Query, modelDef: ModelDef): BuildNode[];
|
|
34
|
+
/**
|
|
35
|
+
* Find the minimal set of root build graphs from a forest of BuildNodes.
|
|
36
|
+
*
|
|
37
|
+
* Uses flattening for ANALYSIS ONLY to identify unique nodes and find roots.
|
|
38
|
+
* Returns original graph structures (NOT flattened) - preserves branching
|
|
39
|
+
* for parallel builds.
|
|
40
|
+
*
|
|
41
|
+
* Roots are sourceIDs that exist but nothing depends on them - these are
|
|
42
|
+
* the entry points for building.
|
|
43
|
+
*
|
|
44
|
+
* @param deps Array of BuildNode trees (potentially overlapping)
|
|
45
|
+
* @returns Array of root BuildNode trees (deduplicated)
|
|
46
|
+
*/
|
|
47
|
+
export declare function minimalBuildGraph(deps: BuildNode[]): BuildNode[];
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.findPersistentDependencies = findPersistentDependencies;
|
|
8
|
+
exports.minimalBuildGraph = minimalBuildGraph;
|
|
9
|
+
const malloy_types_1 = require("./malloy_types");
|
|
10
|
+
const source_def_utils_1 = require("./source_def_utils");
|
|
11
|
+
const annotation_1 = require("../annotation");
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a source name to its definition from model contents.
|
|
14
|
+
*/
|
|
15
|
+
function resolveSource(modelDef, name) {
|
|
16
|
+
const obj = modelDef.contents[name];
|
|
17
|
+
return obj && (0, malloy_types_1.isSourceDef)(obj) ? obj : undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if a source has the #@ persist annotation.
|
|
21
|
+
*/
|
|
22
|
+
function checkPersistAnnotation(source) {
|
|
23
|
+
if (!source.annotation)
|
|
24
|
+
return false;
|
|
25
|
+
const { tag } = (0, annotation_1.annotationToTag)(source.annotation, { prefix: /^#@ / });
|
|
26
|
+
return tag.has('persist');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a sourceID is persistent, using lazy evaluation and caching.
|
|
30
|
+
* Sets the persist flag on the registry entry as a side effect.
|
|
31
|
+
*/
|
|
32
|
+
function isPersistent(sourceID, modelDef) {
|
|
33
|
+
const value = modelDef.sourceRegistry[sourceID];
|
|
34
|
+
if (!value)
|
|
35
|
+
return false;
|
|
36
|
+
if (value.persist === undefined) {
|
|
37
|
+
const sourceDef = (0, source_def_utils_1.resolveSourceID)(modelDef, sourceID);
|
|
38
|
+
value.persist = sourceDef ? checkPersistAnnotation(sourceDef) : false;
|
|
39
|
+
}
|
|
40
|
+
return value.persist;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Find persistent dependencies for a source or query, returning a nested DAG.
|
|
44
|
+
*
|
|
45
|
+
* Walks the full dependency tree but only includes persistent sources in the
|
|
46
|
+
* result. Non-persistent sources are "flattened out" - their persistent
|
|
47
|
+
* dependencies bubble up to become direct dependencies of the caller.
|
|
48
|
+
*
|
|
49
|
+
* Example: source_c (persist) -> source_b (NOT persist) -> source_a (persist)
|
|
50
|
+
* Returns: [{sourceID: source_a, dependsOn: []}]
|
|
51
|
+
* (source_b is flattened out, source_a becomes direct dependency)
|
|
52
|
+
*
|
|
53
|
+
* ## The 6 Dependency Paths in the IR
|
|
54
|
+
*
|
|
55
|
+
* Starting from a Query or SourceDef, these are ALL the ways a SourceDef
|
|
56
|
+
* can be referenced (and thus must be walked for dependency tracking):
|
|
57
|
+
*
|
|
58
|
+
* 1. **Query.structRef** → SourceDef (the FROM clause)
|
|
59
|
+
* 2. **Query.pipeline[].extendSource[]** → JoinFieldDef (joins in extend blocks)
|
|
60
|
+
* 3. **SourceDef.fields[]** → JoinFieldDef (joins defined on a source)
|
|
61
|
+
* 4. **PersistableSourceDef.extends** → SourceID (extend chain reference)
|
|
62
|
+
* 5. **SQLSourceDef.selectSegments[]** → Query | PersistableSourceDef (SQL interpolation)
|
|
63
|
+
* 6. **QuerySourceDef.query** → Query (nested query in query_source)
|
|
64
|
+
*
|
|
65
|
+
* Note: CompositeSourceDef.sources[] is ignored - composite sources and
|
|
66
|
+
* persistence may be incompatible features.
|
|
67
|
+
*
|
|
68
|
+
* @param root The source or query to find dependencies for
|
|
69
|
+
* @param modelDef The model definition containing the source registry
|
|
70
|
+
* @returns Array of BuildNode representing the persistent dependency DAG
|
|
71
|
+
*/
|
|
72
|
+
function findPersistentDependencies(root, modelDef) {
|
|
73
|
+
const visited = new Set();
|
|
74
|
+
function processSourceID(sourceID) {
|
|
75
|
+
if (visited.has(sourceID)) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
visited.add(sourceID);
|
|
79
|
+
const sourceDef = (0, source_def_utils_1.resolveSourceID)(modelDef, sourceID);
|
|
80
|
+
if (!sourceDef) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const childDeps = processSourceDef(sourceDef);
|
|
84
|
+
const persistent = isPersistent(sourceID, modelDef);
|
|
85
|
+
if (persistent) {
|
|
86
|
+
return [{ sourceID, dependsOn: childDeps }];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return childDeps;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function processSourceDef(source) {
|
|
93
|
+
const results = [];
|
|
94
|
+
// Path 4: PersistableSourceDef.extends
|
|
95
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.extends) {
|
|
96
|
+
results.push(...processSourceID(source.extends));
|
|
97
|
+
}
|
|
98
|
+
// Path 6: QuerySourceDef.query
|
|
99
|
+
if (source.type === 'query_source') {
|
|
100
|
+
results.push(...processQuery(source.query));
|
|
101
|
+
}
|
|
102
|
+
// Path 5: SQLSourceDef.selectSegments[]
|
|
103
|
+
if (source.type === 'sql_select' && source.selectSegments) {
|
|
104
|
+
for (const segment of source.selectSegments) {
|
|
105
|
+
results.push(...processSQLSegment(segment));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Path 3: SourceDef.fields[] - joins defined on the source
|
|
109
|
+
for (const field of source.fields) {
|
|
110
|
+
if ((0, malloy_types_1.isJoined)(field) && (0, malloy_types_1.isSourceDef)(field)) {
|
|
111
|
+
results.push(...processJoinedSource(field));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
function processQuery(query) {
|
|
117
|
+
const results = [];
|
|
118
|
+
// Path 1: Query.structRef
|
|
119
|
+
results.push(...processStructRef(query.structRef));
|
|
120
|
+
// Path 2: Query.pipeline[].extendSource[]
|
|
121
|
+
for (const segment of query.pipeline) {
|
|
122
|
+
if (segment.type === 'reduce' ||
|
|
123
|
+
segment.type === 'project' ||
|
|
124
|
+
segment.type === 'partial') {
|
|
125
|
+
const querySegment = segment;
|
|
126
|
+
if (querySegment.extendSource) {
|
|
127
|
+
for (const field of querySegment.extendSource) {
|
|
128
|
+
if ((0, malloy_types_1.isJoined)(field) && (0, malloy_types_1.isSourceDef)(field)) {
|
|
129
|
+
results.push(...processJoinedSource(field));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
function processJoinedSource(source) {
|
|
138
|
+
// If it has a sourceID, go through the registry
|
|
139
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.sourceID) {
|
|
140
|
+
return processSourceID(source.sourceID);
|
|
141
|
+
}
|
|
142
|
+
// Otherwise walk through it transparently
|
|
143
|
+
return processSourceDef(source);
|
|
144
|
+
}
|
|
145
|
+
function processStructRef(ref) {
|
|
146
|
+
if (typeof ref === 'string') {
|
|
147
|
+
const source = resolveSource(modelDef, ref);
|
|
148
|
+
if (!source)
|
|
149
|
+
return [];
|
|
150
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.sourceID) {
|
|
151
|
+
return processSourceID(source.sourceID);
|
|
152
|
+
}
|
|
153
|
+
return processSourceDef(source);
|
|
154
|
+
}
|
|
155
|
+
else if ((0, malloy_types_1.isSourceDef)(ref)) {
|
|
156
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(ref) && ref.sourceID) {
|
|
157
|
+
return processSourceID(ref.sourceID);
|
|
158
|
+
}
|
|
159
|
+
return processSourceDef(ref);
|
|
160
|
+
}
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
function processSQLSegment(segment) {
|
|
164
|
+
if ((0, malloy_types_1.isSegmentSQL)(segment)) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
else if ((0, malloy_types_1.isSegmentSource)(segment)) {
|
|
168
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(segment) && segment.sourceID) {
|
|
169
|
+
return processSourceID(segment.sourceID);
|
|
170
|
+
}
|
|
171
|
+
return processSourceDef(segment);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// It's a Query
|
|
175
|
+
return processQuery(segment);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Entry point: handle both SourceDef and Query
|
|
179
|
+
// Query has required 'structRef', SourceDef does not
|
|
180
|
+
if ('structRef' in root) {
|
|
181
|
+
return processQuery(root);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// If the root source itself is persistable and has a sourceID, process it through
|
|
185
|
+
// processSourceID so it gets included in the result if persistent
|
|
186
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(root) && root.sourceID) {
|
|
187
|
+
return processSourceID(root.sourceID);
|
|
188
|
+
}
|
|
189
|
+
return processSourceDef(root);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Collect all sourceIDs from a BuildNode forest (for analysis only).
|
|
194
|
+
*/
|
|
195
|
+
function collectAllSourceIDs(nodes) {
|
|
196
|
+
const result = new Set();
|
|
197
|
+
for (const node of nodes) {
|
|
198
|
+
result.add(node.sourceID);
|
|
199
|
+
for (const id of collectAllSourceIDs(node.dependsOn)) {
|
|
200
|
+
result.add(id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Collect all sourceIDs that appear in any dependsOn (for analysis only).
|
|
207
|
+
*/
|
|
208
|
+
function collectAllDependedOn(nodes) {
|
|
209
|
+
const result = new Set();
|
|
210
|
+
for (const node of nodes) {
|
|
211
|
+
for (const dep of node.dependsOn) {
|
|
212
|
+
result.add(dep.sourceID);
|
|
213
|
+
}
|
|
214
|
+
for (const id of collectAllDependedOn(node.dependsOn)) {
|
|
215
|
+
result.add(id);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Find the minimal set of root build graphs from a forest of BuildNodes.
|
|
222
|
+
*
|
|
223
|
+
* Uses flattening for ANALYSIS ONLY to identify unique nodes and find roots.
|
|
224
|
+
* Returns original graph structures (NOT flattened) - preserves branching
|
|
225
|
+
* for parallel builds.
|
|
226
|
+
*
|
|
227
|
+
* Roots are sourceIDs that exist but nothing depends on them - these are
|
|
228
|
+
* the entry points for building.
|
|
229
|
+
*
|
|
230
|
+
* @param deps Array of BuildNode trees (potentially overlapping)
|
|
231
|
+
* @returns Array of root BuildNode trees (deduplicated)
|
|
232
|
+
*/
|
|
233
|
+
function minimalBuildGraph(deps) {
|
|
234
|
+
if (deps.length === 0)
|
|
235
|
+
return [];
|
|
236
|
+
// Use flattening for analysis only
|
|
237
|
+
const allSourceIDs = collectAllSourceIDs(deps);
|
|
238
|
+
const dependedOn = collectAllDependedOn(deps);
|
|
239
|
+
// Roots are sourceIDs that exist but nothing depends on them
|
|
240
|
+
const rootIDs = new Set();
|
|
241
|
+
for (const id of allSourceIDs) {
|
|
242
|
+
if (!dependedOn.has(id)) {
|
|
243
|
+
rootIDs.add(id);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Return original graph structures for roots (deduplicated by sourceID)
|
|
247
|
+
const seen = new Set();
|
|
248
|
+
const roots = [];
|
|
249
|
+
for (const node of deps) {
|
|
250
|
+
if (rootIDs.has(node.sourceID) && !seen.has(node.sourceID)) {
|
|
251
|
+
seen.add(node.sourceID);
|
|
252
|
+
roots.push(node);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return roots;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=persist_utils.js.map
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { ModelDef, StructRef, Argument, PrepareResultOptions, Query, SourceDef, SearchIndexResult, CompiledQuery, TurtleDef } from './malloy_types';
|
|
2
2
|
import { StageWriter } from './stage_writer';
|
|
3
3
|
import { type Dialect } from '../dialect';
|
|
4
|
-
import type { EventStream } from '../runtime_types';
|
|
5
4
|
import type { Connection } from '../connection/types';
|
|
6
5
|
import type { ModelRootInterface } from './query_node';
|
|
7
6
|
import { QueryStruct } from './query_node';
|
|
8
7
|
import type { QueryModel, QueryResults } from './query_model_contract';
|
|
9
|
-
export declare function makeQueryModel(modelDef: ModelDef | undefined
|
|
8
|
+
export declare function makeQueryModel(modelDef: ModelDef | undefined): QueryModel;
|
|
10
9
|
export declare class QueryModelImpl implements QueryModel, ModelRootInterface {
|
|
11
|
-
readonly eventStream?: EventStream | undefined;
|
|
12
10
|
dialect: Dialect;
|
|
13
11
|
modelDef: ModelDef | undefined;
|
|
14
12
|
structs: Map<string, QueryStruct>;
|
|
15
|
-
constructor(modelDef: ModelDef | undefined
|
|
13
|
+
constructor(modelDef: ModelDef | undefined);
|
|
16
14
|
getFinalOutputStruct(query: Query, options: PrepareResultOptions | undefined): SourceDef | undefined;
|
|
17
15
|
loadModelFromDef(modelDef: ModelDef): void;
|
|
18
16
|
getStructByName(name: string): QueryStruct;
|
|
@@ -12,15 +12,13 @@ const query_query_1 = require("./query_query");
|
|
|
12
12
|
const malloy_types_1 = require("./malloy_types");
|
|
13
13
|
const stage_writer_1 = require("./stage_writer");
|
|
14
14
|
const dialect_1 = require("../dialect");
|
|
15
|
-
const utils_1 = require("./materialization/utils");
|
|
16
15
|
const query_node_1 = require("./query_node");
|
|
17
16
|
const row_data_utils_1 = require("../api/row_data_utils");
|
|
18
|
-
function makeQueryModel(modelDef
|
|
19
|
-
return new QueryModelImpl(modelDef
|
|
17
|
+
function makeQueryModel(modelDef) {
|
|
18
|
+
return new QueryModelImpl(modelDef);
|
|
20
19
|
}
|
|
21
20
|
class QueryModelImpl {
|
|
22
|
-
constructor(modelDef
|
|
23
|
-
this.eventStream = eventStream;
|
|
21
|
+
constructor(modelDef) {
|
|
24
22
|
this.dialect = new dialect_1.StandardSQLDialect();
|
|
25
23
|
// dialect: Dialect = new PostgresDialect();
|
|
26
24
|
this.modelDef = undefined;
|
|
@@ -141,13 +139,11 @@ class QueryModelImpl {
|
|
|
141
139
|
};
|
|
142
140
|
}
|
|
143
141
|
compileQuery(query, prepareResultOptions, finalize = true) {
|
|
144
|
-
var _a, _b
|
|
145
|
-
let newModel;
|
|
142
|
+
var _a, _b;
|
|
146
143
|
const addDefaultRowLimit = this.addDefaultRowLimit(query, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.defaultRowLimit);
|
|
147
144
|
query = addDefaultRowLimit.query;
|
|
148
145
|
const addedDefaultRowLimit = addDefaultRowLimit.addedDefaultRowLimit;
|
|
149
|
-
const
|
|
150
|
-
const ret = m.loadQuery(query, undefined, prepareResultOptions, finalize, false);
|
|
146
|
+
const ret = this.loadQuery(query, undefined, prepareResultOptions, finalize, false);
|
|
151
147
|
const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
|
|
152
148
|
const sourceExplore = typeof structRef === 'string'
|
|
153
149
|
? structRef
|
|
@@ -163,10 +159,6 @@ class QueryModelImpl {
|
|
|
163
159
|
lastStageName: ret.lastStageName,
|
|
164
160
|
malloy: ret.malloy,
|
|
165
161
|
sql: ret.stageWriter.generateSQLStages(),
|
|
166
|
-
dependenciesToMaterialize: ret.stageWriter.dependenciesToMaterialize,
|
|
167
|
-
materialization: (0, utils_1.shouldMaterialize)(query.annotation)
|
|
168
|
-
? (0, utils_1.buildQueryMaterializationSpec)((_c = query.location) === null || _c === void 0 ? void 0 : _c.url, query.name, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.materializedTablePrefix)
|
|
169
|
-
: undefined,
|
|
170
162
|
structs: ret.structs,
|
|
171
163
|
sourceExplore,
|
|
172
164
|
sourceFilters: query.filterList,
|
|
@@ -61,7 +61,7 @@ export interface ParentQueryStruct {
|
|
|
61
61
|
struct: QueryStruct;
|
|
62
62
|
}
|
|
63
63
|
export interface ModelRootInterface {
|
|
64
|
-
|
|
64
|
+
structs: Map<string, QueryStruct>;
|
|
65
65
|
}
|
|
66
66
|
export interface ParentQueryModel {
|
|
67
67
|
model: ModelRootInterface;
|
|
@@ -119,7 +119,6 @@ export declare class QueryStruct {
|
|
|
119
119
|
resolveQueryFields(finalOutputStruct: (query: Query, options: PrepareResultOptions | undefined) => SourceDef | undefined): void;
|
|
120
120
|
getModel(): ModelRootInterface;
|
|
121
121
|
get eventStream(): EventStream | undefined;
|
|
122
|
-
setParent(parent: ParentQueryStruct | ParentQueryModel): void;
|
|
123
122
|
/** makes a new queryable field object from a fieldDef */
|
|
124
123
|
makeQueryField(field: FieldDef, referenceId?: string): QueryField;
|
|
125
124
|
root(): QueryStruct;
|
package/dist/model/query_node.js
CHANGED
|
@@ -180,7 +180,6 @@ class QueryStruct {
|
|
|
180
180
|
this.nameMap = new Map();
|
|
181
181
|
this._modelTag = undefined;
|
|
182
182
|
this._arguments = undefined;
|
|
183
|
-
this.setParent(parent);
|
|
184
183
|
if ('model' in parent) {
|
|
185
184
|
this.model = parent.model;
|
|
186
185
|
this.pathAliasMap = new Map();
|
|
@@ -192,6 +191,7 @@ class QueryStruct {
|
|
|
192
191
|
}
|
|
193
192
|
}
|
|
194
193
|
else {
|
|
194
|
+
this.parent = parent.struct;
|
|
195
195
|
this.model = this.getModel();
|
|
196
196
|
this.pathAliasMap = this.root().pathAliasMap;
|
|
197
197
|
this.connectionName = this.root().connectionName;
|
|
@@ -471,18 +471,8 @@ class QueryStruct {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
get eventStream() {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
setParent(parent) {
|
|
477
|
-
if ('struct' in parent) {
|
|
478
|
-
this.parent = parent.struct;
|
|
479
|
-
}
|
|
480
|
-
if ('model' in parent) {
|
|
481
|
-
this.model = parent.model;
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
this.model = this.getModel();
|
|
485
|
-
}
|
|
474
|
+
var _a;
|
|
475
|
+
return (_a = this.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.eventStream;
|
|
486
476
|
}
|
|
487
477
|
/** makes a new queryable field object from a fieldDef */
|
|
488
478
|
makeQueryField(field, referenceId) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DialectFieldList } from '../dialect';
|
|
2
|
-
import type { TurtleDef, QueryResultDef, ResultStructMetadataDef, ResultMetadataDef, PipeSegment, QuerySegment, QueryFieldDef, SegmentFieldDef } from './malloy_types';
|
|
2
|
+
import type { TurtleDef, QueryResultDef, ResultStructMetadataDef, ResultMetadataDef, PipeSegment, QuerySegment, QueryFieldDef, SegmentFieldDef, Query, PrepareResultOptions } from './malloy_types';
|
|
3
3
|
import { AndChain } from './utils';
|
|
4
4
|
import type { JoinInstance } from './join_instance';
|
|
5
5
|
import { QueryStruct, QueryField } from './query_node';
|
|
@@ -63,6 +63,22 @@ export declare class QueryQuery extends QueryField {
|
|
|
63
63
|
/** returns a fields and primary key of a struct for this query */
|
|
64
64
|
getResultStructDef(resultStruct?: FieldInstanceResult, isRoot?: boolean): QueryResultDef;
|
|
65
65
|
getStructSourceSQL(qs: QueryStruct, stageWriter: StageWriter): string;
|
|
66
|
+
/**
|
|
67
|
+
* Compile a Query into SQL stages. Used by both query_source compilation
|
|
68
|
+
* and getCompiledSQL for interpolated sources.
|
|
69
|
+
*
|
|
70
|
+
* @param query The query to compile
|
|
71
|
+
* @param prepareResultOptions Options including manifest for substitution
|
|
72
|
+
* @param stageWriter If provided, stages are added to this writer and lastStageName is returned.
|
|
73
|
+
* If undefined, a new isolated writer is created and full SQL is returned.
|
|
74
|
+
* @param isJoinedSubquery Whether this is a joined subquery
|
|
75
|
+
* @returns { lastStageName, stageWriter, sql } - sql is only set if stageWriter was undefined
|
|
76
|
+
*/
|
|
77
|
+
compileQueryToStages(query: Query, prepareResultOptions: PrepareResultOptions, stageWriter: StageWriter | undefined, isJoinedSubquery: boolean): {
|
|
78
|
+
lastStageName: string;
|
|
79
|
+
stageWriter: StageWriter;
|
|
80
|
+
sql?: string;
|
|
81
|
+
};
|
|
66
82
|
generateSQLJoinBlock(stageWriter: StageWriter, ji: JoinInstance, depth: number): string;
|
|
67
83
|
generateSQLPassthroughKeys(qs: QueryStruct): string;
|
|
68
84
|
generateSQLJoins(stageWriter: StageWriter): string;
|