From 4fad955e74e30c3e548db10a29f1717715bb9f19 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 02:54:51 +0000 Subject: [PATCH 001/160] chore(deps): bump sqlquack to a9dde6a0 --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9f5f63d0..6269367f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2174,7 +2174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde48f36530cbd91b65f050101e6c056d6eb743cea2a8f0f19beef69c6742ff8" dependencies = [ "scopeguard", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3199,7 +3199,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3637,7 +3637,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4499,7 +4499,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=4d0c6bc56da0e44eb164601a2af673ae7998257d#4d0c6bc56da0e44eb164601a2af673ae7998257d" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" dependencies = [ "serde", "serde_json", @@ -4512,7 +4512,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=4d0c6bc56da0e44eb164601a2af673ae7998257d#4d0c6bc56da0e44eb164601a2af673ae7998257d" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" dependencies = [ "proc-macro2", "quote", @@ -4522,7 +4522,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=4d0c6bc56da0e44eb164601a2af673ae7998257d#4d0c6bc56da0e44eb164601a2af673ae7998257d" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" dependencies = [ "sqlquack-syntax", ] @@ -4618,7 +4618,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4740,7 +4740,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5054,7 +5054,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5387,7 +5387,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f03cafe02..65a30d1f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,8 +177,8 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "4d0c6bc56da0e44eb164601a2af673ae7998257d" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "4d0c6bc56da0e44eb164601a2af673ae7998257d" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From 2cd06dc49d583a43126fa6ac69d267146ee8d62d Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 04:37:49 +0000 Subject: [PATCH 002/160] feat(analysis): give native dialects a parse and type contract The native dialect trait now has parse and normalize_type, with read_whole and type_match provided on top. read_whole refuses parser gaps, empty input, more than one statement and statements the parser kept unparsed. type_match reports NotCompared when either side has no normalized type, so the contract check makes no finding instead of guessing. Parser gaps carry the parser's message and whether the failure is a syntax error, and the P026 skip appends that message. AnalysisRoute gains an Unavailable arm, which skips a model with P026 instead of analysing it. The contracts type comparer goes through the route's type_match, and normalize_text_type is public so native dialects can reuse the shared text type rules. --- .../src/analysis_route/constants.rs | 8 ++ .../main/clickhouse_type_match.rs | 2 +- .../src/analysis_route/main/native_gap.rs | 2 + .../analysis_route/main/skipped_analysis.rs | 24 +++- .../src/analysis_route/mod.rs | 4 + .../src/analysis_route/models.rs | 23 +++- .../src/analysis_route/tests.rs | 2 + .../tests/test_clickhouse_type_match.rs | 14 +- .../tests/test_native_dialect.rs | 127 ++++++++++++++++++ .../analysis_route/tests/test_native_gap.rs | 4 + .../src/analysis_route/tests/test_types.rs | 54 ++++++-- .../src/analysis_route/types.rs | 65 ++++++++- .../src/contracts/_helpers/columns.rs | 2 +- .../src/contracts/_helpers/type_comparison.rs | 45 ++++--- .../src/contracts/constants.rs | 2 - .../src/type_system/main/mod.rs | 1 + .../type_system/main/normalize_text_type.rs | 10 ++ .../_helpers/input_loops/model_loop.rs | 11 +- .../input_loops/model_loop_opt_out.rs | 30 ++++- .../_helpers/lineage/project_lineage.rs | 4 +- 20 files changed, 381 insertions(+), 53 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs create mode 100644 crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 1e31d6343..eeb7ce5e8 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -4,6 +4,14 @@ use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +/// The dialect a project with no analysis dialect is named by. +pub(crate) const GENERIC_DIALECT: &str = "generic"; +/// The gap of SQL that holds more than one statement. +pub(crate) const MORE_THAN_ONE_STATEMENT: &str = "more than one statement"; +/// The gap of SQL that holds no statement. +pub(crate) const NO_STATEMENT: &str = "no statement"; +/// The gap of a statement the parser kept as raw text. +pub(crate) const UNPARSED_STATEMENT: &str = "unparsed statement"; /// The warning, or under `require_sql_analysis` the error, for a model analysis skips. pub(crate) const SKIPPED_ANALYSIS_CODE: &str = "P026"; /// ClickHouse's top-level nullability wrapper, which the native profile spells as a flag. diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs index 91223ddd4..a5ee54f86 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs @@ -6,7 +6,7 @@ use crate::analysis_route::models::TypeMatch; pub fn clickhouse_type_match(declared: &str, inferred: &str, inferred_nullable: bool) -> TypeMatch { let (declared_base, declared_nullable) = unwrapped(declared); let (inferred_base, wrapped_nullable) = unwrapped(inferred); - TypeMatch { + TypeMatch::Compared { same_type: declared_base == inferred_base, nullable_into_non_null: !declared_nullable && (inferred_nullable || wrapped_nullable), } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs index 2d2c02726..e7c3f7439 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs @@ -27,7 +27,9 @@ fn located(sql: &str, gap: ParserGap) -> LocatedGap { let position: TextPosition = text_position(sql, before.chars().count()); LocatedGap { construct: gap.construct, + message: gap.message, line: position.line, column: position.column, + syntax: gap.syntax, } } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs index 3fba7a616..eacef989f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs @@ -1,15 +1,33 @@ -use crate::analysis_route::constants::SKIPPED_ANALYSIS_CODE; +use crate::analysis_route::constants::{GENERIC_DIALECT, SKIPPED_ANALYSIS_CODE}; use crate::analysis_route::models::LocatedGap; -/// P026's code and message for a model whose SQL the dialect's native parser cannot read. +/// P026's code and message for a model whose SQL the dialect's native parser cannot read, with +/// the parser's own message when it says more than the construct's name. #[must_use] pub fn skipped_analysis(model: &str, dialect: &str, gap: &LocatedGap) -> (&'static str, String) { + let detail: String = if gap.message.is_empty() || gap.message == gap.construct { + String::new() + } else { + format!(": {}", gap.message) + }; ( SKIPPED_ANALYSIS_CODE, format!( "SQL analysis skipped for model '{model}': the {dialect} parser cannot read {} at \ - line {}, column {}", + line {}, column {}{detail}", gap.construct, gap.line, gap.column ), ) } + +/// P026's code and message for a model whose dialect no native parser reads. +#[must_use] +pub fn unavailable_analysis(model: &str, dialect: Option<&str>) -> (&'static str, String) { + ( + SKIPPED_ANALYSIS_CODE, + format!( + "SQL analysis skipped for model '{model}': no native parser reads the {} dialect", + dialect.unwrap_or(GENERIC_DIALECT) + ), + ) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/mod.rs index 76ff5d49a..2e8da8499 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/mod.rs @@ -5,5 +5,9 @@ pub mod main; pub mod models; pub mod types; +pub use crate::analysis_route::main::analysis_route::analysis_route; +pub use crate::analysis_route::models::{AnalysisRoute, ParserGap, TypeMatch}; +pub use crate::analysis_route::types::NativeDialect; + #[cfg(test)] mod tests; diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index a93a53d29..0d4e8f6f5 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -9,27 +9,40 @@ pub enum AnalysisRoute { Legacy, /// The dialect's native module; a model it cannot read whole is not analysed. Native(&'static dyn NativeDialect), + /// No native module reads the dialect: nothing is analysed, and no Polyglot path stands in. + Unavailable, } -/// The first construct a native parser cannot read, and its byte offset in the SQL it read. +/// The first construct or error that stops a native parser, at its byte offset in the SQL it read. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ParserGap { + /// The construct, such as `PIVOT`, or the parser's error message. pub construct: String, + /// The parser's bare message, without the line and column an error's `Display` adds. + pub message: String, pub offset: usize, + /// Whether the parser reports invalid syntax; false for an unsupported construct or a limit. + pub syntax: bool, } /// A gap with the line and column, from 1, of its offset in the analysed SQL. #[derive(Clone, Debug, PartialEq, Eq)] pub struct LocatedGap { pub construct: String, + pub message: String, pub line: usize, pub column: usize, + pub syntax: bool, } /// How an inferred column type meets its declared type: the type itself, then its nullability. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct TypeMatch { - pub same_type: bool, - /// The inferred column may be null, but its declared type does not allow it. - pub nullable_into_non_null: bool, +pub enum TypeMatch { + /// One side is a type the dialect cannot spell or refuses: no finding and no match. + NotCompared, + Compared { + same_type: bool, + /// The inferred column may be null, but its declared type does not allow it. + nullable_into_non_null: bool, + }, } diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index b8464bdb5..93f21c6de 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -1,5 +1,7 @@ #[path = "tests/test_clickhouse_type_match.rs"] mod test_clickhouse_type_match; +#[path = "tests/test_native_dialect.rs"] +mod test_native_dialect; #[path = "tests/test_native_gap.rs"] mod test_native_gap; #[path = "tests/test_types.rs"] diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs index e23bab65e..4e55839f6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs @@ -11,7 +11,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "Nullable(String)", inferred: "String", inferred_nullable: true, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: true, nullable_into_non_null: false, }, @@ -21,7 +21,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "LowCardinality(String)", inferred: "String", inferred_nullable: false, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: true, nullable_into_non_null: false, }, @@ -31,7 +31,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "LowCardinality(Nullable(String))", inferred: "Nullable(String)", inferred_nullable: false, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: true, nullable_into_non_null: false, }, @@ -41,7 +41,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "String", inferred: "String", inferred_nullable: true, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: true, nullable_into_non_null: true, }, @@ -51,7 +51,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "Nullable(Int64)", inferred: "Int64", inferred_nullable: false, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: true, nullable_into_non_null: false, }, @@ -61,7 +61,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "Nullable(Int32)", inferred: "Int64", inferred_nullable: true, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: false, nullable_into_non_null: false, }, @@ -71,7 +71,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu declared: "Array(Nullable(String))", inferred: "Array(String)", inferred_nullable: false, - expected_match: TypeMatch { + expected_match: TypeMatch::Compared { same_type: false, nullable_into_non_null: false, }, diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs new file mode 100644 index 000000000..18baa18a2 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -0,0 +1,127 @@ +use crate::analysis_route::main::skipped_analysis::{skipped_analysis, unavailable_analysis}; +use crate::analysis_route::models::{LocatedGap, TypeMatch}; +use crate::analysis_route::tests::test_types::{ + NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, +}; +use crate::analysis_route::types::NativeDialect; + +#[test] +fn given_sql_when_reading_it_whole_then_only_one_parsed_statement_without_a_gap_is_read() { + let test_cases = [ + ReadWholeTestCase { + description: "one query is read whole", + sql: "SELECT a FROM t", + expected_gap: None, + }, + ReadWholeTestCase { + description: "two statements are not one model", + sql: "SELECT 1; SELECT 2", + expected_gap: Some("more than one statement"), + }, + ReadWholeTestCase { + description: "no statement at all", + sql: " ", + expected_gap: Some("no statement"), + }, + ReadWholeTestCase { + description: "a statement kept as raw text is not read", + sql: "RAW VACUUM t", + expected_gap: Some("unparsed statement"), + }, + ReadWholeTestCase { + description: "a construct the parser refuses stops it first", + sql: "SELECT * FROM t PIVOT (sum(x) FOR y IN (1))", + expected_gap: Some("PIVOT"), + }, + ]; + for test_case in test_cases { + assert_eq!( + PivotlessDialect + .read_whole(test_case.sql) + .err() + .map(|gap| gap.construct), + test_case.expected_gap.map(str::to_owned), + "{}", + test_case.description + ); + } +} + +#[test] +fn given_a_type_the_dialect_refuses_when_matching_types_then_nothing_is_compared() { + let test_cases = [ + NativeTypeMatchTestCase { + description: "a refused declared type is not compared", + declared: "NOT_A_TYPE", + inferred: "INTEGER", + expected_match: TypeMatch::NotCompared, + }, + NativeTypeMatchTestCase { + description: "a refused inferred type is not compared", + declared: "INTEGER", + inferred: "NOT_A_TYPE", + expected_match: TypeMatch::NotCompared, + }, + NativeTypeMatchTestCase { + description: "two known, different types still mismatch", + declared: "INTEGER", + inferred: "TEXT", + expected_match: TypeMatch::Compared { + same_type: false, + nullable_into_non_null: false, + }, + }, + NativeTypeMatchTestCase { + description: "the same type matches", + declared: "INTEGER", + inferred: "integer", + expected_match: TypeMatch::Compared { + same_type: true, + nullable_into_non_null: false, + }, + }, + ]; + for test_case in test_cases { + assert_eq!( + PivotlessDialect.type_match(test_case.declared, test_case.inferred, true), + test_case.expected_match, + "{}", + test_case.description + ); + } +} + +#[test] +fn given_a_parser_message_or_no_parser_when_skipping_analysis_then_p026_says_why() { + let syntax: LocatedGap = LocatedGap { + construct: "syntax error".to_owned(), + message: "syntax error at or near \"FROM\"".to_owned(), + line: 1, + column: 15, + syntax: true, + }; + assert_eq!( + skipped_analysis("orders", "postgres", &syntax), + ( + "P026", + "SQL analysis skipped for model 'orders': the postgres parser cannot read syntax \ + error at line 1, column 15: syntax error at or near \"FROM\"" + .to_owned() + ), + "the parser's message follows the construct" + ); + assert_eq!( + unavailable_analysis("orders", Some("trino")), + ( + "P026", + "SQL analysis skipped for model 'orders': no native parser reads the trino dialect" + .to_owned() + ), + "an unknown dialect is named" + ); + assert_eq!( + unavailable_analysis("orders", None).1, + "SQL analysis skipped for model 'orders': no native parser reads the generic dialect", + "no dialect is the generic dialect" + ); +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 75a160099..a69a2d363 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -105,8 +105,10 @@ fn given_model_sql_when_probing_the_native_parser_then_its_gap_is_located_after_ placeholders: &[], expected_gap: Some(LocatedGap { construct: "PIVOT".to_owned(), + message: "PIVOT".to_owned(), line: 3, column: 3, + syntax: false, }), }, ]; @@ -136,8 +138,10 @@ fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_and_the_ dialect: "postgres", gap: LocatedGap { construct: "PIVOT".to_owned(), + message: "PIVOT".to_owned(), line: 3, column: 3, + syntax: false, }, expected_code: "P026", expected_message: "SQL analysis skipped for model 'orders_pivot': the postgres parser \ diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 9587ebda2..69fd87107 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -1,5 +1,10 @@ +use sqlquack_syntax::expressions::statements::models::Command; +use sqlquack_syntax::{Expression, Parsed}; + use crate::analysis_route::models::{LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::NormalizedType; pub(super) struct NativeGapTestCase { pub(super) description: &'static str, @@ -23,7 +28,8 @@ pub(super) struct SkippedAnalysisTestCase { pub(super) expected_message: &'static str, } -/// A test dialect whose parser refuses PIVOT, unfilled placeholders and one-part stubs. +/// A test dialect over the PostgreSQL parser that also refuses PIVOT, unfilled placeholders and +/// one-part stubs, keeps SQL after `RAW ` as raw text, and refuses the type `NOT_A_TYPE`. pub(super) struct PivotlessDialect; impl NativeDialect for PivotlessDialect { @@ -31,25 +37,57 @@ impl NativeDialect for PivotlessDialect { "postgres" } - fn read_whole(&self, sql: &str) -> Result<(), ParserGap> { + fn stub_prefix(&self) -> &'static str { + "__sqlbuild." + } + + fn parse(&self, sql: &str) -> Result { let refused = ["PIVOT", "@@@", "__sqlbuild_udf_", "__cursor_", "@'"] .iter() .filter_map(|word| sql.find(word).map(|offset| (offset, *word))) .min(); - match refused { - Some((offset, word)) => Err(ParserGap { + if let Some(text) = sql.strip_prefix("RAW ") { + return Ok(Parsed { + statements: vec![Expression::Command(Box::new(Command { + this: text.to_owned(), + }))], + gaps: Vec::new(), + }); + } + if let Some((offset, word)) = refused { + return Err(ParserGap { construct: word.to_owned(), + message: word.to_owned(), offset, - }), - None => Ok(()), + syntax: false, + }); } + sqlquack_syntax_postgres::parse(sql).map_err(|error| ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }) } - fn stub_prefix(&self) -> &'static str { - "__sqlbuild." + fn normalize_type(&self, spelling: &str) -> Option { + (!spelling.eq_ignore_ascii_case("NOT_A_TYPE")).then(|| normalize_text_type(spelling, None)) } } +pub(super) struct ReadWholeTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + pub(super) expected_gap: Option<&'static str>, +} + +pub(super) struct NativeTypeMatchTestCase { + pub(super) description: &'static str, + pub(super) declared: &'static str, + pub(super) inferred: &'static str, + pub(super) expected_match: TypeMatch, +} + pub(super) struct TypeMatchTestCase { pub(super) description: &'static str, pub(super) declared: &'static str, diff --git a/crates/sqlbuild-analysis/src/analysis_route/types.rs b/crates/sqlbuild-analysis/src/analysis_route/types.rs index fedc80ad2..b358e7727 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/types.rs @@ -1,15 +1,72 @@ //! What a dialect's native analysis module provides. -use crate::analysis_route::models::ParserGap; +use sqlquack_syntax::{Expression, Parsed}; + +use crate::analysis_route::constants::{MORE_THAN_ONE_STATEMENT, NO_STATEMENT, UNPARSED_STATEMENT}; +use crate::analysis_route::models::{ParserGap, TypeMatch}; +use crate::type_system::models::NormalizedType; /// A dialect whose SQL SQLQuack analyses in place of Polyglot. pub trait NativeDialect: Sync { /// The adapter's analysis dialect name, such as `postgres`. fn name(&self) -> &'static str; - /// Whether the native parser reads `sql` whole: one statement, no gap and no unparsed bytes. - fn read_whole(&self, sql: &str) -> Result<(), ParserGap>; - /// The engine-accepted prefix of UDF and cursor stubs: `__sqlbuild_`, or `__sqlbuild.` for T-SQL. fn stub_prefix(&self) -> &'static str; + + /// Every statement of `sql` with the parser's named gaps, or the first error that stops it. + fn parse(&self, sql: &str) -> Result; + + /// A type as written in this dialect, as SQLBuild compares types; `None` when the dialect + /// cannot spell it or its engine refuses it, and nothing is compared against it. + fn normalize_type(&self, spelling: &str) -> Option; + + /// The one statement of `sql` when the parser reads it whole: no gap, and not raw text. + fn read_whole(&self, sql: &str) -> Result { + let parsed: Parsed = self.parse(sql)?; + if let Some(gap) = parsed.gaps.first() { + return Err(ParserGap { + construct: gap.construct.to_owned(), + message: gap.construct.to_owned(), + offset: gap.start, + syntax: false, + }); + } + let mut statements = parsed.statements.into_iter(); + let (Some(statement), None) = (statements.next(), statements.next()) else { + let construct: &str = if sql.trim().is_empty() { + NO_STATEMENT + } else { + MORE_THAN_ONE_STATEMENT + }; + return Err(whole_gap(construct, 0)); + }; + match statement { + Expression::Command(_) => Err(whole_gap(UNPARSED_STATEMENT, 0)), + statement => Ok(statement), + } + } + + /// Whether a declared and an inferred type match, nullability apart; `NotCompared` when + /// `normalize_type` gives `None` for either. An override must keep that rule. + fn type_match(&self, declared: &str, inferred: &str, inferred_nullable: bool) -> TypeMatch { + let _ = inferred_nullable; + match (self.normalize_type(declared), self.normalize_type(inferred)) { + (Some(declared), Some(inferred)) => TypeMatch::Compared { + same_type: declared == inferred, + nullable_into_non_null: false, + }, + _ => TypeMatch::NotCompared, + } + } +} + +/// A gap `read_whole` names itself, which no parser reports as invalid syntax. +fn whole_gap(construct: &str, offset: usize) -> ParserGap { + ParserGap { + construct: construct.to_owned(), + message: construct.to_owned(), + offset, + syntax: false, + } } diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs index a5273a521..811f5ebce 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs @@ -310,7 +310,7 @@ fn column_type( }, })); }; - if check.types.types_equal(declared_type, inferred_type)? == TypeComparison::Equal { + if check.types.types_equal(declared_type, inferred_type)? != TypeComparison::Different { return Ok(None); } Ok(Some(ContractDiagnostic { diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs index e439b2d73..5b3513bcb 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs @@ -4,9 +4,8 @@ use std::cell::RefCell; use std::collections::HashMap; use crate::analysis_route::main::analysis_route::analysis_route; -use crate::analysis_route::main::clickhouse_type_match::clickhouse_type_match; -use crate::analysis_route::models::AnalysisRoute; -use crate::contracts::constants::{CLICKHOUSE_DIALECT, UPPER_FOLDING_DIALECT}; +use crate::analysis_route::models::{AnalysisRoute, TypeMatch}; +use crate::contracts::constants::UPPER_FOLDING_DIALECT; use crate::contracts::models::{DeclaredColumn, InferredOutputColumn}; use crate::type_system::errors::TypeNormalizationError; use crate::type_system::main::normalize_type::normalize_type; @@ -17,13 +16,15 @@ use crate::type_system::models::{NormalizedType, TypeNormalization}; pub(crate) enum TypeComparison { Equal, Different, + /// A native route cannot spell one of the types, or has no parser: no finding either way. + NotCompared, } /// One request's type comparisons, normalizing each type string once as Python's cache does. pub(crate) struct TypeComparer<'a> { dialect: &'a str, - /// ClickHouse on its native route, whose types are compared without top-level wrappers. - native_clickhouse: bool, + /// The dialect's route: a native module compares types itself, and no route compares none. + route: AnalysisRoute, normalized: RefCell>>, } @@ -36,8 +37,7 @@ impl<'a> TypeComparer<'a> { pub(crate) fn new(dialect: &'a str) -> Self { Self { dialect, - native_clickhouse: dialect == CLICKHOUSE_DIALECT - && matches!(analysis_route(Some(dialect)), AnalysisRoute::Native(_)), + route: analysis_route(Some(dialect)), normalized: RefCell::new(HashMap::new()), } } @@ -48,12 +48,18 @@ impl<'a> TypeComparer<'a> { left: &str, right: &str, ) -> Result { - if self.native_clickhouse { - return Ok(if clickhouse_type_match(left, right, false).same_type { - TypeComparison::Equal - } else { - TypeComparison::Different - }); + match self.route { + AnalysisRoute::Legacy => {} + AnalysisRoute::Unavailable => return Ok(TypeComparison::NotCompared), + AnalysisRoute::Native(dialect) => { + return Ok(match dialect.type_match(left, right, false) { + TypeMatch::NotCompared => TypeComparison::NotCompared, + TypeMatch::Compared { + same_type: true, .. + } => TypeComparison::Equal, + TypeMatch::Compared { .. } => TypeComparison::Different, + }); + } } let left: NormalizedType = self.normalized(left)?; let right: NormalizedType = self.normalized(right)?; @@ -73,15 +79,20 @@ impl<'a> TypeComparer<'a> { if declared.not_null && inferred.nullable { return true; } - let (true, Some(declared_type), Some(inferred_type)) = ( - self.native_clickhouse, + let (AnalysisRoute::Native(dialect), Some(declared_type), Some(inferred_type)) = ( + self.route, declared.declared_type.as_deref(), inferred.inferred_type.as_deref(), ) else { return false; }; - clickhouse_type_match(declared_type, inferred_type, inferred.nullable) - .nullable_into_non_null + matches!( + dialect.type_match(declared_type, inferred_type, inferred.nullable), + TypeMatch::Compared { + nullable_into_non_null: true, + .. + } + ) } fn normalized(&self, type_sql: &str) -> Result { diff --git a/crates/sqlbuild-analysis/src/contracts/constants.rs b/crates/sqlbuild-analysis/src/contracts/constants.rs index 08cb6e027..c1edad467 100644 --- a/crates/sqlbuild-analysis/src/contracts/constants.rs +++ b/crates/sqlbuild-analysis/src/contracts/constants.rs @@ -42,7 +42,5 @@ pub(crate) const PROMOTION_REMOVE_CONTRACT_HELP: &str = "or remove the model's e pub(crate) const SETTING_SNIPPET_INDENT: &str = " "; pub(crate) const ADDITIONAL_HELP_SEPARATOR: &str = "\n = help: "; -/// The analysis dialect name whose native route compares ClickHouse types without wrappers. -pub(crate) const CLICKHOUSE_DIALECT: &str = "clickhouse"; /// The dialect that folds unquoted identifiers to upper case. pub(crate) const UPPER_FOLDING_DIALECT: &str = "snowflake"; diff --git a/crates/sqlbuild-analysis/src/type_system/main/mod.rs b/crates/sqlbuild-analysis/src/type_system/main/mod.rs index 7434eded5..1ac950cb8 100644 --- a/crates/sqlbuild-analysis/src/type_system/main/mod.rs +++ b/crates/sqlbuild-analysis/src/type_system/main/mod.rs @@ -1,3 +1,4 @@ +pub mod normalize_text_type; pub mod normalize_type; pub mod polyglot_type_args; pub mod same_type; diff --git a/crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs b/crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs new file mode 100644 index 000000000..51e645893 --- /dev/null +++ b/crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs @@ -0,0 +1,10 @@ +//! `normalize_text_type`: a type's comparison shape read from its text alone, without Polyglot. + +use crate::type_system::_helpers::fallback::normalize_with_fallback; +use crate::type_system::models::{NormalizedType, TypeDialect}; + +/// `type_sql`'s name, family and parameters as the text fallback reads them under `dialect`. +#[must_use] +pub fn normalize_text_type(type_sql: &str, dialect: Option) -> NormalizedType { + normalize_with_fallback(type_sql, dialect) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs index c61261dd7..b75a525fa 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs @@ -73,7 +73,8 @@ use crate::bindings::_helpers::input_loops::model_loop_macros::{ }; use crate::bindings::_helpers::input_loops::model_loop_named::{SCHEMA_DECLARATION, named_usages}; use crate::bindings::_helpers::input_loops::model_loop_opt_out::{ - OptOut, OptOutModel, reject_unparseable_query, rejected_opt_out, skip_unreadable, + OptOut, OptOutModel, reject_unparseable_query, rejected_opt_out, skip_unavailable, + skip_unreadable, }; use crate::bindings::_helpers::input_loops::model_loop_prepass::{ NativeHeader, all_header_facts, native_header, @@ -204,6 +205,8 @@ pub(crate) struct NativeModelLoop { pub(crate) project_config_path: String, /// Whether the dialect's SQL is analysed by Polyglot or by its native module. pub(crate) route: AnalysisRoute, + /// The adapter's analysis dialect name, which P026 names when no native parser reads it. + pub(crate) dialect: Option, contract_classes: Py, loop_classes: Py, } @@ -265,6 +268,7 @@ impl NativeModelLoop { }, project_config_path, route: analysis_route(dialect.as_deref()), + dialect, contract_classes, loop_classes, }) @@ -837,6 +841,11 @@ impl NativeModelLoop { skipped = !read; (read, None) } + AnalysisRoute::Unavailable => { + skip_unavailable(self.settings, self.dialect.as_deref(), context, model)?; + skipped = true; + (false, None) + } }, AnalysisGate::Disabled => match rejected_opt_out(self, context, model)? { OptOut::Accepted => (false, None), diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs index b392bc8aa..234f5502b 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs @@ -5,7 +5,9 @@ use std::path::Path; use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyErr, PyResult, Python}; use pyo3::types::{PyBool, PyBoolMethods, PyDict, PyDictMethods, PyList, PyString, PyTuple}; use sqlbuild_analysis::analysis_route::main::native_gap::native_gap; -use sqlbuild_analysis::analysis_route::main::skipped_analysis::skipped_analysis; +use sqlbuild_analysis::analysis_route::main::skipped_analysis::{ + skipped_analysis, unavailable_analysis, +}; use sqlbuild_analysis::analysis_route::models::{AnalysisRoute, LocatedGap}; use sqlbuild_analysis::analysis_route::types::NativeDialect; use sqlbuild_analysis::assembly::project::models::{SyntaxCheck, SyntaxMode}; @@ -77,6 +79,7 @@ pub(crate) fn rejected_opt_out<'py>( let readable: bool = match model_loop.route { AnalysisRoute::Legacy => syntax_valid(model.config.py(), GENERIC_DIALECT, &checks)?, AnalysisRoute::Native(dialect) => first_gap(dialect, context, &checks)?.is_none(), + AnalysisRoute::Unavailable => false, }; if readable { Ok(OptOut::Rejected(location(model_loop, context, model)?)) @@ -97,6 +100,29 @@ pub(crate) fn skip_unreadable( return Ok(true); }; let (code, message) = skipped_analysis(model.name, dialect.name(), &gap); + report_skip(settings, context, model, code, message)?; + Ok(false) +} + +/// P026 for a model whose dialect no native parser reads; it warns, or raises, and is not analysed. +pub(crate) fn skip_unavailable( + settings: ModelLoopSettings, + dialect: Option<&str>, + context: &LoopContext<'_, '_>, + model: &OptOutModel<'_, '_>, +) -> PyResult<()> { + let (code, message) = unavailable_analysis(model.name, dialect); + report_skip(settings, context, model, code, message) +} + +/// Reports P026 as a warning, or raises it under `require_sql_analysis`. +fn report_skip( + settings: ModelLoopSettings, + context: &LoopContext<'_, '_>, + model: &OptOutModel<'_, '_>, + code: &str, + message: String, +) -> PyResult<()> { let py: Python<'_> = model.config.py(); let help: &str = &context.classes.syntax_help; if settings.require_sql_analysis { @@ -134,7 +160,7 @@ pub(crate) fn skip_unreadable( .classes .report_diagnostic .call((), Some(&arguments))?; - Ok(false) + Ok(()) } /// The model's analysis SQL and each hook statement, as filled to run, with its placeholders. diff --git a/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs b/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs index 30e49894f..7bc1ff9fd 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs @@ -170,9 +170,9 @@ fn fast_lineage<'py>( let mut work: Vec> = Vec::with_capacity(models.len()); let mut requests: Vec = Vec::new(); let dialect_name: Option = dialect.map(|name| name.extract()).transpose()?; - let native: bool = matches!( + let native: bool = !matches!( analysis_route(dialect_name.as_deref()), - AnalysisRoute::Native(_) + AnalysisRoute::Legacy ); for model in models { let (has_star, star_resolved): (bool, bool) = star_flags(model)?; From b320f8e305a9a5d373ea99bc131a8907e637ef25 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:34:27 +0000 Subject: [PATCH 003/160] feat(analysis): analyse native dialect queries with sqlquack-semantics A native dialect now names its sqlquack-semantics profile, and the route provides analyse and macro_body on top of read_whole. analyse turns the query's lineage and findings into the facts the analysis session keeps: output columns typed through the dialect's normalize_type, lineage rows over the model's referenced resources, the columns deciding rows, the star flags, and findings in SQLBuild's codes through the same sqlbuild_code the Polyglot path uses, so implicit-conversion findings stay blocking. Unknown functions a dialect declares as extensions are not reported. StandInSchema gives probe queries, such as a macro body's, one relation of known columns. read_whole now reports SQL with no statement, such as a lone comment, as no statement rather than more than one. --- Cargo.lock | 38 ++- Cargo.toml | 2 + crates/sqlbuild-analysis/Cargo.toml | 1 + .../src/analysis_route/_helpers/mod.rs | 1 + .../_helpers/native_analysis.rs | 169 +++++++++++++ .../src/analysis_route/constants.rs | 8 + .../src/analysis_route/mod.rs | 5 +- .../src/analysis_route/models.rs | 75 ++++++ .../tests/test_native_dialect.rs | 238 +++++++++++++++++- .../src/analysis_route/tests/test_types.rs | 60 ++++- .../src/analysis_route/types.rs | 54 +++- .../_helpers/diagnostics.rs | 2 +- .../semantic_validation/main/diagnostics.rs | 7 + 13 files changed, 640 insertions(+), 20 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs diff --git a/Cargo.lock b/Cargo.lock index 6269367f6..7a4ed5142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4142,6 +4142,7 @@ dependencies = [ "sqlbuild-scopes", "sqlbuild-sqltext", "sqlclean", + "sqlquack-semantics", "sqlquack-syntax", "sqlquack-syntax-postgres", ] @@ -4496,6 +4497,37 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "sqlquack-core" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +dependencies = [ + "serde", + "serde_json", + "sqlquack-syntax", +] + +[[package]] +name = "sqlquack-semantics" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +dependencies = [ + "serde", + "serde_json", + "sqlquack-core", + "sqlquack-snowflake", + "sqlquack-syntax", +] + +[[package]] +name = "sqlquack-snowflake" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +dependencies = [ + "serde", + "sqlquack-core", +] + [[package]] name = "sqlquack-syntax" version = "0.2.0" @@ -4618,7 +4650,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5054,7 +5086,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 65a30d1f3..e5e3ab99e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -176,6 +176,8 @@ sqlbuild-sqltext = { path = "crates/sqlbuild-sqltext" } sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } +# Scopes, name resolution, types and lineage for the native analysis route. +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } # The lossless SQL tokenizer behind query fingerprints. sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 80a5ae231..95c643ebd 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -26,6 +26,7 @@ sqlbuild-render.workspace = true sqlbuild-scopes.workspace = true sqlbuild-sqltext.workspace = true sqlclean.workspace = true +sqlquack-semantics.workspace = true sqlquack-syntax.workspace = true sqlquack-syntax-postgres.workspace = true diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs new file mode 100644 index 000000000..801bee731 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -0,0 +1 @@ +pub(crate) mod native_analysis; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs new file mode 100644 index 000000000..e03c775d9 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs @@ -0,0 +1,169 @@ +//! The provided native analysis: sqlquack-semantics lineage and findings as SQLBuild's facts. + +use sqlquack_semantics::lineage::models::{ + LineageContext, NameFinding, Nullability, OutputLineage, QueryLineage, TransformKind, +}; +use sqlquack_semantics::{statement_lineage, statement_name_findings}; +use sqlquack_syntax::Expression; + +use crate::analysis_route::constants::{ERROR_SEVERITY, UNKNOWN_FUNCTION_CODE}; +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::constants::{ + CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, CONFIDENCE_UNKNOWN, NON_NULL_NULLABILITY, + NULLABLE_NULLABILITY, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, + TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, UNKNOWN_NULLABILITY, +}; +use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; +use crate::semantic_validation::main::diagnostics::sqlbuild_code; +use crate::semantic_validation::models::DiagnosticRow; + +/// The analysis of `statement`, the one statement of `input.sql`, under `dialect`'s profile. +pub(crate) fn native_analysis( + dialect: &D, + statement: &Expression, + input: &AnalysisInput<'_>, +) -> NativeAnalysis { + let context: LineageContext<'_> = LineageContext { + profile: dialect.profile(), + schema: input.schema, + sql: Some(input.sql), + }; + let lineage: QueryLineage = statement_lineage(statement, context); + NativeAnalysis { + columns: lineage + .outputs + .iter() + .map(|output| column_fact(dialect, output)) + .collect(), + lineage: lineage + .outputs + .iter() + .map(|output| lineage_row(output, input.references)) + .collect(), + row_sources: lineage + .row_sources + .into_iter() + .map(|source| (source.relation, source.column)) + .collect(), + has_star: lineage.projects_star, + star_resolved: !lineage.open_star, + diagnostics: statement_name_findings(statement, context) + .into_iter() + .filter_map(|finding| diagnostic_row(input, finding)) + .collect(), + } +} + +/// An output column typed through the dialect, so both routes spell its type alike. +fn column_fact(dialect: &D, output: &OutputLineage) -> ColumnFact { + ColumnFact { + name: output.name.clone(), + data_type: output + .data_type + .as_deref() + .and_then(|spelling| dialect.normalize_type(spelling)) + .map(|normalized| normalized.normalized_name), + nullability: match output.nullability { + Nullability::Unknown => UNKNOWN_NULLABILITY, + Nullability::Nullable => NULLABLE_NULLABILITY, + Nullability::NonNull => NON_NULL_NULLABILITY, + } + .to_owned(), + } +} + +/// An output column's lineage over the model's referenced resources, as the legacy rows give it: +/// columns of relations the model does not reference are left out, and a column reading none of +/// its references is of unknown confidence unless it is a constant. +fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) -> LineageRow { + let transform_code: u8 = match output.kind { + TransformKind::Constant => TRANSFORM_CONSTANT, + TransformKind::Direct => TRANSFORM_DIRECT, + TransformKind::Cast => TRANSFORM_CAST, + TransformKind::Expression => TRANSFORM_EXPRESSION, + TransformKind::Aggregation => TRANSFORM_AGGREGATION, + }; + let sources: Vec<(String, String, String)> = output + .sources + .iter() + .filter_map(|source| { + references + .iter() + .find(|(analysis_name, _, _)| *analysis_name == source.relation) + .map(|(_, resource_type, resource_name)| { + ( + resource_type.clone(), + resource_name.clone(), + source.column.clone(), + ) + }) + }) + .collect(); + let confidence_code: u8 = if sources.is_empty() && transform_code != TRANSFORM_CONSTANT { + CONFIDENCE_UNKNOWN + } else if output.resolved { + CONFIDENCE_HIGH + } else { + CONFIDENCE_MEDIUM + }; + LineageRow { + output_column: output.name.clone(), + transform_code, + confidence_code, + sources, + } +} + +/// A finding in SQLBuild's code at its position in `input.sql`; `None` for a code SQLBuild does +/// not report, and for an unknown function the dialect declares as an extension. +fn diagnostic_row(input: &AnalysisInput<'_>, finding: NameFinding) -> Option { + if finding.code == UNKNOWN_FUNCTION_CODE + && input + .extension_functions + .iter() + .any(|name| name.eq_ignore_ascii_case(function_name(&finding))) + { + return None; + } + let code: String = sqlbuild_code(finding.code)?; + let (line, column, start, end) = match finding.span { + Some(span) => { + let (line, column) = line_column(input.sql, span.start); + (Some(line), Some(column), Some(span.start), Some(span.end)) + } + None => (None, None, None, None), + }; + Some(DiagnosticRow { + code, + message: finding.message, + line, + column, + start, + end, + severity: ERROR_SEVERITY.to_owned(), + }) +} + +/// The function an unknown-function finding names: the name as written when the finding keeps +/// it, else the one its message quotes, as in `Unknown function 'NAME' for dialect DuckDB`. +fn function_name(finding: &NameFinding) -> &str { + if finding.written.is_empty() { + finding.message.split('\'').nth(1).unwrap_or_default() + } else { + &finding.written + } +} + +/// The line and column, from 1, of character `offset` in `sql`. +fn line_column(sql: &str, offset: usize) -> (usize, usize) { + sql.chars() + .take(offset) + .fold((1, 1), |(line, column), character| { + if character == '\n' { + (line + 1, 1) + } else { + (line, column + 1) + } + }) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index eeb7ce5e8..9e622059a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -18,3 +18,11 @@ pub(crate) const SKIPPED_ANALYSIS_CODE: &str = "P026"; pub(crate) const NULLABLE_WRAPPER: &str = "Nullable"; /// ClickHouse's top-level dictionary-encoding wrapper, which leaves the column's type as it is. pub(crate) const LOW_CARDINALITY_WRAPPER: &str = "LowCardinality"; +/// The finding of a function the dialect's catalogue does not know. +pub(crate) const UNKNOWN_FUNCTION_CODE: &str = "E202"; +/// The severity of every finding the native analysis reports, as the Polyglot path blocks them. +pub(crate) const ERROR_SEVERITY: &str = "error"; +/// What `macro_body` puts before a body to read it as the one projection of a query. +pub(crate) const MACRO_BODY_PREFIX: &str = "SELECT "; +/// The gap of a macro body that is not one expression. +pub(crate) const NOT_ONE_EXPRESSION: &str = "not one expression"; diff --git a/crates/sqlbuild-analysis/src/analysis_route/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/mod.rs index 2e8da8499..2a9f4bce7 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/mod.rs @@ -1,12 +1,15 @@ //! Which engine analyses a dialect's SQL: the Polyglot path, or a native module over SQLQuack. +pub(crate) mod _helpers; pub(crate) mod constants; pub mod main; pub mod models; pub mod types; pub use crate::analysis_route::main::analysis_route::analysis_route; -pub use crate::analysis_route::models::{AnalysisRoute, ParserGap, TypeMatch}; +pub use crate::analysis_route::models::{ + AnalysisInput, AnalysisRoute, NativeAnalysis, ParserGap, StandInSchema, TypeMatch, +}; pub use crate::analysis_route::types::NativeDialect; #[cfg(test)] diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 0d4e8f6f5..0c487b2f0 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -1,6 +1,11 @@ //! The route a dialect's analysis takes, and what stops a native parser reading SQL whole. +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; +use sqlquack_semantics::lineage::types::LineageSchema; + use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; +use crate::semantic_validation::models::DiagnosticRow; /// The engine that analyses a dialect's SQL. #[derive(Clone, Copy)] @@ -46,3 +51,73 @@ pub enum TypeMatch { nullable_into_non_null: bool, }, } + +/// What a native module analyses: one query, the relations it may read, and how to name them. +#[derive(Clone, Copy, Debug)] +pub struct AnalysisInput<'a> { + /// The analysis SQL, normalised with the dialect's stubs and its placeholders filled. + pub sql: &'a str, + /// The columns, types and nullability of the relations the query may read. + pub schema: &'a dyn LineageSchema, + /// `(analysis name, resource type, resource name)` of each relation the model references. + pub references: &'a [(String, String, String)], + /// Functions the dialect's catalogue cannot know, such as MotherDuck's `md_*`, matched without + /// case: no unknown-function finding names them, and they stay untyped. + pub extension_functions: &'a [String], +} + +/// One query's analysis, in the facts the analysis session keeps for a model. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct NativeAnalysis { + /// The output columns in order, each typed through the dialect's `normalize_type`. + pub columns: Vec, + /// One row per output column: how it is derived and the referenced columns it reads. + pub lineage: Vec, + /// `(relation, column)` of the columns that decide which rows the query returns. + pub row_sources: Vec<(String, String)>, + /// The outermost query level projects a star. + pub has_star: bool, + /// Every column a star projects is known. + pub star_resolved: bool, + /// The findings SQLBuild reports, in its codes and at character positions in `sql`. + pub diagnostics: Vec, +} + +/// One relation of known columns, standing in for what a probe query reads, such as a macro +/// body's arguments as the columns of `SELECT () AS b FROM `. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StandInSchema { + relation: String, + columns: Vec, +} + +impl StandInSchema { + /// `relation` with each `(column, type as the dialect spells it, nullable)` in order. + #[must_use] + pub fn new(relation: &str, columns: &[(&str, &str, bool)]) -> Self { + Self { + relation: relation.to_owned(), + columns: columns + .iter() + .map(|&(name, data_type, nullable)| SchemaColumn { + name: name.to_owned(), + quoted: false, + data_type: Some(data_type.to_owned()), + nullability: if nullable { + Nullability::Nullable + } else { + Nullability::NonNull + }, + }) + .collect(), + } + } +} + +impl LineageSchema for StandInSchema { + fn columns(&self, relation: &str) -> Option<&[SchemaColumn]> { + relation + .eq_ignore_ascii_case(&self.relation) + .then_some(self.columns.as_slice()) + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index 18baa18a2..93e195558 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -1,9 +1,47 @@ use crate::analysis_route::main::skipped_analysis::{skipped_analysis, unavailable_analysis}; -use crate::analysis_route::models::{LocatedGap, TypeMatch}; +use crate::analysis_route::models::{ + AnalysisInput, LocatedGap, NativeAnalysis, StandInSchema, TypeMatch, +}; use crate::analysis_route::tests::test_types::{ - NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, + CataloguedDialect, FindingTestCase, MacroBodyTestCase, NativeTypeMatchTestCase, + PivotlessDialect, ReadWholeTestCase, }; use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::models::LineageRow; + +/// `orders` with a non-null INTEGER id and a nullable VARCHAR status. +fn orders() -> StandInSchema { + StandInSchema::new( + "orders", + &[("id", "INTEGER", false), ("status", "VARCHAR", true)], + ) +} + +/// The analysis of `sql` over `orders`, which the model references as the model `shop.orders`. +fn analysed( + dialect: &dyn NativeDialect, + sql: &str, + extension_functions: &[&str], +) -> NativeAnalysis { + let schema: StandInSchema = orders(); + let references: [(String, String, String); 1] = [( + "orders".to_owned(), + "model".to_owned(), + "shop.orders".to_owned(), + )]; + let extension_functions: Vec = extension_functions + .iter() + .map(|name| (*name).to_owned()) + .collect(); + dialect + .analyse(&AnalysisInput { + sql, + schema: &schema, + references: &references, + extension_functions: &extension_functions, + }) + .expect("the test query is read whole") +} #[test] fn given_sql_when_reading_it_whole_then_only_one_parsed_statement_without_a_gap_is_read() { @@ -23,6 +61,11 @@ fn given_sql_when_reading_it_whole_then_only_one_parsed_statement_without_a_gap_ sql: " ", expected_gap: Some("no statement"), }, + ReadWholeTestCase { + description: "a comment alone holds no statement, not several", + sql: "-- nothing to read", + expected_gap: Some("no statement"), + }, ReadWholeTestCase { description: "a statement kept as raw text is not read", sql: "RAW VACUUM t", @@ -125,3 +168,194 @@ fn given_a_parser_message_or_no_parser_when_skipping_analysis_then_p026_says_why "no dialect is the generic dialect" ); } + +#[test] +fn given_findings_when_analysing_then_they_take_sqlbuild_codes_and_positions() { + let test_cases = [ + FindingTestCase { + description: "a VARCHAR column as a condition is an implicit conversion, blocked as B211", + dialect: &PivotlessDialect, + sql: "SELECT id FROM orders WHERE status", + extension_functions: &[], + expected_findings: &[("B211", Some((1, 29)))], + }, + FindingTestCase { + description: "a cast to a type the engine lacks is blocked as B218, at the value cast", + dialect: &PivotlessDialect, + sql: "SELECT CAST(id AS NOT_A_TYPE) AS x FROM orders", + extension_functions: &[], + expected_findings: &[("B218", Some((1, 13)))], + }, + FindingTestCase { + description: "an unknown column on its own line keeps its line and column", + dialect: &PivotlessDialect, + sql: "SELECT id,\n nope\nFROM orders", + extension_functions: &[], + expected_findings: &[("B002", Some((2, 3)))], + }, + FindingTestCase { + description: "a function the catalogue does not know is B101", + dialect: &CataloguedDialect, + sql: "SELECT CURRENT_TIMESTAMP() AS created_at FROM orders", + extension_functions: &[], + expected_findings: &[("B101", None)], + }, + FindingTestCase { + description: "a declared extension function is known, matched without case", + dialect: &CataloguedDialect, + sql: "SELECT CURRENT_TIMESTAMP() AS created_at FROM orders", + extension_functions: &["current_timestamp"], + expected_findings: &[], + }, + FindingTestCase { + description: "an undeclared function is still unknown beside declared extensions", + dialect: &CataloguedDialect, + sql: "SELECT CURRENT_TIMESTAMP() AS created_at FROM orders", + extension_functions: &["md_user_info"], + expected_findings: &[("B101", None)], + }, + ]; + for test_case in test_cases { + let findings: Vec<(String, Option, Option)> = analysed( + test_case.dialect, + test_case.sql, + test_case.extension_functions, + ) + .diagnostics + .into_iter() + .map(|row| (row.code, row.line, row.column)) + .collect(); + let expected: Vec<(String, Option, Option)> = test_case + .expected_findings + .iter() + .map(|&(code, position)| { + ( + code.to_owned(), + position.map(|(line, _)| line), + position.map(|(_, column)| column), + ) + }) + .collect(); + assert_eq!(findings, expected, "{}", test_case.description); + } +} + +#[test] +fn given_a_query_over_referenced_columns_when_analysing_then_lineage_reads_the_resources() { + let analysis: NativeAnalysis = analysed( + &PivotlessDialect, + "SELECT id, CAST(id AS TEXT) AS id_text, 1 AS one, nope FROM orders WHERE status = 'paid'", + &[], + ); + let source = |column: &str| { + ( + "model".to_owned(), + "shop.orders".to_owned(), + column.to_owned(), + ) + }; + assert_eq!( + analysis.lineage, + vec![ + LineageRow { + output_column: "id".to_owned(), + transform_code: 0, + confidence_code: 1, + sources: vec![source("id")], + }, + LineageRow { + output_column: "id_text".to_owned(), + transform_code: 1, + confidence_code: 1, + sources: vec![source("id")], + }, + LineageRow { + output_column: "one".to_owned(), + transform_code: 5, + confidence_code: 1, + sources: Vec::new(), + }, + LineageRow { + output_column: "nope".to_owned(), + transform_code: 0, + confidence_code: 2, + sources: vec![source("nope")], + }, + ], + "direct, cast and constant columns read the referenced model; an unresolved column is \ + read from the one relation at medium confidence" + ); + assert_eq!( + analysis.row_sources, + vec![("orders".to_owned(), "status".to_owned())], + "the filter's column decides rows" + ); + assert_eq!( + ( + analysis.columns[0].data_type.clone(), + analysis.columns[0].nullability.as_str() + ), + ( + PivotlessDialect + .normalize_type("INTEGER") + .map(|normalized| normalized.normalized_name), + "non_null" + ), + "a column is typed through the dialect and keeps the schema's nullability" + ); +} + +#[test] +fn given_a_star_when_analysing_then_it_is_resolved_only_over_known_columns() { + let known: NativeAnalysis = analysed(&PivotlessDialect, "SELECT * FROM orders", &[]); + assert_eq!( + (known.has_star, known.star_resolved, known.columns.len()), + (true, true, 2), + "a star over a known relation projects its columns" + ); + let unknown: NativeAnalysis = analysed(&PivotlessDialect, "SELECT * FROM elsewhere", &[]); + assert_eq!( + (unknown.has_star, unknown.star_resolved), + (true, false), + "a star over an unknown relation leaves columns out" + ); +} + +#[test] +fn given_a_macro_body_when_reading_it_then_only_one_expression_is_read() { + let test_cases = [ + MacroBodyTestCase { + description: "one expression is the body", + body: "a + 1", + expected_gap: None, + }, + MacroBodyTestCase { + description: "two expressions are not one body", + body: "a, b", + expected_gap: Some(("not one expression", 0)), + }, + MacroBodyTestCase { + description: "a query is not a body", + body: "1 FROM t", + expected_gap: Some(("not one expression", 0)), + }, + MacroBodyTestCase { + description: "a refused construct is placed in the body, not the query around it", + body: "x PIVOT", + expected_gap: Some(("PIVOT", 2)), + }, + ]; + for test_case in test_cases { + assert_eq!( + PivotlessDialect + .macro_body(test_case.body) + .err() + .map(|gap| (gap.construct, gap.offset)), + test_case + .expected_gap + .map(|(construct, offset)| (construct.to_owned(), offset)), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 69fd87107..7ba1c2226 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -1,5 +1,7 @@ +use sqlquack_semantics::dialects::constants::{DUCKDB, POSTGRES}; +use sqlquack_semantics::dialects::types::DialectProfile; use sqlquack_syntax::expressions::statements::models::Command; -use sqlquack_syntax::{Expression, Parsed}; +use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; use crate::analysis_route::models::{LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; @@ -70,11 +72,51 @@ impl NativeDialect for PivotlessDialect { }) } + fn profile(&self) -> &'static dyn DialectProfile { + &POSTGRES + } + fn normalize_type(&self, spelling: &str) -> Option { (!spelling.eq_ignore_ascii_case("NOT_A_TYPE")).then(|| normalize_text_type(spelling, None)) } } +/// DuckDB's shared parser under the DuckDB profile, which knows DuckDB's keyword-only functions, as +/// MotherDuck projects are analysed. +pub(super) struct CataloguedDialect; + +impl NativeDialect for CataloguedDialect { + fn name(&self) -> &'static str { + "duckdb" + } + + fn stub_prefix(&self) -> &'static str { + "__sqlbuild_" + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql, Dialect::DuckDB, &ParseOptions::default()) + .map(|statements| Parsed { + statements, + gaps: Vec::new(), + }) + .map_err(|failure| ParserGap { + construct: failure.to_string(), + message: failure.to_string(), + offset: 0, + syntax: false, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &DUCKDB + } + + fn normalize_type(&self, spelling: &str) -> Option { + PivotlessDialect.normalize_type(spelling) + } +} + pub(super) struct ReadWholeTestCase { pub(super) description: &'static str, pub(super) sql: &'static str, @@ -95,3 +137,19 @@ pub(super) struct TypeMatchTestCase { pub(super) inferred_nullable: bool, pub(super) expected_match: TypeMatch, } + +pub(super) struct FindingTestCase { + pub(super) description: &'static str, + pub(super) dialect: &'static dyn NativeDialect, + pub(super) sql: &'static str, + pub(super) extension_functions: &'static [&'static str], + /// The code of each finding in order, with its line and column when the parse placed it. + pub(super) expected_findings: &'static [(&'static str, Option<(usize, usize)>)], +} + +pub(super) struct MacroBodyTestCase { + pub(super) description: &'static str, + pub(super) body: &'static str, + /// `(construct, offset in the body)` of the gap, when the body is not read. + pub(super) expected_gap: Option<(&'static str, usize)>, +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/types.rs b/crates/sqlbuild-analysis/src/analysis_route/types.rs index b358e7727..35ecde38c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/types.rs @@ -1,9 +1,14 @@ //! What a dialect's native analysis module provides. +use sqlquack_semantics::dialects::types::DialectProfile; use sqlquack_syntax::{Expression, Parsed}; -use crate::analysis_route::constants::{MORE_THAN_ONE_STATEMENT, NO_STATEMENT, UNPARSED_STATEMENT}; -use crate::analysis_route::models::{ParserGap, TypeMatch}; +use crate::analysis_route::_helpers::native_analysis::native_analysis; +use crate::analysis_route::constants::{ + MACRO_BODY_PREFIX, MORE_THAN_ONE_STATEMENT, NO_STATEMENT, NOT_ONE_EXPRESSION, + UNPARSED_STATEMENT, +}; +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap, TypeMatch}; use crate::type_system::models::NormalizedType; /// A dialect whose SQL SQLQuack analyses in place of Polyglot. @@ -17,6 +22,9 @@ pub trait NativeDialect: Sync { /// Every statement of `sql` with the parser's named gaps, or the first error that stops it. fn parse(&self, sql: &str) -> Result; + /// The dialect's sqlquack-semantics profile: how its names resolve, compare and type. + fn profile(&self) -> &'static dyn DialectProfile; + /// A type as written in this dialect, as SQLBuild compares types; `None` when the dialect /// cannot spell it or its engine refuses it, and nothing is compared against it. fn normalize_type(&self, spelling: &str) -> Option; @@ -33,17 +41,39 @@ pub trait NativeDialect: Sync { }); } let mut statements = parsed.statements.into_iter(); - let (Some(statement), None) = (statements.next(), statements.next()) else { - let construct: &str = if sql.trim().is_empty() { - NO_STATEMENT - } else { - MORE_THAN_ONE_STATEMENT - }; - return Err(whole_gap(construct, 0)); + match (statements.next(), statements.next()) { + (None, _) => Err(whole_gap(NO_STATEMENT, 0)), + (Some(_), Some(_)) => Err(whole_gap(MORE_THAN_ONE_STATEMENT, 0)), + (Some(Expression::Command(_)), None) => Err(whole_gap(UNPARSED_STATEMENT, 0)), + (Some(statement), None) => Ok(statement), + } + } + + /// The outputs, lineage and findings of the query `input.sql`, read whole, against + /// `input.schema`; findings are in SQLBuild's codes, as the Polyglot path reports them. + fn analyse(&self, input: &AnalysisInput<'_>) -> Result { + let statement: Expression = self.read_whole(input.sql)?; + Ok(native_analysis(self, &statement, input)) + } + + /// A macro body's one expression, read as the projection of `SELECT `; a gap's offset + /// is in `body`. + fn macro_body(&self, body: &str) -> Result { + let statement: Expression = self + .read_whole(&format!("{MACRO_BODY_PREFIX}{body}")) + .map_err(|gap| ParserGap { + offset: gap.offset.saturating_sub(MACRO_BODY_PREFIX.len()), + ..gap + })?; + let Expression::Select(select) = statement else { + return Err(whole_gap(NOT_ONE_EXPRESSION, 0)); }; - match statement { - Expression::Command(_) => Err(whole_gap(UNPARSED_STATEMENT, 0)), - statement => Ok(statement), + if select.from.is_some() { + return Err(whole_gap(NOT_ONE_EXPRESSION, 0)); + } + match <[Expression; 1]>::try_from(select.expressions) { + Ok([expression]) => Ok(expression), + Err(_) => Err(whole_gap(NOT_ONE_EXPRESSION, 0)), } } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs index b198ae8a5..4440d80a4 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs @@ -177,7 +177,7 @@ pub(crate) fn map_diagnostics( Ok(ValidationResult { valid, errors }) } -fn sqlbuild_code(code: &str) -> Option { +pub(crate) fn sqlbuild_code(code: &str) -> Option { let direct = match code { "E001" | "E002" | "E003" | "E004" | "E200" => Some("B000"), "E201" => Some("B002"), diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs index ae7f19263..0f271d481 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs @@ -7,3 +7,10 @@ pub fn binding_diagnostics( ) -> Result, String> { crate::semantic_validation::_helpers::diagnostics::binding_diagnostics(sql, dialect, rows) } + +/// SQLBuild's code for an analysis finding: E codes directly, implicit-conversion W codes through +/// their blocking B codes; `None` for a code SQLBuild does not report. +#[must_use] +pub fn sqlbuild_code(code: &str) -> Option { + crate::semantic_validation::_helpers::diagnostics::sqlbuild_code(code) +} From eed3343a74d4670bafa8c902cc94d3d3abe58824 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:36:48 +0000 Subject: [PATCH 004/160] fix(types): take a fallback type's family from its top-level type The text fallback gave family Timestamp to any type whose text held TIMESTAMP, so an ARRAY, MAP or STRUCT holding a timestamp read as a timestamp where Polyglot reads it as Other. The family now comes from the leading type name, so TIMESTAMP(3), TIMESTAMP WITH TIME ZONE and TIMESTAMP_NTZ stay timestamps and complex types do not. --- .../src/type_system/_helpers/fallback.rs | 7 ++- .../type_system/tests/test_normalize_type.rs | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs index 6ee0ba4bf..83970ed72 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs @@ -97,7 +97,12 @@ pub(crate) fn normalize_with_fallback( let name: &str = if bigquery { "BOOL" } else { base_type }; return simple(name, TypeFamily::Boolean); } - if base_type.contains(TIMESTAMP_TYPE_TOKEN) { + // The top-level type decides the family, so a MAP or STRUCT holding a timestamp is not one. + let top_level: &str = base_type + .split(|character: char| !(character.is_ascii_alphanumeric() || character == '_')) + .next() + .unwrap_or(base_type); + if top_level.contains(TIMESTAMP_TYPE_TOKEN) { return simple( ×tamp_normalized_name(base_type, dialect), TypeFamily::Timestamp, diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs index afee3fc47..ba70a0280 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs @@ -1,5 +1,7 @@ use crate::type_system::errors::TypeNormalizationError; +use crate::type_system::main::normalize_text_type::normalize_text_type; use crate::type_system::main::normalize_type::normalize_type; +use crate::type_system::models::TypeFamily; use crate::type_system::tests::helpers::normalization_text; use crate::type_system::tests::test_types::{BracketDepthTestCase, NormalizeTypeTestCase}; @@ -179,3 +181,51 @@ fn given_array_suffixes_when_normalizing_on_a_worker_stack_then_python_supported ); } } + +#[test] +fn given_complex_types_holding_a_timestamp_when_normalizing_text_then_the_top_level_type_decides() { + let test_cases = [ + ( + "an array of timestamps is an array", + "ARRAY", + TypeFamily::Other, + ), + ( + "a map of structs holding a timestamp is a map", + "MAP>", + TypeFamily::Other, + ), + ( + "a struct holding a timestamp is a struct", + "STRUCT", + TypeFamily::Other, + ), + ( + "a bare timestamp is a timestamp", + "TIMESTAMP", + TypeFamily::Timestamp, + ), + ( + "a timestamp with precision is a timestamp", + "TIMESTAMP(3)", + TypeFamily::Timestamp, + ), + ( + "a zoned timestamp is a timestamp", + "TIMESTAMP WITH TIME ZONE", + TypeFamily::Timestamp, + ), + ( + "Snowflake's NTZ alias is a timestamp", + "TIMESTAMP_NTZ", + TypeFamily::Timestamp, + ), + ]; + for (description, type_sql, expected_family) in test_cases { + assert_eq!( + normalize_text_type(type_sql, None).family, + expected_family, + "{description}" + ); + } +} From 73af3475df2956002993a71ca59ed95b99acc0b7 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:41:45 +0000 Subject: [PATCH 005/160] fix(analysis): compare ClickHouse nullability without a type, and keep stubs in code type_match now takes the inferred type as optional, and NotCompared keeps whether the declared type alone forbids null. A ClickHouse column declared without Nullable that receives a nullable value of unknown type is flagged again, while no type finding is made. ClickHouse types are compared without whitespace outside quoted strings, so Decimal(10, 2) is Decimal(10,2). Respelling cursor stubs for a native parser now leaves text inside strings and comments, and longer names that end in a stub's text, as written. --- .../main/clickhouse_type_match.rs | 32 +++++++++- .../src/analysis_route/models.rs | 8 ++- .../tests/test_clickhouse_type_match.rs | 61 ++++++++++++++++--- .../tests/test_native_dialect.rs | 24 ++++++-- .../src/analysis_route/tests/test_types.rs | 4 +- .../src/analysis_route/types.rs | 21 +++++-- .../src/contracts/_helpers/type_comparison.rs | 20 +++--- .../_helpers/normalization.rs | 55 ++++++++++++++--- .../tests/test_normalize_batch.rs | 45 +++++++++++++- .../semantic_validation/tests/test_types.rs | 6 ++ 10 files changed, 232 insertions(+), 44 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs index a5ee54f86..b380ebf65 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs @@ -1,17 +1,43 @@ use crate::analysis_route::constants::{LOW_CARDINALITY_WRAPPER, NULLABLE_WRAPPER}; use crate::analysis_route::models::TypeMatch; -/// A declared and an inferred ClickHouse type compared without top-level wrappers, nullability apart. +/// A declared and an inferred ClickHouse type compared without top-level wrappers or spacing, +/// nullability apart. A declared type without `Nullable` forbids null even when the inferred type +/// is unknown. An arm compares spellings it has respelt through its aliases. #[must_use] -pub fn clickhouse_type_match(declared: &str, inferred: &str, inferred_nullable: bool) -> TypeMatch { +pub fn clickhouse_type_match( + declared: &str, + inferred: Option<&str>, + inferred_nullable: bool, +) -> TypeMatch { let (declared_base, declared_nullable) = unwrapped(declared); + let Some(inferred) = inferred else { + return TypeMatch::NotCompared { + nullable_into_non_null: !declared_nullable && inferred_nullable, + }; + }; let (inferred_base, wrapped_nullable) = unwrapped(inferred); TypeMatch::Compared { - same_type: declared_base == inferred_base, + same_type: unspaced(declared_base) == unspaced(inferred_base), nullable_into_non_null: !declared_nullable && (inferred_nullable || wrapped_nullable), } } +/// `type_sql` without whitespace outside its quoted strings, so `Decimal(10, 2)` is `Decimal(10,2)` +/// and an `Enum8('a b' = 1)` value keeps its space. +fn unspaced(type_sql: &str) -> String { + let mut quoted: bool = false; + type_sql + .chars() + .filter(|character| { + if *character == '\'' { + quoted = !quoted; + } + quoted || !character.is_whitespace() + }) + .collect() +} + /// The type inside its top-level `Nullable` and `LowCardinality` wrappers, and whether it is nullable. fn unwrapped(type_sql: &str) -> (&str, bool) { let mut current: &str = type_sql.trim(); diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 0c487b2f0..1a9016953 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -43,8 +43,12 @@ pub struct LocatedGap { /// How an inferred column type meets its declared type: the type itself, then its nullability. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TypeMatch { - /// One side is a type the dialect cannot spell or refuses: no finding and no match. - NotCompared, + /// One side is a type the dialect cannot spell, refuses or does not know: no type finding and + /// no match, though the declared type alone may still forbid null. + NotCompared { + /// The inferred column may be null, but its declared type does not allow it. + nullable_into_non_null: bool, + }, Compared { same_type: bool, /// The inferred column may be null, but its declared type does not allow it. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs index 4e55839f6..c6cab99ac 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs @@ -9,7 +9,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "a nullable String flag matches DESCRIBE's Nullable(String)", declared: "Nullable(String)", - inferred: "String", + inferred: Some("String"), inferred_nullable: true, expected_match: TypeMatch::Compared { same_type: true, @@ -19,7 +19,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "a declared LowCardinality(String) is a String", declared: "LowCardinality(String)", - inferred: "String", + inferred: Some("String"), inferred_nullable: false, expected_match: TypeMatch::Compared { same_type: true, @@ -29,7 +29,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "LowCardinality around Nullable allows null", declared: "LowCardinality(Nullable(String))", - inferred: "Nullable(String)", + inferred: Some("Nullable(String)"), inferred_nullable: false, expected_match: TypeMatch::Compared { same_type: true, @@ -39,7 +39,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "a nullable inference into a plain String declaration is a nullability conflict", declared: "String", - inferred: "String", + inferred: Some("String"), inferred_nullable: true, expected_match: TypeMatch::Compared { same_type: true, @@ -49,7 +49,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "a non-null inference into a Nullable declaration is no conflict", declared: "Nullable(Int64)", - inferred: "Int64", + inferred: Some("Int64"), inferred_nullable: false, expected_match: TypeMatch::Compared { same_type: true, @@ -59,7 +59,7 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "different base types differ whatever their wrappers", declared: "Nullable(Int32)", - inferred: "Int64", + inferred: Some("Int64"), inferred_nullable: true, expected_match: TypeMatch::Compared { same_type: false, @@ -69,13 +69,60 @@ fn given_clickhouse_column_types_when_matching_then_wrappers_are_stripped_and_nu TypeMatchTestCase { description: "a Nullable nested inside Array is part of the type, not stripped", declared: "Array(Nullable(String))", - inferred: "Array(String)", + inferred: Some("Array(String)"), inferred_nullable: false, expected_match: TypeMatch::Compared { same_type: false, nullable_into_non_null: false, }, }, + TypeMatchTestCase { + description: "spacing inside a type's arguments is not a difference", + declared: "Decimal(10, 2)", + inferred: Some("Decimal(10,2)"), + inferred_nullable: false, + expected_match: TypeMatch::Compared { + same_type: true, + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "spacing inside a quoted enum value is part of the type", + declared: "Enum8('a b' = 1)", + inferred: Some("Enum8('ab' = 1)"), + inferred_nullable: false, + expected_match: TypeMatch::Compared { + same_type: false, + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a nullable value of unknown type into a plain String is a nullability conflict", + declared: "String", + inferred: None, + inferred_nullable: true, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: true, + }, + }, + TypeMatchTestCase { + description: "a nullable value of unknown type into a Nullable column is no conflict", + declared: "Nullable(String)", + inferred: None, + inferred_nullable: true, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a non-null value of unknown type is no conflict", + declared: "String", + inferred: None, + inferred_nullable: false, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, ]; for test_case in test_cases { assert_eq!( diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index 93e195558..1e9b9c7ae 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -96,19 +96,31 @@ fn given_a_type_the_dialect_refuses_when_matching_types_then_nothing_is_compared NativeTypeMatchTestCase { description: "a refused declared type is not compared", declared: "NOT_A_TYPE", - inferred: "INTEGER", - expected_match: TypeMatch::NotCompared, + inferred: Some("INTEGER"), + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, }, NativeTypeMatchTestCase { description: "a refused inferred type is not compared", declared: "INTEGER", - inferred: "NOT_A_TYPE", - expected_match: TypeMatch::NotCompared, + inferred: Some("NOT_A_TYPE"), + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + NativeTypeMatchTestCase { + description: "an unknown inferred type is not compared", + declared: "INTEGER", + inferred: None, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, }, NativeTypeMatchTestCase { description: "two known, different types still mismatch", declared: "INTEGER", - inferred: "TEXT", + inferred: Some("TEXT"), expected_match: TypeMatch::Compared { same_type: false, nullable_into_non_null: false, @@ -117,7 +129,7 @@ fn given_a_type_the_dialect_refuses_when_matching_types_then_nothing_is_compared NativeTypeMatchTestCase { description: "the same type matches", declared: "INTEGER", - inferred: "integer", + inferred: Some("integer"), expected_match: TypeMatch::Compared { same_type: true, nullable_into_non_null: false, diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 7ba1c2226..545760014 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -126,14 +126,14 @@ pub(super) struct ReadWholeTestCase { pub(super) struct NativeTypeMatchTestCase { pub(super) description: &'static str, pub(super) declared: &'static str, - pub(super) inferred: &'static str, + pub(super) inferred: Option<&'static str>, pub(super) expected_match: TypeMatch, } pub(super) struct TypeMatchTestCase { pub(super) description: &'static str, pub(super) declared: &'static str, - pub(super) inferred: &'static str, + pub(super) inferred: Option<&'static str>, pub(super) inferred_nullable: bool, pub(super) expected_match: TypeMatch, } diff --git a/crates/sqlbuild-analysis/src/analysis_route/types.rs b/crates/sqlbuild-analysis/src/analysis_route/types.rs index 35ecde38c..2860e079f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/types.rs @@ -77,16 +77,27 @@ pub trait NativeDialect: Sync { } } - /// Whether a declared and an inferred type match, nullability apart; `NotCompared` when - /// `normalize_type` gives `None` for either. An override must keep that rule. - fn type_match(&self, declared: &str, inferred: &str, inferred_nullable: bool) -> TypeMatch { + /// Whether a declared and an inferred type match, nullability apart; `NotCompared` when the + /// inferred type is unknown or `normalize_type` gives `None` for either side. An override must + /// keep that rule, and may still say whether the declared type alone forbids null. + fn type_match( + &self, + declared: &str, + inferred: Option<&str>, + inferred_nullable: bool, + ) -> TypeMatch { let _ = inferred_nullable; - match (self.normalize_type(declared), self.normalize_type(inferred)) { + match ( + self.normalize_type(declared), + inferred.and_then(|inferred| self.normalize_type(inferred)), + ) { (Some(declared), Some(inferred)) => TypeMatch::Compared { same_type: declared == inferred, nullable_into_non_null: false, }, - _ => TypeMatch::NotCompared, + _ => TypeMatch::NotCompared { + nullable_into_non_null: false, + }, } } } diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs index 5b3513bcb..fba3ccc4a 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs @@ -52,8 +52,8 @@ impl<'a> TypeComparer<'a> { AnalysisRoute::Legacy => {} AnalysisRoute::Unavailable => return Ok(TypeComparison::NotCompared), AnalysisRoute::Native(dialect) => { - return Ok(match dialect.type_match(left, right, false) { - TypeMatch::NotCompared => TypeComparison::NotCompared, + return Ok(match dialect.type_match(left, Some(right), false) { + TypeMatch::NotCompared { .. } => TypeComparison::NotCompared, TypeMatch::Compared { same_type: true, .. } => TypeComparison::Equal, @@ -79,18 +79,22 @@ impl<'a> TypeComparer<'a> { if declared.not_null && inferred.nullable { return true; } - let (AnalysisRoute::Native(dialect), Some(declared_type), Some(inferred_type)) = ( - self.route, - declared.declared_type.as_deref(), - inferred.inferred_type.as_deref(), - ) else { + let (AnalysisRoute::Native(dialect), Some(declared_type)) = + (self.route, declared.declared_type.as_deref()) + else { return false; }; matches!( - dialect.type_match(declared_type, inferred_type, inferred.nullable), + dialect.type_match( + declared_type, + inferred.inferred_type.as_deref(), + inferred.nullable + ), TypeMatch::Compared { nullable_into_non_null: true, .. + } | TypeMatch::NotCompared { + nullable_into_non_null: true } ) } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs index daaeed17d..cea988305 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs @@ -194,17 +194,52 @@ pub(crate) fn normalize_native_sql( let udf_prefix: String = format!("{stub_prefix}udf_"); let normalized: String = normalize(sql, dialect, (&none, &none), &udf_prefix)?.sql; let stubs: &Regex = NATIVE_STUBS.as_ref()?; - Ok(stubs - .replace_all(&normalized, |captures: ®ex::Captures<'_>| { - match (captures.get(1), captures.get(2)) { - (Some(bound), _) => format!("{stub_prefix}cursor_{}()", bound.as_str()), - (None, Some(parameter)) => { - format!("'{HOOK_PARAMETER_PREFIX}{}'", parameter.as_str()) - } - (None, None) => captures[0].to_owned(), + let non_code: Vec<(usize, usize)> = non_code_ranges(&normalized)?; + let mut native: String = String::with_capacity(normalized.len()); + let mut copied_until: usize = 0; + for captures in stubs.captures_iter(&normalized) { + let whole = captures.get(0).ok_or("missing stub match")?; + let quoted: bool = non_code + .iter() + .any(|&(start, end)| start <= whole.start() && whole.start() < end); + // A cursor stub is a whole name: `x__cursor_start()` is another function. + let glued: bool = captures.get(1).is_some() + && normalized[..whole.start()] + .chars() + .next_back() + .is_some_and(|character| character.is_alphanumeric() || character == '_'); + if quoted || glued { + continue; + } + native.push_str(&normalized[copied_until..whole.start()]); + match (captures.get(1), captures.get(2)) { + (Some(bound), _) => { + native.push_str(&format!("{stub_prefix}cursor_{}()", bound.as_str())); + } + (None, Some(parameter)) => { + native.push_str(&format!("'{HOOK_PARAMETER_PREFIX}{}'", parameter.as_str())); } - }) - .into_owned()) + (None, None) => native.push_str(whole.as_str()), + } + copied_until = whole.end(); + } + native.push_str(&normalized[copied_until..]); + Ok(native) +} + +/// The byte ranges of `sql`'s strings, quoted identifiers and comments, in order. +fn non_code_ranges(sql: &str) -> Result, String> { + let mut ranges: Vec<(usize, usize)> = Vec::new(); + let mut index: usize = 0; + while index < sql.len() { + if let Some(end) = skip_non_code(sql, index)? { + ranges.push((index, end)); + index = end; + continue; + } + index += sql[index..].chars().next().map_or(1, char::len_utf8); + } + Ok(ranges) } pub(crate) fn normalize_dialect_sql_mapped( diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs index 112492fef..bbc5a4ea3 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; use crate::semantic_validation::main::normalize_batch::normalize_analysis_sqls; -use crate::semantic_validation::tests::test_types::NormalizationBatchTestCase; +use crate::semantic_validation::main::normalize_native::normalize_native_sql; +use crate::semantic_validation::tests::test_types::{ + NativeStubTestCase, NormalizationBatchTestCase, +}; use crate::semantic_validation::types::NormalizationRequest; #[test] @@ -100,3 +103,43 @@ fn given_batch_when_normalizing_on_pool_or_in_turn_then_matches_single_calls_in_ ); } } + +#[test] +fn given_cursor_stubs_when_normalizing_for_a_native_parser_then_only_whole_names_in_code_are_respelt() + { + let test_cases = [ + NativeStubTestCase { + description: "a cursor stub in code takes the dialect's prefix", + sql: "SELECT * FROM t WHERE ts > __cursor_start()", + expected_sql: "SELECT * FROM t WHERE ts > __sqlbuild.cursor_start()", + }, + NativeStubTestCase { + description: "a cursor stub's text inside a string literal is left as written", + sql: "SELECT '__cursor_start()' AS s FROM t", + expected_sql: "SELECT '__cursor_start()' AS s FROM t", + }, + NativeStubTestCase { + description: "a cursor stub's text inside a comment is left as written", + sql: "SELECT 1 FROM t -- __cursor_end()\n", + expected_sql: "SELECT 1 FROM t -- __cursor_end()\n", + }, + NativeStubTestCase { + description: "a longer name ending in a cursor stub's text is another function", + sql: "SELECT x__cursor_end() FROM t", + expected_sql: "SELECT x__cursor_end() FROM t", + }, + NativeStubTestCase { + description: "a quoted stub beside a stub in code leaves only the code one respelt", + sql: "SELECT '__cursor_end()', __cursor_end() FROM t", + expected_sql: "SELECT '__cursor_end()', __sqlbuild.cursor_end() FROM t", + }, + ]; + for test_case in test_cases { + assert_eq!( + normalize_native_sql(test_case.sql, "postgres", "__sqlbuild.").as_deref(), + Ok(test_case.expected_sql), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs index 201a5dc6f..27fa2325e 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs @@ -53,6 +53,12 @@ pub(crate) struct NormalizationBatchTestCase { pub(crate) expected_failures: &'static [usize], } +pub(crate) struct NativeStubTestCase { + pub(crate) description: &'static str, + pub(crate) sql: &'static str, + pub(crate) expected_sql: &'static str, +} + pub(crate) struct TruthTestScanTestCase { pub(crate) description: &'static str, pub(crate) sql: &'static str, From 6e514a2d46f1820b89b8148e4e5aefced5fd5fb3 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:43:08 +0000 Subject: [PATCH 006/160] fix(analysis): skip the Polyglot pivot proof for natively analysed dialects The dynamic pivot proof reads Polyglot's parse of the model. A dialect on a native or unavailable analysis route now gets a proof failure naming why, instead of a Polyglot parse. Every dialect is still on the Polyglot route, so no proof changes yet. --- .../assembly/analysis_session/_helpers/dynamic_pivot.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index e49ac7c85..455457cc6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -8,6 +8,8 @@ use serde_json::{Map, Value}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; +use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::models::AnalysisRoute; use crate::assembly::analysis_session::_helpers::dict_walk::{ casefold, column_name, dict_list, identifier_name, is_single_wildcard, nested, node_key, parse_one_or_error, payload, relation_name, render_type, to_dict, truthy, upper, @@ -101,6 +103,13 @@ pub(crate) fn pivot_outcome( facts.dialect ))); } + // The proof reads Polyglot's tree; a dialect a native module analyses is never parsed by it. + if !matches!(analysis_route(Some(facts.dialect)), AnalysisRoute::Legacy) { + return Ok(failure(format!( + "dynamic pivot proofs do not read the {} native parser's tree", + facts.dialect + ))); + } let parse_dialect: &str = if folded_dialect == MOTHERDUCK_DIALECT { DUCKDB_DIALECT } else { From b2d0feeefdf7d489d6a9718267d694fcd3c0a8d3 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:44:02 +0000 Subject: [PATCH 007/160] Revert "fix(analysis): skip the Polyglot pivot proof for natively analysed dialects" The pivot proof moves onto SQLQuack's parser for every dialect in its own change, so no route needs to skip it. --- .../assembly/analysis_session/_helpers/dynamic_pivot.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index 455457cc6..e49ac7c85 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -8,8 +8,6 @@ use serde_json::{Map, Value}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; -use crate::analysis_route::main::analysis_route::analysis_route; -use crate::analysis_route::models::AnalysisRoute; use crate::assembly::analysis_session::_helpers::dict_walk::{ casefold, column_name, dict_list, identifier_name, is_single_wildcard, nested, node_key, parse_one_or_error, payload, relation_name, render_type, to_dict, truthy, upper, @@ -103,13 +101,6 @@ pub(crate) fn pivot_outcome( facts.dialect ))); } - // The proof reads Polyglot's tree; a dialect a native module analyses is never parsed by it. - if !matches!(analysis_route(Some(facts.dialect)), AnalysisRoute::Legacy) { - return Ok(failure(format!( - "dynamic pivot proofs do not read the {} native parser's tree", - facts.dialect - ))); - } let parse_dialect: &str = if folded_dialect == MOTHERDUCK_DIALECT { DUCKDB_DIALECT } else { From 583e3ea7f79f4822d3e0a91e3c9d4da5de4fcea4 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 08:10:26 +0000 Subject: [PATCH 008/160] refactor(analysis): shape the native analysis route as fensu's Rust rules require Each analysis_route and semantic_validation entry module now exposes one function, with ClickHouse's spelling helpers and the read_whole gap in _helpers. Doc comments are one line, the route's tests run every case from a named test case table, and report_skip takes the P026 code and message together. The analysis_route module no longer re-exports its entries. normalize_text_type and clickhouse_type_match keep an exception for having no importer yet: their native dialect modules land in their own changes. --- .../_helpers/clickhouse_types.rs | 53 +++ .../src/analysis_route/_helpers/mod.rs | 2 + .../_helpers/native_analysis.rs | 43 +-- .../src/analysis_route/_helpers/whole_gap.rs | 11 + .../main/clickhouse_type_match.rs | 59 +--- .../src/analysis_route/main/mod.rs | 1 + .../analysis_route/main/skipped_analysis.rs | 17 +- .../main/unavailable_analysis.rs | 13 + .../src/analysis_route/mod.rs | 6 - .../src/analysis_route/models.rs | 9 +- .../src/analysis_route/tests.rs | 2 + .../src/analysis_route/tests/helpers.rs | 53 +++ .../tests/test_native_dialect.rs | 313 +++++++++--------- .../analysis_route/tests/test_native_gap.rs | 46 ++- .../src/analysis_route/tests/test_types.rs | 36 +- .../src/analysis_route/types.rs | 24 +- .../_helpers/normalization.rs | 1 - .../src/semantic_validation/main.rs | 1 + .../semantic_validation/main/diagnostics.rs | 7 - .../semantic_validation/main/sqlbuild_code.rs | 5 + .../src/type_system/_helpers/fallback.rs | 1 - .../type_system/tests/test_normalize_type.rs | 83 ++--- .../src/type_system/tests/test_types.rs | 6 + .../input_loops/model_loop_opt_out.rs | 24 +- fensu.toml | 10 + 25 files changed, 466 insertions(+), 360 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/whole_gap.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/unavailable_analysis.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs create mode 100644 crates/sqlbuild-analysis/src/semantic_validation/main/sqlbuild_code.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs new file mode 100644 index 000000000..9018bfc67 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs @@ -0,0 +1,53 @@ +use crate::analysis_route::constants::{LOW_CARDINALITY_WRAPPER, NULLABLE_WRAPPER}; + +/// `type_sql` without whitespace outside its quoted strings. +pub(crate) fn unspaced(type_sql: &str) -> String { + let mut quoted: bool = false; + type_sql + .chars() + .filter(|character| { + if *character == '\'' { + quoted = !quoted; + } + quoted || !character.is_whitespace() + }) + .collect() +} + +/// The type inside its top-level `Nullable` and `LowCardinality` wrappers, and whether it is nullable. +pub(crate) fn unwrapped(type_sql: &str) -> (&str, bool) { + let mut current: &str = type_sql.trim(); + let mut nullable: bool = false; + loop { + if let Some(inner) = wrapped(current, NULLABLE_WRAPPER) { + nullable = true; + current = inner; + } else if let Some(inner) = wrapped(current, LOW_CARDINALITY_WRAPPER) { + current = inner; + } else { + return (current, nullable); + } + } +} + +/// The argument of `wrapper(...)` when the whole of `type_sql` is that one call. +fn wrapped<'a>(type_sql: &'a str, wrapper: &str) -> Option<&'a str> { + let head: &str = type_sql.get(..wrapper.len())?; + if !head.eq_ignore_ascii_case(wrapper) { + return None; + } + let inner: &str = type_sql[wrapper.len()..] + .trim_start() + .strip_prefix('(')? + .strip_suffix(')')?; + let mut depth: usize = 0; + for character in inner.chars() { + match character { + '(' => depth += 1, + ')' if depth == 0 => return None, + ')' => depth -= 1, + _ => {} + } + } + Some(inner.trim()) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 801bee731..715122942 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1 +1,3 @@ +pub(crate) mod clickhouse_types; pub(crate) mod native_analysis; +pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs index e03c775d9..ffe1bca27 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs @@ -1,7 +1,8 @@ //! The provided native analysis: sqlquack-semantics lineage and findings as SQLBuild's facts. use sqlquack_semantics::lineage::models::{ - LineageContext, NameFinding, Nullability, OutputLineage, QueryLineage, TransformKind, + LineageContext, NameFinding, Nullability, OutputLineage, QueryLineage, SourceColumn, + TransformKind, }; use sqlquack_semantics::{statement_lineage, statement_name_findings}; use sqlquack_syntax::Expression; @@ -15,7 +16,7 @@ use crate::assembly::analysis_session::constants::{ TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, UNKNOWN_NULLABILITY, }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; -use crate::semantic_validation::main::diagnostics::sqlbuild_code; +use crate::semantic_validation::main::sqlbuild_code::sqlbuild_code; use crate::semantic_validation::models::DiagnosticRow; /// The analysis of `statement`, the one statement of `input.sql`, under `dialect`'s profile. @@ -73,9 +74,7 @@ fn column_fact(dialect: &D, output: &OutputLineage) - } } -/// An output column's lineage over the model's referenced resources, as the legacy rows give it: -/// columns of relations the model does not reference are left out, and a column reading none of -/// its references is of unknown confidence unless it is a constant. +/// An output column's lineage over the model's referenced resources, as the legacy rows give it. fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) -> LineageRow { let transform_code: u8 = match output.kind { TransformKind::Constant => TRANSFORM_CONSTANT, @@ -87,18 +86,7 @@ fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) let sources: Vec<(String, String, String)> = output .sources .iter() - .filter_map(|source| { - references - .iter() - .find(|(analysis_name, _, _)| *analysis_name == source.relation) - .map(|(_, resource_type, resource_name)| { - ( - resource_type.clone(), - resource_name.clone(), - source.column.clone(), - ) - }) - }) + .filter_map(|source| referenced_source(source, references)) .collect(); let confidence_code: u8 = if sources.is_empty() && transform_code != TRANSFORM_CONSTANT { CONFIDENCE_UNKNOWN @@ -115,8 +103,22 @@ fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) } } -/// A finding in SQLBuild's code at its position in `input.sql`; `None` for a code SQLBuild does -/// not report, and for an unknown function the dialect declares as an extension. +/// `(resource type, resource name, column)` of a source column the model references. +fn referenced_source( + source: &SourceColumn, + references: &[(String, String, String)], +) -> Option<(String, String, String)> { + let (_, resource_type, resource_name) = references + .iter() + .find(|(analysis_name, _, _)| *analysis_name == source.relation)?; + Some(( + resource_type.clone(), + resource_name.clone(), + source.column.clone(), + )) +} + +/// A finding in SQLBuild's code at its position in `input.sql`, unless SQLBuild does not report it. fn diagnostic_row(input: &AnalysisInput<'_>, finding: NameFinding) -> Option { if finding.code == UNKNOWN_FUNCTION_CODE && input @@ -145,8 +147,7 @@ fn diagnostic_row(input: &AnalysisInput<'_>, finding: NameFinding) -> Option &str { if finding.written.is_empty() { finding.message.split('\'').nth(1).unwrap_or_default() diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/whole_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/whole_gap.rs new file mode 100644 index 000000000..7b4fda1d4 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/whole_gap.rs @@ -0,0 +1,11 @@ +use crate::analysis_route::models::ParserGap; + +/// A gap `read_whole` names itself, which no parser reports as invalid syntax. +pub(crate) fn whole_gap(construct: &str, offset: usize) -> ParserGap { + ParserGap { + construct: construct.to_owned(), + message: construct.to_owned(), + offset, + syntax: false, + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs index b380ebf65..2a497aa24 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs @@ -1,9 +1,7 @@ -use crate::analysis_route::constants::{LOW_CARDINALITY_WRAPPER, NULLABLE_WRAPPER}; +use crate::analysis_route::_helpers::clickhouse_types::{unspaced, unwrapped}; use crate::analysis_route::models::TypeMatch; -/// A declared and an inferred ClickHouse type compared without top-level wrappers or spacing, -/// nullability apart. A declared type without `Nullable` forbids null even when the inferred type -/// is unknown. An arm compares spellings it has respelt through its aliases. +/// A declared and an inferred ClickHouse type compared without top-level wrappers or spacing. #[must_use] pub fn clickhouse_type_match( declared: &str, @@ -22,56 +20,3 @@ pub fn clickhouse_type_match( nullable_into_non_null: !declared_nullable && (inferred_nullable || wrapped_nullable), } } - -/// `type_sql` without whitespace outside its quoted strings, so `Decimal(10, 2)` is `Decimal(10,2)` -/// and an `Enum8('a b' = 1)` value keeps its space. -fn unspaced(type_sql: &str) -> String { - let mut quoted: bool = false; - type_sql - .chars() - .filter(|character| { - if *character == '\'' { - quoted = !quoted; - } - quoted || !character.is_whitespace() - }) - .collect() -} - -/// The type inside its top-level `Nullable` and `LowCardinality` wrappers, and whether it is nullable. -fn unwrapped(type_sql: &str) -> (&str, bool) { - let mut current: &str = type_sql.trim(); - let mut nullable: bool = false; - loop { - if let Some(inner) = wrapped(current, NULLABLE_WRAPPER) { - nullable = true; - current = inner; - } else if let Some(inner) = wrapped(current, LOW_CARDINALITY_WRAPPER) { - current = inner; - } else { - return (current, nullable); - } - } -} - -/// The argument of `wrapper(...)` when the whole of `type_sql` is that one call. -fn wrapped<'a>(type_sql: &'a str, wrapper: &str) -> Option<&'a str> { - let head: &str = type_sql.get(..wrapper.len())?; - if !head.eq_ignore_ascii_case(wrapper) { - return None; - } - let inner: &str = type_sql[wrapper.len()..] - .trim_start() - .strip_prefix('(')? - .strip_suffix(')')?; - let mut depth: usize = 0; - for character in inner.chars() { - match character { - '(' => depth += 1, - ')' if depth == 0 => return None, - ')' => depth -= 1, - _ => {} - } - } - Some(inner.trim()) -} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs index 87c5c0cfd..4991f9563 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs @@ -2,3 +2,4 @@ pub mod analysis_route; pub mod clickhouse_type_match; pub mod native_gap; pub mod skipped_analysis; +pub mod unavailable_analysis; diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs index eacef989f..6e0d6abaa 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs @@ -1,8 +1,7 @@ -use crate::analysis_route::constants::{GENERIC_DIALECT, SKIPPED_ANALYSIS_CODE}; +use crate::analysis_route::constants::SKIPPED_ANALYSIS_CODE; use crate::analysis_route::models::LocatedGap; -/// P026's code and message for a model whose SQL the dialect's native parser cannot read, with -/// the parser's own message when it says more than the construct's name. +/// P026's code and message for a model whose SQL the dialect's native parser cannot read. #[must_use] pub fn skipped_analysis(model: &str, dialect: &str, gap: &LocatedGap) -> (&'static str, String) { let detail: String = if gap.message.is_empty() || gap.message == gap.construct { @@ -19,15 +18,3 @@ pub fn skipped_analysis(model: &str, dialect: &str, gap: &LocatedGap) -> (&'stat ), ) } - -/// P026's code and message for a model whose dialect no native parser reads. -#[must_use] -pub fn unavailable_analysis(model: &str, dialect: Option<&str>) -> (&'static str, String) { - ( - SKIPPED_ANALYSIS_CODE, - format!( - "SQL analysis skipped for model '{model}': no native parser reads the {} dialect", - dialect.unwrap_or(GENERIC_DIALECT) - ), - ) -} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/unavailable_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/main/unavailable_analysis.rs new file mode 100644 index 000000000..97257e54f --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/main/unavailable_analysis.rs @@ -0,0 +1,13 @@ +use crate::analysis_route::constants::{GENERIC_DIALECT, SKIPPED_ANALYSIS_CODE}; + +/// P026's code and message for a model whose dialect no native parser reads. +#[must_use] +pub fn unavailable_analysis(model: &str, dialect: Option<&str>) -> (&'static str, String) { + ( + SKIPPED_ANALYSIS_CODE, + format!( + "SQL analysis skipped for model '{model}': no native parser reads the {} dialect", + dialect.unwrap_or(GENERIC_DIALECT) + ), + ) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/mod.rs index 2a9f4bce7..4d7376c04 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/mod.rs @@ -6,11 +6,5 @@ pub mod main; pub mod models; pub mod types; -pub use crate::analysis_route::main::analysis_route::analysis_route; -pub use crate::analysis_route::models::{ - AnalysisInput, AnalysisRoute, NativeAnalysis, ParserGap, StandInSchema, TypeMatch, -}; -pub use crate::analysis_route::types::NativeDialect; - #[cfg(test)] mod tests; diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 1a9016953..7330e008d 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -43,8 +43,7 @@ pub struct LocatedGap { /// How an inferred column type meets its declared type: the type itself, then its nullability. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TypeMatch { - /// One side is a type the dialect cannot spell, refuses or does not know: no type finding and - /// no match, though the declared type alone may still forbid null. + /// One side's type is unknown or refused: no type finding, though null may still be forbidden. NotCompared { /// The inferred column may be null, but its declared type does not allow it. nullable_into_non_null: bool, @@ -65,8 +64,7 @@ pub struct AnalysisInput<'a> { pub schema: &'a dyn LineageSchema, /// `(analysis name, resource type, resource name)` of each relation the model references. pub references: &'a [(String, String, String)], - /// Functions the dialect's catalogue cannot know, such as MotherDuck's `md_*`, matched without - /// case: no unknown-function finding names them, and they stay untyped. + /// Functions the catalogue cannot know, such as MotherDuck's `md_*`; never reported unknown. pub extension_functions: &'a [String], } @@ -87,8 +85,7 @@ pub struct NativeAnalysis { pub diagnostics: Vec, } -/// One relation of known columns, standing in for what a probe query reads, such as a macro -/// body's arguments as the columns of `SELECT () AS b FROM `. +/// One relation of known columns, standing in for what a probe query such as a macro's reads. #[derive(Clone, Debug, PartialEq, Eq)] pub struct StandInSchema { relation: String, diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index 93f21c6de..add990df0 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -1,3 +1,5 @@ +#[path = "tests/helpers.rs"] +mod helpers; #[path = "tests/test_clickhouse_type_match.rs"] mod test_clickhouse_type_match; #[path = "tests/test_native_dialect.rs"] diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs new file mode 100644 index 000000000..27e20d3ec --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs @@ -0,0 +1,53 @@ +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, StandInSchema}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::models::LineageRow; + +/// `orders` with a non-null INTEGER id and a nullable VARCHAR status. +pub(super) fn orders() -> StandInSchema { + StandInSchema::new( + "orders", + &[("id", "INTEGER", false), ("status", "VARCHAR", true)], + ) +} + +/// The analysis of `sql` over `orders`, which the model references as the model `shop.orders`. +pub(super) fn analysed( + dialect: &dyn NativeDialect, + sql: &str, + extension_functions: &[&str], +) -> NativeAnalysis { + let schema: StandInSchema = orders(); + let references: [(String, String, String); 1] = [( + "orders".to_owned(), + "model".to_owned(), + "shop.orders".to_owned(), + )]; + let extension_functions: Vec = extension_functions + .iter() + .map(|name| (*name).to_owned()) + .collect(); + dialect + .analyse(&AnalysisInput { + sql, + schema: &schema, + references: &references, + extension_functions: &extension_functions, + }) + .expect("the test query is read whole") +} + +/// The columns a lineage row reads, each of which must come from the model `shop.orders`. +pub(super) fn resource_columns(row: &LineageRow) -> Vec<&str> { + row.sources + .iter() + .map(|(resource_type, resource_name, column)| { + assert_eq!( + (resource_type.as_str(), resource_name.as_str()), + ("model", "shop.orders"), + "a source of {}", + row.output_column + ); + column.as_str() + }) + .collect() +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index 1e9b9c7ae..9f8c47005 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -1,47 +1,12 @@ -use crate::analysis_route::main::skipped_analysis::{skipped_analysis, unavailable_analysis}; -use crate::analysis_route::models::{ - AnalysisInput, LocatedGap, NativeAnalysis, StandInSchema, TypeMatch, -}; +use crate::analysis_route::main::unavailable_analysis::unavailable_analysis; +use crate::analysis_route::models::{NativeAnalysis, TypeMatch}; +use crate::analysis_route::tests::helpers::{analysed, resource_columns}; use crate::analysis_route::tests::test_types::{ - CataloguedDialect, FindingTestCase, MacroBodyTestCase, NativeTypeMatchTestCase, - PivotlessDialect, ReadWholeTestCase, + CataloguedDialect, ColumnTestCase, FindingTestCase, LineageTestCase, MacroBodyTestCase, + NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, StarTestCase, + UnavailableAnalysisTestCase, }; use crate::analysis_route::types::NativeDialect; -use crate::assembly::analysis_session::models::LineageRow; - -/// `orders` with a non-null INTEGER id and a nullable VARCHAR status. -fn orders() -> StandInSchema { - StandInSchema::new( - "orders", - &[("id", "INTEGER", false), ("status", "VARCHAR", true)], - ) -} - -/// The analysis of `sql` over `orders`, which the model references as the model `shop.orders`. -fn analysed( - dialect: &dyn NativeDialect, - sql: &str, - extension_functions: &[&str], -) -> NativeAnalysis { - let schema: StandInSchema = orders(); - let references: [(String, String, String); 1] = [( - "orders".to_owned(), - "model".to_owned(), - "shop.orders".to_owned(), - )]; - let extension_functions: Vec = extension_functions - .iter() - .map(|name| (*name).to_owned()) - .collect(); - dialect - .analyse(&AnalysisInput { - sql, - schema: &schema, - references: &references, - extension_functions: &extension_functions, - }) - .expect("the test query is read whole") -} #[test] fn given_sql_when_reading_it_whole_then_only_one_parsed_statement_without_a_gap_is_read() { @@ -147,38 +112,29 @@ fn given_a_type_the_dialect_refuses_when_matching_types_then_nothing_is_compared } #[test] -fn given_a_parser_message_or_no_parser_when_skipping_analysis_then_p026_says_why() { - let syntax: LocatedGap = LocatedGap { - construct: "syntax error".to_owned(), - message: "syntax error at or near \"FROM\"".to_owned(), - line: 1, - column: 15, - syntax: true, - }; - assert_eq!( - skipped_analysis("orders", "postgres", &syntax), - ( - "P026", - "SQL analysis skipped for model 'orders': the postgres parser cannot read syntax \ - error at line 1, column 15: syntax error at or near \"FROM\"" - .to_owned() - ), - "the parser's message follows the construct" - ); - assert_eq!( - unavailable_analysis("orders", Some("trino")), - ( - "P026", - "SQL analysis skipped for model 'orders': no native parser reads the trino dialect" - .to_owned() - ), - "an unknown dialect is named" - ); - assert_eq!( - unavailable_analysis("orders", None).1, - "SQL analysis skipped for model 'orders': no native parser reads the generic dialect", - "no dialect is the generic dialect" - ); +fn given_no_native_parser_when_skipping_analysis_then_p026_names_the_dialect() { + let test_cases = [ + UnavailableAnalysisTestCase { + description: "an unknown dialect is named", + dialect: Some("trino"), + expected_message: "SQL analysis skipped for model 'orders': no native parser reads \ + the trino dialect", + }, + UnavailableAnalysisTestCase { + description: "no dialect is the generic dialect", + dialect: None, + expected_message: "SQL analysis skipped for model 'orders': no native parser reads \ + the generic dialect", + }, + ]; + for test_case in test_cases { + assert_eq!( + unavailable_analysis("orders", test_case.dialect), + ("P026", test_case.expected_message.to_owned()), + "{}", + test_case.description + ); + } } #[test] @@ -228,109 +184,146 @@ fn given_findings_when_analysing_then_they_take_sqlbuild_codes_and_positions() { }, ]; for test_case in test_cases { - let findings: Vec<(String, Option, Option)> = analysed( + let analysis: NativeAnalysis = analysed( test_case.dialect, test_case.sql, test_case.extension_functions, - ) - .diagnostics - .into_iter() - .map(|row| (row.code, row.line, row.column)) - .collect(); - let expected: Vec<(String, Option, Option)> = test_case - .expected_findings + ); + let findings: Vec<(&str, Option<(usize, usize)>)> = analysis + .diagnostics .iter() - .map(|&(code, position)| { + .map(|row| (row.code.as_str(), row.line.zip(row.column))) + .collect(); + assert_eq!( + findings, test_case.expected_findings, + "{}", + test_case.description + ); + } +} + +#[test] +fn given_a_query_over_referenced_columns_when_analysing_then_lineage_reads_the_resources() { + let test_cases = [ + LineageTestCase { + description: "direct, cast and constant columns read the model; an unresolved one is \ + read from the one relation at medium confidence", + sql: "SELECT id, CAST(id AS TEXT) AS id_text, 1 AS one, nope FROM orders \ + WHERE status = 'paid'", + expected_lineage: &[ + ("id", 0, 1, &["id"]), + ("id_text", 1, 1, &["id"]), + ("one", 5, 1, &[]), + ("nope", 0, 2, &["nope"]), + ], + expected_row_sources: &[("orders", "status")], + }, + LineageTestCase { + description: "a query without a filter has no columns deciding rows", + sql: "SELECT status FROM orders", + expected_lineage: &[("status", 0, 1, &["status"])], + expected_row_sources: &[], + }, + ]; + for test_case in test_cases { + let analysis: NativeAnalysis = analysed(&PivotlessDialect, test_case.sql, &[]); + let lineage: Vec<(&str, u8, u8, Vec<&str>)> = analysis + .lineage + .iter() + .map(|row| { ( - code.to_owned(), - position.map(|(line, _)| line), - position.map(|(_, column)| column), + row.output_column.as_str(), + row.transform_code, + row.confidence_code, + resource_columns(row), ) }) .collect(); - assert_eq!(findings, expected, "{}", test_case.description); + let row_sources: Vec<(&str, &str)> = analysis + .row_sources + .iter() + .map(|(relation, column)| (relation.as_str(), column.as_str())) + .collect(); + assert_eq!( + (lineage, row_sources), + ( + test_case + .expected_lineage + .iter() + .map(|&(name, transform, confidence, columns)| { + (name, transform, confidence, columns.to_vec()) + }) + .collect::>(), + test_case.expected_row_sources.to_vec() + ), + "{}", + test_case.description + ); } } #[test] -fn given_a_query_over_referenced_columns_when_analysing_then_lineage_reads_the_resources() { - let analysis: NativeAnalysis = analysed( - &PivotlessDialect, - "SELECT id, CAST(id AS TEXT) AS id_text, 1 AS one, nope FROM orders WHERE status = 'paid'", - &[], - ); - let source = |column: &str| { - ( - "model".to_owned(), - "shop.orders".to_owned(), - column.to_owned(), - ) - }; - assert_eq!( - analysis.lineage, - vec![ - LineageRow { - output_column: "id".to_owned(), - transform_code: 0, - confidence_code: 1, - sources: vec![source("id")], - }, - LineageRow { - output_column: "id_text".to_owned(), - transform_code: 1, - confidence_code: 1, - sources: vec![source("id")], - }, - LineageRow { - output_column: "one".to_owned(), - transform_code: 5, - confidence_code: 1, - sources: Vec::new(), - }, - LineageRow { - output_column: "nope".to_owned(), - transform_code: 0, - confidence_code: 2, - sources: vec![source("nope")], - }, - ], - "direct, cast and constant columns read the referenced model; an unresolved column is \ - read from the one relation at medium confidence" - ); - assert_eq!( - analysis.row_sources, - vec![("orders".to_owned(), "status".to_owned())], - "the filter's column decides rows" - ); - assert_eq!( - ( - analysis.columns[0].data_type.clone(), - analysis.columns[0].nullability.as_str() - ), - ( - PivotlessDialect - .normalize_type("INTEGER") - .map(|normalized| normalized.normalized_name), - "non_null" - ), - "a column is typed through the dialect and keeps the schema's nullability" - ); +fn given_schema_columns_when_analysing_then_they_keep_their_types_and_nullability() { + let test_cases = [ + ColumnTestCase { + description: "a non-null INTEGER column", + sql: "SELECT id FROM orders", + expected_columns: &[("id", Some("INTEGER"), "non_null")], + }, + ColumnTestCase { + description: "a nullable VARCHAR column", + sql: "SELECT status FROM orders", + expected_columns: &[("status", Some("VARCHAR"), "nullable")], + }, + ]; + for test_case in test_cases { + let analysis: NativeAnalysis = analysed(&PivotlessDialect, test_case.sql, &[]); + let columns: Vec<(&str, Option<&str>, &str)> = analysis + .columns + .iter() + .map(|column| { + ( + column.name.as_str(), + column.data_type.as_deref(), + column.nullability.as_str(), + ) + }) + .collect(); + assert_eq!( + columns, test_case.expected_columns, + "{}", + test_case.description + ); + } } #[test] fn given_a_star_when_analysing_then_it_is_resolved_only_over_known_columns() { - let known: NativeAnalysis = analysed(&PivotlessDialect, "SELECT * FROM orders", &[]); - assert_eq!( - (known.has_star, known.star_resolved, known.columns.len()), - (true, true, 2), - "a star over a known relation projects its columns" - ); - let unknown: NativeAnalysis = analysed(&PivotlessDialect, "SELECT * FROM elsewhere", &[]); - assert_eq!( - (unknown.has_star, unknown.star_resolved), - (true, false), - "a star over an unknown relation leaves columns out" - ); + let test_cases = [ + StarTestCase { + description: "a star over a known relation projects its columns", + sql: "SELECT * FROM orders", + expected_star: (true, true, 2), + }, + StarTestCase { + description: "a star over an unknown relation leaves columns out", + sql: "SELECT * FROM elsewhere", + expected_star: (true, false, 0), + }, + ]; + for test_case in test_cases { + let analysis: NativeAnalysis = analysed(&PivotlessDialect, test_case.sql, &[]); + assert_eq!( + ( + analysis.has_star, + analysis.star_resolved, + analysis.columns.len() + ), + test_case.expected_star, + "{}", + test_case.description + ); + } } #[test] diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index a69a2d363..5c395c6d1 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -132,21 +132,39 @@ fn given_model_sql_when_probing_the_native_parser_then_its_gap_is_located_after_ #[test] fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_and_the_gap() { - let test_cases = [SkippedAnalysisTestCase { - description: "a PIVOT gap on line 3", - model: "orders_pivot", - dialect: "postgres", - gap: LocatedGap { - construct: "PIVOT".to_owned(), - message: "PIVOT".to_owned(), - line: 3, - column: 3, - syntax: false, - }, - expected_code: "P026", - expected_message: "SQL analysis skipped for model 'orders_pivot': the postgres parser \ + let test_cases = [ + SkippedAnalysisTestCase { + description: "the parser's message follows the construct", + model: "orders", + dialect: "postgres", + gap: LocatedGap { + construct: "syntax error".to_owned(), + message: "syntax error at or near \"FROM\"".to_owned(), + line: 1, + column: 15, + syntax: true, + }, + expected_code: "P026", + expected_message: "SQL analysis skipped for model 'orders': the postgres parser \ + cannot read syntax error at line 1, column 15: syntax error at \ + or near \"FROM\"", + }, + SkippedAnalysisTestCase { + description: "a PIVOT gap on line 3", + model: "orders_pivot", + dialect: "postgres", + gap: LocatedGap { + construct: "PIVOT".to_owned(), + message: "PIVOT".to_owned(), + line: 3, + column: 3, + syntax: false, + }, + expected_code: "P026", + expected_message: "SQL analysis skipped for model 'orders_pivot': the postgres parser \ cannot read PIVOT at line 3, column 3", - }]; + }, + ]; for test_case in test_cases { assert_eq!( skipped_analysis(test_case.model, test_case.dialect, &test_case.gap), diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 545760014..95d2188b3 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -30,8 +30,7 @@ pub(super) struct SkippedAnalysisTestCase { pub(super) expected_message: &'static str, } -/// A test dialect over the PostgreSQL parser that also refuses PIVOT, unfilled placeholders and -/// one-part stubs, keeps SQL after `RAW ` as raw text, and refuses the type `NOT_A_TYPE`. +/// The PostgreSQL parser, refusing PIVOT, placeholders, one-part stubs and the type `NOT_A_TYPE`. pub(super) struct PivotlessDialect; impl NativeDialect for PivotlessDialect { @@ -81,8 +80,7 @@ impl NativeDialect for PivotlessDialect { } } -/// DuckDB's shared parser under the DuckDB profile, which knows DuckDB's keyword-only functions, as -/// MotherDuck projects are analysed. +/// DuckDB's shared parser under the DuckDB profile, as MotherDuck projects are analysed. pub(super) struct CataloguedDialect; impl NativeDialect for CataloguedDialect { @@ -153,3 +151,33 @@ pub(super) struct MacroBodyTestCase { /// `(construct, offset in the body)` of the gap, when the body is not read. pub(super) expected_gap: Option<(&'static str, usize)>, } + +pub(super) struct UnavailableAnalysisTestCase { + pub(super) description: &'static str, + pub(super) dialect: Option<&'static str>, + pub(super) expected_message: &'static str, +} + +/// Each output's name, transform code, confidence code and the columns it reads. +type ExpectedLineage = &'static [(&'static str, u8, u8, &'static [&'static str])]; + +pub(super) struct LineageTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + pub(super) expected_lineage: ExpectedLineage, + pub(super) expected_row_sources: &'static [(&'static str, &'static str)], +} + +pub(super) struct ColumnTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + /// Each output's name, type and nullability. + pub(super) expected_columns: &'static [(&'static str, Option<&'static str>, &'static str)], +} + +pub(super) struct StarTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + /// Whether it projects a star, whether every star column is known, and the column count. + pub(super) expected_star: (bool, bool, usize), +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/types.rs b/crates/sqlbuild-analysis/src/analysis_route/types.rs index 2860e079f..d28a0b24f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/types.rs @@ -4,6 +4,7 @@ use sqlquack_semantics::dialects::types::DialectProfile; use sqlquack_syntax::{Expression, Parsed}; use crate::analysis_route::_helpers::native_analysis::native_analysis; +use crate::analysis_route::_helpers::whole_gap::whole_gap; use crate::analysis_route::constants::{ MACRO_BODY_PREFIX, MORE_THAN_ONE_STATEMENT, NO_STATEMENT, NOT_ONE_EXPRESSION, UNPARSED_STATEMENT, @@ -25,8 +26,7 @@ pub trait NativeDialect: Sync { /// The dialect's sqlquack-semantics profile: how its names resolve, compare and type. fn profile(&self) -> &'static dyn DialectProfile; - /// A type as written in this dialect, as SQLBuild compares types; `None` when the dialect - /// cannot spell it or its engine refuses it, and nothing is compared against it. + /// A type as written in this dialect, as SQLBuild compares it; `None` when unknown or refused. fn normalize_type(&self, spelling: &str) -> Option; /// The one statement of `sql` when the parser reads it whole: no gap, and not raw text. @@ -49,15 +49,13 @@ pub trait NativeDialect: Sync { } } - /// The outputs, lineage and findings of the query `input.sql`, read whole, against - /// `input.schema`; findings are in SQLBuild's codes, as the Polyglot path reports them. + /// The outputs, lineage and findings of the query `input.sql` in SQLBuild's facts and codes. fn analyse(&self, input: &AnalysisInput<'_>) -> Result { let statement: Expression = self.read_whole(input.sql)?; Ok(native_analysis(self, &statement, input)) } - /// A macro body's one expression, read as the projection of `SELECT `; a gap's offset - /// is in `body`. + /// A macro body's one expression, read as the projection of `SELECT `. fn macro_body(&self, body: &str) -> Result { let statement: Expression = self .read_whole(&format!("{MACRO_BODY_PREFIX}{body}")) @@ -77,9 +75,7 @@ pub trait NativeDialect: Sync { } } - /// Whether a declared and an inferred type match, nullability apart; `NotCompared` when the - /// inferred type is unknown or `normalize_type` gives `None` for either side. An override must - /// keep that rule, and may still say whether the declared type alone forbids null. + /// Whether a declared and an inferred type match; an override keeps `NotCompared` for unknowns. fn type_match( &self, declared: &str, @@ -101,13 +97,3 @@ pub trait NativeDialect: Sync { } } } - -/// A gap `read_whole` names itself, which no parser reports as invalid syntax. -fn whole_gap(construct: &str, offset: usize) -> ParserGap { - ParserGap { - construct: construct.to_owned(), - message: construct.to_owned(), - offset, - syntax: false, - } -} diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs index cea988305..febdc7140 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs @@ -202,7 +202,6 @@ pub(crate) fn normalize_native_sql( let quoted: bool = non_code .iter() .any(|&(start, end)| start <= whole.start() && whole.start() < end); - // A cursor stub is a whole name: `x__cursor_start()` is another function. let glued: bool = captures.get(1).is_some() && normalized[..whole.start()] .chars() diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main.rs b/crates/sqlbuild-analysis/src/semantic_validation/main.rs index 930655bff..2db687f9e 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/main.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/main.rs @@ -8,6 +8,7 @@ pub mod normalize_batch; pub mod normalize_dialect; pub mod normalize_mapped; pub mod normalize_native; +pub mod sqlbuild_code; pub mod truth_test_spans; use crate::semantic_validation::models::ProjectCatalog; diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs index 0f271d481..ae7f19263 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/main/diagnostics.rs @@ -7,10 +7,3 @@ pub fn binding_diagnostics( ) -> Result, String> { crate::semantic_validation::_helpers::diagnostics::binding_diagnostics(sql, dialect, rows) } - -/// SQLBuild's code for an analysis finding: E codes directly, implicit-conversion W codes through -/// their blocking B codes; `None` for a code SQLBuild does not report. -#[must_use] -pub fn sqlbuild_code(code: &str) -> Option { - crate::semantic_validation::_helpers::diagnostics::sqlbuild_code(code) -} diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main/sqlbuild_code.rs b/crates/sqlbuild-analysis/src/semantic_validation/main/sqlbuild_code.rs new file mode 100644 index 000000000..0187252e4 --- /dev/null +++ b/crates/sqlbuild-analysis/src/semantic_validation/main/sqlbuild_code.rs @@ -0,0 +1,5 @@ +/// SQLBuild's code for an analysis finding; `None` for a code SQLBuild does not report. +#[must_use] +pub fn sqlbuild_code(code: &str) -> Option { + crate::semantic_validation::_helpers::diagnostics::sqlbuild_code(code) +} diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs index 83970ed72..6cc429a7f 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/fallback.rs @@ -97,7 +97,6 @@ pub(crate) fn normalize_with_fallback( let name: &str = if bigquery { "BOOL" } else { base_type }; return simple(name, TypeFamily::Boolean); } - // The top-level type decides the family, so a MAP or STRUCT holding a timestamp is not one. let top_level: &str = base_type .split(|character: char| !(character.is_ascii_alphanumeric() || character == '_')) .next() diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs index ba70a0280..73a83a8cb 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_normalize_type.rs @@ -3,7 +3,9 @@ use crate::type_system::main::normalize_text_type::normalize_text_type; use crate::type_system::main::normalize_type::normalize_type; use crate::type_system::models::TypeFamily; use crate::type_system::tests::helpers::normalization_text; -use crate::type_system::tests::test_types::{BracketDepthTestCase, NormalizeTypeTestCase}; +use crate::type_system::tests::test_types::{ + BracketDepthTestCase, NormalizeTypeTestCase, TextFamilyTestCase, +}; #[test] fn given_type_strings_when_normalizing_then_python_normalization_is_returned() { @@ -185,47 +187,48 @@ fn given_array_suffixes_when_normalizing_on_a_worker_stack_then_python_supported #[test] fn given_complex_types_holding_a_timestamp_when_normalizing_text_then_the_top_level_type_decides() { let test_cases = [ - ( - "an array of timestamps is an array", - "ARRAY", - TypeFamily::Other, - ), - ( - "a map of structs holding a timestamp is a map", - "MAP>", - TypeFamily::Other, - ), - ( - "a struct holding a timestamp is a struct", - "STRUCT", - TypeFamily::Other, - ), - ( - "a bare timestamp is a timestamp", - "TIMESTAMP", - TypeFamily::Timestamp, - ), - ( - "a timestamp with precision is a timestamp", - "TIMESTAMP(3)", - TypeFamily::Timestamp, - ), - ( - "a zoned timestamp is a timestamp", - "TIMESTAMP WITH TIME ZONE", - TypeFamily::Timestamp, - ), - ( - "Snowflake's NTZ alias is a timestamp", - "TIMESTAMP_NTZ", - TypeFamily::Timestamp, - ), + TextFamilyTestCase { + description: "an array of timestamps is an array", + type_sql: "ARRAY", + expected_family: TypeFamily::Other, + }, + TextFamilyTestCase { + description: "a map of structs holding a timestamp is a map", + type_sql: "MAP>", + expected_family: TypeFamily::Other, + }, + TextFamilyTestCase { + description: "a struct holding a timestamp is a struct", + type_sql: "STRUCT", + expected_family: TypeFamily::Other, + }, + TextFamilyTestCase { + description: "a bare timestamp is a timestamp", + type_sql: "TIMESTAMP", + expected_family: TypeFamily::Timestamp, + }, + TextFamilyTestCase { + description: "a timestamp with precision is a timestamp", + type_sql: "TIMESTAMP(3)", + expected_family: TypeFamily::Timestamp, + }, + TextFamilyTestCase { + description: "a zoned timestamp is a timestamp", + type_sql: "TIMESTAMP WITH TIME ZONE", + expected_family: TypeFamily::Timestamp, + }, + TextFamilyTestCase { + description: "Snowflake's NTZ alias is a timestamp", + type_sql: "TIMESTAMP_NTZ", + expected_family: TypeFamily::Timestamp, + }, ]; - for (description, type_sql, expected_family) in test_cases { + for test_case in test_cases { assert_eq!( - normalize_text_type(type_sql, None).family, - expected_family, - "{description}" + normalize_text_type(test_case.type_sql, None).family, + test_case.expected_family, + "{}", + test_case.description ); } } diff --git a/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs b/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs index 2b9aeec95..46c3592c5 100644 --- a/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/type_system/tests/test_types.rs @@ -30,3 +30,9 @@ pub(super) struct SameTypeTestCase { pub(super) types: (&'static str, &'static str), pub(super) expected_same: bool, } + +pub(super) struct TextFamilyTestCase { + pub(super) description: &'static str, + pub(super) type_sql: &'static str, + pub(super) expected_family: crate::type_system::models::TypeFamily, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs index 234f5502b..4020b6e5f 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs @@ -5,9 +5,8 @@ use std::path::Path; use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyErr, PyResult, Python}; use pyo3::types::{PyBool, PyBoolMethods, PyDict, PyDictMethods, PyList, PyString, PyTuple}; use sqlbuild_analysis::analysis_route::main::native_gap::native_gap; -use sqlbuild_analysis::analysis_route::main::skipped_analysis::{ - skipped_analysis, unavailable_analysis, -}; +use sqlbuild_analysis::analysis_route::main::skipped_analysis::skipped_analysis; +use sqlbuild_analysis::analysis_route::main::unavailable_analysis::unavailable_analysis; use sqlbuild_analysis::analysis_route::models::{AnalysisRoute, LocatedGap}; use sqlbuild_analysis::analysis_route::types::NativeDialect; use sqlbuild_analysis::assembly::project::models::{SyntaxCheck, SyntaxMode}; @@ -99,8 +98,12 @@ pub(crate) fn skip_unreadable( let Some(gap) = first_gap(dialect, context, &checks)? else { return Ok(true); }; - let (code, message) = skipped_analysis(model.name, dialect.name(), &gap); - report_skip(settings, context, model, code, message)?; + report_skip( + settings, + context, + model, + skipped_analysis(model.name, dialect.name(), &gap), + )?; Ok(false) } @@ -111,8 +114,12 @@ pub(crate) fn skip_unavailable( context: &LoopContext<'_, '_>, model: &OptOutModel<'_, '_>, ) -> PyResult<()> { - let (code, message) = unavailable_analysis(model.name, dialect); - report_skip(settings, context, model, code, message) + report_skip( + settings, + context, + model, + unavailable_analysis(model.name, dialect), + ) } /// Reports P026 as a warning, or raises it under `require_sql_analysis`. @@ -120,8 +127,7 @@ fn report_skip( settings: ModelLoopSettings, context: &LoopContext<'_, '_>, model: &OptOutModel<'_, '_>, - code: &str, - message: String, + (code, message): (&str, String), ) -> PyResult<()> { let py: Python<'_> = model.config.py(); let help: &str = &context.classes.syntax_help; diff --git a/fensu.toml b/fensu.toml index bee7f18a7..3fee255b7 100644 --- a/fensu.toml +++ b/fensu.toml @@ -2932,3 +2932,13 @@ reason = "The override keeps queue.Queue.put's positional protocol, which every rule = "FFS201" path = "src/sqlbuild/orchestration_declarations/models/sensor_context.py" reason = "Only SensorContext lives here: invocation-local ctx.cursor assignment proposes progress, committed solely by native fenced completion. The package regression guards this declaration-only scope; class-symbol exceptions are unsupported." + +[[targets.rust.rule_exceptions]] +rule = "FPRSL105" +path = "crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs" +reason = "Native dialect modules in analysis_route type their spellings through this entry; each dialect's module lands in its own change." + +[[targets.rust.rule_exceptions]] +rule = "FPRSL105" +path = "crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs" +reason = "The ClickHouse native dialect module's type_match calls this entry; that module lands in its own change." From fea889c3ff7bc26e1628c28254be69fb7b7cec6c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:06:30 +0000 Subject: [PATCH 009/160] chore(deps): bump sqlquack to 19c51fa2 sqlquack main 19c51fa2 carries the Postgres parser work and the train that followed a9dde6a0. sqlquack-syntax-postgres now depends on stacker, which re-resolves a few Windows-only windows-sys edges in the lock. --- Cargo.lock | 13 +++++++------ Cargo.toml | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b09abffe2..4edc9a920 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4499,7 +4499,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" dependencies = [ "serde", "serde_json", @@ -4512,7 +4512,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" dependencies = [ "proc-macro2", "quote", @@ -4522,9 +4522,10 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" dependencies = [ "sqlquack-syntax", + "stacker", ] [[package]] @@ -4618,7 +4619,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5054,7 +5055,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 65a30d1f3..9e8e7cfd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,8 +177,8 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From 80039dcd539e1e1d78f97dbe9e6f658abd399799 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:10:41 +0000 Subject: [PATCH 010/160] chore(deps): pin sqlquack-semantics to 19c51fa2 with the other sqlquack crates --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21077bdb2..7d467327d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4500,7 +4500,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" dependencies = [ "serde", "serde_json", @@ -4510,7 +4510,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" dependencies = [ "serde", "serde_json", @@ -4522,7 +4522,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a9dde6a00b144c6306425cfa84516ad816d8c49c#a9dde6a00b144c6306425cfa84516ad816d8c49c" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" dependencies = [ "serde", "sqlquack-core", @@ -4651,7 +4651,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5087,7 +5087,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bbe26153f..c4475e838 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,7 +177,7 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a9dde6a00b144c6306425cfa84516ad816d8c49c" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } # The lossless SQL tokenizer behind query fingerprints. sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } From ffdc043b9d677b24eb3324d4a1e54960e33696cf Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:38:19 +0000 Subject: [PATCH 011/160] feat(analysis): analyse Snowflake and DuckDB natively with SQLQuack Snowflake, DuckDB and MotherDuck models now take the native analysis route. Each arm parses with its SQLQuack parser, analyses under its sqlquack-semantics profile, and compares types through respellings that keep Polyglot's type names. The SQLite arm is in place but stays on the legacy route until its parser is in SQLQuack's depth probe. SQLQuack crates are pinned to main edfa30b2, which adds the DuckDB and SQLite parsers. --- Cargo.lock | 34 ++- Cargo.toml | 8 +- crates/sqlbuild-analysis/Cargo.toml | 2 + .../analysis_route/_helpers/duckdb_dialect.rs | 51 ++++ .../src/analysis_route/_helpers/mod.rs | 4 + .../_helpers/snowflake_dialect.rs | 81 ++++++ .../analysis_route/_helpers/sqlite_dialect.rs | 50 ++++ .../analysis_route/_helpers/type_spelling.rs | 237 +++++++++++++++ .../src/analysis_route/constants.rs | 115 +++++++- .../src/analysis_route/models.rs | 33 +++ .../src/analysis_route/tests.rs | 4 + .../analysis_route/tests/test_native_arms.rs | 134 +++++++++ .../tests/test_native_dialect.rs | 32 +- .../analysis_route/tests/test_native_gap.rs | 6 +- .../tests/test_type_spelling.rs | 275 ++++++++++++++++++ .../src/analysis_route/tests/test_types.rs | 18 +- 16 files changed, 1069 insertions(+), 15 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/duckdb_dialect.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/sqlite_dialect.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/type_spelling.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs diff --git a/Cargo.lock b/Cargo.lock index 7d467327d..1f5464003 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4144,7 +4144,9 @@ dependencies = [ "sqlclean", "sqlquack-semantics", "sqlquack-syntax", + "sqlquack-syntax-duckdb", "sqlquack-syntax-postgres", + "sqlquack-syntax-sqlite", ] [[package]] @@ -4500,7 +4502,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4510,19 +4512,21 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ + "regex", "serde", "serde_json", "sqlquack-core", "sqlquack-snowflake", "sqlquack-syntax", + "stacker", ] [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "sqlquack-core", @@ -4531,7 +4535,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4544,17 +4548,35 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "proc-macro2", "quote", "syn 2.0.119", ] +[[package]] +name = "sqlquack-syntax-duckdb" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +dependencies = [ + "sqlquack-syntax", + "stacker", +] + [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +dependencies = [ + "sqlquack-syntax", + "stacker", +] + +[[package]] +name = "sqlquack-syntax-sqlite" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "sqlquack-syntax", "stacker", diff --git a/Cargo.toml b/Cargo.toml index c4475e838..3f5ba1222 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,10 +177,12 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 95c643ebd..32b5764d6 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -28,7 +28,9 @@ sqlbuild-sqltext.workspace = true sqlclean.workspace = true sqlquack-semantics.workspace = true sqlquack-syntax.workspace = true +sqlquack-syntax-duckdb.workspace = true sqlquack-syntax-postgres.workspace = true +sqlquack-syntax-sqlite.workspace = true [lints] workspace = true diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/duckdb_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/duckdb_dialect.rs new file mode 100644 index 000000000..b03d844a4 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/duckdb_dialect.rs @@ -0,0 +1,51 @@ +//! DuckDB's native module: SQLQuack's native DuckDB parser reads its SQL. Validation only. + +use sqlquack_semantics::dialects::constants::DUCKDB; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax_duckdb::{Parsed, StrictFailure, parse_strict}; + +use crate::analysis_route::_helpers::type_spelling::respelt; +use crate::analysis_route::constants::{ + DUCKDB_DIALECT, DUCKDB_TYPE_SPELLING, UNQUOTED_STUB_PREFIX, UNSCANNED_CONSTRUCT, +}; +use crate::analysis_route::models::{DuckDbDialect, ParserGap}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeDialect}; + +impl NativeDialect for DuckDbDialect { + fn name(&self) -> &'static str { + DUCKDB_DIALECT + } + + fn stub_prefix(&self) -> &'static str { + UNQUOTED_STUB_PREFIX + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql).map_err(|failure| match failure { + StrictFailure::Parse(error) => ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }, + StrictFailure::Uncovered { position } => ParserGap { + construct: UNSCANNED_CONSTRUCT.to_owned(), + message: UNSCANNED_CONSTRUCT.to_owned(), + offset: position, + syntax: false, + }, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &DUCKDB + } + + /// The type as Polyglot normalised it, or `None` (unknown) where the text fallback can't match it. + fn normalize_type(&self, spelling: &str) -> Option { + respelt(spelling, &DUCKDB_TYPE_SPELLING) + .map(|respelt| normalize_text_type(&respelt, Some(TypeDialect::DuckDb))) + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 715122942..025894d47 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,3 +1,7 @@ pub(crate) mod clickhouse_types; +pub(crate) mod duckdb_dialect; pub(crate) mod native_analysis; +pub(crate) mod snowflake_dialect; +pub(crate) mod sqlite_dialect; +pub(crate) mod type_spelling; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs new file mode 100644 index 000000000..1eff9c3fd --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs @@ -0,0 +1,81 @@ +//! Snowflake's native module: SQLQuack's Snowflake parser reads its SQL. Validation only. + +use sqlquack_semantics::dialects::constants::SNOWFLAKE; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax::{ + Dialect, Error, Expression, ParseOptions, Parsed, StrictFailure, TokenizeFailure, parse_strict, +}; + +use crate::analysis_route::_helpers::type_spelling::respelt; +use crate::analysis_route::constants::{ + SNOWFLAKE_DIALECT, SNOWFLAKE_TYPE_SPELLING, UNQUOTED_STUB_PREFIX, UNSCANNED_CONSTRUCT, +}; +use crate::analysis_route::models::{ParserGap, SnowflakeDialect}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeDialect}; + +impl NativeDialect for SnowflakeDialect { + fn name(&self) -> &'static str { + SNOWFLAKE_DIALECT + } + + fn stub_prefix(&self) -> &'static str { + UNQUOTED_STUB_PREFIX + } + + fn parse(&self, sql: &str) -> Result { + shared_parsed(sql, Dialect::Snowflake) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &SNOWFLAKE + } + + /// The type as Polyglot normalised it, or `None` (unknown) where the text fallback can't match it. + fn normalize_type(&self, spelling: &str) -> Option { + respelt(spelling, &SNOWFLAKE_TYPE_SPELLING) + .map(|respelt| normalize_text_type(&respelt, Some(TypeDialect::Snowflake))) + } +} + +/// Every statement the shared parser reads from `sql` in `dialect`, or its first refusal; it +/// cannot tell invalid syntax from an unsupported construct, so no refusal is marked syntax. +pub(crate) fn shared_parsed(sql: &str, dialect: Dialect) -> Result { + let statements: Vec = parse_strict(sql, dialect, &ParseOptions::default()) + .map_err(|failure| match failure { + StrictFailure::Tokens( + TokenizeFailure::Uncovered { start, .. } | TokenizeFailure::Overlap { start, .. }, + ) => ParserGap { + construct: UNSCANNED_CONSTRUCT.to_owned(), + message: UNSCANNED_CONSTRUCT.to_owned(), + offset: start, + syntax: false, + }, + StrictFailure::Parse(error) + | StrictFailure::Tokens(TokenizeFailure::Tokenizer(error)) => { + let offset: usize = error.start().unwrap_or_default(); + let message: String = unplaced(error); + ParserGap { + construct: message.clone(), + message, + offset, + syntax: false, + } + } + })?; + Ok(Parsed { + statements, + gaps: Vec::new(), + }) +} + +/// The parser's message without its own line and column, which P026 reports from the offset. +fn unplaced(error: Error) -> String { + match error { + Error::Tokenize { message, .. } + | Error::Parse { message, .. } + | Error::Syntax { message, .. } => message, + other => other.to_string(), + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/sqlite_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/sqlite_dialect.rs new file mode 100644 index 000000000..942eadafa --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/sqlite_dialect.rs @@ -0,0 +1,50 @@ +//! SQLite's native module: SQLQuack's native SQLite parser reads its SQL. Validation only. + +use sqlquack_semantics::dialects::constants::SQLITE; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax_sqlite::{Parsed, StrictFailure, parse_strict}; + +use crate::analysis_route::_helpers::type_spelling::respelt; +use crate::analysis_route::constants::{ + SQLITE_DIALECT, SQLITE_TYPE_SPELLING, UNQUOTED_STUB_PREFIX, UNSCANNED_CONSTRUCT, +}; +use crate::analysis_route::models::{ParserGap, SqliteDialect}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::NormalizedType; + +impl NativeDialect for SqliteDialect { + fn name(&self) -> &'static str { + SQLITE_DIALECT + } + + fn stub_prefix(&self) -> &'static str { + UNQUOTED_STUB_PREFIX + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql).map_err(|failure| match failure { + StrictFailure::Parse(error) => ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }, + StrictFailure::Uncovered { position } => ParserGap { + construct: UNSCANNED_CONSTRUCT.to_owned(), + message: UNSCANNED_CONSTRUCT.to_owned(), + offset: position, + syntax: false, + }, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &SQLITE + } + + /// The type as Polyglot normalised it, or `None` (unknown) where the text fallback can't match it. + fn normalize_type(&self, spelling: &str) -> Option { + respelt(spelling, &SQLITE_TYPE_SPELLING).map(|respelt| normalize_text_type(&respelt, None)) + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/type_spelling.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/type_spelling.rs new file mode 100644 index 000000000..1c94661f3 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/type_spelling.rs @@ -0,0 +1,237 @@ +//! A type as written, respelt with the names SQLBuild's type fallback compares. + +use crate::analysis_route::constants::{FALLBACK_TIMESTAMP_WORD, MAX_TYPE_NAME_WORDS}; +use crate::analysis_route::models::TypeSpelling; + +/// `spelling` with each type name renamed by `rules`, or `None` when it is not one type. +pub(crate) fn respelt(spelling: &str, rules: &TypeSpelling) -> Option { + let mut reader = Reader { + source: spelling, + text: spelling.as_bytes(), + position: 0, + rules, + }; + let respelt: String = reader.data_type(true)?; + reader.blank(); + (reader.position == reader.text.len()).then_some(respelt) +} + +struct Reader<'a> { + source: &'a str, + text: &'a [u8], + position: usize, + rules: &'a TypeSpelling, +} + +impl Reader<'_> { + /// One type, renamed, with its parameters and array suffixes; an array's element is nested. + fn data_type(&mut self, top_level: bool) -> Option { + let start: usize = self.position; + let mut respelt: String = self.element(false)?; + if top_level && self.peek_after_blank() != Some(b'[') { + self.position = start; + respelt = self.element(true)?; + } + let parameterised: usize = respelt.len(); + while self.peek_after_blank() == Some(b'[') { + if !self.rules.arrays { + return None; + } + self.blank(); + let suffix: usize = self.position; + self.position += 1; + while self.peek().is_some_and(|byte| byte.is_ascii_digit()) { + self.position += 1; + } + if self.peek() != Some(b']') { + return None; + } + self.position += 1; + respelt.push_str(self.source.get(suffix..self.position)?); + } + let unsplit: bool = respelt.len() > parameterised || respelt.matches(')').count() > 1; + let misread: bool = unsplit && respelt.to_uppercase().contains(FALLBACK_TIMESTAMP_WORD); + (!(top_level && misread)).then_some(respelt) + } + + /// One type without array suffixes, renamed. + fn element(&mut self, top_level: bool) -> Option { + let (written, renamed) = self.name(top_level)?; + let mut respelt: String = renamed.clone(); + self.blank(); + if self.peek() != Some(b'(') && self.rules.bare_kept.contains(&written.as_str()) { + respelt.clone_from(&written); + } + if self.peek() == Some(b'(') { + if self.rules.verbatim.contains(&renamed.as_str()) { + respelt.push_str(&self.balanced()?); + } else { + let fielded: bool = self.rules.fielded.contains(&renamed.as_str()); + let arguments: Vec = self.arguments(fielded)?; + if top_level && self.rules.sizeless_whole.contains(&written.as_str()) { + return None; + } + if !self.rules.sizeless.contains(&written.as_str()) { + respelt = format!("{respelt}({})", arguments.join(", ")); + } + } + } + Some(respelt) + } + + /// The longest type name the rules rename, or one word as written, upper-cased. + fn name(&mut self, top_level: bool) -> Option<(String, String)> { + self.blank(); + let mut words: Vec<(String, usize)> = Vec::new(); + while words.len() < MAX_TYPE_NAME_WORDS { + self.blank_if(!words.is_empty()); + let word_start: usize = self.position; + self.position = self.word_end(); + if self.position == word_start { + break; + } + let word: String = self.source.get(word_start..self.position)?.to_uppercase(); + words.push((word, self.position)); + } + let (first, first_end) = words.first().cloned()?; + for length in (1..=words.len()).rev() { + let phrase: String = words[..length] + .iter() + .map(|(word, _)| word.as_str()) + .collect::>() + .join(" "); + let nested: &[(&str, &str)] = if top_level { + &[] + } else { + self.rules.nested_aliases + }; + if let Some((_, renamed)) = self + .rules + .aliases + .iter() + .chain(nested) + .find(|(from, _)| *from == phrase) + { + self.position = words[length - 1].1; + return Some((phrase, (*renamed).to_owned())); + } + } + self.position = first_end; + Some((first.clone(), first)) + } + + /// A parenthesised argument list: field and type pairs, or types and literals. + fn arguments(&mut self, fielded: bool) -> Option> { + self.position += 1; + let mut arguments: Vec = Vec::new(); + loop { + self.blank(); + let argument: String = if fielded { + let field: String = self.field()?; + format!("{field} {}", self.data_type(false)?) + } else if self + .peek() + .is_some_and(|byte| byte.is_ascii_digit() || byte == b'\'') + { + self.literal()? + } else { + self.data_type(false)? + }; + arguments.push(argument); + self.blank(); + match self.peek()? { + b',' => self.position += 1, + b')' => { + self.position += 1; + return Some(arguments); + } + _ => return None, + } + } + } + + /// A field name as written; a bare one outside ASCII is quoted, as Polyglot quotes it. + fn field(&mut self) -> Option { + let start: usize = self.position; + if self.peek() == Some(b'"') { + self.position += 1; + while self.peek()? != b'"' { + self.position += 1; + } + self.position += 1; + } else { + self.position = self.word_end(); + } + let field: &str = self.source.get(start..self.position)?; + match (field.is_empty(), field.starts_with('"') || field.is_ascii()) { + (true, _) => None, + (false, true) => Some(field.to_owned()), + (false, false) => Some(format!("\"{field}\"")), + } + } + + /// A number or a quoted string, as written. + fn literal(&mut self) -> Option { + let start: usize = self.position; + if self.peek() == Some(b'\'') { + self.position += 1; + while self.peek()? != b'\'' { + self.position += 1; + } + self.position += 1; + } else { + while self.peek().is_some_and(|byte| byte.is_ascii_digit()) { + self.position += 1; + } + } + self.source.get(start..self.position).map(str::to_owned) + } + + /// A parenthesised span copied as written, which the fallback reads as text. + fn balanced(&mut self) -> Option { + let start: usize = self.position; + let mut depth: usize = 0; + loop { + match self.peek()? { + b'(' => depth += 1, + b')' => depth -= 1, + _ => {} + } + self.position += 1; + if depth == 0 { + break; + } + } + self.source.get(start..self.position).map(str::to_owned) + } + + /// Where the word at the position ends: letters and digits in any script, and underscores. + fn word_end(&self) -> usize { + let rest: &str = self.source.get(self.position..).unwrap_or_default(); + self.position + + rest + .find(|character: char| !(character.is_alphanumeric() || character == '_')) + .unwrap_or(rest.len()) + } + + fn peek(&self) -> Option { + self.text.get(self.position).copied() + } + + fn peek_after_blank(&self) -> Option { + self.text[self.position..] + .iter() + .copied() + .find(|byte| !byte.is_ascii_whitespace()) + } + + fn blank(&mut self) { + self.blank_if(true); + } + + fn blank_if(&mut self, skip: bool) { + while skip && self.peek().is_some_and(|byte| byte.is_ascii_whitespace()) { + self.position += 1; + } + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..e3724304e 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,9 +1,27 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. +use crate::analysis_route::models::{DuckDbDialect, SnowflakeDialect, TypeSpelling}; use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. -pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[ + (DUCKDB_DIALECT, &DuckDbDialect), + (SNOWFLAKE_DIALECT, &SnowflakeDialect), +]; +/// DuckDB's (and MotherDuck's) analysis dialect name. +pub(crate) const DUCKDB_DIALECT: &str = "duckdb"; +/// Snowflake's analysis dialect name. +pub(crate) const SNOWFLAKE_DIALECT: &str = "snowflake"; +/// SQLite's analysis dialect name. +pub(crate) const SQLITE_DIALECT: &str = "sqlite"; +/// The stub prefix Snowflake and SQLite read as one unquoted name. +pub(crate) const UNQUOTED_STUB_PREFIX: &str = "__sqlbuild_"; +/// The word that makes SQLBuild's type fallback call a type it cannot split into name and flat parameters a timestamp. +pub(crate) const FALLBACK_TIMESTAMP_WORD: &str = "TIMESTAMP"; +/// The most words a type name spans, as in `TIMESTAMP WITH TIME ZONE`. +pub(crate) const MAX_TYPE_NAME_WORDS: usize = 4; +/// The construct of a byte the scanner reads as no token, blank or comment. +pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. @@ -26,3 +44,98 @@ pub(crate) const ERROR_SEVERITY: &str = "error"; pub(crate) const MACRO_BODY_PREFIX: &str = "SELECT "; /// The gap of a macro body that is not one expression. pub(crate) const NOT_ONE_EXPRESSION: &str = "not one expression"; +/// DuckDB's type names as SQLBuild compares them. +pub(crate) const DUCKDB_TYPE_SPELLING: TypeSpelling = TypeSpelling { + aliases: &[ + ("BINARY", "BLOB"), + ("BLOB", "VARBINARY"), + ("BOOL", "BOOLEAN"), + ("BYTEA", "BLOB"), + ("CHAR", "TEXT"), + ("CHARACTER", "TEXT"), + ("CHARACTER VARYING", "TEXT"), + ("DOUBLE PRECISION", "DOUBLE"), + ("HUGEINT", "INT128"), + ("INT64", "BIGINT"), + ("INTEGER", "INT"), + ("NUMERIC", "DECIMAL"), + ("ROW", "STRUCT"), + ("STRING", "TEXT"), + ("TIME WITH TIME ZONE", "TIMETZ"), + ("TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ"), + ("UHUGEINT", "UINT128"), + ("UINT16", "USMALLINT"), + ("UINT32", "UINTEGER"), + ("UINT64", "UBIGINT"), + ("UINT8", "UTINYINT"), + ("VARBINARY", "BLOB"), + ("VARCHAR", "TEXT"), + ], + nested_aliases: &[("DATETIME", "TIMESTAMP"), ("NVARCHAR", "TEXT")], + sizeless: &["CHAR", "CHARACTER"], + sizeless_whole: &["CHAR", "CHARACTER", "TIMESTAMP"], + bare_kept: &["ROW"], + fielded: &["STRUCT", "UNION"], + verbatim: &["LIST"], + arrays: true, +}; +/// Snowflake's type names as SQLBuild compares them. +pub(crate) const SNOWFLAKE_TYPE_SPELLING: TypeSpelling = TypeSpelling { + aliases: &[ + ("BINARY VARYING", "VARBINARY"), + ("CHAR VARYING", "VARCHAR"), + ("CHARACTER", "CHAR"), + ("CHARACTER VARYING", "VARCHAR"), + ("DOUBLE PRECISION", "DOUBLE"), + ("INTEGER", "INT"), + ("NCHAR VARYING", "VARCHAR"), + ("NUMERIC", "DECIMAL"), + ("REAL", "FLOAT"), + ("TEXT", "VARCHAR"), + ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"), + ("TIMESTAMP_NTZ", "TIMESTAMPNTZ"), + ("TIMESTAMP_TZ", "TIMESTAMPTZ"), + ], + nested_aliases: &[], + sizeless: &[], + sizeless_whole: &["TIMESTAMP", "TIMESTAMP_TZ", "TIMESTAMPTZ"], + bare_kept: &[], + fielded: &["OBJECT"], + verbatim: &[], + arrays: false, +}; +/// SQLite's type names as SQLBuild compares them. +pub(crate) const SQLITE_TYPE_SPELLING: TypeSpelling = TypeSpelling { + aliases: &[ + ("BOOL", "BOOLEAN"), + ("CHAR", "TEXT"), + ("CHARACTER", "TEXT"), + ("DOUBLE", "REAL"), + ("DOUBLE PRECISION", "REAL"), + ("INT", "INTEGER"), + ("NUMERIC", "DECIMAL"), + ("SMALLINT", "INTEGER"), + ("VARBINARY", "BLOB"), + ("VARCHAR", "TEXT"), + ], + nested_aliases: &[], + sizeless: &["CHAR", "CHARACTER"], + sizeless_whole: &[ + "BIGINT", + "CHAR", + "CHARACTER", + "DOUBLE", + "DOUBLE PRECISION", + "FLOAT", + "INT", + "INTEGER", + "MEDIUMINT", + "REAL", + "SMALLINT", + "TINYINT", + ], + bare_kept: &[], + fielded: &[], + verbatim: &[], + arrays: false, +}; diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 7330e008d..07859ad3c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -122,3 +122,36 @@ impl LineageSchema for StandInSchema { .then_some(self.columns.as_slice()) } } + +/// DuckDB and MotherDuck, whose type spellings the native route compares as Polyglot did. +#[derive(Clone, Copy, Debug)] +pub struct DuckDbDialect; + +/// Snowflake, whose SQL SQLQuack's Snowflake parser reads. +#[derive(Clone, Copy, Debug)] +pub struct SnowflakeDialect; + +/// SQLite, whose SQL SQLQuack's native SQLite parser reads. +#[derive(Clone, Copy, Debug)] +pub struct SqliteDialect; + +/// How a dialect's type names are respelt before SQLBuild's type fallback reads them. +#[derive(Clone, Copy, Debug)] +pub struct TypeSpelling { + /// Each name, upper-cased and single-spaced, and the name it is compared as. + pub aliases: &'static [(&'static str, &'static str)], + /// Further aliases that apply only inside another type, such as a list's element. + pub nested_aliases: &'static [(&'static str, &'static str)], + /// Names whose parameters are dropped when they are renamed. + pub sizeless: &'static [&'static str], + /// Names whose parameterised spelling, as a whole type, the fallback cannot spell as compared. + pub sizeless_whole: &'static [&'static str], + /// Names kept as written when they have no parameters. + pub bare_kept: &'static [&'static str], + /// Types whose parameters are named fields. + pub fielded: &'static [&'static str], + /// Types whose parameters are compared as written. + pub verbatim: &'static [&'static str], + /// Whether `[]` array suffixes are part of the dialect's type syntax. + pub arrays: bool, +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index add990df0..60c4b57e2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -2,9 +2,13 @@ mod helpers; #[path = "tests/test_clickhouse_type_match.rs"] mod test_clickhouse_type_match; +#[path = "tests/test_native_arms.rs"] +mod test_native_arms; #[path = "tests/test_native_dialect.rs"] mod test_native_dialect; #[path = "tests/test_native_gap.rs"] mod test_native_gap; +#[path = "tests/test_type_spelling.rs"] +mod test_type_spelling; #[path = "tests/test_types.rs"] mod test_types; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs new file mode 100644 index 000000000..5dd5d461c --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs @@ -0,0 +1,134 @@ +use crate::analysis_route::main::native_gap::native_gap; +use crate::analysis_route::models::{LocatedGap, SnowflakeDialect, SqliteDialect}; +use crate::analysis_route::tests::test_types::ArmGapTestCase; +use crate::assembly::project::models::SyntaxCheck; + +#[test] +fn given_model_sql_when_probing_a_dialects_parser_then_only_sql_it_reads_whole_has_no_gap() { + let test_cases = [ + ArmGapTestCase { + dialect: &SqliteDialect, + description: "a query SQLite reads whole", + sql: "SELECT 1 AS a FROM t WHERE region = @@@region", + placeholders: &[("region", "'eu'")], + expected_gap: None, + }, + ArmGapTestCase { + dialect: &SqliteDialect, + description: "a UDF call's stub reads as one unquoted name", + sql: "SELECT __udf(\"scaled_amount\")(amount, 2) AS scaled FROM t", + placeholders: &[], + expected_gap: None, + }, + ArmGapTestCase { + dialect: &SqliteDialect, + description: "cursor bounds read as stub calls", + sql: "SELECT * FROM t WHERE at >= __cursor_start() AND at < __cursor_end( )", + placeholders: &[], + expected_gap: None, + }, + ArmGapTestCase { + dialect: &SqliteDialect, + description: "a syntax error is located where the parser stops", + sql: "SELECT 1 FROM FROM", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "near \"FROM\": syntax error".to_owned(), + message: "near \"FROM\": syntax error".to_owned(), + line: 1, + column: 15, + syntax: true, + }), + }, + ArmGapTestCase { + dialect: &SqliteDialect, + description: "PIVOT, which SQLite reads as an alias, fails at its parenthesis as sqlite3 does", + sql: "SELECT a\nFROM t\n PIVOT (sum(x) FOR y IN (1))", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "near \"(\": syntax error".to_owned(), + message: "near \"(\": syntax error".to_owned(), + line: 3, + column: 9, + syntax: true, + }), + }, + ArmGapTestCase { + dialect: &SqliteDialect, + description: "a second statement is not read whole", + sql: "SELECT 1;\nSELECT 2", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "more than one statement".to_owned(), + message: "more than one statement".to_owned(), + line: 1, + column: 1, + syntax: false, + }), + }, + ArmGapTestCase { + dialect: &SnowflakeDialect, + description: "a QUALIFY query Snowflake reads whole", + sql: "SELECT a FROM t QUALIFY row_number() OVER (PARTITION BY a ORDER BY b) = 1", + placeholders: &[], + expected_gap: None, + }, + ArmGapTestCase { + dialect: &SnowflakeDialect, + description: "a UDF call's stub reads as one unquoted name", + sql: "SELECT __udf(\"scaled_amount\")(amount, 2) AS scaled FROM t", + placeholders: &[], + expected_gap: None, + }, + ArmGapTestCase { + dialect: &SnowflakeDialect, + description: "cursor bounds read as stub calls", + sql: "SELECT * FROM t WHERE at >= __cursor_start() AND at < __cursor_end( )", + placeholders: &[], + expected_gap: None, + }, + ArmGapTestCase { + dialect: &SnowflakeDialect, + description: "a syntax error is located where the parser stops", + sql: "SELECT 1 FROM FROM", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "Expected table name or subquery, got From".to_owned(), + message: "Expected table name or subquery, got From".to_owned(), + line: 1, + column: 15, + syntax: false, + }), + }, + ArmGapTestCase { + dialect: &SnowflakeDialect, + description: "a second statement is not read whole", + sql: "SELECT 1;\nSELECT 2", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "more than one statement".to_owned(), + message: "more than one statement".to_owned(), + line: 1, + column: 1, + syntax: false, + }), + }, + ]; + for test_case in test_cases { + let check = SyntaxCheck { + sql: test_case.sql.to_owned(), + placeholders: test_case + .placeholders + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) + .collect(), + }; + assert_eq!( + native_gap(test_case.dialect, &check), + Ok(test_case.expected_gap), + "{}: {}", + test_case.dialect.name(), + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index 9f8c47005..9869b0f5d 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -1,5 +1,7 @@ use crate::analysis_route::main::unavailable_analysis::unavailable_analysis; -use crate::analysis_route::models::{NativeAnalysis, TypeMatch}; +use crate::analysis_route::models::{ + DuckDbDialect, NativeAnalysis, SnowflakeDialect, SqliteDialect, TypeMatch, +}; use crate::analysis_route::tests::helpers::{analysed, resource_columns}; use crate::analysis_route::tests::test_types::{ CataloguedDialect, ColumnTestCase, FindingTestCase, LineageTestCase, MacroBodyTestCase, @@ -182,6 +184,34 @@ fn given_findings_when_analysing_then_they_take_sqlbuild_codes_and_positions() { extension_functions: &["md_user_info"], expected_findings: &[("B101", None)], }, + FindingTestCase { + description: "Snowflake's arm reports an unknown column at its line and column", + dialect: &SnowflakeDialect, + sql: "SELECT id,\n nope\nFROM orders", + extension_functions: &[], + expected_findings: &[("B002", Some((2, 3)))], + }, + FindingTestCase { + description: "DuckDB's arm reports an unknown column at its line and column", + dialect: &DuckDbDialect, + sql: "SELECT id,\n nope\nFROM orders", + extension_functions: &[], + expected_findings: &[("B002", Some((2, 3)))], + }, + FindingTestCase { + description: "SQLite's arm reports an unknown column at its line and column", + dialect: &SqliteDialect, + sql: "SELECT id,\n nope\nFROM orders", + extension_functions: &[], + expected_findings: &[("B002", Some((2, 3)))], + }, + FindingTestCase { + description: "DuckDB's arm reports a misspelt function as B101", + dialect: &DuckDbDialect, + sql: "SELECT lenght(status) AS n FROM orders", + extension_functions: &[], + expected_findings: &[("B101", Some((1, 8)))], + }, ]; for test_case in test_cases { let analysis: NativeAnalysis = analysed( diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 5c395c6d1..592c915fb 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -8,7 +8,7 @@ use crate::analysis_route::tests::test_types::{ use crate::assembly::project::models::SyntaxCheck; #[test] -fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { +fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_legacy_path() { let test_cases = [ RouteTestCase { description: "postgres", @@ -18,12 +18,12 @@ fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { RouteTestCase { description: "snowflake", dialect: Some("snowflake"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "duckdb", dialect: Some("duckdb"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "sqlite", diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs new file mode 100644 index 000000000..52079e2cf --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs @@ -0,0 +1,275 @@ +use crate::analysis_route::models::{DuckDbDialect, SnowflakeDialect, SqliteDialect}; +use crate::analysis_route::tests::test_types::TypeSpellingTestCase; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::models::{NormalizedType, TypeFamily}; + +#[test] +fn given_a_type_spelling_when_a_dialect_normalises_it_then_it_is_polyglots_type_or_none() { + let test_cases = [ + TypeSpellingTestCase { + description: "a DuckDB integer keeps Polyglot's INT", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "INTEGER", + expected_type: Some(("INT", TypeFamily::Integer)), + }, + TypeSpellingTestCase { + description: "BLOB and VARBINARY swap names as Polyglot swaps them", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "BLOB", + expected_type: Some(("VARBINARY", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "VARBINARY becomes BLOB", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "VARBINARY", + expected_type: Some(("BLOB", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a sized VARCHAR keeps its length as a whole type", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "VARCHAR(10)", + expected_type: Some(("VARCHAR(10)", TypeFamily::String)), + }, + TypeSpellingTestCase { + description: "a list element is renamed with its length", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "VARCHAR(10)[]", + expected_type: Some(("TEXT(10)[]", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a field named like a type keeps its name", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "STRUCT(integer INTEGER)", + expected_type: Some(("STRUCT(INTEGERINT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a nested CHAR loses its length", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "STRUCT(a CHAR(3))", + expected_type: Some(("STRUCT(ATEXT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a whole CHAR(n) has no comparable name and stays unknown", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "CHAR(3)", + expected_type: None, + }, + TypeSpellingTestCase { + description: "LIST(...) is compared as written", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "LIST(INTEGER)", + expected_type: Some(("LIST(INTEGER)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a bare ROW keeps its name", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "ROW", + expected_type: Some(("ROW", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "ROW with fields is a STRUCT", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "ROW(a INTEGER)", + expected_type: Some(("STRUCT(AINT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "DATETIME is renamed only inside another type", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "STRUCT(a datetime)", + expected_type: Some(("STRUCT(ATIMESTAMP)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a whole DATETIME keeps its name", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "datetime", + expected_type: Some(("DATETIME", TypeFamily::Datetime)), + }, + TypeSpellingTestCase { + description: "a timestamp list, which the fallback would call a timestamp, stays unknown", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "TIMESTAMP[]", + expected_type: None, + }, + TypeSpellingTestCase { + description: "a struct holding a timestamp is not a timestamp", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "STRUCT(a TIMESTAMP, b VARCHAR)", + expected_type: Some(("STRUCT(ATIMESTAMP,BTEXT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a map key keeps its renamed length", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "MAP(VARCHAR(5), INTEGER)", + expected_type: Some(("MAP(TEXT(5),INT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a multi-word name is one alias", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "TIMESTAMP WITH TIME ZONE", + expected_type: Some(("TIMESTAMPTZ", TypeFamily::Timestamp)), + }, + TypeSpellingTestCase { + description: "spaced decimal parameters are kept", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "DECIMAL(12, 2)", + expected_type: Some(("DECIMAL(12,2)", TypeFamily::Decimal)), + }, + TypeSpellingTestCase { + description: "text after the type is not a type", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "VARCHAR NOT NULL", + expected_type: None, + }, + TypeSpellingTestCase { + description: "REAL is FLOAT", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "REAL", + expected_type: Some(("FLOAT", TypeFamily::Float)), + }, + TypeSpellingTestCase { + description: "an integer is NUMBER(38,0)", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "INTEGER", + expected_type: Some(("DECIMAL(38,0)", TypeFamily::Decimal)), + }, + TypeSpellingTestCase { + description: "a nested INTEGER is INT", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "OBJECT(a INTEGER)", + expected_type: Some(("OBJECT(AINT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a nested TIMESTAMP_NTZ loses its underscore", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "OBJECT(a TIMESTAMP_NTZ)", + expected_type: Some(("OBJECT(ATIMESTAMPNTZ)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "TIMESTAMP_NTZ drops its precision", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "TIMESTAMP_NTZ(9)", + expected_type: Some(("TIMESTAMP_NTZ", TypeFamily::Timestamp)), + }, + TypeSpellingTestCase { + description: "TIMESTAMP(9), which Polyglot keeps sized, stays unknown", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "TIMESTAMP(9)", + expected_type: None, + }, + TypeSpellingTestCase { + description: "array brackets are not Snowflake type syntax", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "INTEGER[]", + expected_type: None, + }, + TypeSpellingTestCase { + description: "TEXT is the longest VARCHAR", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "TEXT", + expected_type: Some(("VARCHAR(16777216)", TypeFamily::String)), + }, + TypeSpellingTestCase { + description: "a nested TEXT is VARCHAR", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "ARRAY(TEXT)", + expected_type: Some(("ARRAY(VARCHAR)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "CHARACTER VARYING is VARCHAR with its length", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "CHARACTER VARYING(20)", + expected_type: Some(("VARCHAR(20)", TypeFamily::String)), + }, + TypeSpellingTestCase { + description: "DOUBLE is REAL", + dialect: "sqlite", + normalize: |spelling| SqliteDialect.normalize_type(spelling), + spelling: "DOUBLE", + expected_type: Some(("REAL", TypeFamily::Float)), + }, + TypeSpellingTestCase { + description: "INT is INTEGER", + dialect: "sqlite", + normalize: |spelling| SqliteDialect.normalize_type(spelling), + spelling: "INT", + expected_type: Some(("INTEGER", TypeFamily::Integer)), + }, + TypeSpellingTestCase { + description: "a sized VARCHAR keeps its length", + dialect: "sqlite", + normalize: |spelling| SqliteDialect.normalize_type(spelling), + spelling: "VARCHAR(255)", + expected_type: Some(("VARCHAR(255)", TypeFamily::String)), + }, + TypeSpellingTestCase { + description: "INT(11), which Polyglot keeps sized, stays unknown", + dialect: "sqlite", + normalize: |spelling| SqliteDialect.normalize_type(spelling), + spelling: "INT(11)", + expected_type: None, + }, + TypeSpellingTestCase { + description: "a type name outside ASCII is one word, upper-cased as Python does", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "téxt", + expected_type: Some(("T\u{c9}XT", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "a bare struct field outside ASCII is quoted, as Polyglot quotes it", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "STRUCT(größe INTEGER)", + expected_type: Some(("STRUCT(\"GRÖSSE\"INT)", TypeFamily::Other)), + }, + TypeSpellingTestCase { + description: "NUMERIC is DECIMAL", + dialect: "sqlite", + normalize: |spelling| SqliteDialect.normalize_type(spelling), + spelling: "NUMERIC(10,2)", + expected_type: Some(("DECIMAL(10,2)", TypeFamily::Decimal)), + }, + ]; + for test_case in test_cases { + let found: Option<(String, TypeFamily)> = (test_case.normalize)(test_case.spelling) + .map(|normalized: NormalizedType| (normalized.normalized_name, normalized.family)); + assert_eq!( + found, + test_case + .expected_type + .map(|(name, family)| (name.to_owned(), family)), + "{}: {}", + test_case.dialect, + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 95d2188b3..5495251b2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -6,7 +6,7 @@ use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; use crate::analysis_route::models::{LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; -use crate::type_system::models::NormalizedType; +use crate::type_system::models::{NormalizedType, TypeFamily}; pub(super) struct NativeGapTestCase { pub(super) description: &'static str, @@ -181,3 +181,19 @@ pub(super) struct StarTestCase { /// Whether it projects a star, whether every star column is known, and the column count. pub(super) expected_star: (bool, bool, usize), } + +pub(super) struct ArmGapTestCase { + pub(super) dialect: &'static dyn NativeDialect, + pub(super) description: &'static str, + pub(super) sql: &'static str, + pub(super) placeholders: &'static [(&'static str, &'static str)], + pub(super) expected_gap: Option, +} + +pub(super) struct TypeSpellingTestCase { + pub(super) description: &'static str, + pub(super) dialect: &'static str, + pub(super) normalize: fn(&str) -> Option, + pub(super) spelling: &'static str, + pub(super) expected_type: Option<(&'static str, TypeFamily)>, +} From 02d2101d894d9143353bfbef846fa9fd1554c865 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:47:04 +0000 Subject: [PATCH 012/160] test(differential): keep the seed opt-out model unreadable to the native DuckDB parser The native DuckDB parser reads WHERE id = ANY [1, 2], which DuckDB runs, so the seed's sql_analysis false model no longer needed its opt-out and require_sql_analysis rejected it. A NEAREST join is valid DuckDB that the parser keeps as a gap, and the Postgres and Snowflake parsers refuse it too. Only the fixture's SQL text changes in the six seed goldens. --- scripts/compiler_differential/classes/analysis_features.py | 6 +++++- tests/goldens/compiler/seed/0-postgres.json | 6 +++--- tests/goldens/compiler/seed/0-snowflake.json | 6 +++--- tests/goldens/compiler/seed/0.json | 6 +++--- tests/goldens/compiler/seed/4.json | 6 +++--- tests/goldens/compiler/seed/8.json | 6 +++--- tests/goldens/compiler/seed/9.json | 6 +++--- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/scripts/compiler_differential/classes/analysis_features.py b/scripts/compiler_differential/classes/analysis_features.py index 67cc36c6b..a3cd3a6c4 100644 --- a/scripts/compiler_differential/classes/analysis_features.py +++ b/scripts/compiler_differential/classes/analysis_features.py @@ -314,7 +314,11 @@ def _analysis_opt_out(self) -> None: name=f"unanalysed_{base.name}", description=f"Copy of {base.name} that SQL analysis cannot parse", header=" sql_analysis false,\n", - body=f'SELECT id, amount\nFROM __ref("{base.name}")\nWHERE id = ANY [1, 2]\n', + body=( + "SELECT a.id, a.amount\n" + f'FROM __ref("{base.name}") AS a\n' + f'JOIN __ref("{base.name}") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)\n' + ), ) self.settings_lines.append("require_sql_analysis = true") diff --git a/tests/goldens/compiler/seed/0-postgres.json b/tests/goldens/compiler/seed/0-postgres.json index 47e554132..021cb0bea 100644 --- a/tests/goldens/compiler/seed/0-postgres.json +++ b/tests/goldens/compiler/seed/0-postgres.json @@ -173,9 +173,9 @@ "" ], "models/analysis/unanalysed_stg_fulfillment_0.sql": [ - "SELECT id, amount", - "FROM __ref(\"stg_fulfillment_0\")", - "WHERE id = ANY [1, 2]", + "SELECT a.id, a.amount", + "FROM __ref(\"stg_fulfillment_0\") AS a", + "JOIN __ref(\"stg_fulfillment_0\") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)", "" ], "models/fulfillment/marts/fulfillment_summary_0.sql": [ diff --git a/tests/goldens/compiler/seed/0-snowflake.json b/tests/goldens/compiler/seed/0-snowflake.json index 181fc5142..155ae3efc 100644 --- a/tests/goldens/compiler/seed/0-snowflake.json +++ b/tests/goldens/compiler/seed/0-snowflake.json @@ -174,9 +174,9 @@ "" ], "models/analysis/unanalysed_stg_fulfillment_0.sql": [ - "SELECT id, amount", - "FROM __ref(\"stg_fulfillment_0\")", - "WHERE id = ANY [1, 2]", + "SELECT a.id, a.amount", + "FROM __ref(\"stg_fulfillment_0\") AS a", + "JOIN __ref(\"stg_fulfillment_0\") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)", "" ], "models/fulfillment/marts/fulfillment_summary_0.sql": [ diff --git a/tests/goldens/compiler/seed/0.json b/tests/goldens/compiler/seed/0.json index 9e8f4551c..92bd813db 100644 --- a/tests/goldens/compiler/seed/0.json +++ b/tests/goldens/compiler/seed/0.json @@ -439,9 +439,9 @@ "" ], "models/analysis/unanalysed_stg_sales_0.sql": [ - "SELECT id, amount", - "FROM __ref(\"stg_sales_0\")", - "WHERE id = ANY [1, 2]", + "SELECT a.id, a.amount", + "FROM __ref(\"stg_sales_0\") AS a", + "JOIN __ref(\"stg_sales_0\") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)", "" ], "models/features/append_stg_fulfillment_1.sql": [ diff --git a/tests/goldens/compiler/seed/4.json b/tests/goldens/compiler/seed/4.json index 66eb72f71..6680d7699 100644 --- a/tests/goldens/compiler/seed/4.json +++ b/tests/goldens/compiler/seed/4.json @@ -152,9 +152,9 @@ "" ], "models/analysis/unanalysed_stg_support_0.sql": [ - "SELECT id, amount", - "FROM __ref(\"stg_support_0\")", - "WHERE id = ANY [1, 2]", + "SELECT a.id, a.amount", + "FROM __ref(\"stg_support_0\") AS a", + "JOIN __ref(\"stg_support_0\") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)", "" ], "models/features/append_stg_support_0.sql": [ diff --git a/tests/goldens/compiler/seed/8.json b/tests/goldens/compiler/seed/8.json index 7b4b666cb..b897cf2d2 100644 --- a/tests/goldens/compiler/seed/8.json +++ b/tests/goldens/compiler/seed/8.json @@ -148,9 +148,9 @@ "" ], "models/analysis/unanalysed_stg_support_0.sql": [ - "SELECT id, amount", - "FROM __ref(\"stg_support_0\")", - "WHERE id = ANY [1, 2]", + "SELECT a.id, a.amount", + "FROM __ref(\"stg_support_0\") AS a", + "JOIN __ref(\"stg_support_0\") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)", "" ], "models/features/append_stg_support_0.sql": [ diff --git a/tests/goldens/compiler/seed/9.json b/tests/goldens/compiler/seed/9.json index 2a49c5a58..5c45608e5 100644 --- a/tests/goldens/compiler/seed/9.json +++ b/tests/goldens/compiler/seed/9.json @@ -170,9 +170,9 @@ "" ], "models/analysis/unanalysed_stg_support_1.sql": [ - "SELECT id, amount", - "FROM __ref(\"stg_support_1\")", - "WHERE id = ANY [1, 2]", + "SELECT a.id, a.amount", + "FROM __ref(\"stg_support_1\") AS a", + "JOIN __ref(\"stg_support_1\") AS b NEAREST 1 BY DISTANCE abs(a.amount - b.amount)", "" ], "models/billing/marts/billing_summary_0.sql": [ From a003a5cf07741bd409180e238aed944234ee7dc5 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:55:20 +0000 Subject: [PATCH 013/160] fix(analysis): let a native route's parser decide syntax, after the reference scan On a native analysis route the gap gate reads the model SQL and hooks whole, so assembly no longer reruns Polyglot's syntax validation there: a model the native parser reads gets no Polyglot P001. The gate now runs after reference scanning, so a located scanner error wins over a raw normalisation failure, and a model whose reference calls already failed P012 gets no P026. The Polyglot route keeps its order and validation. --- .../_helpers/compile_driver/assembly_models.rs | 10 +++++++++- .../bindings/_helpers/input_loops/model_loop.rs | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_models.rs b/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_models.rs index af5593c48..99db37ef0 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_models.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_models.rs @@ -4,6 +4,8 @@ use pyo3::IntoPyObject; use pyo3::IntoPyObjectExt; use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyResult, Python}; use pyo3::types::{PyDict, PyDictMethods, PyFrozenSetMethods, PyList, PyString, PyTuple}; +use sqlbuild_analysis::analysis_route::main::analysis_route::analysis_route; +use sqlbuild_analysis::analysis_route::models::AnalysisRoute; use crate::bindings::_helpers::analysis_session::model_analyses::ModelAnalyses; use crate::bindings::_helpers::analysis_session::profile_inputs::profile_dialect; @@ -116,7 +118,13 @@ fn compiled_model<'py>( syntax()?, ) }; - let validates: bool = input_validated && !syntax_validated; + let analysis_dialect: Option = + native_field(&context.profile, "sql_analysis_dialect")?.extract()?; + let legacy_route: bool = matches!( + analysis_route(analysis_dialect.as_deref()), + AnalysisRoute::Legacy + ); + let validates: bool = input_validated && !syntax_validated && legacy_route; if request.sql_validation_enabled && validates { for hook_name in HOOK_NAMES { validate_hook_sql_syntax( diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs index b75a525fa..731e152bd 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs @@ -717,8 +717,10 @@ impl NativeModelLoop { relative_path, contents, }; - let (analysed, rejected_opt_out, analysis_skipped) = - self.analysis(context, &opt_out_model)?; + let legacy_gate = match self.route { + AnalysisRoute::Legacy => Some(self.analysis(context, &opt_out_model, true)?), + AnalysisRoute::Native(_) | AnalysisRoute::Unavailable => None, + }; let scanned = match extract_sql_references(&query_sql, &self.syntax) { ReferenceExtraction::Extracted(scanned) => scanned, ReferenceExtraction::Failed(failure) => { @@ -751,6 +753,10 @@ impl NativeModelLoop { contents, }, )?; + let (analysed, rejected_opt_out, analysis_skipped) = match legacy_gate { + Some(gate) => gate, + None => self.analysis(context, &opt_out_model, scanned.invalid_calls.is_empty())?, + }; let analysis: (bool, Option>) = ( analysed && scanned.invalid_calls.is_empty(), rejected_opt_out, @@ -818,11 +824,12 @@ impl NativeModelLoop { }) } - /// Whether the model's SQL is analysed, its rejected opt-out, and whether its parser skipped it. + /// Analysed, rejected opt-out, skipped; a native route reads no SQL whose calls P012 failed. fn analysis<'py>( &self, context: &LoopContext<'_, 'py>, model: &OptOutModel<'_, 'py>, + calls_valid: bool, ) -> PyResult<(bool, Option>, bool)> { let mut skipped: bool = false; let (analysed, rejected) = @@ -836,6 +843,9 @@ impl NativeModelLoop { } AnalysisGate::Enabled => match self.route { AnalysisRoute::Legacy => (true, None), + AnalysisRoute::Native(_) | AnalysisRoute::Unavailable if !calls_valid => { + (false, None) + } AnalysisRoute::Native(dialect) => { let read: bool = skip_unreadable(self.settings, dialect, context, model)?; skipped = !read; From 6472385ce927cb18384692ba89fe28fcc80be486 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:32:57 +0000 Subject: [PATCH 014/160] fix(analysis): native type spellings take Polyglot's other dialects' names A full comparison with Polyglot's normalize_type showed the DuckDB, Snowflake and SQLite tables missing names from other dialects, such as TIMESTAMP_NTZ on DuckDB, and multi-word names such as TIMESTAMP WITHOUT TIME ZONE, which came back unknown. Two spellings Polyglot treats as one type could then compare as different and raise K002. Every bare name now matches Polyglot except Snowflake's BIT, whose Polyglot family no alias can give. Type names may span five words. The deep-nesting fallback-columns case drops to 100 calls: the native DuckDB parser's nesting guard refuses 200, so that model is skipped with P026. --- .../src/analysis_route/constants.rs | 51 ++++++++++++++++++- .../tests/test_type_spelling.rs | 28 ++++++++++ .../test_native_query_columns.py | 4 +- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index e3724304e..020c5a4c6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -18,8 +18,8 @@ pub(crate) const SQLITE_DIALECT: &str = "sqlite"; pub(crate) const UNQUOTED_STUB_PREFIX: &str = "__sqlbuild_"; /// The word that makes SQLBuild's type fallback call a type it cannot split into name and flat parameters a timestamp. pub(crate) const FALLBACK_TIMESTAMP_WORD: &str = "TIMESTAMP"; -/// The most words a type name spans, as in `TIMESTAMP WITH TIME ZONE`. -pub(crate) const MAX_TYPE_NAME_WORDS: usize = 4; +/// The most words a type name spans, as in `TIMESTAMP WITH LOCAL TIME ZONE`. +pub(crate) const MAX_TYPE_NAME_WORDS: usize = 5; /// The construct of a byte the scanner reads as no token, blank or comment. pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; /// The dialect a project with no analysis dialect is named by. @@ -52,17 +52,26 @@ pub(crate) const DUCKDB_TYPE_SPELLING: TypeSpelling = TypeSpelling { ("BOOL", "BOOLEAN"), ("BYTEA", "BLOB"), ("CHAR", "TEXT"), + ("CHAR VARYING", "TEXT"), ("CHARACTER", "TEXT"), ("CHARACTER VARYING", "TEXT"), ("DOUBLE PRECISION", "DOUBLE"), ("HUGEINT", "INT128"), ("INT64", "BIGINT"), ("INTEGER", "INT"), + ("NCHAR VARYING", "TEXT"), + ("NTEXT", "TEXT"), ("NUMERIC", "DECIMAL"), ("ROW", "STRUCT"), ("STRING", "TEXT"), ("TIME WITH TIME ZONE", "TIMETZ"), + ("TIME WITHOUT TIME ZONE", "TIME"), + ("TIMESTAMP WITH LOCAL TIME ZONE", "TIMESTAMPLTZ"), ("TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ"), + ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"), + ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"), + ("TIMESTAMP_NTZ", "TIMESTAMPNTZ"), + ("TIMESTAMP_TZ", "TIMESTAMPTZ"), ("UHUGEINT", "UINT128"), ("UINT16", "USMALLINT"), ("UINT32", "UINTEGER"), @@ -83,18 +92,34 @@ pub(crate) const DUCKDB_TYPE_SPELLING: TypeSpelling = TypeSpelling { pub(crate) const SNOWFLAKE_TYPE_SPELLING: TypeSpelling = TypeSpelling { aliases: &[ ("BINARY VARYING", "VARBINARY"), + ("BOOL", "BOOLEAN"), + ("BYTEA", "VARBINARY"), ("CHAR VARYING", "VARCHAR"), ("CHARACTER", "CHAR"), ("CHARACTER VARYING", "VARCHAR"), ("DOUBLE PRECISION", "DOUBLE"), + ("HUGEINT", "INT128"), ("INTEGER", "INT"), + ("JSON", "VARIANT"), ("NCHAR VARYING", "VARCHAR"), + ("NTEXT", "VARCHAR"), ("NUMERIC", "DECIMAL"), ("REAL", "FLOAT"), ("TEXT", "VARCHAR"), + ("TIME WITH TIME ZONE", "TIMEWITHTIMEZONE"), + ("TIME WITHOUT TIME ZONE", "TIME"), + ("TIMESTAMP WITH LOCAL TIME ZONE", "TIMESTAMPLTZ"), + ("TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ"), + ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMPNTZ"), ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"), ("TIMESTAMP_NTZ", "TIMESTAMPNTZ"), ("TIMESTAMP_TZ", "TIMESTAMPTZ"), + ("TIMETZ", "TIMEWITHTIMEZONE"), + ("UHUGEINT", "UINT128"), + ("UINT16", "USMALLINT"), + ("UINT32", "UINTEGER"), + ("UINT64", "UBIGINT"), + ("UINT8", "UTINYINT"), ], nested_aliases: &[], sizeless: &[], @@ -107,14 +132,36 @@ pub(crate) const SNOWFLAKE_TYPE_SPELLING: TypeSpelling = TypeSpelling { /// SQLite's type names as SQLBuild compares them. pub(crate) const SQLITE_TYPE_SPELLING: TypeSpelling = TypeSpelling { aliases: &[ + ("BINARY", "BLOB"), ("BOOL", "BOOLEAN"), + ("BYTEA", "BLOB"), ("CHAR", "TEXT"), + ("CHAR VARYING", "TEXT"), ("CHARACTER", "TEXT"), + ("CHARACTER VARYING", "TEXT"), ("DOUBLE", "REAL"), ("DOUBLE PRECISION", "REAL"), + ("HUGEINT", "INT128"), ("INT", "INTEGER"), + ("INT64", "BIGINT"), + ("NCHAR VARYING", "TEXT"), + ("NTEXT", "TEXT"), ("NUMERIC", "DECIMAL"), ("SMALLINT", "INTEGER"), + ("TIME WITH TIME ZONE", "TIMEWITHTIMEZONE"), + ("TIME WITHOUT TIME ZONE", "TIME"), + ("TIMESTAMP WITH LOCAL TIME ZONE", "TIMESTAMPLTZ"), + ("TIMESTAMP WITH TIME ZONE", "TIMESTAMPTZ"), + ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"), + ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"), + ("TIMESTAMP_NTZ", "TIMESTAMPNTZ"), + ("TIMESTAMP_TZ", "TIMESTAMPTZ"), + ("TIMETZ", "TIMEWITHTIMEZONE"), + ("UHUGEINT", "UINT128"), + ("UINT16", "USMALLINT"), + ("UINT32", "UINTEGER"), + ("UINT64", "UBIGINT"), + ("UINT8", "UTINYINT"), ("VARBINARY", "BLOB"), ("VARCHAR", "TEXT"), ], diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs index 52079e2cf..754b1efe5 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_type_spelling.rs @@ -258,6 +258,34 @@ fn given_a_type_spelling_when_a_dialect_normalises_it_then_it_is_polyglots_type_ spelling: "NUMERIC(10,2)", expected_type: Some(("DECIMAL(10,2)", TypeFamily::Decimal)), }, + TypeSpellingTestCase { + description: "a Snowflake timestamp name is the same type as Polyglot's compact spelling", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "TIMESTAMP_NTZ", + expected_type: Some(("TIMESTAMPNTZ", TypeFamily::Timestamp)), + }, + TypeSpellingTestCase { + description: "a five-word type name is read whole", + dialect: "duckdb", + normalize: |spelling| DuckDbDialect.normalize_type(spelling), + spelling: "timestamp with local time zone", + expected_type: Some(("TIMESTAMPLTZ", TypeFamily::Timestamp)), + }, + TypeSpellingTestCase { + description: "a timestamp without time zone is TIMESTAMP_NTZ", + dialect: "snowflake", + normalize: |spelling| SnowflakeDialect.normalize_type(spelling), + spelling: "TIMESTAMP WITHOUT TIME ZONE", + expected_type: Some(("TIMESTAMP_NTZ", TypeFamily::Timestamp)), + }, + TypeSpellingTestCase { + description: "CHARACTER VARYING is TEXT", + dialect: "sqlite", + normalize: |spelling| SqliteDialect.normalize_type(spelling), + spelling: "CHARACTER VARYING", + expected_type: Some(("TEXT", TypeFamily::String)), + }, ]; for test_case in test_cases { let found: Option<(String, TypeFamily)> = (test_case.normalize)(test_case.spelling) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py index 8553dbd30..07c8955d1 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py @@ -40,8 +40,8 @@ expected_columns=(("a", None, "non_null"),), ), FallbackColumnsTestCase( - description="calls nested 200 deep", - query_sql="SELECT " + "f(" * 200 + "1" + ")" * 200 + " AS deep", + description="calls nested 100 deep", + query_sql="SELECT " + "f(" * 100 + "1" + ")" * 100 + " AS deep", expected_columns=(("deep", None, "unknown"),), ), ], From 382d3cf7ba1f5264409282377c2102fb4afbb66c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:38:07 +0000 Subject: [PATCH 015/160] feat(analysis): analyse models on a native route with the dialect's own parser A wave whose dialect routes natively now analyses each model in parallel on the analysis pool through the dialect's analyse, with the session's schema and nullability, instead of Polyglot. Placeholder defaults are filled before the parse, SQL macro checks still run on the outcome, and the Polyglot completion and cache attachment steps are skipped for native routes. --- .../src/analysis_route/tests.rs | 2 + .../tests/test_native_outcome.rs | 82 +++++++++++++ .../src/analysis_route/tests/test_types.rs | 9 ++ .../_helpers/analysis_cache.rs | 4 + .../assembly/analysis_session/_helpers/mod.rs | 1 + .../_helpers/native_outcome.rs | 112 ++++++++++++++++++ .../analysis_session/_helpers/session.rs | 46 ++++++- .../src/assembly/analysis_session/models.rs | 23 ++++ 8 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index add990df0..0b05ee5ff 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -6,5 +6,7 @@ mod test_clickhouse_type_match; mod test_native_dialect; #[path = "tests/test_native_gap.rs"] mod test_native_gap; +#[path = "tests/test_native_outcome.rs"] +mod test_native_outcome; #[path = "tests/test_types.rs"] mod test_types; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs new file mode 100644 index 000000000..59cb6fdaf --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -0,0 +1,82 @@ +use crate::analysis_route::tests::test_types::{CataloguedDialect, NativeOutcomeTestCase}; +use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; +use crate::assembly::analysis_session::_helpers::native_outcome::native_outcome; +use crate::assembly::analysis_session::models::{ModelOutcome, ModelRequest}; +use crate::assembly::analysis_session::types::Shapes; + +#[test] +fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_follow_them() { + let test_cases = [ + NativeOutcomeTestCase { + description: "a closed source expands a star with its types and known nullability", + sql: "SELECT * FROM raw_orders", + shape: &[("order_id", "INTEGER"), ("status", "VARCHAR")], + expected: &[ + "order_id INTEGER non_null", + "status VARCHAR nullable", + "succeeded=true star=true/true", + ], + }, + NativeOutcomeTestCase { + description: "a relation the session has no shape for is unknown, not empty", + sql: "SELECT * FROM raw_orders", + shape: &[], + expected: &["succeeded=true star=true/false"], + }, + NativeOutcomeTestCase { + description: "SQL the dialect cannot read fails the analysis", + sql: "SELECT FROM WHERE", + shape: &[("order_id", "INTEGER")], + expected: &["succeeded=false star=false/false"], + }, + ]; + + for test_case in test_cases { + let shape: Vec<(String, String)> = test_case + .shape + .iter() + .map(|(name, data_type)| ((*name).to_owned(), (*data_type).to_owned())) + .collect(); + let schema: Shapes = vec![("raw_orders".to_owned(), shape)]; + let nullability: ShapeTable = ShapeTable::from_shapes(&vec![( + "raw_orders".to_owned(), + vec![ + ("order_id".to_owned(), "non_null".to_owned()), + ("status".to_owned(), "nullable".to_owned()), + ], + )]); + let model: ModelRequest = ModelRequest { + name: "orders".to_owned(), + query_sql: test_case.sql.to_owned(), + required_names: vec!["raw_orders".to_owned()], + ..ModelRequest::default() + }; + let (outcome, failure): (ModelOutcome, Option) = + native_outcome(&CataloguedDialect, &model, (schema, &nullability)); + let analysis = &outcome.analysis; + let mut lines: Vec = analysis + .columns + .iter() + .flatten() + .map(|column| { + format!( + "{} {} {}", + column.name, + column.data_type.as_deref().unwrap_or("-"), + column.nullability + ) + }) + .collect(); + lines.push(format!( + "succeeded={} star={}/{}", + analysis.analysis_succeeded, analysis.has_star, analysis.star_resolved + )); + assert_eq!(lines, test_case.expected, "{}", test_case.description); + assert_eq!( + failure.is_some(), + !analysis.analysis_succeeded, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 95d2188b3..a6a5fdbb1 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -181,3 +181,12 @@ pub(super) struct StarTestCase { /// Whether it projects a star, whether every star column is known, and the column count. pub(super) expected_star: (bool, bool, usize), } + +pub(super) struct NativeOutcomeTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + /// `raw_orders`' shape as the session knows it; empty when it does not. + pub(super) shape: &'static [(&'static str, &'static str)], + /// Each output column as `name type nullability`, then `succeeded star=/`. + pub(super) expected: &'static [&'static str], +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs index 757a894a9..bd20efd78 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs @@ -7,6 +7,7 @@ use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; use sqlbuild_cache::store::models::NativeStore; +use crate::analysis_route::models::AnalysisRoute; use crate::assembly::analysis_session::constants::{ ANALYSIS_CACHE_FORMAT, VARINT_CONTINUATION, VARINT_MAX_BYTES, VARINT_PAYLOAD_BITS, VARINT_PAYLOAD_MASK, @@ -27,6 +28,9 @@ pub(crate) type CachedOutcome = (ModelOutcome, Option); impl AnalysisSession { /// Read and fill `store`; its environment already names the build and Python's settings. pub(crate) fn attach_cache(&mut self, store: NativeStore) { + if matches!(self.route(), AnalysisRoute::Native(_)) { + return; + } let models: usize = self.request.models.len(); self.cache = Some(AnalysisCache { store, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs index 61615bd51..b9e6bfc97 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs @@ -6,5 +6,6 @@ pub(crate) mod dict_walk; pub(crate) mod dynamic_pivot; pub(crate) mod enrichment; pub(crate) mod mappings; +pub(crate) mod native_outcome; pub(crate) mod query_columns; pub(crate) mod session; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs new file mode 100644 index 000000000..c10454aa2 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs @@ -0,0 +1,112 @@ +//! One model's outcome from its dialect's native analysis module, in the session's facts. + +use std::collections::HashMap; + +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; + +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; +use crate::assembly::analysis_session::constants::UNKNOWN_TYPE; +use crate::assembly::analysis_session::models::{ + LineageFacts, ModelAnalysis, ModelOutcome, ModelRequest, SessionSchema, +}; +use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::semantic_validation::main::normalize_native::normalize_native_sql; + +/// The model's outcome read against `schema`, its complete input shapes, and why it failed if it did. +pub(crate) fn native_outcome( + dialect: &dyn NativeDialect, + model: &ModelRequest, + (schema, nullability): (Shapes, &ShapeTable), +) -> (ModelOutcome, Option) { + let normalised: Result = + normalize_native_sql(&model.query_sql, dialect.name(), dialect.stub_prefix()); + let (cleaned_sql, analysed): (String, Result) = match normalised { + Ok(normalised) => { + let sql: String = placeholder_defaults(&normalised, &model.placeholders); + let relations: SessionSchema = session_schema(&schema, nullability); + let analysed = dialect + .analyse(&AnalysisInput { + sql: &sql, + schema: &relations, + references: &model.lineage_references, + extension_functions: &[], + }) + .map_err(|gap: ParserGap| gap.message); + (sql, analysed) + } + Err(reason) => (String::new(), Err(reason)), + }; + let failure: Option = analysed + .as_ref() + .err() + .map(|reason| format!("{}: {reason}", model.name)); + let analysis: ModelAnalysis = match analysed { + Ok(native) => ModelAnalysis { + analysis_succeeded: true, + columns: Some(native.columns), + lineage: LineageFacts::Native(native.lineage), + has_star: native.has_star, + star_resolved: native.star_resolved, + binding_diagnostics: native.diagnostics, + binding_validated: true, + }, + Err(_) => ModelAnalysis { + analysis_succeeded: false, + columns: None, + lineage: LineageFacts::Native(Vec::new()), + has_star: false, + star_resolved: false, + binding_diagnostics: Vec::new(), + binding_validated: true, + }, + }; + let outcome: ModelOutcome = ModelOutcome { + analysis, + cleaned_sql, + deferred_cleaning: None, + validated_schema: schema, + fused_binding_validated: true, + sql_macro_findings: Vec::new(), + }; + (outcome, failure) +} + +/// Each relation with a known shape, its columns typed and with the nullability the session knows. +fn session_schema(schema: &Shapes, nullability: &ShapeTable) -> SessionSchema { + let relations: HashMap> = schema + .iter() + .filter(|(_, columns)| !columns.is_empty()) + .map(|(relation, columns)| { + let known: Option<&Pairs> = nullability.get(relation); + let typed: Vec = columns + .iter() + .map(|(name, data_type)| SchemaColumn { + name: name.clone(), + quoted: false, + data_type: (!data_type.is_empty() && data_type != UNKNOWN_TYPE) + .then(|| data_type.clone()), + nullability: column_nullability(known, name), + }) + .collect(); + (relation.clone(), typed) + }) + .collect(); + SessionSchema { relations } +} + +fn column_nullability(known: Option<&Pairs>, column: &str) -> Nullability { + let spelled: Option<&str> = known.and_then(|pairs| { + pairs + .iter() + .find(|(name, _)| name == column) + .map(|(_, value)| value.as_str()) + }); + match spelled { + Some("non_null") => Nullability::NonNull, + Some("nullable") => Nullability::Nullable, + _ => Nullability::Unknown, + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 19327a8c9..80ccfc6d0 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -4,12 +4,17 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use rayon::ThreadPool; -use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use rayon::iter::{ + IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, +}; use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; +use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::models::AnalysisRoute; +use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::_helpers::analysis_cache::{ CachedOutcome, decode_outcome, encode_outcome, }; @@ -29,6 +34,7 @@ use crate::assembly::analysis_session::_helpers::mappings::{ ShapeTable, dict_from_pairs, same_keys, same_relations, }; use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; +use crate::assembly::analysis_session::_helpers::native_outcome::native_outcome; use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; use crate::assembly::analysis_session::main::resolved_cleaned_sql::resolved_cleaned_sql; use crate::assembly::analysis_session::models::{ @@ -226,6 +232,9 @@ impl AnalysisSession { /// Python's `_analyze_model_sql_requests` for one wave, less the models the cache answers. fn analyze_wave(&mut self, models: &[usize]) -> Result<(), String> { + if let AnalysisRoute::Native(dialect) = self.route() { + return self.analyze_native_wave(dialect, models); + } let schemas: Vec = models .iter() .map(|model| self.binding_schema(&self.request.models[*model])) @@ -294,6 +303,39 @@ impl AnalysisSession { self.check_sql_macros(models) } + /// Each model of the wave analysed by its dialect's native module, in parallel. + fn analyze_native_wave( + &mut self, + dialect: &'static dyn NativeDialect, + models: &[usize], + ) -> Result<(), String> { + let inputs: Vec<(usize, Shapes)> = models + .iter() + .map(|model| (*model, self.binding_schema(&self.request.models[*model]))) + .collect(); + let pool = self.catalog.native.analysis_pool()?; + let request: &SessionRequest = &self.request; + let nullability: &ShapeTable = &self.available_nullability; + let outcomes: Vec<(ModelOutcome, Option)> = pool.install(|| { + inputs + .into_par_iter() + .map(|(model, schema)| { + native_outcome(dialect, &request.models[model], (schema, nullability)) + }) + .collect() + }); + for (model, (outcome, failure)) in models.iter().zip(outcomes) { + self.failures.extend(failure); + self.outcomes[*model] = Some(outcome); + } + self.check_sql_macros(models) + } + + /// The route of the session's dialect: a native module, or Polyglot. + pub(crate) fn route(&self) -> AnalysisRoute { + analysis_route(Some(&self.request.dialect)) + } + /// Each model's SQL macro call arguments checked against its analysed types, cache hits too. fn check_sql_macros(&mut self, models: &[usize]) -> Result<(), String> { let checked: Vec = models @@ -376,7 +418,7 @@ impl AnalysisSession { /// Python's `_complete_inferred_bindings` star and type pass for one wave. fn complete_wave(&mut self, wave: usize) -> Result<(), String> { - if self.cyclic { + if self.cyclic || matches!(self.route(), AnalysisRoute::Native(_)) { return Ok(()); } let mut candidates: Vec<(usize, Shapes, bool)> = Vec::new(); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 594038d06..b3f7ff410 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -7,6 +7,8 @@ use std::sync::Arc; use rayon::ThreadPool; use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::models::NativeStore; +use sqlquack_semantics::lineage::models::SchemaColumn; +use sqlquack_semantics::lineage::types::LineageSchema; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; @@ -336,6 +338,27 @@ pub struct AnalysisCache { pub(crate) stats: AnalysisCacheStats, } +/// The relations one model reads, as the session knows them, for a native route's analysis. +#[derive(Debug, Default)] +pub(crate) struct SessionSchema { + /// Columns of each relation with a known shape, by the analysis name the query reads. + pub(crate) relations: HashMap>, +} + +impl LineageSchema for SessionSchema { + fn columns(&self, relation: &str) -> Option<&[SchemaColumn]> { + self.relations + .get(relation) + .or_else(|| { + self.relations + .iter() + .find(|(name, _)| name.eq_ignore_ascii_case(relation)) + .map(|(_, columns)| columns) + }) + .map(Vec::as_slice) + } +} + /// One compile's model analysis, advanced phase by phase. #[derive(Debug)] pub struct AnalysisSession { From 183734280b142622e565f38477c1693139590e46 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:41:01 +0000 Subject: [PATCH 016/160] fix(analysis): find a native route's stubs with the dialect's own quoting rules Finding which cursor stubs and hook parameters sit in code used one quoting policy for every dialect, with backslash escapes in single-quoted strings. A DuckDB or Postgres string ending in a literal backslash, such as 'C:\', then read as unclosed and the model failed with a P001. The scan now uses the dialect's lexical syntax, so E'' escape strings, Snowflake backslash escapes and // comments still hide stubs. The dialect syntax mapping is the same one the rules crate moves to sqlbuild-sqltext. --- .../_helpers/normalization.rs | 20 ++------- .../tests/test_normalize_batch.rs | 37 +++++++++++++++- .../semantic_validation/tests/test_types.rs | 1 + .../sql_scan/main/dialect_lexical_syntax.rs | 43 +++++++++++++++++++ .../sqlbuild-sqltext/src/sql_scan/main/mod.rs | 1 + 5 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs index febdc7140..5dd9a55e9 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/normalization.rs @@ -2,6 +2,8 @@ use crate::semantic_checks::constants::UDF_NAME_PREFIX; use regex::Regex; +use sqlbuild_sqltext::sql_scan::main::dialect_lexical_syntax::dialect_lexical_syntax; +use sqlbuild_sqltext::sql_scan::main::dialect_non_code_ranges::dialect_non_code_ranges; use sqlbuild_sqltext::sql_scan::main::matching_paren::matching_paren; use sqlbuild_sqltext::sql_scan::main::non_code_end::non_code_end; use sqlbuild_sqltext::sql_scan::models::QuotePolicy; @@ -194,7 +196,8 @@ pub(crate) fn normalize_native_sql( let udf_prefix: String = format!("{stub_prefix}udf_"); let normalized: String = normalize(sql, dialect, (&none, &none), &udf_prefix)?.sql; let stubs: &Regex = NATIVE_STUBS.as_ref()?; - let non_code: Vec<(usize, usize)> = non_code_ranges(&normalized)?; + let non_code: Vec<(usize, usize)> = + dialect_non_code_ranges(&normalized, &dialect_lexical_syntax(dialect)); let mut native: String = String::with_capacity(normalized.len()); let mut copied_until: usize = 0; for captures in stubs.captures_iter(&normalized) { @@ -226,21 +229,6 @@ pub(crate) fn normalize_native_sql( Ok(native) } -/// The byte ranges of `sql`'s strings, quoted identifiers and comments, in order. -fn non_code_ranges(sql: &str) -> Result, String> { - let mut ranges: Vec<(usize, usize)> = Vec::new(); - let mut index: usize = 0; - while index < sql.len() { - if let Some(end) = skip_non_code(sql, index)? { - ranges.push((index, end)); - index = end; - continue; - } - index += sql[index..].chars().next().map_or(1, char::len_utf8); - } - Ok(ranges) -} - pub(crate) fn normalize_dialect_sql_mapped( sql: &str, dialect: &str, diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs index bbc5a4ea3..05db7af22 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs @@ -109,34 +109,69 @@ fn given_cursor_stubs_when_normalizing_for_a_native_parser_then_only_whole_names { let test_cases = [ NativeStubTestCase { + dialect: "postgres", description: "a cursor stub in code takes the dialect's prefix", sql: "SELECT * FROM t WHERE ts > __cursor_start()", expected_sql: "SELECT * FROM t WHERE ts > __sqlbuild.cursor_start()", }, NativeStubTestCase { + dialect: "postgres", description: "a cursor stub's text inside a string literal is left as written", sql: "SELECT '__cursor_start()' AS s FROM t", expected_sql: "SELECT '__cursor_start()' AS s FROM t", }, NativeStubTestCase { + dialect: "postgres", description: "a cursor stub's text inside a comment is left as written", sql: "SELECT 1 FROM t -- __cursor_end()\n", expected_sql: "SELECT 1 FROM t -- __cursor_end()\n", }, NativeStubTestCase { + dialect: "postgres", description: "a longer name ending in a cursor stub's text is another function", sql: "SELECT x__cursor_end() FROM t", expected_sql: "SELECT x__cursor_end() FROM t", }, NativeStubTestCase { + dialect: "postgres", description: "a quoted stub beside a stub in code leaves only the code one respelt", sql: "SELECT '__cursor_end()', __cursor_end() FROM t", expected_sql: "SELECT '__cursor_end()', __sqlbuild.cursor_end() FROM t", }, + NativeStubTestCase { + dialect: "duckdb", + description: "a DuckDB backslash before a closing quote is literal, so the stub after it is code", + sql: r"SELECT 'C:\' AS folder, __cursor_end() FROM t", + expected_sql: r"SELECT 'C:\' AS folder, __sqlbuild.cursor_end() FROM t", + }, + NativeStubTestCase { + dialect: "postgres", + description: "a Postgres escape string's backslash-quoted quote keeps the stub after it quoted", + sql: r"SELECT E'O\'Brien __cursor_end()', __cursor_end() FROM t", + expected_sql: r"SELECT E'O\'Brien __cursor_end()', __sqlbuild.cursor_end() FROM t", + }, + NativeStubTestCase { + dialect: "postgres", + description: "a plain Postgres string's backslash is literal, so the stub after it is code", + sql: r"SELECT 'C:\', __cursor_end() FROM t", + expected_sql: r"SELECT 'C:\', __sqlbuild.cursor_end() FROM t", + }, + NativeStubTestCase { + dialect: "snowflake", + description: "a Snowflake backslash-quoted quote keeps the stub after it quoted", + sql: r"SELECT 'it\'s __cursor_end()', __cursor_end() FROM t", + expected_sql: r"SELECT 'it\'s __cursor_end()', __sqlbuild.cursor_end() FROM t", + }, + NativeStubTestCase { + dialect: "snowflake", + description: "a cursor stub's text inside a Snowflake // comment is left as written", + sql: "SELECT 1 FROM t // __cursor_end()\n", + expected_sql: "SELECT 1 FROM t // __cursor_end()\n", + }, ]; for test_case in test_cases { assert_eq!( - normalize_native_sql(test_case.sql, "postgres", "__sqlbuild.").as_deref(), + normalize_native_sql(test_case.sql, test_case.dialect, "__sqlbuild.").as_deref(), Ok(test_case.expected_sql), "{}", test_case.description diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs index 27fa2325e..8a51815a1 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs @@ -55,6 +55,7 @@ pub(crate) struct NormalizationBatchTestCase { pub(crate) struct NativeStubTestCase { pub(crate) description: &'static str, + pub(crate) dialect: &'static str, pub(crate) sql: &'static str, pub(crate) expected_sql: &'static str, } diff --git a/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs b/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs new file mode 100644 index 000000000..ff94e94b3 --- /dev/null +++ b/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs @@ -0,0 +1,43 @@ +//! `dialect_lexical_syntax`: the comment and quoted-text syntax of a dialect's SQL, by dialect name. + +use crate::sql_scan::models::LexicalSyntax; + +/// The comment and quoted-text syntax that SQLQuack tokenizes for `dialect`. +pub fn dialect_lexical_syntax(dialect: &str) -> LexicalSyntax { + let strings = |quotes: &[&str]| quotes.iter().map(|quote| (*quote).to_owned()).collect(); + let lines = |prefixes: &[&str]| prefixes.iter().map(|prefix| (*prefix).to_owned()).collect(); + match dialect.to_ascii_lowercase().as_str() { + "bigquery" => LexicalSyntax { + backslash_escape_quotes: strings(&["'", "\""]), + raw_string_prefix: true, + triple_quoted_strings: true, + line_comment_prefixes: lines(&["--", "#"]), + ..LexicalSyntax::default() + }, + "snowflake" => LexicalSyntax { + backslash_escape_quotes: strings(&["'"]), + line_comment_prefixes: lines(&["--", "//"]), + ..LexicalSyntax::default() + }, + "clickhouse" => LexicalSyntax { + backslash_escape_quotes: strings(&["'"]), + line_comment_prefixes: lines(&["--", "#", "//"]), + ..LexicalSyntax::default() + }, + "databricks" | "duckdb" => LexicalSyntax { + line_comment_prefixes: lines(&["--"]), + ..LexicalSyntax::default() + }, + "mssql" | "sqlserver" | "tsql" => LexicalSyntax { + nested_block_comments: true, + line_comment_prefixes: lines(&["--"]), + ..LexicalSyntax::default() + }, + _ => LexicalSyntax { + escape_string_prefix: true, + nested_block_comments: true, + line_comment_prefixes: lines(&["--"]), + ..LexicalSyntax::default() + }, + } +} diff --git a/crates/sqlbuild-sqltext/src/sql_scan/main/mod.rs b/crates/sqlbuild-sqltext/src/sql_scan/main/mod.rs index 4735e4b17..3cc4afefd 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/main/mod.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/main/mod.rs @@ -1,4 +1,5 @@ pub mod comment_end; +pub mod dialect_lexical_syntax; pub mod dialect_matching_paren; pub mod dialect_non_code_end; pub mod dialect_non_code_ranges; From aac5217e0827691918c53f4ea321f54333e1ab3c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:56:52 +0000 Subject: [PATCH 017/160] fix(analysis): keep the analysis cache on a native route A native route skipped the analysis cache, so every compile re-analysed every model and a warm run could not answer from stored outcomes. The cache now attaches on every route. A native wave analyses only the models the cache misses, a hit's cleaned SQL is the dialect's normalised SQL, and the Polyglot catalog is only prepared for the Polyglot route. --- .../_helpers/analysis_cache.rs | 4 - .../_helpers/native_outcome.rs | 43 ++++++---- .../analysis_session/_helpers/session.rs | 85 +++++++++++-------- 3 files changed, 75 insertions(+), 57 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs index bd20efd78..757a894a9 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs @@ -7,7 +7,6 @@ use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; use sqlbuild_cache::store::models::NativeStore; -use crate::analysis_route::models::AnalysisRoute; use crate::assembly::analysis_session::constants::{ ANALYSIS_CACHE_FORMAT, VARINT_CONTINUATION, VARINT_MAX_BYTES, VARINT_PAYLOAD_BITS, VARINT_PAYLOAD_MASK, @@ -28,9 +27,6 @@ pub(crate) type CachedOutcome = (ModelOutcome, Option); impl AnalysisSession { /// Read and fill `store`; its environment already names the build and Python's settings. pub(crate) fn attach_cache(&mut self, store: NativeStore) { - if matches!(self.route(), AnalysisRoute::Native(_)) { - return; - } let models: usize = self.request.models.len(); self.cache = Some(AnalysisCache { store, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs index c10454aa2..921187786 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs @@ -21,24 +21,22 @@ pub(crate) fn native_outcome( model: &ModelRequest, (schema, nullability): (Shapes, &ShapeTable), ) -> (ModelOutcome, Option) { - let normalised: Result = - normalize_native_sql(&model.query_sql, dialect.name(), dialect.stub_prefix()); - let (cleaned_sql, analysed): (String, Result) = match normalised { - Ok(normalised) => { - let sql: String = placeholder_defaults(&normalised, &model.placeholders); - let relations: SessionSchema = session_schema(&schema, nullability); - let analysed = dialect - .analyse(&AnalysisInput { - sql: &sql, - schema: &relations, - references: &model.lineage_references, - extension_functions: &[], - }) - .map_err(|gap: ParserGap| gap.message); - (sql, analysed) - } - Err(reason) => (String::new(), Err(reason)), - }; + let (cleaned_sql, analysed): (String, Result) = + match native_cleaned_sql(dialect, model) { + Ok(sql) => { + let relations: SessionSchema = session_schema(&schema, nullability); + let analysed = dialect + .analyse(&AnalysisInput { + sql: &sql, + schema: &relations, + references: &model.lineage_references, + extension_functions: &[], + }) + .map_err(|gap: ParserGap| gap.message); + (sql, analysed) + } + Err(reason) => (String::new(), Err(reason)), + }; let failure: Option = analysed .as_ref() .err() @@ -74,6 +72,15 @@ pub(crate) fn native_outcome( (outcome, failure) } +/// The model's SQL as its dialect's parser reads it: stubs respelt and placeholders filled. +pub(crate) fn native_cleaned_sql( + dialect: &dyn NativeDialect, + model: &ModelRequest, +) -> Result { + normalize_native_sql(&model.query_sql, dialect.name(), dialect.stub_prefix()) + .map(|normalised| placeholder_defaults(&normalised, &model.placeholders)) +} + /// Each relation with a known shape, its columns typed and with the nullability the session knows. fn session_schema(schema: &Shapes, nullability: &ShapeTable) -> SessionSchema { let relations: HashMap> = schema diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 80ccfc6d0..4e5371158 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -4,9 +4,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use rayon::ThreadPool; -use rayon::iter::{ - IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, -}; +use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; @@ -34,7 +32,9 @@ use crate::assembly::analysis_session::_helpers::mappings::{ ShapeTable, dict_from_pairs, same_keys, same_relations, }; use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; -use crate::assembly::analysis_session::_helpers::native_outcome::native_outcome; +use crate::assembly::analysis_session::_helpers::native_outcome::{ + native_cleaned_sql, native_outcome, +}; use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; use crate::assembly::analysis_session::main::resolved_cleaned_sql::resolved_cleaned_sql; use crate::assembly::analysis_session::models::{ @@ -232,14 +232,14 @@ impl AnalysisSession { /// Python's `_analyze_model_sql_requests` for one wave, less the models the cache answers. fn analyze_wave(&mut self, models: &[usize]) -> Result<(), String> { - if let AnalysisRoute::Native(dialect) = self.route() { - return self.analyze_native_wave(dialect, models); - } let schemas: Vec = models .iter() .map(|model| self.binding_schema(&self.request.models[*model])) .collect(); let misses: Vec = self.read_cache(models, &schemas)?; + if let AnalysisRoute::Native(dialect) = self.route() { + return self.analyze_native_wave(dialect, models, &misses, &schemas); + } let batch = Batch { dialect: &self.request.dialect, function_return_types: &self.request.function_return_types, @@ -303,30 +303,29 @@ impl AnalysisSession { self.check_sql_macros(models) } - /// Each model of the wave analysed by its dialect's native module, in parallel. + /// Each model of the wave the cache did not answer, analysed by its dialect's native module. fn analyze_native_wave( &mut self, dialect: &'static dyn NativeDialect, models: &[usize], + misses: &[usize], + schemas: &[Shapes], ) -> Result<(), String> { - let inputs: Vec<(usize, Shapes)> = models - .iter() - .map(|model| (*model, self.binding_schema(&self.request.models[*model]))) - .collect(); let pool = self.catalog.native.analysis_pool()?; let request: &SessionRequest = &self.request; let nullability: &ShapeTable = &self.available_nullability; let outcomes: Vec<(ModelOutcome, Option)> = pool.install(|| { - inputs - .into_par_iter() - .map(|(model, schema)| { - native_outcome(dialect, &request.models[model], (schema, nullability)) + misses + .par_iter() + .map(|position| { + let model: &ModelRequest = &request.models[models[*position]]; + native_outcome(dialect, model, (schemas[*position].clone(), nullability)) }) .collect() }); - for (model, (outcome, failure)) in models.iter().zip(outcomes) { + for (position, (outcome, failure)) in misses.iter().zip(outcomes) { self.failures.extend(failure); - self.outcomes[*model] = Some(outcome); + self.outcomes[models[*position]] = Some(outcome); } self.check_sql_macros(models) } @@ -584,22 +583,8 @@ impl AnalysisSession { let Some(mut cache) = self.cache.take() else { return Ok((0..models.len()).collect()); }; - let mut required: Vec = Vec::new(); - let mut seen: HashSet<&str> = HashSet::new(); - for model in models { - for reference in &self.request.models[*model].references { - if seen.insert(reference.analysis_name.as_str()) { - required.push(reference.analysis_name.clone()); - } - } - } - self.catalog.prepare_analysis( - &required, - &self.available_types, - &self.available_nullability, - ); - for schema in schemas { - let _ = self.catalog.prepare(&[("", schema)]); + if matches!(self.route(), AnalysisRoute::Legacy) { + self.prepare_catalog(models, schemas); } let pool: Arc = self.catalog.native.analysis_pool()?; let session_digest: ContentDigest = cache.session_digest; @@ -663,7 +648,28 @@ impl AnalysisSession { Ok(misses) } - /// Normalize the cleaned SQL of the cache hits among `models` that still lack it. + /// Load the relations and schemas a Polyglot analysis of `models` reads into its catalog. + fn prepare_catalog(&mut self, models: &[usize], schemas: &[Shapes]) { + let mut required: Vec = Vec::new(); + let mut seen: HashSet<&str> = HashSet::new(); + for model in models { + for reference in &self.request.models[*model].references { + if seen.insert(reference.analysis_name.as_str()) { + required.push(reference.analysis_name.clone()); + } + } + } + self.catalog.prepare_analysis( + &required, + &self.available_types, + &self.available_nullability, + ); + for schema in schemas { + let _ = self.catalog.prepare(&[("", schema)]); + } + } + + /// Normalize the cleaned SQL of the cache hits among `models` that still lack it. fn ensure_cleaned(&mut self, models: &[usize]) -> Result<(), String> { let pending: Vec = models .iter() @@ -673,6 +679,15 @@ impl AnalysisSession { if pending.is_empty() { return Ok(()); } + if let AnalysisRoute::Native(dialect) = self.route() { + for model in pending { + let sql: String = + native_cleaned_sql(dialect, &self.request.models[model]).unwrap_or_default(); + self.outcome_mut(model)?.cleaned_sql = sql; + self.pending_cleaned[model] = false; + } + return Ok(()); + } let schemas: Vec = pending .iter() .map(|model| self.binding_schema(&self.request.models[*model])) From 06bad7a71817697065cdb958cdb44b1d92107e36 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:56:52 +0000 Subject: [PATCH 018/160] fix(lineage): build fast lineage natively for models without facts On a native route a model without lineage facts, such as one with SQL analysis turned off, got no fast lineage, where Polyglot used to parse it. Its lineage is now read by the dialect's own parser and analysis over the fast lineage schema, with its placeholder defaults filled. SQL the parser cannot read is still left out, and the debug event gives P026's reason with the construct, line and column. --- .../src/analysis_route/main/native_gap.rs | 2 +- .../analysis_route/main/skipped_analysis.rs | 21 ++- .../tests/test_native_outcome.rs | 96 +++++++++++++- .../src/analysis_route/tests/test_types.rs | 7 + .../src/lineage/_helpers/mod.rs | 1 + .../src/lineage/_helpers/native_lineage.rs | 120 ++++++++++++++++++ .../src/lineage/main/build_fast_lineage.rs | 47 +++++-- .../sqlbuild-analysis/src/lineage/models.rs | 2 + .../src/lineage/tests/helpers.rs | 1 + .../lineage/tests/test_build_fast_lineage.rs | 1 + .../_helpers/lineage/project_lineage.rs | 35 +++-- 11 files changed, 302 insertions(+), 31 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs index e7c3f7439..58168f462 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs @@ -22,7 +22,7 @@ pub fn native_gap( } /// The gap with the line and column of its offset; an offset past the end is the end. -fn located(sql: &str, gap: ParserGap) -> LocatedGap { +pub(crate) fn located(sql: &str, gap: ParserGap) -> LocatedGap { let before: &str = sql.get(..gap.offset).unwrap_or(sql); let position: TextPosition = text_position(sql, before.chars().count()); LocatedGap { diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs index 6e0d6abaa..3a09a7209 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs @@ -4,17 +4,24 @@ use crate::analysis_route::models::LocatedGap; /// P026's code and message for a model whose SQL the dialect's native parser cannot read. #[must_use] pub fn skipped_analysis(model: &str, dialect: &str, gap: &LocatedGap) -> (&'static str, String) { + ( + SKIPPED_ANALYSIS_CODE, + format!( + "SQL analysis skipped for model '{model}': {}", + unreadable_reason(dialect, gap) + ), + ) +} + +/// Why the dialect's native parser cannot read SQL, as P026 words it. +pub(crate) fn unreadable_reason(dialect: &str, gap: &LocatedGap) -> String { let detail: String = if gap.message.is_empty() || gap.message == gap.construct { String::new() } else { format!(": {}", gap.message) }; - ( - SKIPPED_ANALYSIS_CODE, - format!( - "SQL analysis skipped for model '{model}': the {dialect} parser cannot read {} at \ - line {}, column {}{detail}", - gap.construct, gap.line, gap.column - ), + format!( + "the {dialect} parser cannot read {} at line {}, column {}{detail}", + gap.construct, gap.line, gap.column ) } diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index 59cb6fdaf..bbb153171 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -1,8 +1,12 @@ -use crate::analysis_route::tests::test_types::{CataloguedDialect, NativeOutcomeTestCase}; +use crate::analysis_route::tests::test_types::{ + CataloguedDialect, NativeLineageTestCase, NativeOutcomeTestCase, PivotlessDialect, +}; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; use crate::assembly::analysis_session::_helpers::native_outcome::native_outcome; use crate::assembly::analysis_session::models::{ModelOutcome, ModelRequest}; use crate::assembly::analysis_session::types::Shapes; +use crate::lineage::_helpers::native_lineage::native_model_lineage; +use crate::lineage::models::{FastLineageOutcome, LineageResourceType, LineageSchemaResource}; #[test] fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_follow_them() { @@ -80,3 +84,93 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll ); } } + +#[test] +fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_lineage_or_a_reason() +{ + let schema: Vec = vec![ + LineageSchemaResource { + resource_type: LineageResourceType::Model, + name: "orders".to_owned(), + columns: vec!["order_id".to_owned(), "amount".to_owned()], + }, + LineageSchemaResource { + resource_type: LineageResourceType::Source, + name: "raw_payments".to_owned(), + columns: vec!["payment_id".to_owned()], + }, + ]; + let test_cases = [ + NativeLineageTestCase { + description: "a referenced model's columns are the upstreams of direct and computed outputs", + sql: r#"SELECT o.order_id, o.amount * 2 AS doubled FROM __ref("orders") AS o"#, + placeholders: &[], + expected: &[ + "order_id direct high [model:orders.order_id]", + "doubled expression high [model:orders.amount]", + "star=false", + ], + }, + NativeLineageTestCase { + description: "a placeholder takes its default before the parse, and a source is a source", + sql: r#"SELECT payment_id FROM __source("raw_payments") WHERE payment_id > @@@min_id"#, + placeholders: &[("min_id", "0")], + expected: &[ + "payment_id direct high [source:raw_payments.payment_id]", + "star=false", + ], + }, + NativeLineageTestCase { + description: "a star over a relation of unknown columns is a star without outputs", + sql: r#"SELECT * FROM __ref("unknown_model")"#, + placeholders: &[], + expected: &["star=true"], + }, + NativeLineageTestCase { + description: "SQL the parser cannot read gives P026's reason, located", + sql: "SELECT 1\nFROM t PIVOT (sum(x) FOR y IN (1))", + placeholders: &[], + expected: &["unparsed: the postgres parser cannot read PIVOT at line 2, column 8"], + }, + ]; + + for test_case in test_cases { + let placeholders: Vec<(String, String)> = test_case + .placeholders + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) + .collect(); + let outcome: FastLineageOutcome = + native_model_lineage(&PivotlessDialect, test_case.sql, &placeholders, &schema); + let lines: Vec = match outcome { + FastLineageOutcome::Built { columns, has_star } => columns + .iter() + .map(|column| { + let sources: Vec = column + .upstream_columns + .iter() + .map(|source| { + format!( + "{}:{}.{}", + source.resource_type.as_str(), + source.resource_name, + source.column_name + ) + }) + .collect(); + format!( + "{} {} {} [{}]", + column.output_column, + column.transform_kind.as_str(), + column.confidence.as_str(), + sources.join(", ") + ) + }) + .chain([format!("star={has_star}")]) + .collect(), + FastLineageOutcome::Unparsed(reason) => vec![format!("unparsed: {reason}")], + other => vec![format!("{other:?}")], + }; + assert_eq!(lines, test_case.expected, "{}", test_case.description); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index a6a5fdbb1..6e6be0691 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -190,3 +190,10 @@ pub(super) struct NativeOutcomeTestCase { /// Each output column as `name type nullability`, then `succeeded star=/`. pub(super) expected: &'static [&'static str], } + +pub(super) struct NativeLineageTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + pub(super) placeholders: &'static [(&'static str, &'static str)], + pub(super) expected: &'static [&'static str], +} diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs index 139d0d6c8..31d6889d6 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs @@ -1,6 +1,7 @@ pub(crate) mod authored_files; pub(crate) mod environment_markers; pub(crate) mod fingerprint_digest; +pub(crate) mod native_lineage; pub(crate) mod parsed_lineage; pub(crate) mod projections; pub(crate) mod python_payload; diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs new file mode 100644 index 000000000..328085b21 --- /dev/null +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs @@ -0,0 +1,120 @@ +//! Fast lineage of a model without facts, read by its dialect's native analysis module. + +use std::collections::HashMap; + +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; + +use crate::analysis_route::main::native_gap::located; +use crate::analysis_route::main::skipped_analysis::unreadable_reason; +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::constants::{ + CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, + TRANSFORM_DIRECT, TRANSFORM_STAR, +}; +use crate::assembly::analysis_session::models::{LineageRow, SessionSchema}; +use crate::lineage::_helpers::references::physical_resources; +use crate::lineage::models::{ + FastLineageOutcome, LineageColumn, LineageConfidence, LineageResourceType, + LineageSchemaResource, LineageSource, LineageTransformKind, +}; +use crate::semantic_validation::main::normalize_native::normalize_native_sql; + +/// The model's lineage over `schema`'s column names, or why the dialect's parser cannot read it. +pub(crate) fn native_model_lineage( + dialect: &dyn NativeDialect, + query_sql: &str, + placeholders: &[(String, String)], + schema: &[LineageSchemaResource], +) -> FastLineageOutcome { + let normalised: String = + match normalize_native_sql(query_sql, dialect.name(), dialect.stub_prefix()) { + Ok(normalised) => normalised, + Err(reason) => return FastLineageOutcome::Unparsed(reason), + }; + let sql: String = placeholder_defaults(&normalised, placeholders); + let mut references: Vec<(String, String, String)> = Vec::new(); + for resource in physical_resources(query_sql) { + if !references + .iter() + .any(|(name, _, _)| *name == resource.resource_name) + { + references.push(( + resource.resource_name.clone(), + resource.resource_type.as_str().to_owned(), + resource.resource_name, + )); + } + } + let relations: SessionSchema = names_schema(schema); + let analysed: Result = dialect.analyse(&AnalysisInput { + sql: &sql, + schema: &relations, + references: &references, + extension_functions: &[], + }); + match analysed { + Ok(native) => FastLineageOutcome::Built { + columns: native.lineage.iter().map(lineage_column).collect(), + has_star: native.has_star, + }, + Err(gap) => { + FastLineageOutcome::Unparsed(unreadable_reason(dialect.name(), &located(&sql, gap))) + } + } +} + +/// Each resource with known columns, untyped and of unknown nullability; the first of a name wins. +fn names_schema(schema: &[LineageSchemaResource]) -> SessionSchema { + let mut relations: HashMap> = HashMap::new(); + for resource in schema + .iter() + .filter(|resource| !resource.columns.is_empty()) + { + let _ = relations.entry(resource.name.clone()).or_insert_with(|| { + resource + .columns + .iter() + .map(|name| SchemaColumn { + name: name.clone(), + quoted: false, + data_type: None, + nullability: Nullability::Unknown, + }) + .collect() + }); + } + SessionSchema { relations } +} + +/// A session lineage row as a fast lineage column; sources of other resource kinds are dropped. +fn lineage_column(row: &LineageRow) -> LineageColumn { + LineageColumn { + output_column: row.output_column.clone(), + transform_kind: match row.transform_code { + TRANSFORM_DIRECT => LineageTransformKind::Direct, + TRANSFORM_CAST => LineageTransformKind::Cast, + TRANSFORM_AGGREGATION => LineageTransformKind::Aggregation, + TRANSFORM_STAR => LineageTransformKind::Star, + TRANSFORM_CONSTANT => LineageTransformKind::Constant, + _ => LineageTransformKind::Expression, + }, + confidence: match row.confidence_code { + CONFIDENCE_HIGH => LineageConfidence::High, + CONFIDENCE_MEDIUM => LineageConfidence::Medium, + _ => LineageConfidence::Unknown, + }, + upstream_columns: row + .sources + .iter() + .filter_map(|(resource_type, resource_name, column_name)| { + Some(LineageSource { + resource_type: LineageResourceType::from_value(resource_type)?, + resource_name: resource_name.clone(), + column_name: column_name.clone(), + }) + }) + .collect(), + } +} diff --git a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs index 94dcca4ac..0d74ba11a 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs @@ -8,6 +8,9 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; +use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::models::AnalysisRoute; +use crate::lineage::_helpers::native_lineage::native_model_lineage; use crate::lineage::_helpers::parsed_lineage::{ ParsedModel, parsed_model_lineage, proxy_parse_options, }; @@ -52,23 +55,49 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec = proxy_parse_options(); request .models .par_iter() - .map(|model| match model { - FastLineageModel::StarExpansion { - query_sql, - existing_columns, - } => Ok(FastLineageOutcome::StarColumns(star_lineage( + .map(|model| match (model, route) { + ( + FastLineageModel::StarExpansion { + query_sql, + existing_columns, + }, + _, + ) => Ok(FastLineageOutcome::StarColumns(star_lineage( &schema, &physical_resources(query_sql), existing_columns.iter().map(String::as_str), ))), - FastLineageModel::Parse { - query_sql, - inferred_columns, - } => { + ( + FastLineageModel::Parse { + query_sql, + placeholders, + .. + }, + AnalysisRoute::Native(module), + ) => catch_compiler_panic(|| { + Ok(native_model_lineage( + module, + query_sql, + placeholders, + &request.schema, + )) + }), + (FastLineageModel::Parse { .. }, AnalysisRoute::Unavailable) => { + Ok(FastLineageOutcome::Omitted) + } + ( + FastLineageModel::Parse { + query_sql, + inferred_columns, + .. + }, + AnalysisRoute::Legacy, + ) => { let model = ParsedModel { query_sql, inferred_names: inferred_columns, diff --git a/crates/sqlbuild-analysis/src/lineage/models.rs b/crates/sqlbuild-analysis/src/lineage/models.rs index d8d4a2657..2632824ed 100644 --- a/crates/sqlbuild-analysis/src/lineage/models.rs +++ b/crates/sqlbuild-analysis/src/lineage/models.rs @@ -106,6 +106,8 @@ pub enum FastLineageModel { Parse { query_sql: String, inferred_columns: Vec, + /// `@@@name` defaults a native parser reads in place of each placeholder. + placeholders: Vec<(String, String)>, }, } diff --git a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs index bf332a89d..0553e0e34 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs @@ -45,6 +45,7 @@ pub(crate) fn deep_union_outcome_lines( FastLineageModel::Parse { query_sql: sql, inferred_columns: Vec::new(), + placeholders: Vec::new(), }, ) }) diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index 9998ac560..9e9f1a77d 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -234,6 +234,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( FastLineageModel::Parse { query_sql: test_case.sql.to_owned(), inferred_columns: strings(test_case.inferred_columns), + placeholders: Vec::new(), }, ); assert_eq!( diff --git a/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs b/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs index 7bc1ff9fd..ad9c50e3f 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/lineage/project_lineage.rs @@ -30,6 +30,7 @@ use crate::bindings::_helpers::lineage::lineage_objects::{LineageClasses, log_li use crate::bindings::_helpers::lineage::project_schema::{ fast_schema, inferred_names, rich_schema, }; +use crate::bindings::_helpers::project_assembly::syntax_checks::model_placeholders; use crate::bindings::_helpers::records::construct::error_class; use crate::bindings::_helpers::records::fields::field as native_field; use crate::bindings::_helpers::records::ids::ErrorId; @@ -154,8 +155,6 @@ struct FastModel<'py> { /// A model's lineage facts: none, Python facts, or a compiled model's native facts and edge count. enum FastFacts<'py> { Missing, - /// No facts, on a native route, where Polyglot never parses the model: no lineage. - Skipped, Python(Bound<'py, PyAny>), Native(Bound<'py, PyAny>, usize), } @@ -191,19 +190,15 @@ fn fast_lineage<'py>( continue; } let facts: Bound<'py, PyAny> = native_field(model, "fast_lineage_columns")?; - if facts.is_none() && native { - work.push(FastModel { - name: native_field(model, "name")?.extract()?, - facts: FastFacts::Skipped, - has_star, - expands_star, - }); - continue; - } if facts.is_none() { requests.push(FastLineageModel::Parse { query_sql: native_field(model, "query_sql")?.extract()?, inferred_columns: inferred_names(model)?, + placeholders: if native { + placeholder_pairs(model)? + } else { + Vec::new() + }, }); } else if expands_star { requests.push(FastLineageModel::StarExpansion { @@ -266,7 +261,6 @@ fn fast_lineage<'py>( } CompactColumns::Facts(columns.unbind()) } - FastFacts::Skipped => continue, FastFacts::Missing => { match outcomes.next() { Some(FastLineageOutcome::Built { columns, has_star }) => { @@ -282,7 +276,11 @@ fn fast_lineage<'py>( } Some(FastLineageOutcome::Unparsed(detail)) => log_lineage_event( py, - "fast column lineage parse failed; falling back", + if native { + "fast column lineage not built: the native parser cannot read the model" + } else { + "fast column lineage parse failed; falling back" + }, &model.name, Some(&detail), )?, @@ -310,6 +308,17 @@ fn fast_lineage<'py>( .map(Bound::into_any) } +/// The model's `@@@name` placeholder defaults, which a native parser reads in their place. +fn placeholder_pairs(model: &Bound<'_, PyAny>) -> PyResult> { + let Some(placeholders) = model_placeholders(model)? else { + return Ok(Vec::new()); + }; + placeholders + .iter() + .map(|(name, value)| Ok((name.extract()?, value.extract()?))) + .collect() +} + /// The output column names of compact facts or plain column facts. fn output_columns(classes: &LineageClasses<'_>, facts: &Bound<'_, PyAny>) -> PyResult> { if facts.is_instance(&classes.compact_facts)? { From f07c13aefa720e0e1d0b58e202579d43bced499e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:06:26 +0000 Subject: [PATCH 019/160] test(types): re-record the generated type golden for nested timestamps The text fallback now takes a type's family from its top-level type, so a STRUCT, ARRAY or MAP holding a timestamp reads as other, not timestamp. Only those 666 dialect entries move; every top-level timestamp is unchanged. --- ...rized_nested_padded_and_invalid_types.json | 1332 ++++++++--------- 1 file changed, 666 insertions(+), 666 deletions(-) diff --git a/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json b/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json index 1729e43ae..04b9dd9f0 100644 --- a/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json +++ b/tests/goldens/python_oracle/types_generated_seeded_scalar_parameterized_nested_padded_and_invalid_types.json @@ -478,18 +478,18 @@ [ "type", { -"DuckDB": "98a80237e4e0a12f35bbf17e", -"None": "98a80237e4e0a12f35bbf17e", -"bigquery": "98a80237e4e0a12f35bbf17e", -"databricks": "98a80237e4e0a12f35bbf17e", -"duckdb": "98a80237e4e0a12f35bbf17e", -"generic": "98a80237e4e0a12f35bbf17e", +"DuckDB": "f1dcf3349867744866bb1299", +"None": "f1dcf3349867744866bb1299", +"bigquery": "f1dcf3349867744866bb1299", +"databricks": "f1dcf3349867744866bb1299", +"duckdb": "f1dcf3349867744866bb1299", +"generic": "f1dcf3349867744866bb1299", "motherduck": "901c5133ed47d4769286e860", -"mysql": "98a80237e4e0a12f35bbf17e", -"postgres": "98a80237e4e0a12f35bbf17e", -"postgresql": "98a80237e4e0a12f35bbf17e", -"snowflake": "98a80237e4e0a12f35bbf17e", -"tsql": "98a80237e4e0a12f35bbf17e" +"mysql": "f1dcf3349867744866bb1299", +"postgres": "f1dcf3349867744866bb1299", +"postgresql": "f1dcf3349867744866bb1299", +"snowflake": "f1dcf3349867744866bb1299", +"tsql": "f1dcf3349867744866bb1299" } ], [ @@ -529,18 +529,18 @@ [ "type", { -"DuckDB": "9a79cb58680bebe49009a72d", -"None": "9a79cb58680bebe49009a72d", -"bigquery": "9a79cb58680bebe49009a72d", -"databricks": "9a79cb58680bebe49009a72d", -"duckdb": "9a79cb58680bebe49009a72d", -"generic": "9a79cb58680bebe49009a72d", +"DuckDB": "7c70b66e45566b521b449adc", +"None": "7c70b66e45566b521b449adc", +"bigquery": "7c70b66e45566b521b449adc", +"databricks": "7c70b66e45566b521b449adc", +"duckdb": "7c70b66e45566b521b449adc", +"generic": "7c70b66e45566b521b449adc", "motherduck": "901c5133ed47d4769286e860", -"mysql": "9a79cb58680bebe49009a72d", -"postgres": "9a79cb58680bebe49009a72d", -"postgresql": "9a79cb58680bebe49009a72d", -"snowflake": "9a79cb58680bebe49009a72d", -"tsql": "9a79cb58680bebe49009a72d" +"mysql": "7c70b66e45566b521b449adc", +"postgres": "7c70b66e45566b521b449adc", +"postgresql": "7c70b66e45566b521b449adc", +"snowflake": "7c70b66e45566b521b449adc", +"tsql": "7c70b66e45566b521b449adc" } ], [ @@ -954,18 +954,18 @@ [ "type", { -"DuckDB": "1950e32420d1d40d41ffae64", -"None": "1950e32420d1d40d41ffae64", -"bigquery": "1950e32420d1d40d41ffae64", -"databricks": "1950e32420d1d40d41ffae64", -"duckdb": "1950e32420d1d40d41ffae64", -"generic": "1950e32420d1d40d41ffae64", +"DuckDB": "8f2569067b02a6a41c0caf62", +"None": "8f2569067b02a6a41c0caf62", +"bigquery": "8f2569067b02a6a41c0caf62", +"databricks": "8f2569067b02a6a41c0caf62", +"duckdb": "8f2569067b02a6a41c0caf62", +"generic": "8f2569067b02a6a41c0caf62", "motherduck": "901c5133ed47d4769286e860", -"mysql": "1950e32420d1d40d41ffae64", -"postgres": "1950e32420d1d40d41ffae64", -"postgresql": "1950e32420d1d40d41ffae64", -"snowflake": "1950e32420d1d40d41ffae64", -"tsql": "1950e32420d1d40d41ffae64" +"mysql": "8f2569067b02a6a41c0caf62", +"postgres": "8f2569067b02a6a41c0caf62", +"postgresql": "8f2569067b02a6a41c0caf62", +"snowflake": "8f2569067b02a6a41c0caf62", +"tsql": "8f2569067b02a6a41c0caf62" } ], [ @@ -1311,18 +1311,18 @@ [ "type", { -"DuckDB": "06aec7cca4b30489405665e2", -"None": "06aec7cca4b30489405665e2", -"bigquery": "06aec7cca4b30489405665e2", -"databricks": "06aec7cca4b30489405665e2", -"duckdb": "06aec7cca4b30489405665e2", -"generic": "06aec7cca4b30489405665e2", +"DuckDB": "da06e77421c17a0eee87e6cc", +"None": "da06e77421c17a0eee87e6cc", +"bigquery": "da06e77421c17a0eee87e6cc", +"databricks": "da06e77421c17a0eee87e6cc", +"duckdb": "da06e77421c17a0eee87e6cc", +"generic": "da06e77421c17a0eee87e6cc", "motherduck": "901c5133ed47d4769286e860", -"mysql": "06aec7cca4b30489405665e2", -"postgres": "06aec7cca4b30489405665e2", -"postgresql": "06aec7cca4b30489405665e2", -"snowflake": "06aec7cca4b30489405665e2", -"tsql": "06aec7cca4b30489405665e2" +"mysql": "da06e77421c17a0eee87e6cc", +"postgres": "da06e77421c17a0eee87e6cc", +"postgresql": "da06e77421c17a0eee87e6cc", +"snowflake": "da06e77421c17a0eee87e6cc", +"tsql": "da06e77421c17a0eee87e6cc" } ], [ @@ -1396,35 +1396,35 @@ [ "type", { -"DuckDB": "7aa59eed1bba74a9dee48e59", -"None": "7aa59eed1bba74a9dee48e59", -"bigquery": "7aa59eed1bba74a9dee48e59", -"databricks": "7aa59eed1bba74a9dee48e59", -"duckdb": "7aa59eed1bba74a9dee48e59", -"generic": "7aa59eed1bba74a9dee48e59", +"DuckDB": "85679d39a40088722a25077b", +"None": "85679d39a40088722a25077b", +"bigquery": "85679d39a40088722a25077b", +"databricks": "85679d39a40088722a25077b", +"duckdb": "85679d39a40088722a25077b", +"generic": "85679d39a40088722a25077b", "motherduck": "901c5133ed47d4769286e860", -"mysql": "7aa59eed1bba74a9dee48e59", -"postgres": "7aa59eed1bba74a9dee48e59", -"postgresql": "7aa59eed1bba74a9dee48e59", -"snowflake": "7aa59eed1bba74a9dee48e59", -"tsql": "7aa59eed1bba74a9dee48e59" +"mysql": "85679d39a40088722a25077b", +"postgres": "85679d39a40088722a25077b", +"postgresql": "85679d39a40088722a25077b", +"snowflake": "85679d39a40088722a25077b", +"tsql": "85679d39a40088722a25077b" } ], [ "type", { -"DuckDB": "66e7166890ccedeaad6c241a", -"None": "66e7166890ccedeaad6c241a", -"bigquery": "66e7166890ccedeaad6c241a", -"databricks": "66e7166890ccedeaad6c241a", -"duckdb": "66e7166890ccedeaad6c241a", -"generic": "66e7166890ccedeaad6c241a", +"DuckDB": "af83290b9888e9ea215c9c2a", +"None": "af83290b9888e9ea215c9c2a", +"bigquery": "af83290b9888e9ea215c9c2a", +"databricks": "af83290b9888e9ea215c9c2a", +"duckdb": "af83290b9888e9ea215c9c2a", +"generic": "af83290b9888e9ea215c9c2a", "motherduck": "901c5133ed47d4769286e860", -"mysql": "66e7166890ccedeaad6c241a", -"postgres": "66e7166890ccedeaad6c241a", -"postgresql": "66e7166890ccedeaad6c241a", -"snowflake": "66e7166890ccedeaad6c241a", -"tsql": "66e7166890ccedeaad6c241a" +"mysql": "af83290b9888e9ea215c9c2a", +"postgres": "af83290b9888e9ea215c9c2a", +"postgresql": "af83290b9888e9ea215c9c2a", +"snowflake": "af83290b9888e9ea215c9c2a", +"tsql": "af83290b9888e9ea215c9c2a" } ], [ @@ -1889,18 +1889,18 @@ [ "type", { -"DuckDB": "caf173733defe796376d4efe", -"None": "caf173733defe796376d4efe", -"bigquery": "caf173733defe796376d4efe", -"databricks": "caf173733defe796376d4efe", -"duckdb": "caf173733defe796376d4efe", -"generic": "caf173733defe796376d4efe", +"DuckDB": "a6458f2eeceb4012953d4be0", +"None": "a6458f2eeceb4012953d4be0", +"bigquery": "a6458f2eeceb4012953d4be0", +"databricks": "a6458f2eeceb4012953d4be0", +"duckdb": "a6458f2eeceb4012953d4be0", +"generic": "a6458f2eeceb4012953d4be0", "motherduck": "901c5133ed47d4769286e860", -"mysql": "caf173733defe796376d4efe", -"postgres": "caf173733defe796376d4efe", -"postgresql": "caf173733defe796376d4efe", -"snowflake": "caf173733defe796376d4efe", -"tsql": "caf173733defe796376d4efe" +"mysql": "a6458f2eeceb4012953d4be0", +"postgres": "a6458f2eeceb4012953d4be0", +"postgresql": "a6458f2eeceb4012953d4be0", +"snowflake": "a6458f2eeceb4012953d4be0", +"tsql": "a6458f2eeceb4012953d4be0" } ], [ @@ -1974,18 +1974,18 @@ [ "type", { -"DuckDB": "4f8af4650ce9c18ed0cb0bca", -"None": "4f8af4650ce9c18ed0cb0bca", -"bigquery": "4f8af4650ce9c18ed0cb0bca", -"databricks": "4f8af4650ce9c18ed0cb0bca", -"duckdb": "4f8af4650ce9c18ed0cb0bca", -"generic": "4f8af4650ce9c18ed0cb0bca", +"DuckDB": "a5e56c4a2e3c796d3dca7042", +"None": "a5e56c4a2e3c796d3dca7042", +"bigquery": "a5e56c4a2e3c796d3dca7042", +"databricks": "a5e56c4a2e3c796d3dca7042", +"duckdb": "a5e56c4a2e3c796d3dca7042", +"generic": "a5e56c4a2e3c796d3dca7042", "motherduck": "901c5133ed47d4769286e860", -"mysql": "4f8af4650ce9c18ed0cb0bca", -"postgres": "4f8af4650ce9c18ed0cb0bca", -"postgresql": "4f8af4650ce9c18ed0cb0bca", -"snowflake": "4f8af4650ce9c18ed0cb0bca", -"tsql": "4f8af4650ce9c18ed0cb0bca" +"mysql": "a5e56c4a2e3c796d3dca7042", +"postgres": "a5e56c4a2e3c796d3dca7042", +"postgresql": "a5e56c4a2e3c796d3dca7042", +"snowflake": "a5e56c4a2e3c796d3dca7042", +"tsql": "a5e56c4a2e3c796d3dca7042" } ], [ @@ -2603,18 +2603,18 @@ [ "type", { -"DuckDB": "ad35c4a2523ce8c2f57acf51", -"None": "ad35c4a2523ce8c2f57acf51", -"bigquery": "ad35c4a2523ce8c2f57acf51", -"databricks": "ad35c4a2523ce8c2f57acf51", -"duckdb": "ad35c4a2523ce8c2f57acf51", -"generic": "ad35c4a2523ce8c2f57acf51", +"DuckDB": "e7bc9109e1c25d865732885c", +"None": "e7bc9109e1c25d865732885c", +"bigquery": "e7bc9109e1c25d865732885c", +"databricks": "e7bc9109e1c25d865732885c", +"duckdb": "e7bc9109e1c25d865732885c", +"generic": "e7bc9109e1c25d865732885c", "motherduck": "901c5133ed47d4769286e860", -"mysql": "ad35c4a2523ce8c2f57acf51", -"postgres": "ad35c4a2523ce8c2f57acf51", -"postgresql": "ad35c4a2523ce8c2f57acf51", -"snowflake": "ad35c4a2523ce8c2f57acf51", -"tsql": "ad35c4a2523ce8c2f57acf51" +"mysql": "e7bc9109e1c25d865732885c", +"postgres": "e7bc9109e1c25d865732885c", +"postgresql": "e7bc9109e1c25d865732885c", +"snowflake": "e7bc9109e1c25d865732885c", +"tsql": "e7bc9109e1c25d865732885c" } ], [ @@ -4252,18 +4252,18 @@ [ "type", { -"DuckDB": "96ecb0bd0c27167bf4e31a08", -"None": "96ecb0bd0c27167bf4e31a08", -"bigquery": "96ecb0bd0c27167bf4e31a08", -"databricks": "96ecb0bd0c27167bf4e31a08", -"duckdb": "96ecb0bd0c27167bf4e31a08", -"generic": "96ecb0bd0c27167bf4e31a08", +"DuckDB": "a1ccf5c0db81a578bab8cda9", +"None": "a1ccf5c0db81a578bab8cda9", +"bigquery": "a1ccf5c0db81a578bab8cda9", +"databricks": "a1ccf5c0db81a578bab8cda9", +"duckdb": "a1ccf5c0db81a578bab8cda9", +"generic": "a1ccf5c0db81a578bab8cda9", "motherduck": "901c5133ed47d4769286e860", -"mysql": "96ecb0bd0c27167bf4e31a08", -"postgres": "96ecb0bd0c27167bf4e31a08", -"postgresql": "96ecb0bd0c27167bf4e31a08", -"snowflake": "96ecb0bd0c27167bf4e31a08", -"tsql": "96ecb0bd0c27167bf4e31a08" +"mysql": "a1ccf5c0db81a578bab8cda9", +"postgres": "a1ccf5c0db81a578bab8cda9", +"postgresql": "a1ccf5c0db81a578bab8cda9", +"snowflake": "a1ccf5c0db81a578bab8cda9", +"tsql": "a1ccf5c0db81a578bab8cda9" } ], [ @@ -4423,17 +4423,17 @@ "type", { "DuckDB": "c344d36a6751b773df74ee3c", -"None": "87fed6659d6324c5ef0c6ee2", -"bigquery": "87fed6659d6324c5ef0c6ee2", -"databricks": "87fed6659d6324c5ef0c6ee2", +"None": "2d031f8bbd88fca9a9492b54", +"bigquery": "2d031f8bbd88fca9a9492b54", +"databricks": "2d031f8bbd88fca9a9492b54", "duckdb": "c344d36a6751b773df74ee3c", -"generic": "87fed6659d6324c5ef0c6ee2", +"generic": "2d031f8bbd88fca9a9492b54", "motherduck": "901c5133ed47d4769286e860", -"mysql": "87fed6659d6324c5ef0c6ee2", -"postgres": "87fed6659d6324c5ef0c6ee2", -"postgresql": "87fed6659d6324c5ef0c6ee2", -"snowflake": "87fed6659d6324c5ef0c6ee2", -"tsql": "87fed6659d6324c5ef0c6ee2" +"mysql": "2d031f8bbd88fca9a9492b54", +"postgres": "2d031f8bbd88fca9a9492b54", +"postgresql": "2d031f8bbd88fca9a9492b54", +"snowflake": "2d031f8bbd88fca9a9492b54", +"tsql": "2d031f8bbd88fca9a9492b54" } ], [ @@ -4779,18 +4779,18 @@ [ "type", { -"DuckDB": "2795ccedbfd3fa0bed5fdfcd", -"None": "2795ccedbfd3fa0bed5fdfcd", -"bigquery": "2795ccedbfd3fa0bed5fdfcd", -"databricks": "2795ccedbfd3fa0bed5fdfcd", -"duckdb": "2795ccedbfd3fa0bed5fdfcd", -"generic": "2795ccedbfd3fa0bed5fdfcd", +"DuckDB": "ebd0935007c5cca9d0c14021", +"None": "ebd0935007c5cca9d0c14021", +"bigquery": "ebd0935007c5cca9d0c14021", +"databricks": "ebd0935007c5cca9d0c14021", +"duckdb": "ebd0935007c5cca9d0c14021", +"generic": "ebd0935007c5cca9d0c14021", "motherduck": "901c5133ed47d4769286e860", -"mysql": "2795ccedbfd3fa0bed5fdfcd", -"postgres": "2795ccedbfd3fa0bed5fdfcd", -"postgresql": "2795ccedbfd3fa0bed5fdfcd", -"snowflake": "2795ccedbfd3fa0bed5fdfcd", -"tsql": "2795ccedbfd3fa0bed5fdfcd" +"mysql": "ebd0935007c5cca9d0c14021", +"postgres": "ebd0935007c5cca9d0c14021", +"postgresql": "ebd0935007c5cca9d0c14021", +"snowflake": "ebd0935007c5cca9d0c14021", +"tsql": "ebd0935007c5cca9d0c14021" } ], [ @@ -5357,18 +5357,18 @@ [ "type", { -"DuckDB": "c4860fb394ecca5e4515f1c9", -"None": "c4860fb394ecca5e4515f1c9", -"bigquery": "c4860fb394ecca5e4515f1c9", -"databricks": "c4860fb394ecca5e4515f1c9", -"duckdb": "c4860fb394ecca5e4515f1c9", -"generic": "c4860fb394ecca5e4515f1c9", +"DuckDB": "8e8e75c867befbf0dd5b522f", +"None": "8e8e75c867befbf0dd5b522f", +"bigquery": "8e8e75c867befbf0dd5b522f", +"databricks": "8e8e75c867befbf0dd5b522f", +"duckdb": "8e8e75c867befbf0dd5b522f", +"generic": "8e8e75c867befbf0dd5b522f", "motherduck": "901c5133ed47d4769286e860", -"mysql": "c4860fb394ecca5e4515f1c9", -"postgres": "c4860fb394ecca5e4515f1c9", -"postgresql": "c4860fb394ecca5e4515f1c9", -"snowflake": "c4860fb394ecca5e4515f1c9", -"tsql": "c4860fb394ecca5e4515f1c9" +"mysql": "8e8e75c867befbf0dd5b522f", +"postgres": "8e8e75c867befbf0dd5b522f", +"postgresql": "8e8e75c867befbf0dd5b522f", +"snowflake": "8e8e75c867befbf0dd5b522f", +"tsql": "8e8e75c867befbf0dd5b522f" } ], [ @@ -5697,18 +5697,18 @@ [ "type", { -"DuckDB": "445eba49a8d5ccbd6775e1b6", -"None": "445eba49a8d5ccbd6775e1b6", -"bigquery": "445eba49a8d5ccbd6775e1b6", -"databricks": "445eba49a8d5ccbd6775e1b6", -"duckdb": "445eba49a8d5ccbd6775e1b6", -"generic": "445eba49a8d5ccbd6775e1b6", +"DuckDB": "a8cae774dd1c333f42d6a630", +"None": "a8cae774dd1c333f42d6a630", +"bigquery": "a8cae774dd1c333f42d6a630", +"databricks": "a8cae774dd1c333f42d6a630", +"duckdb": "a8cae774dd1c333f42d6a630", +"generic": "a8cae774dd1c333f42d6a630", "motherduck": "901c5133ed47d4769286e860", -"mysql": "445eba49a8d5ccbd6775e1b6", -"postgres": "445eba49a8d5ccbd6775e1b6", -"postgresql": "445eba49a8d5ccbd6775e1b6", -"snowflake": "445eba49a8d5ccbd6775e1b6", -"tsql": "445eba49a8d5ccbd6775e1b6" +"mysql": "a8cae774dd1c333f42d6a630", +"postgres": "a8cae774dd1c333f42d6a630", +"postgresql": "a8cae774dd1c333f42d6a630", +"snowflake": "a8cae774dd1c333f42d6a630", +"tsql": "a8cae774dd1c333f42d6a630" } ], [ @@ -6054,18 +6054,18 @@ [ "type", { -"DuckDB": "07aa3fe366bb8a3f270ea3d3", -"None": "07aa3fe366bb8a3f270ea3d3", -"bigquery": "07aa3fe366bb8a3f270ea3d3", -"databricks": "07aa3fe366bb8a3f270ea3d3", -"duckdb": "07aa3fe366bb8a3f270ea3d3", -"generic": "07aa3fe366bb8a3f270ea3d3", +"DuckDB": "7a9cb09cc9d8e6470f5c18fb", +"None": "7a9cb09cc9d8e6470f5c18fb", +"bigquery": "7a9cb09cc9d8e6470f5c18fb", +"databricks": "7a9cb09cc9d8e6470f5c18fb", +"duckdb": "7a9cb09cc9d8e6470f5c18fb", +"generic": "7a9cb09cc9d8e6470f5c18fb", "motherduck": "901c5133ed47d4769286e860", -"mysql": "07aa3fe366bb8a3f270ea3d3", -"postgres": "07aa3fe366bb8a3f270ea3d3", -"postgresql": "07aa3fe366bb8a3f270ea3d3", -"snowflake": "07aa3fe366bb8a3f270ea3d3", -"tsql": "07aa3fe366bb8a3f270ea3d3" +"mysql": "7a9cb09cc9d8e6470f5c18fb", +"postgres": "7a9cb09cc9d8e6470f5c18fb", +"postgresql": "7a9cb09cc9d8e6470f5c18fb", +"snowflake": "7a9cb09cc9d8e6470f5c18fb", +"tsql": "7a9cb09cc9d8e6470f5c18fb" } ], [ @@ -6139,18 +6139,18 @@ [ "type", { -"DuckDB": "c426d93641829fa3d8f49b8d", -"None": "c426d93641829fa3d8f49b8d", -"bigquery": "c426d93641829fa3d8f49b8d", -"databricks": "c426d93641829fa3d8f49b8d", -"duckdb": "c426d93641829fa3d8f49b8d", -"generic": "c426d93641829fa3d8f49b8d", +"DuckDB": "efb799417b64225e2ee93447", +"None": "efb799417b64225e2ee93447", +"bigquery": "efb799417b64225e2ee93447", +"databricks": "efb799417b64225e2ee93447", +"duckdb": "efb799417b64225e2ee93447", +"generic": "efb799417b64225e2ee93447", "motherduck": "901c5133ed47d4769286e860", -"mysql": "c426d93641829fa3d8f49b8d", -"postgres": "c426d93641829fa3d8f49b8d", -"postgresql": "c426d93641829fa3d8f49b8d", -"snowflake": "c426d93641829fa3d8f49b8d", -"tsql": "c426d93641829fa3d8f49b8d" +"mysql": "efb799417b64225e2ee93447", +"postgres": "efb799417b64225e2ee93447", +"postgresql": "efb799417b64225e2ee93447", +"snowflake": "efb799417b64225e2ee93447", +"tsql": "efb799417b64225e2ee93447" } ], [ @@ -6224,18 +6224,18 @@ [ "type", { -"DuckDB": "be15dde1d6e0b4bb3f6da66f", -"None": "be15dde1d6e0b4bb3f6da66f", -"bigquery": "be15dde1d6e0b4bb3f6da66f", -"databricks": "be15dde1d6e0b4bb3f6da66f", -"duckdb": "be15dde1d6e0b4bb3f6da66f", -"generic": "be15dde1d6e0b4bb3f6da66f", +"DuckDB": "400acbf184a17c6b468aab31", +"None": "400acbf184a17c6b468aab31", +"bigquery": "400acbf184a17c6b468aab31", +"databricks": "400acbf184a17c6b468aab31", +"duckdb": "400acbf184a17c6b468aab31", +"generic": "400acbf184a17c6b468aab31", "motherduck": "901c5133ed47d4769286e860", -"mysql": "be15dde1d6e0b4bb3f6da66f", -"postgres": "be15dde1d6e0b4bb3f6da66f", -"postgresql": "be15dde1d6e0b4bb3f6da66f", -"snowflake": "be15dde1d6e0b4bb3f6da66f", -"tsql": "be15dde1d6e0b4bb3f6da66f" +"mysql": "400acbf184a17c6b468aab31", +"postgres": "400acbf184a17c6b468aab31", +"postgresql": "400acbf184a17c6b468aab31", +"snowflake": "400acbf184a17c6b468aab31", +"tsql": "400acbf184a17c6b468aab31" } ], [ @@ -6751,18 +6751,18 @@ [ "type", { -"DuckDB": "182a094bae8a9a33f67845ad", -"None": "182a094bae8a9a33f67845ad", -"bigquery": "182a094bae8a9a33f67845ad", -"databricks": "182a094bae8a9a33f67845ad", -"duckdb": "182a094bae8a9a33f67845ad", -"generic": "182a094bae8a9a33f67845ad", +"DuckDB": "fa13cacc15beaca279323bfb", +"None": "fa13cacc15beaca279323bfb", +"bigquery": "fa13cacc15beaca279323bfb", +"databricks": "fa13cacc15beaca279323bfb", +"duckdb": "fa13cacc15beaca279323bfb", +"generic": "fa13cacc15beaca279323bfb", "motherduck": "901c5133ed47d4769286e860", -"mysql": "182a094bae8a9a33f67845ad", -"postgres": "182a094bae8a9a33f67845ad", -"postgresql": "182a094bae8a9a33f67845ad", -"snowflake": "182a094bae8a9a33f67845ad", -"tsql": "182a094bae8a9a33f67845ad" +"mysql": "fa13cacc15beaca279323bfb", +"postgres": "fa13cacc15beaca279323bfb", +"postgresql": "fa13cacc15beaca279323bfb", +"snowflake": "fa13cacc15beaca279323bfb", +"tsql": "fa13cacc15beaca279323bfb" } ], [ @@ -6955,18 +6955,18 @@ [ "type", { -"DuckDB": "7032aa8254308ba815883957", -"None": "7032aa8254308ba815883957", -"bigquery": "7032aa8254308ba815883957", -"databricks": "7032aa8254308ba815883957", -"duckdb": "7032aa8254308ba815883957", -"generic": "7032aa8254308ba815883957", +"DuckDB": "8e263d7157f195e571a4862b", +"None": "8e263d7157f195e571a4862b", +"bigquery": "8e263d7157f195e571a4862b", +"databricks": "8e263d7157f195e571a4862b", +"duckdb": "8e263d7157f195e571a4862b", +"generic": "8e263d7157f195e571a4862b", "motherduck": "901c5133ed47d4769286e860", -"mysql": "7032aa8254308ba815883957", -"postgres": "7032aa8254308ba815883957", -"postgresql": "7032aa8254308ba815883957", -"snowflake": "7032aa8254308ba815883957", -"tsql": "7032aa8254308ba815883957" +"mysql": "8e263d7157f195e571a4862b", +"postgres": "8e263d7157f195e571a4862b", +"postgresql": "8e263d7157f195e571a4862b", +"snowflake": "8e263d7157f195e571a4862b", +"tsql": "8e263d7157f195e571a4862b" } ], [ @@ -7210,18 +7210,18 @@ [ "type", { -"DuckDB": "8789309b90fc585ddbbe90f9", -"None": "8789309b90fc585ddbbe90f9", -"bigquery": "8789309b90fc585ddbbe90f9", -"databricks": "8789309b90fc585ddbbe90f9", -"duckdb": "8789309b90fc585ddbbe90f9", -"generic": "8789309b90fc585ddbbe90f9", +"DuckDB": "6b4faa1df4db47a526d4ae10", +"None": "6b4faa1df4db47a526d4ae10", +"bigquery": "6b4faa1df4db47a526d4ae10", +"databricks": "6b4faa1df4db47a526d4ae10", +"duckdb": "6b4faa1df4db47a526d4ae10", +"generic": "6b4faa1df4db47a526d4ae10", "motherduck": "901c5133ed47d4769286e860", -"mysql": "8789309b90fc585ddbbe90f9", -"postgres": "8789309b90fc585ddbbe90f9", -"postgresql": "8789309b90fc585ddbbe90f9", -"snowflake": "8789309b90fc585ddbbe90f9", -"tsql": "8789309b90fc585ddbbe90f9" +"mysql": "6b4faa1df4db47a526d4ae10", +"postgres": "6b4faa1df4db47a526d4ae10", +"postgresql": "6b4faa1df4db47a526d4ae10", +"snowflake": "6b4faa1df4db47a526d4ae10", +"tsql": "6b4faa1df4db47a526d4ae10" } ], [ @@ -7330,17 +7330,17 @@ "type", { "DuckDB": "c729c38d2df6efedc6288247", -"None": "eea1002d88cc68fad7424aca", -"bigquery": "eea1002d88cc68fad7424aca", -"databricks": "eea1002d88cc68fad7424aca", +"None": "75f1ffee735aaf2dc58efd48", +"bigquery": "75f1ffee735aaf2dc58efd48", +"databricks": "75f1ffee735aaf2dc58efd48", "duckdb": "c729c38d2df6efedc6288247", -"generic": "eea1002d88cc68fad7424aca", +"generic": "75f1ffee735aaf2dc58efd48", "motherduck": "901c5133ed47d4769286e860", -"mysql": "eea1002d88cc68fad7424aca", -"postgres": "eea1002d88cc68fad7424aca", -"postgresql": "eea1002d88cc68fad7424aca", -"snowflake": "eea1002d88cc68fad7424aca", -"tsql": "eea1002d88cc68fad7424aca" +"mysql": "75f1ffee735aaf2dc58efd48", +"postgres": "75f1ffee735aaf2dc58efd48", +"postgresql": "75f1ffee735aaf2dc58efd48", +"snowflake": "75f1ffee735aaf2dc58efd48", +"tsql": "75f1ffee735aaf2dc58efd48" } ], [ @@ -7465,18 +7465,18 @@ [ "type", { -"DuckDB": "733b04338829c5844270d25e", -"None": "733b04338829c5844270d25e", -"bigquery": "733b04338829c5844270d25e", -"databricks": "733b04338829c5844270d25e", -"duckdb": "733b04338829c5844270d25e", -"generic": "733b04338829c5844270d25e", +"DuckDB": "1bbf30e0166bb61bed8be4d6", +"None": "1bbf30e0166bb61bed8be4d6", +"bigquery": "1bbf30e0166bb61bed8be4d6", +"databricks": "1bbf30e0166bb61bed8be4d6", +"duckdb": "1bbf30e0166bb61bed8be4d6", +"generic": "1bbf30e0166bb61bed8be4d6", "motherduck": "901c5133ed47d4769286e860", -"mysql": "733b04338829c5844270d25e", -"postgres": "733b04338829c5844270d25e", -"postgresql": "733b04338829c5844270d25e", -"snowflake": "733b04338829c5844270d25e", -"tsql": "733b04338829c5844270d25e" +"mysql": "1bbf30e0166bb61bed8be4d6", +"postgres": "1bbf30e0166bb61bed8be4d6", +"postgresql": "1bbf30e0166bb61bed8be4d6", +"snowflake": "1bbf30e0166bb61bed8be4d6", +"tsql": "1bbf30e0166bb61bed8be4d6" } ], [ @@ -8264,18 +8264,18 @@ [ "type", { -"DuckDB": "cc33965a669608ec39e8c246", -"None": "cc33965a669608ec39e8c246", -"bigquery": "cc33965a669608ec39e8c246", -"databricks": "cc33965a669608ec39e8c246", -"duckdb": "cc33965a669608ec39e8c246", -"generic": "cc33965a669608ec39e8c246", +"DuckDB": "30a870a18e4d367086a7d302", +"None": "30a870a18e4d367086a7d302", +"bigquery": "30a870a18e4d367086a7d302", +"databricks": "30a870a18e4d367086a7d302", +"duckdb": "30a870a18e4d367086a7d302", +"generic": "30a870a18e4d367086a7d302", "motherduck": "901c5133ed47d4769286e860", -"mysql": "cc33965a669608ec39e8c246", -"postgres": "cc33965a669608ec39e8c246", -"postgresql": "cc33965a669608ec39e8c246", -"snowflake": "cc33965a669608ec39e8c246", -"tsql": "cc33965a669608ec39e8c246" +"mysql": "30a870a18e4d367086a7d302", +"postgres": "30a870a18e4d367086a7d302", +"postgresql": "30a870a18e4d367086a7d302", +"snowflake": "30a870a18e4d367086a7d302", +"tsql": "30a870a18e4d367086a7d302" } ], [ @@ -8740,18 +8740,18 @@ [ "type", { -"DuckDB": "9626d393c137a37008bb4b89", -"None": "9626d393c137a37008bb4b89", -"bigquery": "9626d393c137a37008bb4b89", -"databricks": "9626d393c137a37008bb4b89", -"duckdb": "9626d393c137a37008bb4b89", -"generic": "9626d393c137a37008bb4b89", +"DuckDB": "386957bbe33b0277f99cac09", +"None": "386957bbe33b0277f99cac09", +"bigquery": "386957bbe33b0277f99cac09", +"databricks": "386957bbe33b0277f99cac09", +"duckdb": "386957bbe33b0277f99cac09", +"generic": "386957bbe33b0277f99cac09", "motherduck": "901c5133ed47d4769286e860", -"mysql": "9626d393c137a37008bb4b89", -"postgres": "9626d393c137a37008bb4b89", -"postgresql": "9626d393c137a37008bb4b89", -"snowflake": "9626d393c137a37008bb4b89", -"tsql": "9626d393c137a37008bb4b89" +"mysql": "386957bbe33b0277f99cac09", +"postgres": "386957bbe33b0277f99cac09", +"postgresql": "386957bbe33b0277f99cac09", +"snowflake": "386957bbe33b0277f99cac09", +"tsql": "386957bbe33b0277f99cac09" } ], [ @@ -8791,18 +8791,18 @@ [ "type", { -"DuckDB": "8fe65454f387031aa6231faf", -"None": "8fe65454f387031aa6231faf", -"bigquery": "8fe65454f387031aa6231faf", -"databricks": "8fe65454f387031aa6231faf", -"duckdb": "8fe65454f387031aa6231faf", -"generic": "8fe65454f387031aa6231faf", +"DuckDB": "9304d412ef114a51a170158d", +"None": "9304d412ef114a51a170158d", +"bigquery": "9304d412ef114a51a170158d", +"databricks": "9304d412ef114a51a170158d", +"duckdb": "9304d412ef114a51a170158d", +"generic": "9304d412ef114a51a170158d", "motherduck": "901c5133ed47d4769286e860", -"mysql": "8fe65454f387031aa6231faf", -"postgres": "8fe65454f387031aa6231faf", -"postgresql": "8fe65454f387031aa6231faf", -"snowflake": "8fe65454f387031aa6231faf", -"tsql": "8fe65454f387031aa6231faf" +"mysql": "9304d412ef114a51a170158d", +"postgres": "9304d412ef114a51a170158d", +"postgresql": "9304d412ef114a51a170158d", +"snowflake": "9304d412ef114a51a170158d", +"tsql": "9304d412ef114a51a170158d" } ], [ @@ -9607,18 +9607,18 @@ [ "type", { -"DuckDB": "c4d601bdc1b1f357b3e9bbdf", -"None": "c4d601bdc1b1f357b3e9bbdf", -"bigquery": "c4d601bdc1b1f357b3e9bbdf", -"databricks": "c4d601bdc1b1f357b3e9bbdf", -"duckdb": "c4d601bdc1b1f357b3e9bbdf", -"generic": "c4d601bdc1b1f357b3e9bbdf", +"DuckDB": "5551318f9b0e622b4b928ca3", +"None": "5551318f9b0e622b4b928ca3", +"bigquery": "5551318f9b0e622b4b928ca3", +"databricks": "5551318f9b0e622b4b928ca3", +"duckdb": "5551318f9b0e622b4b928ca3", +"generic": "5551318f9b0e622b4b928ca3", "motherduck": "901c5133ed47d4769286e860", -"mysql": "c4d601bdc1b1f357b3e9bbdf", -"postgres": "c4d601bdc1b1f357b3e9bbdf", -"postgresql": "c4d601bdc1b1f357b3e9bbdf", -"snowflake": "c4d601bdc1b1f357b3e9bbdf", -"tsql": "c4d601bdc1b1f357b3e9bbdf" +"mysql": "5551318f9b0e622b4b928ca3", +"postgres": "5551318f9b0e622b4b928ca3", +"postgresql": "5551318f9b0e622b4b928ca3", +"snowflake": "5551318f9b0e622b4b928ca3", +"tsql": "5551318f9b0e622b4b928ca3" } ], [ @@ -9828,18 +9828,18 @@ [ "type", { -"DuckDB": "b6b82efd03def9465e4f97b2", -"None": "b6b82efd03def9465e4f97b2", -"bigquery": "b6b82efd03def9465e4f97b2", -"databricks": "b6b82efd03def9465e4f97b2", -"duckdb": "b6b82efd03def9465e4f97b2", -"generic": "b6b82efd03def9465e4f97b2", +"DuckDB": "a8face56f690e623ad76d780", +"None": "a8face56f690e623ad76d780", +"bigquery": "a8face56f690e623ad76d780", +"databricks": "a8face56f690e623ad76d780", +"duckdb": "a8face56f690e623ad76d780", +"generic": "a8face56f690e623ad76d780", "motherduck": "901c5133ed47d4769286e860", -"mysql": "b6b82efd03def9465e4f97b2", -"postgres": "b6b82efd03def9465e4f97b2", -"postgresql": "b6b82efd03def9465e4f97b2", -"snowflake": "b6b82efd03def9465e4f97b2", -"tsql": "b6b82efd03def9465e4f97b2" +"mysql": "a8face56f690e623ad76d780", +"postgres": "a8face56f690e623ad76d780", +"postgresql": "a8face56f690e623ad76d780", +"snowflake": "a8face56f690e623ad76d780", +"tsql": "a8face56f690e623ad76d780" } ], [ @@ -10117,18 +10117,18 @@ [ "type", { -"DuckDB": "4b27937f44f966ff9f3a7c76", -"None": "4b27937f44f966ff9f3a7c76", -"bigquery": "4b27937f44f966ff9f3a7c76", -"databricks": "4b27937f44f966ff9f3a7c76", -"duckdb": "4b27937f44f966ff9f3a7c76", -"generic": "4b27937f44f966ff9f3a7c76", +"DuckDB": "c641dec96086f9f69b467459", +"None": "c641dec96086f9f69b467459", +"bigquery": "c641dec96086f9f69b467459", +"databricks": "c641dec96086f9f69b467459", +"duckdb": "c641dec96086f9f69b467459", +"generic": "c641dec96086f9f69b467459", "motherduck": "901c5133ed47d4769286e860", -"mysql": "4b27937f44f966ff9f3a7c76", -"postgres": "4b27937f44f966ff9f3a7c76", -"postgresql": "4b27937f44f966ff9f3a7c76", -"snowflake": "4b27937f44f966ff9f3a7c76", -"tsql": "4b27937f44f966ff9f3a7c76" +"mysql": "c641dec96086f9f69b467459", +"postgres": "c641dec96086f9f69b467459", +"postgresql": "c641dec96086f9f69b467459", +"snowflake": "c641dec96086f9f69b467459", +"tsql": "c641dec96086f9f69b467459" } ], [ @@ -10236,18 +10236,18 @@ [ "type", { -"DuckDB": "14c32cfb5e5135338d015f42", -"None": "14c32cfb5e5135338d015f42", -"bigquery": "14c32cfb5e5135338d015f42", -"databricks": "14c32cfb5e5135338d015f42", -"duckdb": "14c32cfb5e5135338d015f42", -"generic": "14c32cfb5e5135338d015f42", +"DuckDB": "f8e63510f9a31c4fc8f60cb4", +"None": "f8e63510f9a31c4fc8f60cb4", +"bigquery": "f8e63510f9a31c4fc8f60cb4", +"databricks": "f8e63510f9a31c4fc8f60cb4", +"duckdb": "f8e63510f9a31c4fc8f60cb4", +"generic": "f8e63510f9a31c4fc8f60cb4", "motherduck": "901c5133ed47d4769286e860", -"mysql": "14c32cfb5e5135338d015f42", -"postgres": "14c32cfb5e5135338d015f42", -"postgresql": "14c32cfb5e5135338d015f42", -"snowflake": "14c32cfb5e5135338d015f42", -"tsql": "14c32cfb5e5135338d015f42" +"mysql": "f8e63510f9a31c4fc8f60cb4", +"postgres": "f8e63510f9a31c4fc8f60cb4", +"postgresql": "f8e63510f9a31c4fc8f60cb4", +"snowflake": "f8e63510f9a31c4fc8f60cb4", +"tsql": "f8e63510f9a31c4fc8f60cb4" } ], [ @@ -10865,18 +10865,18 @@ [ "type", { -"DuckDB": "c703e456945a8f06d005c98b", -"None": "c703e456945a8f06d005c98b", -"bigquery": "c703e456945a8f06d005c98b", -"databricks": "c703e456945a8f06d005c98b", -"duckdb": "c703e456945a8f06d005c98b", -"generic": "c703e456945a8f06d005c98b", +"DuckDB": "d1ea13fb2c3d459fff6bf7cd", +"None": "d1ea13fb2c3d459fff6bf7cd", +"bigquery": "d1ea13fb2c3d459fff6bf7cd", +"databricks": "d1ea13fb2c3d459fff6bf7cd", +"duckdb": "d1ea13fb2c3d459fff6bf7cd", +"generic": "d1ea13fb2c3d459fff6bf7cd", "motherduck": "901c5133ed47d4769286e860", -"mysql": "c703e456945a8f06d005c98b", -"postgres": "c703e456945a8f06d005c98b", -"postgresql": "c703e456945a8f06d005c98b", -"snowflake": "c703e456945a8f06d005c98b", -"tsql": "c703e456945a8f06d005c98b" +"mysql": "d1ea13fb2c3d459fff6bf7cd", +"postgres": "d1ea13fb2c3d459fff6bf7cd", +"postgresql": "d1ea13fb2c3d459fff6bf7cd", +"snowflake": "d1ea13fb2c3d459fff6bf7cd", +"tsql": "d1ea13fb2c3d459fff6bf7cd" } ], [ @@ -11307,18 +11307,18 @@ [ "type", { -"DuckDB": "aea8a680fa18770e56bf6220", -"None": "aea8a680fa18770e56bf6220", -"bigquery": "aea8a680fa18770e56bf6220", -"databricks": "aea8a680fa18770e56bf6220", -"duckdb": "aea8a680fa18770e56bf6220", -"generic": "aea8a680fa18770e56bf6220", +"DuckDB": "6fa5d7254a7ed25d37629163", +"None": "6fa5d7254a7ed25d37629163", +"bigquery": "6fa5d7254a7ed25d37629163", +"databricks": "6fa5d7254a7ed25d37629163", +"duckdb": "6fa5d7254a7ed25d37629163", +"generic": "6fa5d7254a7ed25d37629163", "motherduck": "901c5133ed47d4769286e860", -"mysql": "aea8a680fa18770e56bf6220", -"postgres": "aea8a680fa18770e56bf6220", -"postgresql": "aea8a680fa18770e56bf6220", -"snowflake": "aea8a680fa18770e56bf6220", -"tsql": "aea8a680fa18770e56bf6220" +"mysql": "6fa5d7254a7ed25d37629163", +"postgres": "6fa5d7254a7ed25d37629163", +"postgresql": "6fa5d7254a7ed25d37629163", +"snowflake": "6fa5d7254a7ed25d37629163", +"tsql": "6fa5d7254a7ed25d37629163" } ], [ @@ -13534,18 +13534,18 @@ [ "type", { -"DuckDB": "7c11e0be7630905d93bd9ce1", -"None": "7c11e0be7630905d93bd9ce1", -"bigquery": "7c11e0be7630905d93bd9ce1", -"databricks": "7c11e0be7630905d93bd9ce1", -"duckdb": "7c11e0be7630905d93bd9ce1", -"generic": "7c11e0be7630905d93bd9ce1", +"DuckDB": "86825c198b081bf6e208eaa7", +"None": "86825c198b081bf6e208eaa7", +"bigquery": "86825c198b081bf6e208eaa7", +"databricks": "86825c198b081bf6e208eaa7", +"duckdb": "86825c198b081bf6e208eaa7", +"generic": "86825c198b081bf6e208eaa7", "motherduck": "901c5133ed47d4769286e860", -"mysql": "7c11e0be7630905d93bd9ce1", -"postgres": "7c11e0be7630905d93bd9ce1", -"postgresql": "7c11e0be7630905d93bd9ce1", -"snowflake": "7c11e0be7630905d93bd9ce1", -"tsql": "7c11e0be7630905d93bd9ce1" +"mysql": "86825c198b081bf6e208eaa7", +"postgres": "86825c198b081bf6e208eaa7", +"postgresql": "86825c198b081bf6e208eaa7", +"snowflake": "86825c198b081bf6e208eaa7", +"tsql": "86825c198b081bf6e208eaa7" } ], [ @@ -14112,18 +14112,18 @@ [ "type", { -"DuckDB": "b55b02912e30e27dcb40ad93", -"None": "b55b02912e30e27dcb40ad93", -"bigquery": "b55b02912e30e27dcb40ad93", -"databricks": "b55b02912e30e27dcb40ad93", -"duckdb": "b55b02912e30e27dcb40ad93", -"generic": "b55b02912e30e27dcb40ad93", +"DuckDB": "f923673fc4041d12a88250a4", +"None": "f923673fc4041d12a88250a4", +"bigquery": "f923673fc4041d12a88250a4", +"databricks": "f923673fc4041d12a88250a4", +"duckdb": "f923673fc4041d12a88250a4", +"generic": "f923673fc4041d12a88250a4", "motherduck": "901c5133ed47d4769286e860", -"mysql": "b55b02912e30e27dcb40ad93", -"postgres": "b55b02912e30e27dcb40ad93", -"postgresql": "b55b02912e30e27dcb40ad93", -"snowflake": "b55b02912e30e27dcb40ad93", -"tsql": "b55b02912e30e27dcb40ad93" +"mysql": "f923673fc4041d12a88250a4", +"postgres": "f923673fc4041d12a88250a4", +"postgresql": "f923673fc4041d12a88250a4", +"snowflake": "f923673fc4041d12a88250a4", +"tsql": "f923673fc4041d12a88250a4" } ], [ @@ -15234,35 +15234,35 @@ [ "type", { -"DuckDB": "8917b70db141a02ac2e2bb1e", -"None": "8917b70db141a02ac2e2bb1e", -"bigquery": "8917b70db141a02ac2e2bb1e", -"databricks": "8917b70db141a02ac2e2bb1e", -"duckdb": "8917b70db141a02ac2e2bb1e", -"generic": "8917b70db141a02ac2e2bb1e", +"DuckDB": "9122e0d12e93512217da87fa", +"None": "9122e0d12e93512217da87fa", +"bigquery": "9122e0d12e93512217da87fa", +"databricks": "9122e0d12e93512217da87fa", +"duckdb": "9122e0d12e93512217da87fa", +"generic": "9122e0d12e93512217da87fa", "motherduck": "901c5133ed47d4769286e860", -"mysql": "8917b70db141a02ac2e2bb1e", -"postgres": "8917b70db141a02ac2e2bb1e", -"postgresql": "8917b70db141a02ac2e2bb1e", -"snowflake": "8917b70db141a02ac2e2bb1e", -"tsql": "8917b70db141a02ac2e2bb1e" +"mysql": "9122e0d12e93512217da87fa", +"postgres": "9122e0d12e93512217da87fa", +"postgresql": "9122e0d12e93512217da87fa", +"snowflake": "9122e0d12e93512217da87fa", +"tsql": "9122e0d12e93512217da87fa" } ], [ "type", { -"DuckDB": "ef480cafe4d260b70ce5214c", -"None": "ef480cafe4d260b70ce5214c", -"bigquery": "ef480cafe4d260b70ce5214c", -"databricks": "ef480cafe4d260b70ce5214c", -"duckdb": "ef480cafe4d260b70ce5214c", -"generic": "ef480cafe4d260b70ce5214c", +"DuckDB": "b89338179329ef976f911a46", +"None": "b89338179329ef976f911a46", +"bigquery": "b89338179329ef976f911a46", +"databricks": "b89338179329ef976f911a46", +"duckdb": "b89338179329ef976f911a46", +"generic": "b89338179329ef976f911a46", "motherduck": "901c5133ed47d4769286e860", -"mysql": "ef480cafe4d260b70ce5214c", -"postgres": "ef480cafe4d260b70ce5214c", -"postgresql": "ef480cafe4d260b70ce5214c", -"snowflake": "ef480cafe4d260b70ce5214c", -"tsql": "ef480cafe4d260b70ce5214c" +"mysql": "b89338179329ef976f911a46", +"postgres": "b89338179329ef976f911a46", +"postgresql": "b89338179329ef976f911a46", +"snowflake": "b89338179329ef976f911a46", +"tsql": "b89338179329ef976f911a46" } ], [ @@ -15846,18 +15846,18 @@ [ "type", { -"DuckDB": "d44c023c720f385769f2ab81", -"None": "d44c023c720f385769f2ab81", -"bigquery": "d44c023c720f385769f2ab81", -"databricks": "d44c023c720f385769f2ab81", -"duckdb": "d44c023c720f385769f2ab81", -"generic": "d44c023c720f385769f2ab81", +"DuckDB": "63904421f84671303411fe93", +"None": "63904421f84671303411fe93", +"bigquery": "63904421f84671303411fe93", +"databricks": "63904421f84671303411fe93", +"duckdb": "63904421f84671303411fe93", +"generic": "63904421f84671303411fe93", "motherduck": "901c5133ed47d4769286e860", -"mysql": "d44c023c720f385769f2ab81", -"postgres": "d44c023c720f385769f2ab81", -"postgresql": "d44c023c720f385769f2ab81", -"snowflake": "d44c023c720f385769f2ab81", -"tsql": "d44c023c720f385769f2ab81" +"mysql": "63904421f84671303411fe93", +"postgres": "63904421f84671303411fe93", +"postgresql": "63904421f84671303411fe93", +"snowflake": "63904421f84671303411fe93", +"tsql": "63904421f84671303411fe93" } ], [ @@ -16288,35 +16288,35 @@ [ "type", { -"DuckDB": "24b677b214ecdacdb007642e", -"None": "24b677b214ecdacdb007642e", -"bigquery": "24b677b214ecdacdb007642e", -"databricks": "24b677b214ecdacdb007642e", -"duckdb": "24b677b214ecdacdb007642e", -"generic": "24b677b214ecdacdb007642e", +"DuckDB": "15897ece5a8fa1f12e32bed8", +"None": "15897ece5a8fa1f12e32bed8", +"bigquery": "15897ece5a8fa1f12e32bed8", +"databricks": "15897ece5a8fa1f12e32bed8", +"duckdb": "15897ece5a8fa1f12e32bed8", +"generic": "15897ece5a8fa1f12e32bed8", "motherduck": "901c5133ed47d4769286e860", -"mysql": "24b677b214ecdacdb007642e", -"postgres": "24b677b214ecdacdb007642e", -"postgresql": "24b677b214ecdacdb007642e", -"snowflake": "24b677b214ecdacdb007642e", -"tsql": "24b677b214ecdacdb007642e" +"mysql": "15897ece5a8fa1f12e32bed8", +"postgres": "15897ece5a8fa1f12e32bed8", +"postgresql": "15897ece5a8fa1f12e32bed8", +"snowflake": "15897ece5a8fa1f12e32bed8", +"tsql": "15897ece5a8fa1f12e32bed8" } ], [ "type", { -"DuckDB": "4c2128e6078118fd0d3856fa", -"None": "4c2128e6078118fd0d3856fa", -"bigquery": "4c2128e6078118fd0d3856fa", -"databricks": "4c2128e6078118fd0d3856fa", -"duckdb": "4c2128e6078118fd0d3856fa", -"generic": "4c2128e6078118fd0d3856fa", +"DuckDB": "2377bd0302f3eba296b44fee", +"None": "2377bd0302f3eba296b44fee", +"bigquery": "2377bd0302f3eba296b44fee", +"databricks": "2377bd0302f3eba296b44fee", +"duckdb": "2377bd0302f3eba296b44fee", +"generic": "2377bd0302f3eba296b44fee", "motherduck": "901c5133ed47d4769286e860", -"mysql": "4c2128e6078118fd0d3856fa", -"postgres": "4c2128e6078118fd0d3856fa", -"postgresql": "4c2128e6078118fd0d3856fa", -"snowflake": "4c2128e6078118fd0d3856fa", -"tsql": "4c2128e6078118fd0d3856fa" +"mysql": "2377bd0302f3eba296b44fee", +"postgres": "2377bd0302f3eba296b44fee", +"postgresql": "2377bd0302f3eba296b44fee", +"snowflake": "2377bd0302f3eba296b44fee", +"tsql": "2377bd0302f3eba296b44fee" } ], [ @@ -16469,7 +16469,7 @@ "postgres": "6f851103102e7b986535376f", "postgresql": "6f851103102e7b986535376f", "snowflake": "87800133cac38d9119180c0a", -"tsql": "0e9ef5373e5d52970e8e1656" +"tsql": "6ec12741f1435c1a4011e935" } ], [ @@ -16543,18 +16543,18 @@ [ "type", { -"DuckDB": "3009be83c84cdfc92bbb075e", -"None": "3009be83c84cdfc92bbb075e", -"bigquery": "3009be83c84cdfc92bbb075e", -"databricks": "3009be83c84cdfc92bbb075e", -"duckdb": "3009be83c84cdfc92bbb075e", -"generic": "3009be83c84cdfc92bbb075e", +"DuckDB": "f9afa4d07d3af890563ba291", +"None": "f9afa4d07d3af890563ba291", +"bigquery": "f9afa4d07d3af890563ba291", +"databricks": "f9afa4d07d3af890563ba291", +"duckdb": "f9afa4d07d3af890563ba291", +"generic": "f9afa4d07d3af890563ba291", "motherduck": "901c5133ed47d4769286e860", -"mysql": "3009be83c84cdfc92bbb075e", -"postgres": "3009be83c84cdfc92bbb075e", -"postgresql": "3009be83c84cdfc92bbb075e", -"snowflake": "3009be83c84cdfc92bbb075e", -"tsql": "3009be83c84cdfc92bbb075e" +"mysql": "f9afa4d07d3af890563ba291", +"postgres": "f9afa4d07d3af890563ba291", +"postgresql": "f9afa4d07d3af890563ba291", +"snowflake": "f9afa4d07d3af890563ba291", +"tsql": "f9afa4d07d3af890563ba291" } ], [ @@ -16747,18 +16747,18 @@ [ "type", { -"DuckDB": "07f8200cee37aa0f6990da3d", -"None": "07f8200cee37aa0f6990da3d", -"bigquery": "07f8200cee37aa0f6990da3d", -"databricks": "07f8200cee37aa0f6990da3d", -"duckdb": "07f8200cee37aa0f6990da3d", -"generic": "07f8200cee37aa0f6990da3d", +"DuckDB": "293b92524b77d21b6865addc", +"None": "293b92524b77d21b6865addc", +"bigquery": "293b92524b77d21b6865addc", +"databricks": "293b92524b77d21b6865addc", +"duckdb": "293b92524b77d21b6865addc", +"generic": "293b92524b77d21b6865addc", "motherduck": "901c5133ed47d4769286e860", -"mysql": "07f8200cee37aa0f6990da3d", -"postgres": "07f8200cee37aa0f6990da3d", -"postgresql": "07f8200cee37aa0f6990da3d", -"snowflake": "07f8200cee37aa0f6990da3d", -"tsql": "07f8200cee37aa0f6990da3d" +"mysql": "293b92524b77d21b6865addc", +"postgres": "293b92524b77d21b6865addc", +"postgresql": "293b92524b77d21b6865addc", +"snowflake": "293b92524b77d21b6865addc", +"tsql": "293b92524b77d21b6865addc" } ], [ @@ -17342,18 +17342,18 @@ [ "type", { -"DuckDB": "d03c5e5797a8cbc0c6a81112", -"None": "d03c5e5797a8cbc0c6a81112", -"bigquery": "d03c5e5797a8cbc0c6a81112", -"databricks": "d03c5e5797a8cbc0c6a81112", -"duckdb": "d03c5e5797a8cbc0c6a81112", -"generic": "d03c5e5797a8cbc0c6a81112", +"DuckDB": "02a19edb16071249930beb5e", +"None": "02a19edb16071249930beb5e", +"bigquery": "02a19edb16071249930beb5e", +"databricks": "02a19edb16071249930beb5e", +"duckdb": "02a19edb16071249930beb5e", +"generic": "02a19edb16071249930beb5e", "motherduck": "901c5133ed47d4769286e860", -"mysql": "d03c5e5797a8cbc0c6a81112", -"postgres": "d03c5e5797a8cbc0c6a81112", -"postgresql": "d03c5e5797a8cbc0c6a81112", -"snowflake": "d03c5e5797a8cbc0c6a81112", -"tsql": "d03c5e5797a8cbc0c6a81112" +"mysql": "02a19edb16071249930beb5e", +"postgres": "02a19edb16071249930beb5e", +"postgresql": "02a19edb16071249930beb5e", +"snowflake": "02a19edb16071249930beb5e", +"tsql": "02a19edb16071249930beb5e" } ], [ @@ -18651,18 +18651,18 @@ [ "type", { -"DuckDB": "d043825e95555167dc033b5f", -"None": "d043825e95555167dc033b5f", -"bigquery": "d043825e95555167dc033b5f", -"databricks": "d043825e95555167dc033b5f", -"duckdb": "d043825e95555167dc033b5f", -"generic": "d043825e95555167dc033b5f", +"DuckDB": "2726169407c1e410edb2f532", +"None": "2726169407c1e410edb2f532", +"bigquery": "2726169407c1e410edb2f532", +"databricks": "2726169407c1e410edb2f532", +"duckdb": "2726169407c1e410edb2f532", +"generic": "2726169407c1e410edb2f532", "motherduck": "901c5133ed47d4769286e860", -"mysql": "d043825e95555167dc033b5f", -"postgres": "d043825e95555167dc033b5f", -"postgresql": "d043825e95555167dc033b5f", -"snowflake": "d043825e95555167dc033b5f", -"tsql": "d043825e95555167dc033b5f" +"mysql": "2726169407c1e410edb2f532", +"postgres": "2726169407c1e410edb2f532", +"postgresql": "2726169407c1e410edb2f532", +"snowflake": "2726169407c1e410edb2f532", +"tsql": "2726169407c1e410edb2f532" } ], [ @@ -18906,18 +18906,18 @@ [ "type", { -"DuckDB": "200c00626c910f6fd6177bb5", -"None": "200c00626c910f6fd6177bb5", -"bigquery": "200c00626c910f6fd6177bb5", -"databricks": "200c00626c910f6fd6177bb5", -"duckdb": "200c00626c910f6fd6177bb5", -"generic": "200c00626c910f6fd6177bb5", +"DuckDB": "e8e44701a29d5f80cd954213", +"None": "e8e44701a29d5f80cd954213", +"bigquery": "e8e44701a29d5f80cd954213", +"databricks": "e8e44701a29d5f80cd954213", +"duckdb": "e8e44701a29d5f80cd954213", +"generic": "e8e44701a29d5f80cd954213", "motherduck": "901c5133ed47d4769286e860", -"mysql": "200c00626c910f6fd6177bb5", -"postgres": "200c00626c910f6fd6177bb5", -"postgresql": "200c00626c910f6fd6177bb5", -"snowflake": "200c00626c910f6fd6177bb5", -"tsql": "200c00626c910f6fd6177bb5" +"mysql": "e8e44701a29d5f80cd954213", +"postgres": "e8e44701a29d5f80cd954213", +"postgresql": "e8e44701a29d5f80cd954213", +"snowflake": "e8e44701a29d5f80cd954213", +"tsql": "e8e44701a29d5f80cd954213" } ], [ @@ -19008,18 +19008,18 @@ [ "type", { -"DuckDB": "55a086bc8305ad303017ae10", -"None": "55a086bc8305ad303017ae10", -"bigquery": "55a086bc8305ad303017ae10", -"databricks": "55a086bc8305ad303017ae10", -"duckdb": "55a086bc8305ad303017ae10", -"generic": "55a086bc8305ad303017ae10", +"DuckDB": "d077d7ce7054943437bc495a", +"None": "d077d7ce7054943437bc495a", +"bigquery": "d077d7ce7054943437bc495a", +"databricks": "d077d7ce7054943437bc495a", +"duckdb": "d077d7ce7054943437bc495a", +"generic": "d077d7ce7054943437bc495a", "motherduck": "901c5133ed47d4769286e860", -"mysql": "55a086bc8305ad303017ae10", -"postgres": "55a086bc8305ad303017ae10", -"postgresql": "55a086bc8305ad303017ae10", -"snowflake": "55a086bc8305ad303017ae10", -"tsql": "55a086bc8305ad303017ae10" +"mysql": "d077d7ce7054943437bc495a", +"postgres": "d077d7ce7054943437bc495a", +"postgresql": "d077d7ce7054943437bc495a", +"snowflake": "d077d7ce7054943437bc495a", +"tsql": "d077d7ce7054943437bc495a" } ], [ @@ -19042,18 +19042,18 @@ [ "type", { -"DuckDB": "d3b6a6835220f9cedfa363c7", -"None": "d3b6a6835220f9cedfa363c7", -"bigquery": "d3b6a6835220f9cedfa363c7", -"databricks": "d3b6a6835220f9cedfa363c7", -"duckdb": "d3b6a6835220f9cedfa363c7", -"generic": "d3b6a6835220f9cedfa363c7", +"DuckDB": "9d9ab720ece92a330f5bf049", +"None": "9d9ab720ece92a330f5bf049", +"bigquery": "9d9ab720ece92a330f5bf049", +"databricks": "9d9ab720ece92a330f5bf049", +"duckdb": "9d9ab720ece92a330f5bf049", +"generic": "9d9ab720ece92a330f5bf049", "motherduck": "901c5133ed47d4769286e860", -"mysql": "d3b6a6835220f9cedfa363c7", -"postgres": "d3b6a6835220f9cedfa363c7", -"postgresql": "d3b6a6835220f9cedfa363c7", -"snowflake": "d3b6a6835220f9cedfa363c7", -"tsql": "d3b6a6835220f9cedfa363c7" +"mysql": "9d9ab720ece92a330f5bf049", +"postgres": "9d9ab720ece92a330f5bf049", +"postgresql": "9d9ab720ece92a330f5bf049", +"snowflake": "9d9ab720ece92a330f5bf049", +"tsql": "9d9ab720ece92a330f5bf049" } ], [ @@ -19246,18 +19246,18 @@ [ "type", { -"DuckDB": "e5199d22ebbd1383c1e821df", -"None": "e5199d22ebbd1383c1e821df", -"bigquery": "e5199d22ebbd1383c1e821df", -"databricks": "e5199d22ebbd1383c1e821df", -"duckdb": "e5199d22ebbd1383c1e821df", -"generic": "e5199d22ebbd1383c1e821df", +"DuckDB": "5a46370a4920d34e81123c9a", +"None": "5a46370a4920d34e81123c9a", +"bigquery": "5a46370a4920d34e81123c9a", +"databricks": "5a46370a4920d34e81123c9a", +"duckdb": "5a46370a4920d34e81123c9a", +"generic": "5a46370a4920d34e81123c9a", "motherduck": "901c5133ed47d4769286e860", -"mysql": "e5199d22ebbd1383c1e821df", -"postgres": "e5199d22ebbd1383c1e821df", -"postgresql": "e5199d22ebbd1383c1e821df", -"snowflake": "e5199d22ebbd1383c1e821df", -"tsql": "e5199d22ebbd1383c1e821df" +"mysql": "5a46370a4920d34e81123c9a", +"postgres": "5a46370a4920d34e81123c9a", +"postgresql": "5a46370a4920d34e81123c9a", +"snowflake": "5a46370a4920d34e81123c9a", +"tsql": "5a46370a4920d34e81123c9a" } ], [ @@ -19314,18 +19314,18 @@ [ "type", { -"DuckDB": "d91c8f17eb347629690400c7", -"None": "d91c8f17eb347629690400c7", -"bigquery": "d91c8f17eb347629690400c7", -"databricks": "d91c8f17eb347629690400c7", -"duckdb": "d91c8f17eb347629690400c7", -"generic": "d91c8f17eb347629690400c7", +"DuckDB": "a908355fb520168da033bacc", +"None": "a908355fb520168da033bacc", +"bigquery": "a908355fb520168da033bacc", +"databricks": "a908355fb520168da033bacc", +"duckdb": "a908355fb520168da033bacc", +"generic": "a908355fb520168da033bacc", "motherduck": "901c5133ed47d4769286e860", -"mysql": "d91c8f17eb347629690400c7", -"postgres": "d91c8f17eb347629690400c7", -"postgresql": "d91c8f17eb347629690400c7", -"snowflake": "d91c8f17eb347629690400c7", -"tsql": "d91c8f17eb347629690400c7" +"mysql": "a908355fb520168da033bacc", +"postgres": "a908355fb520168da033bacc", +"postgresql": "a908355fb520168da033bacc", +"snowflake": "a908355fb520168da033bacc", +"tsql": "a908355fb520168da033bacc" } ], [ @@ -19467,18 +19467,18 @@ [ "type", { -"DuckDB": "fd31c8e468772027804119ec", -"None": "fd31c8e468772027804119ec", -"bigquery": "fd31c8e468772027804119ec", -"databricks": "fd31c8e468772027804119ec", -"duckdb": "fd31c8e468772027804119ec", -"generic": "fd31c8e468772027804119ec", +"DuckDB": "7fe23b4f83ff4afac4eae8da", +"None": "7fe23b4f83ff4afac4eae8da", +"bigquery": "7fe23b4f83ff4afac4eae8da", +"databricks": "7fe23b4f83ff4afac4eae8da", +"duckdb": "7fe23b4f83ff4afac4eae8da", +"generic": "7fe23b4f83ff4afac4eae8da", "motherduck": "901c5133ed47d4769286e860", -"mysql": "fd31c8e468772027804119ec", -"postgres": "fd31c8e468772027804119ec", -"postgresql": "fd31c8e468772027804119ec", -"snowflake": "fd31c8e468772027804119ec", -"tsql": "fd31c8e468772027804119ec" +"mysql": "7fe23b4f83ff4afac4eae8da", +"postgres": "7fe23b4f83ff4afac4eae8da", +"postgresql": "7fe23b4f83ff4afac4eae8da", +"snowflake": "7fe23b4f83ff4afac4eae8da", +"tsql": "7fe23b4f83ff4afac4eae8da" } ], [ @@ -19790,18 +19790,18 @@ [ "type", { -"DuckDB": "4d90ccac34ef123bb0b2a15c", -"None": "4d90ccac34ef123bb0b2a15c", -"bigquery": "4d90ccac34ef123bb0b2a15c", -"databricks": "4d90ccac34ef123bb0b2a15c", -"duckdb": "4d90ccac34ef123bb0b2a15c", -"generic": "4d90ccac34ef123bb0b2a15c", +"DuckDB": "f1fdef11fa8bdd4185ea7944", +"None": "f1fdef11fa8bdd4185ea7944", +"bigquery": "f1fdef11fa8bdd4185ea7944", +"databricks": "f1fdef11fa8bdd4185ea7944", +"duckdb": "f1fdef11fa8bdd4185ea7944", +"generic": "f1fdef11fa8bdd4185ea7944", "motherduck": "901c5133ed47d4769286e860", -"mysql": "4d90ccac34ef123bb0b2a15c", -"postgres": "4d90ccac34ef123bb0b2a15c", -"postgresql": "4d90ccac34ef123bb0b2a15c", -"snowflake": "4d90ccac34ef123bb0b2a15c", -"tsql": "4d90ccac34ef123bb0b2a15c" +"mysql": "f1fdef11fa8bdd4185ea7944", +"postgres": "f1fdef11fa8bdd4185ea7944", +"postgresql": "f1fdef11fa8bdd4185ea7944", +"snowflake": "f1fdef11fa8bdd4185ea7944", +"tsql": "f1fdef11fa8bdd4185ea7944" } ], [ @@ -19960,18 +19960,18 @@ [ "type", { -"DuckDB": "2e5b1ffe62d65d4d8c389ecd", -"None": "2e5b1ffe62d65d4d8c389ecd", -"bigquery": "2e5b1ffe62d65d4d8c389ecd", -"databricks": "2e5b1ffe62d65d4d8c389ecd", -"duckdb": "2e5b1ffe62d65d4d8c389ecd", -"generic": "2e5b1ffe62d65d4d8c389ecd", +"DuckDB": "769fe9b23909b49818dba3fc", +"None": "769fe9b23909b49818dba3fc", +"bigquery": "769fe9b23909b49818dba3fc", +"databricks": "769fe9b23909b49818dba3fc", +"duckdb": "769fe9b23909b49818dba3fc", +"generic": "769fe9b23909b49818dba3fc", "motherduck": "901c5133ed47d4769286e860", -"mysql": "2e5b1ffe62d65d4d8c389ecd", -"postgres": "2e5b1ffe62d65d4d8c389ecd", -"postgresql": "2e5b1ffe62d65d4d8c389ecd", -"snowflake": "2e5b1ffe62d65d4d8c389ecd", -"tsql": "2e5b1ffe62d65d4d8c389ecd" +"mysql": "769fe9b23909b49818dba3fc", +"postgres": "769fe9b23909b49818dba3fc", +"postgresql": "769fe9b23909b49818dba3fc", +"snowflake": "769fe9b23909b49818dba3fc", +"tsql": "769fe9b23909b49818dba3fc" } ], [ @@ -20776,18 +20776,18 @@ [ "type", { -"DuckDB": "78709b3317abd96cc20340f7", -"None": "78709b3317abd96cc20340f7", -"bigquery": "78709b3317abd96cc20340f7", -"databricks": "78709b3317abd96cc20340f7", -"duckdb": "78709b3317abd96cc20340f7", -"generic": "78709b3317abd96cc20340f7", +"DuckDB": "7bd88d7cc8f9a92e789a9f44", +"None": "7bd88d7cc8f9a92e789a9f44", +"bigquery": "7bd88d7cc8f9a92e789a9f44", +"databricks": "7bd88d7cc8f9a92e789a9f44", +"duckdb": "7bd88d7cc8f9a92e789a9f44", +"generic": "7bd88d7cc8f9a92e789a9f44", "motherduck": "901c5133ed47d4769286e860", -"mysql": "78709b3317abd96cc20340f7", -"postgres": "78709b3317abd96cc20340f7", -"postgresql": "78709b3317abd96cc20340f7", -"snowflake": "78709b3317abd96cc20340f7", -"tsql": "78709b3317abd96cc20340f7" +"mysql": "7bd88d7cc8f9a92e789a9f44", +"postgres": "7bd88d7cc8f9a92e789a9f44", +"postgresql": "7bd88d7cc8f9a92e789a9f44", +"snowflake": "7bd88d7cc8f9a92e789a9f44", +"tsql": "7bd88d7cc8f9a92e789a9f44" } ], [ @@ -20929,18 +20929,18 @@ [ "type", { -"DuckDB": "dcbfb83ead5b0e32e500635b", -"None": "dcbfb83ead5b0e32e500635b", -"bigquery": "dcbfb83ead5b0e32e500635b", -"databricks": "dcbfb83ead5b0e32e500635b", -"duckdb": "dcbfb83ead5b0e32e500635b", -"generic": "dcbfb83ead5b0e32e500635b", +"DuckDB": "6672095306335323ebbfa61b", +"None": "6672095306335323ebbfa61b", +"bigquery": "6672095306335323ebbfa61b", +"databricks": "6672095306335323ebbfa61b", +"duckdb": "6672095306335323ebbfa61b", +"generic": "6672095306335323ebbfa61b", "motherduck": "901c5133ed47d4769286e860", -"mysql": "dcbfb83ead5b0e32e500635b", -"postgres": "dcbfb83ead5b0e32e500635b", -"postgresql": "dcbfb83ead5b0e32e500635b", -"snowflake": "dcbfb83ead5b0e32e500635b", -"tsql": "dcbfb83ead5b0e32e500635b" +"mysql": "6672095306335323ebbfa61b", +"postgres": "6672095306335323ebbfa61b", +"postgresql": "6672095306335323ebbfa61b", +"snowflake": "6672095306335323ebbfa61b", +"tsql": "6672095306335323ebbfa61b" } ], [ @@ -21490,18 +21490,18 @@ [ "type", { -"DuckDB": "277693b7c19b2696f01403ba", -"None": "277693b7c19b2696f01403ba", -"bigquery": "277693b7c19b2696f01403ba", -"databricks": "277693b7c19b2696f01403ba", -"duckdb": "277693b7c19b2696f01403ba", -"generic": "277693b7c19b2696f01403ba", +"DuckDB": "1a590fb9ec69f70166bed628", +"None": "1a590fb9ec69f70166bed628", +"bigquery": "1a590fb9ec69f70166bed628", +"databricks": "1a590fb9ec69f70166bed628", +"duckdb": "1a590fb9ec69f70166bed628", +"generic": "1a590fb9ec69f70166bed628", "motherduck": "901c5133ed47d4769286e860", -"mysql": "277693b7c19b2696f01403ba", -"postgres": "277693b7c19b2696f01403ba", -"postgresql": "277693b7c19b2696f01403ba", -"snowflake": "277693b7c19b2696f01403ba", -"tsql": "277693b7c19b2696f01403ba" +"mysql": "1a590fb9ec69f70166bed628", +"postgres": "1a590fb9ec69f70166bed628", +"postgresql": "1a590fb9ec69f70166bed628", +"snowflake": "1a590fb9ec69f70166bed628", +"tsql": "1a590fb9ec69f70166bed628" } ], [ @@ -22085,18 +22085,18 @@ [ "type", { -"DuckDB": "126765943545b2608ebe7128", -"None": "126765943545b2608ebe7128", -"bigquery": "126765943545b2608ebe7128", -"databricks": "126765943545b2608ebe7128", -"duckdb": "126765943545b2608ebe7128", -"generic": "126765943545b2608ebe7128", +"DuckDB": "fd0efa34f8363aad442b368c", +"None": "fd0efa34f8363aad442b368c", +"bigquery": "fd0efa34f8363aad442b368c", +"databricks": "fd0efa34f8363aad442b368c", +"duckdb": "fd0efa34f8363aad442b368c", +"generic": "fd0efa34f8363aad442b368c", "motherduck": "901c5133ed47d4769286e860", -"mysql": "126765943545b2608ebe7128", -"postgres": "126765943545b2608ebe7128", -"postgresql": "126765943545b2608ebe7128", -"snowflake": "126765943545b2608ebe7128", -"tsql": "126765943545b2608ebe7128" +"mysql": "fd0efa34f8363aad442b368c", +"postgres": "fd0efa34f8363aad442b368c", +"postgresql": "fd0efa34f8363aad442b368c", +"snowflake": "fd0efa34f8363aad442b368c", +"tsql": "fd0efa34f8363aad442b368c" } ], [ @@ -22442,18 +22442,18 @@ [ "type", { -"DuckDB": "cb06a5553de70a4924ed6600", -"None": "cb06a5553de70a4924ed6600", -"bigquery": "cb06a5553de70a4924ed6600", -"databricks": "cb06a5553de70a4924ed6600", -"duckdb": "cb06a5553de70a4924ed6600", -"generic": "cb06a5553de70a4924ed6600", +"DuckDB": "5aa2b9544add6f15c3e51f3e", +"None": "5aa2b9544add6f15c3e51f3e", +"bigquery": "5aa2b9544add6f15c3e51f3e", +"databricks": "5aa2b9544add6f15c3e51f3e", +"duckdb": "5aa2b9544add6f15c3e51f3e", +"generic": "5aa2b9544add6f15c3e51f3e", "motherduck": "901c5133ed47d4769286e860", -"mysql": "cb06a5553de70a4924ed6600", -"postgres": "cb06a5553de70a4924ed6600", -"postgresql": "cb06a5553de70a4924ed6600", -"snowflake": "cb06a5553de70a4924ed6600", -"tsql": "cb06a5553de70a4924ed6600" +"mysql": "5aa2b9544add6f15c3e51f3e", +"postgres": "5aa2b9544add6f15c3e51f3e", +"postgresql": "5aa2b9544add6f15c3e51f3e", +"snowflake": "5aa2b9544add6f15c3e51f3e", +"tsql": "5aa2b9544add6f15c3e51f3e" } ], [ @@ -22731,18 +22731,18 @@ [ "type", { -"DuckDB": "e2eee356d05f550b28bf85ad", -"None": "e2eee356d05f550b28bf85ad", -"bigquery": "e2eee356d05f550b28bf85ad", -"databricks": "e2eee356d05f550b28bf85ad", -"duckdb": "e2eee356d05f550b28bf85ad", -"generic": "e2eee356d05f550b28bf85ad", +"DuckDB": "6f1f2ad566432a6140ba8ac3", +"None": "6f1f2ad566432a6140ba8ac3", +"bigquery": "6f1f2ad566432a6140ba8ac3", +"databricks": "6f1f2ad566432a6140ba8ac3", +"duckdb": "6f1f2ad566432a6140ba8ac3", +"generic": "6f1f2ad566432a6140ba8ac3", "motherduck": "901c5133ed47d4769286e860", -"mysql": "e2eee356d05f550b28bf85ad", -"postgres": "e2eee356d05f550b28bf85ad", -"postgresql": "e2eee356d05f550b28bf85ad", -"snowflake": "e2eee356d05f550b28bf85ad", -"tsql": "e2eee356d05f550b28bf85ad" +"mysql": "6f1f2ad566432a6140ba8ac3", +"postgres": "6f1f2ad566432a6140ba8ac3", +"postgresql": "6f1f2ad566432a6140ba8ac3", +"snowflake": "6f1f2ad566432a6140ba8ac3", +"tsql": "6f1f2ad566432a6140ba8ac3" } ], [ @@ -22816,18 +22816,18 @@ [ "type", { -"DuckDB": "cd9c38fc2cf84c8551e29278", -"None": "cd9c38fc2cf84c8551e29278", -"bigquery": "cd9c38fc2cf84c8551e29278", -"databricks": "cd9c38fc2cf84c8551e29278", -"duckdb": "cd9c38fc2cf84c8551e29278", -"generic": "cd9c38fc2cf84c8551e29278", +"DuckDB": "17c9ac03f8051f8cd6cc398f", +"None": "17c9ac03f8051f8cd6cc398f", +"bigquery": "17c9ac03f8051f8cd6cc398f", +"databricks": "17c9ac03f8051f8cd6cc398f", +"duckdb": "17c9ac03f8051f8cd6cc398f", +"generic": "17c9ac03f8051f8cd6cc398f", "motherduck": "901c5133ed47d4769286e860", -"mysql": "cd9c38fc2cf84c8551e29278", -"postgres": "cd9c38fc2cf84c8551e29278", -"postgresql": "cd9c38fc2cf84c8551e29278", -"snowflake": "cd9c38fc2cf84c8551e29278", -"tsql": "cd9c38fc2cf84c8551e29278" +"mysql": "17c9ac03f8051f8cd6cc398f", +"postgres": "17c9ac03f8051f8cd6cc398f", +"postgresql": "17c9ac03f8051f8cd6cc398f", +"snowflake": "17c9ac03f8051f8cd6cc398f", +"tsql": "17c9ac03f8051f8cd6cc398f" } ], [ @@ -22918,18 +22918,18 @@ [ "type", { -"DuckDB": "1fdc0b9f28b115c175e88d46", -"None": "1fdc0b9f28b115c175e88d46", -"bigquery": "1fdc0b9f28b115c175e88d46", -"databricks": "1fdc0b9f28b115c175e88d46", -"duckdb": "1fdc0b9f28b115c175e88d46", -"generic": "1fdc0b9f28b115c175e88d46", +"DuckDB": "c748aee20c9eae68e05429a3", +"None": "c748aee20c9eae68e05429a3", +"bigquery": "c748aee20c9eae68e05429a3", +"databricks": "c748aee20c9eae68e05429a3", +"duckdb": "c748aee20c9eae68e05429a3", +"generic": "c748aee20c9eae68e05429a3", "motherduck": "901c5133ed47d4769286e860", -"mysql": "1fdc0b9f28b115c175e88d46", -"postgres": "1fdc0b9f28b115c175e88d46", -"postgresql": "1fdc0b9f28b115c175e88d46", -"snowflake": "1fdc0b9f28b115c175e88d46", -"tsql": "1fdc0b9f28b115c175e88d46" +"mysql": "c748aee20c9eae68e05429a3", +"postgres": "c748aee20c9eae68e05429a3", +"postgresql": "c748aee20c9eae68e05429a3", +"snowflake": "c748aee20c9eae68e05429a3", +"tsql": "c748aee20c9eae68e05429a3" } ], [ @@ -22969,18 +22969,18 @@ [ "type", { -"DuckDB": "c0d59e65280108f563dbb04b", -"None": "c0d59e65280108f563dbb04b", -"bigquery": "c0d59e65280108f563dbb04b", -"databricks": "c0d59e65280108f563dbb04b", -"duckdb": "c0d59e65280108f563dbb04b", -"generic": "c0d59e65280108f563dbb04b", +"DuckDB": "ac2d267e58647dba68e3f231", +"None": "ac2d267e58647dba68e3f231", +"bigquery": "ac2d267e58647dba68e3f231", +"databricks": "ac2d267e58647dba68e3f231", +"duckdb": "ac2d267e58647dba68e3f231", +"generic": "ac2d267e58647dba68e3f231", "motherduck": "901c5133ed47d4769286e860", -"mysql": "c0d59e65280108f563dbb04b", -"postgres": "c0d59e65280108f563dbb04b", -"postgresql": "c0d59e65280108f563dbb04b", -"snowflake": "c0d59e65280108f563dbb04b", -"tsql": "c0d59e65280108f563dbb04b" +"mysql": "ac2d267e58647dba68e3f231", +"postgres": "ac2d267e58647dba68e3f231", +"postgresql": "ac2d267e58647dba68e3f231", +"snowflake": "ac2d267e58647dba68e3f231", +"tsql": "ac2d267e58647dba68e3f231" } ], [ @@ -23904,18 +23904,18 @@ [ "type", { -"DuckDB": "7a00586f9fdb1d97256bfff8", -"None": "7a00586f9fdb1d97256bfff8", -"bigquery": "7a00586f9fdb1d97256bfff8", -"databricks": "7a00586f9fdb1d97256bfff8", -"duckdb": "7a00586f9fdb1d97256bfff8", -"generic": "7a00586f9fdb1d97256bfff8", +"DuckDB": "c2ad40bc8b050424142cf287", +"None": "c2ad40bc8b050424142cf287", +"bigquery": "c2ad40bc8b050424142cf287", +"databricks": "c2ad40bc8b050424142cf287", +"duckdb": "c2ad40bc8b050424142cf287", +"generic": "c2ad40bc8b050424142cf287", "motherduck": "901c5133ed47d4769286e860", -"mysql": "7a00586f9fdb1d97256bfff8", -"postgres": "7a00586f9fdb1d97256bfff8", -"postgresql": "7a00586f9fdb1d97256bfff8", -"snowflake": "7a00586f9fdb1d97256bfff8", -"tsql": "7a00586f9fdb1d97256bfff8" +"mysql": "c2ad40bc8b050424142cf287", +"postgres": "c2ad40bc8b050424142cf287", +"postgresql": "c2ad40bc8b050424142cf287", +"snowflake": "c2ad40bc8b050424142cf287", +"tsql": "c2ad40bc8b050424142cf287" } ], [ @@ -24057,18 +24057,18 @@ [ "type", { -"DuckDB": "ef2689646fd9e772e27d2727", -"None": "ef2689646fd9e772e27d2727", -"bigquery": "ef2689646fd9e772e27d2727", +"DuckDB": "da9c22183f7aed4dcccc3e86", +"None": "da9c22183f7aed4dcccc3e86", +"bigquery": "da9c22183f7aed4dcccc3e86", "databricks": "e63b3aab09c82955e2560928", -"duckdb": "ef2689646fd9e772e27d2727", -"generic": "ef2689646fd9e772e27d2727", +"duckdb": "da9c22183f7aed4dcccc3e86", +"generic": "da9c22183f7aed4dcccc3e86", "motherduck": "901c5133ed47d4769286e860", "mysql": "6c03a3450ea4742ee7396e3c", -"postgres": "ef2689646fd9e772e27d2727", -"postgresql": "ef2689646fd9e772e27d2727", -"snowflake": "ef2689646fd9e772e27d2727", -"tsql": "ef2689646fd9e772e27d2727" +"postgres": "da9c22183f7aed4dcccc3e86", +"postgresql": "da9c22183f7aed4dcccc3e86", +"snowflake": "da9c22183f7aed4dcccc3e86", +"tsql": "da9c22183f7aed4dcccc3e86" } ], [ From a6f1efd945b0ab58ac8bcb6460db7b03839f8b9b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:06:26 +0000 Subject: [PATCH 020/160] test(lineage): trace fast lineage after a warm restore The same project traced after a cold compile, from the analysis cache alone and from compile reuse, gives the compile report and traces of a run without any cache, so a restore keeps every analysed model's lineage facts. --- .../main/compile/fast_lineage/helpers.py | 28 +++++++++++++++---- .../fast_lineage/test_native_fast_lineage.py | 21 ++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py index a3222d6af..18ab54228 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py @@ -20,6 +20,7 @@ class LineageRun(NamedTuple): compile_report: str compile_returncode: int traces: list[tuple[int, str, str]] + analysis_cache_hits: int = 0 def lineage_run( @@ -28,21 +29,34 @@ def lineage_run( files: dict[str, str], targets: tuple[str, ...], mode: str = "fast", + warm: str | None = None, ) -> LineageRun: - """Write the project, then compile and trace every target both ways in one lineage mode.""" + """Write the project, then compile and trace every target both ways in one lineage mode. + + `warm` names the store a cold compile fills first, so the measured compile and the traces + restore from it: `analysis cache` for the analysis store alone, `compile reuse` for both. + """ for relative_path, contents in files.items(): path: Path = project_dir / "project" / relative_path path.parent.mkdir(parents=True, exist_ok=True) _ = path.write_text(contents, encoding="utf-8") - environment: dict[str, str] = {REUSE_DISABLE_ENV_VAR: "1"} + environment: dict[str, str] = {REUSE_DISABLE_ENV_VAR: "0" if warm == "compile reuse" else "1"} + compile_args: tuple[str, ...] = ( + ("compile", "--json", "--no-cache", "--lineage-mode", mode) + if warm is None + else ("compile", "--json", "--lineage-mode", mode) + ) + if warm is not None: + cold: subprocess.CompletedProcess[str] = run_installed_sqb( + project_dir=project_dir / "project", args=compile_args, env=environment + ) + assert cold.returncode == 0, cold.stdout + cold.stderr compiled: subprocess.CompletedProcess[str] = run_installed_sqb( - project_dir=project_dir / "project", - args=("compile", "--json", "--no-cache", "--lineage-mode", mode), - env=environment, + project_dir=project_dir / "project", args=compile_args, env=environment ) report: dict[str, object] = json.loads(compiled.stdout.replace(str(project_dir), "")) - _ = report.pop("compile_timings", None) + timings: dict[str, int] = cast(dict[str, int], report.pop("compile_timings", {})) traces: list[tuple[int, str, str]] = [] for target, direction in product(targets, _DIRECTIONS): result: subprocess.CompletedProcess[str] = run_installed_sqb( @@ -56,6 +70,8 @@ def lineage_run( compile_report=json.dumps(report, indent=2), compile_returncode=compiled.returncode, traces=traces, + analysis_cache_hits=timings.get("analysis_batch_cache_hits", 0) + + timings.get("analysis_entry_cache_hits", 0), ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py index 930da556b..39d6aea53 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py @@ -180,6 +180,27 @@ def test_given_project_when_compiling_then_fast_lineage_is_traced( ) +@pytest.mark.parametrize("warm", ["analysis cache", "compile reuse"]) +def test_given_a_warm_store_when_tracing_fast_lineage_then_it_matches_the_uncached_run( + warm: str, tmp_path: Path +) -> None: + targets: tuple[str, ...] = ( + "customer_totals.Total_Amount", + "order_copies.rounded", + "stg_orders.amount", + ) + uncached: LineageRun = lineage_run( + project_dir=tmp_path / "uncached", files=_PROJECT_FILES, targets=targets + ) + restored: LineageRun = lineage_run( + project_dir=tmp_path / "warm", files=_PROJECT_FILES, targets=targets, warm=warm + ) + + assert restored.compile_returncode == 0, restored.compile_report + assert (restored.compile_report, restored.traces) == (uncached.compile_report, uncached.traces) + assert warm != "analysis cache" or restored.analysis_cache_hits > 0 + + @pytest.mark.parametrize( "test_case", [ From 5bb708f6d9b863f3b0ecb40398e06a8cd7409aee Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:19:19 +0000 Subject: [PATCH 021/160] fix(analysis): gate a native route on a model's named hooks too A native route's analysis gate ran before hook expansion, when a named SQL hook is still only its name, so it read inline hooks but never named ones. A named hook the parser cannot read then reached the resource SQL check, whose Polyglot parse reported an unlocated P001, while an inline hook got P026. The gate now runs after hook expansion and reads both kinds, and on a native route the resource SQL check no longer turns a Polyglot rejection of hook SQL into a P001: the gate has given P026 for SQL the dialect's parser cannot read. --- .../_helpers/resource_sql/diagnostics.rs | 17 +++++++++--- .../_helpers/input_loops/model_loop.rs | 27 +++++++++++++------ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs index 33ced0fba..18aa73d84 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs @@ -6,6 +6,8 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use sqlbuild_attachments::cursor_intrinsics::main::replace_intrinsics::replace_intrinsics; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::models::AnalysisRoute; use crate::semantic_checks::_helpers::explanation::opt_outs::unneeded_opt_out; use crate::semantic_checks::_helpers::metadata_checks::positions::{text_position, word_start}; use crate::semantic_checks::_helpers::resource_sql::help::{HelpContext, resource_sql_help}; @@ -98,11 +100,16 @@ fn binding_request( } /// Python's `_reported_diagnostic`: drop open-relation lookups, make parser rejections P001. -fn reported(resource: &ResourceSql, finding: &Finding) -> Option { +/// +/// On a native route the analysis gate reads model hooks with the dialect's parser and gives one +/// it cannot read P026, so Polyglot's rejection of hook SQL is never a P001 there. +fn reported(resource: &ResourceSql, finding: &Finding, native_hooks: bool) -> Option { if finding.code != UNKNOWN_TABLE_CODE { return Some(finding.clone()); } - if finding.message.starts_with(UNKNOWN_TABLE_PREFIX) { + if finding.message.starts_with(UNKNOWN_TABLE_PREFIX) + || (native_hooks && resource.kind == ResourceSqlKind::Model) + { return None; } Some(Finding { @@ -134,8 +141,12 @@ fn resource_diagnostics( checking: &Checking<'_>, ) -> Result, ResourceSqlFailure> { let mut found: Vec<(Key, ResourceSqlDiagnostic)> = Vec::new(); + let native_hooks: bool = matches!( + analysis_route(checking.request.dialect.as_deref()), + AnalysisRoute::Native(_) + ); for (index, native) in findings.iter().enumerate() { - let Some(finding) = reported(resource, native) else { + let Some(finding) = reported(resource, native, native_hooks) else { continue; }; let span = location(resource, checking.cleaned_sql, &finding)?; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs index 731e152bd..4d9a37115 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs @@ -753,14 +753,6 @@ impl NativeModelLoop { contents, }, )?; - let (analysed, rejected_opt_out, analysis_skipped) = match legacy_gate { - Some(gate) => gate, - None => self.analysis(context, &opt_out_model, scanned.invalid_calls.is_empty())?, - }; - let analysis: (bool, Option>) = ( - analysed && scanned.invalid_calls.is_empty(), - rejected_opt_out, - ); let references: Vec = merge_call_site_references( scanned.references.iter().map(reference_row).collect(), argument_references, @@ -794,6 +786,25 @@ impl NativeModelLoop { } else { (values, Vec::new()) }; + let (analysed, rejected_opt_out, analysis_skipped) = match legacy_gate { + Some(gate) => gate, + None => { + let expanded_model: OptOutModel<'_, 'py> = OptOutModel { + name, + file_path, + config: &config, + values: &values, + query_sql: &query_sql, + relative_path, + contents, + }; + self.analysis(context, &expanded_model, scanned.invalid_calls.is_empty())? + } + }; + let analysis: (bool, Option>) = ( + analysed && scanned.invalid_calls.is_empty(), + rejected_opt_out, + ); let source: HeaderSource<'_, 'py> = HeaderSource { name, values: &values, From 7cf9e4752b1af86e1b85bb34615c4e2a7a01bb93 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:19:19 +0000 Subject: [PATCH 022/160] fix(analysis): know the adapter's declared functions on a native route A native route analysed every model with no extension functions, so a function the adapter declares a return type for was reported unknown. The session now passes those function names to the dialect's analysis. --- .../tests/test_native_outcome.rs | 49 ++++++++++++++++--- .../src/analysis_route/tests/test_types.rs | 4 +- .../_helpers/native_outcome.rs | 5 +- .../analysis_session/_helpers/session.rs | 10 +++- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index bbb153171..7a9aa872d 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -15,23 +15,46 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll description: "a closed source expands a star with its types and known nullability", sql: "SELECT * FROM raw_orders", shape: &[("order_id", "INTEGER"), ("status", "VARCHAR")], + declared_functions: &[], expected: &[ "order_id INTEGER non_null", "status VARCHAR nullable", - "succeeded=true star=true/true", + "succeeded=true star=true/true findings=[]", ], }, NativeOutcomeTestCase { description: "a relation the session has no shape for is unknown, not empty", sql: "SELECT * FROM raw_orders", shape: &[], - expected: &["succeeded=true star=true/false"], + declared_functions: &[], + expected: &["succeeded=true star=true/false findings=[]"], }, NativeOutcomeTestCase { description: "SQL the dialect cannot read fails the analysis", sql: "SELECT FROM WHERE", shape: &[("order_id", "INTEGER")], - expected: &["succeeded=false star=false/false"], + declared_functions: &[], + expected: &["succeeded=false star=false/false findings=[]"], + }, + NativeOutcomeTestCase { + description: "a function the adapter declares is not unknown", + sql: "SELECT CURRENT_TIMESTAMP() AS at FROM raw_orders", + shape: &[("order_id", "INTEGER")], + declared_functions: &[("CURRENT_TIMESTAMP", "TIMESTAMPTZ")], + expected: &[ + "at TIMESTAMPTZ unknown", + "succeeded=true star=false/true findings=[]", + ], + }, + NativeOutcomeTestCase { + description: "the same function without the declaration is unknown", + sql: "SELECT CURRENT_TIMESTAMP() AS at FROM raw_orders", + shape: &[("order_id", "INTEGER")], + declared_functions: &[], + expected: &[ + "at TIMESTAMPTZ unknown", + "succeeded=true star=false/true findings=[\"B101\"]", + ], }, ]; @@ -55,8 +78,17 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll required_names: vec!["raw_orders".to_owned()], ..ModelRequest::default() }; - let (outcome, failure): (ModelOutcome, Option) = - native_outcome(&CataloguedDialect, &model, (schema, &nullability)); + let declared: Vec = test_case + .declared_functions + .iter() + .map(|(name, _)| (*name).to_owned()) + .collect(); + let (outcome, failure): (ModelOutcome, Option) = native_outcome( + &CataloguedDialect, + &model, + (schema, &nullability), + &declared, + ); let analysis = &outcome.analysis; let mut lines: Vec = analysis .columns @@ -71,8 +103,13 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll ) }) .collect(); + let findings: Vec<&str> = analysis + .binding_diagnostics + .iter() + .map(|row| row.code.as_str()) + .collect(); lines.push(format!( - "succeeded={} star={}/{}", + "succeeded={} star={}/{} findings={findings:?}", analysis.analysis_succeeded, analysis.has_star, analysis.star_resolved )); assert_eq!(lines, test_case.expected, "{}", test_case.description); diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 6e6be0691..d12c9f1af 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -187,7 +187,9 @@ pub(super) struct NativeOutcomeTestCase { pub(super) sql: &'static str, /// `raw_orders`' shape as the session knows it; empty when it does not. pub(super) shape: &'static [(&'static str, &'static str)], - /// Each output column as `name type nullability`, then `succeeded star=/`. + /// The adapter's declared functions as `(name, return type)`. + pub(super) declared_functions: &'static [(&'static str, &'static str)], + /// Each output column as `name type nullability`, then success, star flags and finding codes. pub(super) expected: &'static [&'static str], } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs index 921187786..e3c64caa4 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs @@ -16,10 +16,13 @@ use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::main::normalize_native::normalize_native_sql; /// The model's outcome read against `schema`, its complete input shapes, and why it failed if it did. +/// +/// `extension_functions` are the functions the adapter declares, which are never unknown. pub(crate) fn native_outcome( dialect: &dyn NativeDialect, model: &ModelRequest, (schema, nullability): (Shapes, &ShapeTable), + extension_functions: &[String], ) -> (ModelOutcome, Option) { let (cleaned_sql, analysed): (String, Result) = match native_cleaned_sql(dialect, model) { @@ -30,7 +33,7 @@ pub(crate) fn native_outcome( sql: &sql, schema: &relations, references: &model.lineage_references, - extension_functions: &[], + extension_functions, }) .map_err(|gap: ParserGap| gap.message); (sql, analysed) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 4e5371158..eaa71c0be 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -314,12 +314,18 @@ impl AnalysisSession { let pool = self.catalog.native.analysis_pool()?; let request: &SessionRequest = &self.request; let nullability: &ShapeTable = &self.available_nullability; + let declared: Vec = request + .function_return_types + .iter() + .map(|(name, _)| name.clone()) + .collect(); let outcomes: Vec<(ModelOutcome, Option)> = pool.install(|| { misses .par_iter() .map(|position| { let model: &ModelRequest = &request.models[models[*position]]; - native_outcome(dialect, model, (schemas[*position].clone(), nullability)) + let schema: Shapes = schemas[*position].clone(); + native_outcome(dialect, model, (schema, nullability), &declared) }) .collect() }); @@ -669,7 +675,7 @@ impl AnalysisSession { } } - /// Normalize the cleaned SQL of the cache hits among `models` that still lack it. + /// Normalize the cleaned SQL of the cache hits among `models` that still lack it. fn ensure_cleaned(&mut self, models: &[usize]) -> Result<(), String> { let pending: Vec = models .iter() From 9c886dd8146038628d209c600ab34309f013761c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:34:27 +0000 Subject: [PATCH 023/160] refactor(analysis): shape the native route's model and lineage entries as fensu requires A native model outcome and a native fast lineage are now entry modules of the analysis route, analysis_route::main::model_outcome and ::fast_lineage, each with one visible function. The session builds its own schema for the outcome, fast lineage passes in the references it scanned, and where a parser gap is and why it is unreadable live in one route helper. The warm-restore lineage test takes its stores from local test cases. Behaviour is unchanged. --- .../analysis_route/_helpers/located_gap.rs | 31 +++++ .../src/analysis_route/_helpers/mod.rs | 1 + .../main/fast_lineage.rs} | 27 +--- .../src/analysis_route/main/mod.rs | 2 + .../src/analysis_route/main/model_outcome.rs | 69 ++++++++++ .../src/analysis_route/main/native_gap.rs | 18 +-- .../analysis_route/main/skipped_analysis.rs | 14 +- .../src/analysis_route/tests/helpers.rs | 55 +++++++- .../tests/test_native_outcome.rs | 124 ++++++++---------- .../src/analysis_route/tests/test_types.rs | 9 +- .../analysis_session/_helpers/mappings.rs | 46 ++++++- .../assembly/analysis_session/_helpers/mod.rs | 1 - .../_helpers/native_outcome.rs | 122 ----------------- .../analysis_session/_helpers/session.rs | 18 ++- .../src/lineage/_helpers/mod.rs | 1 - .../src/lineage/main/build_fast_lineage.rs | 26 +++- .../_helpers/resource_sql/diagnostics.rs | 5 +- .../main/compile/fast_lineage/_test_types.py | 9 ++ .../main/compile/fast_lineage/helpers.py | 23 ++-- .../fast_lineage/test_native_fast_lineage.py | 43 ++++-- 20 files changed, 353 insertions(+), 291 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs rename crates/sqlbuild-analysis/src/{lineage/_helpers/native_lineage.rs => analysis_route/main/fast_lineage.rs} (82%) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs delete mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs new file mode 100644 index 000000000..40d950161 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs @@ -0,0 +1,31 @@ +//! Where a native parser's gap is, and why the parser cannot read the SQL there. + +use crate::analysis_route::models::{LocatedGap, ParserGap}; +use crate::compiled_project::main::text_position::text_position; +use crate::compiled_project::models::TextPosition; + +/// The gap with the line and column of its offset; an offset past the end is the end. +pub(crate) fn located(sql: &str, gap: ParserGap) -> LocatedGap { + let before: &str = sql.get(..gap.offset).unwrap_or(sql); + let position: TextPosition = text_position(sql, before.chars().count()); + LocatedGap { + construct: gap.construct, + message: gap.message, + line: position.line, + column: position.column, + syntax: gap.syntax, + } +} + +/// Why the dialect's native parser cannot read SQL, as P026 words it. +pub(crate) fn unreadable_reason(dialect: &str, gap: &LocatedGap) -> String { + let detail: String = if gap.message.is_empty() || gap.message == gap.construct { + String::new() + } else { + format!(": {}", gap.message) + }; + format!( + "the {dialect} parser cannot read {} at line {}, column {}{detail}", + gap.construct, gap.line, gap.column + ) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 715122942..5c472fed4 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod clickhouse_types; +pub(crate) mod located_gap; pub(crate) mod native_analysis; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs similarity index 82% rename from crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs rename to crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs index 328085b21..ee96e88ba 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/native_lineage.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs @@ -5,8 +5,7 @@ use std::collections::HashMap; use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; -use crate::analysis_route::main::native_gap::located; -use crate::analysis_route::main::skipped_analysis::unreadable_reason; +use crate::analysis_route::_helpers::located_gap::{located, unreadable_reason}; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::{ @@ -14,18 +13,17 @@ use crate::assembly::analysis_session::constants::{ TRANSFORM_DIRECT, TRANSFORM_STAR, }; use crate::assembly::analysis_session::models::{LineageRow, SessionSchema}; -use crate::lineage::_helpers::references::physical_resources; use crate::lineage::models::{ FastLineageOutcome, LineageColumn, LineageConfidence, LineageResourceType, LineageSchemaResource, LineageSource, LineageTransformKind, }; use crate::semantic_validation::main::normalize_native::normalize_native_sql; -/// The model's lineage over `schema`'s column names, or why the dialect's parser cannot read it. -pub(crate) fn native_model_lineage( +/// Lineage over `schema` and the `(name, type, resource)` references, or why it is unreadable. +pub fn fast_lineage( dialect: &dyn NativeDialect, - query_sql: &str, - placeholders: &[(String, String)], + (query_sql, placeholders): (&str, &[(String, String)]), + references: &[(String, String, String)], schema: &[LineageSchemaResource], ) -> FastLineageOutcome { let normalised: String = @@ -34,24 +32,11 @@ pub(crate) fn native_model_lineage( Err(reason) => return FastLineageOutcome::Unparsed(reason), }; let sql: String = placeholder_defaults(&normalised, placeholders); - let mut references: Vec<(String, String, String)> = Vec::new(); - for resource in physical_resources(query_sql) { - if !references - .iter() - .any(|(name, _, _)| *name == resource.resource_name) - { - references.push(( - resource.resource_name.clone(), - resource.resource_type.as_str().to_owned(), - resource.resource_name, - )); - } - } let relations: SessionSchema = names_schema(schema); let analysed: Result = dialect.analyse(&AnalysisInput { sql: &sql, schema: &relations, - references: &references, + references, extension_functions: &[], }); match analysed { diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs index 4991f9563..66e1c9dae 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs @@ -1,5 +1,7 @@ pub mod analysis_route; pub mod clickhouse_type_match; +pub mod fast_lineage; +pub(crate) mod model_outcome; pub mod native_gap; pub mod skipped_analysis; pub mod unavailable_analysis; diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs new file mode 100644 index 000000000..1395539f7 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs @@ -0,0 +1,69 @@ +//! One model's outcome from its dialect's native analysis module, in the session's facts. + +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; + +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::models::{ + LineageFacts, ModelAnalysis, ModelOutcome, ModelRequest, SessionSchema, +}; +use crate::assembly::analysis_session::types::Shapes; +use crate::semantic_validation::main::normalize_native::normalize_native_sql; + +/// The model's outcome over `schema` with the adapter's declared functions, and why it failed. +pub(crate) fn model_outcome( + dialect: &dyn NativeDialect, + model: &ModelRequest, + (schema, relations): (Shapes, &SessionSchema), + extension_functions: &[String], +) -> (ModelOutcome, Option) { + let (cleaned_sql, analysed): (String, Result) = + match normalize_native_sql(&model.query_sql, dialect.name(), dialect.stub_prefix()) { + Ok(normalised) => { + let sql: String = placeholder_defaults(&normalised, &model.placeholders); + let analysed = dialect + .analyse(&AnalysisInput { + sql: &sql, + schema: relations, + references: &model.lineage_references, + extension_functions, + }) + .map_err(|gap: ParserGap| gap.message); + (sql, analysed) + } + Err(reason) => (String::new(), Err(reason)), + }; + let failure: Option = analysed + .as_ref() + .err() + .map(|reason| format!("{}: {reason}", model.name)); + let analysis: ModelAnalysis = match analysed { + Ok(native) => ModelAnalysis { + analysis_succeeded: true, + columns: Some(native.columns), + lineage: LineageFacts::Native(native.lineage), + has_star: native.has_star, + star_resolved: native.star_resolved, + binding_diagnostics: native.diagnostics, + binding_validated: true, + }, + Err(_) => ModelAnalysis { + analysis_succeeded: false, + columns: None, + lineage: LineageFacts::Native(Vec::new()), + has_star: false, + star_resolved: false, + binding_diagnostics: Vec::new(), + binding_validated: true, + }, + }; + let outcome: ModelOutcome = ModelOutcome { + analysis, + cleaned_sql, + deferred_cleaning: None, + validated_schema: schema, + fused_binding_validated: true, + sql_macro_findings: Vec::new(), + }; + (outcome, failure) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs index 58168f462..e484e7074 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs @@ -1,10 +1,9 @@ use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; -use crate::analysis_route::models::{LocatedGap, ParserGap}; +use crate::analysis_route::_helpers::located_gap::located; +use crate::analysis_route::models::LocatedGap; use crate::analysis_route::types::NativeDialect; use crate::assembly::project::models::SyntaxCheck; -use crate::compiled_project::main::text_position::text_position; -use crate::compiled_project::models::TextPosition; use crate::semantic_validation::main::normalize_native::normalize_native_sql; /// The native parser's first gap in `check`'s analysis SQL, with the dialect's stub spelling. @@ -20,16 +19,3 @@ pub fn native_gap( .err() .map(|gap| located(&analysed, gap))) } - -/// The gap with the line and column of its offset; an offset past the end is the end. -pub(crate) fn located(sql: &str, gap: ParserGap) -> LocatedGap { - let before: &str = sql.get(..gap.offset).unwrap_or(sql); - let position: TextPosition = text_position(sql, before.chars().count()); - LocatedGap { - construct: gap.construct, - message: gap.message, - line: position.line, - column: position.column, - syntax: gap.syntax, - } -} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs index 3a09a7209..d170a5495 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs @@ -1,3 +1,4 @@ +use crate::analysis_route::_helpers::located_gap::unreadable_reason; use crate::analysis_route::constants::SKIPPED_ANALYSIS_CODE; use crate::analysis_route::models::LocatedGap; @@ -12,16 +13,3 @@ pub fn skipped_analysis(model: &str, dialect: &str, gap: &LocatedGap) -> (&'stat ), ) } - -/// Why the dialect's native parser cannot read SQL, as P026 words it. -pub(crate) fn unreadable_reason(dialect: &str, gap: &LocatedGap) -> String { - let detail: String = if gap.message.is_empty() || gap.message == gap.construct { - String::new() - } else { - format!(": {}", gap.message) - }; - format!( - "the {dialect} parser cannot read {} at line {}, column {}{detail}", - gap.construct, gap.line, gap.column - ) -} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs index 27e20d3ec..f3a99b4bb 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs @@ -1,6 +1,12 @@ +use std::collections::HashMap; + +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; + use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, StandInSchema}; use crate::analysis_route::types::NativeDialect; -use crate::assembly::analysis_session::models::LineageRow; +use crate::assembly::analysis_session::models::{LineageRow, SessionSchema}; +use crate::assembly::analysis_session::types::Shapes; +use crate::lineage::models::{LineageColumn, LineageSource}; /// `orders` with a non-null INTEGER id and a nullable VARCHAR status. pub(super) fn orders() -> StandInSchema { @@ -51,3 +57,50 @@ pub(super) fn resource_columns(row: &LineageRow) -> Vec<&str> { }) .collect() } + +/// `output transform confidence [type:resource.column, ...]`. +pub(super) fn column_line(column: &LineageColumn) -> String { + let sources: Vec = column.upstream_columns.iter().map(source_text).collect(); + format!( + "{} {} {} [{}]", + column.output_column, + column.transform_kind.as_str(), + column.confidence.as_str(), + sources.join(", ") + ) +} + +fn source_text(source: &LineageSource) -> String { + format!( + "{}:{}.{}", + source.resource_type.as_str(), + source.resource_name, + source.column_name + ) +} + +/// `raw_orders` with `shape` as the session's input shapes and its typed schema; none when empty. +pub(super) fn session_relations(shape: &[(&str, &str, Nullability)]) -> (Shapes, SessionSchema) { + let pairs: Vec<(String, String)> = shape + .iter() + .map(|(name, data_type, _)| ((*name).to_owned(), (*data_type).to_owned())) + .collect(); + let columns: Vec = shape.iter().map(schema_column).collect(); + let relations: HashMap> = std::iter::once(("raw_orders", columns)) + .filter(|(_, columns)| !columns.is_empty()) + .map(|(name, columns)| (name.to_owned(), columns)) + .collect(); + ( + vec![("raw_orders".to_owned(), pairs)], + SessionSchema { relations }, + ) +} + +fn schema_column(&(name, data_type, nullability): &(&str, &str, Nullability)) -> SchemaColumn { + SchemaColumn { + name: name.to_owned(), + quoted: false, + data_type: Some(data_type.to_owned()), + nullability, + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index 7a9aa872d..a1d77c535 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -1,12 +1,15 @@ +use crate::analysis_route::main::fast_lineage::fast_lineage; +use crate::analysis_route::main::model_outcome::model_outcome; +use crate::analysis_route::tests::helpers::{column_line, session_relations}; use crate::analysis_route::tests::test_types::{ CataloguedDialect, NativeLineageTestCase, NativeOutcomeTestCase, PivotlessDialect, }; -use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::_helpers::native_outcome::native_outcome; -use crate::assembly::analysis_session::models::{ModelOutcome, ModelRequest}; +use crate::assembly::analysis_session::models::{ModelOutcome, ModelRequest, SessionSchema}; use crate::assembly::analysis_session::types::Shapes; -use crate::lineage::_helpers::native_lineage::native_model_lineage; -use crate::lineage::models::{FastLineageOutcome, LineageResourceType, LineageSchemaResource}; +use crate::lineage::models::{ + LineageColumn, LineageOutcomeParts, LineageResourceType, LineageSchemaResource, +}; +use sqlquack_semantics::lineage::models::Nullability; #[test] fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_follow_them() { @@ -14,9 +17,12 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll NativeOutcomeTestCase { description: "a closed source expands a star with its types and known nullability", sql: "SELECT * FROM raw_orders", - shape: &[("order_id", "INTEGER"), ("status", "VARCHAR")], + shape: &[ + ("order_id", "INTEGER", Nullability::NonNull), + ("status", "VARCHAR", Nullability::Nullable), + ], declared_functions: &[], - expected: &[ + expected_lines: &[ "order_id INTEGER non_null", "status VARCHAR nullable", "succeeded=true star=true/true findings=[]", @@ -27,21 +33,21 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll sql: "SELECT * FROM raw_orders", shape: &[], declared_functions: &[], - expected: &["succeeded=true star=true/false findings=[]"], + expected_lines: &["succeeded=true star=true/false findings=[]"], }, NativeOutcomeTestCase { description: "SQL the dialect cannot read fails the analysis", sql: "SELECT FROM WHERE", - shape: &[("order_id", "INTEGER")], + shape: &[("order_id", "INTEGER", Nullability::NonNull)], declared_functions: &[], - expected: &["succeeded=false star=false/false findings=[]"], + expected_lines: &["succeeded=false star=false/false findings=[]"], }, NativeOutcomeTestCase { description: "a function the adapter declares is not unknown", sql: "SELECT CURRENT_TIMESTAMP() AS at FROM raw_orders", - shape: &[("order_id", "INTEGER")], + shape: &[("order_id", "INTEGER", Nullability::NonNull)], declared_functions: &[("CURRENT_TIMESTAMP", "TIMESTAMPTZ")], - expected: &[ + expected_lines: &[ "at TIMESTAMPTZ unknown", "succeeded=true star=false/true findings=[]", ], @@ -49,9 +55,9 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll NativeOutcomeTestCase { description: "the same function without the declaration is unknown", sql: "SELECT CURRENT_TIMESTAMP() AS at FROM raw_orders", - shape: &[("order_id", "INTEGER")], + shape: &[("order_id", "INTEGER", Nullability::NonNull)], declared_functions: &[], - expected: &[ + expected_lines: &[ "at TIMESTAMPTZ unknown", "succeeded=true star=false/true findings=[\"B101\"]", ], @@ -59,19 +65,7 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll ]; for test_case in test_cases { - let shape: Vec<(String, String)> = test_case - .shape - .iter() - .map(|(name, data_type)| ((*name).to_owned(), (*data_type).to_owned())) - .collect(); - let schema: Shapes = vec![("raw_orders".to_owned(), shape)]; - let nullability: ShapeTable = ShapeTable::from_shapes(&vec![( - "raw_orders".to_owned(), - vec![ - ("order_id".to_owned(), "non_null".to_owned()), - ("status".to_owned(), "nullable".to_owned()), - ], - )]); + let (schema, relations): (Shapes, SessionSchema) = session_relations(test_case.shape); let model: ModelRequest = ModelRequest { name: "orders".to_owned(), query_sql: test_case.sql.to_owned(), @@ -83,12 +77,8 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll .iter() .map(|(name, _)| (*name).to_owned()) .collect(); - let (outcome, failure): (ModelOutcome, Option) = native_outcome( - &CataloguedDialect, - &model, - (schema, &nullability), - &declared, - ); + let (outcome, failure): (ModelOutcome, Option) = + model_outcome(&CataloguedDialect, &model, (schema, &relations), &declared); let analysis = &outcome.analysis; let mut lines: Vec = analysis .columns @@ -112,7 +102,7 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll "succeeded={} star={}/{} findings={findings:?}", analysis.analysis_succeeded, analysis.has_star, analysis.star_resolved )); - assert_eq!(lines, test_case.expected, "{}", test_case.description); + assert_eq!(lines, test_case.expected_lines, "{}", test_case.description); assert_eq!( failure.is_some(), !analysis.analysis_succeeded, @@ -137,37 +127,48 @@ fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_l columns: vec!["payment_id".to_owned()], }, ]; + let references: Vec<(String, String, String)> = [ + ("orders", "model"), + ("unknown_model", "model"), + ("raw_payments", "source"), + ] + .iter() + .map(|(name, kind)| ((*name).to_owned(), (*kind).to_owned(), (*name).to_owned())) + .collect(); let test_cases = [ NativeLineageTestCase { description: "a referenced model's columns are the upstreams of direct and computed outputs", sql: r#"SELECT o.order_id, o.amount * 2 AS doubled FROM __ref("orders") AS o"#, placeholders: &[], - expected: &[ + expected_lines: &[ "order_id direct high [model:orders.order_id]", "doubled expression high [model:orders.amount]", - "star=false", + "built star=false", ], }, NativeLineageTestCase { description: "a placeholder takes its default before the parse, and a source is a source", sql: r#"SELECT payment_id FROM __source("raw_payments") WHERE payment_id > @@@min_id"#, placeholders: &[("min_id", "0")], - expected: &[ + expected_lines: &[ "payment_id direct high [source:raw_payments.payment_id]", - "star=false", + "built star=false", ], }, NativeLineageTestCase { description: "a star over a relation of unknown columns is a star without outputs", sql: r#"SELECT * FROM __ref("unknown_model")"#, placeholders: &[], - expected: &["star=true"], + expected_lines: &["built star=true"], }, NativeLineageTestCase { description: "SQL the parser cannot read gives P026's reason, located", sql: "SELECT 1\nFROM t PIVOT (sum(x) FOR y IN (1))", placeholders: &[], - expected: &["unparsed: the postgres parser cannot read PIVOT at line 2, column 8"], + expected_lines: &[ + "unparsed star=false", + "the postgres parser cannot read PIVOT at line 2, column 8", + ], }, ]; @@ -177,37 +178,16 @@ fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_l .iter() .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) .collect(); - let outcome: FastLineageOutcome = - native_model_lineage(&PivotlessDialect, test_case.sql, &placeholders, &schema); - let lines: Vec = match outcome { - FastLineageOutcome::Built { columns, has_star } => columns - .iter() - .map(|column| { - let sources: Vec = column - .upstream_columns - .iter() - .map(|source| { - format!( - "{}:{}.{}", - source.resource_type.as_str(), - source.resource_name, - source.column_name - ) - }) - .collect(); - format!( - "{} {} {} [{}]", - column.output_column, - column.transform_kind.as_str(), - column.confidence.as_str(), - sources.join(", ") - ) - }) - .chain([format!("star={has_star}")]) - .collect(), - FastLineageOutcome::Unparsed(reason) => vec![format!("unparsed: {reason}")], - other => vec![format!("{other:?}")], - }; - assert_eq!(lines, test_case.expected, "{}", test_case.description); + let parts: LineageOutcomeParts = fast_lineage( + &PivotlessDialect, + (test_case.sql, &placeholders), + &references, + &schema, + ) + .into_parts(); + let mut lines: Vec = parts.columns.iter().map(column_line).collect(); + lines.push(format!("{} star={}", parts.status, parts.has_star)); + lines.extend(parts.detail); + assert_eq!(lines, test_case.expected_lines, "{}", test_case.description); } } diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index d12c9f1af..d814d9e28 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -1,5 +1,6 @@ use sqlquack_semantics::dialects::constants::{DUCKDB, POSTGRES}; use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_semantics::lineage::models::Nullability; use sqlquack_syntax::expressions::statements::models::Command; use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; @@ -185,17 +186,17 @@ pub(super) struct StarTestCase { pub(super) struct NativeOutcomeTestCase { pub(super) description: &'static str, pub(super) sql: &'static str, - /// `raw_orders`' shape as the session knows it; empty when it does not. - pub(super) shape: &'static [(&'static str, &'static str)], + /// `raw_orders`' columns, types and nullability as the session knows them; empty when unknown. + pub(super) shape: &'static [(&'static str, &'static str, Nullability)], /// The adapter's declared functions as `(name, return type)`. pub(super) declared_functions: &'static [(&'static str, &'static str)], /// Each output column as `name type nullability`, then success, star flags and finding codes. - pub(super) expected: &'static [&'static str], + pub(super) expected_lines: &'static [&'static str], } pub(super) struct NativeLineageTestCase { pub(super) description: &'static str, pub(super) sql: &'static str, pub(super) placeholders: &'static [(&'static str, &'static str)], - pub(super) expected: &'static [&'static str], + pub(super) expected_lines: &'static [&'static str], } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs index 0018c8c5c..d34f769e5 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs @@ -4,9 +4,10 @@ use std::collections::{BTreeSet, HashMap}; use std::sync::OnceLock; use sha2::{Digest, Sha256}; +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; -use crate::assembly::analysis_session::constants::DIALECT_ALIASES; -use crate::assembly::analysis_session::models::ModelRequest; +use crate::assembly::analysis_session::constants::{DIALECT_ALIASES, UNKNOWN_TYPE}; +use crate::assembly::analysis_session::models::{ModelRequest, SessionSchema}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::Columns; use crate::semantic_validation::types::Relations; @@ -238,3 +239,44 @@ pub(crate) fn waves(producers: &[Vec]) -> Option>> { } (scheduled == producers.len()).then_some(waves) } + +/// Each relation with a known shape, its columns typed and with the nullability the session knows. +pub(crate) fn session_schema(schema: &Shapes, nullability: &ShapeTable) -> SessionSchema { + let relations: HashMap> = schema + .iter() + .filter(|(_, columns)| !columns.is_empty()) + .map(|(relation, columns)| { + let typed: Vec = typed_columns(columns, nullability.get(relation)); + (relation.clone(), typed) + }) + .collect(); + SessionSchema { relations } +} + +/// Each column with its known type and the nullability `known` spells for it. +fn typed_columns(columns: &Pairs, known: Option<&Pairs>) -> Vec { + columns + .iter() + .map(|(name, data_type)| SchemaColumn { + name: name.clone(), + quoted: false, + data_type: (!data_type.is_empty() && data_type != UNKNOWN_TYPE) + .then(|| data_type.clone()), + nullability: column_nullability(known, name), + }) + .collect() +} + +fn column_nullability(known: Option<&Pairs>, column: &str) -> Nullability { + let spelled: Option<&str> = known.and_then(|pairs| { + pairs + .iter() + .find(|(name, _)| name == column) + .map(|(_, value)| value.as_str()) + }); + match spelled { + Some("non_null") => Nullability::NonNull, + Some("nullable") => Nullability::Nullable, + _ => Nullability::Unknown, + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs index b9e6bfc97..61615bd51 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs @@ -6,6 +6,5 @@ pub(crate) mod dict_walk; pub(crate) mod dynamic_pivot; pub(crate) mod enrichment; pub(crate) mod mappings; -pub(crate) mod native_outcome; pub(crate) mod query_columns; pub(crate) mod session; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs deleted file mode 100644 index e3c64caa4..000000000 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/native_outcome.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! One model's outcome from its dialect's native analysis module, in the session's facts. - -use std::collections::HashMap; - -use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; -use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; - -use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; -use crate::analysis_route::types::NativeDialect; -use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::constants::UNKNOWN_TYPE; -use crate::assembly::analysis_session::models::{ - LineageFacts, ModelAnalysis, ModelOutcome, ModelRequest, SessionSchema, -}; -use crate::assembly::analysis_session::types::{Pairs, Shapes}; -use crate::semantic_validation::main::normalize_native::normalize_native_sql; - -/// The model's outcome read against `schema`, its complete input shapes, and why it failed if it did. -/// -/// `extension_functions` are the functions the adapter declares, which are never unknown. -pub(crate) fn native_outcome( - dialect: &dyn NativeDialect, - model: &ModelRequest, - (schema, nullability): (Shapes, &ShapeTable), - extension_functions: &[String], -) -> (ModelOutcome, Option) { - let (cleaned_sql, analysed): (String, Result) = - match native_cleaned_sql(dialect, model) { - Ok(sql) => { - let relations: SessionSchema = session_schema(&schema, nullability); - let analysed = dialect - .analyse(&AnalysisInput { - sql: &sql, - schema: &relations, - references: &model.lineage_references, - extension_functions, - }) - .map_err(|gap: ParserGap| gap.message); - (sql, analysed) - } - Err(reason) => (String::new(), Err(reason)), - }; - let failure: Option = analysed - .as_ref() - .err() - .map(|reason| format!("{}: {reason}", model.name)); - let analysis: ModelAnalysis = match analysed { - Ok(native) => ModelAnalysis { - analysis_succeeded: true, - columns: Some(native.columns), - lineage: LineageFacts::Native(native.lineage), - has_star: native.has_star, - star_resolved: native.star_resolved, - binding_diagnostics: native.diagnostics, - binding_validated: true, - }, - Err(_) => ModelAnalysis { - analysis_succeeded: false, - columns: None, - lineage: LineageFacts::Native(Vec::new()), - has_star: false, - star_resolved: false, - binding_diagnostics: Vec::new(), - binding_validated: true, - }, - }; - let outcome: ModelOutcome = ModelOutcome { - analysis, - cleaned_sql, - deferred_cleaning: None, - validated_schema: schema, - fused_binding_validated: true, - sql_macro_findings: Vec::new(), - }; - (outcome, failure) -} - -/// The model's SQL as its dialect's parser reads it: stubs respelt and placeholders filled. -pub(crate) fn native_cleaned_sql( - dialect: &dyn NativeDialect, - model: &ModelRequest, -) -> Result { - normalize_native_sql(&model.query_sql, dialect.name(), dialect.stub_prefix()) - .map(|normalised| placeholder_defaults(&normalised, &model.placeholders)) -} - -/// Each relation with a known shape, its columns typed and with the nullability the session knows. -fn session_schema(schema: &Shapes, nullability: &ShapeTable) -> SessionSchema { - let relations: HashMap> = schema - .iter() - .filter(|(_, columns)| !columns.is_empty()) - .map(|(relation, columns)| { - let known: Option<&Pairs> = nullability.get(relation); - let typed: Vec = columns - .iter() - .map(|(name, data_type)| SchemaColumn { - name: name.clone(), - quoted: false, - data_type: (!data_type.is_empty() && data_type != UNKNOWN_TYPE) - .then(|| data_type.clone()), - nullability: column_nullability(known, name), - }) - .collect(); - (relation.clone(), typed) - }) - .collect(); - SessionSchema { relations } -} - -fn column_nullability(known: Option<&Pairs>, column: &str) -> Nullability { - let spelled: Option<&str> = known.and_then(|pairs| { - pairs - .iter() - .find(|(name, _)| name == column) - .map(|(_, value)| value.as_str()) - }); - match spelled { - Some("non_null") => Nullability::NonNull, - Some("nullable") => Nullability::Nullable, - _ => Nullability::Unknown, - } -} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index eaa71c0be..395ee1dbc 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -9,8 +9,10 @@ use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::errors::StoreDecodeError; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; +use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::main::model_outcome::model_outcome; use crate::analysis_route::models::AnalysisRoute; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::_helpers::analysis_cache::{ @@ -29,20 +31,18 @@ use crate::assembly::analysis_session::_helpers::enrichment::{ Enrichment, EnrichmentInput, enrichment, }; use crate::assembly::analysis_session::_helpers::mappings::{ - ShapeTable, dict_from_pairs, same_keys, same_relations, + ShapeTable, dict_from_pairs, same_keys, same_relations, session_schema, }; use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; -use crate::assembly::analysis_session::_helpers::native_outcome::{ - native_cleaned_sql, native_outcome, -}; use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; use crate::assembly::analysis_session::main::resolved_cleaned_sql::resolved_cleaned_sql; use crate::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, ContractProof, DeferredCleaning, FinishedSession, LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, - RelationTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, + RelationTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionSchema, SessionStep, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::semantic_validation::main::normalize_native::normalize_native_sql; use crate::semantic_validation::models::DiagnosticRow; use crate::semantic_validation::models::ProjectCatalog; use crate::sql_macros::main::check_sql_macro_arguments::check_sql_macro_arguments; @@ -325,7 +325,8 @@ impl AnalysisSession { .map(|position| { let model: &ModelRequest = &request.models[models[*position]]; let schema: Shapes = schemas[*position].clone(); - native_outcome(dialect, model, (schema, nullability), &declared) + let relations: SessionSchema = session_schema(&schema, nullability); + model_outcome(dialect, model, (schema, &relations), &declared) }) .collect() }); @@ -687,8 +688,11 @@ impl AnalysisSession { } if let AnalysisRoute::Native(dialect) = self.route() { for model in pending { + let request: &ModelRequest = &self.request.models[model]; let sql: String = - native_cleaned_sql(dialect, &self.request.models[model]).unwrap_or_default(); + normalize_native_sql(&request.query_sql, dialect.name(), dialect.stub_prefix()) + .map(|normalised| placeholder_defaults(&normalised, &request.placeholders)) + .unwrap_or_default(); self.outcome_mut(model)?.cleaned_sql = sql; self.pending_cleaned[model] = false; } diff --git a/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs b/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs index 31d6889d6..139d0d6c8 100644 --- a/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/lineage/_helpers/mod.rs @@ -1,7 +1,6 @@ pub(crate) mod authored_files; pub(crate) mod environment_markers; pub(crate) mod fingerprint_digest; -pub(crate) mod native_lineage; pub(crate) mod parsed_lineage; pub(crate) mod projections; pub(crate) mod python_payload; diff --git a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs index 0d74ba11a..96e1790c9 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs @@ -9,8 +9,8 @@ use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use sqlbuild_core::panics::main::native_failure::native_failure; use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::main::fast_lineage::fast_lineage; use crate::analysis_route::models::AnalysisRoute; -use crate::lineage::_helpers::native_lineage::native_model_lineage; use crate::lineage::_helpers::parsed_lineage::{ ParsedModel, parsed_model_lineage, proxy_parse_options, }; @@ -80,10 +80,10 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec catch_compiler_panic(|| { - Ok(native_model_lineage( + Ok(fast_lineage( module, - query_sql, - placeholders, + (query_sql, placeholders), + &lineage_references(query_sql), &request.schema, )) }), @@ -110,3 +110,21 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec Vec<(String, String, String)> { + let mut references: Vec<(String, String, String)> = Vec::new(); + for resource in physical_resources(query_sql) { + if !references + .iter() + .any(|(name, _, _)| *name == resource.resource_name) + { + references.push(( + resource.resource_name.clone(), + resource.resource_type.as_str().to_owned(), + resource.resource_name, + )); + } + } + references +} diff --git a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs index 18aa73d84..0064cf828 100644 --- a/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_checks/_helpers/resource_sql/diagnostics.rs @@ -99,10 +99,7 @@ fn binding_request( (sql.to_owned(), references, overrides) } -/// Python's `_reported_diagnostic`: drop open-relation lookups, make parser rejections P001. -/// -/// On a native route the analysis gate reads model hooks with the dialect's parser and gives one -/// it cannot read P026, so Polyglot's rejection of hook SQL is never a P001 there. +/// `_reported_diagnostic`: drop open-relation lookups; parser rejections are P001, natively P026's. fn reported(resource: &ResourceSql, finding: &Finding, native_hooks: bool) -> Option { if finding.code != UNKNOWN_TABLE_CODE { return Some(finding.clone()); diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py index 5d57d2623..7d0eec5f3 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/_test_types.py @@ -14,6 +14,15 @@ class NativeFastLineageCliTestCase: expected_minimum_traced_edges: int +@dataclass(frozen=True) +class WarmFastLineageCliTestCase: + """Fast lineage traced after a cold compile filled the compile's stores.""" + + description: str + reuse_disabled: str + expected_minimum_cache_hits: int + + @dataclass(frozen=True) class NativeRichLineageFailureCliTestCase: """A native rich lineage failure injected at the binding, compiled through the CLI.""" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py index 18ab54228..546d0400a 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/helpers.py @@ -29,31 +29,28 @@ def lineage_run( files: dict[str, str], targets: tuple[str, ...], mode: str = "fast", - warm: str | None = None, + cache_args: tuple[str, ...] = ("--no-cache",), + environment: tuple[tuple[str, str], ...] = ((REUSE_DISABLE_ENV_VAR, "1"),), + cold_compiles: int = 0, ) -> LineageRun: """Write the project, then compile and trace every target both ways in one lineage mode. - `warm` names the store a cold compile fills first, so the measured compile and the traces - restore from it: `analysis cache` for the analysis store alone, `compile reuse` for both. + `cold_compiles` compiles run first, so the measured compile and traces read what they stored. """ for relative_path, contents in files.items(): path: Path = project_dir / "project" / relative_path path.parent.mkdir(parents=True, exist_ok=True) _ = path.write_text(contents, encoding="utf-8") - environment: dict[str, str] = {REUSE_DISABLE_ENV_VAR: "0" if warm == "compile reuse" else "1"} - compile_args: tuple[str, ...] = ( - ("compile", "--json", "--no-cache", "--lineage-mode", mode) - if warm is None - else ("compile", "--json", "--lineage-mode", mode) - ) - if warm is not None: + env: dict[str, str] = dict(environment) + compile_args: tuple[str, ...] = ("compile", "--json", *cache_args, "--lineage-mode", mode) + for _ in range(cold_compiles): cold: subprocess.CompletedProcess[str] = run_installed_sqb( - project_dir=project_dir / "project", args=compile_args, env=environment + project_dir=project_dir / "project", args=compile_args, env=env ) assert cold.returncode == 0, cold.stdout + cold.stderr compiled: subprocess.CompletedProcess[str] = run_installed_sqb( - project_dir=project_dir / "project", args=compile_args, env=environment + project_dir=project_dir / "project", args=compile_args, env=env ) report: dict[str, object] = json.loads(compiled.stdout.replace(str(project_dir), "")) timings: dict[str, int] = cast(dict[str, int], report.pop("compile_timings", {})) @@ -62,7 +59,7 @@ def lineage_run( result: subprocess.CompletedProcess[str] = run_installed_sqb( project_dir=project_dir / "project", args=("lineage", target, "--mode", mode, "--direction", direction, "--json"), - env=environment, + env=env, ) stdout: str = result.stdout.replace(str(project_dir), "") traces.append((result.returncode, stdout, _TIMING.sub("", result.stderr))) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py index 39d6aea53..0621386f9 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/fast_lineage/test_native_fast_lineage.py @@ -6,10 +6,12 @@ import pytest +from sqlbuild._native import COMPILE_REUSE_DISABLE_ENV_VAR as REUSE_DISABLE_ENV_VAR from tests.e2e.src.sqlbuild.cli.commands.main.compile.fast_lineage._test_types import ( NativeFastLineageCliTestCase, NativeRichLineageCliTestCase, NativeRichLineageFailureCliTestCase, + WarmFastLineageCliTestCase, ) from tests.e2e.src.sqlbuild.cli.commands.main.compile.fast_lineage.helpers import ( FailedLineageRun, @@ -52,6 +54,12 @@ ), } +_WARM_TARGETS: tuple[str, ...] = ( + "customer_totals.Total_Amount", + "order_copies.rounded", + "stg_orders.amount", +) + _DEEP_UNION_BRANCHES: int = 480 _DEEP_UNION_FILES: dict[str, str] = { "sqlbuild_project.toml": _PROJECT_FILES["sqlbuild_project.toml"], @@ -180,25 +188,40 @@ def test_given_project_when_compiling_then_fast_lineage_is_traced( ) -@pytest.mark.parametrize("warm", ["analysis cache", "compile reuse"]) +@pytest.mark.parametrize( + "test_case", + [ + WarmFastLineageCliTestCase( + description="the analysis cache a cold compile filled", + reuse_disabled="1", + expected_minimum_cache_hits=1, + ), + WarmFastLineageCliTestCase( + description="the compiled project a cold compile stored for reuse", + reuse_disabled="0", + expected_minimum_cache_hits=0, + ), + ], + ids=lambda case: case.description, +) def test_given_a_warm_store_when_tracing_fast_lineage_then_it_matches_the_uncached_run( - warm: str, tmp_path: Path + test_case: WarmFastLineageCliTestCase, tmp_path: Path ) -> None: - targets: tuple[str, ...] = ( - "customer_totals.Total_Amount", - "order_copies.rounded", - "stg_orders.amount", - ) uncached: LineageRun = lineage_run( - project_dir=tmp_path / "uncached", files=_PROJECT_FILES, targets=targets + project_dir=tmp_path / "uncached", files=_PROJECT_FILES, targets=_WARM_TARGETS ) restored: LineageRun = lineage_run( - project_dir=tmp_path / "warm", files=_PROJECT_FILES, targets=targets, warm=warm + project_dir=tmp_path / "warm", + files=_PROJECT_FILES, + targets=_WARM_TARGETS, + cache_args=(), + environment=((REUSE_DISABLE_ENV_VAR, test_case.reuse_disabled),), + cold_compiles=1, ) assert restored.compile_returncode == 0, restored.compile_report assert (restored.compile_report, restored.traces) == (uncached.compile_report, uncached.traces) - assert warm != "analysis cache" or restored.analysis_cache_hits > 0 + assert restored.analysis_cache_hits >= test_case.expected_minimum_cache_hits @pytest.mark.parametrize( From 8fdd54a93e43ff265231c225fb2f0a3bcb310e5a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:35:02 +0000 Subject: [PATCH 024/160] feat(analysis): analyse SQLite natively with SQLQuack Pins SQLQuack at main eba39976 and lists SQLite among the native dialects. The generated-contracts golden records 47 K002 rows that the native type comparison now leaves NotCompared: declared or inferred types it cannot normalise, such as CHAR(3) under DuckDB or malformed spellings. --- Cargo.lock | 22 ++--- Cargo.toml | 10 +-- .../src/analysis_route/constants.rs | 3 +- .../analysis_route/tests/test_native_gap.rs | 2 +- fensu.toml | 5 -- ...ped_nullability_and_dynamic_contracts.json | 84 +++++++++---------- 6 files changed, 61 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f5464003..75f121af0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4502,7 +4502,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "serde", "serde_json", @@ -4512,7 +4512,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "regex", "serde", @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "serde", "sqlquack-core", @@ -4535,7 +4535,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "serde", "serde_json", @@ -4548,7 +4548,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "proc-macro2", "quote", @@ -4558,7 +4558,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-duckdb" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "sqlquack-syntax", "stacker", @@ -4567,7 +4567,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "sqlquack-syntax", "stacker", @@ -4576,7 +4576,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-sqlite" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" dependencies = [ "sqlquack-syntax", "stacker", @@ -4673,7 +4673,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5109,7 +5109,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3f5ba1222..3d6d0d18c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,12 +177,12 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } -sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } -sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } +sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } +sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 020c5a4c6..349523ced 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,12 +1,13 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. -use crate::analysis_route::models::{DuckDbDialect, SnowflakeDialect, TypeSpelling}; +use crate::analysis_route::models::{DuckDbDialect, SnowflakeDialect, SqliteDialect, TypeSpelling}; use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[ (DUCKDB_DIALECT, &DuckDbDialect), (SNOWFLAKE_DIALECT, &SnowflakeDialect), + (SQLITE_DIALECT, &SqliteDialect), ]; /// DuckDB's (and MotherDuck's) analysis dialect name. pub(crate) const DUCKDB_DIALECT: &str = "duckdb"; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 592c915fb..5343279c2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -28,7 +28,7 @@ fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_leg RouteTestCase { description: "sqlite", dialect: Some("sqlite"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "bigquery", diff --git a/fensu.toml b/fensu.toml index 3fee255b7..c3109bfec 100644 --- a/fensu.toml +++ b/fensu.toml @@ -2933,11 +2933,6 @@ rule = "FFS201" path = "src/sqlbuild/orchestration_declarations/models/sensor_context.py" reason = "Only SensorContext lives here: invocation-local ctx.cursor assignment proposes progress, committed solely by native fenced completion. The package regression guards this declaration-only scope; class-symbol exceptions are unsupported." -[[targets.rust.rule_exceptions]] -rule = "FPRSL105" -path = "crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs" -reason = "Native dialect modules in analysis_route type their spellings through this entry; each dialect's module lands in its own change." - [[targets.rust.rule_exceptions]] rule = "FPRSL105" path = "crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs" diff --git a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json index 06b2d6254..41254f04b 100644 --- a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json +++ b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json @@ -115,7 +115,7 @@ "databricks": "839434d5b73abdf9499ad59e", "duckdb": "839434d5b73abdf9499ad59e", "postgres": "839434d5b73abdf9499ad59e", -"snowflake": "839434d5b73abdf9499ad59e", +"snowflake": "013b57a3487bfc47b4ea1985", "tsql": "839434d5b73abdf9499ad59e" } ], @@ -149,9 +149,9 @@ "None": "48ff6f452c064d9c0d6f68ed", "bigquery": "48ff6f452c064d9c0d6f68ed", "databricks": "48ff6f452c064d9c0d6f68ed", -"duckdb": "48ff6f452c064d9c0d6f68ed", +"duckdb": "f5caf46b08c258cc0379d7f0", "postgres": "48ff6f452c064d9c0d6f68ed", -"snowflake": "48ff6f452c064d9c0d6f68ed", +"snowflake": "f5caf46b08c258cc0379d7f0", "tsql": "78d512867db744527fe3982b" } ], @@ -161,9 +161,9 @@ "None": "17c278499d9aad7922d610d5", "bigquery": "17c278499d9aad7922d610d5", "databricks": "17c278499d9aad7922d610d5", -"duckdb": "17c278499d9aad7922d610d5", +"duckdb": "9a8e3bcf9632a8cf5e072c2c", "postgres": "17c278499d9aad7922d610d5", -"snowflake": "17c278499d9aad7922d610d5", +"snowflake": "9a8e3bcf9632a8cf5e072c2c", "tsql": "17c278499d9aad7922d610d5" } ], @@ -197,9 +197,9 @@ "None": "bcc0d54db4ee9d237ef10dd4", "bigquery": "bcc0d54db4ee9d237ef10dd4", "databricks": "bcc0d54db4ee9d237ef10dd4", -"duckdb": "bcc0d54db4ee9d237ef10dd4", +"duckdb": "c977930632cd22e603ff63c5", "postgres": "bcc0d54db4ee9d237ef10dd4", -"snowflake": "bcc0d54db4ee9d237ef10dd4", +"snowflake": "c977930632cd22e603ff63c5", "tsql": "bcc0d54db4ee9d237ef10dd4" } ], @@ -209,9 +209,9 @@ "None": "0d0a771fb0ff7b39fb021d4d", "bigquery": "0d0a771fb0ff7b39fb021d4d", "databricks": "0d0a771fb0ff7b39fb021d4d", -"duckdb": "0d0a771fb0ff7b39fb021d4d", +"duckdb": "a2cbcbd14fe4ea1524c2a97a", "postgres": "0d0a771fb0ff7b39fb021d4d", -"snowflake": "0d0a771fb0ff7b39fb021d4d", +"snowflake": "a2cbcbd14fe4ea1524c2a97a", "tsql": "0d0a771fb0ff7b39fb021d4d" } ], @@ -257,9 +257,9 @@ "None": "2d4b87c5b06ff335bd6c741b", "bigquery": "2d4b87c5b06ff335bd6c741b", "databricks": "2d4b87c5b06ff335bd6c741b", -"duckdb": "2d4b87c5b06ff335bd6c741b", +"duckdb": "869e1c4232a27a6852792cc2", "postgres": "2d4b87c5b06ff335bd6c741b", -"snowflake": "2d4b87c5b06ff335bd6c741b", +"snowflake": "869e1c4232a27a6852792cc2", "tsql": "2d4b87c5b06ff335bd6c741b" } ], @@ -269,9 +269,9 @@ "None": "a2dffd29b4f083e47c096d79", "bigquery": "a2dffd29b4f083e47c096d79", "databricks": "a2dffd29b4f083e47c096d79", -"duckdb": "a2dffd29b4f083e47c096d79", +"duckdb": "b3283bf184bb082f364b8537", "postgres": "a2dffd29b4f083e47c096d79", -"snowflake": "a2dffd29b4f083e47c096d79", +"snowflake": "b3283bf184bb082f364b8537", "tsql": "a2dffd29b4f083e47c096d79" } ], @@ -293,9 +293,9 @@ "None": "ffd2f2034ea38a1c665386fe", "bigquery": "ffd2f2034ea38a1c665386fe", "databricks": "ffd2f2034ea38a1c665386fe", -"duckdb": "ffd2f2034ea38a1c665386fe", +"duckdb": "4f2e740fcc73cbe30799945e", "postgres": "ffd2f2034ea38a1c665386fe", -"snowflake": "ffd2f2034ea38a1c665386fe", +"snowflake": "4f2e740fcc73cbe30799945e", "tsql": "ffd2f2034ea38a1c665386fe" } ], @@ -317,9 +317,9 @@ "None": "c11f8c29184eeda19979e5d2", "bigquery": "c11f8c29184eeda19979e5d2", "databricks": "c11f8c29184eeda19979e5d2", -"duckdb": "c11f8c29184eeda19979e5d2", +"duckdb": "693ade4356c6087b9c1d8b19", "postgres": "c11f8c29184eeda19979e5d2", -"snowflake": "c11f8c29184eeda19979e5d2", +"snowflake": "693ade4356c6087b9c1d8b19", "tsql": "c11f8c29184eeda19979e5d2" } ], @@ -329,9 +329,9 @@ "None": "6a14752b404ff8337dbb4c51", "bigquery": "6a14752b404ff8337dbb4c51", "databricks": "6a14752b404ff8337dbb4c51", -"duckdb": "6a14752b404ff8337dbb4c51", +"duckdb": "8ff067c990a41011273611c2", "postgres": "6a14752b404ff8337dbb4c51", -"snowflake": "6a14752b404ff8337dbb4c51", +"snowflake": "b1eba4d04aa9dfe0a2d0865b", "tsql": "6a14752b404ff8337dbb4c51" } ], @@ -353,9 +353,9 @@ "None": "d766e542b46b503b1d64b5ec", "bigquery": "d766e542b46b503b1d64b5ec", "databricks": "d766e542b46b503b1d64b5ec", -"duckdb": "d766e542b46b503b1d64b5ec", +"duckdb": "6f3824fb29e0dde7383108c6", "postgres": "d766e542b46b503b1d64b5ec", -"snowflake": "d766e542b46b503b1d64b5ec", +"snowflake": "21635e15f68b0683f29535b2", "tsql": "d766e542b46b503b1d64b5ec" } ], @@ -425,9 +425,9 @@ "None": "0fb1be349cccda275d77113d", "bigquery": "0fb1be349cccda275d77113d", "databricks": "0fb1be349cccda275d77113d", -"duckdb": "0fb1be349cccda275d77113d", +"duckdb": "dfe34264a6edca41378b9402", "postgres": "0fb1be349cccda275d77113d", -"snowflake": "0fb1be349cccda275d77113d", +"snowflake": "dfe34264a6edca41378b9402", "tsql": "0fb1be349cccda275d77113d" } ], @@ -437,9 +437,9 @@ "None": "d9843bd5f2e76b8d875badaf", "bigquery": "d9843bd5f2e76b8d875badaf", "databricks": "d9843bd5f2e76b8d875badaf", -"duckdb": "d9843bd5f2e76b8d875badaf", +"duckdb": "876a438e514952d77fd02fbb", "postgres": "d9843bd5f2e76b8d875badaf", -"snowflake": "d9843bd5f2e76b8d875badaf", +"snowflake": "876a438e514952d77fd02fbb", "tsql": "d9843bd5f2e76b8d875badaf" } ], @@ -461,7 +461,7 @@ "None": "2a167c9cb4bdcef7cfbcd99a", "bigquery": "2a167c9cb4bdcef7cfbcd99a", "databricks": "2a167c9cb4bdcef7cfbcd99a", -"duckdb": "2a167c9cb4bdcef7cfbcd99a", +"duckdb": "5fcd211d1b9c6dd59fb52d91", "postgres": "2a167c9cb4bdcef7cfbcd99a", "snowflake": "2a167c9cb4bdcef7cfbcd99a", "tsql": "2a167c9cb4bdcef7cfbcd99a" @@ -473,9 +473,9 @@ "None": "ea9fe2e44b57cc7f6a5654aa", "bigquery": "ea9fe2e44b57cc7f6a5654aa", "databricks": "ea9fe2e44b57cc7f6a5654aa", -"duckdb": "ea9fe2e44b57cc7f6a5654aa", +"duckdb": "d7722781d12891e5bd225237", "postgres": "ea9fe2e44b57cc7f6a5654aa", -"snowflake": "ea9fe2e44b57cc7f6a5654aa", +"snowflake": "d7722781d12891e5bd225237", "tsql": "ea9fe2e44b57cc7f6a5654aa" } ], @@ -485,9 +485,9 @@ "None": "fe017b6cb02eef5219322f7c", "bigquery": "fe017b6cb02eef5219322f7c", "databricks": "fe017b6cb02eef5219322f7c", -"duckdb": "fe017b6cb02eef5219322f7c", +"duckdb": "f64c5f87267975253d4269d5", "postgres": "fe017b6cb02eef5219322f7c", -"snowflake": "34168d14eef2cb971b3882fb", +"snowflake": "b3283bf184bb082f364b8537", "tsql": "fe017b6cb02eef5219322f7c" } ], @@ -497,7 +497,7 @@ "None": "7d8609a5dcd3a0f32d347465", "bigquery": "7d8609a5dcd3a0f32d347465", "databricks": "7d8609a5dcd3a0f32d347465", -"duckdb": "7d8609a5dcd3a0f32d347465", +"duckdb": "6242d56d5832fdeefd826de9", "postgres": "7d8609a5dcd3a0f32d347465", "snowflake": "7d8609a5dcd3a0f32d347465", "tsql": "7d8609a5dcd3a0f32d347465" @@ -521,9 +521,9 @@ "None": "891f3d8b2c293698577c7813", "bigquery": "891f3d8b2c293698577c7813", "databricks": "891f3d8b2c293698577c7813", -"duckdb": "891f3d8b2c293698577c7813", +"duckdb": "12a74be4926070cf6793cb4d", "postgres": "891f3d8b2c293698577c7813", -"snowflake": "891f3d8b2c293698577c7813", +"snowflake": "12a74be4926070cf6793cb4d", "tsql": "891f3d8b2c293698577c7813" } ], @@ -557,9 +557,9 @@ "None": "2fb2d4657d6af84c32083288", "bigquery": "2fb2d4657d6af84c32083288", "databricks": "2fb2d4657d6af84c32083288", -"duckdb": "2fb2d4657d6af84c32083288", +"duckdb": "3c9b74c5b22f656137431938", "postgres": "2fb2d4657d6af84c32083288", -"snowflake": "2fb2d4657d6af84c32083288", +"snowflake": "3c9b74c5b22f656137431938", "tsql": "2fb2d4657d6af84c32083288" } ], @@ -569,7 +569,7 @@ "None": "a9bdcb7ee2642092b9c2e2ae", "bigquery": "a9bdcb7ee2642092b9c2e2ae", "databricks": "a9bdcb7ee2642092b9c2e2ae", -"duckdb": "a9bdcb7ee2642092b9c2e2ae", +"duckdb": "f7a56e3326d02b7968c8ffe8", "postgres": "a9bdcb7ee2642092b9c2e2ae", "snowflake": "a9bdcb7ee2642092b9c2e2ae", "tsql": "a9bdcb7ee2642092b9c2e2ae" @@ -581,7 +581,7 @@ "None": "a8bc34d679ed5c1bd41c17ba", "bigquery": "0f72c2e3063cc166300becea", "databricks": "a8bc34d679ed5c1bd41c17ba", -"duckdb": "a8bc34d679ed5c1bd41c17ba", +"duckdb": "cc37eb626963366490c09c21", "postgres": "a8bc34d679ed5c1bd41c17ba", "snowflake": "0f72c2e3063cc166300becea", "tsql": "a8bc34d679ed5c1bd41c17ba" @@ -617,9 +617,9 @@ "None": "6581612a11662f5afd928e61", "bigquery": "6581612a11662f5afd928e61", "databricks": "6581612a11662f5afd928e61", -"duckdb": "6581612a11662f5afd928e61", +"duckdb": "a39ffda7852644471dc4cd01", "postgres": "6581612a11662f5afd928e61", -"snowflake": "6581612a11662f5afd928e61", +"snowflake": "a39ffda7852644471dc4cd01", "tsql": "6581612a11662f5afd928e61" } ], @@ -629,7 +629,7 @@ "None": "3405db15b2687320907ae528", "bigquery": "3405db15b2687320907ae528", "databricks": "3405db15b2687320907ae528", -"duckdb": "3405db15b2687320907ae528", +"duckdb": "257b09b04f10f75c5c4e4c2e", "postgres": "3405db15b2687320907ae528", "snowflake": "3405db15b2687320907ae528", "tsql": "3405db15b2687320907ae528" @@ -713,9 +713,9 @@ "None": "866ae81507c3a535c0225de2", "bigquery": "866ae81507c3a535c0225de2", "databricks": "866ae81507c3a535c0225de2", -"duckdb": "866ae81507c3a535c0225de2", +"duckdb": "20e5bc9de6e4d1de2bf5a08f", "postgres": "866ae81507c3a535c0225de2", -"snowflake": "866ae81507c3a535c0225de2", +"snowflake": "20e5bc9de6e4d1de2bf5a08f", "tsql": "866ae81507c3a535c0225de2" } ] From bd5a916a2fa45dd941fe6b94cc1c065bc1cddb53 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:15:32 +0000 Subject: [PATCH 025/160] build(analysis): pin SQLQuack at the DuckDB window, PIVOT and arity parity link Not a SQLQuack main commit yet; the pin moves to main once the link lands. --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75f121af0..3104605b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4502,7 +4502,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "serde", "serde_json", @@ -4512,7 +4512,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "regex", "serde", @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "serde", "sqlquack-core", @@ -4535,7 +4535,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "serde", "serde_json", @@ -4548,7 +4548,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "proc-macro2", "quote", @@ -4558,7 +4558,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-duckdb" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "sqlquack-syntax", "stacker", @@ -4567,7 +4567,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "sqlquack-syntax", "stacker", @@ -4576,7 +4576,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-sqlite" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=eba399766f15c62e9d15ececd101b7d086a01839#eba399766f15c62e9d15ececd101b7d086a01839" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" dependencies = [ "sqlquack-syntax", "stacker", @@ -4673,7 +4673,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5109,7 +5109,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3d6d0d18c..bd81a45ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,12 +177,12 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } -sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } -sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "eba399766f15c62e9d15ececd101b7d086a01839" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } +sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } +sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From ad355f4434a7be94152649cf115b2197b492529e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:19:06 +0000 Subject: [PATCH 026/160] test(analysis): re-record the unsupported-cast golden at the statement start The native DuckDB route places the invalid DATE literal's B218 at the statement start, because SQLQuack literals carry no span. Code, line and message are unchanged; the column, end position and the literal's note go. --- .../compiler/failure/analysis-unsupported-cast.json | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/goldens/compiler/failure/analysis-unsupported-cast.json b/tests/goldens/compiler/failure/analysis-unsupported-cast.json index 413dbe469..90f1790e5 100644 --- a/tests/goldens/compiler/failure/analysis-unsupported-cast.json +++ b/tests/goldens/compiler/failure/analysis-unsupported-cast.json @@ -13,18 +13,13 @@ "resource_name": "customer_totals", "path": "models/marts/customer_totals.sql", "line": 5, - "column": 21, + "column": 1, "location": { "path": "models/marts/customer_totals.sql", "line": 5, - "column": 21, - "end_line": 5, - "end_column": 38 + "column": 1 }, - "help": "choose a supported source-to-target cast for this dialect", - "notes": [ - "DATE '2026-13-45' is DATE" - ] + "help": "choose a supported source-to-target cast for this dialect" } ] } From 71a7e3c51eecdbc5e46a88a1ac1457a6712b380c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:34:06 +0000 Subject: [PATCH 027/160] WIP (not shipping): Databricks' arm on the NativeDialect trait, on a SQLQuack validation pin Databricks' module parses with SQLQuack's Databricks SQL parser (parse_strict), analyses under the Databricks semantics profile, and types spellings through normalize_text_type after folding the Databricks synonyms Polyglot spells another way (INTEGER, REAL, DOUBLE PRECISION, NUMERIC, TIMESTAMP_LTZ, TIMESTAMP_NTZ). It joins NATIVE_DIALECTS under the adapter's analysis dialect name, databricks. A byte the scanner reads as no token is a syntax error, as on BigQuery's arm. Every sqlquack crate is pinned to a37cc362, a validation pin: one commit on SQLQuack main eba39976 adding the Databricks semantics profile, which is not on main yet. Repin every sqlquack crate to the SQLQuack main commit that carries the profile. The syntax error cases use SELECT 1 FROM t WHERE ) rather than FROM FROM: Databricks does not enforce ANSI reserved words, and its reference reads SELECT where from FROM where select as a query, so the parser correctly reads FROM FROM as a table named FROM. --- Cargo.lock | 24 ++- Cargo.toml | 7 +- crates/sqlbuild-analysis/Cargo.toml | 1 + .../_helpers/databricks_dialect.rs | 78 ++++++++ .../src/analysis_route/_helpers/mod.rs | 1 + .../src/analysis_route/constants.rs | 20 +- .../src/analysis_route/models.rs | 4 + .../src/analysis_route/tests.rs | 2 + .../tests/test_databricks_dialect.rs | 185 ++++++++++++++++++ .../analysis_route/tests/test_native_gap.rs | 6 +- .../src/analysis_route/tests/test_types.rs | 13 ++ 11 files changed, 328 insertions(+), 13 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs diff --git a/Cargo.lock b/Cargo.lock index 7d467327d..188f0f15a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4144,6 +4144,7 @@ dependencies = [ "sqlclean", "sqlquack-semantics", "sqlquack-syntax", + "sqlquack-syntax-databricks", "sqlquack-syntax-postgres", ] @@ -4500,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" dependencies = [ "serde", "serde_json", @@ -4510,19 +4511,21 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" dependencies = [ + "regex", "serde", "serde_json", "sqlquack-core", "sqlquack-snowflake", "sqlquack-syntax", + "stacker", ] [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" dependencies = [ "serde", "sqlquack-core", @@ -4531,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" dependencies = [ "serde", "serde_json", @@ -4541,10 +4544,19 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "sqlquack-syntax-databricks" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +dependencies = [ + "sqlquack-syntax", + "stacker", +] + [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" dependencies = [ "proc-macro2", "quote", @@ -4554,7 +4566,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" dependencies = [ "sqlquack-syntax", "stacker", diff --git a/Cargo.toml b/Cargo.toml index c4475e838..9872e5e17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,10 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } +sqlquack-syntax-databricks = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 95c643ebd..4ed6b4e0f 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -28,6 +28,7 @@ sqlbuild-sqltext.workspace = true sqlclean.workspace = true sqlquack-semantics.workspace = true sqlquack-syntax.workspace = true +sqlquack-syntax-databricks.workspace = true sqlquack-syntax-postgres.workspace = true [lints] diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs new file mode 100644 index 000000000..5dc52b48c --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs @@ -0,0 +1,78 @@ +//! Databricks' native module: SQLQuack's Databricks SQL parser and the Databricks semantics profile. + +use sqlquack_semantics::dialects::constants::DATABRICKS; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax_databricks::{Parsed, StrictFailure, parse_strict}; + +use crate::analysis_route::constants::{ + DATABRICKS_DIALECT, DATABRICKS_STUB_PREFIX, DATABRICKS_TYPE_ALIASES, UNSCANNED_CONSTRUCT, +}; +use crate::analysis_route::models::{DatabricksDialect, ParserGap}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeDialect}; + +impl NativeDialect for DatabricksDialect { + fn name(&self) -> &'static str { + DATABRICKS_DIALECT + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql).map_err(|failure| match failure { + StrictFailure::Parse(error) => ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }, + StrictFailure::Uncovered { position } => ParserGap { + construct: UNSCANNED_CONSTRUCT.to_owned(), + message: UNSCANNED_CONSTRUCT.to_owned(), + offset: position, + syntax: true, + }, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &DATABRICKS + } + + fn normalize_type(&self, spelling: &str) -> Option { + let upper: String = spelling + .split_whitespace() + .collect::>() + .join(" ") + .to_ascii_uppercase(); + if upper.is_empty() { + return None; + } + let spelt: String = DATABRICKS_TYPE_ALIASES + .iter() + .fold(upper, |text, (alias, name)| aliased(&text, alias, name)); + Some(normalize_text_type(&spelt, Some(TypeDialect::Databricks))) + } + + fn stub_prefix(&self) -> &'static str { + DATABRICKS_STUB_PREFIX + } +} + +/// `text` with each whole word `alias` spelt `name`, at any nesting depth. +fn aliased(text: &str, alias: &str, name: &str) -> String { + let word = |c: char| c.is_ascii_alphanumeric() || c == '_'; + let mut out: String = String::with_capacity(text.len()); + let mut rest: &str = text; + while let Some(at) = rest.find(alias) { + let before: Option = rest[..at].chars().next_back().or(out.chars().next_back()); + let after: Option = rest[at + alias.len()..].chars().next(); + out.push_str(&rest[..at]); + match before.is_some_and(word) || after.is_some_and(word) { + true => out.push_str(alias), + false => out.push_str(name), + } + rest = &rest[at + alias.len()..]; + } + out.push_str(rest); + out +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 715122942..c39082146 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod clickhouse_types; +pub(crate) mod databricks_dialect; pub(crate) mod native_analysis; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..d122e8ab8 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,9 +1,27 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. +use crate::analysis_route::models::DatabricksDialect; use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. -pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = + &[(DATABRICKS_DIALECT, &DatabricksDialect)]; +/// Databricks' analysis dialect name. +pub(crate) const DATABRICKS_DIALECT: &str = "databricks"; +/// The stub prefix Databricks reads as one unquoted name. +pub(crate) const DATABRICKS_STUB_PREFIX: &str = "__sqlbuild_"; +/// The construct of a byte the native scanner reads as no token, blank or comment. +pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; +/// Databricks type words Polyglot spells another way, so the text fallback matches it. +/// The space in DOUBLE PRECISION is taken out before the fallback compares names. +pub(crate) const DATABRICKS_TYPE_ALIASES: &[(&str, &str)] = &[ + ("INTEGER", "INT"), + ("REAL", "FLOAT"), + ("DOUBLE PRECISION", "DOUBLE"), + ("NUMERIC", "DECIMAL"), + ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"), + ("TIMESTAMP_NTZ", "TIMESTAMPNTZ"), +]; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 7330e008d..f298567f2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -122,3 +122,7 @@ impl LineageSchema for StandInSchema { .then_some(self.columns.as_slice()) } } + +/// Databricks, whose SQL SQLQuack's Databricks SQL parser reads. +#[derive(Clone, Copy, Debug)] +pub struct DatabricksDialect; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index 0b05ee5ff..9e2534a3c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -2,6 +2,8 @@ mod helpers; #[path = "tests/test_clickhouse_type_match.rs"] mod test_clickhouse_type_match; +#[path = "tests/test_databricks_dialect.rs"] +mod test_databricks_dialect; #[path = "tests/test_native_dialect.rs"] mod test_native_dialect; #[path = "tests/test_native_gap.rs"] diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs new file mode 100644 index 000000000..e87651915 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs @@ -0,0 +1,185 @@ +use crate::analysis_route::main::native_gap::native_gap; +use crate::analysis_route::models::{DatabricksDialect, LocatedGap}; +use crate::analysis_route::tests::test_types::{ + NativeGapTestCase, ParseFailureTestCase, PolyglotTypeTestCase, +}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::project::models::SyntaxCheck; +use crate::type_system::main::normalize_type::normalize_type; +use crate::type_system::models::NormalizedType; + +#[test] +fn given_databricks_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole_has_no_gap() { + let test_cases = [ + NativeGapTestCase { + description: "a query Databricks reads whole", + sql: "SELECT 1 AS a FROM t WHERE region = @@@region", + placeholders: &[("region", "'eu'")], + expected_gap: None, + }, + NativeGapTestCase { + description: "a UDF call's stub reads as one name", + sql: "SELECT __udf(\"scaled_amount\")(amount, 2) AS scaled FROM t", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "cursor bounds read as stub calls", + sql: "SELECT * FROM t WHERE updated_at >= __cursor_start() AND updated_at < __cursor_end( )", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "a syntax error is located where Databricks locates it", + sql: "SELECT 1 FROM t WHERE )", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "Syntax error at or near ')'".to_owned(), + message: "Syntax error at or near ')'".to_owned(), + line: 1, + column: 23, + syntax: true, + }), + }, + NativeGapTestCase { + description: "a construct the shared tree lacks is a gap named by the parser", + sql: "VACUUM t", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "vacuum".to_owned(), + message: "vacuum".to_owned(), + line: 1, + column: 1, + syntax: false, + }), + }, + NativeGapTestCase { + description: "a second statement is not read whole", + sql: "SELECT 1;\nSELECT 2", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "more than one statement".to_owned(), + message: "more than one statement".to_owned(), + line: 1, + column: 1, + syntax: false, + }), + }, + ]; + for test_case in test_cases { + let check = SyntaxCheck { + sql: test_case.sql.to_owned(), + placeholders: test_case + .placeholders + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) + .collect(), + }; + assert_eq!( + native_gap(&DatabricksDialect, &check), + Ok(test_case.expected_gap), + "{}", + test_case.description + ); + } +} + +#[test] +fn given_a_databricks_type_spelling_when_normalising_then_the_type_is_polyglot_s() { + let test_cases = [ + PolyglotTypeTestCase { + description: "INTEGER is Polyglot's INT", + spelling: "INTEGER", + }, + PolyglotTypeTestCase { + description: "REAL is Polyglot's FLOAT", + spelling: "real", + }, + PolyglotTypeTestCase { + description: "DOUBLE PRECISION is Polyglot's DOUBLE, whatever the spacing", + spelling: "DOUBLE PRECISION", + }, + PolyglotTypeTestCase { + description: "NUMERIC with arguments is DECIMAL with its precision and scale", + spelling: "NUMERIC(9, 3)", + }, + PolyglotTypeTestCase { + description: "NUMERIC alone is DECIMAL", + spelling: "NUMERIC", + }, + PolyglotTypeTestCase { + description: "TIMESTAMP_NTZ is Polyglot's TIMESTAMPNTZ", + spelling: "TIMESTAMP_NTZ", + }, + PolyglotTypeTestCase { + description: "TIMESTAMP_LTZ is Polyglot's TIMESTAMPLTZ", + spelling: "TIMESTAMP_LTZ", + }, + PolyglotTypeTestCase { + description: "a name Polyglot keeps stays as written", + spelling: "LONG", + }, + PolyglotTypeTestCase { + description: "a word holding an alias is not changed inside", + spelling: "INTEGERS", + }, + PolyglotTypeTestCase { + description: "a sized string keeps its length", + spelling: "VARCHAR(10)", + }, + PolyglotTypeTestCase { + description: "an alias inside an array, whose family ARRAY decides", + spelling: "ARRAY", + }, + PolyglotTypeTestCase { + description: "aliases inside a map and a struct, whose family MAP decides", + spelling: "MAP>", + }, + ]; + for test_case in test_cases { + let polyglot: NormalizedType = normalize_type(test_case.spelling, "databricks") + .expect("Polyglot normalizes every Databricks spelling") + .normalized; + assert_eq!( + DatabricksDialect.normalize_type(test_case.spelling), + Some(polyglot), + "{}", + test_case.description + ); + } +} + +#[test] +fn given_sql_databricks_refuses_when_parsing_then_the_gap_says_whether_it_is_a_syntax_error() { + let test_cases = [ + ParseFailureTestCase { + description: "a syntax error, at its byte offset after a multi-byte character", + sql: "SELECT 'é' FROM t WHERE )".to_owned(), + expected_syntax: true, + expected_offset: Some(25), + }, + ParseFailureTestCase { + description: "invalid syntax at the end of the input", + sql: "SELECT (".to_owned(), + expected_syntax: true, + expected_offset: Some(8), + }, + ParseFailureTestCase { + description: "the nesting limit is not a syntax error", + sql: format!("SELECT {}1{}", "(".repeat(300), ")".repeat(300)), + expected_syntax: false, + expected_offset: None, + }, + ]; + for test_case in test_cases { + let gap = DatabricksDialect + .parse(&test_case.sql) + .expect_err(test_case.description); + assert_eq!( + (gap.syntax, test_case.expected_offset.map(|_| gap.offset)), + (test_case.expected_syntax, test_case.expected_offset), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 5c395c6d1..79ff0ad5f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -8,7 +8,7 @@ use crate::analysis_route::tests::test_types::{ use crate::assembly::project::models::SyntaxCheck; #[test] -fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { +fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_legacy_path() { let test_cases = [ RouteTestCase { description: "postgres", @@ -36,9 +36,9 @@ fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { expected_legacy: true, }, RouteTestCase { - description: "databricks", + description: "databricks, read by SQLQuack's Databricks SQL parser", dialect: Some("databricks"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "clickhouse", diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 6e6be0691..484a625b3 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -197,3 +197,16 @@ pub(super) struct NativeLineageTestCase { pub(super) placeholders: &'static [(&'static str, &'static str)], pub(super) expected: &'static [&'static str], } + +pub(super) struct PolyglotTypeTestCase { + pub(super) description: &'static str, + pub(super) spelling: &'static str, +} + +pub(super) struct ParseFailureTestCase { + pub(super) description: &'static str, + pub(super) sql: String, + pub(super) expected_syntax: bool, + /// The byte offset of a syntax error; a limit has none worth comparing. + pub(super) expected_offset: Option, +} From 50b4ee0d5c41e7bdd88df415020411e854bb7612 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:34:19 +0000 Subject: [PATCH 028/160] WIP (not shipping): drop the fensu exception Databricks' arm resolves With Databricks' module typing through normalize_text_type, its FPRSL105 exception no longer matches a fault and is removed. --- fensu.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fensu.toml b/fensu.toml index 3fee255b7..c3109bfec 100644 --- a/fensu.toml +++ b/fensu.toml @@ -2933,11 +2933,6 @@ rule = "FFS201" path = "src/sqlbuild/orchestration_declarations/models/sensor_context.py" reason = "Only SensorContext lives here: invocation-local ctx.cursor assignment proposes progress, committed solely by native fenced completion. The package regression guards this declaration-only scope; class-symbol exceptions are unsupported." -[[targets.rust.rule_exceptions]] -rule = "FPRSL105" -path = "crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs" -reason = "Native dialect modules in analysis_route type their spellings through this entry; each dialect's module lands in its own change." - [[targets.rust.rule_exceptions]] rule = "FPRSL105" path = "crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs" From d9a9e16787aac77e498ad33a2b09863955f7a2db Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:36:53 +0000 Subject: [PATCH 029/160] fix(analysis): revalidate type recovery with the native analysis on native routes Type recovery revalidated each model downstream of a poisoned output through the Polyglot binding catalog. On a native route that catalog was never prepared, so preparing it while the semantic stage held a borrow on it panicked with "Already borrowed" whenever a model with its own binding error sat downstream of a blocking type error. On a native route, revalidation now reads each planned model's analysed SQL again with its dialect's analysis, over the schema the session bound and with the poisoned outputs untyped. The findings come back at the session's own positions, so type recovery matches them against the raw rows directly, and the Polyglot catalog is no longer on that path. The finished session keeps the model order and the declared functions it analysed with. The Polyglot route is unchanged. --- .../src/analysis_route/_helpers/mod.rs | 1 + .../analysis_route/_helpers/revalidation.rs | 107 ++++++++++++++++++ .../src/analysis_route/main/mod.rs | 1 + .../main/native_revalidation.rs | 31 +++++ .../analysis_session/_helpers/session.rs | 12 ++ .../src/assembly/analysis_session/models.rs | 4 + .../analysis_session/tests/helpers.rs | 2 + .../_helpers/analysis_session/session.rs | 5 + .../_helpers/semantic_checks/type_recovery.rs | 50 +++++++- .../sqlbuild/compiler/pipeline/_test_types.py | 10 ++ .../compiler/pipeline/test_semantic_triage.py | 42 +++++++ 11 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 5c472fed4..393034d76 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod clickhouse_types; pub(crate) mod located_gap; pub(crate) mod native_analysis; +pub(crate) mod revalidation; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs new file mode 100644 index 000000000..d72643443 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs @@ -0,0 +1,107 @@ +//! One model's native findings read again over its bound schema with poisoned outputs untyped. + +use std::borrow::Cow; +use std::collections::HashMap; + +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; + +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::constants::UNKNOWN_TYPE; +use crate::assembly::analysis_session::main::resolved_cleaned_sql::resolved_cleaned_sql; +use crate::assembly::analysis_session::models::{ + FinishedSession, ModelOutcome, PivotOutcome, SessionSchema, +}; +use crate::assembly::analysis_session::types::Pairs; +use crate::semantic_checks::types::RevisedBinding; +use crate::semantic_validation::models::DiagnosticRow; + +/// The finished outcome of the model the session analysed as `name`. +pub(crate) fn session_outcome<'a>( + finished: &FinishedSession, + outcomes: &'a [(ModelOutcome, PivotOutcome)], + name: &str, +) -> Result<&'a ModelOutcome, String> { + let position: Option = finished + .model_names + .iter() + .position(|analysed| analysed == name); + position + .and_then(|position| outcomes.get(position)) + .map(|(outcome, _)| outcome) + .ok_or_else(|| format!("the session analysed no model {name}")) +} + +/// The findings of `outcome`'s analysed SQL over its bound schema, the poisoned columns untyped. +pub(crate) fn revised( + dialect: &dyn NativeDialect, + outcome: &ModelOutcome, + poisoned: &[(String, String)], + declared: &[String], +) -> Result, String> { + let sql: Cow<'_, str> = resolved_cleaned_sql(outcome)?; + let relations: SessionSchema = SessionSchema { + relations: outcome + .validated_schema + .iter() + .filter(|(_, columns)| !columns.is_empty()) + .map(|(relation, columns)| { + ( + relation.clone(), + schema_columns(relation, columns, poisoned), + ) + }) + .collect::>>(), + }; + let analysis: NativeAnalysis = dialect + .analyse(&AnalysisInput { + sql: &sql, + schema: &relations, + references: &[], + extension_functions: declared, + }) + .map_err(|gap| gap.message)?; + analysis + .diagnostics + .into_iter() + .map(revised_binding) + .collect() +} + +/// `relation`'s columns as the session bound them, each poisoned or unknown one untyped. +fn schema_columns( + relation: &str, + columns: &Pairs, + poisoned: &[(String, String)], +) -> Vec { + let mut schema: Vec = Vec::with_capacity(columns.len()); + for (name, data_type) in columns { + let untyped: bool = data_type.is_empty() + || data_type == UNKNOWN_TYPE + || poisoned + .iter() + .any(|(model, output)| model == relation && output == name); + schema.push(SchemaColumn { + name: name.clone(), + quoted: false, + data_type: (!untyped).then(|| data_type.clone()), + nullability: Nullability::Unknown, + }); + } + schema +} + +/// A finding as type recovery matches it: its code and character span. +fn revised_binding(row: DiagnosticRow) -> Result { + let start: Option = row + .start + .map(i64::try_from) + .transpose() + .map_err(|error| error.to_string())?; + let end: Option = row + .end + .map(i64::try_from) + .transpose() + .map_err(|error| error.to_string())?; + Ok((row.code, start, end)) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs index 66e1c9dae..9d6f86f1f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs @@ -3,5 +3,6 @@ pub mod clickhouse_type_match; pub mod fast_lineage; pub(crate) mod model_outcome; pub mod native_gap; +pub mod native_revalidation; pub mod skipped_analysis; pub mod unavailable_analysis; diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs new file mode 100644 index 000000000..84efea9ac --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs @@ -0,0 +1,31 @@ +//! Type recovery's revalidation on a native route, read by the dialect's own analysis. + +use std::sync::Arc; + +use rayon::ThreadPool; +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; + +use crate::analysis_route::_helpers::revalidation::{revised, session_outcome}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::analysis_session::models::{FinishedSession, ModelOutcome, PivotOutcome}; +use crate::semantic_checks::types::RevisedBinding; + +/// Each named model's findings, at the session's positions, with the `poisoned` outputs untyped. +pub fn native_revalidation( + dialect: &dyn NativeDialect, + (finished, outcomes): (&FinishedSession, &[(ModelOutcome, PivotOutcome)]), + models: &[String], + poisoned: &[(String, String)], +) -> Result>, String> { + let pool: Arc = finished.pool.clone()?; + pool.install(|| { + models + .par_iter() + .map(|name| { + let outcome: &ModelOutcome = session_outcome(finished, outcomes, name)?; + revised(dialect, outcome, poisoned, &finished.declared_functions) + .map_err(|reason| format!("{name}: {reason}")) + }) + .collect() + }) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 395ee1dbc..4e49c054a 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -171,6 +171,16 @@ impl AnalysisSession { dynamic_proofs.insert(model.name.clone(), proof.clone()); } } + let model_names: Vec = request + .models + .iter() + .map(|model| model.name.clone()) + .collect(); + let declared_functions: Vec = request + .function_return_types + .iter() + .map(|(name, _)| name.clone()) + .collect(); let finished = FinishedSession { tables: PivotTables { dialect: request.dialect, @@ -183,6 +193,8 @@ impl AnalysisSession { models: model_facts, dynamic_proofs, relations, + model_names, + declared_functions, }; let outcome = SessionOutcome { models, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index b3f7ff410..537ed0f04 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -114,6 +114,10 @@ pub struct FinishedSession { pub(crate) dynamic_proofs: HashMap, /// The relation facts the session ended with, when the request asked to keep them. pub(crate) relations: Option, + /// Each requested model's name, in request order, as the session's outcomes are. + pub(crate) model_names: Vec, + /// The adapter's declared functions, which the native analysis knew. + pub(crate) declared_functions: Vec, } /// The session's typed result for one compiled model, borrowed from the finished session. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 8205ca892..3597a8c17 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -400,6 +400,8 @@ pub(crate) fn finished_session(tables: PivotTables) -> FinishedSession { models: HashMap::new(), dynamic_proofs: HashMap::new(), relations: None, + model_names: Vec::new(), + declared_functions: Vec::new(), } } diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 096934a64..83e702475 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -215,6 +215,11 @@ impl NativeModelAnalysisSession { self.finished.as_ref() } + /// Each requested model's finished outcome and dynamic pivot proof, in request order. + pub(crate) fn outcomes(&self) -> &[(ModelOutcome, PivotOutcome)] { + &self.outcomes + } + /// The finished outcome and dynamic pivot proof of the model requested at `index`. pub(crate) fn outcome(&self, index: usize) -> PyResult<&(ModelOutcome, PivotOutcome)> { self.outcomes diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs index c75a2b2af..7317f9b6b 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs @@ -4,6 +4,10 @@ use std::collections::HashSet; use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyResult, Python}; use pyo3::types::{PyDict, PyDictMethods, PyFrozenSet, PyList, PyString, PyTuple}; +use sqlbuild_analysis::analysis_route::main::analysis_route::analysis_route; +use sqlbuild_analysis::analysis_route::main::native_revalidation::native_revalidation; +use sqlbuild_analysis::analysis_route::models::AnalysisRoute; +use sqlbuild_analysis::analysis_route::types::NativeDialect; use sqlbuild_analysis::semantic_checks::main::finish_type_recovery::finish_type_recovery; use sqlbuild_analysis::semantic_checks::main::plan_type_recovery::plan_type_recovery; use sqlbuild_analysis::semantic_checks::models::{ @@ -101,8 +105,15 @@ pub(crate) fn recovered_types<'py>( .plan() .ok_or_else(|| internal_error("finishing a type recovery that has no plan"))?; let poisoned: Vec<(String, String)> = plan.poisoned_outputs(); - let revised: Vec> = - revalidated(classes, project, &plan.revalidated_models(), &poisoned)?; + let models_to_revalidate: Vec = plan.revalidated_models(); + let revised: Vec> = match analysis_route(request.dialect.as_deref()) { + AnalysisRoute::Native(dialect) => { + natively_revalidated(dialect, project, (&models_to_revalidate, &poisoned), shared)? + } + AnalysisRoute::Legacy | AnalysisRoute::Unavailable => { + revalidated(classes, project, &models_to_revalidate, &poisoned)? + } + }; let outcome = finish_type_recovery(&request, plan, &revised).map_err(|reason| internal_error(&reason))?; let mut kept: Vec> = Vec::with_capacity(outcome.kept.len()); @@ -243,6 +254,41 @@ fn recovery_model( }) } +/// Each planned model revalidated by the dialect's native analysis, never the Polyglot catalog. +fn natively_revalidated( + dialect: &'static dyn NativeDialect, + project: &Bound<'_, PyAny>, + (models, poisoned): (&[usize], &[(String, String)]), + shared: &StageShared<'_>, +) -> PyResult>> { + if models.is_empty() { + return Ok(Vec::new()); + } + let py: Python<'_> = project.py(); + let session: &NativeModelAnalysisSession = shared + .session + .ok_or_else(|| internal_error("revalidating a native route without its session"))?; + let finished = session + .finished_session() + .ok_or_else(|| internal_error("revalidating before the session finished"))?; + let project_models: Bound<'_, PyAny> = native_field(project, "models")?; + let mut names: Vec = Vec::with_capacity(models.len()); + for index in models { + names.push(native_field(&project_models.get_item(*index)?, "name")?.extract()?); + } + count_stage(py, SCHEMA_REVALIDATIONS, names.len() as u64)?; + py.compiler_detach(|| { + Ok(native_revalidation( + dialect, + (finished, session.outcomes()), + &names, + poisoned, + )) + }) + .map_err(compiler_error)? + .map_err(|reason| internal_error(&reason)) +} + /// Python's revalidation of each planned model with unknown types for poisoned outputs. fn revalidated<'py>( classes: &StageClasses<'py>, diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py b/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py index ded903710..f55a9bd70 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/_test_types.py @@ -390,3 +390,13 @@ class StarShapeOrderTestCase: type PreparedCompile = tuple[ CompileOutcome, tuple[str, ...], dict[str, object | None], tuple[bytes, ...] ] + + +@dataclass(frozen=True) +class AdapterRecoveryCase: + description: str + adapter: str + expected_findings: tuple[tuple[str, str], ...] = ( + ("B002", "downstream"), + ("B212", "upstream"), + ) diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_triage.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_triage.py index 6bc407759..ad9eeb940 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_triage.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_triage.py @@ -18,6 +18,7 @@ from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter from sqlbuild.compiler.compile.types import CompiledProjectView from tests.integration.src.sqlbuild.compiler.pipeline._test_types import ( + AdapterRecoveryCase, FunctionArgumentTypeCase, SemanticTriageCase, ) @@ -239,6 +240,47 @@ def test_given_cached_validation_when_warm_or_schema_changed_then_reuses_only_ma assert tuple(item["code"] for item in result["diagnostics"]) == test_case.expected_codes +@pytest.mark.parametrize( + "test_case", + [ + AdapterRecoveryCase("bigquery", adapter="bigquery"), + AdapterRecoveryCase("duckdb", adapter="duckdb"), + AdapterRecoveryCase("postgres", adapter="postgres"), + AdapterRecoveryCase("snowflake", adapter="snowflake"), + ], + ids=lambda case: case.description, +) +def test_given_a_type_error_upstream_of_a_model_with_its_own_error_when_compiling_then_both_are_reported( + test_case: AdapterRecoveryCase, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + (tmp_path / "sqlbuild_project.toml").write_text( + f'name = "orders"\nadapter = "{test_case.adapter}"\n[rules]\nselect = []\n' + ) + (tmp_path / "models").mkdir() + header: str = ( + "MODEL (description 'Test model.', materialized view, database warehouse, schema analytics" + ) + files: dict[str, str] = { + "orders": f"{header});\n" + "SELECT CAST(1 AS INTEGER) AS quantity, CAST('2026-04-01' AS TIMESTAMP) AS ordered_at", + "upstream": f'{header});\nSELECT quantity, ordered_at, quantity + ordered_at AS bad FROM __ref("orders")', + "downstream": f'{header});\nSELECT bad > ordered_at AS compared, missing FROM __ref("upstream")', + } + for name, sql in files.items(): + (tmp_path / "models" / f"{name}.sql").write_text(sql) + assert main(["--project-dir", str(tmp_path), "compile", "--json", "--no-cache"]) == 1 + result: dict[str, Any] = json.loads(capsys.readouterr().out) + found: dict[tuple[str, str], list[str]] = { + (item["code"], item["resource_name"]): item.get("notes", []) + for item in result["diagnostics"] + } + assert tuple(sorted(found)) == test_case.expected_findings, result["diagnostics"] + assert ( + "1 downstream output uses were not type-checked because of this error" + in found[("B212", "upstream")] + ) + + @pytest.mark.parametrize( "test_case", [SemanticTriageCase("distinct spans with identical messages", expected_codes=("B212", "B212"))], From db4420d26d66d8ef38f525f1fd8673363be4146a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:36:53 +0000 Subject: [PATCH 030/160] fix(analysis): keep marker lineage and SQLBuild's text checks on native routes Fast lineage on a native route passed relation markers to the parser as written. Markers now read as indexed names through the shared relation marker reader, each tied to the resource it calls, so a model and a source of the same name stay apart. A source column's relation is compared as the dialect's profile resolves names, so upper-case folding dialects such as Snowflake keep their sources too, in fast lineage and in session lineage alike, and repeated sources of one column are listed once. Native analysis now adds SQLBuild's own text checks that the Polyglot route runs: truth-value tests the warehouse rejects (B103) and SQLite's temporal casts (B104). Output column facts keep the analysis's own type spelling, such as INTEGER or VARCHAR, and use the dialect's normalised name only to decide whether the type is known. The relation marker reader is the same as on the parser train branch. --- .../_helpers/native_analysis.rs | 41 +++++++---- .../src/analysis_route/main/fast_lineage.rs | 71 +++++++++++++----- .../tests/test_native_dialect.rs | 11 ++- .../tests/test_native_outcome.rs | 70 +++++++++++++----- .../src/analysis_route/tests/test_types.rs | 43 ++++++++++- crates/sqlbuild-analysis/src/lib.rs | 1 + .../src/lineage/main/build_fast_lineage.rs | 19 ----- .../src/lineage/tests/helpers.rs | 2 +- .../lineage/tests/test_build_fast_lineage.rs | 30 ++++---- .../src/relation_markers/_helpers/mod.rs | 1 + .../src/relation_markers/_helpers/pattern.rs | 15 ++++ .../src/relation_markers/constants.rs | 9 +++ .../relation_markers/main/markers_as_names.rs | 60 ++++++++++++++++ .../src/relation_markers/main/mod.rs | 1 + .../src/relation_markers/mod.rs | 9 +++ .../src/relation_markers/tests.rs | 4 ++ .../tests/test_markers_as_names.rs | 72 +++++++++++++++++++ .../src/relation_markers/tests/test_types.rs | 10 +++ .../src/relation_markers/types.rs | 4 ++ .../semantic_validation/_helpers/catalog.rs | 2 +- .../src/semantic_validation/main.rs | 1 + .../semantic_validation/main/text_findings.rs | 19 +++++ 22 files changed, 412 insertions(+), 83 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/relation_markers/_helpers/mod.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/_helpers/pattern.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/constants.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/main/markers_as_names.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/main/mod.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/mod.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/tests.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/tests/test_markers_as_names.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/tests/test_types.rs create mode 100644 crates/sqlbuild-analysis/src/relation_markers/types.rs create mode 100644 crates/sqlbuild-analysis/src/semantic_validation/main/text_findings.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs index ffe1bca27..3f6577207 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs @@ -1,5 +1,6 @@ //! The provided native analysis: sqlquack-semantics lineage and findings as SQLBuild's facts. +use sqlquack_semantics::dialects::types::DialectProfile; use sqlquack_semantics::lineage::models::{ LineageContext, NameFinding, Nullability, OutputLineage, QueryLineage, SourceColumn, TransformKind, @@ -17,6 +18,7 @@ use crate::assembly::analysis_session::constants::{ }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; use crate::semantic_validation::main::sqlbuild_code::sqlbuild_code; +use crate::semantic_validation::main::text_findings::text_findings; use crate::semantic_validation::models::DiagnosticRow; /// The analysis of `statement`, the one statement of `input.sql`, under `dialect`'s profile. @@ -40,7 +42,7 @@ pub(crate) fn native_analysis( lineage: lineage .outputs .iter() - .map(|output| lineage_row(output, input.references)) + .map(|output| lineage_row(dialect.profile(), output, input.references)) .collect(), row_sources: lineage .row_sources @@ -52,19 +54,20 @@ pub(crate) fn native_analysis( diagnostics: statement_name_findings(statement, context) .into_iter() .filter_map(|finding| diagnostic_row(input, finding)) + .chain(text_findings(input.sql, dialect.name())) .collect(), } } -/// An output column typed through the dialect, so both routes spell its type alike. +/// An output column in the analysis's own type spelling, kept when the dialect can compare it. fn column_fact(dialect: &D, output: &OutputLineage) -> ColumnFact { ColumnFact { name: output.name.clone(), data_type: output .data_type .as_deref() - .and_then(|spelling| dialect.normalize_type(spelling)) - .map(|normalized| normalized.normalized_name), + .filter(|spelling| dialect.normalize_type(spelling).is_some()) + .map(str::to_owned), nullability: match output.nullability { Nullability::Unknown => UNKNOWN_NULLABILITY, Nullability::Nullable => NULLABLE_NULLABILITY, @@ -75,7 +78,11 @@ fn column_fact(dialect: &D, output: &OutputLineage) - } /// An output column's lineage over the model's referenced resources, as the legacy rows give it. -fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) -> LineageRow { +fn lineage_row( + profile: &dyn DialectProfile, + output: &OutputLineage, + references: &[(String, String, String)], +) -> LineageRow { let transform_code: u8 = match output.kind { TransformKind::Constant => TRANSFORM_CONSTANT, TransformKind::Direct => TRANSFORM_DIRECT, @@ -83,11 +90,16 @@ fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) TransformKind::Expression => TRANSFORM_EXPRESSION, TransformKind::Aggregation => TRANSFORM_AGGREGATION, }; - let sources: Vec<(String, String, String)> = output + let mut sources: Vec<(String, String, String)> = Vec::new(); + for source in output .sources .iter() - .filter_map(|source| referenced_source(source, references)) - .collect(); + .filter_map(|source| referenced_source(profile, source, references)) + { + if !sources.contains(&source) { + sources.push(source); + } + } let confidence_code: u8 = if sources.is_empty() && transform_code != TRANSFORM_CONSTANT { CONFIDENCE_UNKNOWN } else if output.resolved { @@ -103,14 +115,19 @@ fn lineage_row(output: &OutputLineage, references: &[(String, String, String)]) } } -/// `(resource type, resource name, column)` of a source column the model references. +/// `(resource type, resource name, column)` of a source column, each name resolved as the profile does. fn referenced_source( + profile: &dyn DialectProfile, source: &SourceColumn, references: &[(String, String, String)], ) -> Option<(String, String, String)> { - let (_, resource_type, resource_name) = references - .iter() - .find(|(analysis_name, _, _)| *analysis_name == source.relation)?; + let (_, resource_type, resource_name) = references.iter().find(|(analysis_name, _, _)| { + *analysis_name == source.relation + || profile.same_identifier( + &profile.resolved_identifier(analysis_name, false), + &source.relation, + ) + })?; Some(( resource_type.clone(), resource_name.clone(), diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs index ee96e88ba..64eb6e6a6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use sqlbuild_sqltext::compiler::main::placeholder_defaults::placeholder_defaults; +use sqlbuild_sqltext::sql_scan::main::dialect_lexical_syntax::dialect_lexical_syntax; use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; use crate::analysis_route::_helpers::located_gap::{located, unreadable_reason}; @@ -17,26 +18,56 @@ use crate::lineage::models::{ FastLineageOutcome, LineageColumn, LineageConfidence, LineageResourceType, LineageSchemaResource, LineageSource, LineageTransformKind, }; +use crate::relation_markers::constants::MARKER_NAME_PREFIX; +use crate::relation_markers::main::markers_as_names::markers_as_names; +use crate::relation_markers::types::MarkerCall; use crate::semantic_validation::main::normalize_native::normalize_native_sql; -/// Lineage over `schema` and the `(name, type, resource)` references, or why it is unreadable. +/// Lineage over `schema` of `query_sql`, its relation markers read as names, or why it is unreadable. pub fn fast_lineage( dialect: &dyn NativeDialect, (query_sql, placeholders): (&str, &[(String, String)]), - references: &[(String, String, String)], schema: &[LineageSchemaResource], ) -> FastLineageOutcome { + let (named, calls): (String, Vec) = + match markers_as_names(query_sql, &dialect_lexical_syntax(dialect.name())) { + Ok(named) => named, + Err(reason) => return FastLineageOutcome::Unparsed(reason), + }; let normalised: String = - match normalize_native_sql(query_sql, dialect.name(), dialect.stub_prefix()) { + match normalize_native_sql(&named, dialect.name(), dialect.stub_prefix()) { Ok(normalised) => normalised, Err(reason) => return FastLineageOutcome::Unparsed(reason), }; let sql: String = placeholder_defaults(&normalised, placeholders); - let relations: SessionSchema = names_schema(schema); + let referenced: Vec<(String, LineageResourceType, String)> = calls + .into_iter() + .enumerate() + .filter_map(|(index, (function, name))| { + let resource_type: LineageResourceType = match function.as_str() { + "__ref" => LineageResourceType::Model, + "__source" => LineageResourceType::Source, + "__seed" => LineageResourceType::Seed, + _ => return None, + }; + Some((format!("{MARKER_NAME_PREFIX}{index}"), resource_type, name)) + }) + .collect(); + let references: Vec<(String, String, String)> = referenced + .iter() + .map(|(marker, resource_type, name)| { + ( + marker.clone(), + resource_type.as_str().to_owned(), + name.clone(), + ) + }) + .collect(); + let relations: SessionSchema = markers_schema(&referenced, schema); let analysed: Result = dialect.analyse(&AnalysisInput { sql: &sql, schema: &relations, - references, + references: &references, extension_functions: &[], }); match analysed { @@ -50,25 +81,33 @@ pub fn fast_lineage( } } -/// Each resource with known columns, untyped and of unknown nullability; the first of a name wins. -fn names_schema(schema: &[LineageSchemaResource]) -> SessionSchema { +/// Each marker name's resource columns, untyped and of unknown nullability, when the schema knows them. +fn markers_schema( + referenced: &[(String, LineageResourceType, String)], + schema: &[LineageSchemaResource], +) -> SessionSchema { let mut relations: HashMap> = HashMap::new(); - for resource in schema - .iter() - .filter(|resource| !resource.columns.is_empty()) - { - let _ = relations.entry(resource.name.clone()).or_insert_with(|| { + for (marker, resource_type, name) in referenced { + let Some(resource) = schema.iter().find(|resource| { + resource.resource_type == *resource_type + && resource.name == *name + && !resource.columns.is_empty() + }) else { + continue; + }; + let _ = relations.insert( + marker.clone(), resource .columns .iter() - .map(|name| SchemaColumn { - name: name.clone(), + .map(|column| SchemaColumn { + name: column.clone(), quoted: false, data_type: None, nullability: Nullability::Unknown, }) - .collect() - }); + .collect(), + ); } SessionSchema { relations } } diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index 9f8c47005..20eabe24a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -2,8 +2,8 @@ use crate::analysis_route::main::unavailable_analysis::unavailable_analysis; use crate::analysis_route::models::{NativeAnalysis, TypeMatch}; use crate::analysis_route::tests::helpers::{analysed, resource_columns}; use crate::analysis_route::tests::test_types::{ - CataloguedDialect, ColumnTestCase, FindingTestCase, LineageTestCase, MacroBodyTestCase, - NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, StarTestCase, + CataloguedDialect, ColumnTestCase, FindingTestCase, FoldingDialect, LineageTestCase, + MacroBodyTestCase, NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, StarTestCase, UnavailableAnalysisTestCase, }; use crate::analysis_route::types::NativeDialect; @@ -161,6 +161,13 @@ fn given_findings_when_analysing_then_they_take_sqlbuild_codes_and_positions() { extension_functions: &[], expected_findings: &[("B002", Some((2, 3)))], }, + FindingTestCase { + description: "a truth-value test the warehouse rejects is SQLBuild's own B103, after the parse's findings", + dialect: &FoldingDialect, + sql: "SELECT id, nope FROM orders WHERE (id > 1) IS NOT TRUE", + extension_functions: &[], + expected_findings: &[("B002", Some((1, 12))), ("B103", Some((1, 44)))], + }, FindingTestCase { description: "a function the catalogue does not know is B101", dialect: &CataloguedDialect, diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index a1d77c535..20eaba37c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -2,7 +2,8 @@ use crate::analysis_route::main::fast_lineage::fast_lineage; use crate::analysis_route::main::model_outcome::model_outcome; use crate::analysis_route::tests::helpers::{column_line, session_relations}; use crate::analysis_route::tests::test_types::{ - CataloguedDialect, NativeLineageTestCase, NativeOutcomeTestCase, PivotlessDialect, + CataloguedDialect, FoldingDialect, MarkerLineageTestCase, NativeLineageTestCase, + NativeOutcomeTestCase, PivotlessDialect, }; use crate::assembly::analysis_session::models::{ModelOutcome, ModelRequest, SessionSchema}; use crate::assembly::analysis_session::types::Shapes; @@ -127,14 +128,6 @@ fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_l columns: vec!["payment_id".to_owned()], }, ]; - let references: Vec<(String, String, String)> = [ - ("orders", "model"), - ("unknown_model", "model"), - ("raw_payments", "source"), - ] - .iter() - .map(|(name, kind)| ((*name).to_owned(), (*kind).to_owned(), (*name).to_owned())) - .collect(); let test_cases = [ NativeLineageTestCase { description: "a referenced model's columns are the upstreams of direct and computed outputs", @@ -178,13 +171,58 @@ fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_l .iter() .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) .collect(); - let parts: LineageOutcomeParts = fast_lineage( - &PivotlessDialect, - (test_case.sql, &placeholders), - &references, - &schema, - ) - .into_parts(); + let parts: LineageOutcomeParts = + fast_lineage(&PivotlessDialect, (test_case.sql, &placeholders), &schema).into_parts(); + let mut lines: Vec = parts.columns.iter().map(column_line).collect(); + lines.push(format!("{} star={}", parts.status, parts.has_star)); + lines.extend(parts.detail); + assert_eq!(lines, test_case.expected_lines, "{}", test_case.description); + } +} + +#[test] +fn given_relation_markers_when_each_dialect_reads_fast_lineage_then_sources_keep_their_resource() { + let schema: Vec = vec![ + LineageSchemaResource { + resource_type: LineageResourceType::Source, + name: "orders".to_owned(), + columns: vec!["order_id".to_owned(), "loaded_at".to_owned()], + }, + LineageSchemaResource { + resource_type: LineageResourceType::Model, + name: "orders".to_owned(), + columns: vec!["order_id".to_owned(), "amount".to_owned()], + }, + ]; + let sql: &str = r#"SELECT o.order_id, o.amount, s.loaded_at +FROM __ref("orders") AS o JOIN __source("orders") AS s ON o.order_id = s.order_id"#; + let expected_lines: &[&str] = &[ + "order_id direct high [model:orders.order_id]", + "amount direct high [model:orders.amount]", + "loaded_at direct high [source:orders.loaded_at]", + "built star=false", + ]; + let test_cases = [ + MarkerLineageTestCase { + description: "a lower-case folding dialect matches each marker's name", + dialect: &PivotlessDialect, + expected_lines, + }, + MarkerLineageTestCase { + description: "a case-preserving dialect matches each marker's name", + dialect: &CataloguedDialect, + expected_lines, + }, + MarkerLineageTestCase { + description: "an upper-case folding dialect matches each marker's name", + dialect: &FoldingDialect, + expected_lines, + }, + ]; + + for test_case in test_cases { + let parts: LineageOutcomeParts = + fast_lineage(test_case.dialect, (sql, &[]), &schema).into_parts(); let mut lines: Vec = parts.columns.iter().map(column_line).collect(); lines.push(format!("{} star={}", parts.status, parts.has_star)); lines.extend(parts.detail); diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index d814d9e28..a9b789e10 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -1,4 +1,4 @@ -use sqlquack_semantics::dialects::constants::{DUCKDB, POSTGRES}; +use sqlquack_semantics::dialects::constants::{DUCKDB, POSTGRES, SNOWFLAKE}; use sqlquack_semantics::dialects::types::DialectProfile; use sqlquack_semantics::lineage::models::Nullability; use sqlquack_syntax::expressions::statements::models::Command; @@ -116,6 +116,47 @@ impl NativeDialect for CataloguedDialect { } } +/// Snowflake's shared parser under the Snowflake profile, which folds unquoted names to upper case. +pub(super) struct FoldingDialect; + +impl NativeDialect for FoldingDialect { + fn name(&self) -> &'static str { + "snowflake" + } + + fn stub_prefix(&self) -> &'static str { + "__sqlbuild_" + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql, Dialect::Snowflake, &ParseOptions::default()) + .map(|statements| Parsed { + statements, + gaps: Vec::new(), + }) + .map_err(|failure| ParserGap { + construct: failure.to_string(), + message: failure.to_string(), + offset: 0, + syntax: false, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &SNOWFLAKE + } + + fn normalize_type(&self, spelling: &str) -> Option { + PivotlessDialect.normalize_type(spelling) + } +} + +pub(super) struct MarkerLineageTestCase { + pub(super) description: &'static str, + pub(super) dialect: &'static dyn NativeDialect, + pub(super) expected_lines: &'static [&'static str], +} + pub(super) struct ReadWholeTestCase { pub(super) description: &'static str, pub(super) sql: &'static str, diff --git a/crates/sqlbuild-analysis/src/lib.rs b/crates/sqlbuild-analysis/src/lib.rs index 5ad16bf8b..e8d6a2920 100644 --- a/crates/sqlbuild-analysis/src/lib.rs +++ b/crates/sqlbuild-analysis/src/lib.rs @@ -37,6 +37,7 @@ pub mod python_plan; pub mod python_selection; pub mod python_values; pub mod query_analysis; +pub mod relation_markers; pub mod relation_names; pub mod required_columns; pub mod resource_diagnostics; diff --git a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs index 96e1790c9..64418fc60 100644 --- a/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/main/build_fast_lineage.rs @@ -83,7 +83,6 @@ fn model_outcomes(request: &FastLineageRequest) -> Vec Vec Vec<(String, String, String)> { - let mut references: Vec<(String, String, String)> = Vec::new(); - for resource in physical_resources(query_sql) { - if !references - .iter() - .any(|(name, _, _)| *name == resource.resource_name) - { - references.push(( - resource.resource_name.clone(), - resource.resource_type.as_str().to_owned(), - resource.resource_name, - )); - } - } - references -} diff --git a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs index 0553e0e34..5a0bbaff0 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/helpers.rs @@ -36,7 +36,7 @@ pub(crate) fn deep_union_outcome_lines( stack_bytes: usize, ) -> Vec<(&'static str, Vec, bool, Option)> { let sql: String = - vec!["SELECT order_id, amount FROM __ref('orders')"; branches].join(" UNION ALL "); + vec!["SELECT order_id, amount FROM __ref(\"orders\")"; branches].join(" UNION ALL "); let worker = std::thread::Builder::new() .stack_size(stack_bytes) .spawn(move || { diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index 9e9f1a77d..6d225a9b5 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -11,7 +11,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( description: "joins, aggregation, cast, constant and expression columns", sql: "SELECT o.order_id, c.name AS customer_name, SUM(o.amount) AS total, \ CAST(o.amount AS BIGINT) AS amount_big, 1 AS one, o.amount + c.customer_id AS mix \ - FROM __ref('orders') o JOIN __ref('customers') c ON o.customer_id = c.customer_id \ + FROM __ref(\"orders\") o JOIN __ref(\"customers\") c ON o.customer_id = c.customer_id \ GROUP BY 1, 2", dialect: None, inferred_columns: &[], @@ -29,7 +29,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "unqualified columns of one resource and an unknown qualifier", - sql: "SELECT order_id, amount * 2 AS doubled, missing.thing FROM __ref('orders')", + sql: "SELECT order_id, amount * 2 AS doubled, missing.thing FROM __ref(\"orders\")", dialect: None, inferred_columns: &[], expected_status: "built", @@ -43,7 +43,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "a root star expands known columns after explicit outputs", - sql: "SELECT *, amount AS amt FROM __ref('orders')", + sql: "SELECT *, amount AS amt FROM __ref(\"orders\")", dialect: None, inferred_columns: &[], expected_status: "built", @@ -58,7 +58,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "a qualified star expands every referenced resource", - sql: "SELECT o.* FROM __ref('orders') o JOIN __seed('regions') r ON TRUE", + sql: "SELECT o.* FROM __ref(\"orders\") o JOIN __seed(\"regions\") r ON TRUE", dialect: None, inferred_columns: &[], expected_status: "built", @@ -74,8 +74,8 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "a three-branch union merges branch sources", - sql: "SELECT order_id FROM __ref('orders') UNION ALL SELECT customer_id \ - FROM __ref('customers') UNION ALL SELECT region_id + 1 FROM __seed('regions')", + sql: "SELECT order_id FROM __ref(\"orders\") UNION ALL SELECT customer_id \ + FROM __ref(\"customers\") UNION ALL SELECT region_id + 1 FROM __seed(\"regions\")", dialect: None, inferred_columns: &[], expected_status: "built", @@ -88,7 +88,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "a union with a star branch takes inferred names", - sql: "SELECT order_id, amount FROM __ref('orders') UNION SELECT * FROM __ref('customers')", + sql: "SELECT order_id, amount FROM __ref(\"orders\") UNION SELECT * FROM __ref(\"customers\")", dialect: None, inferred_columns: &["first", "second"], expected_status: "built", @@ -105,7 +105,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "a CTE collapses onto its referenced model", - sql: "WITH base AS (SELECT order_id, amount FROM __ref('orders')) \ + sql: "WITH base AS (SELECT order_id, amount FROM __ref(\"orders\")) \ SELECT order_id, amount FROM base", dialect: None, inferred_columns: &[], @@ -119,7 +119,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "unnamed projections take inferred names", - sql: "SELECT 1 + 1, COUNT(*) FROM __ref('orders')", + sql: "SELECT 1 + 1, COUNT(*) FROM __ref(\"orders\")", dialect: None, inferred_columns: &["a", "b"], expected_status: "built", @@ -129,7 +129,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( }, ParsedLineageTestCase { description: "quoted and case-varied identifiers keep their spelling", - sql: "SELECT \"Order_ID\", O.Amount FROM __ref('orders') AS O", + sql: "SELECT \"Order_ID\", O.Amount FROM __ref(\"orders\") AS O", dialect: None, inferred_columns: &[], expected_status: "built", @@ -148,7 +148,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( order_id IN (1, customer_id) AS listed, \ ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY amount) AS rn, \ EXTRACT(YEAR FROM amount) AS yr, order_id || '-' || amount AS joined \ - FROM __ref('orders')", + FROM __ref(\"orders\")", dialect: None, inferred_columns: &[], expected_status: "built", @@ -168,7 +168,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( ParsedLineageTestCase { description: "a top-level EXCEPT has no fast lineage", dialect: None, - sql: "SELECT order_id FROM __ref('orders') EXCEPT SELECT customer_id FROM __ref('customers')", + sql: "SELECT order_id FROM __ref(\"orders\") EXCEPT SELECT customer_id FROM __ref(\"customers\")", inferred_columns: &[], expected_status: "omitted", expected_lines: &[], @@ -256,8 +256,8 @@ fn given_compact_facts_with_unresolved_star_when_building_then_appends_unseen_co let test_cases = [ StarExpansionTestCase { description: "existing outputs and repeated references are skipped", - sql: "SELECT *, 1 AS amount FROM __ref('orders') JOIN __ref('orders') USING (order_id) \ - JOIN __seed('regions') USING (region_id)", + sql: "SELECT *, 1 AS amount FROM __ref(\"orders\") JOIN __ref(\"orders\") USING (order_id) \ + JOIN __seed(\"regions\") USING (region_id)", existing_columns: &["amount"], expected_lines: &[ "order_id star medium [model:orders.order_id]", @@ -268,7 +268,7 @@ fn given_compact_facts_with_unresolved_star_when_building_then_appends_unseen_co }, StarExpansionTestCase { description: "resources without known columns add nothing", - sql: "SELECT * FROM __ref('empty') JOIN __source('raw') ON TRUE", + sql: "SELECT * FROM __ref(\"empty\") JOIN __source(\"raw\") ON TRUE", existing_columns: &[], expected_lines: &[], }, diff --git a/crates/sqlbuild-analysis/src/relation_markers/_helpers/mod.rs b/crates/sqlbuild-analysis/src/relation_markers/_helpers/mod.rs new file mode 100644 index 000000000..b13857eb1 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/_helpers/mod.rs @@ -0,0 +1 @@ +pub(crate) mod pattern; diff --git a/crates/sqlbuild-analysis/src/relation_markers/_helpers/pattern.rs b/crates/sqlbuild-analysis/src/relation_markers/_helpers/pattern.rs new file mode 100644 index 000000000..4fa72a78b --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/_helpers/pattern.rs @@ -0,0 +1,15 @@ +//! The compiled relation marker call pattern. + +use std::sync::LazyLock; + +use regex::Regex; + +use crate::relation_markers::constants::RELATION_MARKER_PATTERN; + +static RELATION_MARKER: LazyLock> = + LazyLock::new(|| Regex::new(RELATION_MARKER_PATTERN).map_err(|error| error.to_string())); + +/// The compiled relation marker call pattern, or its compile error. +pub(crate) fn relation_marker_pattern() -> Result<&'static Regex, String> { + RELATION_MARKER.as_ref().map_err(Clone::clone) +} diff --git a/crates/sqlbuild-analysis/src/relation_markers/constants.rs b/crates/sqlbuild-analysis/src/relation_markers/constants.rs new file mode 100644 index 000000000..4fef92b98 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/constants.rs @@ -0,0 +1,9 @@ +//! The relation marker call pattern and the names marker calls stand as. + +/// The prefix of the name a marker call stands as while parsed; the call's index follows it. +pub const MARKER_NAME_PREFIX: &str = "__sqb_"; +/// Any relation marker call: `__name("x")`, or `__name("a", "b")` as `__dbt_ref` writes it. +pub(crate) const RELATION_MARKER_PATTERN: &str = + r#"(?i)\b(__[a-z_]+)\(\s*"([^"]+)"\s*(?:,\s*"([^"]+)"\s*)?\)"#; +/// The marker whose two names read as one, joined by `__`. +pub(crate) const DBT_REF_FUNCTION: &str = "__dbt_ref"; diff --git a/crates/sqlbuild-analysis/src/relation_markers/main/markers_as_names.rs b/crates/sqlbuild-analysis/src/relation_markers/main/markers_as_names.rs new file mode 100644 index 000000000..9330c9493 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/main/markers_as_names.rs @@ -0,0 +1,60 @@ +//! `markers_as_names`: relation marker calls in code as plain names that keep every position. + +use regex::Regex; +use sqlbuild_sqltext::sql_scan::main::dialect_non_code_ranges::dialect_non_code_ranges; +use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; + +use crate::relation_markers::_helpers::pattern::relation_marker_pattern; +use crate::relation_markers::constants::{DBT_REF_FUNCTION, MARKER_NAME_PREFIX}; +use crate::relation_markers::types::MarkerCall; + +/// `sql` with each marker call in code as `__sqb_`, padded so later text keeps its line and column, and the calls by index. +pub fn markers_as_names( + sql: &str, + syntax: &LexicalSyntax, +) -> Result<(String, Vec), String> { + let pattern: &Regex = relation_marker_pattern()?; + let mut protected: Option> = None; + let mut named: String = String::with_capacity(sql.len()); + let mut calls: Vec = Vec::new(); + let mut cursor: usize = 0; + for captures in pattern.captures_iter(sql) { + let (Some(full), Some(function), Some(first)) = + (captures.get(0), captures.get(1), captures.get(2)) + else { + continue; + }; + let ranges = protected.get_or_insert_with(|| dialect_non_code_ranges(sql, syntax)); + let following: usize = ranges.partition_point(|(start, _)| *start <= full.start()); + if following > 0 && full.start() < ranges[following - 1].1 { + continue; + } + let function_name: String = function.as_str().to_ascii_lowercase(); + let referenced_name: String = match (captures.get(3), function_name == DBT_REF_FUNCTION) { + (None, _) => first.as_str().to_owned(), + (Some(second), true) => format!("{}__{}", first.as_str(), second.as_str()), + (Some(_), false) => continue, + }; + named.push_str(&sql[cursor..full.start()]); + named.push_str(&padded_name( + &format!("{MARKER_NAME_PREFIX}{}", calls.len()), + full.as_str(), + )); + cursor = full.end(); + calls.push((function_name, referenced_name)); + } + named.push_str(&sql[cursor..]); + Ok((named, calls)) +} + +/// `name`, then the call's line breaks, then spaces as wide as the call's last line. +fn padded_name(name: &str, call: &str) -> String { + let breaks: usize = call.matches('\n').count(); + let last_line: &str = call.rsplit('\n').next().unwrap_or(call); + let width: usize = if breaks == 0 { + call.chars().count().saturating_sub(name.chars().count()) + } else { + last_line.chars().count() + }; + format!("{name}{}{}", "\n".repeat(breaks), " ".repeat(width)) +} diff --git a/crates/sqlbuild-analysis/src/relation_markers/main/mod.rs b/crates/sqlbuild-analysis/src/relation_markers/main/mod.rs new file mode 100644 index 000000000..32d7644fc --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/main/mod.rs @@ -0,0 +1 @@ +pub mod markers_as_names; diff --git a/crates/sqlbuild-analysis/src/relation_markers/mod.rs b/crates/sqlbuild-analysis/src/relation_markers/mod.rs new file mode 100644 index 000000000..af27b6661 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/mod.rs @@ -0,0 +1,9 @@ +//! Relation marker calls, such as `__ref("orders")`, read as plain relation names by strict parsers. + +mod _helpers; +pub mod constants; +pub mod main; +pub mod types; + +#[cfg(test)] +mod tests; diff --git a/crates/sqlbuild-analysis/src/relation_markers/tests.rs b/crates/sqlbuild-analysis/src/relation_markers/tests.rs new file mode 100644 index 000000000..c7c433498 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/tests.rs @@ -0,0 +1,4 @@ +#[path = "tests/test_markers_as_names.rs"] +mod test_markers_as_names; +#[path = "tests/test_types.rs"] +mod test_types; diff --git a/crates/sqlbuild-analysis/src/relation_markers/tests/test_markers_as_names.rs b/crates/sqlbuild-analysis/src/relation_markers/tests/test_markers_as_names.rs new file mode 100644 index 000000000..6df1acbc7 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/tests/test_markers_as_names.rs @@ -0,0 +1,72 @@ +use sqlbuild_sqltext::sql_scan::main::dialect_lexical_syntax::dialect_lexical_syntax; + +use crate::relation_markers::main::markers_as_names::markers_as_names; +use crate::relation_markers::tests::test_types::MarkersAsNamesTestCase; + +#[test] +fn given_marker_calls_when_reading_them_as_names_then_code_calls_become_names_and_positions_hold() { + let test_cases = [ + MarkersAsNamesTestCase { + description: "each call becomes its indexed name, padded to the call's width", + dialect: "snowflake", + sql: r#"SELECT * FROM __ref("orders") o JOIN __source("raw") r ON TRUE"#, + expected_sql: format!( + "SELECT * FROM __sqb_0{} o JOIN __sqb_1{} r ON TRUE", + " ".repeat(8), + " ".repeat(8) + ), + expected_calls: &[("__ref", "orders"), ("__source", "raw")], + }, + MarkersAsNamesTestCase { + description: "calls in quoted text and comments stay; an upper-case call folds", + dialect: "snowflake", + sql: "SELECT '__ref(\"a\")' AS q -- __ref(\"b\")\nFROM __REF(\"Orders\") // __ref(\"c\")", + expected_sql: format!( + "SELECT '__ref(\"a\")' AS q -- __ref(\"b\")\nFROM __sqb_0{} // __ref(\"c\")", + " ".repeat(8) + ), + expected_calls: &[("__ref", "Orders")], + }, + MarkersAsNamesTestCase { + description: "a call over three lines keeps its line breaks and its last line's width", + dialect: "duckdb", + sql: "FROM __ref(\n \"orders\"\n) AS o", + expected_sql: "FROM __sqb_0\n\n AS o".to_owned(), + expected_calls: &[("__ref", "orders")], + }, + MarkersAsNamesTestCase { + description: "two dbt names join; a non-ASCII name pads by characters, not bytes", + dialect: "postgres", + sql: r#"FROM __dbt_ref("shop", "customers") d JOIN __ref("café") c ON TRUE"#, + expected_sql: format!( + "FROM __sqb_0{} d JOIN __sqb_1{} c ON TRUE", + " ".repeat(23), + " ".repeat(6) + ), + expected_calls: &[("__dbt_ref", "shop__customers"), ("__ref", "café")], + }, + MarkersAsNamesTestCase { + description: "calls with a second name, or a non-literal argument, stay calls", + dialect: "duckdb", + sql: r#"FROM __ref("a", "b") JOIN __table_fn("orders", 2) ON TRUE"#, + expected_sql: r#"FROM __ref("a", "b") JOIN __table_fn("orders", 2) ON TRUE"#.to_owned(), + expected_calls: &[], + }, + ]; + for test_case in test_cases { + let (named, calls) = + markers_as_names(test_case.sql, &dialect_lexical_syntax(test_case.dialect)) + .expect(test_case.description); + let expected_calls: Vec<(String, String)> = test_case + .expected_calls + .iter() + .map(|(function, name)| ((*function).to_owned(), (*name).to_owned())) + .collect(); + assert_eq!( + (named, calls), + (test_case.expected_sql, expected_calls), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/relation_markers/tests/test_types.rs b/crates/sqlbuild-analysis/src/relation_markers/tests/test_types.rs new file mode 100644 index 000000000..b92a6d74f --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/tests/test_types.rs @@ -0,0 +1,10 @@ +//! Test case types for relation markers read as names. + +/// SQL in one dialect, the SQL its markers read as, and the calls they stand for. +pub(crate) struct MarkersAsNamesTestCase { + pub(crate) description: &'static str, + pub(crate) dialect: &'static str, + pub(crate) sql: &'static str, + pub(crate) expected_sql: String, + pub(crate) expected_calls: &'static [(&'static str, &'static str)], +} diff --git a/crates/sqlbuild-analysis/src/relation_markers/types.rs b/crates/sqlbuild-analysis/src/relation_markers/types.rs new file mode 100644 index 000000000..9b90a22d2 --- /dev/null +++ b/crates/sqlbuild-analysis/src/relation_markers/types.rs @@ -0,0 +1,4 @@ +//! Shapes of relation marker calls. + +/// A marker call's lower-case function, such as `__ref`, and the name it references. +pub type MarkerCall = (String, String); diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs index 32fd8e819..ff89c238f 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs @@ -656,7 +656,7 @@ fn messaged_row(error: ValidationError) -> DiagnosticRow { row } -pub(super) fn diagnostic_row(error: ValidationError) -> DiagnosticRow { +pub(crate) fn diagnostic_row(error: ValidationError) -> DiagnosticRow { let severity = match error.severity { polyglot_sql::ValidationSeverity::Error => "error", _ => "warning", diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main.rs b/crates/sqlbuild-analysis/src/semantic_validation/main.rs index 2db687f9e..3805b70c7 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/main.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/main.rs @@ -9,6 +9,7 @@ pub mod normalize_dialect; pub mod normalize_mapped; pub mod normalize_native; pub mod sqlbuild_code; +pub mod text_findings; pub mod truth_test_spans; use crate::semantic_validation::models::ProjectCatalog; diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main/text_findings.rs b/crates/sqlbuild-analysis/src/semantic_validation/main/text_findings.rs new file mode 100644 index 000000000..7ff13fcb0 --- /dev/null +++ b/crates/sqlbuild-analysis/src/semantic_validation/main/text_findings.rs @@ -0,0 +1,19 @@ +//! SQLBuild's own checks of SQL text that every analysis route adds to the parse's findings. + +use crate::lineage::main::parser_dialect::parser_dialect; +use crate::semantic_validation::_helpers::catalog::diagnostic_row; +use crate::semantic_validation::_helpers::sqlite_temporal_casts::sqlite_temporal_cast_warnings; +use crate::semantic_validation::_helpers::truth_tests::unsupported_truth_test_errors; +use crate::semantic_validation::models::DiagnosticRow; + +/// Truth-value tests the warehouse rejects (B103) and SQLite's temporal casts (B104) in `sql`. +pub fn text_findings(sql: &str, dialect: &str) -> Vec { + let Ok(dialect) = parser_dialect(Some(dialect)) else { + return Vec::new(); + }; + unsupported_truth_test_errors(sql, dialect) + .into_iter() + .chain(sqlite_temporal_cast_warnings(sql, dialect)) + .map(diagnostic_row) + .collect() +} From 3151511d38e718d9e38f54ed54092a91551f443f Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:58:09 +0000 Subject: [PATCH 031/160] test(analysis): re-record native-route goldens and pin SQLQuack at the parity merge Pins SQLQuack at ed14b717 (main fa55d9a1 with the DuckDB parity link); the pin moves to main once that link lands. Golden changes: - model-sql-syntax, syntax-hook, syntax-inline-hook: SQL the native parser cannot read is P026 rather than P001. The cases now set require_sql_analysis so P026 is the failing error. - seed 0, 6 and 10: amount passes through a CAST in a CTE, so its trace is cast rather than direct; seed 10 is now high confidence. - analysis-window-without-over, analysis-unknown-function, analysis-aggregate-in-where: the finding is placed on the call itself. --- Cargo.lock | 22 +++++++++---------- Cargo.toml | 10 ++++----- .../_helpers/analysis_corpus/failure_cases.py | 16 +++++++++----- .../_helpers/corpus/failure_cases.py | 12 ++++++---- .../failure/analysis-aggregate-in-where.json | 6 ++--- .../failure/analysis-unknown-function.json | 6 ++--- .../failure/analysis-window-without-over.json | 6 +++-- .../compiler/failure/model-sql-syntax.json | 4 ++-- .../goldens/compiler/failure/syntax-hook.json | 4 ++-- .../compiler/failure/syntax-inline-hook.json | 4 ++-- tests/goldens/compiler/seed/0.json | 2 +- tests/goldens/compiler/seed/10.json | 4 ++-- tests/goldens/compiler/seed/6.json | 2 +- 13 files changed, 55 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3104605b4..cbf760e52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4502,7 +4502,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "serde", "serde_json", @@ -4512,7 +4512,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "regex", "serde", @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "serde", "sqlquack-core", @@ -4535,7 +4535,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "serde", "serde_json", @@ -4548,7 +4548,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "proc-macro2", "quote", @@ -4558,7 +4558,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-duckdb" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "sqlquack-syntax", "stacker", @@ -4567,7 +4567,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "sqlquack-syntax", "stacker", @@ -4576,7 +4576,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-sqlite" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=951f8cac05040ff3f5aea7ab370f95153b3e8bf9#951f8cac05040ff3f5aea7ab370f95153b3e8bf9" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" dependencies = [ "sqlquack-syntax", "stacker", @@ -4673,7 +4673,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5109,7 +5109,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bd81a45ff..59b18ec7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,12 +177,12 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } -sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } -sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "951f8cac05040ff3f5aea7ab370f95153b3e8bf9" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } +sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } +sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/scripts/compiler_differential/_helpers/analysis_corpus/failure_cases.py b/scripts/compiler_differential/_helpers/analysis_corpus/failure_cases.py index 72be26188..3c57090b5 100644 --- a/scripts/compiler_differential/_helpers/analysis_corpus/failure_cases.py +++ b/scripts/compiler_differential/_helpers/analysis_corpus/failure_cases.py @@ -464,18 +464,24 @@ def _syntax_cases() -> tuple[FailureCase, ...]: return ( failure_case( name="syntax-hook", - expected_code="P001", - expected_message="record_refresh", + expected_code="P026", + expected_message="customer_totals", files={ + FAILURE_CONFIG_PATH: FAILURE_BASE_CONFIG + + "\n[settings]\nrequire_sql_analysis = true\n", _HOOK_PATH: ('HOOK (\n description "Record a refresh"\n);\n\nSELECT FROM WHERE\n'), **_mart_header(extra=' post_hooks [sql("record_refresh")],'), }, ), failure_case( name="syntax-inline-hook", - expected_code="P001", - expected_message="hook", - files=_mart_header(extra=' post_hooks [inline_sql("SELECT FROM WHERE")],'), + expected_code="P026", + expected_message="customer_totals", + files={ + FAILURE_CONFIG_PATH: FAILURE_BASE_CONFIG + + "\n[settings]\nrequire_sql_analysis = true\n", + **_mart_header(extra=' post_hooks [inline_sql("SELECT FROM WHERE")],'), + }, ), failure_case( name="syntax-function", diff --git a/scripts/compiler_differential/_helpers/corpus/failure_cases.py b/scripts/compiler_differential/_helpers/corpus/failure_cases.py index f09db8c72..ca39d793a 100644 --- a/scripts/compiler_differential/_helpers/corpus/failure_cases.py +++ b/scripts/compiler_differential/_helpers/corpus/failure_cases.py @@ -113,10 +113,14 @@ def _compile_failure_cases() -> tuple[FailureCase, ...]: ), failure_case( name="model-sql-syntax", - expected_code="P001", - files=staging_files( - FAILURE_BASE_STAGING.replace("SELECT order_id,", "SELECT order_id,, ") - ), + expected_code="P026", + files={ + FAILURE_CONFIG_PATH: FAILURE_BASE_CONFIG + + "\n[settings]\nrequire_sql_analysis = true\n", + **staging_files( + FAILURE_BASE_STAGING.replace("SELECT order_id,", "SELECT order_id,, ") + ), + }, ), failure_case( name="test-parse", diff --git a/tests/goldens/compiler/failure/analysis-aggregate-in-where.json b/tests/goldens/compiler/failure/analysis-aggregate-in-where.json index 773acca30..9d8102710 100644 --- a/tests/goldens/compiler/failure/analysis-aggregate-in-where.json +++ b/tests/goldens/compiler/failure/analysis-aggregate-in-where.json @@ -13,13 +13,13 @@ "resource_name": "customer_totals", "path": "models/marts/customer_totals.sql", "line": 7, - "column": 11, + "column": 7, "location": { "path": "models/marts/customer_totals.sql", "line": 7, - "column": 11, + "column": 7, "end_line": 7, - "end_column": 17 + "end_column": 18 }, "help": "move this aggregate to SELECT or HAVING, or compute it in an input CTE" } diff --git a/tests/goldens/compiler/failure/analysis-unknown-function.json b/tests/goldens/compiler/failure/analysis-unknown-function.json index 6f34c1a3a..f421777a0 100644 --- a/tests/goldens/compiler/failure/analysis-unknown-function.json +++ b/tests/goldens/compiler/failure/analysis-unknown-function.json @@ -13,13 +13,13 @@ "resource_name": "customer_totals", "path": "models/marts/customer_totals.sql", "line": 5, - "column": 36, + "column": 21, "location": { "path": "models/marts/customer_totals.sql", "line": 5, - "column": 36, + "column": 21, "end_line": 5, - "end_column": 42 + "end_column": 43 }, "help": "check the function spelling or declare the project function before using it" } diff --git a/tests/goldens/compiler/failure/analysis-window-without-over.json b/tests/goldens/compiler/failure/analysis-window-without-over.json index b9762e9a4..c4ea9ab5f 100644 --- a/tests/goldens/compiler/failure/analysis-window-without-over.json +++ b/tests/goldens/compiler/failure/analysis-window-without-over.json @@ -13,11 +13,13 @@ "resource_name": "customer_totals", "path": "models/marts/customer_totals.sql", "line": 5, - "column": 1, + "column": 21, "location": { "path": "models/marts/customer_totals.sql", "line": 5, - "column": 1 + "column": 21, + "end_line": 5, + "end_column": 33 }, "help": "use this window function with a valid OVER clause and window context" } diff --git a/tests/goldens/compiler/failure/model-sql-syntax.json b/tests/goldens/compiler/failure/model-sql-syntax.json index d0fd5e25f..d848b2834 100644 --- a/tests/goldens/compiler/failure/model-sql-syntax.json +++ b/tests/goldens/compiler/failure/model-sql-syntax.json @@ -7,8 +7,8 @@ { "phase": "compile", "severity": "error", - "code": "P001", - "message": "SQL syntax error in model 'stg_orders' (/models/staging/stg_orders.sql): Unexpected token: Comma", + "code": "P026", + "message": "SQL analysis skipped for model 'stg_orders': the duckdb parser cannot read Syntax error at or near ',' at line 1, column 17", "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" } ] diff --git a/tests/goldens/compiler/failure/syntax-hook.json b/tests/goldens/compiler/failure/syntax-hook.json index d8ab5bfc2..ea7695256 100644 --- a/tests/goldens/compiler/failure/syntax-hook.json +++ b/tests/goldens/compiler/failure/syntax-hook.json @@ -7,8 +7,8 @@ { "phase": "compile", "severity": "error", - "code": "P001", - "message": "Polyglot could not parse model 'customer_totals' post_hooks[0] sql(\"record_refresh\") (hooks/sql/record_refresh.sql): Expected table name or subquery, got Where", + "code": "P026", + "message": "SQL analysis skipped for model 'customer_totals': the duckdb parser cannot read Syntax error at or near 'WHERE' at line 1, column 13", "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" } ] diff --git a/tests/goldens/compiler/failure/syntax-inline-hook.json b/tests/goldens/compiler/failure/syntax-inline-hook.json index 1a52b3698..ea7695256 100644 --- a/tests/goldens/compiler/failure/syntax-inline-hook.json +++ b/tests/goldens/compiler/failure/syntax-inline-hook.json @@ -7,8 +7,8 @@ { "phase": "compile", "severity": "error", - "code": "P001", - "message": "Polyglot could not parse model 'customer_totals' post_hooks[0] inline_sql(\"...\") (/models/marts/customer_totals.sql): Expected table name or subquery, got Where", + "code": "P026", + "message": "SQL analysis skipped for model 'customer_totals': the duckdb parser cannot read Syntax error at or near 'WHERE' at line 1, column 13", "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" } ] diff --git a/tests/goldens/compiler/seed/0.json b/tests/goldens/compiler/seed/0.json index 92bd813db..b93e0579c 100644 --- a/tests/goldens/compiler/seed/0.json +++ b/tests/goldens/compiler/seed/0.json @@ -199,7 +199,7 @@ "resource_name": "cte_facts_stg_fulfillment_1", "column_name": "amount" }, - "transform": "direct", + "transform": "cast", "confidence": "high" }, { diff --git a/tests/goldens/compiler/seed/10.json b/tests/goldens/compiler/seed/10.json index 9f1ba0819..2dbbb539c 100644 --- a/tests/goldens/compiler/seed/10.json +++ b/tests/goldens/compiler/seed/10.json @@ -213,8 +213,8 @@ "resource_name": "cte_facts_stg_sales_0", "column_name": "amount" }, - "transform": "direct", - "confidence": "medium" + "transform": "cast", + "confidence": "high" }, { "source": { diff --git a/tests/goldens/compiler/seed/6.json b/tests/goldens/compiler/seed/6.json index ccc390528..6b89a5755 100644 --- a/tests/goldens/compiler/seed/6.json +++ b/tests/goldens/compiler/seed/6.json @@ -157,7 +157,7 @@ "resource_name": "cte_facts_stg_fulfillment_1", "column_name": "amount" }, - "transform": "direct", + "transform": "cast", "confidence": "high" }, { From b2660123510e7fa80e9623dc95e2bce8c4274166 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:01:14 +0000 Subject: [PATCH 032/160] test(lineage): an unterminated DuckDB query reports where the native parser stops --- .../src/lineage/tests/test_build_fast_lineage.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index 6d225a9b5..ec6bce1e4 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -176,14 +176,16 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( expected_detail: None, }, ParsedLineageTestCase { - description: "an unterminated query reports the parse error Python logs", + description: "an unterminated query reports where the dialect's parser stops", dialect: Some("duckdb"), sql: "SELECT order_id FROM (", inferred_columns: &[], expected_status: "unparsed", expected_lines: &[], expected_has_star: false, - expected_detail: Some("Parse error at line 1, column 23: Unexpected end of input"), + expected_detail: Some( + "the duckdb parser cannot read Syntax error at or near end of input at line 1, column 23", + ), }, ParsedLineageTestCase { description: "two statements report the statement count", From 5813dd12b5a6abaf23f69dc7b7553d946695b445 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:01:37 +0000 Subject: [PATCH 033/160] fix(sqltext): read T-SQL bracket names and ClickHouse quoting in dialect scans A T-SQL `[...]` name was code to the dialect scan, so an apostrophe, `--` or `/*` inside one opened a string or comment that hid everything after it. On a native route a cursor stub after such a name was not respelt, and a valid model was skipped with P026. Lexical rules gain bracket identifiers, closing at `]` with `]]` for one bracket, and the T-SQL rules set them. Adapter records never set the flag, so their keys and scans are unchanged. ClickHouse's rules now follow its lexer: a backslash escapes inside single quotes, double quotes and backticks, and `#` starts a comment only before a space or `!`. --- .../tests/test_normalize_batch.rs | 12 ++++++ .../_helpers/sqltext/lexical_syntax.rs | 2 + .../src/sql_scan/_helpers/dialect.rs | 15 +++++++ .../src/sql_scan/constants.rs | 2 + .../sql_scan/main/dialect_lexical_syntax.rs | 5 ++- .../src/sql_scan/main/lexical_syntax_key.rs | 12 ++++-- .../main/reads_differently_from_generic.rs | 3 ++ .../sqlbuild-sqltext/src/sql_scan/models.rs | 3 ++ .../src/sql_scan/tests/helpers.rs | 1 + .../src/sql_scan/tests/test_dialect_scan.rs | 43 +++++++++++++++++++ 10 files changed, 92 insertions(+), 6 deletions(-) diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs index 05db7af22..95dcadd3f 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_normalize_batch.rs @@ -162,6 +162,18 @@ fn given_cursor_stubs_when_normalizing_for_a_native_parser_then_only_whole_names sql: r"SELECT 'it\'s __cursor_end()', __cursor_end() FROM t", expected_sql: r"SELECT 'it\'s __cursor_end()', __sqlbuild.cursor_end() FROM t", }, + NativeStubTestCase { + dialect: "tsql", + description: "a T-SQL bracket name's apostrophe opens no string, so the stub after it is code", + sql: "SELECT [customer's name] AS n FROM t WHERE ts >= __cursor_start()", + expected_sql: "SELECT [customer's name] AS n FROM t WHERE ts >= __sqlbuild.cursor_start()", + }, + NativeStubTestCase { + dialect: "clickhouse", + description: "a ClickHouse backslash-quoted quote in an identifier keeps the stub after it in code", + sql: r#"SELECT 1 AS "a\"b", __cursor_end() FROM t"#, + expected_sql: r#"SELECT 1 AS "a\"b", __sqlbuild.cursor_end() FROM t"#, + }, NativeStubTestCase { dialect: "snowflake", description: "a cursor stub's text inside a Snowflake // comment is left as written", diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/lexical_syntax.rs b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/lexical_syntax.rs index 600dd863e..39148bb37 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sqltext/lexical_syntax.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sqltext/lexical_syntax.rs @@ -30,6 +30,7 @@ impl From for LexicalSyntax { triple_quoted_strings: input.triple_quoted_strings, nested_block_comments: input.nested_block_comments, line_comment_prefixes: input.line_comment_prefixes, + bracket_identifiers: false, } } } @@ -54,6 +55,7 @@ pub(crate) fn record_syntax(instance: &Bound<'_, PyAny>) -> PyResult dollar_quote_end(sql, index), Some(b'\'' | b'"' | b'`') => quoted_text_end(sql, index, syntax).map(Some), + Some(b'[') if syntax.bracket_identifiers => bracket_identifier_end(sql, index).map(Some), _ => Ok(None), } } @@ -44,6 +45,7 @@ pub(crate) fn non_code_end( /// Whether a comment or quoted text may open with `byte`; most code bytes are rejected here cheaply. fn may_open_non_code(byte: u8, syntax: &LexicalSyntax) -> bool { matches!(byte, b'/' | b'$' | b'\'' | b'"' | b'`') + || (byte == b'[' && syntax.bracket_identifiers) || syntax .line_comment_prefixes .iter() @@ -133,6 +135,19 @@ fn quoted_text_end(sql: &[u8], start: usize, syntax: &LexicalSyntax) -> Result Result { + let mut index = start + 1; + while let Some(offset) = sql[index..].iter().position(|byte| *byte == b']') { + let close = index + offset; + if sql.get(close + 1) != Some(&b']') { + return Ok(close + 1); + } + index = close + 2; + } + Err(Unclosed::Quote) +} + fn string_literal_prefix(sql: &[u8], start: usize) -> Vec { let mut prefix_start = start; while prefix_start > 0 diff --git a/crates/sqlbuild-sqltext/src/sql_scan/constants.rs b/crates/sqlbuild-sqltext/src/sql_scan/constants.rs index f7d11818a..b604b49f1 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/constants.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/constants.rs @@ -14,3 +14,5 @@ pub const GENERIC_LINE_COMMENT_PREFIX: &str = "--"; pub const TRIPLE_QUOTES: [&str; 2] = ["'''", "\"\"\""]; pub const ESCAPE_CHARACTER: char = '\\'; pub const BLOCK_COMMENT_OPEN: &str = "/*"; +/// What a lexical key adds for bracket identifiers. +pub(crate) const BRACKET_IDENTIFIERS_KEY: &str = "brackets"; diff --git a/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs b/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs index ff94e94b3..3028fc6db 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/main/dialect_lexical_syntax.rs @@ -20,8 +20,8 @@ pub fn dialect_lexical_syntax(dialect: &str) -> LexicalSyntax { ..LexicalSyntax::default() }, "clickhouse" => LexicalSyntax { - backslash_escape_quotes: strings(&["'"]), - line_comment_prefixes: lines(&["--", "#", "//"]), + backslash_escape_quotes: strings(&["'", "\"", "`"]), + line_comment_prefixes: lines(&["--", "# ", "#!", "//"]), ..LexicalSyntax::default() }, "databricks" | "duckdb" => LexicalSyntax { @@ -31,6 +31,7 @@ pub fn dialect_lexical_syntax(dialect: &str) -> LexicalSyntax { "mssql" | "sqlserver" | "tsql" => LexicalSyntax { nested_block_comments: true, line_comment_prefixes: lines(&["--"]), + bracket_identifiers: true, ..LexicalSyntax::default() }, _ => LexicalSyntax { diff --git a/crates/sqlbuild-sqltext/src/sql_scan/main/lexical_syntax_key.rs b/crates/sqlbuild-sqltext/src/sql_scan/main/lexical_syntax_key.rs index ff2cd882d..5bcdb7be2 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/main/lexical_syntax_key.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/main/lexical_syntax_key.rs @@ -1,8 +1,9 @@ //! The process-stable text key of one dialect's lexical rules. +use crate::sql_scan::constants::BRACKET_IDENTIFIERS_KEY; use crate::sql_scan::models::LexicalSyntax; -/// `SqlLexicalSyntax.cache_key`: sorted quotes, the four flags as Python booleans, sorted prefixes. +/// `SqlLexicalSyntax.cache_key`: quotes, the four flags, prefixes, then `brackets` only when set. #[must_use] pub fn lexical_syntax_key(syntax: &LexicalSyntax) -> String { let flag = |value: bool| if value { "True" } else { "False" }; @@ -18,13 +19,16 @@ pub fn lexical_syntax_key(syntax: &LexicalSyntax) -> String { .map(String::as_str) .collect(); prefixes.sort_unstable(); - [ + let mut parts: Vec = vec![ quotes.join(","), flag(syntax.escape_string_prefix).to_owned(), flag(syntax.raw_string_prefix).to_owned(), flag(syntax.triple_quoted_strings).to_owned(), flag(syntax.nested_block_comments).to_owned(), prefixes.join(","), - ] - .join("|") + ]; + if syntax.bracket_identifiers { + parts.push(BRACKET_IDENTIFIERS_KEY.to_owned()); + } + parts.join("|") } diff --git a/crates/sqlbuild-sqltext/src/sql_scan/main/reads_differently_from_generic.rs b/crates/sqlbuild-sqltext/src/sql_scan/main/reads_differently_from_generic.rs index 05479da83..3ea203edd 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/main/reads_differently_from_generic.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/main/reads_differently_from_generic.rs @@ -19,6 +19,9 @@ pub fn reads_differently_from_generic(syntax: &LexicalSyntax, sql: &str) -> bool if syntax.nested_block_comments && sql.matches(BLOCK_COMMENT_OPEN).count() > 1 { return true; } + if syntax.bracket_identifiers && sql.contains('[') { + return true; + } syntax .line_comment_prefixes .iter() diff --git a/crates/sqlbuild-sqltext/src/sql_scan/models.rs b/crates/sqlbuild-sqltext/src/sql_scan/models.rs index 632b1b5b0..b89ffe1f8 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/models.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/models.rs @@ -81,6 +81,9 @@ pub struct LexicalSyntax { pub nested_block_comments: bool, /// Prefixes that start a line comment, such as `--`, `#` or `//`. pub line_comment_prefixes: Vec, + /// `[...]` quotes an identifier, `]]` standing for one `]`, as in T-SQL. + #[serde(default)] + pub bracket_identifiers: bool, } impl LexicalSyntax { diff --git a/crates/sqlbuild-sqltext/src/sql_scan/tests/helpers.rs b/crates/sqlbuild-sqltext/src/sql_scan/tests/helpers.rs index 911a58165..ad9f12b51 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/tests/helpers.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/tests/helpers.rs @@ -127,5 +127,6 @@ pub(crate) fn syntax(quotes: &[&str], flags: [bool; 4], prefixes: &[&str]) -> Le triple_quoted_strings: flags[2], nested_block_comments: flags[3], line_comment_prefixes: prefixes.iter().map(|prefix| (*prefix).to_owned()).collect(), + bracket_identifiers: false, } } diff --git a/crates/sqlbuild-sqltext/src/sql_scan/tests/test_dialect_scan.rs b/crates/sqlbuild-sqltext/src/sql_scan/tests/test_dialect_scan.rs index b23fd27f9..24354b62d 100644 --- a/crates/sqlbuild-sqltext/src/sql_scan/tests/test_dialect_scan.rs +++ b/crates/sqlbuild-sqltext/src/sql_scan/tests/test_dialect_scan.rs @@ -1,3 +1,4 @@ +use crate::sql_scan::main::dialect_lexical_syntax::dialect_lexical_syntax; use crate::sql_scan::main::dialect_non_code_end::dialect_non_code_end; use crate::sql_scan::main::dialect_non_code_ranges::dialect_non_code_ranges; use crate::sql_scan::models::{LexicalSyntax, Unclosed}; @@ -151,6 +152,48 @@ fn given_dialect_lexical_syntax_when_scanning_non_code_then_dialect_decides_boun syntax: generic(), expected_first_range: Some((0, 12)), }, + DialectFirstNonCodeRangeTestCase { + description: "a T-SQL bracket identifier hides the apostrophe inside it", + sql: "[customer's name] x", + syntax: dialect_lexical_syntax("tsql"), + expected_first_range: Some((0, 17)), + }, + DialectFirstNonCodeRangeTestCase { + description: "a doubled closing bracket stays inside the identifier", + sql: "[a]]'b] x", + syntax: dialect_lexical_syntax("tsql"), + expected_first_range: Some((0, 7)), + }, + DialectFirstNonCodeRangeTestCase { + description: "a comment prefix inside a bracket identifier opens no comment", + sql: "[a--b] x", + syntax: dialect_lexical_syntax("tsql"), + expected_first_range: Some((0, 6)), + }, + DialectFirstNonCodeRangeTestCase { + description: "outside T-SQL a bracket is code, so the apostrophe opens a quote", + sql: "[customer's name] x", + syntax: generic(), + expected_first_range: Some((9, 19)), + }, + DialectFirstNonCodeRangeTestCase { + description: "ClickHouse backslash escapes hold in double-quoted identifiers", + sql: r#""a\"b" x"#, + syntax: dialect_lexical_syntax("clickhouse"), + expected_first_range: Some((0, 6)), + }, + DialectFirstNonCodeRangeTestCase { + description: "a ClickHouse hash before a word is code, not a comment", + sql: "#x 'a'", + syntax: dialect_lexical_syntax("clickhouse"), + expected_first_range: Some((3, 6)), + }, + DialectFirstNonCodeRangeTestCase { + description: "a ClickHouse hash before a space opens a comment", + sql: "# it's\nx", + syntax: dialect_lexical_syntax("clickhouse"), + expected_first_range: Some((0, 7)), + }, DialectFirstNonCodeRangeTestCase { description: "an unclosed backslash-escaped quote extends to the end", sql: r"'O\'", From 1eaa0f1de6eeea410723a9831015bedb6d70238a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:01:37 +0000 Subject: [PATCH 034/160] fix(analysis): read repeated markers as one relation and keep unreadable revalidations Native fast lineage gave every relation marker call its own name, so a union of two branches over one model read two relations and its columns became expressions. Calls of the same marker now share their first call's name, padded to keep positions. Native revalidation in type recovery failed the compile when the dialect's parser could not read a planned model again. Such a model now keeps its own findings, as an unrevised model would. --- .../analysis_route/_helpers/marker_names.rs | 50 +++++++++++ .../src/analysis_route/_helpers/mod.rs | 1 + .../analysis_route/_helpers/revalidation.rs | 34 +++++--- .../src/analysis_route/main/fast_lineage.rs | 21 +++-- .../main/native_revalidation.rs | 18 ++-- .../src/analysis_route/tests.rs | 2 + .../src/analysis_route/tests/helpers.rs | 42 +++++++++- .../tests/test_native_outcome.rs | 9 ++ .../tests/test_native_revalidation.rs | 84 +++++++++++++++++++ .../src/analysis_route/tests/test_types.rs | 7 ++ .../_helpers/semantic_checks/type_recovery.rs | 28 ++++--- 11 files changed, 258 insertions(+), 38 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/marker_names.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/marker_names.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/marker_names.rs new file mode 100644 index 000000000..c5e547fe7 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/marker_names.rs @@ -0,0 +1,50 @@ +//! One analysis name per distinct relation marker call, so repeated calls read as one relation. + +use std::sync::LazyLock; + +use regex::{Captures, Regex}; + +use crate::relation_markers::constants::MARKER_NAME_PREFIX; +use crate::relation_markers::types::MarkerCall; + +static MARKER_NAME: LazyLock> = LazyLock::new(|| { + Regex::new(&format!(r"\b{MARKER_NAME_PREFIX}(\d+)\b")).map_err(|error| error.to_string()) +}); + +/// `named` with each marker name renamed to its call's first index, padded to keep positions. +pub(crate) fn first_call_names(named: &str, calls: &[MarkerCall]) -> Result { + let pattern: &Regex = MARKER_NAME.as_ref().map_err(Clone::clone)?; + Ok(pattern + .replace_all(named, |captures: &Captures<'_>| { + let written: &str = captures.get(0).map_or("", |found| found.as_str()); + let first: Option = match captures.get(1).map(|index| index.as_str().parse()) { + Some(Ok(index)) => first_index(calls, index), + Some(Err(_)) | None => None, + }; + match first { + Some(first) => { + let name: String = format!("{MARKER_NAME_PREFIX}{first}"); + format!("{name: written.to_owned(), + } + }) + .into_owned()) +} + +/// The calls whose index is their call's first, in order, with that index. +pub(crate) fn distinct_calls(calls: &[MarkerCall]) -> Vec<(usize, &MarkerCall)> { + let mut distinct: Vec<(usize, &MarkerCall)> = Vec::with_capacity(calls.len()); + for (index, call) in calls.iter().enumerate() { + if first_index(calls, index) == Some(index) { + distinct.push((index, call)); + } + } + distinct +} + +/// The first index of the call at `index`, which is `index` unless an earlier call is the same. +fn first_index(calls: &[MarkerCall], index: usize) -> Option { + let call: &MarkerCall = calls.get(index)?; + calls.iter().position(|earlier| earlier == call) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 393034d76..279c10ae9 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod clickhouse_types; pub(crate) mod located_gap; +pub(crate) mod marker_names; pub(crate) mod native_analysis; pub(crate) mod revalidation; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs index d72643443..44d397612 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; -use crate::analysis_route::models::{AnalysisInput, NativeAnalysis}; +use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::UNKNOWN_TYPE; use crate::assembly::analysis_session::main::resolved_cleaned_sql::resolved_cleaned_sql; @@ -13,6 +13,7 @@ use crate::assembly::analysis_session::models::{ FinishedSession, ModelOutcome, PivotOutcome, SessionSchema, }; use crate::assembly::analysis_session::types::Pairs; +use crate::semantic_checks::models::RecoveryModel; use crate::semantic_checks::types::RevisedBinding; use crate::semantic_validation::models::DiagnosticRow; @@ -32,13 +33,13 @@ pub(crate) fn session_outcome<'a>( .ok_or_else(|| format!("the session analysed no model {name}")) } -/// The findings of `outcome`'s analysed SQL over its bound schema, the poisoned columns untyped. +/// The findings of `outcome`'s SQL with the poisoned columns untyped; `None` when it is unreadable. pub(crate) fn revised( dialect: &dyn NativeDialect, outcome: &ModelOutcome, poisoned: &[(String, String)], declared: &[String], -) -> Result, String> { +) -> Result>, String> { let sql: Cow<'_, str> = resolved_cleaned_sql(outcome)?; let relations: SessionSchema = SessionSchema { relations: outcome @@ -53,18 +54,29 @@ pub(crate) fn revised( }) .collect::>>(), }; - let analysis: NativeAnalysis = dialect - .analyse(&AnalysisInput { - sql: &sql, - schema: &relations, - references: &[], - extension_functions: declared, - }) - .map_err(|gap| gap.message)?; + let analysed: Result = dialect.analyse(&AnalysisInput { + sql: &sql, + schema: &relations, + references: &[], + extension_functions: declared, + }); + let Ok(analysis) = analysed else { + return Ok(None); + }; analysis .diagnostics .into_iter() .map(revised_binding) + .collect::, String>>() + .map(Some) +} + +/// The model's own findings, as type recovery matches them, kept as they are. +pub(crate) fn unrevised(model: &RecoveryModel) -> Vec { + model + .raw_bindings + .iter() + .map(|raw| (raw.code.clone(), raw.start, raw.end)) .collect() } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs index 64eb6e6a6..e95f61f52 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs @@ -7,6 +7,7 @@ use sqlbuild_sqltext::sql_scan::main::dialect_lexical_syntax::dialect_lexical_sy use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; use crate::analysis_route::_helpers::located_gap::{located, unreadable_reason}; +use crate::analysis_route::_helpers::marker_names::{distinct_calls, first_call_names}; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::{ @@ -34,15 +35,15 @@ pub fn fast_lineage( Ok(named) => named, Err(reason) => return FastLineageOutcome::Unparsed(reason), }; - let normalised: String = - match normalize_native_sql(&named, dialect.name(), dialect.stub_prefix()) { - Ok(normalised) => normalised, - Err(reason) => return FastLineageOutcome::Unparsed(reason), - }; + let normalised: Result = first_call_names(&named, &calls) + .and_then(|named| normalize_native_sql(&named, dialect.name(), dialect.stub_prefix())); + let normalised: String = match normalised { + Ok(normalised) => normalised, + Err(reason) => return FastLineageOutcome::Unparsed(reason), + }; let sql: String = placeholder_defaults(&normalised, placeholders); - let referenced: Vec<(String, LineageResourceType, String)> = calls + let referenced: Vec<(String, LineageResourceType, String)> = distinct_calls(&calls) .into_iter() - .enumerate() .filter_map(|(index, (function, name))| { let resource_type: LineageResourceType = match function.as_str() { "__ref" => LineageResourceType::Model, @@ -50,7 +51,11 @@ pub fn fast_lineage( "__seed" => LineageResourceType::Seed, _ => return None, }; - Some((format!("{MARKER_NAME_PREFIX}{index}"), resource_type, name)) + Some(( + format!("{MARKER_NAME_PREFIX}{index}"), + resource_type, + name.clone(), + )) }) .collect(); let references: Vec<(String, String, String)> = referenced diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs index 84efea9ac..67ae52f5c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs @@ -5,26 +5,30 @@ use std::sync::Arc; use rayon::ThreadPool; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; -use crate::analysis_route::_helpers::revalidation::{revised, session_outcome}; +use crate::analysis_route::_helpers::revalidation::{revised, session_outcome, unrevised}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::models::{FinishedSession, ModelOutcome, PivotOutcome}; +use crate::semantic_checks::models::RecoveryModel; use crate::semantic_checks::types::RevisedBinding; -/// Each named model's findings, at the session's positions, with the `poisoned` outputs untyped. +/// Each model's findings with the `poisoned` outputs untyped; an unreadable model keeps its own. pub fn native_revalidation( dialect: &dyn NativeDialect, (finished, outcomes): (&FinishedSession, &[(ModelOutcome, PivotOutcome)]), - models: &[String], + models: &[&RecoveryModel], poisoned: &[(String, String)], ) -> Result>, String> { let pool: Arc = finished.pool.clone()?; pool.install(|| { models .par_iter() - .map(|name| { - let outcome: &ModelOutcome = session_outcome(finished, outcomes, name)?; - revised(dialect, outcome, poisoned, &finished.declared_functions) - .map_err(|reason| format!("{name}: {reason}")) + .map(|model| { + let outcome: &ModelOutcome = session_outcome(finished, outcomes, &model.name)?; + match revised(dialect, outcome, poisoned, &finished.declared_functions) { + Ok(Some(rows)) => Ok(rows), + Ok(None) => Ok(unrevised(model)), + Err(reason) => Err(format!("{}: {reason}", model.name)), + } }) .collect() }) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index 0b05ee5ff..242ac2a38 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -8,5 +8,7 @@ mod test_native_dialect; mod test_native_gap; #[path = "tests/test_native_outcome.rs"] mod test_native_outcome; +#[path = "tests/test_native_revalidation.rs"] +mod test_native_revalidation; #[path = "tests/test_types.rs"] mod test_types; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs index f3a99b4bb..9dd60dfa2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs @@ -4,9 +4,12 @@ use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, StandInSchema}; use crate::analysis_route::types::NativeDialect; -use crate::assembly::analysis_session::models::{LineageRow, SessionSchema}; +use crate::assembly::analysis_session::models::{ + LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, SessionSchema, +}; use crate::assembly::analysis_session::types::Shapes; use crate::lineage::models::{LineageColumn, LineageSource}; +use crate::semantic_checks::types::RevisedBinding; /// `orders` with a non-null INTEGER id and a nullable VARCHAR status. pub(super) fn orders() -> StandInSchema { @@ -104,3 +107,40 @@ fn schema_column(&(name, data_type, nullability): &(&str, &str, Nullability)) -> nullability, } } + +/// A finished outcome of `sql` over `orders`, as the session bound it. +pub(super) fn outcome(sql: &str) -> ModelOutcome { + ModelOutcome { + analysis: ModelAnalysis { + analysis_succeeded: true, + columns: None, + lineage: LineageFacts::Native(Vec::new()), + has_star: false, + star_resolved: false, + binding_diagnostics: Vec::new(), + binding_validated: true, + }, + cleaned_sql: sql.to_owned(), + deferred_cleaning: None, + validated_schema: vec![( + "orders".to_owned(), + vec![ + ("id".to_owned(), "INTEGER".to_owned()), + ("status".to_owned(), "VARCHAR".to_owned()), + ], + )], + fused_binding_validated: true, + sql_macro_findings: Vec::new(), + } +} + +/// Each revised finding of every model as `(code, start, end)`, in order. +pub(super) fn finding_rows( + models: &[Vec], +) -> Vec<(&str, Option, Option)> { + let mut rows: Vec<(&str, Option, Option)> = Vec::new(); + for (code, start, end) in models.iter().flatten() { + rows.push((code.as_str(), *start, *end)); + } + rows +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index 20eaba37c..095d840e1 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -148,6 +148,15 @@ fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_l "built star=false", ], }, + NativeLineageTestCase { + description: "repeated calls of one marker read as one relation, so a union stays direct", + sql: r#"SELECT order_id FROM __ref("orders") UNION ALL SELECT order_id FROM __ref("orders")"#, + placeholders: &[], + expected_lines: &[ + "order_id direct high [model:orders.order_id]", + "built star=false", + ], + }, NativeLineageTestCase { description: "a star over a relation of unknown columns is a star without outputs", sql: r#"SELECT * FROM __ref("unknown_model")"#, diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs new file mode 100644 index 000000000..0e68c40a8 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs @@ -0,0 +1,84 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use crate::analysis_route::main::native_revalidation::native_revalidation; +use crate::analysis_route::tests::helpers::{finding_rows, outcome}; +use crate::analysis_route::tests::test_types::{PivotlessDialect, RevalidationTestCase}; +use crate::assembly::analysis_session::models::{ + FinishedSession, ModelOutcome, PivotOutcome, PivotTables, +}; +use crate::semantic_checks::models::{RawBinding, RecoveryModel}; +use crate::semantic_checks::types::RevisedBinding; +use crate::semantic_validation::main::new_analysis_pool::new_analysis_pool; + +#[test] +fn given_poisoned_outputs_when_revalidating_natively_then_findings_keep_session_positions() { + let test_cases = [ + RevalidationTestCase { + description: "a finding on a typed column stays at the session's span", + sql: "SELECT id FROM orders WHERE status", + poisoned: &[], + expected_rows: &[("B211", Some(28), Some(34))], + }, + RevalidationTestCase { + description: "a finding only a poisoned column causes drops out", + sql: "SELECT id FROM orders WHERE status", + poisoned: &[("orders", "status")], + expected_rows: &[], + }, + RevalidationTestCase { + description: "a model the parser cannot read again keeps its own findings", + sql: "SELECT id FROM orders PIVOT (sum(id) FOR status IN (1))", + poisoned: &[("orders", "status")], + expected_rows: &[("B002", Some(7), Some(9))], + }, + ]; + + for test_case in test_cases { + let finished: FinishedSession = FinishedSession { + tables: PivotTables::default(), + pool: new_analysis_pool().map(Arc::new), + models: HashMap::new(), + dynamic_proofs: HashMap::new(), + relations: None, + model_names: vec!["other".to_owned(), "downstream".to_owned()], + declared_functions: Vec::new(), + }; + let outcomes: Vec<(ModelOutcome, PivotOutcome)> = vec![ + (outcome("SELECT 1"), PivotOutcome::Absent), + (outcome(test_case.sql), PivotOutcome::Absent), + ]; + let model: RecoveryModel = RecoveryModel { + name: "downstream".to_owned(), + query_sql: test_case.sql.to_owned(), + inferred_columns: None, + references: vec!["orders".to_owned()], + lineage: Vec::new(), + bindings: Vec::new(), + raw_bindings: vec![RawBinding { + id: 0, + code: "B002".to_owned(), + message: "Unknown column".to_owned(), + start: Some(7), + end: Some(9), + }], + }; + let poisoned: Vec<(String, String)> = test_case + .poisoned + .iter() + .map(|(relation, column)| ((*relation).to_owned(), (*column).to_owned())) + .collect(); + let revised: Result>, String> = native_revalidation( + &PivotlessDialect, + (&finished, &outcomes), + &[&model], + &poisoned, + ); + assert_eq!( + revised.as_deref().map(finding_rows), + Ok(test_case.expected_rows.to_vec()), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index a9b789e10..3bd8d1685 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -157,6 +157,13 @@ pub(super) struct MarkerLineageTestCase { pub(super) expected_lines: &'static [&'static str], } +pub(super) struct RevalidationTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + pub(super) poisoned: &'static [(&'static str, &'static str)], + pub(super) expected_rows: &'static [(&'static str, Option, Option)], +} + pub(super) struct ReadWholeTestCase { pub(super) description: &'static str, pub(super) sql: &'static str, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs index 7317f9b6b..e65856696 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs @@ -107,9 +107,12 @@ pub(crate) fn recovered_types<'py>( let poisoned: Vec<(String, String)> = plan.poisoned_outputs(); let models_to_revalidate: Vec = plan.revalidated_models(); let revised: Vec> = match analysis_route(request.dialect.as_deref()) { - AnalysisRoute::Native(dialect) => { - natively_revalidated(dialect, project, (&models_to_revalidate, &poisoned), shared)? - } + AnalysisRoute::Native(dialect) => natively_revalidated( + py, + dialect, + (&request, &models_to_revalidate, &poisoned), + shared, + )?, AnalysisRoute::Legacy | AnalysisRoute::Unavailable => { revalidated(classes, project, &models_to_revalidate, &poisoned)? } @@ -256,32 +259,35 @@ fn recovery_model( /// Each planned model revalidated by the dialect's native analysis, never the Polyglot catalog. fn natively_revalidated( + py: Python<'_>, dialect: &'static dyn NativeDialect, - project: &Bound<'_, PyAny>, - (models, poisoned): (&[usize], &[(String, String)]), + (request, models, poisoned): (&TypeRecoveryRequest, &[usize], &[(String, String)]), shared: &StageShared<'_>, ) -> PyResult>> { if models.is_empty() { return Ok(Vec::new()); } - let py: Python<'_> = project.py(); let session: &NativeModelAnalysisSession = shared .session .ok_or_else(|| internal_error("revalidating a native route without its session"))?; let finished = session .finished_session() .ok_or_else(|| internal_error("revalidating before the session finished"))?; - let project_models: Bound<'_, PyAny> = native_field(project, "models")?; - let mut names: Vec = Vec::with_capacity(models.len()); + let mut planned: Vec<&RecoveryModel> = Vec::with_capacity(models.len()); for index in models { - names.push(native_field(&project_models.get_item(*index)?, "name")?.extract()?); + planned.push( + request + .models + .get(*index) + .ok_or_else(|| internal_error("type recovery planned a model it does not hold"))?, + ); } - count_stage(py, SCHEMA_REVALIDATIONS, names.len() as u64)?; + count_stage(py, SCHEMA_REVALIDATIONS, planned.len() as u64)?; py.compiler_detach(|| { Ok(native_revalidation( dialect, (finished, session.outcomes()), - &names, + &planned, poisoned, )) }) From b5a20e70329f6a89f493f1c185c9cac7c0ed8b62 Mon Sep 17 00:00:00 2001 From: kvlonge <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:18:37 +0000 Subject: [PATCH 035/160] feat(analysis): read SQL Server models with SQLQuack's T-SQL parser The tsql analysis dialect now routes natively. TsqlDialect implements NativeDialect: parse reads with sqlquack-syntax-sqlserver, an unknown one-part function staying a syntax error as SQL Server's 195 is; profile is sqlquack-semantics' SQL Server profile; and normalize_type gives Polyglot's T-SQL canonical names, or None for a type neither SQL Server's built-in types nor SQLBuild's logical types know. SQLQuack is pinned to a commit carrying the SQL Server semantics profile. --- Cargo.lock | 24 +++- Cargo.toml | 7 +- crates/sqlbuild-analysis/Cargo.toml | 1 + .../src/analysis_route/_helpers/mod.rs | 1 + .../analysis_route/_helpers/tsql_dialect.rs | 128 +++++++++++++++++ .../src/analysis_route/constants.rs | 59 +++++++- .../src/analysis_route/models.rs | 4 + .../analysis_route/tests/test_native_gap.rs | 135 +++++++++++++++++- .../lineage/tests/test_build_fast_lineage.rs | 4 +- 9 files changed, 348 insertions(+), 15 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/tsql_dialect.rs diff --git a/Cargo.lock b/Cargo.lock index 7d467327d..ecf254451 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4145,6 +4145,7 @@ dependencies = [ "sqlquack-semantics", "sqlquack-syntax", "sqlquack-syntax-postgres", + "sqlquack-syntax-sqlserver", ] [[package]] @@ -4500,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" dependencies = [ "serde", "serde_json", @@ -4510,19 +4511,21 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" dependencies = [ + "regex", "serde", "serde_json", "sqlquack-core", "sqlquack-snowflake", "sqlquack-syntax", + "stacker", ] [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" dependencies = [ "serde", "sqlquack-core", @@ -4531,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" dependencies = [ "serde", "serde_json", @@ -4544,7 +4547,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" dependencies = [ "proc-macro2", "quote", @@ -4554,7 +4557,16 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +dependencies = [ + "sqlquack-syntax", + "stacker", +] + +[[package]] +name = "sqlquack-syntax-sqlserver" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" dependencies = [ "sqlquack-syntax", "stacker", diff --git a/Cargo.toml b/Cargo.toml index c4475e838..fa6717db0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,10 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } +sqlquack-syntax-sqlserver = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 95c643ebd..08805e34f 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -29,6 +29,7 @@ sqlclean.workspace = true sqlquack-semantics.workspace = true sqlquack-syntax.workspace = true sqlquack-syntax-postgres.workspace = true +sqlquack-syntax-sqlserver.workspace = true [lints] workspace = true diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 279c10ae9..a5cfcaa2c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -3,4 +3,5 @@ pub(crate) mod located_gap; pub(crate) mod marker_names; pub(crate) mod native_analysis; pub(crate) mod revalidation; +pub(crate) mod tsql_dialect; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/tsql_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/tsql_dialect.rs new file mode 100644 index 000000000..e735b0678 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/tsql_dialect.rs @@ -0,0 +1,128 @@ +//! SQL Server's native module: SQLQuack's T-SQL parser and its sqlquack-semantics profile. + +use sqlquack_semantics::dialects::constants::SQL_SERVER; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax::{DataType, Expression, Gap, Parsed}; +use sqlquack_syntax_sqlserver::parse_strict; + +use crate::analysis_route::constants::{ + LOGICAL_TYPES, TSQL_BUILT_IN_TYPES, TSQL_DIALECT, TSQL_STUB_PREFIX, TSQL_TYPE_ALIASES, + TSQL_TYPE_SYNONYMS, +}; +use crate::analysis_route::models::{ParserGap, TsqlDialect}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeDialect, TypeFamily}; + +impl NativeDialect for TsqlDialect { + fn name(&self) -> &'static str { + TSQL_DIALECT + } + + fn stub_prefix(&self) -> &'static str { + TSQL_STUB_PREFIX + } + + /// Every statement with its gaps; an unknown one-part function (195) is a syntax error, as the engine raises it. + fn parse(&self, sql: &str) -> Result { + let parsed = parse_strict(sql).map_err(|failure| { + let error = failure.parse_error(); + ParserGap { + construct: error.message.clone(), + message: error.message.clone(), + offset: error.position, + syntax: error.syntax, + } + })?; + Ok(Parsed { + statements: parsed.statements, + gaps: parsed + .gaps + .into_iter() + .map(|gap| Gap { + construct: gap.construct, + start: gap.start, + end: gap.end, + }) + .collect(), + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &SQL_SERVER + } + + /// The type spelt with Polyglot's T-SQL canonical names; `None` for a type neither SQL Server nor SQLBuild knows. + fn normalize_type(&self, spelling: &str) -> Option { + let written: String = spelling + .split_whitespace() + .collect::>() + .join(" ") + .to_ascii_uppercase(); + let (base, arguments): (&str, String) = match written.find('(') { + Some(open) => (written[..open].trim(), written[open..].replace(' ', "")), + None => (written.as_str(), String::new()), + }; + if let Some(direct) = canonical_type(base, &arguments) { + return Some(direct); + } + if !LOGICAL_TYPES.contains(&base) && !built_in_type(self, &written) { + return None; + } + let aliased: String = match type_alias(base) { + Some(alias) if alias.contains('(') => alias.to_owned(), + Some(alias) => format!("{alias}{arguments}"), + None => written.clone(), + }; + Some(normalize_text_type(&aliased, Some(TypeDialect::Tsql))) + } +} + +/// Whether SQL Server reads `spelling` as one of its built-in types. +fn built_in_type(dialect: &TsqlDialect, spelling: &str) -> bool { + let Ok(parsed) = dialect.parse(&format!("SELECT CAST(NULL AS {spelling})")) else { + return false; + }; + let target: Option<&DataType> = match (parsed.gaps.is_empty(), parsed.statements.as_slice()) { + (true, [Expression::Select(select)]) => match select.expressions.as_slice() { + [Expression::Cast(cast)] => Some(&cast.to), + _ => None, + }, + _ => None, + }; + match target { + Some(DataType::Custom { name }) => { + let upper: String = name.to_ascii_uppercase(); + let base: &str = upper.split('(').next().unwrap_or_default().trim(); + TSQL_BUILT_IN_TYPES.contains(&base) || TSQL_TYPE_SYNONYMS.contains(&base) + } + Some(_) => true, + None => false, + } +} + +/// The types whose canonical name or family the text fallback cannot give, built directly. +fn canonical_type(base: &str, arguments: &str) -> Option { + let (name, family): (String, TypeFamily) = match base { + "BOOLEAN" => ("BIT".to_owned(), TypeFamily::Boolean), + "TIMESTAMP" => ("DATETIME2".to_owned(), TypeFamily::Timestamp), + "TEXT" | "NTEXT" | "STRING" => ("VARCHAR(MAX)".to_owned(), TypeFamily::String), + "FLOAT" if !arguments.is_empty() => (format!("FLOAT{arguments}"), TypeFamily::Float), + _ => return None, + }; + Some(NormalizedType { + normalized_name: name, + family, + precision: None, + scale: None, + length: None, + }) +} + +/// The canonical name Polyglot gives an alias, before the text fallback reads it. +fn type_alias(base: &str) -> Option<&'static str> { + TSQL_TYPE_ALIASES + .iter() + .find(|(alias, _)| *alias == base) + .map(|(_, canonical)| *canonical) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..6fa81eda6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,9 +1,10 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. +use crate::analysis_route::models::TsqlDialect; use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. -pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[(TSQL_DIALECT, &TsqlDialect)]; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. @@ -26,3 +27,59 @@ pub(crate) const ERROR_SEVERITY: &str = "error"; pub(crate) const MACRO_BODY_PREFIX: &str = "SELECT "; /// The gap of a macro body that is not one expression. pub(crate) const NOT_ONE_EXPRESSION: &str = "not one expression"; +/// SQL Server's analysis dialect name. +pub(crate) const TSQL_DIALECT: &str = "tsql"; +/// The stub prefix SQL Server reads as a schema: `__sqlbuild.udf_x(...)`, `__sqlbuild.cursor_start()`. +pub(crate) const TSQL_STUB_PREFIX: &str = "__sqlbuild."; +/// SQL Server 2022's built-in type names (`sys.types` where `is_user_defined = 0`, 16.0.4305.5). +pub(crate) const TSQL_BUILT_IN_TYPES: [&str; 34] = [ + "BIGINT", + "BINARY", + "BIT", + "CHAR", + "DATE", + "DATETIME", + "DATETIME2", + "DATETIMEOFFSET", + "DECIMAL", + "FLOAT", + "GEOGRAPHY", + "GEOMETRY", + "HIERARCHYID", + "IMAGE", + "INT", + "MONEY", + "NCHAR", + "NTEXT", + "NUMERIC", + "NVARCHAR", + "REAL", + "SMALLDATETIME", + "SMALLINT", + "SMALLMONEY", + "SQL_VARIANT", + "SYSNAME", + "TEXT", + "TIME", + "TIMESTAMP", + "TINYINT", + "UNIQUEIDENTIFIER", + "VARBINARY", + "VARCHAR", + "XML", +]; +/// Built-in type synonyms `sys.types` does not list, which SQL Server 16.0.4305.5 accepts. +pub(crate) const TSQL_TYPE_SYNONYMS: [&str; 1] = ["ROWVERSION"]; +/// SQLBuild's logical types SQL Server's parser refuses, which its adapter writes as built-in ones. +pub(crate) const LOGICAL_TYPES: [&str; 4] = ["BOOLEAN", "DOUBLE", "JSON", "STRING"]; +/// Polyglot's T-SQL canonical name of each alias the text fallback keeps as written. +pub(crate) const TSQL_TYPE_ALIASES: [(&str, &str); 8] = [ + ("INT", "INTEGER"), + ("NUMERIC", "DECIMAL"), + ("DOUBLE", "FLOAT"), + ("DOUBLE PRECISION", "FLOAT"), + ("JSON", "NVARCHAR(MAX)"), + ("CHARACTER VARYING", "VARCHAR"), + ("NATIONAL CHARACTER VARYING", "VARCHAR"), + ("NATIONAL CHAR", "NCHAR"), +]; diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 7330e008d..3e2852707 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -122,3 +122,7 @@ impl LineageSchema for StandInSchema { .then_some(self.columns.as_slice()) } } + +/// SQL Server, whose SQL SQLQuack's T-SQL parser reads. +#[derive(Clone, Copy, Debug)] +pub struct TsqlDialect; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 5c395c6d1..ae2a699e5 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -1,14 +1,16 @@ use crate::analysis_route::main::analysis_route::analysis_route; use crate::analysis_route::main::native_gap::native_gap; use crate::analysis_route::main::skipped_analysis::skipped_analysis; -use crate::analysis_route::models::{AnalysisRoute, LocatedGap}; +use crate::analysis_route::models::{AnalysisRoute, LocatedGap, TsqlDialect}; +use crate::analysis_route::types::NativeDialect; use crate::analysis_route::tests::test_types::{ NativeGapTestCase, PivotlessDialect, RouteTestCase, SkippedAnalysisTestCase, }; use crate::assembly::project::models::SyntaxCheck; +use crate::type_system::models::TypeFamily; #[test] -fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { +fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_legacy_path() { let test_cases = [ RouteTestCase { description: "postgres", @@ -48,7 +50,7 @@ fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { RouteTestCase { description: "tsql", dialect: Some("tsql"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "no analysis dialect", @@ -177,3 +179,130 @@ fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_and_the_ ); } } + +#[test] +fn given_tsql_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole_has_no_gap() { + let test_cases = [ + NativeGapTestCase { + description: "a query SQL Server reads whole, placeholder filled", + sql: "SELECT TOP (5) [id] AS a FROM dbo.t WHERE region = @@@region", + placeholders: &[("region", "N'eu'")], + expected_gap: None, + }, + NativeGapTestCase { + description: "a UDF call's stub reads as a schema-qualified call", + sql: "SELECT __udf(\"scaled_amount\")(amount, 2) AS scaled FROM t", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "cursor bounds read as schema-qualified stub calls", + sql: "SELECT * FROM t WHERE updated_at >= __cursor_start() AND updated_at < __cursor_end( )", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "an unknown one-part function stops the parser at its call, where SQL Server raises 195", + sql: "SELECT id,\n LENGTH(name) AS n FROM t", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "not a recognized built-in function name".to_owned(), + message: "not a recognized built-in function name".to_owned(), + line: 2, + column: 9, + syntax: true, + }), + }, + NativeGapTestCase { + description: "LIMIT reads as a table alias, so SQL Server stops at its count", + sql: "SELECT id FROM t LIMIT 5", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "Incorrect syntax near '5'.".to_owned(), + message: "Incorrect syntax near '5'.".to_owned(), + line: 1, + column: 24, + syntax: true, + }), + }, + NativeGapTestCase { + description: "a second statement is not read whole", + sql: "SELECT 1;\nSELECT 2", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "more than one statement".to_owned(), + message: "more than one statement".to_owned(), + line: 1, + column: 1, + syntax: false, + }), + }, + ]; + for test_case in test_cases { + let check = SyntaxCheck { + sql: test_case.sql.to_owned(), + placeholders: test_case + .placeholders + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) + .collect(), + }; + assert_eq!( + native_gap(&TsqlDialect, &check), + Ok(test_case.expected_gap), + "{}", + test_case.description + ); + } +} + +#[test] +fn given_tsql_parser_errors_when_parsing_then_syntax_errors_are_told_from_gaps() { + let refused = TsqlDialect.parse("SELECT LENGTH(name) FROM t"); + assert_eq!( + refused.map_err(|gap| (gap.syntax, gap.offset)), + Err((true, 13)) + ); + let parsed = TsqlDialect + .parse("SELECT 1; SELECT 2") + .expect("two statements parse"); + assert_eq!((parsed.statements.len(), parsed.gaps.len()), (2, 0)); +} + +/// Polyglot's T-SQL names; `None` where neither SQL Server nor SQLBuild's logical types know the type. +#[test] +fn given_tsql_type_spellings_when_normalizing_then_they_take_polyglots_canonical_names() { + let test_cases = [ + ("int", Some(("INTEGER", TypeFamily::Integer))), + ( + "numeric(12, 2)", + Some(("DECIMAL(12,2)", TypeFamily::Decimal)), + ), + ("dec", Some(("DEC", TypeFamily::Other))), + ("FLOAT(24)", Some(("FLOAT(24)", TypeFamily::Float))), + ("DOUBLE PRECISION", Some(("FLOAT", TypeFamily::Float))), + ("DOUBLE", Some(("FLOAT", TypeFamily::Float))), + ("BOOLEAN", Some(("BIT", TypeFamily::Boolean))), + ("TIMESTAMP", Some(("DATETIME2", TypeFamily::Timestamp))), + ("NTEXT", Some(("VARCHAR(MAX)", TypeFamily::String))), + ( + "national character varying(10)", + Some(("VARCHAR(10)", TypeFamily::String)), + ), + ("nvarchar(max)", Some(("NVARCHAR(MAX)", TypeFamily::Other))), + ("datetime2(3)", Some(("DATETIME2(3)", TypeFamily::Other))), + ("rowversion", Some(("ROWVERSION", TypeFamily::Other))), + ("dbo.money_code", None), + ("INT64", None), + ]; + for (spelling, expected) in test_cases { + let normalized = TsqlDialect.normalize_type(spelling); + assert_eq!( + normalized + .as_ref() + .map(|normalized| (normalized.normalized_name.as_str(), normalized.family)), + expected, + "{spelling}" + ); + } +} diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index 6d225a9b5..95366ccc3 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -196,7 +196,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( expected_detail: Some("Expected 1 statement, found 2"), }, ParsedLineageTestCase { - description: "truncated T-SQL reports its parse error", + description: "truncated T-SQL reports its parse error where SQL Server's 156 does, at IF", sql: "SELECT IF(region > 1, re", dialect: Some("tsql"), inferred_columns: &[], @@ -204,7 +204,7 @@ fn given_models_without_compact_facts_when_building_then_matches_python_lineage( expected_lines: &[], expected_has_star: false, expected_detail: Some( - "Parse error at line 1, column 25: Expected ')' to close the IF condition", + "the tsql parser cannot read Incorrect syntax near 'IF'. at line 1, column 8", ), }, ParsedLineageTestCase { From a1c4a5db4ab75babe73e6a8cb7b9734b1c256f57 Mon Sep 17 00:00:00 2001 From: kvlonge <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:22:14 +0000 Subject: [PATCH 036/160] test(analysis): state the T-SQL parse and type cases as named test cases TsqlDialect now calls normalize_text_type, so the rule exception that waited for a native dialect module to call it is removed. --- .../analysis_route/tests/test_native_gap.rs | 145 +++++++++++++----- .../src/analysis_route/tests/test_types.rs | 15 +- fensu.toml | 5 - 3 files changed, 121 insertions(+), 44 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index ae2a699e5..458e945c3 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -2,10 +2,11 @@ use crate::analysis_route::main::analysis_route::analysis_route; use crate::analysis_route::main::native_gap::native_gap; use crate::analysis_route::main::skipped_analysis::skipped_analysis; use crate::analysis_route::models::{AnalysisRoute, LocatedGap, TsqlDialect}; -use crate::analysis_route::types::NativeDialect; use crate::analysis_route::tests::test_types::{ - NativeGapTestCase, PivotlessDialect, RouteTestCase, SkippedAnalysisTestCase, + NativeGapTestCase, PivotlessDialect, RouteTestCase, SkippedAnalysisTestCase, TsqlParseTestCase, + TsqlTypeTestCase, }; +use crate::analysis_route::types::NativeDialect; use crate::assembly::project::models::SyntaxCheck; use crate::type_system::models::TypeFamily; @@ -258,51 +259,119 @@ fn given_tsql_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole_has #[test] fn given_tsql_parser_errors_when_parsing_then_syntax_errors_are_told_from_gaps() { - let refused = TsqlDialect.parse("SELECT LENGTH(name) FROM t"); - assert_eq!( - refused.map_err(|gap| (gap.syntax, gap.offset)), - Err((true, 13)) - ); - let parsed = TsqlDialect - .parse("SELECT 1; SELECT 2") - .expect("two statements parse"); - assert_eq!((parsed.statements.len(), parsed.gaps.len()), (2, 0)); + let test_cases = [ + TsqlParseTestCase { + description: "an unknown one-part function is a syntax error at its name's end, as SQL Server's 195", + sql: "SELECT LENGTH(name) FROM t", + expected_parse: Err((true, 13)), + }, + TsqlParseTestCase { + description: "two statements parse whole with no gap", + sql: "SELECT 1; SELECT 2", + expected_parse: Ok((2, 0)), + }, + ]; + for test_case in test_cases { + assert_eq!( + TsqlDialect + .parse(test_case.sql) + .map(|parsed| (parsed.statements.len(), parsed.gaps.len())) + .map_err(|gap| (gap.syntax, gap.offset)), + test_case.expected_parse, + "{}", + test_case.description + ); + } } -/// Polyglot's T-SQL names; `None` where neither SQL Server nor SQLBuild's logical types know the type. #[test] fn given_tsql_type_spellings_when_normalizing_then_they_take_polyglots_canonical_names() { let test_cases = [ - ("int", Some(("INTEGER", TypeFamily::Integer))), - ( - "numeric(12, 2)", - Some(("DECIMAL(12,2)", TypeFamily::Decimal)), - ), - ("dec", Some(("DEC", TypeFamily::Other))), - ("FLOAT(24)", Some(("FLOAT(24)", TypeFamily::Float))), - ("DOUBLE PRECISION", Some(("FLOAT", TypeFamily::Float))), - ("DOUBLE", Some(("FLOAT", TypeFamily::Float))), - ("BOOLEAN", Some(("BIT", TypeFamily::Boolean))), - ("TIMESTAMP", Some(("DATETIME2", TypeFamily::Timestamp))), - ("NTEXT", Some(("VARCHAR(MAX)", TypeFamily::String))), - ( - "national character varying(10)", - Some(("VARCHAR(10)", TypeFamily::String)), - ), - ("nvarchar(max)", Some(("NVARCHAR(MAX)", TypeFamily::Other))), - ("datetime2(3)", Some(("DATETIME2(3)", TypeFamily::Other))), - ("rowversion", Some(("ROWVERSION", TypeFamily::Other))), - ("dbo.money_code", None), - ("INT64", None), + TsqlTypeTestCase { + description: "a built-in integer", + spelling: "int", + expected_type: Some(("INTEGER", TypeFamily::Integer)), + }, + TsqlTypeTestCase { + description: "numeric with precision and scale", + spelling: "numeric(12, 2)", + expected_type: Some(("DECIMAL(12,2)", TypeFamily::Decimal)), + }, + TsqlTypeTestCase { + description: "bare dec", + spelling: "dec", + expected_type: Some(("DEC", TypeFamily::Other)), + }, + TsqlTypeTestCase { + description: "float with a mantissa width", + spelling: "FLOAT(24)", + expected_type: Some(("FLOAT(24)", TypeFamily::Float)), + }, + TsqlTypeTestCase { + description: "double precision", + spelling: "DOUBLE PRECISION", + expected_type: Some(("FLOAT", TypeFamily::Float)), + }, + TsqlTypeTestCase { + description: "SQLBuild's logical double", + spelling: "DOUBLE", + expected_type: Some(("FLOAT", TypeFamily::Float)), + }, + TsqlTypeTestCase { + description: "SQLBuild's logical boolean", + spelling: "BOOLEAN", + expected_type: Some(("BIT", TypeFamily::Boolean)), + }, + TsqlTypeTestCase { + description: "SQLBuild's logical timestamp", + spelling: "TIMESTAMP", + expected_type: Some(("DATETIME2", TypeFamily::Timestamp)), + }, + TsqlTypeTestCase { + description: "ntext", + spelling: "NTEXT", + expected_type: Some(("VARCHAR(MAX)", TypeFamily::String)), + }, + TsqlTypeTestCase { + description: "national character varying", + spelling: "national character varying(10)", + expected_type: Some(("VARCHAR(10)", TypeFamily::String)), + }, + TsqlTypeTestCase { + description: "nvarchar(max)", + spelling: "nvarchar(max)", + expected_type: Some(("NVARCHAR(MAX)", TypeFamily::Other)), + }, + TsqlTypeTestCase { + description: "datetime2 with a precision", + spelling: "datetime2(3)", + expected_type: Some(("DATETIME2(3)", TypeFamily::Other)), + }, + TsqlTypeTestCase { + description: "rowversion", + spelling: "rowversion", + expected_type: Some(("ROWVERSION", TypeFamily::Other)), + }, + TsqlTypeTestCase { + description: "a user-defined alias type is unknown", + spelling: "dbo.money_code", + expected_type: None, + }, + TsqlTypeTestCase { + description: "another dialect's type is unknown", + spelling: "INT64", + expected_type: None, + }, ]; - for (spelling, expected) in test_cases { - let normalized = TsqlDialect.normalize_type(spelling); + for test_case in test_cases { assert_eq!( - normalized + TsqlDialect + .normalize_type(test_case.spelling) .as_ref() .map(|normalized| (normalized.normalized_name.as_str(), normalized.family)), - expected, - "{spelling}" + test_case.expected_type, + "{}", + test_case.description ); } } diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 3bd8d1685..552ef1444 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -7,7 +7,7 @@ use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; use crate::analysis_route::models::{LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; -use crate::type_system::models::NormalizedType; +use crate::type_system::models::{NormalizedType, TypeFamily}; pub(super) struct NativeGapTestCase { pub(super) description: &'static str, @@ -248,3 +248,16 @@ pub(super) struct NativeLineageTestCase { pub(super) placeholders: &'static [(&'static str, &'static str)], pub(super) expected_lines: &'static [&'static str], } + +pub(super) struct TsqlParseTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + /// `Ok((statements, gaps))` when the parser reads the SQL, `Err((syntax, offset))` when it refuses it. + pub(super) expected_parse: Result<(usize, usize), (bool, usize)>, +} + +pub(super) struct TsqlTypeTestCase { + pub(super) description: &'static str, + pub(super) spelling: &'static str, + pub(super) expected_type: Option<(&'static str, TypeFamily)>, +} diff --git a/fensu.toml b/fensu.toml index 3fee255b7..c3109bfec 100644 --- a/fensu.toml +++ b/fensu.toml @@ -2933,11 +2933,6 @@ rule = "FFS201" path = "src/sqlbuild/orchestration_declarations/models/sensor_context.py" reason = "Only SensorContext lives here: invocation-local ctx.cursor assignment proposes progress, committed solely by native fenced completion. The package regression guards this declaration-only scope; class-symbol exceptions are unsupported." -[[targets.rust.rule_exceptions]] -rule = "FPRSL105" -path = "crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs" -reason = "Native dialect modules in analysis_route type their spellings through this entry; each dialect's module lands in its own change." - [[targets.rust.rule_exceptions]] rule = "FPRSL105" path = "crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs" From 1ebf4ca663ebfb6c4af65d6e3781d0874820a8c8 Mon Sep 17 00:00:00 2001 From: kvlonge <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:53:34 +0000 Subject: [PATCH 037/160] chore(deps): pin SQLQuack to the main commit carrying the SQL Server semantics profile --- Cargo.lock | 14 +++++++------- Cargo.toml | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecf254451..c3907a3b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4501,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "serde", "serde_json", @@ -4511,7 +4511,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "regex", "serde", @@ -4525,7 +4525,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "serde", "sqlquack-core", @@ -4534,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "serde", "serde_json", @@ -4547,7 +4547,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "proc-macro2", "quote", @@ -4557,7 +4557,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "sqlquack-syntax", "stacker", @@ -4566,7 +4566,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-sqlserver" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=5bc585414accaa0470fad79927ab7ca126eaf645#5bc585414accaa0470fad79927ab7ca126eaf645" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=fa55d9a18e32f8d6992b21aa0e750d89db2619cc#fa55d9a18e32f8d6992b21aa0e750d89db2619cc" dependencies = [ "sqlquack-syntax", "stacker", diff --git a/Cargo.toml b/Cargo.toml index fa6717db0..0e49a0eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,11 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "fa55d9a18e32f8d6992b21aa0e750d89db2619cc" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } -sqlquack-syntax-sqlserver = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "5bc585414accaa0470fad79927ab7ca126eaf645" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "fa55d9a18e32f8d6992b21aa0e750d89db2619cc" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "fa55d9a18e32f8d6992b21aa0e750d89db2619cc" } +sqlquack-syntax-sqlserver = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "fa55d9a18e32f8d6992b21aa0e750d89db2619cc" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From a8d1dc7213441f8bbf777d5717908fa05c4b8249 Mon Sep 17 00:00:00 2001 From: kvlonge <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:55:20 +0000 Subject: [PATCH 038/160] test(analysis): keep T-SQL cursor stubs after bracket names holding quotes and comment openers Gap-test rows put a quote, an escaped bracket and both comment openers in a bracket name before a cursor stub; a SQL Server project requiring SQL analysis compiles a microbatch model whose stub follows [x--y]. --- .../analysis_route/tests/test_native_gap.rs | 24 +++++++++++++++++++ .../cli/commands/main/compile/helpers.py | 21 ++++++++++++++++ .../main/compile/test_require_sql_analysis.py | 15 ++++++++++++ 3 files changed, 60 insertions(+) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 458e945c3..55160d385 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -202,6 +202,30 @@ fn given_tsql_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole_has placeholders: &[], expected_gap: None, }, + NativeGapTestCase { + description: "a quote inside a bracket name does not hide a later cursor stub", + sql: "SELECT [customer's name] AS n FROM t WHERE updated_at >= __cursor_start()", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "an escaped bracket keeps the name open past its quote, so the stub stays code", + sql: "SELECT [it]]'s] AS n FROM t WHERE updated_at >= __cursor_start()", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "a line-comment opener inside a bracket name does not hide a later cursor stub", + sql: "SELECT [x--y] AS n FROM t WHERE updated_at >= __cursor_start()", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "a block-comment opener inside a bracket name does not hide a later cursor stub", + sql: "SELECT [x /* y] AS n FROM t WHERE updated_at >= __cursor_start()", + placeholders: &[], + expected_gap: None, + }, NativeGapTestCase { description: "an unknown one-part function stops the parser at its call, where SQL Server raises 195", sql: "SELECT id,\n LENGTH(name) AS n FROM t", diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py index c14d1cc03..d0c9eb027 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py @@ -2847,6 +2847,27 @@ def resource_sql_project_files(files: tuple[tuple[str, str], ...]) -> dict[str, 'name = "orders"\nadapter = "duckdb"\n\n[settings]\nrequire_sql_analysis = true\n' ) OPTIONAL_SQL_ANALYSIS_PROJECT: str = 'name = "orders"\nadapter = "duckdb"\n' +SQLSERVER_REQUIRE_SQL_ANALYSIS_PROJECT: str = ( + 'name = "orders"\nadapter = "sqlserver"\ndefault_target = "dev"\n\n' + '[targets.dev]\ndatabase = "warehouse"\nschema = "analytics"\n\n' + "[settings]\nrequire_sql_analysis = true\n" +) +SQLSERVER_EVENTS_SOURCE: str = ( + "sources:\n - name: raw_events\n description: Event feed.\n columns:\n" + " - name: id\n type: INTEGER\n - name: amount\n type: INTEGER\n" + " - name: created_at\n type: TIMESTAMP\n" +) +SQLSERVER_BRACKETED_CURSOR_MODEL: str = ( + 'MODEL (\n description "Events whose bracket name holds a comment opener",\n' + " materialized incremental,\n incremental_strategy delete_insert,\n unique_key [id],\n" + " cursor created_at,\n cursor_type timestamp,\n cursor_grain day,\n" + " incremental_mode microbatch,\n microbatch_strategy rolling_window,\n lookback 1d,\n" + " batch_size 1d,\n cursor_inputs (\n raw_events created_at,\n ),\n);\n\n" + "SELECT id, amount AS [x--y], " + "CASE WHEN created_at >= __cursor_start() THEN 1 END AS in_window, created_at\n" + 'FROM __source("raw_events")\n' + "WHERE created_at >= __cursor_start() AND created_at < __cursor_end()\n" +) PARSEABLE_OPT_OUT_MODEL: str = ( 'MODEL (\n description "Order flags",\n sql_analysis false\n);\n' "SELECT 1 = 'pending' AS is_pending, 2 = 'shipped' AS is_shipped\n" diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_require_sql_analysis.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_require_sql_analysis.py index c34877a32..5df14fef4 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_require_sql_analysis.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_require_sql_analysis.py @@ -14,6 +14,9 @@ PARSEABLE_OPT_OUT_MODEL, PATH_DEFAULT_MODEL, REQUIRE_SQL_ANALYSIS_PROJECT, + SQLSERVER_BRACKETED_CURSOR_MODEL, + SQLSERVER_EVENTS_SOURCE, + SQLSERVER_REQUIRE_SQL_ANALYSIS_PROJECT, UNPARSEABLE_OPT_OUT_MODEL, require_sql_analysis_output, ) @@ -84,6 +87,18 @@ expected_diagnostics=(), expected_text_fragments=(), ), + RequireSqlAnalysisCase( + description="a T-SQL cursor stub after a bracket name holding -- is analysed, not skipped", + repo_files=( + ("sqlbuild_project.toml", SQLSERVER_REQUIRE_SQL_ANALYSIS_PROJECT), + ("sources/raw.yml", SQLSERVER_EVENTS_SOURCE), + ("models/bracketed_events.sql", SQLSERVER_BRACKETED_CURSOR_MODEL), + ), + command=_COMPILE, + expected_returncode=0, + expected_diagnostics=(), + expected_text_fragments=(), + ), RequireSqlAnalysisCase( description="a single-run --no-sql-analysis still works", repo_files=( From 2eb605ea9a002f7b32c15f456a8fed640be16810 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:54:13 +0000 Subject: [PATCH 039/160] Route ClickHouse analysis to SQLQuack's ClickHouse parser and profile - ClickHouse implements the native dialect: SQLQuack's ClickHouse parser with its named gaps, the ClickHouse semantics profile, types normalized as Polyglot normalizes them, and contract types compared after both sides are respelt as ClickHouse prints them, so BIGINT is a non-null Int64. - A declared type ClickHouse refuses compares nothing; an inferred type it refuses or does not know still meets a non-null declaration. - Every SQLQuack crate is pinned to SQLQuack main edfa30b2, which carries the ClickHouse parser, and ClickHouse is listed as a native dialect. - The ClickHouse gap-gate tests cover an apostrophe in a `#` and a `#!` comment, which ClickHouse reads as comments. --- Cargo.lock | 24 +- Cargo.toml | 7 +- crates/sqlbuild-analysis/Cargo.toml | 1 + .../_helpers/clickhouse_dialect.rs | 59 +++ .../_helpers/clickhouse_types.rs | 179 +++++++++- .../src/analysis_route/_helpers/mod.rs | 1 + .../src/analysis_route/constants.rs | 34 +- .../main/clickhouse_type_match.rs | 22 -- .../src/analysis_route/main/mod.rs | 1 - .../src/analysis_route/models.rs | 4 + .../src/analysis_route/tests.rs | 2 + .../tests/test_clickhouse_dialect.rs | 337 ++++++++++++++++++ .../tests/test_clickhouse_type_match.rs | 2 +- .../analysis_route/tests/test_native_gap.rs | 4 +- .../src/analysis_route/tests/test_types.rs | 6 + fensu.toml | 10 - 16 files changed, 644 insertions(+), 49 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_dialect.rs delete mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_dialect.rs diff --git a/Cargo.lock b/Cargo.lock index 7d467327d..f9ae9aae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4144,6 +4144,7 @@ dependencies = [ "sqlclean", "sqlquack-semantics", "sqlquack-syntax", + "sqlquack-syntax-clickhouse", "sqlquack-syntax-postgres", ] @@ -4500,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4510,19 +4511,21 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ + "regex", "serde", "serde_json", "sqlquack-core", "sqlquack-snowflake", "sqlquack-syntax", + "stacker", ] [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "sqlquack-core", @@ -4531,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4541,10 +4544,19 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "sqlquack-syntax-clickhouse" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +dependencies = [ + "sqlquack-syntax", + "stacker", +] + [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "proc-macro2", "quote", @@ -4554,7 +4566,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "sqlquack-syntax", "stacker", diff --git a/Cargo.toml b/Cargo.toml index c4475e838..795eecc33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,10 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-clickhouse = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 95c643ebd..9d15a26bb 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -28,6 +28,7 @@ sqlbuild-sqltext.workspace = true sqlclean.workspace = true sqlquack-semantics.workspace = true sqlquack-syntax.workspace = true +sqlquack-syntax-clickhouse.workspace = true sqlquack-syntax-postgres.workspace = true [lints] diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_dialect.rs new file mode 100644 index 000000000..78229fd5e --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_dialect.rs @@ -0,0 +1,59 @@ +//! ClickHouse's native module: SQLQuack's ClickHouse parser, profile and type spellings. + +use sqlquack_semantics::dialects::constants::CLICKHOUSE; +use sqlquack_semantics::dialects::main::clickhouse_type::clickhouse_type; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax::Parsed; +use sqlquack_syntax_clickhouse::parse_strict; + +use crate::analysis_route::_helpers::clickhouse_types::{ + clickhouse_spelling, clickhouse_type_match, +}; +use crate::analysis_route::constants::{CLICKHOUSE_DIALECT, CLICKHOUSE_STUB_PREFIX}; +use crate::analysis_route::models::{ClickHouseDialect, ParserGap, TypeMatch}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::models::NormalizedType; + +impl NativeDialect for ClickHouseDialect { + fn name(&self) -> &'static str { + CLICKHOUSE_DIALECT + } + + fn stub_prefix(&self) -> &'static str { + CLICKHOUSE_STUB_PREFIX + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql).map_err(|error| ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &CLICKHOUSE + } + + /// A type normalized as Polyglot normalizes it for ClickHouse; `None` when empty. + fn normalize_type(&self, spelling: &str) -> Option { + clickhouse_spelling(spelling) + } + + /// Both sides respelt as ClickHouse's `toTypeName` prints them, then compared wrappers apart. + fn type_match( + &self, + declared: &str, + inferred: Option<&str>, + inferred_nullable: bool, + ) -> TypeMatch { + let Some(declared) = clickhouse_type(declared) else { + return TypeMatch::NotCompared { + nullable_into_non_null: false, + }; + }; + let inferred: Option = inferred.and_then(clickhouse_type); + clickhouse_type_match(&declared, inferred.as_deref(), inferred_nullable) + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs index 9018bfc67..033cc512a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/clickhouse_types.rs @@ -1,7 +1,180 @@ -use crate::analysis_route::constants::{LOW_CARDINALITY_WRAPPER, NULLABLE_WRAPPER}; +//! ClickHouse type text: Polyglot-equal normalization, and declared and inferred types compared. + +use crate::analysis_route::constants::{ + CLICKHOUSE_TYPE_ALIASES, CLICKHOUSE_VERBATIM_TYPES, LOW_CARDINALITY_WRAPPER, NULLABLE_WRAPPER, +}; +use crate::analysis_route::models::TypeMatch; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeFamily}; + +/// `spelling` respelt through Polyglot's ClickHouse aliases, with Polyglot's family; `None` if empty. +pub(crate) fn clickhouse_spelling(spelling: &str) -> Option { + let written: &str = spelling.trim(); + if written.is_empty() { + return None; + } + let respelt: Option = respelt(written) + .filter(|(_, renamed)| *renamed) + .map(|(text, _)| text); + let text: &str = respelt.as_deref().unwrap_or(written); + let mut normalized: NormalizedType = normalize_text_type(text, None); + let family: Option = respelt + .as_ref() + .and(type_name(written)) + .and_then(|(name, _)| alias_family(&name)); + if let Some(family) = family { + normalized.normalized_name = text + .to_uppercase() + .chars() + .filter(|character| !character.is_whitespace()) + .collect(); + normalized.family = family; + } + Some(normalized) +} + +/// Polyglot's family for a top-level alias whose family the type fallback misreads. +fn alias_family(name: &str) -> Option { + CLICKHOUSE_TYPE_ALIASES + .iter() + .find(|(alias, _, _)| alias.eq_ignore_ascii_case(name)) + .and_then(|(_, _, family)| *family) +} + +/// One type respelt through the alias table, with whether any name in it was renamed. +fn respelt(written: &str) -> Option<(String, bool)> { + let (name, rest) = type_name(written)?; + let rest: &str = rest.trim_start(); + if CLICKHOUSE_VERBATIM_TYPES + .iter() + .any(|verbatim| verbatim.eq_ignore_ascii_case(&name)) + { + return Some((format!("{name}{rest}"), false)); + } + let (arguments, renamed): (String, bool) = if rest.is_empty() { + (String::new(), false) + } else { + let inner: &str = rest.strip_prefix('(')?.strip_suffix(')')?; + let written: Vec<&str> = split_arguments(inner)?; + let respelt: Vec<(String, bool)> = written + .iter() + .map(|argument| argument_respelt(argument.trim())) + .collect::>()?; + let renamed: bool = respelt.iter().any(|(_, renamed)| *renamed); + let respelt: Vec = respelt.into_iter().map(|(text, _)| text).collect(); + if let ([only_written], [only]) = (written.as_slice(), respelt.as_slice()) + && name.eq_ignore_ascii_case(NULLABLE_WRAPPER) + && !wraps_nullable(only_written.trim()) + && wraps_nullable(only) + { + return Some((only.clone(), renamed)); + } + (format!("({})", respelt.join(", ")), renamed) + }; + match alias(&name) { + Some(template) => Some((template.replacen("{}", &arguments, 1), true)), + None => Some((format!("{name}{arguments}"), renamed)), + } +} + +/// A literal argument as written, or a type argument respelt, with whether it was renamed. +fn argument_respelt(argument: &str) -> Option<(String, bool)> { + let literal: bool = argument + .bytes() + .next() + .is_some_and(|byte| byte.is_ascii_digit() || matches!(byte, b'\'' | b'-')); + if literal { + Some((argument.to_owned(), false)) + } else { + respelt(argument) + } +} + +/// The longest leading type name the alias table knows, or the first word, and the text after it. +fn type_name(written: &str) -> Option<(String, &str)> { + let word_end = |text: &str| { + text.find(|character: char| !(character.is_ascii_alphanumeric() || character == '_')) + .unwrap_or(text.len()) + }; + let first_end: usize = word_end(written); + if first_end == 0 { + return None; + } + let after_first: &str = &written[first_end..]; + let second_start: usize = first_end + (after_first.len() - after_first.trim_start().len()); + let second_end: usize = second_start + word_end(&written[second_start..]); + if second_end > second_start { + let phrase: String = format!( + "{} {}", + &written[..first_end], + &written[second_start..second_end] + ); + if alias(&phrase).is_some() { + return Some((phrase, &written[second_end..])); + } + } + Some((written[..first_end].to_owned(), after_first)) +} + +/// The alias template for a case-insensitive type name: `{}` takes the arguments as written. +fn alias(name: &str) -> Option<&'static str> { + CLICKHOUSE_TYPE_ALIASES + .iter() + .find(|(alias, _, _)| alias.eq_ignore_ascii_case(name)) + .map(|(_, template, _)| *template) +} + +/// The top-level comma-separated arguments, with commas inside quotes or parentheses kept. +fn split_arguments(inner: &str) -> Option> { + let mut arguments: Vec<&str> = Vec::new(); + let (mut depth, mut quoted, mut start) = (0_usize, false, 0_usize); + for (index, character) in inner.char_indices() { + match character { + '\'' => quoted = !quoted, + '(' if !quoted => depth += 1, + ')' if !quoted => depth = depth.checked_sub(1)?, + ',' if !quoted && depth == 0 => { + arguments.push(&inner[start..index]); + start = index + 1; + } + _ => {} + } + } + (depth == 0 && !quoted).then(|| { + arguments.push(&inner[start..]); + arguments + }) +} + +/// Whether a type is a top-level `Nullable(...)`: Polyglot does not wrap an alias it makes nullable twice. +fn wraps_nullable(respelt: &str) -> bool { + respelt + .get(..=NULLABLE_WRAPPER.len()) + .is_some_and(|head| head.eq_ignore_ascii_case(&format!("{NULLABLE_WRAPPER}("))) +} + +/// A declared and an inferred ClickHouse type compared without top-level wrappers or spacing. +#[must_use] +pub(crate) fn clickhouse_type_match( + declared: &str, + inferred: Option<&str>, + inferred_nullable: bool, +) -> TypeMatch { + let (declared_base, declared_nullable) = unwrapped(declared); + let Some(inferred) = inferred else { + return TypeMatch::NotCompared { + nullable_into_non_null: !declared_nullable && inferred_nullable, + }; + }; + let (inferred_base, wrapped_nullable) = unwrapped(inferred); + TypeMatch::Compared { + same_type: unspaced(declared_base) == unspaced(inferred_base), + nullable_into_non_null: !declared_nullable && (inferred_nullable || wrapped_nullable), + } +} /// `type_sql` without whitespace outside its quoted strings. -pub(crate) fn unspaced(type_sql: &str) -> String { +fn unspaced(type_sql: &str) -> String { let mut quoted: bool = false; type_sql .chars() @@ -15,7 +188,7 @@ pub(crate) fn unspaced(type_sql: &str) -> String { } /// The type inside its top-level `Nullable` and `LowCardinality` wrappers, and whether it is nullable. -pub(crate) fn unwrapped(type_sql: &str) -> (&str, bool) { +fn unwrapped(type_sql: &str) -> (&str, bool) { let mut current: &str = type_sql.trim(); let mut nullable: bool = false; loop { diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 279c10ae9..98469456d 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod clickhouse_dialect; pub(crate) mod clickhouse_types; pub(crate) mod located_gap; pub(crate) mod marker_names; diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..df210a662 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,9 +1,12 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. +use crate::analysis_route::models::ClickHouseDialect; use crate::analysis_route::types::NativeDialect; +use crate::type_system::models::TypeFamily; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. -pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = + &[(CLICKHOUSE_DIALECT, &ClickHouseDialect)]; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. @@ -26,3 +29,32 @@ pub(crate) const ERROR_SEVERITY: &str = "error"; pub(crate) const MACRO_BODY_PREFIX: &str = "SELECT "; /// The gap of a macro body that is not one expression. pub(crate) const NOT_ONE_EXPRESSION: &str = "not one expression"; +/// ClickHouse's analysis dialect name. +pub(crate) const CLICKHOUSE_DIALECT: &str = "clickhouse"; +/// The stub prefix ClickHouse reads as one unquoted name. +pub(crate) const CLICKHOUSE_STUB_PREFIX: &str = "__sqlbuild_"; +/// Polyglot's ClickHouse respelling of each SQL type alias (`{}` keeps the arguments) and family. +pub(crate) const CLICKHOUSE_TYPE_ALIASES: &[(&str, &str, Option)] = &[ + ("BOOLEAN", "Bool", None), + ("CHAR", "String", None), + ("TEXT", "String", None), + ("VARCHAR", "String", None), + ("NUMERIC", "Decimal{}", None), + ("BINARY", "Nullable(BINARY{})", None), + ("TINYINT", "Nullable(Int8)", Some(TypeFamily::Integer)), + ("SMALLINT", "Nullable(Int16)", Some(TypeFamily::Integer)), + ("INT", "Nullable(Int32)", Some(TypeFamily::Integer)), + ("INTEGER", "Nullable(Int32)", Some(TypeFamily::Integer)), + ("BIGINT", "Nullable(Int64)", Some(TypeFamily::Integer)), + ("REAL", "Nullable(Float32){}", Some(TypeFamily::Float)), + ("FLOAT", "Nullable(Float32){}", Some(TypeFamily::Float)), + ("DOUBLE", "Nullable(Float64){}", Some(TypeFamily::Float)), + ( + "DOUBLE PRECISION", + "Nullable(Float64){}", + Some(TypeFamily::Float), + ), + ("TIMESTAMP", "DateTime{}", Some(TypeFamily::Timestamp)), +]; +/// The ClickHouse types whose arguments Polyglot keeps as written, aliases unrenamed. +pub(crate) const CLICKHOUSE_VERBATIM_TYPES: &[&str] = &["LowCardinality", "Tuple"]; diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs deleted file mode 100644 index 2a497aa24..000000000 --- a/crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::analysis_route::_helpers::clickhouse_types::{unspaced, unwrapped}; -use crate::analysis_route::models::TypeMatch; - -/// A declared and an inferred ClickHouse type compared without top-level wrappers or spacing. -#[must_use] -pub fn clickhouse_type_match( - declared: &str, - inferred: Option<&str>, - inferred_nullable: bool, -) -> TypeMatch { - let (declared_base, declared_nullable) = unwrapped(declared); - let Some(inferred) = inferred else { - return TypeMatch::NotCompared { - nullable_into_non_null: !declared_nullable && inferred_nullable, - }; - }; - let (inferred_base, wrapped_nullable) = unwrapped(inferred); - TypeMatch::Compared { - same_type: unspaced(declared_base) == unspaced(inferred_base), - nullable_into_non_null: !declared_nullable && (inferred_nullable || wrapped_nullable), - } -} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs index 9d6f86f1f..0772c402f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs @@ -1,5 +1,4 @@ pub mod analysis_route; -pub mod clickhouse_type_match; pub mod fast_lineage; pub(crate) mod model_outcome; pub mod native_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 7330e008d..764e53be7 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -122,3 +122,7 @@ impl LineageSchema for StandInSchema { .then_some(self.columns.as_slice()) } } + +/// ClickHouse, whose SQL SQLQuack's ClickHouse parser reads. +#[derive(Clone, Copy, Debug)] +pub struct ClickHouseDialect; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index 242ac2a38..3a6d549d6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -1,5 +1,7 @@ #[path = "tests/helpers.rs"] mod helpers; +#[path = "tests/test_clickhouse_dialect.rs"] +mod test_clickhouse_dialect; #[path = "tests/test_clickhouse_type_match.rs"] mod test_clickhouse_type_match; #[path = "tests/test_native_dialect.rs"] diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_dialect.rs new file mode 100644 index 000000000..aba490c8c --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_dialect.rs @@ -0,0 +1,337 @@ +use crate::analysis_route::constants::CLICKHOUSE_DIALECT; +use crate::analysis_route::main::native_gap::native_gap; +use crate::analysis_route::models::{ClickHouseDialect, LocatedGap, TypeMatch}; +use crate::analysis_route::tests::test_types::{ + NativeGapTestCase, TypeMatchTestCase, TypeSpellingsTestCase, +}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::project::models::SyntaxCheck; +use crate::type_system::main::normalize_type::normalize_type; + +/// Type spellings from SQLBuild's compiled ClickHouse corpus, with case, nesting and width variants. +const CLICKHOUSE_SPELLINGS: &[&str] = &[ + "Array(INT(11))", + "Array(Int32)", + "Array(String)", + "BIGINT", + "BIGINT(20)", + "BIGINT(20) UNSIGNED", + "BINARY", + "BINARY(5)", + "BOOLEAN", + "Bool", + "CHAR", + "DATE", + "DECIMAL(10,2)", + "DOUBLE", + "DOUBLE PRECISION", + "DOUBLE(10,2)", + "Date32", + "DateTime", + "DateTime('UTC')", + "DateTime64( 6 , 'Europe/Amsterdam' )", + "DateTime64(3)", + "DateTime64(3, 'UTC')", + "DateTime64(3,'UTC')", + "Decimal", + "Decimal(10, 2)", + "Decimal(38, 4)", + "Decimal32(2)", + "FLOAT", + "FLOAT(12)", + "FLOAT(53)", + "FixedString", + "FixedString(3)", + "FixedString(4)", + "Float32", + "Float64", + "Float64(3)", + "INT UNSIGNED", + "INT(11)", + "INTEGER", + "INTEGER(11)", + "IPv4", + "Int128", + "Int16", + "Int32", + "Int32(11)", + "Int64", + "Int8", + "JSON", + "LowCardinality(Nullable(String))", + "LowCardinality(String)", + "Map(String, Int64)", + "NUMERIC(10)", + "NUMERIC(12, 3)", + "Nothing", + "Nullable(DateTime64(3,'UTC'))", + "Nullable(Int32)", + "Nullable(Int64)", + "Nullable(String)", + "REAL", + "SMALLINT", + "SMALLINT(5)", + "String", + "TEXT", + "TIMESTAMP", + "TINYINT", + "TINYINT(1)", + "UInt16", + "UInt256", + "UInt32", + "UInt64", + "UInt8", + "UUID", + "VARCHAR", + "VARCHAR(10)", + "VARCHAR(22)", + "int", + "Float", + "double", + "timestamp", + "varchar(5)", + "boolean", + "Array(Nullable(INT))", + "Tuple(a INT)", + "Enum8('a' = 1)", + "Nullable(INT)", + "LowCardinality(VARCHAR)", + "REAL(5)", + "TIMESTAMP(3)", + "DECIMAL(10)", + "numeric", + "Map(String, BIGINT)", + "Array(Array(TEXT))", + "Nullable(TIMESTAMP)", + "Array(VARCHAR(10))", + "Array(BOOLEAN)", + "LowCardinality(INT)", + "Nullable(Nullable(INT))", + "Nullable(BOOLEAN)", + "Tuple(INT, VARCHAR)", +]; + +#[test] +fn given_clickhouse_model_sql_when_probing_the_native_parser_then_only_unreadable_sql_has_a_gap() { + let test_cases = [ + NativeGapTestCase { + description: "a UDF call's stub is one unquoted name", + sql: "SELECT __udf(\"scaled_amount\")(amount, 2) AS scaled FROM t", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "cursor bounds are stubs ClickHouse reads", + sql: "SELECT * FROM t WHERE at >= __cursor_start() AND at < __cursor_end( )", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "a quoted hook parameter becomes a string literal", + sql: "SELECT * FROM t WHERE region = @'region'", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "an apostrophe in a hash comment opens no string", + sql: "WITH o AS (SELECT 1 AS id)\n# don't keep the star\nSELECT * FROM o", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "an apostrophe in a shebang comment opens no string", + sql: "SELECT * FROM t #!it's\n", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "a FROM after FROM is a table name, as ClickHouse reads it", + sql: "SELECT 1 FROM FROM", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "WITH TIES is a named gap at its line and column", + sql: "SELECT 'é' AS a\nFROM t ORDER BY a\n LIMIT 1 WITH TIES", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "with_ties".to_owned(), + message: "with_ties".to_owned(), + line: 3, + column: 11, + syntax: false, + }), + }, + NativeGapTestCase { + description: "invalid SQL is a gap at the parser's refusal", + sql: "SELECT (1 + FROM t", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "Syntax error: failed at position 18 (t)".to_owned(), + message: "Syntax error: failed at position 18 (t)".to_owned(), + line: 1, + column: 18, + syntax: true, + }), + }, + NativeGapTestCase { + description: "two statements are not one model body", + sql: "SELECT 1; SELECT 2", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "more than one statement".to_owned(), + message: "more than one statement".to_owned(), + line: 1, + column: 1, + syntax: false, + }), + }, + ]; + for test_case in test_cases { + let check = SyntaxCheck { + sql: test_case.sql.to_owned(), + placeholders: test_case + .placeholders + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) + .collect(), + }; + assert_eq!( + native_gap(&ClickHouseDialect, &check), + Ok(test_case.expected_gap), + "{}", + test_case.description + ); + } +} + +#[test] +fn given_a_clickhouse_type_when_normalising_then_it_is_polyglots_type_family_included() { + let test_cases = [TypeSpellingsTestCase { + description: "every corpus spelling and its case, nesting and width variants", + spellings: CLICKHOUSE_SPELLINGS, + expected_mismatches: &[], + }]; + for test_case in test_cases { + let mismatches: Vec<&str> = test_case + .spellings + .iter() + .copied() + .filter(|spelling| { + let polyglot = normalize_type(spelling, CLICKHOUSE_DIALECT) + .ok() + .map(|typed| typed.normalized); + ClickHouseDialect.normalize_type(spelling) != polyglot + }) + .collect(); + assert_eq!( + mismatches, test_case.expected_mismatches, + "{}", + test_case.description + ); + } +} + +#[test] +fn given_clickhouse_contract_types_when_matching_then_aliases_compare_as_the_engine_spells_them() { + let test_cases = [ + TypeMatchTestCase { + description: "a declared INTEGER is a non-null Int32", + declared: "INTEGER", + inferred: Some("Int32"), + inferred_nullable: false, + expected_match: TypeMatch::Compared { + same_type: true, + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a declared BIGINT is the engine's Int64", + declared: "BIGINT", + inferred: Some("Int64"), + inferred_nullable: false, + expected_match: TypeMatch::Compared { + same_type: true, + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a declared VARCHAR refuses a nullable String", + declared: "VARCHAR", + inferred: Some("String"), + inferred_nullable: true, + expected_match: TypeMatch::Compared { + same_type: true, + nullable_into_non_null: true, + }, + }, + TypeMatchTestCase { + description: "argument spacing does not change the type", + declared: "DateTime64(3,'UTC')", + inferred: Some("DateTime64(3, 'UTC')"), + inferred_nullable: false, + expected_match: TypeMatch::Compared { + same_type: true, + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a different width is a different type", + declared: "BIGINT", + inferred: Some("Int32"), + inferred_nullable: false, + expected_match: TypeMatch::Compared { + same_type: false, + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a declared BIGINT refuses a nullable value of unknown type", + declared: "BIGINT", + inferred: None, + inferred_nullable: true, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: true, + }, + }, + TypeMatchTestCase { + description: "a declared Nullable type takes a nullable value of unknown type", + declared: "Nullable(BIGINT)", + inferred: None, + inferred_nullable: true, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a type ClickHouse refuses is not compared", + declared: "FixedString", + inferred: Some("String"), + inferred_nullable: true, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + TypeMatchTestCase { + description: "a misspelt inferred type is not compared, and a nullable one still meets a non-null declaration", + declared: "String", + inferred: Some("Strng"), + inferred_nullable: true, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: true, + }, + }, + ]; + for test_case in test_cases { + assert_eq!( + ClickHouseDialect.type_match( + test_case.declared, + test_case.inferred, + test_case.inferred_nullable + ), + test_case.expected_match, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs index c6cab99ac..fd23771cf 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_clickhouse_type_match.rs @@ -1,4 +1,4 @@ -use crate::analysis_route::main::clickhouse_type_match::clickhouse_type_match; +use crate::analysis_route::_helpers::clickhouse_types::clickhouse_type_match; use crate::analysis_route::models::TypeMatch; use crate::analysis_route::tests::test_types::TypeMatchTestCase; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 5c395c6d1..18b00e7fa 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -8,7 +8,7 @@ use crate::analysis_route::tests::test_types::{ use crate::assembly::project::models::SyntaxCheck; #[test] -fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { +fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_legacy_path() { let test_cases = [ RouteTestCase { description: "postgres", @@ -43,7 +43,7 @@ fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { RouteTestCase { description: "clickhouse", dialect: Some("clickhouse"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "tsql", diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 3bd8d1685..a02d720dc 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -248,3 +248,9 @@ pub(super) struct NativeLineageTestCase { pub(super) placeholders: &'static [(&'static str, &'static str)], pub(super) expected_lines: &'static [&'static str], } + +pub(super) struct TypeSpellingsTestCase { + pub(super) description: &'static str, + pub(super) spellings: &'static [&'static str], + pub(super) expected_mismatches: &'static [&'static str], +} diff --git a/fensu.toml b/fensu.toml index 3fee255b7..bee7f18a7 100644 --- a/fensu.toml +++ b/fensu.toml @@ -2932,13 +2932,3 @@ reason = "The override keeps queue.Queue.put's positional protocol, which every rule = "FFS201" path = "src/sqlbuild/orchestration_declarations/models/sensor_context.py" reason = "Only SensorContext lives here: invocation-local ctx.cursor assignment proposes progress, committed solely by native fenced completion. The package regression guards this declaration-only scope; class-symbol exceptions are unsupported." - -[[targets.rust.rule_exceptions]] -rule = "FPRSL105" -path = "crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs" -reason = "Native dialect modules in analysis_route type their spellings through this entry; each dialect's module lands in its own change." - -[[targets.rust.rule_exceptions]] -rule = "FPRSL105" -path = "crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs" -reason = "The ClickHouse native dialect module's type_match calls this entry; that module lands in its own change." From 398a3b1394e2624d68360cbb2396be500d37340e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:05:25 +0000 Subject: [PATCH 040/160] test(analysis): re-record native spellings and the DuckDB union guard Typed markers now report DuckDB's own VARCHAR and INTEGER. A deep union of one marker resolves its columns at high confidence, and the DuckDB parser reads 8,000 UNION ALL branches and refuses 8,001. --- .../analysis_session/tests/test_typed_markers.rs | 12 ++++++------ .../src/lineage/tests/test_build_fast_lineage.rs | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_typed_markers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_typed_markers.rs index 8a8a270e5..ffc15cfd2 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_typed_markers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_typed_markers.rs @@ -13,23 +13,23 @@ fn given_typed_markers_when_analysing_then_each_call_has_its_asserted_or_inferre TypedMarkerTestCase { description: "an unknown body takes the asserted type", sql: "SELECT __polyglot_typed(bar(amount, 0, 10), 'VARCHAR') AS v FROM __ref(\"staged\")", - expected_column: "v TEXT", + expected_column: "v VARCHAR", }, TypedMarkerTestCase { description: "a known body keeps its inferred type", sql: "SELECT __polyglot_typed(order_id, 'VARCHAR') AS v FROM __ref(\"staged\")", - expected_column: "v INT ", + expected_column: "v INTEGER ", }, TypedMarkerTestCase { description: "a known aliased column keeps its inferred type", sql: "SELECT __polyglot_typed(o.order_id, 'VARCHAR') AS v FROM __ref(\"staged\") AS o", - expected_column: "v INT ", + expected_column: "v INTEGER ", }, TypedMarkerTestCase { description: "a later set-operation branch types the union", sql: "SELECT CAST(NULL AS VARCHAR) AS v FROM __ref(\"staged\") UNION ALL \ SELECT __polyglot_typed(bar(amount, 0, 10), 'VARCHAR') AS v FROM __ref(\"staged\")", - expected_column: "v TEXT", + expected_column: "v VARCHAR", }, TypedMarkerTestCase { description: "a NULL body takes the asserted type in a union", @@ -41,13 +41,13 @@ fn given_typed_markers_when_analysing_then_each_call_has_its_asserted_or_inferre description: "a CTE read by name", sql: "WITH c AS (SELECT __polyglot_typed(bar(amount, 0, 10), 'VARCHAR') AS v \ FROM __ref(\"staged\")) SELECT v FROM c", - expected_column: "v TEXT", + expected_column: "v VARCHAR", }, TypedMarkerTestCase { description: "an operand of a larger expression", sql: "SELECT COALESCE(__polyglot_typed(bar(amount, 0, 10), 'VARCHAR'), 'x') AS v \ FROM __ref(\"staged\")", - expected_column: "v TEXT", + expected_column: "v VARCHAR", }, ]; diff --git a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs index ec6bce1e4..f4a57023e 100644 --- a/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/lineage/tests/test_build_fast_lineage.rs @@ -297,23 +297,23 @@ fn given_deep_union_chain_when_building_from_small_stack_then_runs_on_the_analys let test_cases = [ DeepUnionTestCase { description: "the deepest chain the parser guard accepts", - branches: 512, + branches: 8000, stack_bytes: 256 * 1024, expected_status: "built", expected_lines: &[ - "order_id direct medium [model:orders.order_id]", - "amount direct medium [model:orders.amount]", + "order_id direct high [model:orders.order_id]", + "amount direct high [model:orders.amount]", ], expected_detail: None, }, DeepUnionTestCase { description: "one branch past the parser guard reports its parse error", - branches: 513, + branches: 8001, stack_bytes: 256 * 1024, expected_status: "unparsed", expected_lines: &[], expected_detail: Some( - "Parse error at line 0, column 0: E_GUARD_AST_DEPTH_EXCEEDED: value 513 exceeds configured limit 512", + "the duckdb parser cannot read the statement nests too deeply at line 1, column 1", ), }, ]; From 78241c317b6224cde868a9a0ad8bd63692ae85ff Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:09:34 +0000 Subject: [PATCH 041/160] test(analysis): re-record type notes in the engine's own spellings Native analysis keeps the dialect's type names, so DuckDB notes and messages say VARCHAR and INTEGER where Polyglot printed its comparison spellings TEXT and INT. --- .../goldens/compiler/failure/analysis-arithmetic-operands.json | 2 +- tests/goldens/compiler/failure/analysis-cursor-type-family.json | 2 +- .../compiler/failure/analysis-function-argument-type.json | 2 +- .../compiler/failure/analysis-non-boolean-predicate.json | 2 +- .../goldens/compiler/failure/analysis-udf-argument-family.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/goldens/compiler/failure/analysis-arithmetic-operands.json b/tests/goldens/compiler/failure/analysis-arithmetic-operands.json index eb6041025..2c63ae063 100644 --- a/tests/goldens/compiler/failure/analysis-arithmetic-operands.json +++ b/tests/goldens/compiler/failure/analysis-arithmetic-operands.json @@ -23,7 +23,7 @@ }, "help": "use operands supported by this arithmetic operator, or convert them explicitly", "notes": [ - "status is TEXT, 1 is INTEGER" + "status is VARCHAR, 1 is INTEGER" ] } ] diff --git a/tests/goldens/compiler/failure/analysis-cursor-type-family.json b/tests/goldens/compiler/failure/analysis-cursor-type-family.json index 207baf4a1..4291b3366 100644 --- a/tests/goldens/compiler/failure/analysis-cursor-type-family.json +++ b/tests/goldens/compiler/failure/analysis-cursor-type-family.json @@ -8,7 +8,7 @@ "phase": "compile", "severity": "error", "code": "B301", - "message": "cursor_type TIMESTAMP does not match column 'customer_id' type INT", + "message": "cursor_type TIMESTAMP does not match column 'customer_id' type INTEGER", "resource_type": "model", "resource_name": "customer_totals", "path": "models/marts/customer_totals.sql", diff --git a/tests/goldens/compiler/failure/analysis-function-argument-type.json b/tests/goldens/compiler/failure/analysis-function-argument-type.json index 1e1430c78..e9caf69e1 100644 --- a/tests/goldens/compiler/failure/analysis-function-argument-type.json +++ b/tests/goldens/compiler/failure/analysis-function-argument-type.json @@ -23,7 +23,7 @@ }, "help": "supply argument types supported by this function or conditional expression", "notes": [ - "status is TEXT" + "status is VARCHAR" ] } ] diff --git a/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json b/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json index 5f5423e60..9f3028796 100644 --- a/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json +++ b/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json @@ -23,7 +23,7 @@ }, "help": "use a Boolean predicate in this condition", "notes": [ - "status is TEXT" + "status is VARCHAR" ] } ] diff --git a/tests/goldens/compiler/failure/analysis-udf-argument-family.json b/tests/goldens/compiler/failure/analysis-udf-argument-family.json index 6bb4f4a10..945bc8950 100644 --- a/tests/goldens/compiler/failure/analysis-udf-argument-family.json +++ b/tests/goldens/compiler/failure/analysis-udf-argument-family.json @@ -8,7 +8,7 @@ "phase": "compile", "severity": "error", "code": "B301", - "message": "Function 'scaled_amount' argument 'p_amount' expects DOUBLE, received TEXT; convert the argument explicitly", + "message": "Function 'scaled_amount' argument 'p_amount' expects DOUBLE, received VARCHAR; convert the argument explicitly", "resource_type": "model", "resource_name": "customer_totals", "path": "models/marts/customer_totals.sql", From 195fa99b430f29127cbfc4fdf1bfea54d86200d9 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:30:44 +0000 Subject: [PATCH 042/160] fix(analysis): name the hook and the model file line in P026 A native route's P026 for a hook now names the hook and its file, as the Polyglot syntax error did, such as post_hooks[0] sql("record_refresh") (hooks/sql/record_refresh.sql), and places the gap in the hook's SQL. A gap in the model's query is placed at its line and column in the model file when the text maps back through stub spelling, placeholders and expansion; otherwise the message says the position is in the analysed SQL. --- .../analysis_route/_helpers/located_gap.rs | 51 +++++- .../src/analysis_route/main/aligned_offset.rs | 5 + .../src/analysis_route/main/fast_lineage.rs | 7 +- .../src/analysis_route/main/mod.rs | 1 + .../src/analysis_route/main/native_gap.rs | 2 +- .../analysis_route/main/skipped_analysis.rs | 15 +- .../src/analysis_route/models.rs | 23 +++ .../analysis_route/tests/test_native_gap.rs | 95 ++++++++-- .../src/analysis_route/tests/test_types.rs | 3 +- .../_helpers/input_loops/model_loop.rs | 16 +- .../input_loops/model_loop_opt_out.rs | 166 +++++++++++++++--- .../project_assembly/syntax_validation.rs | 3 +- 12 files changed, 330 insertions(+), 57 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/aligned_offset.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs index 40d950161..852c8a28a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/located_gap.rs @@ -1,31 +1,70 @@ //! Where a native parser's gap is, and why the parser cannot read the SQL there. -use crate::analysis_route::models::{LocatedGap, ParserGap}; +use crate::analysis_route::models::{GapPosition, GapSite, LocatedGap, ParserGap}; use crate::compiled_project::main::text_position::text_position; use crate::compiled_project::models::TextPosition; -/// The gap with the line and column of its offset; an offset past the end is the end. -pub(crate) fn located(sql: &str, gap: ParserGap) -> LocatedGap { +/// The gap at its line and column in `sql`, and its offset in `source` when the text holds. +pub(crate) fn located(sql: &str, gap: ParserGap, source: Option<&str>) -> LocatedGap { let before: &str = sql.get(..gap.offset).unwrap_or(sql); - let position: TextPosition = text_position(sql, before.chars().count()); + let offset: usize = before.chars().count(); + let position: TextPosition = text_position(sql, offset); LocatedGap { construct: gap.construct, message: gap.message, line: position.line, column: position.column, syntax: gap.syntax, + source_offset: source.and_then(|source| aligned(sql, offset, source)), } } +/// The character of `source` that `derived`'s `offset` came from, when the text before or after holds. +pub(crate) fn aligned(derived: &str, offset: usize, source: &str) -> Option { + let derived: Vec = derived.chars().collect(); + let source: Vec = source.chars().collect(); + if offset > derived.len() { + return None; + } + if source.starts_with(&derived[..offset]) { + return Some(offset); + } + source + .ends_with(&derived[offset..]) + .then(|| source.len() - (derived.len() - offset)) +} + /// Why the dialect's native parser cannot read SQL, as P026 words it. pub(crate) fn unreadable_reason(dialect: &str, gap: &LocatedGap) -> String { + unreadable_at(dialect, gap, (gap.line, gap.column, "")) +} + +/// P026's text for a gap at its site: the hook and file, then the parser's reason there. +pub(crate) fn sited_reason(dialect: &str, gap: &LocatedGap, site: &GapSite<'_>) -> String { + let label: String = site + .label + .map_or_else(String::new, |label| format!(" {label}")); + let at: (usize, usize, &str) = match site.position { + GapPosition::File(position) => (position.line, position.column, ""), + GapPosition::Statement(position) => (position.line, position.column, " of its SQL"), + GapPosition::Analysed => (gap.line, gap.column, " of the analysed SQL"), + }; + format!( + "{label} ({}): {}", + site.path, + unreadable_at(dialect, gap, at) + ) +} + +fn unreadable_at(dialect: &str, gap: &LocatedGap, at: (usize, usize, &str)) -> String { + let (line, column, within) = at; let detail: String = if gap.message.is_empty() || gap.message == gap.construct { String::new() } else { format!(": {}", gap.message) }; format!( - "the {dialect} parser cannot read {} at line {}, column {}{detail}", - gap.construct, gap.line, gap.column + "the {dialect} parser cannot read {} at line {line}, column {column}{within}{detail}", + gap.construct ) } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/aligned_offset.rs b/crates/sqlbuild-analysis/src/analysis_route/main/aligned_offset.rs new file mode 100644 index 000000000..4d603f0ce --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/main/aligned_offset.rs @@ -0,0 +1,5 @@ +/// The character of `source` that `derived`'s `offset` came from, when the text before or after holds. +#[must_use] +pub fn aligned_offset(derived: &str, offset: usize, source: &str) -> Option { + crate::analysis_route::_helpers::located_gap::aligned(derived, offset, source) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs index e95f61f52..3f3fbf262 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs @@ -80,9 +80,10 @@ pub fn fast_lineage( columns: native.lineage.iter().map(lineage_column).collect(), has_star: native.has_star, }, - Err(gap) => { - FastLineageOutcome::Unparsed(unreadable_reason(dialect.name(), &located(&sql, gap))) - } + Err(gap) => FastLineageOutcome::Unparsed(unreadable_reason( + dialect.name(), + &located(&sql, gap, None), + )), } } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs index 9d6f86f1f..578305573 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs @@ -1,3 +1,4 @@ +pub mod aligned_offset; pub mod analysis_route; pub mod clickhouse_type_match; pub mod fast_lineage; diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs index e484e7074..aa68f6705 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_gap.rs @@ -17,5 +17,5 @@ pub fn native_gap( Ok(dialect .read_whole(&analysed) .err() - .map(|gap| located(&analysed, gap))) + .map(|gap| located(&analysed, gap, Some(&check.sql)))) } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs index d170a5495..961719fc8 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/skipped_analysis.rs @@ -1,15 +1,20 @@ -use crate::analysis_route::_helpers::located_gap::unreadable_reason; +use crate::analysis_route::_helpers::located_gap::sited_reason; use crate::analysis_route::constants::SKIPPED_ANALYSIS_CODE; -use crate::analysis_route::models::LocatedGap; +use crate::analysis_route::models::{GapSite, LocatedGap}; /// P026's code and message for a model whose SQL the dialect's native parser cannot read. #[must_use] -pub fn skipped_analysis(model: &str, dialect: &str, gap: &LocatedGap) -> (&'static str, String) { +pub fn skipped_analysis( + model: &str, + dialect: &str, + gap: &LocatedGap, + site: &GapSite<'_>, +) -> (&'static str, String) { ( SKIPPED_ANALYSIS_CODE, format!( - "SQL analysis skipped for model '{model}': {}", - unreadable_reason(dialect, gap) + "SQL analysis skipped for model '{model}'{}", + sited_reason(dialect, gap, site) ), ) } diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 7330e008d..838fd60c2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -5,6 +5,7 @@ use sqlquack_semantics::lineage::types::LineageSchema; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; +use crate::compiled_project::models::TextPosition; use crate::semantic_validation::models::DiagnosticRow; /// The engine that analyses a dialect's SQL. @@ -38,6 +39,28 @@ pub struct LocatedGap { pub line: usize, pub column: usize, pub syntax: bool, + /// The gap's character offset in the SQL as checked, before normalisation, when its text holds. + pub source_offset: Option, +} + +/// Where P026 places a gap. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum GapPosition { + /// In the file the site names. + File(TextPosition), + /// In the site's own SQL statement, such as a hook's. + Statement(TextPosition), + /// Only in the analysed SQL, at the gap's own line and column. + Analysed, +} + +/// The SQL a gap stops: the hook it belongs to, if any, its file, and where the gap is. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct GapSite<'a> { + /// Such as `post_hooks[0] sql("record_refresh")`; none for the model's query. + pub label: Option<&'a str>, + pub path: &'a str, + pub position: GapPosition, } /// How an inferred column type meets its declared type: the type itself, then its nullability. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 5c395c6d1..fc714b2bd 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -1,11 +1,12 @@ use crate::analysis_route::main::analysis_route::analysis_route; use crate::analysis_route::main::native_gap::native_gap; use crate::analysis_route::main::skipped_analysis::skipped_analysis; -use crate::analysis_route::models::{AnalysisRoute, LocatedGap}; +use crate::analysis_route::models::{AnalysisRoute, GapPosition, GapSite, LocatedGap}; use crate::analysis_route::tests::test_types::{ NativeGapTestCase, PivotlessDialect, RouteTestCase, SkippedAnalysisTestCase, }; use crate::assembly::project::models::SyntaxCheck; +use crate::compiled_project::models::TextPosition; #[test] fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { @@ -109,6 +110,33 @@ fn given_model_sql_when_probing_the_native_parser_then_its_gap_is_located_after_ line: 3, column: 3, syntax: false, + source_offset: Some(25), + }), + }, + NativeGapTestCase { + description: "a stub respelt before the gap keeps its offset in the checked SQL", + sql: "SELECT __cursor_start() AS a FROM t PIVOT (sum(x) FOR y IN (1))", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "PIVOT".to_owned(), + message: "PIVOT".to_owned(), + line: 1, + column: 46, + syntax: false, + source_offset: Some(36), + }), + }, + NativeGapTestCase { + description: "text changed on both sides of the gap leaves it only in the analysed SQL", + sql: "SELECT __cursor_start() AS a FROM t PIVOT (sum(x) FOR y IN (@@@n))", + placeholders: &[("n", "1")], + expected_gap: Some(LocatedGap { + construct: "PIVOT".to_owned(), + message: "PIVOT".to_owned(), + line: 1, + column: 46, + syntax: false, + source_offset: None, }), }, ]; @@ -131,10 +159,10 @@ fn given_model_sql_when_probing_the_native_parser_then_its_gap_is_located_after_ } #[test] -fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_and_the_gap() { +fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_its_site_and_the_gap() { let test_cases = [ SkippedAnalysisTestCase { - description: "the parser's message follows the construct", + description: "a query gap placed in the model file, with the parser's message", model: "orders", dialect: "postgres", gap: LocatedGap { @@ -143,14 +171,49 @@ fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_and_the_ line: 1, column: 15, syntax: true, + source_offset: Some(14), + }, + site: GapSite { + label: None, + path: "models/orders.sql", + position: GapPosition::File(TextPosition { + line: 5, + column: 18, + }), + }, + expected_code: "P026", + expected_message: "SQL analysis skipped for model 'orders' (models/orders.sql): the \ + postgres parser cannot read syntax error at line 5, column 18: \ + syntax error at or near \"FROM\"", + }, + SkippedAnalysisTestCase { + description: "a hook gap names the hook and is placed in its statement", + model: "customer_totals", + dialect: "duckdb", + gap: LocatedGap { + construct: "Syntax error at or near 'WHERE'".to_owned(), + message: "Syntax error at or near 'WHERE'".to_owned(), + line: 1, + column: 13, + syntax: true, + source_offset: Some(12), + }, + site: GapSite { + label: Some("post_hooks[0] sql(\"record_refresh\")"), + path: "hooks/sql/record_refresh.sql", + position: GapPosition::Statement(TextPosition { + line: 1, + column: 13, + }), }, expected_code: "P026", - expected_message: "SQL analysis skipped for model 'orders': the postgres parser \ - cannot read syntax error at line 1, column 15: syntax error at \ - or near \"FROM\"", + expected_message: "SQL analysis skipped for model 'customer_totals' post_hooks[0] \ + sql(\"record_refresh\") (hooks/sql/record_refresh.sql): the duckdb \ + parser cannot read Syntax error at or near 'WHERE' at line 1, \ + column 13 of its SQL", }, SkippedAnalysisTestCase { - description: "a PIVOT gap on line 3", + description: "a gap only the analysed SQL places says so", model: "orders_pivot", dialect: "postgres", gap: LocatedGap { @@ -159,15 +222,27 @@ fn given_a_located_gap_when_skipping_analysis_then_p026_names_the_model_and_the_ line: 3, column: 3, syntax: false, + source_offset: None, + }, + site: GapSite { + label: None, + path: "models/orders_pivot.sql", + position: GapPosition::Analysed, }, expected_code: "P026", - expected_message: "SQL analysis skipped for model 'orders_pivot': the postgres parser \ - cannot read PIVOT at line 3, column 3", + expected_message: "SQL analysis skipped for model 'orders_pivot' \ + (models/orders_pivot.sql): the postgres parser cannot read PIVOT at \ + line 3, column 3 of the analysed SQL", }, ]; for test_case in test_cases { assert_eq!( - skipped_analysis(test_case.model, test_case.dialect, &test_case.gap), + skipped_analysis( + test_case.model, + test_case.dialect, + &test_case.gap, + &test_case.site + ), ( test_case.expected_code, test_case.expected_message.to_owned() diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 3bd8d1685..382cb4133 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -4,7 +4,7 @@ use sqlquack_semantics::lineage::models::Nullability; use sqlquack_syntax::expressions::statements::models::Command; use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; -use crate::analysis_route::models::{LocatedGap, ParserGap, TypeMatch}; +use crate::analysis_route::models::{GapSite, LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; use crate::type_system::models::NormalizedType; @@ -27,6 +27,7 @@ pub(super) struct SkippedAnalysisTestCase { pub(super) model: &'static str, pub(super) dialect: &'static str, pub(super) gap: LocatedGap, + pub(super) site: GapSite<'static>, pub(super) expected_code: &'static str, pub(super) expected_message: &'static str, } diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs index 4d9a37115..5d5df952e 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop.rs @@ -73,8 +73,8 @@ use crate::bindings::_helpers::input_loops::model_loop_macros::{ }; use crate::bindings::_helpers::input_loops::model_loop_named::{SCHEMA_DECLARATION, named_usages}; use crate::bindings::_helpers::input_loops::model_loop_opt_out::{ - OptOut, OptOutModel, reject_unparseable_query, rejected_opt_out, skip_unavailable, - skip_unreadable, + AuthoredQuery, OptOut, OptOutModel, reject_unparseable_query, rejected_opt_out, + skip_unavailable, skip_unreadable, }; use crate::bindings::_helpers::input_loops::model_loop_prepass::{ NativeHeader, all_header_facts, native_header, @@ -708,6 +708,16 @@ impl NativeModelLoop { (&declarations.spliced.sql, ¯o_sql, &query_sql), ¯o_spans, ); + let authored_passes: Vec> = vec![ + declarations.spliced.spans.clone(), + spliced_spans(¯o_spans), + ]; + let authored_body: Option> = (interpolated.as_str() == &**authored_query) + .then_some(AuthoredQuery { + query: authored_query, + macro_sql: ¯o_sql, + passes: &authored_passes, + }); let opt_out_model: OptOutModel<'_, 'py> = OptOutModel { name, file_path, @@ -716,6 +726,7 @@ impl NativeModelLoop { query_sql: &query_sql, relative_path, contents, + authored: authored_body, }; let legacy_gate = match self.route { AnalysisRoute::Legacy => Some(self.analysis(context, &opt_out_model, true)?), @@ -797,6 +808,7 @@ impl NativeModelLoop { query_sql: &query_sql, relative_path, contents, + authored: authored_body, }; self.analysis(context, &expanded_model, scanned.invalid_calls.is_empty())? } diff --git a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs index 4020b6e5f..322134ab1 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/input_loops/model_loop_opt_out.rs @@ -4,15 +4,18 @@ use std::path::Path; use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyErr, PyResult, Python}; use pyo3::types::{PyBool, PyBoolMethods, PyDict, PyDictMethods, PyList, PyString, PyTuple}; +use sqlbuild_analysis::analysis_route::main::aligned_offset::aligned_offset; use sqlbuild_analysis::analysis_route::main::native_gap::native_gap; use sqlbuild_analysis::analysis_route::main::skipped_analysis::skipped_analysis; use sqlbuild_analysis::analysis_route::main::unavailable_analysis::unavailable_analysis; -use sqlbuild_analysis::analysis_route::models::{AnalysisRoute, LocatedGap}; +use sqlbuild_analysis::analysis_route::models::{AnalysisRoute, GapPosition, GapSite, LocatedGap}; use sqlbuild_analysis::analysis_route::types::NativeDialect; use sqlbuild_analysis::assembly::project::models::{SyntaxCheck, SyntaxMode}; +use sqlbuild_analysis::compiled_project::main::authored_model_offset::authored_model_offset; use sqlbuild_analysis::compiled_project::main::opt_out_position::opt_out_position; +use sqlbuild_analysis::compiled_project::main::text_position::text_position; use sqlbuild_analysis::compiled_project::models::ModelLoopSettings; -use sqlbuild_analysis::compiled_project::models::TextPosition; +use sqlbuild_analysis::compiled_project::models::{SplicedSpan, TextPosition}; use sqlbuild_attachments::cursor_intrinsics::main::replace_intrinsics::replace_intrinsics; use crate::bindings::_helpers::input_loops::model_loop::{ @@ -20,6 +23,7 @@ use crate::bindings::_helpers::input_loops::model_loop::{ }; use crate::bindings::_helpers::input_loops::model_loop_inputs::{HOOK_KEYS, present}; use crate::bindings::_helpers::project_assembly::resources::{syntax_error_message, syntax_valid}; +use crate::bindings::_helpers::project_assembly::syntax_validation::sql_entry_label; use crate::bindings::_helpers::records::construct::{new_helped_error, new_record_with}; use crate::bindings::_helpers::records::fields::field as native_field; use crate::bindings::_helpers::records::ids::{ErrorId, RecordId}; @@ -42,6 +46,7 @@ const LINE_FIELD: &str = "line"; const COLUMN_FIELD: &str = "column"; const HELP_ARGUMENT: &str = "help"; const MODEL_RESOURCE: &str = "model"; +const RELATIVE_PATH_ATTRIBUTE: &str = "relative_path"; /// Where the opt-out was authored, or a check the attachment still answers. pub(crate) enum OptOut<'py> { @@ -58,6 +63,32 @@ pub(crate) struct OptOutModel<'a, 'py> { pub(crate) query_sql: &'a str, pub(crate) relative_path: &'a str, pub(crate) contents: &'a str, + /// How `query_sql` maps back to the model file; none when interpolation rewrote the query. + pub(crate) authored: Option>, +} + +/// The query as the model file holds it, as macros expanded it, and the passes between them. +#[derive(Clone, Copy)] +pub(crate) struct AuthoredQuery<'a> { + pub(crate) query: &'a str, + pub(crate) macro_sql: &'a str, + /// The declaration and macro expansion passes, in that order. + pub(crate) passes: &'a [Vec], +} + +/// One statement a native parser must read, with the site P026 names it by. +struct SiteCheck { + check: SyntaxCheck, + /// The hook, such as `post_hooks[0] sql("record_refresh")`; none for the model's query. + label: Option, + path: String, +} + +/// A hook value being walked: its key, its file and the label its statements are named by. +struct HookSite<'a> { + hook_name: &'a str, + path: String, + label: Option, } /// Whether the opt-out is rejected, and where it was authored. @@ -74,9 +105,13 @@ pub(crate) fn rejected_opt_out<'py>( { return Ok(OptOut::Accepted); } - let checks: Vec = model_checks(context, model)?; + let checks: Vec = model_checks(context, model)?; let readable: bool = match model_loop.route { - AnalysisRoute::Legacy => syntax_valid(model.config.py(), GENERIC_DIALECT, &checks)?, + AnalysisRoute::Legacy => { + let statements: Vec = + checks.iter().map(|site| site.check.clone()).collect(); + syntax_valid(model.config.py(), GENERIC_DIALECT, &statements)? + } AnalysisRoute::Native(dialect) => first_gap(dialect, context, &checks)?.is_none(), AnalysisRoute::Unavailable => false, }; @@ -94,19 +129,49 @@ pub(crate) fn skip_unreadable( context: &LoopContext<'_, '_>, model: &OptOutModel<'_, '_>, ) -> PyResult { - let checks: Vec = model_checks(context, model)?; - let Some(gap) = first_gap(dialect, context, &checks)? else { + let checks: Vec = model_checks(context, model)?; + let Some((check, gap)) = first_gap(dialect, context, &checks)? else { return Ok(true); }; + let site: GapSite<'_> = GapSite { + label: check.label.as_deref(), + path: &check.path, + position: gap_position(model, check, &gap), + }; report_skip( settings, context, model, - skipped_analysis(model.name, dialect.name(), &gap), + skipped_analysis(model.name, dialect.name(), &gap, &site), )?; Ok(false) } +/// A hook's gap in its statement; the query's in the model file, when its text maps back there. +fn gap_position(model: &OptOutModel<'_, '_>, check: &SiteCheck, gap: &LocatedGap) -> GapPosition { + let Some(offset) = gap.source_offset else { + return GapPosition::Analysed; + }; + if check.label.is_some() { + return GapPosition::Statement(text_position(&check.check.sql, offset)); + } + model + .authored + .and_then(|authored| { + let query: usize = aligned_offset(&check.check.sql, offset, model.query_sql)?; + let expanded: usize = aligned_offset(model.query_sql, query, authored.macro_sql)?; + authored_model_offset( + model.contents, + authored.query, + (authored.macro_sql, expanded), + authored.passes, + ) + }) + .map_or(GapPosition::Analysed, |file| { + GapPosition::File(text_position(model.contents, file)) + }) +} + /// P026 for a model whose dialect no native parser reads; it warns, or raises, and is not analysed. pub(crate) fn skip_unavailable( settings: ModelLoopSettings, @@ -173,34 +238,47 @@ fn report_skip( fn model_checks( context: &LoopContext<'_, '_>, model: &OptOutModel<'_, '_>, -) -> PyResult> { - let mut statements: Vec = vec![analysis_sql(context, model)?]; +) -> PyResult> { + let mut statements: Vec<(Option, String, String)> = vec![( + None, + model.file_path.to_owned(), + analysis_sql(context, model)?, + )]; for key in HOOK_KEYS { if let Some(hooks) = model.values.get_item(key)? { - statements.extend(hook_statements(context, &hooks)?); + let site: HookSite<'_> = HookSite { + hook_name: key, + path: model.file_path.to_owned(), + label: None, + }; + statements.extend(hook_statements(context, &hooks, site)?); } } let placeholders: Vec<(String, String)> = placeholders(model.values)?; Ok(statements .into_iter() - .map(|sql| SyntaxCheck { - sql, - placeholders: placeholders.clone(), + .map(|(label, path, sql)| SiteCheck { + check: SyntaxCheck { + sql, + placeholders: placeholders.clone(), + }, + label, + path, }) .collect()) } -/// The native parser's first gap across `checks`; none when it reads each one whole. -fn first_gap( +/// The native parser's first gap across `checks`, with its check; none when it reads each whole. +fn first_gap<'a>( dialect: &dyn NativeDialect, context: &LoopContext<'_, '_>, - checks: &[SyntaxCheck], -) -> PyResult> { - for check in checks { - if let Some(gap) = native_gap(dialect, check) + checks: &'a [SiteCheck], +) -> PyResult> { + for site in checks { + if let Some(gap) = native_gap(dialect, &site.check) .map_err(|message| compile_error(context.classes, &message, None))? { - return Ok(Some(gap)); + return Ok(Some((site, gap))); } } Ok(None) @@ -262,21 +340,53 @@ fn opted_out(values: &Bound<'_, PyDict>) -> PyResult { Ok(false) } -/// `hook_sql_statements`: strings, SQL hook statements, and nested lists or tuples, in order. +/// `hook_sql_statements` as `(label, file, SQL)`, labelled as `validate_hook_sql_syntax` names them. fn hook_statements( context: &LoopContext<'_, '_>, value: &Bound<'_, PyAny>, -) -> PyResult> { + site: HookSite<'_>, +) -> PyResult, String, String)>> { + let sql_hook_entry: &Bound<'_, PyAny> = &context.classes.sql_hook_entry; if let Ok(text) = value.downcast::() { - return Ok(vec![text.extract()?]); + let label: String = site.label.unwrap_or_else(|| site.hook_name.to_owned()); + return Ok(vec![(Some(label), site.path, text.extract()?)]); } - if value.is_instance(&context.classes.sql_hook_entry)? { - return hook_statements(context, &native_field(value, STATEMENT_ATTRIBUTE)?); + if value.is_instance(sql_hook_entry)? { + let label: String = match site.label { + Some(label) => label, + None => sql_entry_label(site.hook_name, value)?, + }; + let relative_path: Bound<'_, PyAny> = native_field(value, RELATIVE_PATH_ATTRIBUTE)?; + let path: String = if relative_path.is_truthy()? { + relative_path.str()?.to_string() + } else { + site.path + }; + let inner: HookSite<'_> = HookSite { + hook_name: site.hook_name, + path, + label: Some(label), + }; + return hook_statements(context, &native_field(value, STATEMENT_ATTRIBUTE)?, inner); } - let mut statements: Vec = Vec::new(); + let mut statements: Vec<(Option, String, String)> = Vec::new(); if value.is_instance_of::() || value.is_instance_of::() { - for item in value.try_iter()? { - statements.extend(hook_statements(context, &item?)?); + for (index, item) in value.try_iter()?.enumerate() { + let item: Bound<'_, PyAny> = item?; + let prefix: String = format!("{}[{index}]", site.hook_name); + let label: Option = if item.is_instance(sql_hook_entry)? { + Some(sql_entry_label(&prefix, &item)?) + } else if item.is_instance_of::() { + Some(format!("{prefix} inline_sql(\"...\")")) + } else { + None + }; + let inner: HookSite<'_> = HookSite { + hook_name: site.hook_name, + path: site.path.clone(), + label, + }; + statements.extend(hook_statements(context, &item, inner)?); } } Ok(statements) diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/syntax_validation.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/syntax_validation.rs index b36fb23fb..1a14e9d13 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/syntax_validation.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/syntax_validation.rs @@ -64,7 +64,8 @@ fn parse_error(context: &SyntaxContext<'_, '_>, sql: &str) -> PyResult) -> PyResult { +/// How errors name a SQL hook entry: `{prefix} sql("name")`, or `{prefix} inline_sql("...")`. +pub(crate) fn sql_entry_label(prefix: &str, entry: &Bound<'_, PyAny>) -> PyResult { let name: Bound<'_, PyAny> = native_field(entry, "name")?; Ok(if name.is_none() { format!("{prefix} inline_sql(\"...\")") From 4409f42fae12a5ba02fbe820214a794d5a66a894 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:34:52 +0000 Subject: [PATCH 043/160] feat(analysis): let a native dialect respell session column types NativeDialect gains session_type, the identity by default: the session schema and revalidation give each known column type to the parser in the spelling the dialect reads, for dialects whose parser cannot read every type SQLBuild keeps. --- .../analysis_route/_helpers/revalidation.rs | 7 ++++--- .../src/analysis_route/types.rs | 5 +++++ .../analysis_session/_helpers/mappings.rs | 20 ++++++++++++++----- .../analysis_session/_helpers/session.rs | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs index 44d397612..96530c5d2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs @@ -49,7 +49,7 @@ pub(crate) fn revised( .map(|(relation, columns)| { ( relation.clone(), - schema_columns(relation, columns, poisoned), + schema_columns(dialect, relation, columns, poisoned), ) }) .collect::>>(), @@ -80,8 +80,9 @@ pub(crate) fn unrevised(model: &RecoveryModel) -> Vec { .collect() } -/// `relation`'s columns as the session bound them, each poisoned or unknown one untyped. +/// `relation`'s columns as the session bound them and `dialect` reads them, poisoned ones untyped. fn schema_columns( + dialect: &dyn NativeDialect, relation: &str, columns: &Pairs, poisoned: &[(String, String)], @@ -96,7 +97,7 @@ fn schema_columns( schema.push(SchemaColumn { name: name.clone(), quoted: false, - data_type: (!untyped).then(|| data_type.clone()), + data_type: (!untyped).then(|| dialect.session_type(data_type)), nullability: Nullability::Unknown, }); } diff --git a/crates/sqlbuild-analysis/src/analysis_route/types.rs b/crates/sqlbuild-analysis/src/analysis_route/types.rs index d28a0b24f..d22d4c9a3 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/types.rs @@ -29,6 +29,11 @@ pub trait NativeDialect: Sync { /// A type as written in this dialect, as SQLBuild compares it; `None` when unknown or refused. fn normalize_type(&self, spelling: &str) -> Option; + /// A session column type, spelled as SQLBuild keeps it, as this dialect's parser reads it. + fn session_type(&self, spelling: &str) -> String { + spelling.to_owned() + } + /// The one statement of `sql` when the parser reads it whole: no gap, and not raw text. fn read_whole(&self, sql: &str) -> Result { let parsed: Parsed = self.parse(sql)?; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs index d34f769e5..fc2bdaeb8 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs @@ -6,6 +6,7 @@ use std::sync::OnceLock; use sha2::{Digest, Sha256}; use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; +use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::{DIALECT_ALIASES, UNKNOWN_TYPE}; use crate::assembly::analysis_session::models::{ModelRequest, SessionSchema}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -241,27 +242,36 @@ pub(crate) fn waves(producers: &[Vec]) -> Option>> { } /// Each relation with a known shape, its columns typed and with the nullability the session knows. -pub(crate) fn session_schema(schema: &Shapes, nullability: &ShapeTable) -> SessionSchema { +pub(crate) fn session_schema( + dialect: &dyn NativeDialect, + schema: &Shapes, + nullability: &ShapeTable, +) -> SessionSchema { let relations: HashMap> = schema .iter() .filter(|(_, columns)| !columns.is_empty()) .map(|(relation, columns)| { - let typed: Vec = typed_columns(columns, nullability.get(relation)); + let typed: Vec = + typed_columns(dialect, columns, nullability.get(relation)); (relation.clone(), typed) }) .collect(); SessionSchema { relations } } -/// Each column with its known type and the nullability `known` spells for it. -fn typed_columns(columns: &Pairs, known: Option<&Pairs>) -> Vec { +/// Each column with its known type as `dialect` reads it, and the nullability `known` spells. +fn typed_columns( + dialect: &dyn NativeDialect, + columns: &Pairs, + known: Option<&Pairs>, +) -> Vec { columns .iter() .map(|(name, data_type)| SchemaColumn { name: name.clone(), quoted: false, data_type: (!data_type.is_empty() && data_type != UNKNOWN_TYPE) - .then(|| data_type.clone()), + .then(|| dialect.session_type(data_type)), nullability: column_nullability(known, name), }) .collect() diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 4e49c054a..97dcb6abc 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -337,7 +337,7 @@ impl AnalysisSession { .map(|position| { let model: &ModelRequest = &request.models[models[*position]]; let schema: Shapes = schemas[*position].clone(); - let relations: SessionSchema = session_schema(&schema, nullability); + let relations: SessionSchema = session_schema(dialect, &schema, nullability); model_outcome(dialect, model, (schema, &relations), &declared) }) .collect() From bfa1b3003868d471841f932d0e88a61f4ba2aa45 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:46:13 +0000 Subject: [PATCH 044/160] WIP (not shipping): BigQuery's arm on the NativeDialect trait, pinned to SQLQuack main edfa30b2 --- Cargo.lock | 24 +- Cargo.toml | 7 +- crates/sqlbuild-analysis/Cargo.toml | 1 + .../_helpers/bigquery_dialect.rs | 339 ++++++++++++++ .../src/analysis_route/_helpers/mod.rs | 1 + .../src/analysis_route/constants.rs | 44 +- .../src/analysis_route/models.rs | 4 + .../src/analysis_route/tests.rs | 2 + .../tests/test_bigquery_dialect.rs | 413 ++++++++++++++++++ .../analysis_route/tests/test_native_gap.rs | 5 +- .../src/analysis_route/tests/test_types.rs | 22 +- 11 files changed, 849 insertions(+), 13 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs diff --git a/Cargo.lock b/Cargo.lock index 7d467327d..c1a769a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4144,6 +4144,7 @@ dependencies = [ "sqlclean", "sqlquack-semantics", "sqlquack-syntax", + "sqlquack-syntax-bigquery", "sqlquack-syntax-postgres", ] @@ -4500,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4510,19 +4511,21 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ + "regex", "serde", "serde_json", "sqlquack-core", "sqlquack-snowflake", "sqlquack-syntax", + "stacker", ] [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "sqlquack-core", @@ -4531,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4541,10 +4544,19 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "sqlquack-syntax-bigquery" +version = "0.2.0" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" +dependencies = [ + "sqlquack-syntax", + "stacker", +] + [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "proc-macro2", "quote", @@ -4554,7 +4566,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "sqlquack-syntax", "stacker", diff --git a/Cargo.toml b/Cargo.toml index c4475e838..5115ee765 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,10 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-bigquery = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 95c643ebd..322ba3bc4 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -28,6 +28,7 @@ sqlbuild-sqltext.workspace = true sqlclean.workspace = true sqlquack-semantics.workspace = true sqlquack-syntax.workspace = true +sqlquack-syntax-bigquery.workspace = true sqlquack-syntax-postgres.workspace = true [lints] diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs new file mode 100644 index 000000000..4e1685341 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs @@ -0,0 +1,339 @@ +//! BigQuery's native module: SQLQuack's GoogleSQL parser and the BigQuery semantics profile. + +use sqlquack_semantics::dialects::constants::BIGQUERY; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax::{DataType, Expression}; +use sqlquack_syntax_bigquery::{Parsed, StrictFailure, parse_strict}; + +use crate::analysis_route::constants::{ + BIGNUMERIC_DIGITS, BIGQUERY_DIALECT, BIGQUERY_STUB_PREFIX, CAST_TYPE_PREFIX, DECIMAL_RADIX, + NUMERIC_DIGITS, POLYGLOT_MAX_DROPPED, POLYGLOT_NESTED_TYPE_NAMES, + POLYGLOT_TOP_LEVEL_TYPE_NAMES, RANGE_ELEMENT_TYPES, RANGE_TYPE_NAME, UNSCANNED_CONSTRUCT, +}; +use crate::analysis_route::models::{BigQueryDialect, ParserGap}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeDialect, TypeFamily}; + +impl NativeDialect for BigQueryDialect { + fn name(&self) -> &'static str { + BIGQUERY_DIALECT + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql).map_err(|failure| match failure { + StrictFailure::Parse(error) => ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }, + StrictFailure::Uncovered { position } => ParserGap { + construct: UNSCANNED_CONSTRUCT.to_owned(), + message: UNSCANNED_CONSTRUCT.to_owned(), + offset: position, + syntax: true, + }, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &BIGQUERY + } + + fn normalize_type(&self, spelling: &str) -> Option { + let sql: String = format!("{CAST_TYPE_PREFIX}{spelling})"); + let parsed: Parsed = match parse_strict(&sql) { + Ok(parsed) => parsed, + Err(StrictFailure::Parse(_) | StrictFailure::Uncovered { .. }) => return None, + }; + let ([Expression::Select(select)], []) = + (parsed.statements.as_slice(), parsed.gaps.as_slice()) + else { + return None; + }; + let [Expression::Cast(cast)] = select.expressions.as_slice() else { + return None; + }; + let ends_the_query: bool = cast + .span + .0 + .is_some_and(|span| span.end == sql.chars().count()); + (ends_the_query && native_type(&cast.to).is_some()) + .then(|| polyglot_type(spelling, &cast.to)) + } + + fn stub_prefix(&self) -> &'static str { + BIGQUERY_STUB_PREFIX + } +} + +/// A type BigQuery's dry run accepts, as BigQuery names it; `None` for one it refuses. +fn native_type(data_type: &DataType) -> Option { + Some(match data_type { + DataType::BigInt { length: None } => "INT64".to_owned(), + DataType::Decimal { precision, scale } => { + let given: Vec = [*precision, *scale].into_iter().flatten().collect(); + decimal("NUMERIC", &given, NUMERIC_DIGITS)? + } + DataType::Double { + precision: None, + scale: None, + } => "FLOAT64".to_owned(), + DataType::Boolean => "BOOLEAN".to_owned(), + DataType::String { length } => sized("STRING", *length)?, + DataType::VarBinary { length } => sized("BYTES", *length)?, + DataType::Date => "DATE".to_owned(), + DataType::Time { + precision: None, + timezone: false, + } => "TIME".to_owned(), + DataType::Timestamp { + precision: None, + timezone: false, + } => "DATETIME".to_owned(), + DataType::Timestamp { + precision: None, + timezone: true, + } => "TIMESTAMP".to_owned(), + DataType::Interval { + unit: None, + to: None, + } => "INTERVAL".to_owned(), + DataType::Json => "JSON".to_owned(), + DataType::Geography { + subtype: None, + srid: None, + } => "GEOGRAPHY".to_owned(), + DataType::Array { + element_type, + dimension: None, + } => format!("ARRAY<{}>", native_type(element_type)?), + DataType::Struct { + fields, + nested: true, + } => { + let fields: Vec = fields + .iter() + .map(|field| { + let spelt: String = native_type(&field.data_type)?; + Some(format!("{}{spelt}", field.name.to_ascii_uppercase())) + }) + .collect::>()?; + format!("STRUCT<{}>", fields.join(",")) + } + DataType::Custom { name } => custom_type(&name.replace(' ', "").to_ascii_uppercase())?, + _ => return None, + }) +} + +/// BIGNUMERIC, RANGE and the MAX-parameterised types, which the parser keeps by name. +fn custom_type(compact: &str) -> Option { + if let Some(element) = compact + .strip_prefix("RANGE<") + .and_then(|rest| rest.strip_suffix('>')) + { + return RANGE_ELEMENT_TYPES + .contains(&element) + .then(|| compact.to_owned()); + } + if ["BIGNUMERIC", "BIGNUMERIC(MAX)", "STRING(MAX)", "BYTES(MAX)"].contains(&compact) { + return Some(compact.to_owned()); + } + if let Some(scale) = compact + .strip_prefix("BIGNUMERIC(MAX,") + .and_then(|rest| rest.strip_suffix(')')) + { + return parameter(scale) + .filter(|scale| *scale <= BIGNUMERIC_DIGITS.0) + .map(|_| compact.to_owned()); + } + let inner: &str = compact + .strip_prefix("BIGNUMERIC(") + .and_then(|rest| rest.strip_suffix(')'))?; + let given: Vec = inner.split(',').map(parameter).collect::>()?; + decimal("BIGNUMERIC", &given, BIGNUMERIC_DIGITS) +} + +/// A decimal type within BigQuery's scale and integer-digit bounds. +fn decimal( + base: &str, + given: &[u32], + (max_scale, max_integer_digits): (u32, u32), +) -> Option { + let (precision, scale): (u32, u32) = match *given { + [] => return Some(base.to_owned()), + [precision] => (precision, 0), + [precision, scale] => (precision, scale), + _ => return None, + }; + let valid: bool = + scale <= max_scale && precision >= scale.max(1) && precision <= scale + max_integer_digits; + let shown: Vec = given.iter().map(u32::to_string).collect(); + valid.then(|| format!("{base}({})", shown.join(","))) +} + +/// A type parameter written in digits. +fn parameter(text: &str) -> Option { + if text.is_empty() { + return None; + } + text.chars().try_fold(0_u32, |value, character| { + let digit: u32 = character.to_digit(DECIMAL_RADIX)?; + value.checked_mul(DECIMAL_RADIX)?.checked_add(digit) + }) +} + +/// A STRING or BYTES type, whose length when given is at least 1. +fn sized(base: &str, length: Option) -> Option { + match length { + None => Some(base.to_owned()), + Some(0) => None, + Some(length) => Some(format!("{base}({length})")), + } +} + +/// The type Polyglot gives `spelling`: its name, the text fallback's parameters, and its family. +fn polyglot_type(spelling: &str, data_type: &DataType) -> NormalizedType { + let name: String = polyglot_name(spelling, data_type, true); + let read: NormalizedType = normalize_text_type(&name, Some(TypeDialect::BigQuery)); + let family: TypeFamily = match data_type { + DataType::Array { .. } | DataType::Struct { .. } + if !polyglot_reads_as_text(spelling, data_type) => + { + TypeFamily::Other + } + DataType::Timestamp { timezone: true, .. } => TypeFamily::Timestamp, + _ => read.family, + }; + NormalizedType { + normalized_name: name, + family, + ..read + } +} + +/// Whether Polyglot cannot parse nested `spelling`: a RANGE closed by `>>`, or an unnamed RANGE field. +fn polyglot_reads_as_text(spelling: &str, data_type: &DataType) -> bool { + let upper: String = spelling.to_ascii_uppercase(); + let closed_by_shift: bool = upper.match_indices(RANGE_TYPE_NAME).any(|(at, _)| { + let rest: &str = upper[at + RANGE_TYPE_NAME.len()..].trim_start(); + rest.starts_with('<') + && rest + .find('>') + .is_some_and(|close| rest[close + 1..].starts_with('>')) + }); + closed_by_shift || unnamed_range_field(data_type) +} + +/// Whether a STRUCT within `data_type` has an unnamed RANGE field. +fn unnamed_range_field(data_type: &DataType) -> bool { + match data_type { + DataType::Array { element_type, .. } => unnamed_range_field(element_type), + DataType::Struct { fields, .. } => fields.iter().any(|field| { + let range: bool = matches!( + &field.data_type, + DataType::Custom { name } if compact(name).starts_with(RANGE_TYPE_NAME) + ); + field.name.is_empty() && range || unnamed_range_field(&field.data_type) + }), + _ => false, + } +} + +/// The name Polyglot gives `spelling`, a type BigQuery accepts parsed as `data_type`. +fn polyglot_name(spelling: &str, data_type: &DataType, top: bool) -> String { + let text: &str = spelling.trim(); + let inner = || { + let open: usize = text.find('<').map_or(text.len(), |at| at + 1); + let close: usize = text.rfind('>').unwrap_or(text.len()).max(open); + &text[open..close] + }; + match data_type { + DataType::Array { element_type, .. } => { + format!("ARRAY<{}>", polyglot_name(inner(), element_type, false)) + } + DataType::Struct { fields, .. } => { + let named: Vec = top_level_fields(inner()) + .into_iter() + .zip(fields) + .map(|(field_text, field)| { + if field.name.is_empty() && field_text.contains('(') { + return compact(field_text); + } + if field.name.is_empty() { + return polyglot_name(field_text, &field.data_type, false); + } + let (name, rest) = split_field_name(field_text.trim_start()); + format!( + "{}{}", + compact(name), + polyglot_name(rest, &field.data_type, false) + ) + }) + .collect(); + format!("STRUCT<{}>", named.join(",")) + } + _ => { + let spelt: String = compact(text); + let base_end: usize = spelt.find(['(', '<']).unwrap_or(spelt.len()); + let (base, parameters) = spelt.split_at(base_end); + let names: &[(&str, &str)] = if top { + POLYGLOT_TOP_LEVEL_TYPE_NAMES + } else { + POLYGLOT_NESTED_TYPE_NAMES + }; + let parameters: String = match parameters.strip_prefix("(MAX") { + Some(rest) if top && POLYGLOT_MAX_DROPPED.contains(&base) => rest + .strip_prefix(',') + .map_or(String::new(), |scale| format!("({scale}")), + _ => parameters.to_owned(), + }; + match names.iter().find(|(alias, _)| *alias == base) { + Some((_, name)) => format!("{name}{parameters}"), + None => format!("{base}{parameters}"), + } + } + } +} + +/// Upper case without whitespace, as Polyglot's generated type text compares. +fn compact(text: &str) -> String { + text.chars() + .filter(|character| !character.is_whitespace()) + .collect::() + .to_ascii_uppercase() +} + +/// A STRUCT's field texts: its commas outside brackets, parentheses and quoted names. +fn top_level_fields(text: &str) -> Vec<&str> { + let mut fields: Vec<&str> = Vec::new(); + let (mut depth, mut quote, mut start): (usize, Option, usize) = (0, None, 0); + for (at, character) in text.char_indices() { + match (quote, character) { + (Some(open), _) if character == open => quote = None, + (Some(_), _) => {} + (None, '`' | '\'' | '"') => quote = Some(character), + (None, '<' | '(') => depth += 1, + (None, '>' | ')') => depth = depth.saturating_sub(1), + (None, ',') if depth == 0 => { + fields.push(&text[start..at]); + start = at + 1; + } + _ => {} + } + } + fields.push(&text[start..]); + fields +} + +/// A named field's name, backquoted or a word, and its type text. +fn split_field_name(text: &str) -> (&str, &str) { + let end: usize = match text.strip_prefix('`') { + Some(rest) => rest.find('`').map_or(text.len(), |at| at + 2), + None => text + .find(|character: char| !(character.is_ascii_alphanumeric() || character == '_')) + .unwrap_or(text.len()), + }; + text.split_at(end) +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 279c10ae9..c0c03cfe6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod bigquery_dialect; pub(crate) mod clickhouse_types; pub(crate) mod located_gap; pub(crate) mod marker_names; diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..c3f8120c1 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,9 +1,51 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. +use crate::analysis_route::models::BigQueryDialect; use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. -pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = + &[(BIGQUERY_DIALECT, &BigQueryDialect)]; +/// BigQuery's analysis dialect name. +pub(crate) const BIGQUERY_DIALECT: &str = "bigquery"; +/// The stub prefix BigQuery reads as one unquoted name. +pub(crate) const BIGQUERY_STUB_PREFIX: &str = "__sqlbuild_"; +/// The construct of a byte the BigQuery scanner reads as no token, blank or comment. +pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; +/// The query a type spelling completes, with `)`, so BigQuery's parser reads it as a cast target. +pub(crate) const CAST_TYPE_PREFIX: &str = "SELECT CAST(NULL AS "; +/// The name of BigQuery's RANGE type. +pub(crate) const RANGE_TYPE_NAME: &str = "RANGE"; +/// The element types of a BigQuery RANGE. +pub(crate) const RANGE_ELEMENT_TYPES: &[&str] = &["DATE", "DATETIME", "TIMESTAMP"]; +/// The base type parameters are written in. +pub(crate) const DECIMAL_RADIX: u32 = 10; +/// NUMERIC's largest scale and its most digits before the point. +pub(crate) const NUMERIC_DIGITS: (u32, u32) = (9, 29); +/// BIGNUMERIC's largest scale and its most digits before the point. +pub(crate) const BIGNUMERIC_DIGITS: (u32, u32) = (38, 38); +/// Polyglot's BigQuery names for a whole type's scalar spellings, which SQLBuild compares (D460). +pub(crate) const POLYGLOT_TOP_LEVEL_TYPE_NAMES: &[(&str, &str)] = &[ + ("BIGDECIMAL", "NUMERIC"), + ("BIGINT", "INT64"), + ("BOOL", "BOOLEAN"), + ("DECIMAL", "NUMERIC"), + ("INT", "INT64"), + ("INTEGER", "INT64"), + ("SMALLINT", "INT64"), + ("TIMESTAMP", "DATETIME"), +]; +/// Polyglot's BigQuery names for scalar spellings inside ARRAY and STRUCT. +pub(crate) const POLYGLOT_NESTED_TYPE_NAMES: &[(&str, &str)] = &[ + ("BOOL", "BOOLEAN"), + ("DECIMAL", "NUMERIC"), + ("INT", "INT64"), + ("INTEGER", "INT64"), + ("SMALLINT", "INT64"), + ("TIMESTAMP", "DATETIME"), +]; +/// The whole types whose MAX parameter Polyglot drops; BYTES and nested types keep it. +pub(crate) const POLYGLOT_MAX_DROPPED: &[&str] = &["BIGDECIMAL", "BIGNUMERIC", "STRING"]; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 838fd60c2..3a34582ee 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -145,3 +145,7 @@ impl LineageSchema for StandInSchema { .then_some(self.columns.as_slice()) } } + +/// BigQuery, whose SQL SQLQuack's GoogleSQL parser reads. +#[derive(Clone, Copy, Debug)] +pub struct BigQueryDialect; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index 242ac2a38..c027c4fae 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -1,5 +1,7 @@ #[path = "tests/helpers.rs"] mod helpers; +#[path = "tests/test_bigquery_dialect.rs"] +mod test_bigquery_dialect; #[path = "tests/test_clickhouse_type_match.rs"] mod test_clickhouse_type_match; #[path = "tests/test_native_dialect.rs"] diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs new file mode 100644 index 000000000..05bb71201 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs @@ -0,0 +1,413 @@ +use crate::analysis_route::main::native_gap::native_gap; +use crate::analysis_route::models::{BigQueryDialect, LocatedGap}; +use crate::analysis_route::tests::test_types::{ + NativeGapTestCase, NormalizeTypeTestCase, NormalizedTypeTestCase, ParseFailureTestCase, +}; +use crate::analysis_route::types::NativeDialect; +use crate::assembly::project::models::SyntaxCheck; +use crate::type_system::models::{NormalizedType, PythonInteger, TypeFamily}; + +#[test] +fn given_bigquery_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole_has_no_gap() { + let test_cases = [ + NativeGapTestCase { + description: "a query BigQuery reads whole", + sql: "SELECT 1 AS a FROM t WHERE region = @@@region", + placeholders: &[("region", "'eu'")], + expected_gap: None, + }, + NativeGapTestCase { + description: "a UDF call's stub reads as one name", + sql: "SELECT __udf(\"scaled_amount\")(amount, 2) AS scaled FROM t", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "cursor bounds read as stub calls", + sql: "SELECT * FROM t WHERE updated_at >= __cursor_start() AND updated_at < __cursor_end( )", + placeholders: &[], + expected_gap: None, + }, + NativeGapTestCase { + description: "a syntax error is located where GoogleSQL locates it", + sql: "SELECT 1 FROM FROM", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "Syntax error at or near 'FROM'".to_owned(), + line: 1, + column: 15, + }), + }, + NativeGapTestCase { + description: "a scripting statement is a named gap", + sql: "DECLARE x INT64 DEFAULT 1", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "declare".to_owned(), + line: 1, + column: 1, + }), + }, + NativeGapTestCase { + description: "a second statement is not read whole", + sql: "SELECT 1;\nSELECT 2", + placeholders: &[], + expected_gap: Some(LocatedGap { + construct: "more than one statement".to_owned(), + line: 2, + column: 9, + }), + }, + ]; + for test_case in test_cases { + let check = SyntaxCheck { + sql: test_case.sql.to_owned(), + placeholders: test_case + .placeholders + .iter() + .map(|(name, value)| ((*name).to_owned(), (*value).to_owned())) + .collect(), + }; + assert_eq!( + native_gap(&BigQueryDialect, &check), + Ok(test_case.expected_gap), + "{}", + test_case.description + ); + } +} + + +#[test] +fn given_a_bigquery_type_spelling_when_normalising_then_a_valid_one_keeps_polyglot_s_name() { + let test_cases = [ + NormalizeTypeTestCase { + description: "lower-case INT64", + spelling: "int64", + expected_name: Some("INT64"), + }, + NormalizeTypeTestCase { + description: "BYTEINT keeps Polyglot's spelling", + spelling: "BYTEINT", + expected_name: Some("BYTEINT"), + }, + NormalizeTypeTestCase { + description: "TINYINT keeps Polyglot's spelling", + spelling: "TINYINT", + expected_name: Some("TINYINT"), + }, + NormalizeTypeTestCase { + description: "SMALLINT is INT64", + spelling: "SMALLINT", + expected_name: Some("INT64"), + }, + NormalizeTypeTestCase { + description: "DECIMAL with parameters is NUMERIC", + spelling: "DECIMAL(38, 9)", + expected_name: Some("NUMERIC(38,9)"), + }, + NormalizeTypeTestCase { + description: "the widest NUMERIC precision", + spelling: "NUMERIC(29)", + expected_name: Some("NUMERIC(29)"), + }, + NormalizeTypeTestCase { + description: "NUMERIC precision past 29 is refused", + spelling: "NUMERIC(30)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "NUMERIC scale past 9 is refused", + spelling: "NUMERIC(10,10)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "a whole BIGDECIMAL is Polyglot's NUMERIC", + spelling: "BIGDECIMAL", + expected_name: Some("NUMERIC"), + }, + NormalizeTypeTestCase { + description: "BIGNUMERIC precision past 38 without a scale is refused", + spelling: "BIGNUMERIC(40)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "the widest BIGNUMERIC", + spelling: "BIGNUMERIC(76, 38)", + expected_name: Some("BIGNUMERIC(76,38)"), + }, + NormalizeTypeTestCase { + description: "BIGNUMERIC precision past 76 is refused", + spelling: "BIGNUMERIC(77,38)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "BOOL is BOOLEAN", + spelling: "BOOL", + expected_name: Some("BOOLEAN"), + }, + NormalizeTypeTestCase { + description: "a whole TIMESTAMP is Polyglot's DATETIME", + spelling: "TIMESTAMP", + expected_name: Some("DATETIME"), + }, + NormalizeTypeTestCase { + description: "DATETIME", + spelling: "DATETIME", + expected_name: Some("DATETIME"), + }, + NormalizeTypeTestCase { + description: "a padded STRING length", + spelling: "STRING( 10 )", + expected_name: Some("STRING(10)"), + }, + NormalizeTypeTestCase { + description: "a zero STRING length is refused", + spelling: "STRING(0)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "nested field names are upper case", + spelling: "array>", + expected_name: Some("ARRAY>"), + }, + NormalizeTypeTestCase { + description: "BIGINT keeps its spelling inside ARRAY", + spelling: "ARRAY", + expected_name: Some("ARRAY"), + }, + NormalizeTypeTestCase { + description: "TIMESTAMP inside ARRAY is DATETIME", + spelling: "ARRAY", + expected_name: Some("ARRAY"), + }, + NormalizeTypeTestCase { + description: "STRUCT renames INTEGER but keeps BIGDECIMAL", + spelling: "STRUCT", + expected_name: Some("STRUCT"), + }, + NormalizeTypeTestCase { + description: "an unnamed field with parameters keeps its spelling", + spelling: "STRUCT", + expected_name: Some("STRUCT"), + }, + NormalizeTypeTestCase { + description: "a whole STRING drops MAX", + spelling: "STRING(MAX)", + expected_name: Some("STRING"), + }, + NormalizeTypeTestCase { + description: "a whole BIGDECIMAL drops MAX and keeps its scale", + spelling: "BIGDECIMAL(MAX, 2)", + expected_name: Some("NUMERIC(2)"), + }, + NormalizeTypeTestCase { + description: "BYTES keeps MAX", + spelling: "BYTES(MAX)", + expected_name: Some("BYTES(MAX)"), + }, + NormalizeTypeTestCase { + description: "a nested STRING keeps MAX", + spelling: "ARRAY", + expected_name: Some("ARRAY"), + }, + NormalizeTypeTestCase { + description: "NUMERIC takes no MAX", + spelling: "NUMERIC(MAX)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "a RANGE of DATE", + spelling: "range", + expected_name: Some("RANGE"), + }, + NormalizeTypeTestCase { + description: "a RANGE of INT64 is refused", + spelling: "RANGE", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "TIMESTAMP takes no precision", + spelling: "TIMESTAMP(6)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "FLOAT is no BigQuery type", + spelling: "FLOAT", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "VARCHAR is no BigQuery type", + spelling: "VARCHAR(10)", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "a collated STRING is no cast target", + spelling: "STRING COLLATE 'und:ci'", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "a spelling that closes the cast early", + spelling: "INT64) FROM t WHERE (1", + expected_name: None, + }, + NormalizeTypeTestCase { + description: "a spelling that comments out the rest", + spelling: "INT64) -- ", + expected_name: None, + }, + ]; + for test_case in test_cases { + assert_eq!( + BigQueryDialect + .normalize_type(test_case.spelling) + .map(|normalized| normalized.normalized_name) + .as_deref(), + test_case.expected_name, + "{}", + test_case.description + ); + } +} + +/// Each expected type is the one Polyglot's `normalize_type(spelling, "bigquery")` gives. +#[test] +fn given_a_valid_bigquery_type_spelling_when_normalising_then_the_type_is_polyglot_s() { + let test_cases = [ + NormalizedTypeTestCase { + description: "TIMESTAMP is Polyglot's DATETIME of the timestamp family", + spelling: "TIMESTAMP", + expected_type: ("DATETIME", TypeFamily::Timestamp, [None, None, None]), + }, + NormalizedTypeTestCase { + description: "BOOL is Polyglot's BOOLEAN", + spelling: "BOOL", + expected_type: ("BOOLEAN", TypeFamily::Boolean, [None, None, None]), + }, + NormalizedTypeTestCase { + description: "TINYINT keeps its name in the integer family", + spelling: "TINYINT", + expected_type: ("TINYINT", TypeFamily::Integer, [None, None, None]), + }, + NormalizedTypeTestCase { + description: "a NUMERIC alias keeps its precision and scale", + spelling: "DECIMAL(38, 9)", + expected_type: ( + "NUMERIC(38,9)", + TypeFamily::Decimal, + [Some(38), Some(9), None], + ), + }, + NormalizedTypeTestCase { + description: "a sized STRING keeps its length", + spelling: "STRING( 10 )", + expected_type: ("STRING(10)", TypeFamily::String, [None, None, Some(10)]), + }, + NormalizedTypeTestCase { + description: "BYTES(MAX) is of no family Polyglot knows", + spelling: "BYTES(MAX)", + expected_type: ("BYTES(MAX)", TypeFamily::Other, [None, None, None]), + }, + NormalizedTypeTestCase { + description: "a top-level RANGE is read as text, so of the timestamp family", + spelling: "range", + expected_type: ( + "RANGE", + TypeFamily::Timestamp, + [None, None, None], + ), + }, + NormalizedTypeTestCase { + description: "an ARRAY is of no family", + spelling: "ARRAY>", + expected_type: ( + "ARRAY>", + TypeFamily::Other, + [None, None, None], + ), + }, + NormalizedTypeTestCase { + description: "a RANGE closed by >> fails Polyglot's parse, so is read as text", + spelling: "ARRAY>", + expected_type: ( + "ARRAY>", + TypeFamily::Timestamp, + [None, None, None], + ), + }, + NormalizedTypeTestCase { + description: "a RANGE closed by > > parses", + spelling: "ARRAY< RANGE< TIMESTAMP > >", + expected_type: ( + "ARRAY>", + TypeFamily::Other, + [None, None, None], + ), + }, + NormalizedTypeTestCase { + description: "a named RANGE field parses", + spelling: "STRUCT >", + expected_type: ( + "STRUCT>", + TypeFamily::Other, + [None, None, None], + ), + }, + NormalizedTypeTestCase { + description: "an unnamed RANGE field fails Polyglot's parse", + spelling: "STRUCT< RANGE< TIMESTAMP > >", + expected_type: ( + "STRUCT>", + TypeFamily::Timestamp, + [None, None, None], + ), + }, + ]; + let normalized = + |(name, family, [precision, scale, length]): (&str, TypeFamily, [Option; 3])| { + NormalizedType { + normalized_name: name.to_owned(), + family, + precision: precision.map(PythonInteger::from), + scale: scale.map(PythonInteger::from), + length: length.map(PythonInteger::from), + } + }; + for test_case in test_cases { + assert_eq!( + BigQueryDialect.normalize_type(test_case.spelling), + Some(normalized(test_case.expected_type)), + "{}", + test_case.description + ); + } +} + +#[test] +fn given_sql_bigquery_refuses_when_parsing_then_the_gap_says_whether_it_is_a_syntax_error() { + let test_cases = [ + ParseFailureTestCase { + description: "a syntax error, at its byte offset after a multi-byte character", + sql: "SELECT 'é' FROM FROM".to_owned(), + expected_syntax: true, + expected_offset: Some(17), + }, + ParseFailureTestCase { + description: "the nesting limit is not a syntax error", + sql: format!("SELECT {}1{}", "(".repeat(300), ")".repeat(300)), + expected_syntax: false, + expected_offset: None, + }, + ]; + for test_case in test_cases { + let gap = BigQueryDialect + .parse(&test_case.sql) + .expect_err(test_case.description); + assert_eq!( + (gap.syntax, test_case.expected_offset.map(|_| gap.offset)), + (test_case.expected_syntax, test_case.expected_offset), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index fc714b2bd..28469fffc 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -9,7 +9,7 @@ use crate::assembly::project::models::SyntaxCheck; use crate::compiled_project::models::TextPosition; #[test] -fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { +fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_legacy_path() { let test_cases = [ RouteTestCase { description: "postgres", @@ -34,7 +34,7 @@ fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { RouteTestCase { description: "bigquery", dialect: Some("bigquery"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "databricks", @@ -67,6 +67,7 @@ fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { } } + #[test] fn given_model_sql_when_probing_the_native_parser_then_its_gap_is_located_after_placeholders() { let test_cases = [ diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 382cb4133..e5854d91e 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -7,7 +7,7 @@ use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; use crate::analysis_route::models::{GapSite, LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; -use crate::type_system::models::NormalizedType; +use crate::type_system::models::{NormalizedType, TypeFamily}; pub(super) struct NativeGapTestCase { pub(super) description: &'static str, @@ -249,3 +249,23 @@ pub(super) struct NativeLineageTestCase { pub(super) placeholders: &'static [(&'static str, &'static str)], pub(super) expected_lines: &'static [&'static str], } + +pub(super) struct NormalizeTypeTestCase { + pub(super) description: &'static str, + pub(super) spelling: &'static str, + pub(super) expected_name: Option<&'static str>, +} + +pub(super) struct NormalizedTypeTestCase { + pub(super) description: &'static str, + pub(super) spelling: &'static str, + pub(super) expected_type: (&'static str, TypeFamily, [Option; 3]), +} + +pub(super) struct ParseFailureTestCase { + pub(super) description: &'static str, + pub(super) sql: String, + pub(super) expected_syntax: bool, + /// The byte offset of a syntax error; a limit has none worth comparing. + pub(super) expected_offset: Option, +} From eb9eba3991047aab3b6282918f3ef5ec01f470cd Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:36:22 +0000 Subject: [PATCH 045/160] build(analysis): pin SQLQuack at main 38be86ba --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbf760e52..4fffc29aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4502,7 +4502,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "serde", "serde_json", @@ -4512,7 +4512,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "regex", "serde", @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "serde", "sqlquack-core", @@ -4535,7 +4535,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "serde", "serde_json", @@ -4548,7 +4548,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "proc-macro2", "quote", @@ -4558,7 +4558,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-duckdb" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "sqlquack-syntax", "stacker", @@ -4567,7 +4567,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "sqlquack-syntax", "stacker", @@ -4576,7 +4576,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-sqlite" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=ed14b717466c2d619dfe057d6b6701797b69c2c2#ed14b717466c2d619dfe057d6b6701797b69c2c2" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=38be86ba9078355f135e6d5aa9e96950c7b21c7f#38be86ba9078355f135e6d5aa9e96950c7b21c7f" dependencies = [ "sqlquack-syntax", "stacker", @@ -4673,7 +4673,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5109,7 +5109,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 59b18ec7e..54d395df7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,12 +177,12 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "38be86ba9078355f135e6d5aa9e96950c7b21c7f" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } -sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } -sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "ed14b717466c2d619dfe057d6b6701797b69c2c2" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "38be86ba9078355f135e6d5aa9e96950c7b21c7f" } +sqlquack-syntax-duckdb = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "38be86ba9078355f135e6d5aa9e96950c7b21c7f" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "38be86ba9078355f135e6d5aa9e96950c7b21c7f" } +sqlquack-syntax-sqlite = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "38be86ba9078355f135e6d5aa9e96950c7b21c7f" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From 07d40d622c46436e365fe05564b9cfe1adc06af6 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 10:54:43 +0000 Subject: [PATCH 046/160] WIP (not shipping): align the BigQuery arm's tests with the native dialect trait The trait's shared read_whole places "more than one statement" at the start of the SQL for every native dialect, and the text fallback now takes a type's family from its top-level type, so RANGE and the complex types holding one read as Other on both routes. With BigQuery's module typing through normalize_text_type, its fensu exception no longer matches a fault and is removed. --- .../tests/test_bigquery_dialect.rs | 30 +++++++++++-------- .../analysis_route/tests/test_native_gap.rs | 1 - fensu.toml | 5 ---- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs index 05bb71201..ce335ffb2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs @@ -34,8 +34,11 @@ fn given_bigquery_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole placeholders: &[], expected_gap: Some(LocatedGap { construct: "Syntax error at or near 'FROM'".to_owned(), + message: "Syntax error at or near 'FROM'".to_owned(), line: 1, column: 15, + syntax: true, + source_offset: Some(14), }), }, NativeGapTestCase { @@ -44,8 +47,11 @@ fn given_bigquery_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole placeholders: &[], expected_gap: Some(LocatedGap { construct: "declare".to_owned(), + message: "declare".to_owned(), line: 1, column: 1, + syntax: false, + source_offset: Some(0), }), }, NativeGapTestCase { @@ -54,8 +60,11 @@ fn given_bigquery_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole placeholders: &[], expected_gap: Some(LocatedGap { construct: "more than one statement".to_owned(), - line: 2, - column: 9, + message: "more than one statement".to_owned(), + line: 1, + column: 1, + syntax: false, + source_offset: Some(0), }), }, ]; @@ -77,7 +86,6 @@ fn given_bigquery_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole } } - #[test] fn given_a_bigquery_type_spelling_when_normalising_then_a_valid_one_keeps_polyglot_s_name() { let test_cases = [ @@ -309,13 +317,9 @@ fn given_a_valid_bigquery_type_spelling_when_normalising_then_the_type_is_polygl expected_type: ("BYTES(MAX)", TypeFamily::Other, [None, None, None]), }, NormalizedTypeTestCase { - description: "a top-level RANGE is read as text, so of the timestamp family", + description: "a top-level RANGE is read as text, so its top-level type decides: no family", spelling: "range", - expected_type: ( - "RANGE", - TypeFamily::Timestamp, - [None, None, None], - ), + expected_type: ("RANGE", TypeFamily::Other, [None, None, None]), }, NormalizedTypeTestCase { description: "an ARRAY is of no family", @@ -327,11 +331,11 @@ fn given_a_valid_bigquery_type_spelling_when_normalising_then_the_type_is_polygl ), }, NormalizedTypeTestCase { - description: "a RANGE closed by >> fails Polyglot's parse, so is read as text", + description: "a RANGE closed by >> fails Polyglot's parse, so is read as text, where ARRAY decides the family", spelling: "ARRAY>", expected_type: ( "ARRAY>", - TypeFamily::Timestamp, + TypeFamily::Other, [None, None, None], ), }, @@ -354,11 +358,11 @@ fn given_a_valid_bigquery_type_spelling_when_normalising_then_the_type_is_polygl ), }, NormalizedTypeTestCase { - description: "an unnamed RANGE field fails Polyglot's parse", + description: "an unnamed RANGE field fails Polyglot's parse, so STRUCT decides the family", spelling: "STRUCT< RANGE< TIMESTAMP > >", expected_type: ( "STRUCT>", - TypeFamily::Timestamp, + TypeFamily::Other, [None, None, None], ), }, diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index 28469fffc..947eaf186 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -67,7 +67,6 @@ fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_leg } } - #[test] fn given_model_sql_when_probing_the_native_parser_then_its_gap_is_located_after_placeholders() { let test_cases = [ diff --git a/fensu.toml b/fensu.toml index 3fee255b7..c3109bfec 100644 --- a/fensu.toml +++ b/fensu.toml @@ -2933,11 +2933,6 @@ rule = "FFS201" path = "src/sqlbuild/orchestration_declarations/models/sensor_context.py" reason = "Only SensorContext lives here: invocation-local ctx.cursor assignment proposes progress, committed solely by native fenced completion. The package regression guards this declaration-only scope; class-symbol exceptions are unsupported." -[[targets.rust.rule_exceptions]] -rule = "FPRSL105" -path = "crates/sqlbuild-analysis/src/type_system/main/normalize_text_type.rs" -reason = "Native dialect modules in analysis_route type their spellings through this entry; each dialect's module lands in its own change." - [[targets.rust.rule_exceptions]] rule = "FPRSL105" path = "crates/sqlbuild-analysis/src/analysis_route/main/clickhouse_type_match.rs" From 9a38e05c5528061bb5d6134a4619dc1640b359ec Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:42:48 +0000 Subject: [PATCH 047/160] WIP (not shipping): read session types BigQuery cannot spell by their family The session keeps SQLBuild's spellings, such as TEXT for an expression source's string columns, VARCHAR(n) and DOUBLE, which BigQuery's parser cannot read, so every column downstream of them lost its type on the native route. BigQuery overrides #1369's NativeDialect::session_type to respell an unreadable spelling as its family's BigQuery type. Two BigQuery e2e cases held SQL that GoogleSQL refuses: WINDOW is reserved as an alias, and EXCEPT and INTERSECT need DISTINCT or ALL. --- .../_helpers/bigquery_dialect.rs | 17 +++++++ .../tests/test_bigquery_dialect.rs | 51 +++++++++++++++++++ .../src/analysis_route/tests/test_types.rs | 6 +++ .../main/compile/test_dialect_syntax.py | 2 +- ...st_set_operation_after_subquery_operand.py | 19 ++++++- 5 files changed, 93 insertions(+), 2 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs index 4e1685341..2721f571f 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs @@ -41,6 +41,23 @@ impl NativeDialect for BigQueryDialect { &BIGQUERY } + /// A spelling BigQuery cannot read, such as TEXT or DOUBLE, takes its family's BigQuery type. + fn session_type(&self, spelling: &str) -> String { + if self.normalize_type(spelling).is_some() { + return spelling.to_owned(); + } + let native: &str = match normalize_text_type(spelling, None).family { + TypeFamily::String => "STRING", + TypeFamily::Integer => "INT64", + TypeFamily::Float => "FLOAT64", + TypeFamily::Decimal => "NUMERIC", + TypeFamily::Boolean => "BOOL", + TypeFamily::Date => "DATE", + _ => spelling, + }; + native.to_owned() + } + fn normalize_type(&self, spelling: &str) -> Option { let sql: String = format!("{CAST_TYPE_PREFIX}{spelling})"); let parsed: Parsed = match parse_strict(&sql) { diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs index ce335ffb2..e2d7c8958 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs @@ -2,6 +2,7 @@ use crate::analysis_route::main::native_gap::native_gap; use crate::analysis_route::models::{BigQueryDialect, LocatedGap}; use crate::analysis_route::tests::test_types::{ NativeGapTestCase, NormalizeTypeTestCase, NormalizedTypeTestCase, ParseFailureTestCase, + SessionTypeTestCase, }; use crate::analysis_route::types::NativeDialect; use crate::assembly::project::models::SyntaxCheck; @@ -415,3 +416,53 @@ fn given_sql_bigquery_refuses_when_parsing_then_the_gap_says_whether_it_is_a_syn ); } } + +#[test] +fn given_a_session_type_when_bigquery_reads_it_then_spellings_it_cannot_read_take_their_family_type() + { + let test_cases = [ + SessionTypeTestCase { + description: "a BigQuery spelling is kept", + spelling: "NUMERIC(10, 2)", + expected_type: "NUMERIC(10, 2)", + }, + SessionTypeTestCase { + description: "Polyglot's DATETIME name is a BigQuery type, so it is kept", + spelling: "DATETIME", + expected_type: "DATETIME", + }, + SessionTypeTestCase { + description: "Polyglot's TEXT for string literals reads as STRING", + spelling: "TEXT", + expected_type: "STRING", + }, + SessionTypeTestCase { + description: "a sized VARCHAR reads as STRING", + spelling: "VARCHAR(12)", + expected_type: "STRING", + }, + SessionTypeTestCase { + description: "DOUBLE reads as FLOAT64", + spelling: "DOUBLE", + expected_type: "FLOAT64", + }, + SessionTypeTestCase { + description: "a zoned timestamp has no one BigQuery type, so stays unread", + spelling: "TIMESTAMPTZ", + expected_type: "TIMESTAMPTZ", + }, + SessionTypeTestCase { + description: "a MAP has no BigQuery type", + spelling: "MAP(VARCHAR, INTEGER)", + expected_type: "MAP(VARCHAR, INTEGER)", + }, + ]; + for test_case in test_cases { + assert_eq!( + BigQueryDialect.session_type(test_case.spelling), + test_case.expected_type, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index e5854d91e..1e110704c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -269,3 +269,9 @@ pub(super) struct ParseFailureTestCase { /// The byte offset of a syntax error; a limit has none worth comparing. pub(super) expected_offset: Option, } + +pub(super) struct SessionTypeTestCase { + pub(super) description: &'static str, + pub(super) spelling: &'static str, + pub(super) expected_type: &'static str, +} diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dialect_syntax.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dialect_syntax.py index 5d5e846f1..021315bea 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dialect_syntax.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dialect_syntax.py @@ -173,7 +173,7 @@ "periods": dialect_syntax_model( "Periods.", "SELECT CAST(NULL AS RANGE) AS period, " - "CAST(NULL AS RANGE) AS window", + "CAST(NULL AS RANGE) AS active_window", ), }, expected_exit_code=0, diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py index 1908970a7..9789e73ab 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py @@ -3,6 +3,7 @@ from __future__ import annotations import subprocess +from dataclasses import replace from pathlib import Path import pytest @@ -69,6 +70,22 @@ ) +def _from_query_models(adapter: str) -> tuple[SetOperationModel, ...]: + """The FROM-query models; BigQuery requires DISTINCT or ALL after EXCEPT and INTERSECT.""" + + if adapter != "bigquery": + return _FROM_QUERY_MODELS + return tuple( + replace( + model, + query_sql=model.query_sql.replace("\nEXCEPT\n", "\nEXCEPT DISTINCT\n").replace( + "\nINTERSECT\n", "\nINTERSECT DISTINCT\n" + ), + ) + for model in _FROM_QUERY_MODELS + ) + + @pytest.mark.parametrize( "test_case", [ @@ -233,7 +250,7 @@ def test_given_set_operation_after_subquery_operand_when_compiling_building_and_ FromQuerySetOperationTestCase( description=f"{adapter} FROM query before a set operation", adapter=adapter, - models=_FROM_QUERY_MODELS, + models=_from_query_models(adapter), expected_column_counts=( ("union_all_orders", 2), ("except_orders", 2), From 9cfc75394fe4439450d914f26eea52fb4b97e9e1 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 11:53:37 +0000 Subject: [PATCH 048/160] WIP (not shipping): let BigQuery casts name RANGE types SQLQuack's cast check knows a named type only through an engine family or the schema's known_type, and no family names RANGE, so a valid CAST(x AS RANGE) was blocked as B218 on the native route. BigQuery's analyse now reads the query against a schema that also knows every type BigQuery's type grammar reads; RANGE stays B218. The BigQuery set-operation models move into a constant, as fensu requires of test modules. --- .../_helpers/bigquery_dialect.rs | 53 ++++++++++++++++++- .../src/analysis_route/models.rs | 6 +++ .../tests/test_bigquery_dialect.rs | 50 ++++++++++++++++- ...st_set_operation_after_subquery_operand.py | 25 ++++----- 4 files changed, 116 insertions(+), 18 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs index 2721f571f..335f857d5 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/bigquery_dialect.rs @@ -2,15 +2,20 @@ use sqlquack_semantics::dialects::constants::BIGQUERY; use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_semantics::lineage::models::{NullabilityRule, SchemaColumn}; +use sqlquack_semantics::lineage::types::LineageSchema; use sqlquack_syntax::{DataType, Expression}; use sqlquack_syntax_bigquery::{Parsed, StrictFailure, parse_strict}; +use crate::analysis_route::_helpers::native_analysis::native_analysis; use crate::analysis_route::constants::{ BIGNUMERIC_DIGITS, BIGQUERY_DIALECT, BIGQUERY_STUB_PREFIX, CAST_TYPE_PREFIX, DECIMAL_RADIX, NUMERIC_DIGITS, POLYGLOT_MAX_DROPPED, POLYGLOT_NESTED_TYPE_NAMES, POLYGLOT_TOP_LEVEL_TYPE_NAMES, RANGE_ELEMENT_TYPES, RANGE_TYPE_NAME, UNSCANNED_CONSTRUCT, }; -use crate::analysis_route::models::{BigQueryDialect, ParserGap}; +use crate::analysis_route::models::{ + AnalysisInput, BigQueryDialect, BigQueryTypes, NativeAnalysis, ParserGap, +}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; use crate::type_system::models::{NormalizedType, TypeDialect, TypeFamily}; @@ -41,6 +46,22 @@ impl NativeDialect for BigQueryDialect { &BIGQUERY } + /// The query read against a schema that knows BigQuery's own types, which a cast may name. + fn analyse(&self, input: &AnalysisInput<'_>) -> Result { + let statement: Expression = self.read_whole(input.sql)?; + let types: BigQueryTypes<'_> = BigQueryTypes { + schema: input.schema, + }; + Ok(native_analysis( + self, + &statement, + &AnalysisInput { + schema: &types, + ..*input + }, + )) + } + /// A spelling BigQuery cannot read, such as TEXT or DOUBLE, takes its family's BigQuery type. fn session_type(&self, spelling: &str) -> String { if self.normalize_type(spelling).is_some() { @@ -85,6 +106,36 @@ impl NativeDialect for BigQueryDialect { } } +impl LineageSchema for BigQueryTypes<'_> { + fn columns(&self, relation: &str) -> Option<&[SchemaColumn]> { + self.schema.columns(relation) + } + + fn open_columns(&self, relation: &str) -> Option<&[SchemaColumn]> { + self.schema.open_columns(relation) + } + + fn function_type(&self, name: &str) -> Option<&str> { + self.schema.function_type(name) + } + + fn function_nullability(&self, name: &str) -> Option { + self.schema.function_nullability(name) + } + + fn known_function(&self, name: &str) -> bool { + self.schema.known_function(name) + } + + fn function_arities(&self, name: &str, canonical: &str) -> Option)>> { + self.schema.function_arities(name, canonical) + } + + fn known_type(&self, name: &str) -> bool { + self.schema.known_type(name) || BigQueryDialect.normalize_type(name).is_some() + } +} + /// A type BigQuery's dry run accepts, as BigQuery names it; `None` for one it refuses. fn native_type(data_type: &DataType) -> Option { Some(match data_type { diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 3a34582ee..c4e0073b0 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -149,3 +149,9 @@ impl LineageSchema for StandInSchema { /// BigQuery, whose SQL SQLQuack's GoogleSQL parser reads. #[derive(Clone, Copy, Debug)] pub struct BigQueryDialect; + +/// A query's schema that also knows each type BigQuery's type grammar reads, such as RANGE. +#[derive(Debug)] +pub(crate) struct BigQueryTypes<'a> { + pub(crate) schema: &'a dyn LineageSchema, +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs index e2d7c8958..cca308af9 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_bigquery_dialect.rs @@ -1,8 +1,10 @@ use crate::analysis_route::main::native_gap::native_gap; +use crate::analysis_route::models::NativeAnalysis; use crate::analysis_route::models::{BigQueryDialect, LocatedGap}; +use crate::analysis_route::tests::helpers::analysed; use crate::analysis_route::tests::test_types::{ - NativeGapTestCase, NormalizeTypeTestCase, NormalizedTypeTestCase, ParseFailureTestCase, - SessionTypeTestCase, + FindingTestCase, NativeGapTestCase, NormalizeTypeTestCase, NormalizedTypeTestCase, + ParseFailureTestCase, SessionTypeTestCase, }; use crate::analysis_route::types::NativeDialect; use crate::assembly::project::models::SyntaxCheck; @@ -466,3 +468,47 @@ fn given_a_session_type_when_bigquery_reads_it_then_spellings_it_cannot_read_tak ); } } + +#[test] +fn given_a_cast_to_a_bigquery_type_when_analysing_then_only_types_bigquery_lacks_are_b218() { + let test_cases = [ + FindingTestCase { + description: "RANGE is a BigQuery type the engine families do not name", + dialect: &BigQueryDialect, + sql: "SELECT CAST(NULL AS RANGE) AS period FROM orders", + extension_functions: &[], + expected_findings: &[], + }, + FindingTestCase { + description: "RANGE is a BigQuery type", + dialect: &BigQueryDialect, + sql: "SELECT CAST(NULL AS RANGE) AS active_window FROM orders", + extension_functions: &[], + expected_findings: &[], + }, + FindingTestCase { + description: "RANGE is no BigQuery type, so its cast stays B218, unplaced", + dialect: &BigQueryDialect, + sql: "SELECT CAST(NULL AS RANGE) AS span FROM orders", + extension_functions: &[], + expected_findings: &[("B218", None)], + }, + ]; + for test_case in test_cases { + let analysis: NativeAnalysis = analysed( + test_case.dialect, + test_case.sql, + test_case.extension_functions, + ); + let findings: Vec<(&str, Option<(usize, usize)>)> = analysis + .diagnostics + .iter() + .map(|row| (row.code.as_str(), row.line.zip(row.column))) + .collect(); + assert_eq!( + findings, test_case.expected_findings, + "{}", + test_case.description + ); + } +} diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py index 9789e73ab..73b0a3901 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_set_operation_after_subquery_operand.py @@ -69,21 +69,16 @@ ), ) - -def _from_query_models(adapter: str) -> tuple[SetOperationModel, ...]: - """The FROM-query models; BigQuery requires DISTINCT or ALL after EXCEPT and INTERSECT.""" - - if adapter != "bigquery": - return _FROM_QUERY_MODELS - return tuple( - replace( - model, - query_sql=model.query_sql.replace("\nEXCEPT\n", "\nEXCEPT DISTINCT\n").replace( - "\nINTERSECT\n", "\nINTERSECT DISTINCT\n" - ), - ) - for model in _FROM_QUERY_MODELS +# BigQuery requires DISTINCT or ALL after EXCEPT and INTERSECT. +_BIGQUERY_FROM_QUERY_MODELS: tuple[SetOperationModel, ...] = tuple( + replace( + model, + query_sql=model.query_sql.replace("\nEXCEPT\n", "\nEXCEPT DISTINCT\n").replace( + "\nINTERSECT\n", "\nINTERSECT DISTINCT\n" + ), ) + for model in _FROM_QUERY_MODELS +) @pytest.mark.parametrize( @@ -250,7 +245,7 @@ def test_given_set_operation_after_subquery_operand_when_compiling_building_and_ FromQuerySetOperationTestCase( description=f"{adapter} FROM query before a set operation", adapter=adapter, - models=_from_query_models(adapter), + models=_BIGQUERY_FROM_QUERY_MODELS if adapter == "bigquery" else _FROM_QUERY_MODELS, expected_column_counts=( ("union_all_orders", 2), ("except_orders", 2), From 09194f880805446e8f66140445a472c2dde4172d Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 12:51:05 +0000 Subject: [PATCH 049/160] WIP (not shipping): re-record the BigQuery entries of the generated contract and analysis goldens BigQuery now analyses natively, so only its entries move; every None, DuckDB, Postgres, Snowflake, Databricks and T-SQL entry is unchanged. contracts_generated (32 of 60 entries, only the bigquery key): 56 K002 findings (49 errors, 7 warnings) no longer appear and none is added. Each pairs a type GoogleSQL refuses, such as REAL, CHAR(3), VARIANT, MAP, TIMESTAMPTZ, int4 or VARCHAR, or an empty spelling, with the other side; the native comparison leaves such pairs uncompared. analysis_generated (the two cases with a BigQuery profile; 36 BigQuery groups): column types take SQLQuack's BigQuery spellings (INT64, STRING, BOOL); casts to VARCHAR or TEXT, which GoogleSQL refuses, and the columns passing them on are untyped, as is COALESCE over INT64 and STRING; more outputs are proven non-null; B002 messages drop Polyglot's context suffix; SELECT ... AS "Mixed_Case", which GoogleSQL refuses, fails analysis as a parser gap rather than as B000; and B002 on s.side over a star subquery below that gap is not reported, though a compile of the same shape reports it. --- ...ntyped_inputs_cte_facts_and_contracts.json | 1124 +++++++++-- ...tions_untyped_inputs_contracts_pivots.json | 1724 ++++++++++++----- ...ped_nullability_and_dynamic_contracts.json | 64 +- 3 files changed, 2224 insertions(+), 688 deletions(-) diff --git a/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json b/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json index 3a75d3870..aca17144f 100644 --- a/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json +++ b/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json @@ -525,43 +525,171 @@ [ "models", { -"orders_model_0": "dc4da2070db56afde4157773", -"orders_model_1": "9b0cc36921b5b8b8a8dea025", -"orders_model_10": "d4f810ff3ba14295385d6fb9", -"orders_model_11": "f7cdb34524d45acad3b71f7d", -"orders_model_12": "c380ae05f3bd0627628ab6fc", -"orders_model_13": "11f35c6f0b5918adfca2c40f", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "26974efa98703503eb817f97", -"orders_model_16": "c5a645b8cfb02ddc927fc297", -"orders_model_17": "669b30fefd09621ed0f2ad55", -"orders_model_18": "60e19e6908bb5c06d38eeb01", -"orders_model_19": "5318b049f2f651a689fb5e87", -"orders_model_2": "361de61cfd22f09af946862f", -"orders_model_20": "407c766e409ebec28d0c232a", -"orders_model_21": "9c26876fe71c235386932362", -"orders_model_22": "86880e31d61c6c5522256c62", -"orders_model_23": "209048028aa76bf4c2bd28c6", -"orders_model_3": "3ed083bce693c01d151c8081", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", -"orders_model_5": "a89cb10ab6dfef246cc36b07", -"orders_model_6": "a903f36871fea6d8ac818922", -"orders_model_7": "a4c400c5fa2da4107098339e", -"orders_model_8": "5400ca61f1ed2f29d89e2d23", -"orders_model_9": "67dc0f1b5ce04c8989ad1aca", -"payment_totals": "692528d52a5623a0e90d7fb4", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "335dfa47b776524b62ee26e6", +"orders_model_0": "57f83c252462e8577dcf4be4", +"orders_model_1": "20605564924616434b1fb36e", +"orders_model_10": "272fde8f3fc5755011016e81", +"orders_model_11": "7bb2fcc6f2416eca786fe5e3", +"orders_model_12": "4c1fd04cd9c309cbdf527da8", +"orders_model_13": "5c82a7215eca51b535c75c12", +"orders_model_14": "8caff2e3d6770d9547058318", +"orders_model_15": "9962066584fca5e32a728916", +"orders_model_16": "784a847c3f91c55acfaf95c4", +"orders_model_17": "9da324e94b8ba3bd42ae4ce5", +"orders_model_18": "915d3f5b79b0e36cb3458e71", +"orders_model_19": "2eb849800f03cc8bb2308be9", +"orders_model_2": "821ff3afef7feae09d2e9f64", +"orders_model_20": "fa4aadb4c9153cf9f0149363", +"orders_model_21": "d966cbad54511889dbcd7ba3", +"orders_model_22": "3d301c306c6784c35e030aa9", +"orders_model_23": "8ffc408e8f6b04e5be8d4347", +"orders_model_3": "4611a8f0e998e06f86b866d1", +"orders_model_4": "b53a0a8eb7eb79c13f84547a", +"orders_model_5": "a1486bf45f02a0af3467e7dc", +"orders_model_6": "4f28abcbc57f00e073a6d862", +"orders_model_7": "23020bef9b06547bfa9c5d4a", +"orders_model_8": "bc08f3df1c04d8f451169c9c", +"orders_model_9": "632ce5a31c3bae13f0f4a7e9", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", +"pivot_0": "111edafe3adf02dec49c33d9", "pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_2": "d19d284de9a3b5b5e4ae478d", "pivot_3": "826e0ad9aec9b04097d58b37", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"c44c8633473bde950e573766" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -1089,43 +1217,171 @@ [ "models", { -"orders_model_0": "dc4da2070db56afde4157773", -"orders_model_1": "3e1948d706ff1cecf42f7863", -"orders_model_10": "476ca953b1cb041d05ab9663", -"orders_model_11": "78eeedc510efd8cc89dfea31", -"orders_model_12": "b06fcf5168d718f9042eb9f0", -"orders_model_13": "25952bed7c264fceaeec357c", -"orders_model_14": "c5aa1f63d80b5d306477c290", -"orders_model_15": "2a978f96fe42e545ae0e5f8e", -"orders_model_16": "b67d418ac9424b16909d6a26", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "8a322bf0beb93c0034bd28d5", -"orders_model_19": "a4c400c5fa2da4107098339e", -"orders_model_2": "2f584b5185d90ee4c66c8339", -"orders_model_20": "1bb9c3971b4488e0b7023645", -"orders_model_21": "67dc0f1b5ce04c8989ad1aca", -"orders_model_22": "c838c05a0db9b7bdb5a1e793", -"orders_model_23": "18c951cb594f929547ec6527", -"orders_model_3": "cdd3d071e7283000e9b176dd", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", -"orders_model_5": "2f2cbf190f07bea2687a7a1e", -"orders_model_6": "b8c7c62baf6528acb751f82c", -"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", -"orders_model_8": "63ebe9cbc63523f4c4adddb2", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", -"payment_totals": "692528d52a5623a0e90d7fb4", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"orders_model_0": "57f83c252462e8577dcf4be4", +"orders_model_1": "e15045e42cce4a8ca6093093", +"orders_model_10": "bfd75cdcf0a589405e8f4b82", +"orders_model_11": "1188a32502da22a4d01e5cf1", +"orders_model_12": "9510e788c57b4fd68697a0af", +"orders_model_13": "c4d90d37ea2084e00f0e9fe6", +"orders_model_14": "fa27f672be3ec10d2375791d", +"orders_model_15": "ad2c189e83819cfcbba92fbb", +"orders_model_16": "189354f06e9d01a11555a27b", +"orders_model_17": "12d01e7c5e471b6d9a04c18d", +"orders_model_18": "733893b0f1a671a4856e8275", +"orders_model_19": "23020bef9b06547bfa9c5d4a", +"orders_model_2": "38f2a7cad5231801dc2315e8", +"orders_model_20": "c574125edd75a542219a5743", +"orders_model_21": "632ce5a31c3bae13f0f4a7e9", +"orders_model_22": "0daf1e0f4e471c4d0278dd75", +"orders_model_23": "3ef24fbdba7ff72e4ff82cdb", +"orders_model_3": "29a02b84ed11c2326db0a4ea", +"orders_model_4": "b53a0a8eb7eb79c13f84547a", +"orders_model_5": "0a8e0cf430bd2f18a43e54b7", +"orders_model_6": "b1e9cf8c36b429f5e168e814", +"orders_model_7": "35292446cc2fc21193857839", +"orders_model_8": "f4d48ae7f620d08cbef75e23", +"orders_model_9": "caa8198e7e417423bacef68e", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "db45532b4aa0746bb6155579", "pivot_1": "826e0ad9aec9b04097d58b37", "pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "0b5af099963f99173031a52f", +"pivot_3": "24c24d51c08f887f5728018a", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"f48ca046110efa93dc8e7b54" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -1653,43 +1909,171 @@ [ "models", { -"orders_model_0": "279aba0c75055164f61b65f8", -"orders_model_1": "45de3ae2081edb1155411120", -"orders_model_10": "ce0bc9f834e9623dddad0b3a", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "dc4da2070db56afde4157773", -"orders_model_13": "87a319b5caf7cc32bc6aa50e", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "18cc8b92ab2adfb7e7e160a3", -"orders_model_16": "28610c39619a490a015886ec", -"orders_model_17": "664cb83e35a8ecea0758a450", -"orders_model_18": "8cf38d3ff37cd8143561920a", -"orders_model_19": "dcb9d89d4b588b53682fb0b4", -"orders_model_2": "2c4a0d5f37d1033435dc44b8", -"orders_model_20": "f2329324ed72baeb2911ce6a", -"orders_model_21": "af51dc83785211c066cbb51f", -"orders_model_22": "9cc8cf8a102827df55622a21", -"orders_model_23": "857c9a58882bddd68cec3bd5", -"orders_model_3": "d43b073d40eb97e87277c137", -"orders_model_4": "a249acc516a880c9fbe25e88", -"orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "0541825c1a3bbc51c23f3a53", -"orders_model_7": "526a9e7adadbf085a39764b5", -"orders_model_8": "fcc5ac474974dee716908c65", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "692528d52a5623a0e90d7fb4", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"orders_model_0": "f3a28e68eda251e6e17610a5", +"orders_model_1": "a019b8a524e444deb1b35806", +"orders_model_10": "5ab415548b474d9fa6dfa44b", +"orders_model_11": "2a8e6fa3dbb50e535e675e82", +"orders_model_12": "57f83c252462e8577dcf4be4", +"orders_model_13": "f1dd0092ea84c5b9eb583812", +"orders_model_14": "5edece4951889d356b309723", +"orders_model_15": "376ae6a5aded8c2e9854a8fa", +"orders_model_16": "e7ca1400208788059e41ecf2", +"orders_model_17": "1de0aa6fad02c1503ec7ea49", +"orders_model_18": "57231f6794782af4dc39aa35", +"orders_model_19": "73641b3d85c979e99523e06a", +"orders_model_2": "e046a6fd4f1a58881ae438b2", +"orders_model_20": "86e27b2b418bbbf8eb6a977b", +"orders_model_21": "bc50b8c6a24550d482679380", +"orders_model_22": "502f53f453940fd06d14e994", +"orders_model_23": "17511de7e876f9e2db635f69", +"orders_model_3": "c364f8d41469ddb166f7b4b1", +"orders_model_4": "b821e5c64befff0af1eec1a9", +"orders_model_5": "25f9536a285cd74e175e4383", +"orders_model_6": "5214545969beb7231b166301", +"orders_model_7": "c0684bd88c7c075730c08d0f", +"orders_model_8": "290eb194213195210fb72867", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "0b5af099963f99173031a52f", +"pivot_1": "24c24d51c08f887f5728018a", "pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "d19d284de9a3b5b5e4ae478d", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"7f3cc4630798c6d611e9c144" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -2217,43 +2601,171 @@ [ "models", { -"orders_model_0": "dc4da2070db56afde4157773", -"orders_model_1": "b6345243b4bb0009ec2dfb5d", -"orders_model_10": "408cbd03f775698d2714eb62", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "279aba0c75055164f61b65f8", -"orders_model_13": "87a1dbf609e37b67c90e35b2", -"orders_model_14": "544d358c786985653996181d", -"orders_model_15": "e643f7ef7c9f860822ad8ba7", -"orders_model_16": "2ee8d0a917d274c8d55f4370", -"orders_model_17": "bac0193c7d43a68153ce544e", -"orders_model_18": "60e19e6908bb5c06d38eeb01", -"orders_model_19": "ab249088d32d341a013be687", -"orders_model_2": "3c07dff3140b081ee33fd13e", -"orders_model_20": "40a0a6e57912d17b00b26274", -"orders_model_21": "176a688a47e60b55e1ce0673", -"orders_model_22": "c6255585e2f5156964d8f8f8", -"orders_model_23": "e0ec1521e19c47996421ec94", -"orders_model_3": "f22120166b4143e8fd193e3d", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", -"orders_model_5": "bac0193c7d43a68153ce544e", -"orders_model_6": "c2b91215ea727d9313a22740", -"orders_model_7": "afb5b3102eaac7fa202355c0", -"orders_model_8": "19280031a53ac7db42aa7136", -"orders_model_9": "283c165f2da9102ffec377c4", -"payment_totals": "692528d52a5623a0e90d7fb4", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"orders_model_0": "57f83c252462e8577dcf4be4", +"orders_model_1": "bc96470c8b9c6987123f27a3", +"orders_model_10": "ad76ec9b595ad63d2f5313d4", +"orders_model_11": "2a8e6fa3dbb50e535e675e82", +"orders_model_12": "f3a28e68eda251e6e17610a5", +"orders_model_13": "830999b2b9c2acd310001776", +"orders_model_14": "948863478e41de5ee8207133", +"orders_model_15": "ae0b5392b8c40e81c9be6d81", +"orders_model_16": "3add3d442e8e056e1e9bc000", +"orders_model_17": "973cb3fe84976987ea3e9651", +"orders_model_18": "915d3f5b79b0e36cb3458e71", +"orders_model_19": "39cdbdef2d08814b5c4ad98c", +"orders_model_2": "9d575d623eb411f9b808077f", +"orders_model_20": "ef6c08365b3b385340a04618", +"orders_model_21": "0d746493f70e4eb85bd9a815", +"orders_model_22": "994f04ef650347210f28b880", +"orders_model_23": "7c47e5b1d00353f5112f933f", +"orders_model_3": "92dc4c4c6fa862e1dc0e8882", +"orders_model_4": "b53a0a8eb7eb79c13f84547a", +"orders_model_5": "973cb3fe84976987ea3e9651", +"orders_model_6": "11cb67812fb478c7209a0de8", +"orders_model_7": "6a9bf7ea75425bbd38345fec", +"orders_model_8": "37bc0de545f0673f6e303398", +"orders_model_9": "349a9972e1773da1af6a4fc0", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "bd6145283beb468ce86b460b", "pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "0b5af099963f99173031a52f", +"pivot_2": "c95a0a529686650cf07d2c66", +"pivot_3": "24c24d51c08f887f5728018a", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"767dbad2dc0bb5d013f7a196" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -2781,43 +3293,171 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "1070e59d687e0fed0e44b651", -"orders_model_10": "fa26ab8510354dd670525ac2", -"orders_model_11": "eb7d097be111399bcd2f940f", -"orders_model_12": "4054517533ad5199a1077065", -"orders_model_13": "ab1f9c5b6ef07ffa40eeadaf", -"orders_model_14": "273c339ab90e9a64a7c8f8a5", -"orders_model_15": "90310178ea5674f6669a72f4", -"orders_model_16": "e018db5f6e3eaf8888bd8eff", -"orders_model_17": "fe03c6a89b870943b6dd5922", -"orders_model_18": "fd5138ac4af02cd168a423e6", -"orders_model_19": "e24f113150b49c7e955cf81b", -"orders_model_2": "daca571af4a4d1cb1afbce74", -"orders_model_20": "9b30de5669877213eebaf0e5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "56f27ec8513b46291154e8cd", -"orders_model_23": "e94cdbeab01617cd05a55f82", -"orders_model_3": "72f0c384f9c169084cf65392", -"orders_model_4": "39a96779a764101f7d1dbae2", -"orders_model_5": "7af136b7e522ccf14f427e1e", -"orders_model_6": "8a322bf0beb93c0034bd28d5", -"orders_model_7": "4983a5e9302c8b9e28e75202", -"orders_model_8": "e7532f3dad5c3e5750265e5b", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", -"payment_totals": "692528d52a5623a0e90d7fb4", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_1": "9c277e649e1dc4b89e656ea0", +"orders_model_10": "43cd3fa513c204d3a57fa901", +"orders_model_11": "3dd78cb20c4917eb6343c502", +"orders_model_12": "8c7b8d2065fd669635240ffe", +"orders_model_13": "6859c12300b608143da427d1", +"orders_model_14": "26250c6b35dd88a05db888d8", +"orders_model_15": "f3da1e24e86d3eab0ea13f70", +"orders_model_16": "3c4b5451c2beb77780064153", +"orders_model_17": "a7e8aeaf1b3eae5b69d159ac", +"orders_model_18": "f1aba95e27aa7a60abefae15", +"orders_model_19": "5e51d59fc334a29010e3d44e", +"orders_model_2": "6b16025a9177e143d9715d81", +"orders_model_20": "4c086754fa5a73014f538ec5", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_22": "f5a6a117ffc927edf8ce56c7", +"orders_model_23": "1e6e09a3f7f31150b64ba14d", +"orders_model_3": "85c7320204178ea2783cfd0d", +"orders_model_4": "f1482176e48efb5c746720bc", +"orders_model_5": "60dd766ceda631cfc8d22967", +"orders_model_6": "733893b0f1a671a4856e8275", +"orders_model_7": "8f594c3da00e5ae41fbdb9fa", +"orders_model_8": "962e90a730f4c515cbb695f3", +"orders_model_9": "caa8198e7e417423bacef68e", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "0b5af099963f99173031a52f", +"pivot_1": "24c24d51c08f887f5728018a", "pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", +"pivot_3": "8cc3b9dc92e59c2e38d0aa1f", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"165df7e540546e89f76b0819" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -3345,42 +3985,170 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "18de106324e6bd649e9a0662", -"orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "68b962a61fcbd39f299bf7b9", -"orders_model_12": "31d217ac4f1c012c19d14c40", -"orders_model_13": "1d827eced23366a37ddba6b8", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "d1fb37ffa36108edc2a01aba", -"orders_model_16": "39a96779a764101f7d1dbae2", -"orders_model_17": "185a865c8cf0d67b3027a313", -"orders_model_18": "02d5138c4e7513182fc7cd52", -"orders_model_19": "3f36107952adc3d06995d9db", -"orders_model_2": "be51e7ded79e692b0f86ad8e", -"orders_model_20": "0c798fa3e6d807bf94e96eee", -"orders_model_21": "08e7a885c1d9662b2678c3ea", -"orders_model_22": "1b893dcaf72edc288a3b9710", -"orders_model_23": "f35475b4e5e4ae85ae46e91d", -"orders_model_3": "85fda65b19f7375e90342fa9", -"orders_model_4": "2cfd6426be9711fc3a185538", -"orders_model_5": "949207159f621de6e270aed5", -"orders_model_6": "60e19e6908bb5c06d38eeb01", -"orders_model_7": "07289e08df177aa672b5da80", -"orders_model_8": "c18f5ec77f58bbb47cc78168", -"orders_model_9": "05bfc38d1bf1cc771029edec", -"payment_totals": "692528d52a5623a0e90d7fb4", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "0b5af099963f99173031a52f", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_1": "96b31dc011cc2699a6ed9d9d", +"orders_model_10": "f81c6732137e43e2b3fe4cdf", +"orders_model_11": "27914a9d4926e65fe5517265", +"orders_model_12": "99f662718443f6eef76e7200", +"orders_model_13": "aa89292681775f9638cecfca", +"orders_model_14": "7121820324bbf56c9ff91a9d", +"orders_model_15": "42560181c0ab2e440ca79811", +"orders_model_16": "f1482176e48efb5c746720bc", +"orders_model_17": "b69a9c1c1cfd0c7f633b7f3d", +"orders_model_18": "d9d62aad4f23f44c76ad3c31", +"orders_model_19": "4805572c94a1c1585fbc80a4", +"orders_model_2": "ed022702df155c64c4051d51", +"orders_model_20": "7e5c44dd92a90f05405370cd", +"orders_model_21": "128cfebc1329ecada99dfe9d", +"orders_model_22": "805a519344d06e8347059b4d", +"orders_model_23": "05922259ccc00df8e9f26e62", +"orders_model_3": "55433102e6970d39531053d6", +"orders_model_4": "c2fe0f7b43dfbe33fede2811", +"orders_model_5": "d694de48e34c3ac0a2cb455c", +"orders_model_6": "915d3f5b79b0e36cb3458e71", +"orders_model_7": "7c580310fb12108f678dac23", +"orders_model_8": "15f34377b6795393014442c1", +"orders_model_9": "55e61452dde435068437d93e", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", +"pivot_0": "24c24d51c08f887f5728018a", "pivot_1": "bd6145283beb468ce86b460b", "pivot_2": "db45532b4aa0746bb6155579", "pivot_3": "5cf2515c32c9c98b0a9d8315", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"8808b0d353112b97c19536d1" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ] ] diff --git a/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json b/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json index 0fe10d8eb..40b35df4e 100644 --- a/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json +++ b/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json @@ -525,43 +525,171 @@ [ "models", { -"orders_model_0": "dc4da2070db56afde4157773", -"orders_model_1": "9b0cc36921b5b8b8a8dea025", -"orders_model_10": "d4f810ff3ba14295385d6fb9", -"orders_model_11": "f7cdb34524d45acad3b71f7d", -"orders_model_12": "c380ae05f3bd0627628ab6fc", -"orders_model_13": "9f0321d97fb59396dfa496d9", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", -"orders_model_16": "c5a645b8cfb02ddc927fc297", -"orders_model_17": "669b30fefd09621ed0f2ad55", -"orders_model_18": "60e19e6908bb5c06d38eeb01", -"orders_model_19": "5318b049f2f651a689fb5e87", -"orders_model_2": "361de61cfd22f09af946862f", -"orders_model_20": "032990c645506e1e133c4cd0", -"orders_model_21": "9c26876fe71c235386932362", -"orders_model_22": "86880e31d61c6c5522256c62", -"orders_model_23": "209048028aa76bf4c2bd28c6", -"orders_model_3": "482e641536f8899b756807cf", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", -"orders_model_5": "e672120be8dbea0cda9c4f28", -"orders_model_6": "a903f36871fea6d8ac818922", -"orders_model_7": "a4c400c5fa2da4107098339e", -"orders_model_8": "aba445941b1d72b9d3d5acb6", -"orders_model_9": "67dc0f1b5ce04c8989ad1aca", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "335dfa47b776524b62ee26e6", +"orders_model_0": "57f83c252462e8577dcf4be4", +"orders_model_1": "20605564924616434b1fb36e", +"orders_model_10": "272fde8f3fc5755011016e81", +"orders_model_11": "7bb2fcc6f2416eca786fe5e3", +"orders_model_12": "4c1fd04cd9c309cbdf527da8", +"orders_model_13": "5c82a7215eca51b535c75c12", +"orders_model_14": "8caff2e3d6770d9547058318", +"orders_model_15": "9962066584fca5e32a728916", +"orders_model_16": "784a847c3f91c55acfaf95c4", +"orders_model_17": "9da324e94b8ba3bd42ae4ce5", +"orders_model_18": "915d3f5b79b0e36cb3458e71", +"orders_model_19": "2eb849800f03cc8bb2308be9", +"orders_model_2": "821ff3afef7feae09d2e9f64", +"orders_model_20": "fa4aadb4c9153cf9f0149363", +"orders_model_21": "d966cbad54511889dbcd7ba3", +"orders_model_22": "3d301c306c6784c35e030aa9", +"orders_model_23": "8ffc408e8f6b04e5be8d4347", +"orders_model_3": "4611a8f0e998e06f86b866d1", +"orders_model_4": "b53a0a8eb7eb79c13f84547a", +"orders_model_5": "a1486bf45f02a0af3467e7dc", +"orders_model_6": "4f28abcbc57f00e073a6d862", +"orders_model_7": "23020bef9b06547bfa9c5d4a", +"orders_model_8": "bc08f3df1c04d8f451169c9c", +"orders_model_9": "632ce5a31c3bae13f0f4a7e9", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", +"pivot_0": "111edafe3adf02dec49c33d9", "pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "3767f4902ed5f3ab1acc83a1", +"pivot_2": "d19d284de9a3b5b5e4ae478d", "pivot_3": "826e0ad9aec9b04097d58b37", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"9c7db1355dd2394241b43e81" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -1089,88 +1217,43 @@ [ "models", { -"orders_model_0": "dc4da2070db56afde4157773", -"orders_model_1": "3e1948d706ff1cecf42f7863", -"orders_model_10": "fbdacfcd4b561abe157cdfda", -"orders_model_11": "17515e5f7babd19ac587b1d3", -"orders_model_12": "b06fcf5168d718f9042eb9f0", -"orders_model_13": "25952bed7c264fceaeec357c", -"orders_model_14": "c5aa1f63d80b5d306477c290", -"orders_model_15": "a533a3838eb00372891616ed", -"orders_model_16": "b67d418ac9424b16909d6a26", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "8a322bf0beb93c0034bd28d5", -"orders_model_19": "a4c400c5fa2da4107098339e", -"orders_model_2": "2f584b5185d90ee4c66c8339", -"orders_model_20": "c2da59cc5e833d3421094ea9", -"orders_model_21": "67dc0f1b5ce04c8989ad1aca", -"orders_model_22": "866b4de40862e3cc32f54936", -"orders_model_23": "1c16922430c6790814d0a7df", -"orders_model_3": "f922a0153bfa71727dd6241a", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", -"orders_model_5": "2f2cbf190f07bea2687a7a1e", -"orders_model_6": "b8c7c62baf6528acb751f82c", -"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", -"orders_model_8": "70f77b7d63c1bbe6e602e9bb", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", +"orders_model_0": "57f83c252462e8577dcf4be4", +"orders_model_1": "e15045e42cce4a8ca6093093", +"orders_model_10": "bfd75cdcf0a589405e8f4b82", +"orders_model_11": "1188a32502da22a4d01e5cf1", +"orders_model_12": "9510e788c57b4fd68697a0af", +"orders_model_13": "c4d90d37ea2084e00f0e9fe6", +"orders_model_14": "fa27f672be3ec10d2375791d", +"orders_model_15": "ad2c189e83819cfcbba92fbb", +"orders_model_16": "189354f06e9d01a11555a27b", +"orders_model_17": "12d01e7c5e471b6d9a04c18d", +"orders_model_18": "733893b0f1a671a4856e8275", +"orders_model_19": "23020bef9b06547bfa9c5d4a", +"orders_model_2": "38f2a7cad5231801dc2315e8", +"orders_model_20": "c574125edd75a542219a5743", +"orders_model_21": "632ce5a31c3bae13f0f4a7e9", +"orders_model_22": "0daf1e0f4e471c4d0278dd75", +"orders_model_23": "3ef24fbdba7ff72e4ff82cdb", +"orders_model_3": "29a02b84ed11c2326db0a4ea", +"orders_model_4": "b53a0a8eb7eb79c13f84547a", +"orders_model_5": "0a8e0cf430bd2f18a43e54b7", +"orders_model_6": "b1e9cf8c36b429f5e168e814", +"orders_model_7": "35292446cc2fc21193857839", +"orders_model_8": "f4d48ae7f620d08cbef75e23", +"orders_model_9": "caa8198e7e417423bacef68e", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "db45532b4aa0746bb6155579", "pivot_1": "826e0ad9aec9b04097d58b37", "pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "0b5af099963f99173031a52f", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" -} -], -[ -"catalog", -"b83e75232474640290815c9d" -], -[ -"shapes", -"78e5cd70ce361d823798634b" -], -[ -"models", -{ -"orders_model_0": "dc7f3dec778fd7cc3b47de47", -"orders_model_1": "98e73c916923e6ad3dbb4fe8", -"orders_model_10": "bba235f3ba5a071d31c89cfc", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "7dde13e57109cfb992bc623d", -"orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "2ab277361bc756f8553cd4fc", -"orders_model_15": "37833d5d1b748c8811d3704a", -"orders_model_16": "bf62990d9480cf71956fd3c9", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "288bd78cd1025c86da017d6d", -"orders_model_19": "bc46ad8533a003d33c266a57", -"orders_model_2": "c4c391b60a4436e3a6a9105e", -"orders_model_20": "5660541da0116a33de85cf37", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "9cc8cf8a102827df55622a21", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "d545cb7b8ca26b55d32439c0", -"orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "529410c5a69b04b7172c6dda", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "9a483c2d5942e8857be41233", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "8cf82ad30e9cce84bc8a3529", -"payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", +"pivot_3": "24c24d51c08f887f5728018a", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"b2b2221f2da5d47b86b56575" +"2b0e82358ee720a47bc2531b" ], [ "proof", @@ -1278,48 +1361,48 @@ ], [ "proof", -"0d91dfe8eefbbb36998db250" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"b9689124495085d917d65ebf" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"4994bb2a572acbed6354a2bb" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"ff4d4f462aae3b9e95d9a5ab" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"28c524727756a048843237ef" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"99b1ccea692774bb61737217" +"97e1ede8284ba792471c8c3e" ], [ "shapes", -"b40871514fad0707c8cdbee7" +"78e5cd70ce361d823798634b" ], [ "models", { -"orders_model_0": "b21bb72c9b7652fb99f52bf8", +"orders_model_0": "dc7f3dec778fd7cc3b47de47", "orders_model_1": "98e73c916923e6ad3dbb4fe8", "orders_model_10": "bba235f3ba5a071d31c89cfc", "orders_model_11": "b92be214f33c9cf71eea3bc2", "orders_model_12": "7dde13e57109cfb992bc623d", "orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_14": "2ab277361bc756f8553cd4fc", "orders_model_15": "37833d5d1b748c8811d3704a", -"orders_model_16": "24d75028ccc9de7633328c96", +"orders_model_16": "bf62990d9480cf71956fd3c9", "orders_model_17": "cb0cef5752047e0f1201dfb4", "orders_model_18": "288bd78cd1025c86da017d6d", "orders_model_19": "bc46ad8533a003d33c266a57", -"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_2": "c4c391b60a4436e3a6a9105e", "orders_model_20": "5660541da0116a33de85cf37", "orders_model_21": "bbb7494cbb82085e1c034f21", "orders_model_22": "9cc8cf8a102827df55622a21", @@ -1329,12 +1412,12 @@ "orders_model_5": "ad3b7e6c56a628aff0799f39", "orders_model_6": "529410c5a69b04b7172c6dda", "orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "692f18d932cd76c441a79e81", +"orders_model_8": "9a483c2d5942e8857be41233", "orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "e406a89772022ea3c32aefef", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "e6d82b48a7a5593bd747ea69", +"pivot_1": "af151ba34f16c141fdee1ac3", "pivot_2": "5cf2515c32c9c98b0a9d8315", "pivot_3": "3767f4902ed5f3ab1acc83a1", "status_amounts": "0fbfc8d810e430c5814769d6", @@ -1343,7 +1426,7 @@ ], [ "catalog", -"3a861a8c7cc6422ec893ea77" +"b2b2221f2da5d47b86b56575" ], [ "proof", @@ -1451,72 +1534,72 @@ ], [ "proof", -"200eb30f2a045b54792500f6" +"0d91dfe8eefbbb36998db250" ], [ "proof", -"200eb30f2a045b54792500f6" +"b9689124495085d917d65ebf" ], [ "proof", -"200eb30f2a045b54792500f6" +"4994bb2a572acbed6354a2bb" ], [ "proof", -"200eb30f2a045b54792500f6" +"ff4d4f462aae3b9e95d9a5ab" ], [ "proof", -"200eb30f2a045b54792500f6" +"28c524727756a048843237ef" ], [ "proof", -"200eb30f2a045b54792500f6" +"99b1ccea692774bb61737217" ], [ "shapes", -"78e5cd70ce361d823798634b" +"b40871514fad0707c8cdbee7" ], [ "models", { -"orders_model_0": "b09efd1e7181db7773253200", -"orders_model_1": "f8df97502ce8987ade2b8ae6", -"orders_model_10": "d9e87a16e347f6f0316452d6", +"orders_model_0": "b21bb72c9b7652fb99f52bf8", +"orders_model_1": "98e73c916923e6ad3dbb4fe8", +"orders_model_10": "bba235f3ba5a071d31c89cfc", "orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "1c5f7e1a73535a5617fb070a", +"orders_model_12": "7dde13e57109cfb992bc623d", "orders_model_13": "528bfc7a27293f659b6eec35", "orders_model_14": "611ea76d354e7180f2fe0906", "orders_model_15": "37833d5d1b748c8811d3704a", -"orders_model_16": "0eca13897e0abb4131837a39", +"orders_model_16": "24d75028ccc9de7633328c96", "orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "74eb746be2a4278dae684968", +"orders_model_18": "288bd78cd1025c86da017d6d", "orders_model_19": "bc46ad8533a003d33c266a57", "orders_model_2": "2c4a0d5f37d1033435dc44b8", "orders_model_20": "5660541da0116a33de85cf37", "orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "83e085c3265373904422aad9", +"orders_model_22": "9cc8cf8a102827df55622a21", "orders_model_23": "ccf2498a5b3526260f88df6f", "orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_4": "d545cb7b8ca26b55d32439c0", "orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "c88625738d77bd28714c4086", +"orders_model_6": "529410c5a69b04b7172c6dda", "orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "ec0818249ce5c1a3abdbba12", +"orders_model_8": "692f18d932cd76c441a79e81", "orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "80bd8eb5eab6ed918ec31949", -"payments_by_customer": "9e1265b9f4134636cb508259", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_1": "e6d82b48a7a5593bd747ea69", "pivot_2": "5cf2515c32c9c98b0a9d8315", "pivot_3": "3767f4902ed5f3ab1acc83a1", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"status_passthrough": "57dbca31518ecf81d68ec4fe" } ], [ "catalog", -"01ad0f760ce6f305ea550857" +"3a861a8c7cc6422ec893ea77" ], [ "proof", @@ -1624,11 +1707,192 @@ ], [ "proof", -"c67f69d1828d209a58642e36" +"200eb30f2a045b54792500f6" ], [ "proof", -"b9689124495085d917d65ebf" +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "b09efd1e7181db7773253200", +"orders_model_1": "f8df97502ce8987ade2b8ae6", +"orders_model_10": "d9e87a16e347f6f0316452d6", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "1c5f7e1a73535a5617fb070a", +"orders_model_13": "528bfc7a27293f659b6eec35", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_16": "0eca13897e0abb4131837a39", +"orders_model_17": "cb0cef5752047e0f1201dfb4", +"orders_model_18": "74eb746be2a4278dae684968", +"orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_2": "2c4a0d5f37d1033435dc44b8", +"orders_model_20": "5660541da0116a33de85cf37", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_22": "83e085c3265373904422aad9", +"orders_model_23": "ccf2498a5b3526260f88df6f", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_5": "ad3b7e6c56a628aff0799f39", +"orders_model_6": "c88625738d77bd28714c4086", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_8": "ec0818249ce5c1a3abdbba12", +"orders_model_9": "0b71c12f30a7617191bc2e17", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "bbd8b7298ce68a222ba84345" +} +], +[ +"catalog", +"01ad0f760ce6f305ea550857" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"c67f69d1828d209a58642e36" +], +[ +"proof", +"c67f69d1828d209a58642e36" ], [ "proof", @@ -1636,60 +1900,526 @@ ], [ "proof", -"c67f69d1828d209a58642e36" +"99b1ccea692774bb61737217" +], +[ +"shapes", +"c0d3bfda902cb95cd1564269" +], +[ +"models", +{ +"orders_model_0": "f3a28e68eda251e6e17610a5", +"orders_model_1": "a019b8a524e444deb1b35806", +"orders_model_10": "5ab415548b474d9fa6dfa44b", +"orders_model_11": "2a8e6fa3dbb50e535e675e82", +"orders_model_12": "57f83c252462e8577dcf4be4", +"orders_model_13": "f1dd0092ea84c5b9eb583812", +"orders_model_14": "5edece4951889d356b309723", +"orders_model_15": "376ae6a5aded8c2e9854a8fa", +"orders_model_16": "e7ca1400208788059e41ecf2", +"orders_model_17": "1de0aa6fad02c1503ec7ea49", +"orders_model_18": "57231f6794782af4dc39aa35", +"orders_model_19": "73641b3d85c979e99523e06a", +"orders_model_2": "e046a6fd4f1a58881ae438b2", +"orders_model_20": "86e27b2b418bbbf8eb6a977b", +"orders_model_21": "bc50b8c6a24550d482679380", +"orders_model_22": "502f53f453940fd06d14e994", +"orders_model_23": "17511de7e876f9e2db635f69", +"orders_model_3": "c364f8d41469ddb166f7b4b1", +"orders_model_4": "b821e5c64befff0af1eec1a9", +"orders_model_5": "25f9536a285cd74e175e4383", +"orders_model_6": "5214545969beb7231b166301", +"orders_model_7": "c0684bd88c7c075730c08d0f", +"orders_model_8": "290eb194213195210fb72867", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "24c24d51c08f887f5728018a", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "d19d284de9a3b5b5e4ae478d", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "15c7374b0ebb45265a111112" +} +], +[ +"catalog", +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "d7498e8ac1b0afeb189ff727", +"orders_model_10": "264e63bb81a8673659bbb9db", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "dc7f3dec778fd7cc3b47de47", +"orders_model_13": "35b1097a860faac43e81b044", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_16": "f386cc5235b54c550f4924f8", +"orders_model_17": "60e141a5769c44084f76723b", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "85ea595b45b20d75c0d8df80", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "36c156c5a6601badf8124464", +"orders_model_7": "ef0eb5ded0428f49ac6cb411", +"orders_model_8": "1d2ab23b591c4ca7f5e6359b", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "af151ba34f16c141fdee1ac3", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"4f98b4e1b4999a33ba1ff099" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"1241165b261dcfe84ece9067" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"b40871514fad0707c8cdbee7" +], +[ +"models", +{ +"orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_1": "d7498e8ac1b0afeb189ff727", +"orders_model_10": "264e63bb81a8673659bbb9db", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_12": "b21bb72c9b7652fb99f52bf8", +"orders_model_13": "35b1097a860faac43e81b044", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_16": "f386cc5235b54c550f4924f8", +"orders_model_17": "60e141a5769c44084f76723b", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_2": "3c07dff3140b081ee33fd13e", +"orders_model_20": "85ea595b45b20d75c0d8df80", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_23": "8942d18a241e8dcd1831addc", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_5": "60e141a5769c44084f76723b", +"orders_model_6": "777e18bfdcc2423b67211d39", +"orders_model_7": "80a1f1b7b38c1afa802d7625", +"orders_model_8": "1d2ab23b591c4ca7f5e6359b", +"orders_model_9": "338aef528aa5f199e2ffa973", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", +"pivot_0": "bd6145283beb468ce86b460b", +"pivot_1": "8eb04463a7e8015fcc9b8a63", +"pivot_2": "18ecbe5e93fe3ced5cbfd24a", +"pivot_3": "e6d82b48a7a5593bd747ea69", +"status_amounts": "0fbfc8d810e430c5814769d6", +"status_passthrough": "57dbca31518ecf81d68ec4fe" +} +], +[ +"catalog", +"055d801ba49e61e8a1e414b2" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" ], [ "proof", -"c67f69d1828d209a58642e36" +"74234e98afe7498fb5daf1f3" ], [ "proof", -"99b1ccea692774bb61737217" +"74234e98afe7498fb5daf1f3" ], [ -"shapes", -"c0d3bfda902cb95cd1564269" +"proof", +"74234e98afe7498fb5daf1f3" ], [ -"models", -{ -"orders_model_0": "36669f325385a145cfe71921", -"orders_model_1": "45de3ae2081edb1155411120", -"orders_model_10": "bba235f3ba5a071d31c89cfc", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "dc4da2070db56afde4157773", -"orders_model_13": "87a319b5caf7cc32bc6aa50e", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "37833d5d1b748c8811d3704a", -"orders_model_16": "8ef4aea858f63561f24ec68b", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "8cf38d3ff37cd8143561920a", -"orders_model_19": "dcb9d89d4b588b53682fb0b4", -"orders_model_2": "2c4a0d5f37d1033435dc44b8", -"orders_model_20": "5660541da0116a33de85cf37", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "9cc8cf8a102827df55622a21", -"orders_model_23": "857c9a58882bddd68cec3bd5", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "a249acc516a880c9fbe25e88", -"orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "0541825c1a3bbc51c23f3a53", -"orders_model_7": "526a9e7adadbf085a39764b5", -"orders_model_8": "3bf1aff8cb84ab7999a0b5fd", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "0b5af099963f99173031a52f", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" -} +"proof", +"74234e98afe7498fb5daf1f3" ], [ -"catalog", -"032717ee1f3f581899d54bf7" +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" +], +[ +"proof", +"200eb30f2a045b54792500f6" ], [ "shapes", @@ -1698,43 +2428,43 @@ [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "d7498e8ac1b0afeb189ff727", -"orders_model_10": "264e63bb81a8673659bbb9db", +"orders_model_0": "1c5f7e1a73535a5617fb070a", +"orders_model_1": "673e75c341c3f2437091de09", +"orders_model_10": "0df969272aa19a78a705bc92", "orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "dc7f3dec778fd7cc3b47de47", -"orders_model_13": "35b1097a860faac43e81b044", -"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_12": "b09efd1e7181db7773253200", +"orders_model_13": "bc666dca3ca9016f640dfe68", +"orders_model_14": "7c3ab71786810446d45e97a3", "orders_model_15": "a2c41a95e7f2097d2e9b9bd1", -"orders_model_16": "f386cc5235b54c550f4924f8", +"orders_model_16": "cf4db989e60b4ff2ee7b8b90", "orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_18": "b1a0e72c622ac940a7b5fb00", "orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", "orders_model_2": "3c07dff3140b081ee33fd13e", "orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "fbd144b873208a2bd80a35a4", -"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_21": "59228560ec9692b57930d8b5", +"orders_model_22": "b8238ed1e26320d8f8912882", "orders_model_23": "8942d18a241e8dcd1831addc", "orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "526ce0a1d0a274582f9d35f1", +"orders_model_4": "1cefa36f28721fe677e6a684", "orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "36c156c5a6601badf8124464", -"orders_model_7": "ef0eb5ded0428f49ac6cb411", +"orders_model_6": "3197497d139b9241f4b6e04c", +"orders_model_7": "44dc5de3912695d88a818a03", "orders_model_8": "1d2ab23b591c4ca7f5e6359b", "orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payment_totals": "80bd8eb5eab6ed918ec31949", "payments_by_customer": "9e1265b9f4134636cb508259", "pivot_0": "bd6145283beb468ce86b460b", "pivot_1": "8eb04463a7e8015fcc9b8a63", "pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "af151ba34f16c141fdee1ac3", +"pivot_3": "b2268fc29a60313e3b8c34ed", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"status_passthrough": "bbd8b7298ce68a222ba84345" } ], [ "catalog", -"4f98b4e1b4999a33ba1ff099" +"74156f1f4b47943ba3b769dd" ], [ "proof", @@ -1842,15 +2572,15 @@ ], [ "proof", -"1241165b261dcfe84ece9067" +"c67f69d1828d209a58642e36" ], [ "proof", -"c3cb3226e55a418829fd059c" +"c67f69d1828d209a58642e36" ], [ "proof", -"20e6b6374d63663154468bed" +"c67f69d1828d209a58642e36" ], [ "proof", @@ -1858,7 +2588,7 @@ ], [ "proof", -"28c524727756a048843237ef" +"c67f69d1828d209a58642e36" ], [ "proof", @@ -1866,48 +2596,48 @@ ], [ "shapes", -"b40871514fad0707c8cdbee7" +"c0d3bfda902cb95cd1564269" ], [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "d7498e8ac1b0afeb189ff727", -"orders_model_10": "264e63bb81a8673659bbb9db", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "b21bb72c9b7652fb99f52bf8", -"orders_model_13": "35b1097a860faac43e81b044", -"orders_model_14": "28b94fa5aaa6225fd8809740", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", -"orders_model_16": "f386cc5235b54c550f4924f8", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", -"orders_model_2": "3c07dff3140b081ee33fd13e", -"orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "fbd144b873208a2bd80a35a4", -"orders_model_22": "4e068f2ba3ce583e4d5b6006", -"orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "75f6764c319047022968f0b3", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "777e18bfdcc2423b67211d39", -"orders_model_7": "80a1f1b7b38c1afa802d7625", -"orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "e406a89772022ea3c32aefef", +"orders_model_0": "57f83c252462e8577dcf4be4", +"orders_model_1": "bc96470c8b9c6987123f27a3", +"orders_model_10": "ad76ec9b595ad63d2f5313d4", +"orders_model_11": "2a8e6fa3dbb50e535e675e82", +"orders_model_12": "f3a28e68eda251e6e17610a5", +"orders_model_13": "830999b2b9c2acd310001776", +"orders_model_14": "948863478e41de5ee8207133", +"orders_model_15": "ae0b5392b8c40e81c9be6d81", +"orders_model_16": "3add3d442e8e056e1e9bc000", +"orders_model_17": "973cb3fe84976987ea3e9651", +"orders_model_18": "915d3f5b79b0e36cb3458e71", +"orders_model_19": "39cdbdef2d08814b5c4ad98c", +"orders_model_2": "9d575d623eb411f9b808077f", +"orders_model_20": "ef6c08365b3b385340a04618", +"orders_model_21": "0d746493f70e4eb85bd9a815", +"orders_model_22": "994f04ef650347210f28b880", +"orders_model_23": "7c47e5b1d00353f5112f933f", +"orders_model_3": "92dc4c4c6fa862e1dc0e8882", +"orders_model_4": "b53a0a8eb7eb79c13f84547a", +"orders_model_5": "973cb3fe84976987ea3e9651", +"orders_model_6": "11cb67812fb478c7209a0de8", +"orders_model_7": "6a9bf7ea75425bbd38345fec", +"orders_model_8": "37bc0de545f0673f6e303398", +"orders_model_9": "349a9972e1773da1af6a4fc0", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "bd6145283beb468ce86b460b", "pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "e6d82b48a7a5593bd747ea69", +"pivot_2": "c95a0a529686650cf07d2c66", +"pivot_3": "24c24d51c08f887f5728018a", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"055d801ba49e61e8a1e414b2" +"2b0e82358ee720a47bc2531b" ], [ "proof", @@ -2015,27 +2745,27 @@ ], [ "proof", -"200eb30f2a045b54792500f6" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"200eb30f2a045b54792500f6" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"200eb30f2a045b54792500f6" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"200eb30f2a045b54792500f6" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"200eb30f2a045b54792500f6" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"200eb30f2a045b54792500f6" +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -2044,43 +2774,43 @@ [ "models", { -"orders_model_0": "1c5f7e1a73535a5617fb070a", -"orders_model_1": "673e75c341c3f2437091de09", -"orders_model_10": "0df969272aa19a78a705bc92", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "b09efd1e7181db7773253200", -"orders_model_13": "bc666dca3ca9016f640dfe68", -"orders_model_14": "7c3ab71786810446d45e97a3", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", -"orders_model_16": "cf4db989e60b4ff2ee7b8b90", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "b1a0e72c622ac940a7b5fb00", -"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", -"orders_model_2": "3c07dff3140b081ee33fd13e", -"orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "59228560ec9692b57930d8b5", -"orders_model_22": "b8238ed1e26320d8f8912882", -"orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "3197497d139b9241f4b6e04c", -"orders_model_7": "44dc5de3912695d88a818a03", -"orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_1": "abadf69a0572399b6536a2c1", +"orders_model_10": "c46ffece29c5df800b8f74df", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_13": "278d4fd453aea2eca14894ea", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_16": "eeaefdbd50fdc13681d8653a", +"orders_model_17": "3515712bb3c936c534706554", +"orders_model_18": "5ccadf27b4bf1a37e0f30682", +"orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_20": "12b73be6a6b5985f71de1ca5", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_22": "91d92b539582dec7737d1704", +"orders_model_23": "c8ff9a2130c4a7a57073fb6d", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_4": "e29709e8e7acb44cc782304f", +"orders_model_5": "afd6d68f0a0a6689404e0260", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_8": "6be36f8074a475ab3f5bce6c", +"orders_model_9": "d34e456ade2ced8792534866", +"payment_totals": "8cf82ad30e9cce84bc8a3529", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "bd6145283beb468ce86b460b", -"pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "b2268fc29a60313e3b8c34ed", +"pivot_0": "8ef48104ced62eb9ff31c2b8", +"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", +"pivot_3": "00a890bbfc402b53ea021e5a", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"status_passthrough": "57dbca31518ecf81d68ec4fe" } ], [ "catalog", -"74156f1f4b47943ba3b769dd" +"c7b70a4fc469694d9d64eff8" ], [ "proof", @@ -2188,23 +2918,23 @@ ], [ "proof", -"c67f69d1828d209a58642e36" +"0d91dfe8eefbbb36998db250" ], [ "proof", -"c67f69d1828d209a58642e36" +"b9689124495085d917d65ebf" ], [ "proof", -"c67f69d1828d209a58642e36" +"1241165b261dcfe84ece9067" ], [ "proof", -"b9689124495085d917d65ebf" +"20e6b6374d63663154468bed" ], [ "proof", -"c67f69d1828d209a58642e36" +"28c524727756a048843237ef" ], [ "proof", @@ -2212,84 +2942,39 @@ ], [ "shapes", -"c0d3bfda902cb95cd1564269" -], -[ -"models", -{ -"orders_model_0": "dc4da2070db56afde4157773", -"orders_model_1": "b6345243b4bb0009ec2dfb5d", -"orders_model_10": "264e63bb81a8673659bbb9db", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "36669f325385a145cfe71921", -"orders_model_13": "87a1dbf609e37b67c90e35b2", -"orders_model_14": "544d358c786985653996181d", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", -"orders_model_16": "cf4db989e60b4ff2ee7b8b90", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "60e19e6908bb5c06d38eeb01", -"orders_model_19": "ab249088d32d341a013be687", -"orders_model_2": "3c07dff3140b081ee33fd13e", -"orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "176a688a47e60b55e1ce0673", -"orders_model_22": "c6255585e2f5156964d8f8f8", -"orders_model_23": "e0ec1521e19c47996421ec94", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "c2b91215ea727d9313a22740", -"orders_model_7": "afb5b3102eaac7fa202355c0", -"orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "283c165f2da9102ffec377c4", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "bd6145283beb468ce86b460b", -"pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "0b5af099963f99173031a52f", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" -} -], -[ -"catalog", -"3d1560ed382e916606e5f85f" -], -[ -"shapes", -"78e5cd70ce361d823798634b" +"b40871514fad0707c8cdbee7" ], [ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "abadf69a0572399b6536a2c1", +"orders_model_1": "a23763739c27a47ae12ebed2", "orders_model_10": "c46ffece29c5df800b8f74df", "orders_model_11": "8003e1680dc046032ceea404", "orders_model_12": "edf57c58d750226a747455ba", -"orders_model_13": "278d4fd453aea2eca14894ea", +"orders_model_13": "56d09679844a976541cce33c", "orders_model_14": "e51070f44666248f5b7764b3", "orders_model_15": "88ec6fdf0edc3ff0a0acb78c", "orders_model_16": "eeaefdbd50fdc13681d8653a", "orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "5ccadf27b4bf1a37e0f30682", +"orders_model_18": "f73c7edc9f35cb7956a40b52", "orders_model_19": "5ab5711e085cc014f3e44950", "orders_model_2": "835e887650bbf9b124ae26b1", "orders_model_20": "12b73be6a6b5985f71de1ca5", "orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "91d92b539582dec7737d1704", +"orders_model_22": "2853fca6c5782a3d8f7f3229", "orders_model_23": "c8ff9a2130c4a7a57073fb6d", "orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "e29709e8e7acb44cc782304f", +"orders_model_4": "c8c4091a11671ad7035944d6", "orders_model_5": "afd6d68f0a0a6689404e0260", "orders_model_6": "099cb926af524f10d27cc108", "orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "6be36f8074a475ab3f5bce6c", +"orders_model_8": "8a583d5a0fe333e4583746b2", "orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "8cf82ad30e9cce84bc8a3529", -"payments_by_customer": "9e1265b9f4134636cb508259", +"payment_totals": "10740c01d3d11a1de6799033", +"payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "af151ba34f16c141fdee1ac3", +"pivot_1": "e6d82b48a7a5593bd747ea69", "pivot_2": "5adb7a56c3dfd3d1c3cf58c1", "pivot_3": "00a890bbfc402b53ea021e5a", "status_amounts": "0fbfc8d810e430c5814769d6", @@ -2298,7 +2983,7 @@ ], [ "catalog", -"c7b70a4fc469694d9d64eff8" +"8cc95852afc3fab17c43340c" ], [ "proof", @@ -2406,72 +3091,72 @@ ], [ "proof", -"0d91dfe8eefbbb36998db250" +"200eb30f2a045b54792500f6" ], [ "proof", -"b9689124495085d917d65ebf" +"200eb30f2a045b54792500f6" ], [ "proof", -"1241165b261dcfe84ece9067" +"200eb30f2a045b54792500f6" ], [ "proof", -"20e6b6374d63663154468bed" +"200eb30f2a045b54792500f6" ], [ "proof", -"28c524727756a048843237ef" +"200eb30f2a045b54792500f6" ], [ "proof", -"99b1ccea692774bb61737217" +"200eb30f2a045b54792500f6" ], [ "shapes", -"b40871514fad0707c8cdbee7" +"78e5cd70ce361d823798634b" ], [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "a23763739c27a47ae12ebed2", -"orders_model_10": "c46ffece29c5df800b8f74df", +"orders_model_0": "94f95487333b2561382c2981", +"orders_model_1": "ab25f3ba101f081f8a3f936c", +"orders_model_10": "3aec7a059badfa0e968d5216", "orders_model_11": "8003e1680dc046032ceea404", -"orders_model_12": "edf57c58d750226a747455ba", -"orders_model_13": "56d09679844a976541cce33c", +"orders_model_12": "cb6d3e8487b533349a2bad04", +"orders_model_13": "d55e6b7bb6533cea41bcfeda", "orders_model_14": "e51070f44666248f5b7764b3", "orders_model_15": "88ec6fdf0edc3ff0a0acb78c", -"orders_model_16": "eeaefdbd50fdc13681d8653a", +"orders_model_16": "97e322d50d70c30c61fb7a15", "orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "f73c7edc9f35cb7956a40b52", +"orders_model_18": "32b8af78b243949e0198fda6", "orders_model_19": "5ab5711e085cc014f3e44950", -"orders_model_2": "835e887650bbf9b124ae26b1", +"orders_model_2": "345f166deff852e9116d8801", "orders_model_20": "12b73be6a6b5985f71de1ca5", "orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "2853fca6c5782a3d8f7f3229", +"orders_model_22": "368c97dc5eea624b492ce042", "orders_model_23": "c8ff9a2130c4a7a57073fb6d", "orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "c8c4091a11671ad7035944d6", -"orders_model_5": "afd6d68f0a0a6689404e0260", -"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_4": "7565624f8000d1c0566d8ed3", +"orders_model_5": "ee556ea5f790f1d885cee84a", +"orders_model_6": "5d2a8162e9742273846f69e3", "orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "8a583d5a0fe333e4583746b2", +"orders_model_8": "c7d3f9e5025dad577c958a44", "orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "e406a89772022ea3c32aefef", +"payment_totals": "80bd8eb5eab6ed918ec31949", +"payments_by_customer": "9e1265b9f4134636cb508259", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "e6d82b48a7a5593bd747ea69", +"pivot_1": "b2268fc29a60313e3b8c34ed", "pivot_2": "5adb7a56c3dfd3d1c3cf58c1", "pivot_3": "00a890bbfc402b53ea021e5a", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"status_passthrough": "bbd8b7298ce68a222ba84345" } ], [ "catalog", -"8cc95852afc3fab17c43340c" +"08a4d5d6c6bf5e75b2e23d81" ], [ "proof", @@ -2579,72 +3264,72 @@ ], [ "proof", -"200eb30f2a045b54792500f6" +"c67f69d1828d209a58642e36" ], [ "proof", -"200eb30f2a045b54792500f6" +"b9689124495085d917d65ebf" ], [ "proof", -"200eb30f2a045b54792500f6" +"c67f69d1828d209a58642e36" ], [ "proof", -"200eb30f2a045b54792500f6" +"c67f69d1828d209a58642e36" ], [ "proof", -"200eb30f2a045b54792500f6" +"c67f69d1828d209a58642e36" ], [ "proof", -"200eb30f2a045b54792500f6" +"99b1ccea692774bb61737217" ], [ "shapes", -"78e5cd70ce361d823798634b" +"c0d3bfda902cb95cd1564269" ], [ "models", { -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "ab25f3ba101f081f8a3f936c", -"orders_model_10": "3aec7a059badfa0e968d5216", -"orders_model_11": "8003e1680dc046032ceea404", -"orders_model_12": "cb6d3e8487b533349a2bad04", -"orders_model_13": "d55e6b7bb6533cea41bcfeda", -"orders_model_14": "e51070f44666248f5b7764b3", -"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", -"orders_model_16": "97e322d50d70c30c61fb7a15", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "32b8af78b243949e0198fda6", -"orders_model_19": "5ab5711e085cc014f3e44950", -"orders_model_2": "345f166deff852e9116d8801", -"orders_model_20": "12b73be6a6b5985f71de1ca5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "368c97dc5eea624b492ce042", -"orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "7565624f8000d1c0566d8ed3", -"orders_model_5": "ee556ea5f790f1d885cee84a", -"orders_model_6": "5d2a8162e9742273846f69e3", -"orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "c7d3f9e5025dad577c958a44", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "80bd8eb5eab6ed918ec31949", -"payments_by_customer": "9e1265b9f4134636cb508259", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_1": "9c277e649e1dc4b89e656ea0", +"orders_model_10": "43cd3fa513c204d3a57fa901", +"orders_model_11": "3dd78cb20c4917eb6343c502", +"orders_model_12": "8c7b8d2065fd669635240ffe", +"orders_model_13": "6859c12300b608143da427d1", +"orders_model_14": "26250c6b35dd88a05db888d8", +"orders_model_15": "f3da1e24e86d3eab0ea13f70", +"orders_model_16": "3c4b5451c2beb77780064153", +"orders_model_17": "a7e8aeaf1b3eae5b69d159ac", +"orders_model_18": "f1aba95e27aa7a60abefae15", +"orders_model_19": "5e51d59fc334a29010e3d44e", +"orders_model_2": "6b16025a9177e143d9715d81", +"orders_model_20": "4c086754fa5a73014f538ec5", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_22": "f5a6a117ffc927edf8ce56c7", +"orders_model_23": "1e6e09a3f7f31150b64ba14d", +"orders_model_3": "85c7320204178ea2783cfd0d", +"orders_model_4": "f1482176e48efb5c746720bc", +"orders_model_5": "60dd766ceda631cfc8d22967", +"orders_model_6": "733893b0f1a671a4856e8275", +"orders_model_7": "8f594c3da00e5ae41fbdb9fa", +"orders_model_8": "962e90a730f4c515cbb695f3", +"orders_model_9": "caa8198e7e417423bacef68e", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "b2268fc29a60313e3b8c34ed", +"pivot_1": "24c24d51c08f887f5728018a", "pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", +"pivot_3": "8cc3b9dc92e59c2e38d0aa1f", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"08a4d5d6c6bf5e75b2e23d81" +"2b0e82358ee720a47bc2531b" ], [ "proof", @@ -2752,72 +3437,27 @@ ], [ "proof", -"c67f69d1828d209a58642e36" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"b9689124495085d917d65ebf" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"c67f69d1828d209a58642e36" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"c67f69d1828d209a58642e36" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"c67f69d1828d209a58642e36" +"97e1ede8284ba792471c8c3e" ], [ "proof", -"99b1ccea692774bb61737217" -], -[ -"shapes", -"c0d3bfda902cb95cd1564269" -], -[ -"models", -{ -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "c7630eba442c12475a304749", -"orders_model_10": "fa26ab8510354dd670525ac2", -"orders_model_11": "eb7d097be111399bcd2f940f", -"orders_model_12": "edf57c58d750226a747455ba", -"orders_model_13": "ab1f9c5b6ef07ffa40eeadaf", -"orders_model_14": "273c339ab90e9a64a7c8f8a5", -"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", -"orders_model_16": "e018db5f6e3eaf8888bd8eff", -"orders_model_17": "fe03c6a89b870943b6dd5922", -"orders_model_18": "fd5138ac4af02cd168a423e6", -"orders_model_19": "e24f113150b49c7e955cf81b", -"orders_model_2": "daca571af4a4d1cb1afbce74", -"orders_model_20": "12b73be6a6b5985f71de1ca5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "24bdd574c66fd2f692429cfb", -"orders_model_23": "e94cdbeab01617cd05a55f82", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "a908ebc07c10d1423a970a49", -"orders_model_5": "7af136b7e522ccf14f427e1e", -"orders_model_6": "8a322bf0beb93c0034bd28d5", -"orders_model_7": "4983a5e9302c8b9e28e75202", -"orders_model_8": "8a583d5a0fe333e4583746b2", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "0b5af099963f99173031a52f", -"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" -} -], -[ -"catalog", -"f19e292dff42d4c08b5da8ad" +"97e1ede8284ba792471c8c3e" ], [ "shapes", @@ -3345,42 +3985,170 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "18de106324e6bd649e9a0662", -"orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "68b962a61fcbd39f299bf7b9", -"orders_model_12": "bd31b96a3fa2e1bb80699d6e", -"orders_model_13": "1d827eced23366a37ddba6b8", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", -"orders_model_16": "a908ebc07c10d1423a970a49", -"orders_model_17": "185a865c8cf0d67b3027a313", -"orders_model_18": "02d5138c4e7513182fc7cd52", -"orders_model_19": "3f36107952adc3d06995d9db", -"orders_model_2": "be51e7ded79e692b0f86ad8e", -"orders_model_20": "59129bc02b2fd6f6f8bfb348", -"orders_model_21": "08e7a885c1d9662b2678c3ea", -"orders_model_22": "1b893dcaf72edc288a3b9710", -"orders_model_23": "f35475b4e5e4ae85ae46e91d", -"orders_model_3": "fd09d2408d6ef7eceb04f8e3", -"orders_model_4": "2cfd6426be9711fc3a185538", -"orders_model_5": "949207159f621de6e270aed5", -"orders_model_6": "60e19e6908bb5c06d38eeb01", -"orders_model_7": "07289e08df177aa672b5da80", -"orders_model_8": "4fe24f5b9f54838716b41386", -"orders_model_9": "05bfc38d1bf1cc771029edec", -"payment_totals": "10740c01d3d11a1de6799033", -"payments_by_customer": "c29bf4a09e3c8e9cff564a2f", -"pivot_0": "0b5af099963f99173031a52f", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_1": "96b31dc011cc2699a6ed9d9d", +"orders_model_10": "f81c6732137e43e2b3fe4cdf", +"orders_model_11": "27914a9d4926e65fe5517265", +"orders_model_12": "99f662718443f6eef76e7200", +"orders_model_13": "aa89292681775f9638cecfca", +"orders_model_14": "7121820324bbf56c9ff91a9d", +"orders_model_15": "42560181c0ab2e440ca79811", +"orders_model_16": "f1482176e48efb5c746720bc", +"orders_model_17": "b69a9c1c1cfd0c7f633b7f3d", +"orders_model_18": "d9d62aad4f23f44c76ad3c31", +"orders_model_19": "4805572c94a1c1585fbc80a4", +"orders_model_2": "ed022702df155c64c4051d51", +"orders_model_20": "7e5c44dd92a90f05405370cd", +"orders_model_21": "128cfebc1329ecada99dfe9d", +"orders_model_22": "805a519344d06e8347059b4d", +"orders_model_23": "05922259ccc00df8e9f26e62", +"orders_model_3": "55433102e6970d39531053d6", +"orders_model_4": "c2fe0f7b43dfbe33fede2811", +"orders_model_5": "d694de48e34c3ac0a2cb455c", +"orders_model_6": "915d3f5b79b0e36cb3458e71", +"orders_model_7": "7c580310fb12108f678dac23", +"orders_model_8": "15f34377b6795393014442c1", +"orders_model_9": "55e61452dde435068437d93e", +"payment_totals": "4305f3153b974cee803f57b1", +"payments_by_customer": "1d6a0338bf72d10443d4e6e3", +"pivot_0": "24c24d51c08f887f5728018a", "pivot_1": "bd6145283beb468ce86b460b", "pivot_2": "db45532b4aa0746bb6155579", "pivot_3": "5cf2515c32c9c98b0a9d8315", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"23b5038345131be7d003ad7b" +"2b0e82358ee720a47bc2531b" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" +], +[ +"proof", +"97e1ede8284ba792471c8c3e" ] ] diff --git a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json index 06b2d6254..6a3a86bee 100644 --- a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json +++ b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json @@ -3,7 +3,7 @@ "contracts", { "None": "946a3061a7e72843e29b93c8", -"bigquery": "946a3061a7e72843e29b93c8", +"bigquery": "9f697d91a5bd8293d4c78868", "databricks": "946a3061a7e72843e29b93c8", "duckdb": "946a3061a7e72843e29b93c8", "postgres": "946a3061a7e72843e29b93c8", @@ -75,7 +75,7 @@ "contracts", { "None": "0b6e350a4f4afde493cb8626", -"bigquery": "0b6e350a4f4afde493cb8626", +"bigquery": "15c4bcdde1ae6d0d695d8fd6", "databricks": "0b6e350a4f4afde493cb8626", "duckdb": "0b6e350a4f4afde493cb8626", "postgres": "0b6e350a4f4afde493cb8626", @@ -111,7 +111,7 @@ "contracts", { "None": "839434d5b73abdf9499ad59e", -"bigquery": "839434d5b73abdf9499ad59e", +"bigquery": "93171de36b7cdbf8cb98f4f9", "databricks": "839434d5b73abdf9499ad59e", "duckdb": "839434d5b73abdf9499ad59e", "postgres": "839434d5b73abdf9499ad59e", @@ -123,7 +123,7 @@ "contracts", { "None": "ab9b0c5c3dd6f54aea71a5f1", -"bigquery": "ab9b0c5c3dd6f54aea71a5f1", +"bigquery": "304a4034b8020f8712de3185", "databricks": "ab9b0c5c3dd6f54aea71a5f1", "duckdb": "ab9b0c5c3dd6f54aea71a5f1", "postgres": "ab9b0c5c3dd6f54aea71a5f1", @@ -147,7 +147,7 @@ "contracts", { "None": "48ff6f452c064d9c0d6f68ed", -"bigquery": "48ff6f452c064d9c0d6f68ed", +"bigquery": "a932012ddaf72dbb7a5600e1", "databricks": "48ff6f452c064d9c0d6f68ed", "duckdb": "48ff6f452c064d9c0d6f68ed", "postgres": "48ff6f452c064d9c0d6f68ed", @@ -159,7 +159,7 @@ "contracts", { "None": "17c278499d9aad7922d610d5", -"bigquery": "17c278499d9aad7922d610d5", +"bigquery": "4488a1489430ab1c4db97e43", "databricks": "17c278499d9aad7922d610d5", "duckdb": "17c278499d9aad7922d610d5", "postgres": "17c278499d9aad7922d610d5", @@ -183,7 +183,7 @@ "contracts", { "None": "23893ae7f9f05f0777697eba", -"bigquery": "23893ae7f9f05f0777697eba", +"bigquery": "64f58724212d29d25c0e3f1d", "databricks": "23893ae7f9f05f0777697eba", "duckdb": "23893ae7f9f05f0777697eba", "postgres": "23893ae7f9f05f0777697eba", @@ -195,7 +195,7 @@ "contracts", { "None": "bcc0d54db4ee9d237ef10dd4", -"bigquery": "bcc0d54db4ee9d237ef10dd4", +"bigquery": "c977930632cd22e603ff63c5", "databricks": "bcc0d54db4ee9d237ef10dd4", "duckdb": "bcc0d54db4ee9d237ef10dd4", "postgres": "bcc0d54db4ee9d237ef10dd4", @@ -207,7 +207,7 @@ "contracts", { "None": "0d0a771fb0ff7b39fb021d4d", -"bigquery": "0d0a771fb0ff7b39fb021d4d", +"bigquery": "aaea819afff0f389c94a3eb8", "databricks": "0d0a771fb0ff7b39fb021d4d", "duckdb": "0d0a771fb0ff7b39fb021d4d", "postgres": "0d0a771fb0ff7b39fb021d4d", @@ -243,7 +243,7 @@ "contracts", { "None": "d03a2acd92b5e17c753a9272", -"bigquery": "d03a2acd92b5e17c753a9272", +"bigquery": "fc85f9a45d4598f1c6786a3f", "databricks": "d03a2acd92b5e17c753a9272", "duckdb": "d03a2acd92b5e17c753a9272", "postgres": "d03a2acd92b5e17c753a9272", @@ -255,7 +255,7 @@ "contracts", { "None": "2d4b87c5b06ff335bd6c741b", -"bigquery": "2d4b87c5b06ff335bd6c741b", +"bigquery": "a0c38fc940179b1197e2d11d", "databricks": "2d4b87c5b06ff335bd6c741b", "duckdb": "2d4b87c5b06ff335bd6c741b", "postgres": "2d4b87c5b06ff335bd6c741b", @@ -267,7 +267,7 @@ "contracts", { "None": "a2dffd29b4f083e47c096d79", -"bigquery": "a2dffd29b4f083e47c096d79", +"bigquery": "b3283bf184bb082f364b8537", "databricks": "a2dffd29b4f083e47c096d79", "duckdb": "a2dffd29b4f083e47c096d79", "postgres": "a2dffd29b4f083e47c096d79", @@ -291,7 +291,7 @@ "contracts", { "None": "ffd2f2034ea38a1c665386fe", -"bigquery": "ffd2f2034ea38a1c665386fe", +"bigquery": "4f2e740fcc73cbe30799945e", "databricks": "ffd2f2034ea38a1c665386fe", "duckdb": "ffd2f2034ea38a1c665386fe", "postgres": "ffd2f2034ea38a1c665386fe", @@ -315,7 +315,7 @@ "contracts", { "None": "c11f8c29184eeda19979e5d2", -"bigquery": "c11f8c29184eeda19979e5d2", +"bigquery": "693ade4356c6087b9c1d8b19", "databricks": "c11f8c29184eeda19979e5d2", "duckdb": "c11f8c29184eeda19979e5d2", "postgres": "c11f8c29184eeda19979e5d2", @@ -327,7 +327,7 @@ "contracts", { "None": "6a14752b404ff8337dbb4c51", -"bigquery": "6a14752b404ff8337dbb4c51", +"bigquery": "2d6a82bfe80342be2bb552ef", "databricks": "6a14752b404ff8337dbb4c51", "duckdb": "6a14752b404ff8337dbb4c51", "postgres": "6a14752b404ff8337dbb4c51", @@ -351,7 +351,7 @@ "contracts", { "None": "d766e542b46b503b1d64b5ec", -"bigquery": "d766e542b46b503b1d64b5ec", +"bigquery": "6f3824fb29e0dde7383108c6", "databricks": "d766e542b46b503b1d64b5ec", "duckdb": "d766e542b46b503b1d64b5ec", "postgres": "d766e542b46b503b1d64b5ec", @@ -411,7 +411,7 @@ "contracts", { "None": "cd5296ac257b6e4f10c3000d", -"bigquery": "cd5296ac257b6e4f10c3000d", +"bigquery": "48415c26019755009787f14c", "databricks": "cd5296ac257b6e4f10c3000d", "duckdb": "cd5296ac257b6e4f10c3000d", "postgres": "cd5296ac257b6e4f10c3000d", @@ -423,7 +423,7 @@ "contracts", { "None": "0fb1be349cccda275d77113d", -"bigquery": "0fb1be349cccda275d77113d", +"bigquery": "b0c0b670609049fee2ff6f77", "databricks": "0fb1be349cccda275d77113d", "duckdb": "0fb1be349cccda275d77113d", "postgres": "0fb1be349cccda275d77113d", @@ -435,7 +435,7 @@ "contracts", { "None": "d9843bd5f2e76b8d875badaf", -"bigquery": "d9843bd5f2e76b8d875badaf", +"bigquery": "876a438e514952d77fd02fbb", "databricks": "d9843bd5f2e76b8d875badaf", "duckdb": "d9843bd5f2e76b8d875badaf", "postgres": "d9843bd5f2e76b8d875badaf", @@ -459,7 +459,7 @@ "contracts", { "None": "2a167c9cb4bdcef7cfbcd99a", -"bigquery": "2a167c9cb4bdcef7cfbcd99a", +"bigquery": "d270f17ca2b64c571cb98bbd", "databricks": "2a167c9cb4bdcef7cfbcd99a", "duckdb": "2a167c9cb4bdcef7cfbcd99a", "postgres": "2a167c9cb4bdcef7cfbcd99a", @@ -471,7 +471,7 @@ "contracts", { "None": "ea9fe2e44b57cc7f6a5654aa", -"bigquery": "ea9fe2e44b57cc7f6a5654aa", +"bigquery": "896ce437f811f03b69f82839", "databricks": "ea9fe2e44b57cc7f6a5654aa", "duckdb": "ea9fe2e44b57cc7f6a5654aa", "postgres": "ea9fe2e44b57cc7f6a5654aa", @@ -483,7 +483,7 @@ "contracts", { "None": "fe017b6cb02eef5219322f7c", -"bigquery": "fe017b6cb02eef5219322f7c", +"bigquery": "b3283bf184bb082f364b8537", "databricks": "fe017b6cb02eef5219322f7c", "duckdb": "fe017b6cb02eef5219322f7c", "postgres": "fe017b6cb02eef5219322f7c", @@ -495,7 +495,7 @@ "contracts", { "None": "7d8609a5dcd3a0f32d347465", -"bigquery": "7d8609a5dcd3a0f32d347465", +"bigquery": "6242d56d5832fdeefd826de9", "databricks": "7d8609a5dcd3a0f32d347465", "duckdb": "7d8609a5dcd3a0f32d347465", "postgres": "7d8609a5dcd3a0f32d347465", @@ -543,7 +543,7 @@ "contracts", { "None": "6f1634dc604b82f58c1d760f", -"bigquery": "6f1634dc604b82f58c1d760f", +"bigquery": "c969edb8ec7bbec85e3606d2", "databricks": "6f1634dc604b82f58c1d760f", "duckdb": "6f1634dc604b82f58c1d760f", "postgres": "6f1634dc604b82f58c1d760f", @@ -555,7 +555,7 @@ "contracts", { "None": "2fb2d4657d6af84c32083288", -"bigquery": "2fb2d4657d6af84c32083288", +"bigquery": "3c9b74c5b22f656137431938", "databricks": "2fb2d4657d6af84c32083288", "duckdb": "2fb2d4657d6af84c32083288", "postgres": "2fb2d4657d6af84c32083288", @@ -567,7 +567,7 @@ "contracts", { "None": "a9bdcb7ee2642092b9c2e2ae", -"bigquery": "a9bdcb7ee2642092b9c2e2ae", +"bigquery": "667b5495d0a7267e742cdf83", "databricks": "a9bdcb7ee2642092b9c2e2ae", "duckdb": "a9bdcb7ee2642092b9c2e2ae", "postgres": "a9bdcb7ee2642092b9c2e2ae", @@ -579,7 +579,7 @@ "contracts", { "None": "a8bc34d679ed5c1bd41c17ba", -"bigquery": "0f72c2e3063cc166300becea", +"bigquery": "be7b04ec20ebcdd8742b4180", "databricks": "a8bc34d679ed5c1bd41c17ba", "duckdb": "a8bc34d679ed5c1bd41c17ba", "postgres": "a8bc34d679ed5c1bd41c17ba", @@ -591,7 +591,7 @@ "contracts", { "None": "6e5f3057275c88687658bb88", -"bigquery": "6e5f3057275c88687658bb88", +"bigquery": "bb67625de7198e2dc8e3b23b", "databricks": "6e5f3057275c88687658bb88", "duckdb": "6e5f3057275c88687658bb88", "postgres": "6e5f3057275c88687658bb88", @@ -615,7 +615,7 @@ "contracts", { "None": "6581612a11662f5afd928e61", -"bigquery": "6581612a11662f5afd928e61", +"bigquery": "81109ce0b75717f432eec784", "databricks": "6581612a11662f5afd928e61", "duckdb": "6581612a11662f5afd928e61", "postgres": "6581612a11662f5afd928e61", @@ -627,7 +627,7 @@ "contracts", { "None": "3405db15b2687320907ae528", -"bigquery": "3405db15b2687320907ae528", +"bigquery": "257b09b04f10f75c5c4e4c2e", "databricks": "3405db15b2687320907ae528", "duckdb": "3405db15b2687320907ae528", "postgres": "3405db15b2687320907ae528", @@ -663,7 +663,7 @@ "contracts", { "None": "70130ae9c4f7aa6a50756c6b", -"bigquery": "70130ae9c4f7aa6a50756c6b", +"bigquery": "197e3351cf8e73647dafe210", "databricks": "70130ae9c4f7aa6a50756c6b", "duckdb": "70130ae9c4f7aa6a50756c6b", "postgres": "70130ae9c4f7aa6a50756c6b", @@ -711,7 +711,7 @@ "contracts", { "None": "866ae81507c3a535c0225de2", -"bigquery": "866ae81507c3a535c0225de2", +"bigquery": "20e5bc9de6e4d1de2bf5a08f", "databricks": "866ae81507c3a535c0225de2", "duckdb": "866ae81507c3a535c0225de2", "postgres": "866ae81507c3a535c0225de2", From bb1d291cbf6fd3cb592877bf75ae83b5e99a954e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:48:49 +0000 Subject: [PATCH 050/160] test(analysis): re-record P026 messages and fast-lineage golden for native arms The P026 rows now name the model file or the hook with its file, with the gap placed in that text. The generated fast-lineage golden moves with the native route: root stars resolved, medium confidence now high, the schema's own column spelling, set-operation columns that read different branch columns now expression, and edges for models that had none. --- .../compiler/failure/model-sql-syntax.json | 2 +- .../goldens/compiler/failure/syntax-hook.json | 2 +- .../compiler/failure/syntax-inline-hook.json | 2 +- ...erations_quoting_and_opted_out_models.json | 2726 ++++++++--------- 4 files changed, 1366 insertions(+), 1366 deletions(-) diff --git a/tests/goldens/compiler/failure/model-sql-syntax.json b/tests/goldens/compiler/failure/model-sql-syntax.json index d848b2834..c512e39bc 100644 --- a/tests/goldens/compiler/failure/model-sql-syntax.json +++ b/tests/goldens/compiler/failure/model-sql-syntax.json @@ -8,7 +8,7 @@ "phase": "compile", "severity": "error", "code": "P026", - "message": "SQL analysis skipped for model 'stg_orders': the duckdb parser cannot read Syntax error at or near ',' at line 1, column 17", + "message": "SQL analysis skipped for model 'stg_orders' (/models/staging/stg_orders.sql): the duckdb parser cannot read Syntax error at or near ',' at line 5, column 17", "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" } ] diff --git a/tests/goldens/compiler/failure/syntax-hook.json b/tests/goldens/compiler/failure/syntax-hook.json index ea7695256..a40ff2470 100644 --- a/tests/goldens/compiler/failure/syntax-hook.json +++ b/tests/goldens/compiler/failure/syntax-hook.json @@ -8,7 +8,7 @@ "phase": "compile", "severity": "error", "code": "P026", - "message": "SQL analysis skipped for model 'customer_totals': the duckdb parser cannot read Syntax error at or near 'WHERE' at line 1, column 13", + "message": "SQL analysis skipped for model 'customer_totals' post_hooks[0] sql(\"record_refresh\") (hooks/sql/record_refresh.sql): the duckdb parser cannot read Syntax error at or near 'WHERE' at line 1, column 13 of its SQL", "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" } ] diff --git a/tests/goldens/compiler/failure/syntax-inline-hook.json b/tests/goldens/compiler/failure/syntax-inline-hook.json index ea7695256..96dcf4ced 100644 --- a/tests/goldens/compiler/failure/syntax-inline-hook.json +++ b/tests/goldens/compiler/failure/syntax-inline-hook.json @@ -8,7 +8,7 @@ "phase": "compile", "severity": "error", "code": "P026", - "message": "SQL analysis skipped for model 'customer_totals': the duckdb parser cannot read Syntax error at or near 'WHERE' at line 1, column 13", + "message": "SQL analysis skipped for model 'customer_totals' post_hooks[0] inline_sql(\"...\") (/models/marts/customer_totals.sql): the duckdb parser cannot read Syntax error at or near 'WHERE' at line 1, column 13 of its SQL", "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" } ] diff --git a/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json b/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json index 1c47a3ac4..ed393e613 100644 --- a/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json +++ b/tests/goldens/python_oracle/fast_lineage_generated_stars_nested_ctes_set_operations_quoting_and_opted_out_models.json @@ -3,6 +3,15 @@ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "5a2ba1c1876f477d1e26f62e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -11,9 +20,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"18:orders_model_3.targeting": "893c539cb301d6133966b471", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -24,18 +32,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", -"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "30072ad8f2c8be88e3acec82", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -44,9 +50,8 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", +"48:orders_model_4.targeting": "e93a18f8aaf99c489dbfea65", "49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "1990f5d01e632714338d0160", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -57,7 +62,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -66,9 +70,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", "69:orders_model_2.sourced": "414698d284dfb6c4759e3907", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -79,17 +82,23 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "8076db03c4b29af16b33b043", -"81:edges": "7c6782de6171aee87a5c63ca", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "44dff69fc65dfef14e018a86", +"81:edges": "1e7114dff10842540425801d" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -100,7 +109,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -111,18 +119,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", "34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -133,7 +139,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -144,7 +149,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -153,9 +157,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", "69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -166,28 +169,33 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "6c2eb1c2dab2a62dcfe944fe", -"81:edges": "59aa1a7bc998bf685d690875", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "4b5ac10a6ba0e0deb29b7b77", +"81:edges": "ea2acc236e8da65861a8b1af" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "5a2ba1c1876f477d1e26f62e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"18:orders_model_3.targeting": "893c539cb301d6133966b471", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -197,19 +205,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", -"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "3f12ee2602150ccd7674411c", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -218,63 +224,65 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", -"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "1990f5d01e632714338d0160", +"48:orders_model_4.targeting": "e93a18f8aaf99c489dbfea65", +"49:orders_model_4.sourced": "b8522a4ee9c571012a9fdb0d", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "7542506da60c852ee39e187c", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"63:orders_model_14.targeting": "58f281ed1c1efe8b8c79104e", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "414698d284dfb6c4759e3907", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "544fae8daf34c98b0d6d481b", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "01f59befad4cafe3bdb502d6", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "8076db03c4b29af16b33b043", -"81:edges": "7c6782de6171aee87a5c63ca", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "71c5788c00559317388c777e", +"81:edges": "592b3500f02cc633e51b14fd" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -284,19 +292,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", -"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "dca1a8f3d87cbef40227c061", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -306,8 +312,7 @@ "46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", -"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"49:orders_model_4.sourced": "01f59befad4cafe3bdb502d6", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -316,9 +321,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -327,30 +331,35 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "d3efe717b762c729423af4be", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "01f59befad4cafe3bdb502d6", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "6c2eb1c2dab2a62dcfe944fe", -"81:edges": "59aa1a7bc998bf685d690875", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "529ca81bd1bb31af9ff7f7dc", +"81:edges": "d89765b94cf581fb4a4dd058" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "5a2ba1c1876f477d1e26f62e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -359,9 +368,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"18:orders_model_3.targeting": "893c539cb301d6133966b471", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -372,18 +380,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", -"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "30072ad8f2c8be88e3acec82", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -392,9 +398,8 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", +"48:orders_model_4.targeting": "e93a18f8aaf99c489dbfea65", "49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "1990f5d01e632714338d0160", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -405,7 +410,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -414,9 +418,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", "69:orders_model_2.sourced": "414698d284dfb6c4759e3907", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -427,17 +430,23 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "8076db03c4b29af16b33b043", -"81:edges": "7c6782de6171aee87a5c63ca", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "44dff69fc65dfef14e018a86", +"81:edges": "1e7114dff10842540425801d" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -448,7 +457,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -459,18 +467,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", "34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -481,7 +487,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -492,7 +497,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -501,9 +505,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", "69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -514,28 +517,33 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "6c2eb1c2dab2a62dcfe944fe", -"81:edges": "59aa1a7bc998bf685d690875", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "4b5ac10a6ba0e0deb29b7b77", +"81:edges": "ea2acc236e8da65861a8b1af" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "5a2ba1c1876f477d1e26f62e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "857c83e64739100814d8f497", +"18:orders_model_3.targeting": "893c539cb301d6133966b471", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -545,19 +553,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", -"34:orders_model_1.sourced": "833e3ae8571732e99d47e0fc", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "3f12ee2602150ccd7674411c", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -566,63 +572,65 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "e84cca707b52fbf7a63726fd", -"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "1990f5d01e632714338d0160", +"48:orders_model_4.targeting": "e93a18f8aaf99c489dbfea65", +"49:orders_model_4.sourced": "529faf1c9e3968a1b7930bf5", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "7542506da60c852ee39e187c", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"63:orders_model_14.targeting": "92b3a8b917ef164ded686fab", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "414698d284dfb6c4759e3907", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "544fae8daf34c98b0d6d481b", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "79ad128ab6507f177ea552ad", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "8076db03c4b29af16b33b043", -"81:edges": "7c6782de6171aee87a5c63ca", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "cf7dbf56d4a9a08f2ec818cb", +"81:edges": "dc9c7ad34877ef1602b3c076" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -632,19 +640,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", -"33:orders_model_1.targeting": "3bcfcc6ee82a411e69515125", -"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "dca1a8f3d87cbef40227c061", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -654,8 +660,7 @@ "46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", -"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"49:orders_model_4.sourced": "79ad128ab6507f177ea552ad", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -664,9 +669,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -675,30 +679,35 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "d3efe717b762c729423af4be", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "79ad128ab6507f177ea552ad", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "6c2eb1c2dab2a62dcfe944fe", -"81:edges": "59aa1a7bc998bf685d690875", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "e2d94aa0fe5e55362b1ff5cb", +"81:edges": "9df890b8d68ecee725d3e323" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"4:orders_model_0.sourced": "34b95a9bb89c9221f0e0a471", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -709,7 +718,6 @@ "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", "18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -720,18 +728,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "a967e822b57213ec858768ca", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "ef618974a44a96a56d259e25", "34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "ef2d127de37b942baad06145", "38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", @@ -742,7 +748,6 @@ "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", "48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", "49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -753,7 +758,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -764,7 +768,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", "69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -775,17 +778,23 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "92046d27e611a2b8961bfcc5", -"81:edges": "9c2ff0e1313dd442bb1ac017", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "9890c5460ed26fae589d12aa", +"81:edges": "53f47847c6d2fa1430c27806" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -796,7 +805,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -807,18 +815,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "a967e822b57213ec858768ca", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "ef618974a44a96a56d259e25", "34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -829,7 +835,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -840,7 +845,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -851,7 +855,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", "69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -862,137 +865,140 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "9cc5472deaed036c6f68e314", -"81:edges": "1960382314765257e21dd758", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "1b101fdf2c367713196927c1", +"81:edges": "3f74d94c93c88710972b1f73" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "5a2ba1c1876f477d1e26f62e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", +"18:orders_model_3.targeting": "5068ccdac8d5eb850de7eea1", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "0fcf4ef01f4ca62bb77c3d80", -"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"23:orders_model_6.targeting": "e875a603bb4eaac6f28aef6b", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", -"28:orders_model_9.targeting": "a967e822b57213ec858768ca", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", -"34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "3f12ee2602150ccd7674411c", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "ef2d127de37b942baad06145", -"38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", -"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "e93a18f8aaf99c489dbfea65", +"49:orders_model_4.sourced": "b8522a4ee9c571012a9fdb0d", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "7542506da60c852ee39e187c", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"63:orders_model_14.targeting": "58f281ed1c1efe8b8c79104e", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "544fae8daf34c98b0d6d481b", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "01f59befad4cafe3bdb502d6", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "92046d27e611a2b8961bfcc5", -"81:edges": "9c2ff0e1313dd442bb1ac017", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "3c6eb9eac0390ee43f16dd6c", +"81:edges": "9a9c454613cad10deb400296" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", "23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", -"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", -"28:orders_model_9.targeting": "a967e822b57213ec858768ca", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", -"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "dca1a8f3d87cbef40227c061", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -1002,8 +1008,7 @@ "46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", -"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", +"49:orders_model_4.sourced": "01f59befad4cafe3bdb502d6", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -1012,9 +1017,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -1023,30 +1027,35 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "d3efe717b762c729423af4be", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "01f59befad4cafe3bdb502d6", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "9cc5472deaed036c6f68e314", -"81:edges": "1960382314765257e21dd758", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "681434dc6d02d369e3ef75f8", +"81:edges": "d89765b94cf581fb4a4dd058" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"4:orders_model_0.sourced": "34b95a9bb89c9221f0e0a471", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1057,7 +1066,6 @@ "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", "18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -1068,18 +1076,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "a967e822b57213ec858768ca", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "ef618974a44a96a56d259e25", "34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "ef2d127de37b942baad06145", "38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", @@ -1090,7 +1096,6 @@ "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", "48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", "49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -1101,7 +1106,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -1112,7 +1116,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", "69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -1123,17 +1126,23 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "92046d27e611a2b8961bfcc5", -"81:edges": "9c2ff0e1313dd442bb1ac017", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "9890c5460ed26fae589d12aa", +"81:edges": "53f47847c6d2fa1430c27806" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "de72923afa7d269dec06170d", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1144,7 +1153,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -1155,18 +1163,16 @@ "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", "28:orders_model_9.targeting": "a967e822b57213ec858768ca", "29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "ef618974a44a96a56d259e25", "34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -1177,7 +1183,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -1188,7 +1193,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -1199,7 +1203,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", "69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -1210,137 +1213,140 @@ "77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", "78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "9cc5472deaed036c6f68e314", -"81:edges": "1960382314765257e21dd758", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "1b101fdf2c367713196927c1", +"81:edges": "3f74d94c93c88710972b1f73" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "5a2ba1c1876f477d1e26f62e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "1ff64b327984da82f76cce2e", +"18:orders_model_3.targeting": "5068ccdac8d5eb850de7eea1", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "0fcf4ef01f4ca62bb77c3d80", -"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"23:orders_model_6.targeting": "e875a603bb4eaac6f28aef6b", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", -"28:orders_model_9.targeting": "a967e822b57213ec858768ca", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", -"34:orders_model_1.sourced": "0c1d78a57ca6b9e16bcd6266", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "3f12ee2602150ccd7674411c", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "ef2d127de37b942baad06145", -"38:orders_model_10.targeting": "2591b6af4aaafcb322150ab2", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "c66d9a109cb01d45dd1a86a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "1aa2dcab73d2bc39dc008009", -"49:orders_model_4.sourced": "64ca73dda13db25820ee20e0", -"4:orders_model_0.sourced": "8bcc9afb9b2508beebd187ed", +"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", +"48:orders_model_4.targeting": "e93a18f8aaf99c489dbfea65", +"49:orders_model_4.sourced": "529faf1c9e3968a1b7930bf5", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "79896e5d232604aea741ec1b", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "7542506da60c852ee39e187c", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "906c9537cb6a0320a5145beb", +"63:orders_model_14.targeting": "92b3a8b917ef164ded686fab", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "9fd9dbb60bf071aa3417a06f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "544fae8daf34c98b0d6d481b", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "79ad128ab6507f177ea552ad", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "92046d27e611a2b8961bfcc5", -"81:edges": "9c2ff0e1313dd442bb1ac017", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "d057d38a14f48b3255c0daa4", +"81:edges": "7dc3eb417188c3af41c4da8f" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0a51338d2cf0b7f4b891db04", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "60283655b8430c7e6df81ead", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "8fe2f3d0a303eca5503031b5", +"13:orders_model_15.targeting": "84c6d681794f1ace5e2678b9", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", "23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", -"24:orders_model_6.sourced": "a967e822b57213ec858768ca", +"24:orders_model_6.sourced": "c1aee4a42103a082541c19b7", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "ef2d127de37b942baad06145", -"28:orders_model_9.targeting": "a967e822b57213ec858768ca", -"29:orders_model_9.sourced": "6bbb8ae1c03cae3174007e24", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "c1aee4a42103a082541c19b7", +"29:orders_model_9.sourced": "968a3e0c5afb6e2bec8a44f1", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "bbedd18bce59f23468d191c5", -"34:orders_model_1.sourced": "98471e4c377a8d5d7068e4ec", +"32:orders_model_1.edge_count": "4b227777d4dd1fc61c6f884f", +"33:orders_model_1.targeting": "8705e086b45cfb0e3782e1a4", +"34:orders_model_1.sourced": "dca1a8f3d87cbef40227c061", "35:orders_model_10.has_model": "fcbcf165908dd18a9e49f7ff", "36:orders_model_10.has_star": "fcbcf165908dd18a9e49f7ff", "37:orders_model_10.edge_count": "5feceb66ffc86f38d952786c", "38:orders_model_10.targeting": "6786f18f6ff42aa577c99d86", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -1350,8 +1356,7 @@ "46:orders_model_4.has_star": "fcbcf165908dd18a9e49f7ff", "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", -"49:orders_model_4.sourced": "415c0d50fd79d684f7e738ac", -"4:orders_model_0.sourced": "dee9dc3632e9c38c6ba815ac", +"49:orders_model_4.sourced": "79ad128ab6507f177ea552ad", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -1360,9 +1365,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71a6cc1ef787af00586cdf2e", +"58:orders_model_11.targeting": "f7609924d233ae6bfda9a51d", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -1371,30 +1375,35 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "ab366fc8e364e0827653c9de", -"69:orders_model_2.sourced": "bd62b4f2d8abfbc93f9a9d9b", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "0474afd0baf489f62d212f54", +"69:orders_model_2.sourced": "d3efe717b762c729423af4be", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "415c0d50fd79d684f7e738ac", +"73:orders_model_5.targeting": "79ad128ab6507f177ea552ad", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "6b86b273ff34fce19d6b804e", -"78:orders_model_8.targeting": "bd62b4f2d8abfbc93f9a9d9b", +"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", +"78:orders_model_8.targeting": "d3efe717b762c729423af4be", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "9cc5472deaed036c6f68e314", -"81:edges": "1960382314765257e21dd758", -"8:orders_model_12.targeting": "de72923afa7d269dec06170d", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "743945954ca7a3c41302cd17", +"81:edges": "9df890b8d68ecee725d3e323" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "3b22e79981224e88a038b75c", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1403,9 +1412,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", -"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", +"19:orders_model_3.sourced": "8805af3e97106cbcae9e604f", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -1414,9 +1422,8 @@ "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", -"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "d5c06226ff05c628e41d8c34", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", @@ -1425,20 +1432,18 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", -"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", +"39:orders_model_10.sourced": "7f5f4e3f56f71c666e1cf4e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -1447,41 +1452,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"58:orders_model_11.targeting": "7f5f4e3f56f71c666e1cf4e6", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"63:orders_model_14.targeting": "8805af3e97106cbcae9e604f", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"73:orders_model_5.targeting": "01ff8f312ff9618ede5382bb", "74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", "78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "37d91f3ee68d4dc24cff6ae3", -"81:edges": "13f3bb62cb14932238e532fe", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "fb1a8ff343f6ac76afdb5837", +"81:edges": "60df3b1d0f0b68e0754bca58" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "d6c75550cd2a38432d5e6574", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1490,9 +1499,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -1502,8 +1510,7 @@ "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -1512,20 +1519,18 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -1536,7 +1541,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -1545,9 +1549,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -1558,17 +1561,23 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "2d7de32bd10b12e919c42b4e", -"81:edges": "2309dd07ea524a7832a2e342", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "1e771b42efaa66a5ee7cc8f7", +"81:edges": "eb5a92deebe12072697fe072" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "3b22e79981224e88a038b75c", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1577,9 +1586,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", -"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", +"19:orders_model_3.sourced": "8805af3e97106cbcae9e604f", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -1588,31 +1596,28 @@ "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", -"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "d5c06226ff05c628e41d8c34", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", -"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "f1945e0627f604c602f1a19d", "34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", -"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", +"39:orders_model_10.sourced": "7f5f4e3f56f71c666e1cf4e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -1621,41 +1626,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"58:orders_model_11.targeting": "7f5f4e3f56f71c666e1cf4e6", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"63:orders_model_14.targeting": "8805af3e97106cbcae9e604f", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"73:orders_model_5.targeting": "01ff8f312ff9618ede5382bb", "74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", -"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"77:orders_model_8.edge_count": "ef2d127de37b942baad06145", +"78:orders_model_8.targeting": "81cc7bd55faa6546ba2a3303", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "37d91f3ee68d4dc24cff6ae3", -"81:edges": "13f3bb62cb14932238e532fe", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "dd9f4ebb6e7f09cc2cbbbb25", +"81:edges": "6bc498ee1db3c58ea64917ac" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "d6c75550cd2a38432d5e6574", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1664,9 +1673,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -1676,8 +1684,7 @@ "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -1686,20 +1693,18 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -1710,7 +1715,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -1719,9 +1723,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -1732,17 +1735,23 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "2d7de32bd10b12e919c42b4e", -"81:edges": "2309dd07ea524a7832a2e342", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "1e771b42efaa66a5ee7cc8f7", +"81:edges": "eb5a92deebe12072697fe072" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "3b22e79981224e88a038b75c", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1751,9 +1760,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", -"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", +"19:orders_model_3.sourced": "8805af3e97106cbcae9e604f", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -1762,9 +1770,8 @@ "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", -"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "d5c06226ff05c628e41d8c34", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", @@ -1773,20 +1780,18 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", -"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", +"39:orders_model_10.sourced": "7f5f4e3f56f71c666e1cf4e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -1795,41 +1800,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"58:orders_model_11.targeting": "7f5f4e3f56f71c666e1cf4e6", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"63:orders_model_14.targeting": "8805af3e97106cbcae9e604f", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"73:orders_model_5.targeting": "01ff8f312ff9618ede5382bb", "74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", "78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "37d91f3ee68d4dc24cff6ae3", -"81:edges": "13f3bb62cb14932238e532fe", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "fb1a8ff343f6ac76afdb5837", +"81:edges": "60df3b1d0f0b68e0754bca58" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "d6c75550cd2a38432d5e6574", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1838,9 +1847,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -1850,8 +1858,7 @@ "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -1860,20 +1867,18 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -1884,7 +1889,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -1893,9 +1897,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -1906,17 +1909,23 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "2d7de32bd10b12e919c42b4e", -"81:edges": "2309dd07ea524a7832a2e342", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "1e771b42efaa66a5ee7cc8f7", +"81:edges": "eb5a92deebe12072697fe072" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "3b22e79981224e88a038b75c", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -1925,9 +1934,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", -"19:orders_model_3.sourced": "7f655ae63057e87b3fa79c3c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", +"19:orders_model_3.sourced": "8805af3e97106cbcae9e604f", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -1936,31 +1944,28 @@ "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", -"28:orders_model_9.targeting": "6ec77a263392a2e5ea8ee05a", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "d5c06226ff05c628e41d8c34", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", -"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "f1945e0627f604c602f1a19d", "34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", -"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", +"39:orders_model_10.sourced": "7f5f4e3f56f71c666e1cf4e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "b390008e1f822c92bd0b26de", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -1969,41 +1974,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"58:orders_model_11.targeting": "7f5f4e3f56f71c666e1cf4e6", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "7f655ae63057e87b3fa79c3c", +"63:orders_model_14.targeting": "8805af3e97106cbcae9e604f", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "2a6f7207b52f1b1ffe6d2ca0", +"73:orders_model_5.targeting": "01ff8f312ff9618ede5382bb", "74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", -"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"77:orders_model_8.edge_count": "ef2d127de37b942baad06145", +"78:orders_model_8.targeting": "81cc7bd55faa6546ba2a3303", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "37d91f3ee68d4dc24cff6ae3", -"81:edges": "13f3bb62cb14932238e532fe", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "dd9f4ebb6e7f09cc2cbbbb25", +"81:edges": "6bc498ee1db3c58ea64917ac" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "d6c75550cd2a38432d5e6574", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -2012,9 +2021,8 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "844829a9cd0c4a620ba890aa", +"18:orders_model_3.targeting": "de3b49e9b64821c2b887aad7", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -2024,8 +2032,7 @@ "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "69251b0e05822c5fbcc9d552", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -2034,20 +2041,18 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", -"38:orders_model_10.targeting": "37bf1d6a9739dac6c07b8f77", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "4654ad70130bf45723d2b8e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "99579faa128e6c8875362de3", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "a65b23ca24a1df85b7ea6b02", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -2058,7 +2063,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -2067,9 +2071,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", "69:orders_model_2.sourced": "5c53070d70386db96f97a69e", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -2080,17 +2083,23 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "2d7de32bd10b12e919c42b4e", -"81:edges": "2309dd07ea524a7832a2e342", -"8:orders_model_12.targeting": "235f9bd1bde8e82fc2f4a84f", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "1e771b42efaa66a5ee7cc8f7", +"81:edges": "eb5a92deebe12072697fe072" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"4:orders_model_0.sourced": "82f913a206fd3861d2dfc13d", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -2101,7 +2110,6 @@ "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", "18:orders_model_3.targeting": "76047432b32c8034e32c2670", "19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -2111,8 +2119,7 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", "28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "f3d42152e63cd118e3aaeccb", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", @@ -2121,9 +2128,8 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"38:orders_model_10.targeting": "9266f701958e3b6ac13db6fb", "39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", @@ -2131,10 +2137,9 @@ "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "d5dead8860388236c2668d99", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -2145,7 +2150,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -2156,7 +2160,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", "69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -2167,17 +2170,23 @@ "77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", "78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "cd078ceff6e73868a0930f57", -"81:edges": "1e08d370da3b775c6f1f8cec", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "265cbd05b4bfa1dae1523ccc", +"81:edges": "38413b31c5a7a31d79ac12dd" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"4:orders_model_0.sourced": "d5dead8860388236c2668d99", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -2188,7 +2197,6 @@ "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", "18:orders_model_3.targeting": "76047432b32c8034e32c2670", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -2198,8 +2206,7 @@ "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "f3d42152e63cd118e3aaeccb", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -2208,9 +2215,8 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"38:orders_model_10.targeting": "9266f701958e3b6ac13db6fb", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", @@ -2218,10 +2224,9 @@ "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "d5dead8860388236c2668d99", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -2232,7 +2237,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -2243,7 +2247,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", "69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -2254,126 +2257,130 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "42d3866a260676aa81413170", -"81:edges": "d40d2911f4142b96d5bebfc1", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "addcdbd28bc939000dd40d79", +"81:edges": "f073ca9a3af7758731dff04d" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "3b22e79981224e88a038b75c", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "76047432b32c8034e32c2670", -"19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "46d6247d80d4c6be98174e3a", +"19:orders_model_3.sourced": "8805af3e97106cbcae9e604f", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "2380b94a2203ebdd80fee91b", -"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"23:orders_model_6.targeting": "e38eef92273ee4afe348ad87", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", -"28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "d5c06226ff05c628e41d8c34", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", -"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", -"34:orders_model_1.sourced": "67bc1693660f805fe71b8562", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "f1945e0627f604c602f1a19d", +"34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", -"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", +"39:orders_model_10.sourced": "7f5f4e3f56f71c666e1cf4e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "67bc1693660f805fe71b8562", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "79fe2d0a0a77035de3d04db1", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"58:orders_model_11.targeting": "7f5f4e3f56f71c666e1cf4e6", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "9402db7a493e80bca6d9bf8c", +"63:orders_model_14.targeting": "8805af3e97106cbcae9e604f", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", -"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "eeeef68a0473424e96f4b55e", -"74:orders_model_5.sourced": "2380b94a2203ebdd80fee91b", +"73:orders_model_5.targeting": "01ff8f312ff9618ede5382bb", +"74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", -"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"77:orders_model_8.edge_count": "ef2d127de37b942baad06145", +"78:orders_model_8.targeting": "81cc7bd55faa6546ba2a3303", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "cd078ceff6e73868a0930f57", -"81:edges": "1e08d370da3b775c6f1f8cec", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "63d80a9475d03b1b6babc3a4", +"81:edges": "ea39f6ad16390f730f659147" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "d6c75550cd2a38432d5e6574", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"18:orders_model_3.targeting": "46d6247d80d4c6be98174e3a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", "23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", -"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", "25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -2381,21 +2388,19 @@ "34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -2406,7 +2411,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -2415,9 +2419,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", -"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -2428,17 +2431,23 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "42d3866a260676aa81413170", -"81:edges": "d40d2911f4142b96d5bebfc1", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "dd1cb21e432169aec4a1e328", +"81:edges": "47a138a1157194674d22323e" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"4:orders_model_0.sourced": "82f913a206fd3861d2dfc13d", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -2449,7 +2458,6 @@ "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", "18:orders_model_3.targeting": "76047432b32c8034e32c2670", "19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -2459,8 +2467,7 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", "28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "f3d42152e63cd118e3aaeccb", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", @@ -2469,9 +2476,8 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"38:orders_model_10.targeting": "9266f701958e3b6ac13db6fb", "39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", @@ -2479,10 +2485,9 @@ "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "d5dead8860388236c2668d99", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -2493,7 +2498,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -2504,7 +2508,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", "69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -2515,17 +2518,23 @@ "77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", "78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "cd078ceff6e73868a0930f57", -"81:edges": "1e08d370da3b775c6f1f8cec", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "265cbd05b4bfa1dae1523ccc", +"81:edges": "38413b31c5a7a31d79ac12dd" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"4:orders_model_0.sourced": "d5dead8860388236c2668d99", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -2536,7 +2545,6 @@ "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", "18:orders_model_3.targeting": "76047432b32c8034e32c2670", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -2546,8 +2554,7 @@ "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "f3d42152e63cd118e3aaeccb", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -2556,9 +2563,8 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"38:orders_model_10.targeting": "9266f701958e3b6ac13db6fb", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", @@ -2566,10 +2572,9 @@ "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", +"48:orders_model_4.targeting": "d5dead8860388236c2668d99", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -2580,7 +2585,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -2591,7 +2595,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", "69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -2602,126 +2605,130 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "42d3866a260676aa81413170", -"81:edges": "d40d2911f4142b96d5bebfc1", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "addcdbd28bc939000dd40d79", +"81:edges": "f073ca9a3af7758731dff04d" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "f6ed9eea5eb9c6327e305a40", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "76047432b32c8034e32c2670", -"19:orders_model_3.sourced": "9402db7a493e80bca6d9bf8c", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "46d6247d80d4c6be98174e3a", +"19:orders_model_3.sourced": "8d265d45d29e720203836349", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "2380b94a2203ebdd80fee91b", -"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"23:orders_model_6.targeting": "e38eef92273ee4afe348ad87", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "4e07408562bedb8b60ce05c1", -"28:orders_model_9.targeting": "09930b7e9d1880a7b1ca2490", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"28:orders_model_9.targeting": "d5c06226ff05c628e41d8c34", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "7902699be42c8a8e46fbbb45", -"33:orders_model_1.targeting": "c9904eaf37f8410c86a5700e", -"34:orders_model_1.sourced": "67bc1693660f805fe71b8562", +"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", +"33:orders_model_1.targeting": "f1945e0627f604c602f1a19d", +"34:orders_model_1.sourced": "79fe2d0a0a77035de3d04db1", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", -"39:orders_model_10.sourced": "71858f9d94fda04aed071aa0", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", +"39:orders_model_10.sourced": "7f5f4e3f56f71c666e1cf4e6", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "0cf680b6627d425d75523b70", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "67bc1693660f805fe71b8562", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "79fe2d0a0a77035de3d04db1", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "71858f9d94fda04aed071aa0", +"58:orders_model_11.targeting": "7f5f4e3f56f71c666e1cf4e6", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "9402db7a493e80bca6d9bf8c", +"63:orders_model_14.targeting": "8d265d45d29e720203836349", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", -"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "eeeef68a0473424e96f4b55e", -"74:orders_model_5.sourced": "2380b94a2203ebdd80fee91b", +"73:orders_model_5.targeting": "ec65e401bd237ac537e036da", +"74:orders_model_5.sourced": "e38eef92273ee4afe348ad87", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4e07408562bedb8b60ce05c1", -"78:orders_model_8.targeting": "46d90f7d3dfc791867204a4c", +"77:orders_model_8.edge_count": "ef2d127de37b942baad06145", +"78:orders_model_8.targeting": "81cc7bd55faa6546ba2a3303", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "cd078ceff6e73868a0930f57", -"81:edges": "1e08d370da3b775c6f1f8cec", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "b3eedccd37a3538668bb7f96", +"81:edges": "cd4c671df8d437b25a183d02" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"3:orders_model_0.targeting": "aee68019acd37d10a817b41d", +"4:orders_model_0.sourced": "d6c75550cd2a38432d5e6574", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "6b86b273ff34fce19d6b804e", +"8:orders_model_12.targeting": "ee8db1ac88a604ccc9c30680", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "2670c877f940c332e0ed75b6", +"13:orders_model_15.targeting": "1964126d46cdab65c924dd1a", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "76047432b32c8034e32c2670", +"18:orders_model_3.targeting": "46d6247d80d4c6be98174e3a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", "23:orders_model_6.targeting": "6786f18f6ff42aa577c99d86", -"24:orders_model_6.sourced": "2670c877f940c332e0ed75b6", +"24:orders_model_6.sourced": "1964126d46cdab65c924dd1a", "25:orders_model_9.has_model": "fcbcf165908dd18a9e49f7ff", "26:orders_model_9.has_star": "fcbcf165908dd18a9e49f7ff", "27:orders_model_9.edge_count": "5feceb66ffc86f38d952786c", "28:orders_model_9.targeting": "6786f18f6ff42aa577c99d86", -"29:orders_model_9.sourced": "ba772cfacb11c32601e27380", -"2:orders_model_0.edge_count": "4e07408562bedb8b60ce05c1", +"29:orders_model_9.sourced": "0736167af6999a1ca8bbc3af", "30:orders_model_1.has_model": "fcbcf165908dd18a9e49f7ff", "31:orders_model_1.has_star": "fcbcf165908dd18a9e49f7ff", "32:orders_model_1.edge_count": "5feceb66ffc86f38d952786c", @@ -2729,21 +2736,19 @@ "34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "e7f6c011776e8db7cd330b54", -"38:orders_model_10.targeting": "86484ebd52c56f5232097531", +"37:orders_model_10.edge_count": "4b227777d4dd1fc61c6f884f", +"38:orders_model_10.targeting": "9ca0255153d28c0d6154a28a", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "d11526ad01eb59472574a2a3", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "a7fe2c291b441e90fb5a61b8", +"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", +"43:orders_model_13.targeting": "2dcfee715e4753e72981967c", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2d45ce46fb153f6a59805da3", +"48:orders_model_4.targeting": "d6c75550cd2a38432d5e6574", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "2d45ce46fb153f6a59805da3", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -2754,7 +2759,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "6786f18f6ff42aa577c99d86", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -2763,9 +2767,8 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "944516a2d4e18b4cc263aa14", -"69:orders_model_2.sourced": "0be757d135da1c02bdf86760", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "83e416387be3b7cde32211a2", +"69:orders_model_2.sourced": "5c53070d70386db96f97a69e", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -2776,17 +2779,23 @@ "77:orders_model_8.edge_count": "5feceb66ffc86f38d952786c", "78:orders_model_8.targeting": "6786f18f6ff42aa577c99d86", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "42d3866a260676aa81413170", -"81:edges": "d40d2911f4142b96d5bebfc1", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "dd1cb21e432169aec4a1e328", +"81:edges": "47a138a1157194674d22323e" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "8e37616080b3d31ce9a9ebed", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -2794,10 +2803,9 @@ "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"17:orders_model_3.edge_count": "6b86b273ff34fce19d6b804e", +"18:orders_model_3.targeting": "f4a5e31e638eecc9056cff7a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -2807,30 +2815,27 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", "33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"43:orders_model_13.targeting": "890bab62e965e59b44e3f645", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", -"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "04ab2e934d65095923df3f91", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -2839,9 +2844,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"58:orders_model_11.targeting": "b5dcaec1a8ff1f7ba123eb77", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -2850,30 +2854,35 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "3e0cdfcee002eb4e503eeccc", +"69:orders_model_2.sourced": "c7a63cf925eed47cb44bb860", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "766e6cff4a7c97985d321915", -"81:edges": "66def530d7ac79c6a553f347", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "77250e40495dfc84ea5602ca", +"81:edges": "4b9b4d65908745ce615e818b" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "7e060fd44c3105ad0992a036", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -2884,7 +2893,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -2894,19 +2902,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", "33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -2915,9 +2921,8 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", -"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "81a68bfd82b628c40476efb5", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -2928,7 +2933,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -2939,39 +2943,43 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "e3f9826cd489c85cd139787f", -"81:edges": "ff90a6fe0029bd81150fd61c", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "ad00119fb32649d2c9293e70", +"81:edges": "90fc9e8c26e158f7bad0ca0a" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "8e37616080b3d31ce9a9ebed", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"13:orders_model_15.targeting": "0ce6d818d1610e8e65b1b23e", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"17:orders_model_3.edge_count": "6b86b273ff34fce19d6b804e", +"18:orders_model_3.targeting": "f4a5e31e638eecc9056cff7a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -2981,73 +2989,74 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"43:orders_model_13.targeting": "890bab62e965e59b44e3f645", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", -"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "04ab2e934d65095923df3f91", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", "53:orders_model_7.targeting": "723ca453518b102baf754807", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"58:orders_model_11.targeting": "b5dcaec1a8ff1f7ba123eb77", +"59:orders_model_11.sourced": "be2a84a3926b97ae72762cbb", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "be2a84a3926b97ae72762cbb", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "3e0cdfcee002eb4e503eeccc", +"69:orders_model_2.sourced": "4a5554fd93e075e1708287ca", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "766e6cff4a7c97985d321915", -"81:edges": "66def530d7ac79c6a553f347", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "8613beb56787f7fe0c35a415", +"81:edges": "8290e75b7b2cd2a3906c03ec" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "7e060fd44c3105ad0992a036", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -3058,7 +3067,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -3068,19 +3076,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -3089,63 +3095,65 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", -"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "81a68bfd82b628c40476efb5", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", "53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"59:orders_model_11.sourced": "be2a84a3926b97ae72762cbb", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "be2a84a3926b97ae72762cbb", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "e3f9826cd489c85cd139787f", -"81:edges": "ff90a6fe0029bd81150fd61c", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "7098e22aff0fcaaedbfd4bcc", +"81:edges": "69d0ce1ada77bb3e1cb7529f" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", -"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", -"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", -"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", -"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "8e37616080b3d31ce9a9ebed", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", +"10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", +"11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", +"12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", +"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"17:orders_model_3.edge_count": "6b86b273ff34fce19d6b804e", +"18:orders_model_3.targeting": "f4a5e31e638eecc9056cff7a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -3155,30 +3163,27 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", "33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"43:orders_model_13.targeting": "890bab62e965e59b44e3f645", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", -"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "04ab2e934d65095923df3f91", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -3187,9 +3192,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", +"58:orders_model_11.targeting": "b5dcaec1a8ff1f7ba123eb77", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -3198,30 +3202,35 @@ "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "3e0cdfcee002eb4e503eeccc", +"69:orders_model_2.sourced": "c7a63cf925eed47cb44bb860", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "766e6cff4a7c97985d321915", -"81:edges": "66def530d7ac79c6a553f347", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "77250e40495dfc84ea5602ca", +"81:edges": "4b9b4d65908745ce615e818b" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "7e060fd44c3105ad0992a036", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -3232,7 +3241,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -3242,19 +3250,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", "33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -3263,9 +3269,8 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", -"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "81a68bfd82b628c40476efb5", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -3276,7 +3281,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -3287,39 +3291,43 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "e3f9826cd489c85cd139787f", -"81:edges": "ff90a6fe0029bd81150fd61c", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "ad00119fb32649d2c9293e70", +"81:edges": "90fc9e8c26e158f7bad0ca0a" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "8e37616080b3d31ce9a9ebed", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"13:orders_model_15.targeting": "0ce6d818d1610e8e65b1b23e", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "c6aabcf090220f68834084fb", +"17:orders_model_3.edge_count": "6b86b273ff34fce19d6b804e", +"18:orders_model_3.targeting": "f4a5e31e638eecc9056cff7a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -3329,73 +3337,74 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"43:orders_model_13.targeting": "890bab62e965e59b44e3f645", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "3c447bb413c543ef9ff9f02b", -"4:orders_model_0.sourced": "2dcc5c2c565c905a98b1f3e0", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "04ab2e934d65095923df3f91", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", "53:orders_model_7.targeting": "723ca453518b102baf754807", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"58:orders_model_11.targeting": "b5dcaec1a8ff1f7ba123eb77", +"59:orders_model_11.sourced": "bdafcc11fe0543411e1610c3", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "bdafcc11fe0543411e1610c3", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "dbcb0d17df16270e8948b6a7", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "3e0cdfcee002eb4e503eeccc", +"69:orders_model_2.sourced": "4a5554fd93e075e1708287ca", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "766e6cff4a7c97985d321915", -"81:edges": "66def530d7ac79c6a553f347", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "2488a9d5025e37aeb9c1b218", +"81:edges": "0331b932d6829fa30ffadcf0" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "7e060fd44c3105ad0992a036", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "4e85e92220b909cdfd5ef3f6", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -3406,7 +3415,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -3416,19 +3424,17 @@ "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "ebabe32d36bc3dcf101f953f", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", -"37:orders_model_10.edge_count": "d4735e3a265e16eee03f5971", -"38:orders_model_10.targeting": "1b17f70a4e68bc83fc11a319", +"37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -3437,52 +3443,55 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "39f9d2b502e4a51f5da00952", -"4:orders_model_0.sourced": "c67fcc4d7a42429aab9315c9", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "81a68bfd82b628c40476efb5", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", "53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"59:orders_model_11.sourced": "bdafcc11fe0543411e1610c3", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "bdafcc11fe0543411e1610c3", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "39f9d2b502e4a51f5da00952", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "e3f9826cd489c85cd139787f", -"81:edges": "ff90a6fe0029bd81150fd61c", -"8:orders_model_12.targeting": "d7d16e7a663f8aecabea3ed6", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "d29965054690ee9b68764420", +"81:edges": "076f5d2be102144013ff9e21" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"4:orders_model_0.sourced": "434259a07e47113a3bb2221b", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -3493,7 +3502,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -3504,7 +3512,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", "29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", @@ -3515,18 +3522,16 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"42:orders_model_13.edge_count": "6b86b273ff34fce19d6b804e", +"43:orders_model_13.targeting": "10abe28d8c9bb85d5d0169c4", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"47:orders_model_4.edge_count": "6b86b273ff34fce19d6b804e", +"48:orders_model_4.targeting": "32a9b69149a17769369b2037", "49:orders_model_4.sourced": "801fdd180a6d4964a072f211", -"4:orders_model_0.sourced": "9efe230670328edc3abca75d", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -3537,7 +3542,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -3547,8 +3551,7 @@ "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"69:orders_model_2.sourced": "910a66c0655c07b49ece72dc", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -3559,17 +3562,23 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "726a3631488829e7d85f22a4", -"81:edges": "353ba5589c448640b32285cf", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "8f262afe99b2adfdcfdbe1f6", +"81:edges": "a5da6bbc05319686b7fd071f" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0ec34324e275d32e6760bf1e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -3580,7 +3589,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -3591,7 +3599,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", "29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", @@ -3602,7 +3609,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -3610,10 +3616,9 @@ "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"47:orders_model_4.edge_count": "6b86b273ff34fce19d6b804e", +"48:orders_model_4.targeting": "32a9b69149a17769369b2037", "49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", -"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -3624,7 +3629,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -3635,7 +3639,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -3646,104 +3649,110 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "634a199b0204ba6c3ca102dc", -"81:edges": "5258bb66e465fbd07d87c222", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "5ac26f100c32e0d9c3600c1f", +"81:edges": "b44acc01611556ffb71401fb" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "8e37616080b3d31ce9a9ebed", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "ac4ce157c76d0556f7dae315", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"13:orders_model_15.targeting": "0ce6d818d1610e8e65b1b23e", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", -"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", -"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"17:orders_model_3.edge_count": "6b86b273ff34fce19d6b804e", +"18:orders_model_3.targeting": "f4a5e31e638eecc9056cff7a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "1bc33c8cc436098dc553984f", +"23:orders_model_6.targeting": "cf6090675ae627461ef89e59", "24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"43:orders_model_13.targeting": "890bab62e965e59b44e3f645", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "801fdd180a6d4964a072f211", -"4:orders_model_0.sourced": "9efe230670328edc3abca75d", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "04ab2e934d65095923df3f91", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "a14cb265582196761feee603", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "723ca453518b102baf754807", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"58:orders_model_11.targeting": "b5dcaec1a8ff1f7ba123eb77", +"59:orders_model_11.sourced": "be2a84a3926b97ae72762cbb", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "be2a84a3926b97ae72762cbb", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "3e0cdfcee002eb4e503eeccc", +"69:orders_model_2.sourced": "4a5554fd93e075e1708287ca", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "726a3631488829e7d85f22a4", -"81:edges": "353ba5589c448640b32285cf", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "6cf735c1be935314b9613133", +"81:edges": "dcb26ef8b356a02c2f36805c" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "7e060fd44c3105ad0992a036", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "ac4ce157c76d0556f7dae315", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -3754,7 +3763,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -3763,20 +3771,18 @@ "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -3785,52 +3791,55 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", -"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "81a68bfd82b628c40476efb5", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", "53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"59:orders_model_11.sourced": "be2a84a3926b97ae72762cbb", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "be2a84a3926b97ae72762cbb", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"73:orders_model_5.targeting": "81a68bfd82b628c40476efb5", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "634a199b0204ba6c3ca102dc", -"81:edges": "5258bb66e465fbd07d87c222", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "8d010edb3e511f2a8ce44531", +"81:edges": "cd059dd936f456a4fe43e6df" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"4:orders_model_0.sourced": "434259a07e47113a3bb2221b", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -3841,7 +3850,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -3852,7 +3860,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", "29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", @@ -3863,18 +3870,16 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"42:orders_model_13.edge_count": "6b86b273ff34fce19d6b804e", +"43:orders_model_13.targeting": "10abe28d8c9bb85d5d0169c4", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"47:orders_model_4.edge_count": "6b86b273ff34fce19d6b804e", +"48:orders_model_4.targeting": "32a9b69149a17769369b2037", "49:orders_model_4.sourced": "801fdd180a6d4964a072f211", -"4:orders_model_0.sourced": "9efe230670328edc3abca75d", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -3885,7 +3890,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -3895,8 +3899,7 @@ "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"69:orders_model_2.sourced": "910a66c0655c07b49ece72dc", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -3907,17 +3910,23 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "726a3631488829e7d85f22a4", -"81:edges": "353ba5589c448640b32285cf", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "8f262afe99b2adfdcfdbe1f6", +"81:edges": "a5da6bbc05319686b7fd071f" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "0ec34324e275d32e6760bf1e", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -3928,7 +3937,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -3939,7 +3947,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", "29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "ef2d127de37b942baad06145", @@ -3950,7 +3957,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -3958,10 +3964,9 @@ "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", +"47:orders_model_4.edge_count": "6b86b273ff34fce19d6b804e", +"48:orders_model_4.targeting": "32a9b69149a17769369b2037", "49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", -"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -3972,7 +3977,6 @@ "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", "59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -3983,7 +3987,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -3994,104 +3997,110 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "efe9b5370341db1214ac6791", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "634a199b0204ba6c3ca102dc", -"81:edges": "5258bb66e465fbd07d87c222", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "5ac26f100c32e0d9c3600c1f", +"81:edges": "b44acc01611556ffb71401fb" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "8e37616080b3d31ce9a9ebed", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "ac4ce157c76d0556f7dae315", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "c64df03c7fbbe86c07c4c602", +"13:orders_model_15.targeting": "0ce6d818d1610e8e65b1b23e", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", -"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", -"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", +"17:orders_model_3.edge_count": "6b86b273ff34fce19d6b804e", +"18:orders_model_3.targeting": "f4a5e31e638eecc9056cff7a", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "1bc33c8cc436098dc553984f", +"23:orders_model_6.targeting": "cf6090675ae627461ef89e59", "24:orders_model_6.sourced": "6786f18f6ff42aa577c99d86", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "d4735e3a265e16eee03f5971", -"43:orders_model_13.targeting": "fb2bd7475476ed8980a4ac2c", +"43:orders_model_13.targeting": "890bab62e965e59b44e3f645", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "801fdd180a6d4964a072f211", -"4:orders_model_0.sourced": "9efe230670328edc3abca75d", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "118aee16f181d7cb27d6826b", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "a14cb265582196761feee603", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "723ca453518b102baf754807", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "8e0e0c410e143c02cde2f39d", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"58:orders_model_11.targeting": "b5dcaec1a8ff1f7ba123eb77", +"59:orders_model_11.sourced": "bdafcc11fe0543411e1610c3", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "bdafcc11fe0543411e1610c3", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "a1cfbb075e39a1f31cf0a389", -"69:orders_model_2.sourced": "bed262571d7e3d10fddbcd5c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "3e0cdfcee002eb4e503eeccc", +"69:orders_model_2.sourced": "4a5554fd93e075e1708287ca", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"73:orders_model_5.targeting": "3bd0c7b5e829a3117c48f1ca", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "726a3631488829e7d85f22a4", -"81:edges": "353ba5589c448640b32285cf", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "fe1116052660b2d7c16262af", +"81:edges": "da10dea66d15d5ba9acfba16" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "7e060fd44c3105ad0992a036", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "ac4ce157c76d0556f7dae315", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "fcbcf165908dd18a9e49f7ff", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "5feceb66ffc86f38d952786c", @@ -4102,7 +4111,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "6786f18f6ff42aa577c99d86", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "fcbcf165908dd18a9e49f7ff", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "5feceb66ffc86f38d952786c", @@ -4111,20 +4119,18 @@ "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "dd982a80f52d1f2e0b2aa038", -"29:orders_model_9.sourced": "1b17f70a4e68bc83fc11a319", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"28:orders_model_9.targeting": "ba0aa56821a51ebc64bd22bd", +"29:orders_model_9.sourced": "59516ca47a52ce6effacbbdc", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", -"32:orders_model_1.edge_count": "ef2d127de37b942baad06145", -"33:orders_model_1.targeting": "a1542a5c221b33c2756c09ec", -"34:orders_model_1.sourced": "e6943a672bbc12e8187aada0", +"32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", +"33:orders_model_1.targeting": "eb0ba2ccc248a42fdd4801d9", +"34:orders_model_1.sourced": "24f24c7f04de860766f65dd4", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "c5c2638bd29835ebf459d9a4", +"38:orders_model_10.targeting": "06bb834b82875fa7c69744a7", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -4133,52 +4139,55 @@ "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", "47:orders_model_4.edge_count": "d4735e3a265e16eee03f5971", -"48:orders_model_4.targeting": "f50bdb75eee2988eed8ac15d", -"49:orders_model_4.sourced": "387649cf44d2ffb7009909a1", -"4:orders_model_0.sourced": "6ee202eaef83cf0a4a6ed539", +"48:orders_model_4.targeting": "3daa1b496c25aac19650d330", +"49:orders_model_4.sourced": "3bd0c7b5e829a3117c48f1ca", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", "53:orders_model_7.targeting": "6786f18f6ff42aa577c99d86", -"54:orders_model_7.sourced": "efe9b5370341db1214ac6791", +"54:orders_model_7.sourced": "bac638b21a9370f38ca8e782", "55:orders_model_11.has_model": "fcbcf165908dd18a9e49f7ff", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "5feceb66ffc86f38d952786c", "58:orders_model_11.targeting": "6786f18f6ff42aa577c99d86", -"59:orders_model_11.sourced": "994b51789f15903e6e3ff4f7", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"59:orders_model_11.sourced": "bdafcc11fe0543411e1610c3", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "994b51789f15903e6e3ff4f7", +"63:orders_model_14.targeting": "bdafcc11fe0543411e1610c3", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "fcbcf165908dd18a9e49f7ff", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "387649cf44d2ffb7009909a1", +"73:orders_model_5.targeting": "3bd0c7b5e829a3117c48f1ca", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "efe9b5370341db1214ac6791", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "bac638b21a9370f38ca8e782", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "634a199b0204ba6c3ca102dc", -"81:edges": "5258bb66e465fbd07d87c222", -"8:orders_model_12.targeting": "8a14701822a8a3706d504b23", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "106c0fe3539070f03fa68c82", +"81:edges": "c73178de64843f9efa8a5cf5" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4187,31 +4196,28 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", +"18:orders_model_3.targeting": "e521909a2fac11893ae5b5ae", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "1ba5a493bfc856d8a9385414", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", "33:orders_model_1.targeting": "452427dd584527df4f8ece1d", -"34:orders_model_1.sourced": "3099535ea109c12832471665", +"34:orders_model_1.sourced": "e05e3a7e05249cb12b8be71b", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -4222,7 +4228,6 @@ "47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", "48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -4231,41 +4236,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"63:orders_model_14.targeting": "3090be96d94c07fa82955d5d", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", -"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "298d5b8a35679c43fb6941c0", +"69:orders_model_2.sourced": "72e65253e3b52c42cc301d3d", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"73:orders_model_5.targeting": "b97016fc29f1f6ec26ef8fc7", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "51075542f67bd94c29d3f0c4", -"81:edges": "bbe2efc481c2f6f64d4e62a1", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "c5ed5fd791a3bd2be9bcb2c7", +"81:edges": "7bb824b41d56e86ebaea79c1" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4276,18 +4285,16 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "1ba5a493bfc856d8a9385414", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -4298,7 +4305,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -4309,7 +4315,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -4318,9 +4323,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -4331,7 +4335,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -4342,17 +4345,23 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "162bd27b0ba0371264b7f846", -"81:edges": "7f0f5f6e0554b4505652d3aa", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "4111e82444dd000efd53ccbd", +"81:edges": "f25440bef65fac94b909ae29" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4361,31 +4370,28 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "e521909a2fac11893ae5b5ae", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", "33:orders_model_1.targeting": "452427dd584527df4f8ece1d", -"34:orders_model_1.sourced": "3099535ea109c12832471665", +"34:orders_model_1.sourced": "e05e3a7e05249cb12b8be71b", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -4396,7 +4402,6 @@ "47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", "48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -4405,41 +4410,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"63:orders_model_14.targeting": "3090be96d94c07fa82955d5d", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", -"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "298d5b8a35679c43fb6941c0", +"69:orders_model_2.sourced": "72e65253e3b52c42cc301d3d", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"73:orders_model_5.targeting": "b97016fc29f1f6ec26ef8fc7", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "51075542f67bd94c29d3f0c4", -"81:edges": "bbe2efc481c2f6f64d4e62a1", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "cad28d453612016de9d710ad", +"81:edges": "94ef51eb7120c6d28d78e7a9" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4449,19 +4458,17 @@ "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -4470,9 +4477,8 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -4483,7 +4489,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -4492,9 +4497,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -4505,7 +4509,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -4513,20 +4516,26 @@ "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "162bd27b0ba0371264b7f846", -"81:edges": "7f0f5f6e0554b4505652d3aa", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "dd4819e2c32cab1e116b1e8a", +"81:edges": "e2c3abddd046f87a696fcea8" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4535,31 +4544,28 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", +"18:orders_model_3.targeting": "e521909a2fac11893ae5b5ae", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "1ba5a493bfc856d8a9385414", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", "33:orders_model_1.targeting": "452427dd584527df4f8ece1d", -"34:orders_model_1.sourced": "3099535ea109c12832471665", +"34:orders_model_1.sourced": "e05e3a7e05249cb12b8be71b", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -4570,7 +4576,6 @@ "47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", "48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -4579,41 +4584,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"63:orders_model_14.targeting": "3090be96d94c07fa82955d5d", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", -"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "298d5b8a35679c43fb6941c0", +"69:orders_model_2.sourced": "72e65253e3b52c42cc301d3d", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"73:orders_model_5.targeting": "b97016fc29f1f6ec26ef8fc7", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "51075542f67bd94c29d3f0c4", -"81:edges": "bbe2efc481c2f6f64d4e62a1", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "c5ed5fd791a3bd2be9bcb2c7", +"81:edges": "7bb824b41d56e86ebaea79c1" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4624,18 +4633,16 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "1ba5a493bfc856d8a9385414", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -4646,7 +4653,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -4657,7 +4663,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -4666,9 +4671,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -4679,7 +4683,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -4690,17 +4693,23 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "162bd27b0ba0371264b7f846", -"81:edges": "7f0f5f6e0554b4505652d3aa", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "4111e82444dd000efd53ccbd", +"81:edges": "f25440bef65fac94b909ae29" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4709,31 +4718,28 @@ "15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", -"18:orders_model_3.targeting": "5d4db6051418c956e0e9bf9f", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"18:orders_model_3.targeting": "e521909a2fac11893ae5b5ae", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", "33:orders_model_1.targeting": "452427dd584527df4f8ece1d", -"34:orders_model_1.sourced": "3099535ea109c12832471665", +"34:orders_model_1.sourced": "e05e3a7e05249cb12b8be71b", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", @@ -4744,7 +4750,6 @@ "47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", "48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", @@ -4753,41 +4758,45 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "c65a7937992d637d9dc783ca", +"63:orders_model_14.targeting": "3090be96d94c07fa82955d5d", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", -"69:orders_model_2.sourced": "931996e30dd330a5cf09825c", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "298d5b8a35679c43fb6941c0", +"69:orders_model_2.sourced": "72e65253e3b52c42cc301d3d", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "9509b49ef34d9b478bf17b70", +"73:orders_model_5.targeting": "b97016fc29f1f6ec26ef8fc7", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "51075542f67bd94c29d3f0c4", -"81:edges": "bbe2efc481c2f6f64d4e62a1", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "cad28d453612016de9d710ad", +"81:edges": "94ef51eb7120c6d28d78e7a9" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "6b6e7d84a554f1c399102dc2", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4797,19 +4806,17 @@ "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", "23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", -"24:orders_model_6.sourced": "1883ac21741e29509968e6b0", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -4818,9 +4825,8 @@ "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -4831,7 +4837,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -4840,9 +4845,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", "59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", -"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -4853,7 +4857,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -4861,20 +4864,26 @@ "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", -"80:models": "162bd27b0ba0371264b7f846", -"81:edges": "7f0f5f6e0554b4505652d3aa", -"8:orders_model_12.targeting": "7eb1a6cd5b907b1881d7b4cb", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "dd4819e2c32cab1e116b1e8a", +"81:edges": "e2c3abddd046f87a696fcea8" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4885,7 +4894,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -4896,7 +4904,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -4907,7 +4914,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", @@ -4918,7 +4924,6 @@ "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", "48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -4929,7 +4934,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", "59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -4940,7 +4944,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", "69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -4951,17 +4954,23 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", "80:models": "27e54dae2504a7f831e0369b", -"81:edges": "3530701087d04534df62ce37", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"81:edges": "3530701087d04534df62ce37" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -4972,7 +4981,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -4983,7 +4991,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -4994,7 +5001,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -5005,7 +5011,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -5016,7 +5021,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", "59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -5027,7 +5031,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -5038,137 +5041,140 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", "80:models": "582bc1ad59a348db0ee52665", -"81:edges": "d24dafbe5842db03ac5ee2e0", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"81:edges": "d24dafbe5842db03ac5ee2e0" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "2e559d49d7d0b34e2fb72a1e", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", -"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", -"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "ede16fe13c0e215ff994d215", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", -"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", -"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", -"34:orders_model_1.sourced": "52882debecc7c972eb26e4ff", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "e05e3a7e05249cb12b8be71b", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", +"47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", +"48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "9df29af9bb05304b24f1c2a5", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "537db932e35c0fb59ac91164", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", -"59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "2f589a910397d13d89a42610", +"63:orders_model_14.targeting": "3090be96d94c07fa82955d5d", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", -"69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "298d5b8a35679c43fb6941c0", +"69:orders_model_2.sourced": "72e65253e3b52c42cc301d3d", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "720842844b7fea44c5badfc4", +"73:orders_model_5.targeting": "b97016fc29f1f6ec26ef8fc7", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "27e54dae2504a7f831e0369b", -"81:edges": "3530701087d04534df62ce37", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "64c1751a718523064895b2d8", +"81:edges": "61c84ea2095f6eb3fee182d6" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "2e559d49d7d0b34e2fb72a1e", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", -"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", -"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", "34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -5179,7 +5185,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -5188,9 +5193,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", -"59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -5201,7 +5205,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -5209,20 +5212,26 @@ "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "582bc1ad59a348db0ee52665", -"81:edges": "d24dafbe5842db03ac5ee2e0", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "9d57ed38acd5f54a80de27b4", +"81:edges": "3414804b470666ada13b9648" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -5233,7 +5242,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -5244,7 +5252,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -5255,7 +5262,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", "42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", @@ -5266,7 +5272,6 @@ "47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", "48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", @@ -5277,7 +5282,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", "59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", @@ -5288,7 +5292,6 @@ "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", "68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", "69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", @@ -5299,17 +5302,23 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", "80:models": "27e54dae2504a7f831e0369b", -"81:edges": "3530701087d04534df62ce37", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"81:edges": "3530701087d04534df62ce37" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", +"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", @@ -5320,7 +5329,6 @@ "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", "19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", @@ -5331,7 +5339,6 @@ "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", "28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", @@ -5342,7 +5349,6 @@ "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", "38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -5353,7 +5359,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -5364,7 +5369,6 @@ "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", "58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", "59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -5375,7 +5379,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -5386,137 +5389,140 @@ "77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", "78:orders_model_8.targeting": "52ad3bd8074b30476404477a", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", "80:models": "582bc1ad59a348db0ee52665", -"81:edges": "d24dafbe5842db03ac5ee2e0", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"81:edges": "d24dafbe5842db03ac5ee2e0" } ], [ "lineage", { "0:orders_model_0.has_model": "b5bea41b6c623f7c09f1bf24", +"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", +"3:orders_model_0.targeting": "24786d0b1ed2268bbe02c10e", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "2e559d49d7d0b34e2fb72a1e", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", -"15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", +"15:orders_model_3.has_model": "b5bea41b6c623f7c09f1bf24", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", -"17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", -"18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "b5bea41b6c623f7c09f1bf24", +"17:orders_model_3.edge_count": "d4735e3a265e16eee03f5971", +"18:orders_model_3.targeting": "ede16fe13c0e215ff994d215", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", -"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "d4735e3a265e16eee03f5971", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", -"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", -"34:orders_model_1.sourced": "52882debecc7c972eb26e4ff", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", +"34:orders_model_1.sourced": "e05e3a7e05249cb12b8be71b", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "10f59ba6b47d64ce6b56d32b", "40:orders_model_13.has_model": "b5bea41b6c623f7c09f1bf24", "41:orders_model_13.has_star": "b5bea41b6c623f7c09f1bf24", -"42:orders_model_13.edge_count": "4e07408562bedb8b60ce05c1", -"43:orders_model_13.targeting": "c5bd06cf3f6a094d7fb244f2", +"42:orders_model_13.edge_count": "4b227777d4dd1fc61c6f884f", +"43:orders_model_13.targeting": "15745747c1d507b3b5c6d94e", "44:orders_model_13.sourced": "6786f18f6ff42aa577c99d86", "45:orders_model_4.has_model": "b5bea41b6c623f7c09f1bf24", "46:orders_model_4.has_star": "b5bea41b6c623f7c09f1bf24", -"47:orders_model_4.edge_count": "4e07408562bedb8b60ce05c1", -"48:orders_model_4.targeting": "2fe1b26c19346af87d9dce63", +"47:orders_model_4.edge_count": "4b227777d4dd1fc61c6f884f", +"48:orders_model_4.targeting": "190d3318e6fbd9ae444edfd6", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "b5bea41b6c623f7c09f1bf24", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", -"52:orders_model_7.edge_count": "7902699be42c8a8e46fbbb45", -"53:orders_model_7.targeting": "9df29af9bb05304b24f1c2a5", +"52:orders_model_7.edge_count": "19581e27de7ced00ff1ce50b", +"53:orders_model_7.targeting": "537db932e35c0fb59ac91164", "54:orders_model_7.sourced": "6786f18f6ff42aa577c99d86", "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", -"59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", "60:orders_model_14.has_model": "b5bea41b6c623f7c09f1bf24", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "d4735e3a265e16eee03f5971", -"63:orders_model_14.targeting": "2f589a910397d13d89a42610", +"63:orders_model_14.targeting": "f63576d0da13d3ee8bfdbf30", "64:orders_model_14.sourced": "6786f18f6ff42aa577c99d86", "65:orders_model_2.has_model": "b5bea41b6c623f7c09f1bf24", "66:orders_model_2.has_star": "fcbcf165908dd18a9e49f7ff", "67:orders_model_2.edge_count": "6b86b273ff34fce19d6b804e", -"68:orders_model_2.targeting": "62f420101b911c5efeabdd8a", -"69:orders_model_2.sourced": "8320fd4ca2df04ccf0261f2f", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"68:orders_model_2.targeting": "298d5b8a35679c43fb6941c0", +"69:orders_model_2.sourced": "5c72eea1190c5e8b2c441550", "70:orders_model_5.has_model": "b5bea41b6c623f7c09f1bf24", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "d4735e3a265e16eee03f5971", -"73:orders_model_5.targeting": "720842844b7fea44c5badfc4", +"73:orders_model_5.targeting": "5ed587f142c7cc6fbf1b6c9b", "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "27e54dae2504a7f831e0369b", -"81:edges": "3530701087d04534df62ce37", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "a3934c39d8cb6f03e8f7cdd6", +"81:edges": "a4fa14227c0bffa6291f91e5" } ], [ "lineage", { "0:orders_model_0.has_model": "fcbcf165908dd18a9e49f7ff", +"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", +"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", +"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", +"5:orders_model_12.has_model": "b5bea41b6c623f7c09f1bf24", +"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", +"7:orders_model_12.edge_count": "d4735e3a265e16eee03f5971", +"8:orders_model_12.targeting": "2e559d49d7d0b34e2fb72a1e", +"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86", "10:orders_model_15.has_model": "b5bea41b6c623f7c09f1bf24", "11:orders_model_15.has_star": "fcbcf165908dd18a9e49f7ff", "12:orders_model_15.edge_count": "4e07408562bedb8b60ce05c1", -"13:orders_model_15.targeting": "623473241d8ca9bda2996980", +"13:orders_model_15.targeting": "3a2211ec7f6ecf213884189f", "14:orders_model_15.sourced": "6786f18f6ff42aa577c99d86", "15:orders_model_3.has_model": "fcbcf165908dd18a9e49f7ff", "16:orders_model_3.has_star": "fcbcf165908dd18a9e49f7ff", "17:orders_model_3.edge_count": "5feceb66ffc86f38d952786c", "18:orders_model_3.targeting": "6786f18f6ff42aa577c99d86", -"19:orders_model_3.sourced": "67ac37eb58f45c8042ba79fb", -"1:orders_model_0.has_star": "fcbcf165908dd18a9e49f7ff", +"19:orders_model_3.sourced": "8c1dc968fef9c4d3ebd841c0", "20:orders_model_6.has_model": "b5bea41b6c623f7c09f1bf24", "21:orders_model_6.has_star": "fcbcf165908dd18a9e49f7ff", "22:orders_model_6.edge_count": "4e07408562bedb8b60ce05c1", -"23:orders_model_6.targeting": "f8fbb61b3c961fa2694c9c39", -"24:orders_model_6.sourced": "38e17123b61705a17df7d300", +"23:orders_model_6.targeting": "bba7433c100a9ca120f6fbcf", +"24:orders_model_6.sourced": "afa78feb25c212fcbee75d31", "25:orders_model_9.has_model": "b5bea41b6c623f7c09f1bf24", "26:orders_model_9.has_star": "b5bea41b6c623f7c09f1bf24", "27:orders_model_9.edge_count": "d4735e3a265e16eee03f5971", -"28:orders_model_9.targeting": "67ac37eb58f45c8042ba79fb", +"28:orders_model_9.targeting": "8c1dc968fef9c4d3ebd841c0", "29:orders_model_9.sourced": "6786f18f6ff42aa577c99d86", -"2:orders_model_0.edge_count": "5feceb66ffc86f38d952786c", "30:orders_model_1.has_model": "b5bea41b6c623f7c09f1bf24", "31:orders_model_1.has_star": "b5bea41b6c623f7c09f1bf24", "32:orders_model_1.edge_count": "4e07408562bedb8b60ce05c1", -"33:orders_model_1.targeting": "18ac30c5dd99f8c1b7a2db88", +"33:orders_model_1.targeting": "452427dd584527df4f8ece1d", "34:orders_model_1.sourced": "6786f18f6ff42aa577c99d86", "35:orders_model_10.has_model": "b5bea41b6c623f7c09f1bf24", "36:orders_model_10.has_star": "b5bea41b6c623f7c09f1bf24", "37:orders_model_10.edge_count": "4e07408562bedb8b60ce05c1", -"38:orders_model_10.targeting": "6ce4efde8f1573118d43d489", +"38:orders_model_10.targeting": "27ce031841f10335f63d3d81", "39:orders_model_10.sourced": "6786f18f6ff42aa577c99d86", -"3:orders_model_0.targeting": "6786f18f6ff42aa577c99d86", "40:orders_model_13.has_model": "fcbcf165908dd18a9e49f7ff", "41:orders_model_13.has_star": "fcbcf165908dd18a9e49f7ff", "42:orders_model_13.edge_count": "5feceb66ffc86f38d952786c", @@ -5527,7 +5533,6 @@ "47:orders_model_4.edge_count": "5feceb66ffc86f38d952786c", "48:orders_model_4.targeting": "6786f18f6ff42aa577c99d86", "49:orders_model_4.sourced": "6786f18f6ff42aa577c99d86", -"4:orders_model_0.sourced": "75fb8b4ea541b83a96bb78aa", "50:orders_model_7.has_model": "fcbcf165908dd18a9e49f7ff", "51:orders_model_7.has_star": "fcbcf165908dd18a9e49f7ff", "52:orders_model_7.edge_count": "5feceb66ffc86f38d952786c", @@ -5536,9 +5541,8 @@ "55:orders_model_11.has_model": "b5bea41b6c623f7c09f1bf24", "56:orders_model_11.has_star": "fcbcf165908dd18a9e49f7ff", "57:orders_model_11.edge_count": "6b86b273ff34fce19d6b804e", -"58:orders_model_11.targeting": "422c92f9dd61b2ef65dee3be", -"59:orders_model_11.sourced": "623473241d8ca9bda2996980", -"5:orders_model_12.has_model": "fcbcf165908dd18a9e49f7ff", +"58:orders_model_11.targeting": "63c799142d7ae70d96822fd3", +"59:orders_model_11.sourced": "3a2211ec7f6ecf213884189f", "60:orders_model_14.has_model": "fcbcf165908dd18a9e49f7ff", "61:orders_model_14.has_star": "fcbcf165908dd18a9e49f7ff", "62:orders_model_14.edge_count": "5feceb66ffc86f38d952786c", @@ -5549,7 +5553,6 @@ "67:orders_model_2.edge_count": "5feceb66ffc86f38d952786c", "68:orders_model_2.targeting": "6786f18f6ff42aa577c99d86", "69:orders_model_2.sourced": "6786f18f6ff42aa577c99d86", -"6:orders_model_12.has_star": "fcbcf165908dd18a9e49f7ff", "70:orders_model_5.has_model": "fcbcf165908dd18a9e49f7ff", "71:orders_model_5.has_star": "fcbcf165908dd18a9e49f7ff", "72:orders_model_5.edge_count": "5feceb66ffc86f38d952786c", @@ -5557,14 +5560,11 @@ "74:orders_model_5.sourced": "6786f18f6ff42aa577c99d86", "75:orders_model_8.has_model": "b5bea41b6c623f7c09f1bf24", "76:orders_model_8.has_star": "b5bea41b6c623f7c09f1bf24", -"77:orders_model_8.edge_count": "4b227777d4dd1fc61c6f884f", -"78:orders_model_8.targeting": "52ad3bd8074b30476404477a", +"77:orders_model_8.edge_count": "e7f6c011776e8db7cd330b54", +"78:orders_model_8.targeting": "c51ede09dd0dd9ea307d8b73", "79:orders_model_8.sourced": "6786f18f6ff42aa577c99d86", -"7:orders_model_12.edge_count": "5feceb66ffc86f38d952786c", -"80:models": "582bc1ad59a348db0ee52665", -"81:edges": "d24dafbe5842db03ac5ee2e0", -"8:orders_model_12.targeting": "6786f18f6ff42aa577c99d86", -"9:orders_model_12.sourced": "6786f18f6ff42aa577c99d86" +"80:models": "9d57ed38acd5f54a80de27b4", +"81:edges": "3414804b470666ada13b9648" } ] ] From b87d99ccee96501a8f9c1203e1ad03dbc57542b6 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:50:50 +0000 Subject: [PATCH 051/160] WIP (not shipping): route Postgres analysis to SQLQuack's PostgreSQL parser and profile PostgresDialect reads model SQL with sqlquack-syntax-postgres's strict parser, and normalises a type only when PostgreSQL's type grammar reads it. Every SQLQuack crate is pinned to main edfa30b2. --- Cargo.lock | 20 ++++---- Cargo.toml | 6 +-- .../src/analysis_route/_helpers/mod.rs | 1 + .../_helpers/postgres_dialect.rs | 51 +++++++++++++++++++ .../src/analysis_route/constants.rs | 10 +++- .../src/analysis_route/models.rs | 4 ++ .../analysis_route/tests/test_native_gap.rs | 4 +- .../tests/test_analysis_session.rs | 7 +-- 8 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs diff --git a/Cargo.lock b/Cargo.lock index 7d467327d..d71414006 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4500,7 +4500,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4510,19 +4510,21 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ + "regex", "serde", "serde_json", "sqlquack-core", "sqlquack-snowflake", "sqlquack-syntax", + "stacker", ] [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "sqlquack-core", @@ -4531,7 +4533,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "serde", "serde_json", @@ -4544,7 +4546,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "proc-macro2", "quote", @@ -4554,7 +4556,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5#19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=edfa30b26a0bab86661f96490b86a4f24b92db0a#edfa30b26a0bab86661f96490b86a4f24b92db0a" dependencies = [ "sqlquack-syntax", "stacker", @@ -4651,7 +4653,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5087,7 +5089,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c4475e838..e7a3b4a99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,10 +177,10 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "19c51fa29ab01dbef9dddd18e8bfc406ed8e65f5" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "edfa30b26a0bab86661f96490b86a4f24b92db0a" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs index 279c10ae9..5d339132a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/mod.rs @@ -2,5 +2,6 @@ pub(crate) mod clickhouse_types; pub(crate) mod located_gap; pub(crate) mod marker_names; pub(crate) mod native_analysis; +pub(crate) mod postgres_dialect; pub(crate) mod revalidation; pub(crate) mod whole_gap; diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs new file mode 100644 index 000000000..bc843fdc5 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs @@ -0,0 +1,51 @@ +//! PostgreSQL's native module: SQLQuack's PostgreSQL 18 parser reads its SQL. + +use sqlquack_semantics::dialects::constants::POSTGRES; +use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax_postgres::{Parsed, StrictFailure, parse_strict, parse_type}; + +use crate::analysis_route::constants::{ + POSTGRES_DIALECT, POSTGRES_STUB_PREFIX, UNSCANNED_CONSTRUCT, +}; +use crate::analysis_route::models::{ParserGap, PostgresDialect}; +use crate::analysis_route::types::NativeDialect; +use crate::type_system::main::normalize_text_type::normalize_text_type; +use crate::type_system::models::{NormalizedType, TypeDialect}; + +impl NativeDialect for PostgresDialect { + fn name(&self) -> &'static str { + POSTGRES_DIALECT + } + + fn stub_prefix(&self) -> &'static str { + POSTGRES_STUB_PREFIX + } + + fn parse(&self, sql: &str) -> Result { + parse_strict(sql).map_err(|failure| match failure { + StrictFailure::Parse(error) => ParserGap { + construct: error.message.clone(), + message: error.message, + offset: error.position, + syntax: error.syntax, + }, + StrictFailure::Uncovered { position } => ParserGap { + construct: UNSCANNED_CONSTRUCT.to_owned(), + message: UNSCANNED_CONSTRUCT.to_owned(), + offset: position, + syntax: false, + }, + }) + } + + fn profile(&self) -> &'static dyn DialectProfile { + &POSTGRES + } + + /// The type's comparison shape under PostgreSQL; `None` (unknown) when PostgreSQL cannot name it. + fn normalize_type(&self, spelling: &str) -> Option { + parse_type(spelling) + .is_ok() + .then(|| normalize_text_type(spelling, Some(TypeDialect::Postgres))) + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..3153f54fc 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -1,9 +1,17 @@ //! The native dialects and the diagnostic a model the native parser cannot read takes. +use crate::analysis_route::models::PostgresDialect; use crate::analysis_route::types::NativeDialect; /// Each dialect, by its adapter's analysis dialect name, whose SQL a native module analyses. -pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = &[]; +pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = + &[(POSTGRES_DIALECT, &PostgresDialect)]; +/// PostgreSQL's analysis dialect name. +pub(crate) const POSTGRES_DIALECT: &str = "postgres"; +/// The stub prefix PostgreSQL reads as one unquoted name. +pub(crate) const POSTGRES_STUB_PREFIX: &str = "__sqlbuild_"; +/// The construct of a byte the scanner reads as no token, blank or comment. +pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 838fd60c2..d152cfec0 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -145,3 +145,7 @@ impl LineageSchema for StandInSchema { .then_some(self.columns.as_slice()) } } + +/// PostgreSQL, whose SQL SQLQuack's PostgreSQL 18 parser reads. +#[derive(Clone, Copy, Debug)] +pub struct PostgresDialect; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs index fc714b2bd..fdcdf47b6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_gap.rs @@ -9,12 +9,12 @@ use crate::assembly::project::models::SyntaxCheck; use crate::compiled_project::models::TextPosition; #[test] -fn given_any_dialect_when_routing_analysis_then_it_stays_on_the_legacy_path() { +fn given_a_dialect_when_routing_analysis_then_only_native_dialects_leave_the_legacy_path() { let test_cases = [ RouteTestCase { description: "postgres", dialect: Some("postgres"), - expected_legacy: true, + expected_legacy: false, }, RouteTestCase { description: "snowflake", diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index 77aab0583..b654bde0d 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -375,13 +375,10 @@ fn given_enrichment_dialect_when_running_then_answers_natively_or_fails_internal expected_error: None, }, SessionFailureTestCase { - description: "an adapter alias compile maps before analysis never reaches the session", + description: "the postgres analysis dialect answers natively", dialect: "postgres", models: &[UNTYPED_ORDERS], - expected_error: Some( - "NativeCompilerError: native model analysis of model 'orders_untyped' enrichment: \ - query analysis options: unknown variant `postgres`", - ), + expected_error: None, }, ]; for test_case in test_cases { From 57d686154bfe4c56b200ee7a24428cafc738bddc Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:52:16 +0000 Subject: [PATCH 052/160] WIP (not shipping): shape the Databricks arm's type test and constant as fensu requires The type test case names Polyglot's spelling of each type as its expected name, so its assertion reads an expected field as well as comparing the whole type with Polyglot's, and the alias constant keeps a one-line doc comment. --- .../src/analysis_route/constants.rs | 1 - .../tests/test_databricks_dialect.rs | 20 +++++++++++++++++-- .../src/analysis_route/tests/test_types.rs | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index d122e8ab8..9f19885c6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -13,7 +13,6 @@ pub(crate) const DATABRICKS_STUB_PREFIX: &str = "__sqlbuild_"; /// The construct of a byte the native scanner reads as no token, blank or comment. pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; /// Databricks type words Polyglot spells another way, so the text fallback matches it. -/// The space in DOUBLE PRECISION is taken out before the fallback compares names. pub(crate) const DATABRICKS_TYPE_ALIASES: &[(&str, &str)] = &[ ("INTEGER", "INT"), ("REAL", "FLOAT"), diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs index e87651915..5f61a515a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs @@ -90,59 +90,75 @@ fn given_a_databricks_type_spelling_when_normalising_then_the_type_is_polyglot_s PolyglotTypeTestCase { description: "INTEGER is Polyglot's INT", spelling: "INTEGER", + expected_name: "INT", }, PolyglotTypeTestCase { description: "REAL is Polyglot's FLOAT", spelling: "real", + expected_name: "FLOAT", }, PolyglotTypeTestCase { description: "DOUBLE PRECISION is Polyglot's DOUBLE, whatever the spacing", spelling: "DOUBLE PRECISION", + expected_name: "DOUBLE", }, PolyglotTypeTestCase { description: "NUMERIC with arguments is DECIMAL with its precision and scale", spelling: "NUMERIC(9, 3)", + expected_name: "DECIMAL(9,3)", }, PolyglotTypeTestCase { description: "NUMERIC alone is DECIMAL", spelling: "NUMERIC", + expected_name: "DECIMAL", }, PolyglotTypeTestCase { description: "TIMESTAMP_NTZ is Polyglot's TIMESTAMPNTZ", spelling: "TIMESTAMP_NTZ", + expected_name: "TIMESTAMPNTZ", }, PolyglotTypeTestCase { description: "TIMESTAMP_LTZ is Polyglot's TIMESTAMPLTZ", spelling: "TIMESTAMP_LTZ", + expected_name: "TIMESTAMPLTZ", }, PolyglotTypeTestCase { description: "a name Polyglot keeps stays as written", spelling: "LONG", + expected_name: "LONG", }, PolyglotTypeTestCase { description: "a word holding an alias is not changed inside", spelling: "INTEGERS", + expected_name: "INTEGERS", }, PolyglotTypeTestCase { description: "a sized string keeps its length", spelling: "VARCHAR(10)", + expected_name: "VARCHAR(10)", }, PolyglotTypeTestCase { description: "an alias inside an array, whose family ARRAY decides", spelling: "ARRAY", + expected_name: "ARRAY", }, PolyglotTypeTestCase { description: "aliases inside a map and a struct, whose family MAP decides", spelling: "MAP>", + expected_name: "MAP>", }, ]; for test_case in test_cases { let polyglot: NormalizedType = normalize_type(test_case.spelling, "databricks") .expect("Polyglot normalizes every Databricks spelling") .normalized; + let native: Option = DatabricksDialect.normalize_type(test_case.spelling); assert_eq!( - DatabricksDialect.normalize_type(test_case.spelling), - Some(polyglot), + ( + native.as_ref().map(|native| native.normalized_name.as_str()), + native.as_ref() + ), + (Some(test_case.expected_name), Some(&polyglot)), "{}", test_case.description ); diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 1c8c21ece..700c4fd9b 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -252,6 +252,8 @@ pub(super) struct NativeLineageTestCase { pub(super) struct PolyglotTypeTestCase { pub(super) description: &'static str, pub(super) spelling: &'static str, + /// Polyglot's name for the spelling, which the native type must equal whole. + pub(super) expected_name: &'static str, } pub(super) struct ParseFailureTestCase { From a1e552ab87e6e0a049807732bc08c1c48227491a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:54:43 +0000 Subject: [PATCH 053/160] test(analysis): cover a root star over an opted-out model in fast lineage Native analysis resolves every root star over a relation with known columns, so the generated projects no longer leave stars for fast lineage and their star minimum goes. A dedicated case keeps a root star over a model without SQL analysis, whose facts leave the star unresolved, and checks that restored lineage equals the catalogued lineage on every dialect. The arm gap tests set the new gap offset. --- .../analysis_route/tests/test_native_arms.rs | 5 ++ .../sqlbuild/compiler/lineage/_test_types.py | 10 +++- .../lineage/test_native_fast_lineage.py | 47 ++++++++++++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs index 5dd5d461c..367fd5285 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_arms.rs @@ -38,6 +38,7 @@ fn given_model_sql_when_probing_a_dialects_parser_then_only_sql_it_reads_whole_h line: 1, column: 15, syntax: true, + source_offset: Some(14), }), }, ArmGapTestCase { @@ -51,6 +52,7 @@ fn given_model_sql_when_probing_a_dialects_parser_then_only_sql_it_reads_whole_h line: 3, column: 9, syntax: true, + source_offset: Some(24), }), }, ArmGapTestCase { @@ -64,6 +66,7 @@ fn given_model_sql_when_probing_a_dialects_parser_then_only_sql_it_reads_whole_h line: 1, column: 1, syntax: false, + source_offset: Some(0), }), }, ArmGapTestCase { @@ -98,6 +101,7 @@ fn given_model_sql_when_probing_a_dialects_parser_then_only_sql_it_reads_whole_h line: 1, column: 15, syntax: false, + source_offset: Some(14), }), }, ArmGapTestCase { @@ -111,6 +115,7 @@ fn given_model_sql_when_probing_a_dialects_parser_then_only_sql_it_reads_whole_h line: 1, column: 1, syntax: false, + source_offset: Some(0), }), }, ]; diff --git a/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py b/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py index 398b3d8e0..486eaeaa6 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/_test_types.py @@ -13,7 +13,15 @@ class GeneratedLineageParityTestCase: dialects: tuple[str | None, ...] expected_minimum_native: int expected_minimum_parsed: int - expected_minimum_star_expansions: int + + +@dataclass(frozen=True) +class RootStarLineageTestCase: + """A root star over a model without SQL analysis, whose facts leave the star to fast lineage.""" + + description: str + dialect: str | None + expected_star_expansions: int @dataclass(frozen=True) diff --git a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py index 3dcaebd13..9742b8444 100644 --- a/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py +++ b/tests/integration/src/sqlbuild/compiler/lineage/test_native_fast_lineage.py @@ -28,6 +28,7 @@ from tests.integration.src.sqlbuild.compiler.lineage._test_types import ( FormerlyDeferredLineageTestCase, GeneratedLineageParityTestCase, + RootStarLineageTestCase, TruncatedSqlLineageTestCase, UnparsedLineageTestCase, ) @@ -54,7 +55,6 @@ dialects=(None, "duckdb", "postgres", "snowflake"), expected_minimum_native=400, expected_minimum_parsed=250, - expected_minimum_star_expansions=20, ) ], ids=lambda case: case.description, @@ -112,7 +112,50 @@ def test_given_generated_projects_when_building_fast_lineage_then_restored_proje assert differences == [] assert sum(work.values()) >= test_case.expected_minimum_native assert work["parse"] >= test_case.expected_minimum_parsed - assert work["star"] >= test_case.expected_minimum_star_expansions + + +@pytest.mark.parametrize( + "test_case", + [ + RootStarLineageTestCase( + description=f"a root star over a model without SQL analysis, {dialect} dialect", + dialect=dialect, + expected_star_expansions=1, + ) + for dialect in (None, "duckdb", "postgres", "snowflake") + ], + ids=lambda case: case.description, +) +def test_given_root_star_over_opted_out_model_when_building_fast_lineage_then_restored_project_agrees( + test_case: RootStarLineageTestCase, + tmp_path: Path, +) -> None: + project: CompiledProjectView = compiled_project( + project_dir=tmp_path / "project", + files={ + **generated_lineage_files(rng=random.Random(5), model_count=_MODEL_COUNT), + "models/orders_staged.sql": ( + 'MODEL (description "Staged orders", sql_analysis false);\n\n' + 'SELECT customer_id, order_id FROM __source("raw_orders")\n' + ), + "models/orders_all.sql": ( + 'MODEL (description "All orders");\n\nSELECT * FROM __ref("orders_staged")\n' + ), + }, + ) + + names, with_catalog = lineage_views( + project=project, dialect=test_case.dialect, model_names=None + ) + _, restored = lineage_views( + project=project_with(project=project, binding_catalog=None), + dialect=test_case.dialect, + model_names=None, + ) + work: Counter[str] = fast_lineage_work(project=project, model_names=frozenset({"orders_all"})) + + assert work["star"] == test_case.expected_star_expansions + assert mismatches(inputs=names, expected=with_catalog, actual=restored) == [] @pytest.mark.parametrize( From bff6ed269af005aeae4f582df2086a8670d7ef34 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 13:55:02 +0000 Subject: [PATCH 054/160] WIP (not shipping): compare Postgres types as PostgreSQL names them PostgresDialect::normalize_type reads a type with PostgreSQL's type grammar and names it as format_type does, so INT, int4 and INTEGER are one type, before SQLBuild's comparison shape. --- .../_helpers/postgres_dialect.rs | 37 +++++++-- .../src/analysis_route/constants.rs | 14 ++++ .../src/analysis_route/tests.rs | 2 + .../tests/test_postgres_dialect.rs | 77 +++++++++++++++++++ 4 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/tests/test_postgres_dialect.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs index bc843fdc5..9677446a6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/postgres_dialect.rs @@ -2,10 +2,11 @@ use sqlquack_semantics::dialects::constants::POSTGRES; use sqlquack_semantics::dialects::types::DialectProfile; -use sqlquack_syntax_postgres::{Parsed, StrictFailure, parse_strict, parse_type}; +use sqlquack_syntax_postgres::{Parsed, StrictFailure, format_type, parse_strict, parse_type}; use crate::analysis_route::constants::{ - POSTGRES_DIALECT, POSTGRES_STUB_PREFIX, UNSCANNED_CONSTRUCT, + POSTGRES_DIALECT, POSTGRES_STUB_PREFIX, POSTGRES_TEMPORAL_SPELLINGS, POSTGRES_TYPE_SPELLINGS, + UNSCANNED_CONSTRUCT, }; use crate::analysis_route::models::{ParserGap, PostgresDialect}; use crate::analysis_route::types::NativeDialect; @@ -42,10 +43,34 @@ impl NativeDialect for PostgresDialect { &POSTGRES } - /// The type's comparison shape under PostgreSQL; `None` (unknown) when PostgreSQL cannot name it. + /// The comparison shape of the type PostgreSQL names; `None` (unknown) when it cannot name it. fn normalize_type(&self, spelling: &str) -> Option { - parse_type(spelling) - .is_ok() - .then(|| normalize_text_type(spelling, Some(TypeDialect::Postgres))) + let canonical: String = format_type(&parse_type(spelling).ok()?)?; + Some(normalize_text_type( + &fallback_spelling(&canonical), + Some(TypeDialect::Postgres), + )) } } + +/// PostgreSQL's `format_type` name with its multi-word names in the words SQLBuild's fallback reads. +fn fallback_spelling(canonical: &str) -> String { + for (name, zone, respelt) in POSTGRES_TEMPORAL_SPELLINGS { + if let Some(precision) = canonical + .strip_prefix(name) + .and_then(|rest| rest.strip_suffix(zone)) + .filter(|precision| precision.is_empty() || precision.starts_with('(')) + { + return format!("{respelt}{precision}"); + } + } + for (name, respelt) in POSTGRES_TYPE_SPELLINGS { + if let Some(rest) = canonical + .strip_prefix(name) + .filter(|rest| rest.is_empty() || rest.starts_with(['(', '['])) + { + return format!("{respelt}{rest}"); + } + } + canonical.to_owned() +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 3153f54fc..d8b239864 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -10,6 +10,20 @@ pub(crate) const NATIVE_DIALECTS: &[(&str, &dyn NativeDialect)] = pub(crate) const POSTGRES_DIALECT: &str = "postgres"; /// The stub prefix PostgreSQL reads as one unquoted name. pub(crate) const POSTGRES_STUB_PREFIX: &str = "__sqlbuild_"; +/// `format_type`'s time and timestamp names, `(name, zone words, respelt)`, around any precision. +pub(crate) const POSTGRES_TEMPORAL_SPELLINGS: &[(&str, &str, &str)] = &[ + ("timestamp", " without time zone", "TIMESTAMP"), + ("timestamp", " with time zone", "TIMESTAMPTZ"), + ("time", " without time zone", "TIME"), + ("time", " with time zone", "TIMETZ"), +]; +/// `format_type`'s other multi-word names, as SQLBuild's type fallback spells them. +pub(crate) const POSTGRES_TYPE_SPELLINGS: &[(&str, &str)] = &[ + ("character varying", "VARCHAR"), + ("character", "CHAR"), + ("double precision", "DOUBLE"), + ("bit varying", "VARBIT"), +]; /// The construct of a byte the scanner reads as no token, blank or comment. pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; /// The dialect a project with no analysis dialect is named by. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests.rs b/crates/sqlbuild-analysis/src/analysis_route/tests.rs index 242ac2a38..9c6c14757 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests.rs @@ -10,5 +10,7 @@ mod test_native_gap; mod test_native_outcome; #[path = "tests/test_native_revalidation.rs"] mod test_native_revalidation; +#[path = "tests/test_postgres_dialect.rs"] +mod test_postgres_dialect; #[path = "tests/test_types.rs"] mod test_types; diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_postgres_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_postgres_dialect.rs new file mode 100644 index 000000000..3a41997c0 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_postgres_dialect.rs @@ -0,0 +1,77 @@ +use crate::analysis_route::models::{PostgresDialect, TypeMatch}; +use crate::analysis_route::tests::test_types::NativeTypeMatchTestCase; +use crate::analysis_route::types::NativeDialect; + +#[test] +fn given_postgres_type_spellings_when_matching_types_then_the_types_postgres_names_are_compared() { + let compared = |same_type: bool| TypeMatch::Compared { + same_type, + nullable_into_non_null: false, + }; + let test_cases = [ + NativeTypeMatchTestCase { + description: "INT and INTEGER both name integer", + declared: "INTEGER", + inferred: Some("INT"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "int4 is integer", + declared: "int4", + inferred: Some("integer"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "VARCHAR(10) is character varying(10)", + declared: "character varying(10)", + inferred: Some("VARCHAR(10)"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "a different length is a different type", + declared: "VARCHAR(10)", + inferred: Some("VARCHAR(20)"), + expected_match: compared(false), + }, + NativeTypeMatchTestCase { + description: "TIMESTAMP is timestamp without time zone", + declared: "timestamp without time zone", + inferred: Some("TIMESTAMP"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "TIMESTAMPTZ is not TIMESTAMP", + declared: "TIMESTAMPTZ", + inferred: Some("TIMESTAMP"), + expected_match: compared(false), + }, + NativeTypeMatchTestCase { + description: "float8 is double precision", + declared: "DOUBLE PRECISION", + inferred: Some("float8"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "integer and bigint differ", + declared: "INTEGER", + inferred: Some("BIGINT"), + expected_match: compared(false), + }, + NativeTypeMatchTestCase { + description: "a type PostgreSQL's grammar refuses is not compared", + declared: "SETOF integer", + inferred: Some("integer"), + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + ]; + for test_case in test_cases { + assert_eq!( + PostgresDialect.type_match(test_case.declared, test_case.inferred, true), + test_case.expected_match, + "{}", + test_case.description + ); + } +} From 7f07e822abb0090395428eddb436e3d4a236ffdd Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:28:52 +0000 Subject: [PATCH 055/160] WIP (not shipping): type Databricks spellings through Databricks' own type grammar normalize_type now decides whether a type is known, so it must refuse what Databricks refuses. The arm reads a spelling as the target of SELECT CAST(NULL AS ...) with Databricks' parser, as BigQuery's arm does, and abstains on anything else: TEXT, BOOL, INT64, FLOAT64, DATETIME, JSON, TIMESTAMPTZ, INTEGER[], MAP(K, V), NUMBER(p, s), DOUBLE PRECISION (the DOUBLE type's documented syntax is DOUBLE alone) and the empty spelling. Such pairs, and the columns typed with them, are left uncompared. A read type is respelt from its parsed form, so Databricks' synonyms meet: INTEGER and INT, LONG and BIGINT, SHORT and SMALLINT, BYTE and TINYINT, DEC and NUMERIC and DECIMAL with Databricks' (10,0) default and scale 0 after a lone precision, TIMESTAMP_LTZ and TIMESTAMP, TIMESTAMP_NTZ and TIMESTAMP WITHOUT TIME ZONE, VARCHAR and CHAR without a length and STRING, and a STRUCT field written with or without its colon. Field constraints, comments and collations are not part of the type. Where Polyglot keeps LONG, SHORT, DEC or TIMESTAMPLTZ apart, the native route follows Databricks' synonym table and the semantics profile's own type synonyms. Databricks types a cast to CHAR(n) or VARCHAR(n) as STRING and reads a CHAR or VARCHAR column as STRING; those types are a length check on STRING when a table is written. type_match therefore takes CHAR(n) or VARCHAR(n) on one side and STRING on the other as one type. Without this every contracted VARCHAR(n) column of the generated seed projects raised K002 on the native route. --- .../_helpers/databricks_dialect.rs | 168 +++++++++-- .../src/analysis_route/constants.rs | 15 +- .../tests/test_databricks_dialect.rs | 264 ++++++++++++++---- .../src/analysis_route/tests/test_types.rs | 10 +- 4 files changed, 363 insertions(+), 94 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs index 5dc52b48c..302a6b811 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs @@ -2,15 +2,17 @@ use sqlquack_semantics::dialects::constants::DATABRICKS; use sqlquack_semantics::dialects::types::DialectProfile; +use sqlquack_syntax::{DataType, Expression}; use sqlquack_syntax_databricks::{Parsed, StrictFailure, parse_strict}; use crate::analysis_route::constants::{ - DATABRICKS_DIALECT, DATABRICKS_STUB_PREFIX, DATABRICKS_TYPE_ALIASES, UNSCANNED_CONSTRUCT, + DATABRICKS_CAST_TYPE_PREFIX, DATABRICKS_DEFAULT_DECIMAL, DATABRICKS_DIALECT, + DATABRICKS_STRING_TYPE, DATABRICKS_STUB_PREFIX, UNSCANNED_CONSTRUCT, }; -use crate::analysis_route::models::{DatabricksDialect, ParserGap}; +use crate::analysis_route::models::{DatabricksDialect, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; -use crate::type_system::models::{NormalizedType, TypeDialect}; +use crate::type_system::models::{NormalizedType, TypeDialect, TypeFamily}; impl NativeDialect for DatabricksDialect { fn name(&self) -> &'static str { @@ -39,40 +41,148 @@ impl NativeDialect for DatabricksDialect { } fn normalize_type(&self, spelling: &str) -> Option { - let upper: String = spelling - .split_whitespace() - .collect::>() - .join(" ") - .to_ascii_uppercase(); - if upper.is_empty() { - return None; - } - let spelt: String = DATABRICKS_TYPE_ALIASES - .iter() - .fold(upper, |text, (alias, name)| aliased(&text, alias, name)); + let spelt: String = canonical(&cast_target(spelling)?)?; Some(normalize_text_type(&spelt, Some(TypeDialect::Databricks))) } fn stub_prefix(&self) -> &'static str { DATABRICKS_STUB_PREFIX } + + fn type_match( + &self, + declared: &str, + inferred: Option<&str>, + inferred_nullable: bool, + ) -> TypeMatch { + let _ = inferred_nullable; + match ( + self.normalize_type(declared), + inferred.and_then(|inferred| self.normalize_type(inferred)), + ) { + (Some(declared), Some(inferred)) => TypeMatch::Compared { + same_type: declared == inferred + || overlays_string(&declared, &inferred) + || overlays_string(&inferred, &declared), + nullable_into_non_null: false, + }, + _ => TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + } + } } -/// `text` with each whole word `alias` spelt `name`, at any nesting depth. -fn aliased(text: &str, alias: &str, name: &str) -> String { - let word = |c: char| c.is_ascii_alphanumeric() || c == '_'; - let mut out: String = String::with_capacity(text.len()); - let mut rest: &str = text; - while let Some(at) = rest.find(alias) { - let before: Option = rest[..at].chars().next_back().or(out.chars().next_back()); - let after: Option = rest[at + alias.len()..].chars().next(); - out.push_str(&rest[..at]); - match before.is_some_and(word) || after.is_some_and(word) { - true => out.push_str(alias), - false => out.push_str(name), +/// The type `spelling` names when Databricks' parser reads it whole as a cast target. +fn cast_target(spelling: &str) -> Option { + let mut depth: usize = 0; + for character in spelling.chars() { + match character { + '(' => depth += 1, + ')' => depth = depth.checked_sub(1)?, + ';' => return None, + _ => {} } - rest = &rest[at + alias.len()..]; } - out.push_str(rest); - out + if depth != 0 || spelling.trim().is_empty() { + return None; + } + let parsed: Parsed = parse_strict(&format!("{DATABRICKS_CAST_TYPE_PREFIX}{spelling})")).ok()?; + let ([Expression::Select(select)], []) = (parsed.statements.as_slice(), parsed.gaps.as_slice()) + else { + return None; + }; + match (select.expressions.as_slice(), &select.from) { + ([Expression::Cast(cast)], None) => Some(cast.to.clone()), + _ => None, + } +} + +/// One spelling for every way of writing a Databricks type, in Polyglot's names where they agree. +fn canonical(data_type: &DataType) -> Option { + Some(match data_type { + DataType::Array { element_type, .. } => format!("ARRAY<{}>", canonical(element_type)?), + DataType::Map { + key_type, + value_type, + } => format!("MAP<{}, {}>", canonical(key_type)?, canonical(value_type)?), + DataType::Struct { fields, .. } => { + let fields: Vec = fields + .iter() + .map(|field| { + Some(format!( + "{}: {}", + field_name(&field.name), + canonical(&field.data_type)? + )) + }) + .collect::>()?; + format!("STRUCT<{}>", fields.join(", ")) + } + DataType::Char { + length: Some(length), + } => format!("CHAR({length})"), + DataType::VarChar { + length: Some(length), + .. + } => format!("VARCHAR({length})"), + DataType::String { .. } | DataType::Char { .. } | DataType::VarChar { .. } => { + DATABRICKS_STRING_TYPE.to_owned() + } + DataType::Decimal { precision, scale } => { + let (default_precision, default_scale) = DATABRICKS_DEFAULT_DECIMAL; + format!( + "DECIMAL({},{})", + precision.unwrap_or(default_precision), + scale.unwrap_or(default_scale) + ) + } + DataType::Timestamp { timezone: true, .. } => "TIMESTAMP".to_owned(), + DataType::Timestamp { .. } => "TIMESTAMPNTZ".to_owned(), + DataType::Time { .. } => "TIME".to_owned(), + DataType::Interval { unit, to } => match (unit, to) { + (Some(unit), Some(to)) => format!("INTERVAL {unit} TO {to}"), + (Some(unit), None) => format!("INTERVAL {unit}"), + _ => "INTERVAL".to_owned(), + }, + DataType::Boolean => "BOOLEAN".to_owned(), + DataType::TinyInt { .. } => "TINYINT".to_owned(), + DataType::SmallInt { .. } => "SMALLINT".to_owned(), + DataType::Int { .. } => "INT".to_owned(), + DataType::BigInt { .. } => "BIGINT".to_owned(), + DataType::Float { .. } => "FLOAT".to_owned(), + DataType::Double { .. } => "DOUBLE".to_owned(), + DataType::Date => "DATE".to_owned(), + DataType::Binary { .. } => "BINARY".to_owned(), + DataType::Geography { srid, .. } => spatial("GEOGRAPHY", *srid), + DataType::Geometry { srid, .. } => spatial("GEOMETRY", *srid), + DataType::Custom { name } => name.to_ascii_uppercase(), + _ => return None, + }) +} + +/// A STRUCT field's name, backquoted unless it is one word. +fn field_name(name: &str) -> String { + match name + .chars() + .all(|character| character.is_ascii_alphanumeric() || character == '_') + { + true => name.to_owned(), + false => format!("`{}`", name.replace('`', "``")), + } +} + +/// A GEOGRAPHY or GEOMETRY type with its SRID, or `ANY`. +fn spatial(base: &str, srid: Option) -> String { + match srid { + Some(srid) => format!("{base}({srid})"), + None => format!("{base}(ANY)"), + } +} + +/// Whether `sized` is CHAR(n) or VARCHAR(n), a length check on STRING, and `other` is STRING. +fn overlays_string(sized: &NormalizedType, other: &NormalizedType) -> bool { + sized.family == TypeFamily::String + && sized.length.is_some() + && other.normalized_name == DATABRICKS_STRING_TYPE } diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9f19885c6..a7b9cd725 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -12,15 +12,12 @@ pub(crate) const DATABRICKS_DIALECT: &str = "databricks"; pub(crate) const DATABRICKS_STUB_PREFIX: &str = "__sqlbuild_"; /// The construct of a byte the native scanner reads as no token, blank or comment. pub(crate) const UNSCANNED_CONSTRUCT: &str = "unscanned text"; -/// Databricks type words Polyglot spells another way, so the text fallback matches it. -pub(crate) const DATABRICKS_TYPE_ALIASES: &[(&str, &str)] = &[ - ("INTEGER", "INT"), - ("REAL", "FLOAT"), - ("DOUBLE PRECISION", "DOUBLE"), - ("NUMERIC", "DECIMAL"), - ("TIMESTAMP_LTZ", "TIMESTAMPLTZ"), - ("TIMESTAMP_NTZ", "TIMESTAMPNTZ"), -]; +/// The query a type spelling completes, with `)`, so Databricks' parser reads it as a cast target. +pub(crate) const DATABRICKS_CAST_TYPE_PREFIX: &str = "SELECT CAST(NULL AS "; +/// DECIMAL's precision and scale where Databricks' spelling leaves them out. +pub(crate) const DATABRICKS_DEFAULT_DECIMAL: (u32, u32) = (10, 0); +/// The type Databricks gives every string expression, a cast to CHAR(n) or VARCHAR(n) included. +pub(crate) const DATABRICKS_STRING_TYPE: &str = "STRING"; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs index 5f61a515a..7181eabfd 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs @@ -1,12 +1,12 @@ use crate::analysis_route::main::native_gap::native_gap; -use crate::analysis_route::models::{DatabricksDialect, LocatedGap}; +use crate::analysis_route::models::{DatabricksDialect, LocatedGap, TypeMatch}; use crate::analysis_route::tests::test_types::{ - NativeGapTestCase, ParseFailureTestCase, PolyglotTypeTestCase, + DatabricksTypeTestCase, NativeGapTestCase, NativeTypeMatchTestCase, ParseFailureTestCase, }; use crate::analysis_route::types::NativeDialect; use crate::assembly::project::models::SyntaxCheck; use crate::type_system::main::normalize_type::normalize_type; -use crate::type_system::models::NormalizedType; +use crate::type_system::models::{NormalizedType, TypeFamily}; #[test] fn given_databricks_model_sql_when_probing_its_parser_then_only_sql_it_reads_whole_has_no_gap() { @@ -85,80 +85,169 @@ fn given_databricks_model_sql_when_probing_its_parser_then_only_sql_it_reads_who } #[test] -fn given_a_databricks_type_spelling_when_normalising_then_the_type_is_polyglot_s() { +fn given_a_databricks_type_spelling_when_normalising_then_its_parser_decides_and_synonyms_meet() { let test_cases = [ - PolyglotTypeTestCase { - description: "INTEGER is Polyglot's INT", + DatabricksTypeTestCase { + description: "INTEGER is INT", spelling: "INTEGER", - expected_name: "INT", + expected_type: Some(("INT", TypeFamily::Integer)), + expected_as_polyglot: true, }, - PolyglotTypeTestCase { - description: "REAL is Polyglot's FLOAT", + DatabricksTypeTestCase { + description: "REAL is FLOAT", spelling: "real", - expected_name: "FLOAT", + expected_type: Some(("FLOAT", TypeFamily::Float)), + expected_as_polyglot: true, }, - PolyglotTypeTestCase { - description: "DOUBLE PRECISION is Polyglot's DOUBLE, whatever the spacing", - spelling: "DOUBLE PRECISION", - expected_name: "DOUBLE", - }, - PolyglotTypeTestCase { + DatabricksTypeTestCase { description: "NUMERIC with arguments is DECIMAL with its precision and scale", spelling: "NUMERIC(9, 3)", - expected_name: "DECIMAL(9,3)", + expected_type: Some(("DECIMAL(9,3)", TypeFamily::Decimal)), + expected_as_polyglot: true, + }, + DatabricksTypeTestCase { + description: "a sized string keeps its length", + spelling: "VARCHAR(10)", + expected_type: Some(("VARCHAR(10)", TypeFamily::String)), + expected_as_polyglot: true, }, - PolyglotTypeTestCase { - description: "NUMERIC alone is DECIMAL", - spelling: "NUMERIC", - expected_name: "DECIMAL", + DatabricksTypeTestCase { + description: "VARCHAR alone is STRING", + spelling: "varchar", + expected_type: Some(("STRING", TypeFamily::String)), + expected_as_polyglot: true, }, - PolyglotTypeTestCase { + DatabricksTypeTestCase { description: "TIMESTAMP_NTZ is Polyglot's TIMESTAMPNTZ", spelling: "TIMESTAMP_NTZ", - expected_name: "TIMESTAMPNTZ", + expected_type: Some(("TIMESTAMPNTZ", TypeFamily::Timestamp)), + expected_as_polyglot: true, }, - PolyglotTypeTestCase { - description: "TIMESTAMP_LTZ is Polyglot's TIMESTAMPLTZ", - spelling: "TIMESTAMP_LTZ", - expected_name: "TIMESTAMPLTZ", + DatabricksTypeTestCase { + description: "a struct field with or without its colon is one type", + spelling: "STRUCT", + expected_type: Some(("STRUCT", TypeFamily::Other)), + expected_as_polyglot: true, }, - PolyglotTypeTestCase { - description: "a name Polyglot keeps stays as written", + DatabricksTypeTestCase { + description: "synonyms inside a map and a struct", + spelling: "MAP>", + expected_type: Some(( + "MAP>", + TypeFamily::Other, + )), + expected_as_polyglot: true, + }, + DatabricksTypeTestCase { + description: "LONG is BIGINT, where Polyglot keeps LONG", spelling: "LONG", - expected_name: "LONG", + expected_type: Some(("BIGINT", TypeFamily::Integer)), + expected_as_polyglot: false, }, - PolyglotTypeTestCase { - description: "a word holding an alias is not changed inside", - spelling: "INTEGERS", - expected_name: "INTEGERS", + DatabricksTypeTestCase { + description: "SHORT is SMALLINT, where Polyglot knows no SHORT", + spelling: "SHORT", + expected_type: Some(("SMALLINT", TypeFamily::Integer)), + expected_as_polyglot: false, }, - PolyglotTypeTestCase { - description: "a sized string keeps its length", - spelling: "VARCHAR(10)", - expected_name: "VARCHAR(10)", + DatabricksTypeTestCase { + description: "TIMESTAMP_LTZ is TIMESTAMP, where Polyglot keeps TIMESTAMPLTZ", + spelling: "TIMESTAMP_LTZ", + expected_type: Some(("TIMESTAMP", TypeFamily::Timestamp)), + expected_as_polyglot: false, }, - PolyglotTypeTestCase { - description: "an alias inside an array, whose family ARRAY decides", - spelling: "ARRAY", - expected_name: "ARRAY", + DatabricksTypeTestCase { + description: "DECIMAL alone is DECIMAL(10,0)", + spelling: "DECIMAL", + expected_type: Some(("DECIMAL(10,0)", TypeFamily::Decimal)), + expected_as_polyglot: false, }, - PolyglotTypeTestCase { - description: "aliases inside a map and a struct, whose family MAP decides", - spelling: "MAP>", - expected_name: "MAP>", + DatabricksTypeTestCase { + description: "DECIMAL(p) has scale 0", + spelling: "DEC(9)", + expected_type: Some(("DECIMAL(9,0)", TypeFamily::Decimal)), + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "a struct field's constraint and comment are not its type", + spelling: "STRUCT", + expected_type: Some(("STRUCT", TypeFamily::Other)), + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "a collation is not its type", + spelling: "VARCHAR(10) COLLATE UTF8_BINARY", + expected_type: Some(("VARCHAR(10)", TypeFamily::String)), + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "an interval keeps its fields", + spelling: "INTERVAL YEAR TO MONTH", + expected_type: Some(("INTERVALYEARTOMONTH", TypeFamily::Other)), + expected_as_polyglot: true, + }, + DatabricksTypeTestCase { + description: "DOUBLE PRECISION is not a Databricks spelling", + spelling: "DOUBLE PRECISION", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "TEXT is not a Databricks type", + spelling: "TEXT", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "BOOL is not a Databricks type", + spelling: "BOOL", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "INT64 is not a Databricks type", + spelling: "INT64", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "an array is ARRAY, not T[]", + spelling: "INTEGER[]", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "a map is MAP, not MAP(K, V)", + spelling: "MAP(VARCHAR, INTEGER)", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "an empty type", + spelling: " ", + expected_type: None, + expected_as_polyglot: false, + }, + DatabricksTypeTestCase { + description: "text that closes the cast is not a type", + spelling: "INT) FROM t WHERE (1", + expected_type: None, + expected_as_polyglot: false, }, ]; for test_case in test_cases { - let polyglot: NormalizedType = normalize_type(test_case.spelling, "databricks") - .expect("Polyglot normalizes every Databricks spelling") - .normalized; let native: Option = DatabricksDialect.normalize_type(test_case.spelling); + let polyglot: Option = normalize_type(test_case.spelling, "databricks") + .ok() + .map(|normalization| normalization.normalized); assert_eq!( ( - native.as_ref().map(|native| native.normalized_name.as_str()), - native.as_ref() + native + .as_ref() + .map(|native| (native.normalized_name.as_str(), native.family)), + native.is_some() && native == polyglot ), - (Some(test_case.expected_name), Some(&polyglot)), + (test_case.expected_type, test_case.expected_as_polyglot), "{}", test_case.description ); @@ -199,3 +288,74 @@ fn given_sql_databricks_refuses_when_parsing_then_the_gap_says_whether_it_is_a_s ); } } + +#[test] +fn given_a_declared_and_an_inferred_databricks_type_when_matching_then_a_length_check_on_string_is_string() + { + let compared = |same_type: bool| TypeMatch::Compared { + same_type, + nullable_into_non_null: false, + }; + let test_cases = [ + NativeTypeMatchTestCase { + description: "a VARCHAR(n) column takes the STRING a cast to VARCHAR(n) gives", + declared: "VARCHAR(19)", + inferred: Some("STRING"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "a CHAR(n) column takes a STRING too", + declared: "char(3)", + inferred: Some("string"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "a STRING column takes a VARCHAR(n) column, which Databricks reads as STRING", + declared: "STRING", + inferred: Some("VARCHAR(19)"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "a VARCHAR(n) column does not take an integer", + declared: "VARCHAR(19)", + inferred: Some("INT"), + expected_match: compared(false), + }, + NativeTypeMatchTestCase { + description: "synonyms match through the alias fold", + declared: "INTEGER", + inferred: Some("INT"), + expected_match: compared(true), + }, + NativeTypeMatchTestCase { + description: "sized strings of other lengths differ", + declared: "VARCHAR(19)", + inferred: Some("VARCHAR(20)"), + expected_match: compared(false), + }, + NativeTypeMatchTestCase { + description: "a type Databricks refuses is not compared", + declared: "TEXT", + inferred: Some("STRING"), + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + NativeTypeMatchTestCase { + description: "an untyped output is not compared", + declared: "VARCHAR(19)", + inferred: None, + expected_match: TypeMatch::NotCompared { + nullable_into_non_null: false, + }, + }, + ]; + for test_case in test_cases { + assert_eq!( + DatabricksDialect.type_match(test_case.declared, test_case.inferred, false), + test_case.expected_match, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 700c4fd9b..98898842a 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -7,7 +7,7 @@ use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; use crate::analysis_route::models::{LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; -use crate::type_system::models::NormalizedType; +use crate::type_system::models::{NormalizedType, TypeFamily}; pub(super) struct NativeGapTestCase { pub(super) description: &'static str, @@ -249,11 +249,13 @@ pub(super) struct NativeLineageTestCase { pub(super) expected_lines: &'static [&'static str], } -pub(super) struct PolyglotTypeTestCase { +pub(super) struct DatabricksTypeTestCase { pub(super) description: &'static str, pub(super) spelling: &'static str, - /// Polyglot's name for the spelling, which the native type must equal whole. - pub(super) expected_name: &'static str, + /// The type's name and family, or `None` for a spelling Databricks' parser refuses. + pub(super) expected_type: Option<(&'static str, TypeFamily)>, + /// Whether the whole type is the one Polyglot gives the spelling. + pub(super) expected_as_polyglot: bool, } pub(super) struct ParseFailureTestCase { From 888fd5c566bb13f723ad043c1b4276138d406cd8 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:33:19 +0000 Subject: [PATCH 056/160] WIP (not shipping): re-record the Databricks entries of the generated contract golden Databricks now analyses natively, so only its entries move: 32 of 60 entries of contracts_generated change, only under the databricks key, and every None, BigQuery, DuckDB, Postgres, Snowflake and T-SQL entry is unchanged. analysis_generated is unchanged for both of its cases. 55 K002 findings (50 errors, 5 warnings) no longer appear and none is added. 54 pair a spelling Databricks' type grammar refuses with the other side: TEXT, BOOL, INT64, FLOAT64, int4, JSON, DATETIME, TIMESTAMPTZ, TIMESTAMP WITH TIME ZONE (Databricks reads only WITH LOCAL TIME ZONE), INTEGER[], MAP(VARCHAR, INTEGER), NUMBER(38, 0), "not a type (" or an empty spelling. The native comparison leaves such pairs uncompared, as on BigQuery's arm. The other is a warning on CHAR(3) inferred against VARCHAR declared: Databricks reads a CHAR(n) column as STRING, and VARCHAR without a length is STRING, so the two are one type there. --- ...ped_nullability_and_dynamic_contracts.json | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json index 06b2d6254..33c2b4db3 100644 --- a/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json +++ b/tests/goldens/python_oracle/contracts_generated_enforced_implicit_typed_nullability_and_dynamic_contracts.json @@ -4,7 +4,7 @@ { "None": "946a3061a7e72843e29b93c8", "bigquery": "946a3061a7e72843e29b93c8", -"databricks": "946a3061a7e72843e29b93c8", +"databricks": "9f697d91a5bd8293d4c78868", "duckdb": "946a3061a7e72843e29b93c8", "postgres": "946a3061a7e72843e29b93c8", "snowflake": "946a3061a7e72843e29b93c8", @@ -76,7 +76,7 @@ { "None": "0b6e350a4f4afde493cb8626", "bigquery": "0b6e350a4f4afde493cb8626", -"databricks": "0b6e350a4f4afde493cb8626", +"databricks": "6a937f42b20fa1400d4c5fe7", "duckdb": "0b6e350a4f4afde493cb8626", "postgres": "0b6e350a4f4afde493cb8626", "snowflake": "0b6e350a4f4afde493cb8626", @@ -88,7 +88,7 @@ { "None": "ec97cc8e40b7631612395b21", "bigquery": "ec97cc8e40b7631612395b21", -"databricks": "ec97cc8e40b7631612395b21", +"databricks": "055df6be920a595cb3395d9f", "duckdb": "ec97cc8e40b7631612395b21", "postgres": "ec97cc8e40b7631612395b21", "snowflake": "ec97cc8e40b7631612395b21", @@ -112,7 +112,7 @@ { "None": "839434d5b73abdf9499ad59e", "bigquery": "839434d5b73abdf9499ad59e", -"databricks": "839434d5b73abdf9499ad59e", +"databricks": "93171de36b7cdbf8cb98f4f9", "duckdb": "839434d5b73abdf9499ad59e", "postgres": "839434d5b73abdf9499ad59e", "snowflake": "839434d5b73abdf9499ad59e", @@ -124,7 +124,7 @@ { "None": "ab9b0c5c3dd6f54aea71a5f1", "bigquery": "ab9b0c5c3dd6f54aea71a5f1", -"databricks": "ab9b0c5c3dd6f54aea71a5f1", +"databricks": "05d66700d24bbf96856e543f", "duckdb": "ab9b0c5c3dd6f54aea71a5f1", "postgres": "ab9b0c5c3dd6f54aea71a5f1", "snowflake": "ab9b0c5c3dd6f54aea71a5f1", @@ -148,7 +148,7 @@ { "None": "48ff6f452c064d9c0d6f68ed", "bigquery": "48ff6f452c064d9c0d6f68ed", -"databricks": "48ff6f452c064d9c0d6f68ed", +"databricks": "3fbfd492b77ec7500f792854", "duckdb": "48ff6f452c064d9c0d6f68ed", "postgres": "48ff6f452c064d9c0d6f68ed", "snowflake": "48ff6f452c064d9c0d6f68ed", @@ -160,7 +160,7 @@ { "None": "17c278499d9aad7922d610d5", "bigquery": "17c278499d9aad7922d610d5", -"databricks": "17c278499d9aad7922d610d5", +"databricks": "4488a1489430ab1c4db97e43", "duckdb": "17c278499d9aad7922d610d5", "postgres": "17c278499d9aad7922d610d5", "snowflake": "17c278499d9aad7922d610d5", @@ -172,7 +172,7 @@ { "None": "c332cbe9d45bff687e91f93b", "bigquery": "c332cbe9d45bff687e91f93b", -"databricks": "c332cbe9d45bff687e91f93b", +"databricks": "b079fa87ec3327c35e724edc", "duckdb": "c332cbe9d45bff687e91f93b", "postgres": "c332cbe9d45bff687e91f93b", "snowflake": "c332cbe9d45bff687e91f93b", @@ -196,7 +196,7 @@ { "None": "bcc0d54db4ee9d237ef10dd4", "bigquery": "bcc0d54db4ee9d237ef10dd4", -"databricks": "bcc0d54db4ee9d237ef10dd4", +"databricks": "688b71a51fef751ef9030e7f", "duckdb": "bcc0d54db4ee9d237ef10dd4", "postgres": "bcc0d54db4ee9d237ef10dd4", "snowflake": "bcc0d54db4ee9d237ef10dd4", @@ -208,7 +208,7 @@ { "None": "0d0a771fb0ff7b39fb021d4d", "bigquery": "0d0a771fb0ff7b39fb021d4d", -"databricks": "0d0a771fb0ff7b39fb021d4d", +"databricks": "aaea819afff0f389c94a3eb8", "duckdb": "0d0a771fb0ff7b39fb021d4d", "postgres": "0d0a771fb0ff7b39fb021d4d", "snowflake": "0d0a771fb0ff7b39fb021d4d", @@ -220,7 +220,7 @@ { "None": "98b119ddd61340fcdf4830fb", "bigquery": "98b119ddd61340fcdf4830fb", -"databricks": "98b119ddd61340fcdf4830fb", +"databricks": "346a296ad1a69553ae2e11a1", "duckdb": "98b119ddd61340fcdf4830fb", "postgres": "98b119ddd61340fcdf4830fb", "snowflake": "98b119ddd61340fcdf4830fb", @@ -256,7 +256,7 @@ { "None": "2d4b87c5b06ff335bd6c741b", "bigquery": "2d4b87c5b06ff335bd6c741b", -"databricks": "2d4b87c5b06ff335bd6c741b", +"databricks": "905364c274a12a2c2babf099", "duckdb": "2d4b87c5b06ff335bd6c741b", "postgres": "2d4b87c5b06ff335bd6c741b", "snowflake": "2d4b87c5b06ff335bd6c741b", @@ -292,7 +292,7 @@ { "None": "ffd2f2034ea38a1c665386fe", "bigquery": "ffd2f2034ea38a1c665386fe", -"databricks": "ffd2f2034ea38a1c665386fe", +"databricks": "4f2e740fcc73cbe30799945e", "duckdb": "ffd2f2034ea38a1c665386fe", "postgres": "ffd2f2034ea38a1c665386fe", "snowflake": "ffd2f2034ea38a1c665386fe", @@ -316,7 +316,7 @@ { "None": "c11f8c29184eeda19979e5d2", "bigquery": "c11f8c29184eeda19979e5d2", -"databricks": "c11f8c29184eeda19979e5d2", +"databricks": "693ade4356c6087b9c1d8b19", "duckdb": "c11f8c29184eeda19979e5d2", "postgres": "c11f8c29184eeda19979e5d2", "snowflake": "c11f8c29184eeda19979e5d2", @@ -328,7 +328,7 @@ { "None": "6a14752b404ff8337dbb4c51", "bigquery": "6a14752b404ff8337dbb4c51", -"databricks": "6a14752b404ff8337dbb4c51", +"databricks": "2d6a82bfe80342be2bb552ef", "duckdb": "6a14752b404ff8337dbb4c51", "postgres": "6a14752b404ff8337dbb4c51", "snowflake": "6a14752b404ff8337dbb4c51", @@ -352,7 +352,7 @@ { "None": "d766e542b46b503b1d64b5ec", "bigquery": "d766e542b46b503b1d64b5ec", -"databricks": "d766e542b46b503b1d64b5ec", +"databricks": "6f3824fb29e0dde7383108c6", "duckdb": "d766e542b46b503b1d64b5ec", "postgres": "d766e542b46b503b1d64b5ec", "snowflake": "d766e542b46b503b1d64b5ec", @@ -412,7 +412,7 @@ { "None": "cd5296ac257b6e4f10c3000d", "bigquery": "cd5296ac257b6e4f10c3000d", -"databricks": "cd5296ac257b6e4f10c3000d", +"databricks": "48415c26019755009787f14c", "duckdb": "cd5296ac257b6e4f10c3000d", "postgres": "cd5296ac257b6e4f10c3000d", "snowflake": "cd5296ac257b6e4f10c3000d", @@ -424,7 +424,7 @@ { "None": "0fb1be349cccda275d77113d", "bigquery": "0fb1be349cccda275d77113d", -"databricks": "0fb1be349cccda275d77113d", +"databricks": "103d29f4aef0090a36226ef7", "duckdb": "0fb1be349cccda275d77113d", "postgres": "0fb1be349cccda275d77113d", "snowflake": "0fb1be349cccda275d77113d", @@ -436,7 +436,7 @@ { "None": "d9843bd5f2e76b8d875badaf", "bigquery": "d9843bd5f2e76b8d875badaf", -"databricks": "d9843bd5f2e76b8d875badaf", +"databricks": "876a438e514952d77fd02fbb", "duckdb": "d9843bd5f2e76b8d875badaf", "postgres": "d9843bd5f2e76b8d875badaf", "snowflake": "d9843bd5f2e76b8d875badaf", @@ -460,7 +460,7 @@ { "None": "2a167c9cb4bdcef7cfbcd99a", "bigquery": "2a167c9cb4bdcef7cfbcd99a", -"databricks": "2a167c9cb4bdcef7cfbcd99a", +"databricks": "1ab94ef2126776626e8e801d", "duckdb": "2a167c9cb4bdcef7cfbcd99a", "postgres": "2a167c9cb4bdcef7cfbcd99a", "snowflake": "2a167c9cb4bdcef7cfbcd99a", @@ -472,7 +472,7 @@ { "None": "ea9fe2e44b57cc7f6a5654aa", "bigquery": "ea9fe2e44b57cc7f6a5654aa", -"databricks": "ea9fe2e44b57cc7f6a5654aa", +"databricks": "896ce437f811f03b69f82839", "duckdb": "ea9fe2e44b57cc7f6a5654aa", "postgres": "ea9fe2e44b57cc7f6a5654aa", "snowflake": "ea9fe2e44b57cc7f6a5654aa", @@ -484,7 +484,7 @@ { "None": "fe017b6cb02eef5219322f7c", "bigquery": "fe017b6cb02eef5219322f7c", -"databricks": "fe017b6cb02eef5219322f7c", +"databricks": "b3283bf184bb082f364b8537", "duckdb": "fe017b6cb02eef5219322f7c", "postgres": "fe017b6cb02eef5219322f7c", "snowflake": "34168d14eef2cb971b3882fb", @@ -496,7 +496,7 @@ { "None": "7d8609a5dcd3a0f32d347465", "bigquery": "7d8609a5dcd3a0f32d347465", -"databricks": "7d8609a5dcd3a0f32d347465", +"databricks": "6242d56d5832fdeefd826de9", "duckdb": "7d8609a5dcd3a0f32d347465", "postgres": "7d8609a5dcd3a0f32d347465", "snowflake": "7d8609a5dcd3a0f32d347465", @@ -520,7 +520,7 @@ { "None": "891f3d8b2c293698577c7813", "bigquery": "891f3d8b2c293698577c7813", -"databricks": "891f3d8b2c293698577c7813", +"databricks": "12a74be4926070cf6793cb4d", "duckdb": "891f3d8b2c293698577c7813", "postgres": "891f3d8b2c293698577c7813", "snowflake": "891f3d8b2c293698577c7813", @@ -544,7 +544,7 @@ { "None": "6f1634dc604b82f58c1d760f", "bigquery": "6f1634dc604b82f58c1d760f", -"databricks": "6f1634dc604b82f58c1d760f", +"databricks": "c969edb8ec7bbec85e3606d2", "duckdb": "6f1634dc604b82f58c1d760f", "postgres": "6f1634dc604b82f58c1d760f", "snowflake": "6f1634dc604b82f58c1d760f", @@ -556,7 +556,7 @@ { "None": "2fb2d4657d6af84c32083288", "bigquery": "2fb2d4657d6af84c32083288", -"databricks": "2fb2d4657d6af84c32083288", +"databricks": "3c9b74c5b22f656137431938", "duckdb": "2fb2d4657d6af84c32083288", "postgres": "2fb2d4657d6af84c32083288", "snowflake": "2fb2d4657d6af84c32083288", @@ -568,7 +568,7 @@ { "None": "a9bdcb7ee2642092b9c2e2ae", "bigquery": "a9bdcb7ee2642092b9c2e2ae", -"databricks": "a9bdcb7ee2642092b9c2e2ae", +"databricks": "ac04ad46924f4659fa3494b0", "duckdb": "a9bdcb7ee2642092b9c2e2ae", "postgres": "a9bdcb7ee2642092b9c2e2ae", "snowflake": "a9bdcb7ee2642092b9c2e2ae", @@ -580,7 +580,7 @@ { "None": "a8bc34d679ed5c1bd41c17ba", "bigquery": "0f72c2e3063cc166300becea", -"databricks": "a8bc34d679ed5c1bd41c17ba", +"databricks": "cc37eb626963366490c09c21", "duckdb": "a8bc34d679ed5c1bd41c17ba", "postgres": "a8bc34d679ed5c1bd41c17ba", "snowflake": "0f72c2e3063cc166300becea", @@ -592,7 +592,7 @@ { "None": "6e5f3057275c88687658bb88", "bigquery": "6e5f3057275c88687658bb88", -"databricks": "6e5f3057275c88687658bb88", +"databricks": "2a42e291118d5fa419ada6b8", "duckdb": "6e5f3057275c88687658bb88", "postgres": "6e5f3057275c88687658bb88", "snowflake": "6e5f3057275c88687658bb88", @@ -616,7 +616,7 @@ { "None": "6581612a11662f5afd928e61", "bigquery": "6581612a11662f5afd928e61", -"databricks": "6581612a11662f5afd928e61", +"databricks": "81109ce0b75717f432eec784", "duckdb": "6581612a11662f5afd928e61", "postgres": "6581612a11662f5afd928e61", "snowflake": "6581612a11662f5afd928e61", @@ -664,7 +664,7 @@ { "None": "70130ae9c4f7aa6a50756c6b", "bigquery": "70130ae9c4f7aa6a50756c6b", -"databricks": "70130ae9c4f7aa6a50756c6b", +"databricks": "2eb73b6d0fe25a8e439c0023", "duckdb": "70130ae9c4f7aa6a50756c6b", "postgres": "70130ae9c4f7aa6a50756c6b", "snowflake": "70130ae9c4f7aa6a50756c6b", @@ -712,7 +712,7 @@ { "None": "866ae81507c3a535c0225de2", "bigquery": "866ae81507c3a535c0225de2", -"databricks": "866ae81507c3a535c0225de2", +"databricks": "20e5bc9de6e4d1de2bf5a08f", "duckdb": "866ae81507c3a535c0225de2", "postgres": "866ae81507c3a535c0225de2", "snowflake": "866ae81507c3a535c0225de2", From e60342d442fc6728a816e6d78fba088f39a81724 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:39:05 +0000 Subject: [PATCH 057/160] WIP (not shipping): match the Databricks type probe's parse failures explicitly, as fensu requires --- .../src/analysis_route/_helpers/databricks_dialect.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs index 302a6b811..87398f6a9 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs @@ -87,7 +87,10 @@ fn cast_target(spelling: &str) -> Option { if depth != 0 || spelling.trim().is_empty() { return None; } - let parsed: Parsed = parse_strict(&format!("{DATABRICKS_CAST_TYPE_PREFIX}{spelling})")).ok()?; + let parsed: Parsed = match parse_strict(&format!("{DATABRICKS_CAST_TYPE_PREFIX}{spelling})")) { + Ok(parsed) => parsed, + Err(StrictFailure::Parse(_) | StrictFailure::Uncovered { .. }) => return None, + }; let ([Expression::Select(select)], []) = (parsed.statements.as_slice(), parsed.gaps.as_slice()) else { return None; From 2e36db4c1afd24e2cd057535b24d70ae5c394951 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 14:42:50 +0000 Subject: [PATCH 058/160] test(analysis): re-record native-arm spellings, a B231 position and a refused DuckDB audit Under the native DuckDB route, inferred types keep DuckDB's own names: INTEGER for a cast to INTEGER, VARCHAR for a string or a cast to VARCHAR, where the legacy analysis spelled INT and TEXT. B231 for an aggregate in WHERE now points at the call, as the COUNT(*) row already did. A relationships audit from a string key to an integer key now reports B301 and B217: DuckDB 2.0 refuses the compiled audit's NOT IN with "Cannot compare values of type VARCHAR and INTEGER in IN/ANY/ALL clause", so the case expects both findings and a failed compile. --- .../src/sqlbuild/cli/commands/main/test_rules.py | 4 ++-- .../integration/src/sqlbuild/compiler/pipeline/test_main.py | 2 +- .../src/sqlbuild/compiler/pipeline/test_review_bindings.py | 6 ++++-- .../src/sqlbuild/compiler/pipeline/test_semantic_scopes.py | 2 +- .../cli/commands/main/compile/test_compile_command.py | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/integration/src/sqlbuild/cli/commands/main/test_rules.py b/tests/integration/src/sqlbuild/cli/commands/main/test_rules.py index 66c39436d..1be16fb04 100644 --- a/tests/integration/src/sqlbuild/cli/commands/main/test_rules.py +++ b/tests/integration/src/sqlbuild/cli/commands/main/test_rules.py @@ -3193,7 +3193,7 @@ def test_given_custom_rule_cache_when_inputs_change_then_only_unchanged_runs_hit "not_null,accepted_values,accepted_values|1|3|order_status|minimum_amount|1|1|" "order_totals", "order_totals|models/marts/order_totals.sql|table|137|79|stg_orders|||" - "order_id:INT:non_null;doubled_amount:DOUBLE:unknown|order_id,doubled_amount|" + "order_id:INTEGER:non_null;doubled_amount:DOUBLE:unknown|order_id,doubled_amount|" "False||test_order_totals|", "stg_orders|models/staging/stg_orders.sql|None|417|109||order_totals|" "order_id:INTEGER:False:Order key:not_null:[]:None/accepted_values:" @@ -3201,7 +3201,7 @@ def test_given_custom_rule_cache_when_inputs_change_then_only_unchanged_runs_hit "amount:DOUBLE:None:None::models/staging/stg_orders.sql:4;" "status:VARCHAR:None:None:accepted_values:[('values', ('PAID', 'OPEN'))]:None:" "models/staging/stg_orders.sql:5|" - "order_id:INT:unknown;amount:DOUBLE:unknown;status:TEXT:non_null|" + "order_id:INTEGER:unknown;amount:DOUBLE:unknown;status:VARCHAR:non_null|" "order_id,amount,status|True|||not_null,accepted_values,accepted_values", ), expected_hits_and_misses=((0, 4), (4, 0), (3, 1)), diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_main.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_main.py index f88a27c3f..d0feea682 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_main.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_main.py @@ -87,7 +87,7 @@ ), }, expected_column_name="amount", - expected_column_type="INT", + expected_column_type="INTEGER", ) ], ids=lambda case: case.description, diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py index ab108f5ff..71cbad4e2 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_review_bindings.py @@ -179,8 +179,9 @@ def test_given_inferred_output_when_binding_downstream_then_preserves_dialect_id 'MODEL (description "Test model.", materialized view, columns (customer_id (audits [relationships (to __ref("customers"), field customer_id)]))); SELECT 1 AS customer_id', ), NativeCatalogCase( - "relationship coercion", + "relationship from a string key to an integer key, which DuckDB refuses", "MODEL (description 'Test model.', materialized view, columns (customer_id (audits [relationships (to __ref(\"customers\"), field customer_id)]))); SELECT '1' AS customer_id", + ("B301", "B217"), ), ], ids=lambda case: case.description, @@ -198,9 +199,10 @@ def test_given_metadata_predicates_when_compiling_then_allows_dialect_coercion_a (tmp_path / "models/customers.sql").write_text( "MODEL (description 'Test model customers.', materialized view); SELECT 1 AS CUSTOMER_ID" ) - assert main(["--project-dir", str(tmp_path), "compile", "--no-cache", "--json"]) == 0 + result: int = main(["--project-dir", str(tmp_path), "compile", "--no-cache", "--json"]) payload: dict[str, Any] = json.loads(capsys.readouterr().out) assert tuple(item["code"] for item in payload["diagnostics"]) == test_case.expected_codes + assert result == int(bool(test_case.expected_codes)) if __name__ == "__main__": diff --git a/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_scopes.py b/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_scopes.py index a5274acf8..992e9b586 100644 --- a/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_scopes.py +++ b/tests/integration/src/sqlbuild/compiler/pipeline/test_semantic_scopes.py @@ -150,7 +150,7 @@ def test_given_semantic_scope_when_compiling_then_closed_shapes_are_checked( "SELECT 1 AS id", 'SELECT id FROM __ref("upstream") WHERE SUM(id) > 0', "B231", - expected_column=44, + expected_column=40, ), SemanticCompileCase( "count star after macro", diff --git a/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_command.py b/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_command.py index 7921c088c..a20253d69 100644 --- a/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_command.py +++ b/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_command.py @@ -268,14 +268,14 @@ def test_given_python_project_dag_flag_when_running_compile_then_writes_python_d description="reports contract and output locations for type diagnostics", expected_exit_code=1, expected_stdout_fragments=( - "error[K002]: column 'amount_cents' inferred as TEXT but declared type is INTEGER", + "error[K002]: column 'amount_cents' inferred as VARCHAR but declared type is INTEGER", " --> models/orders.sql:4:5", " 4 | amount_cents (type INTEGER),", " | ^^^^^^^^^^^^", " output:", " --> models/orders.sql:9:3", " 9 | CAST('1' AS VARCHAR) AS amount_cents", - "inferred TEXT", + "inferred VARCHAR", " = help: change the declared type or cast the expression explicitly", ), model_sql=( From 926616da881bfbd6fe2233fdfd48bf0635d73a71 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:06:29 +0000 Subject: [PATCH 059/160] WIP (not shipping): Databricks reads session types it cannot spell by their family, where the family has one type The session keeps SQLBuild's spellings, such as TEXT for an expression source's string columns, which Databricks' parser refuses, so the columns downstream lost their type on the native route. session_type keeps a spelling Databricks reads and respells STRING, BOOLEAN and DATE families, which have one Databricks type each. Integer, decimal, float and timestamp spellings stay unread: Databricks has several types in each family, and a family alone cannot say which. The Databricks gap test cases carry the source offset the native route now records. --- .../_helpers/databricks_dialect.rs | 24 ++++++++- .../src/analysis_route/constants.rs | 4 ++ .../tests/test_databricks_dialect.rs | 53 +++++++++++++++++++ .../src/analysis_route/tests/test_types.rs | 6 +++ 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs index 87398f6a9..3bef82bcd 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs @@ -6,8 +6,9 @@ use sqlquack_syntax::{DataType, Expression}; use sqlquack_syntax_databricks::{Parsed, StrictFailure, parse_strict}; use crate::analysis_route::constants::{ - DATABRICKS_CAST_TYPE_PREFIX, DATABRICKS_DEFAULT_DECIMAL, DATABRICKS_DIALECT, - DATABRICKS_STRING_TYPE, DATABRICKS_STUB_PREFIX, UNSCANNED_CONSTRUCT, + DATABRICKS_BOOLEAN_TYPE, DATABRICKS_CAST_TYPE_PREFIX, DATABRICKS_DATE_TYPE, + DATABRICKS_DEFAULT_DECIMAL, DATABRICKS_DIALECT, DATABRICKS_STRING_TYPE, DATABRICKS_STUB_PREFIX, + UNSCANNED_CONSTRUCT, }; use crate::analysis_route::models::{DatabricksDialect, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; @@ -40,6 +41,25 @@ impl NativeDialect for DatabricksDialect { &DATABRICKS } + /// A spelling Databricks cannot read takes its family's type where the family has only one. + fn session_type(&self, spelling: &str) -> String { + if self.normalize_type(spelling).is_some() { + return spelling.to_owned(); + } + let native: &str = match normalize_text_type(spelling, None).family { + TypeFamily::String => DATABRICKS_STRING_TYPE, + TypeFamily::Boolean => DATABRICKS_BOOLEAN_TYPE, + TypeFamily::Date => DATABRICKS_DATE_TYPE, + TypeFamily::Integer + | TypeFamily::Decimal + | TypeFamily::Float + | TypeFamily::Timestamp + | TypeFamily::Datetime + | TypeFamily::Other => spelling, + }; + native.to_owned() + } + fn normalize_type(&self, spelling: &str) -> Option { let spelt: String = canonical(&cast_target(spelling)?)?; Some(normalize_text_type(&spelt, Some(TypeDialect::Databricks))) diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index a7b9cd725..959459a5b 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -18,6 +18,10 @@ pub(crate) const DATABRICKS_CAST_TYPE_PREFIX: &str = "SELECT CAST(NULL AS "; pub(crate) const DATABRICKS_DEFAULT_DECIMAL: (u32, u32) = (10, 0); /// The type Databricks gives every string expression, a cast to CHAR(n) or VARCHAR(n) included. pub(crate) const DATABRICKS_STRING_TYPE: &str = "STRING"; +/// Databricks' one boolean type. +pub(crate) const DATABRICKS_BOOLEAN_TYPE: &str = "BOOLEAN"; +/// Databricks' one date type. +pub(crate) const DATABRICKS_DATE_TYPE: &str = "DATE"; /// The dialect a project with no analysis dialect is named by. pub(crate) const GENERIC_DIALECT: &str = "generic"; /// The gap of SQL that holds more than one statement. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs index 7181eabfd..3126ff3d5 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_databricks_dialect.rs @@ -2,6 +2,7 @@ use crate::analysis_route::main::native_gap::native_gap; use crate::analysis_route::models::{DatabricksDialect, LocatedGap, TypeMatch}; use crate::analysis_route::tests::test_types::{ DatabricksTypeTestCase, NativeGapTestCase, NativeTypeMatchTestCase, ParseFailureTestCase, + SessionTypeTestCase, }; use crate::analysis_route::types::NativeDialect; use crate::assembly::project::models::SyntaxCheck; @@ -39,6 +40,7 @@ fn given_databricks_model_sql_when_probing_its_parser_then_only_sql_it_reads_who line: 1, column: 23, syntax: true, + source_offset: Some(22), }), }, NativeGapTestCase { @@ -51,6 +53,7 @@ fn given_databricks_model_sql_when_probing_its_parser_then_only_sql_it_reads_who line: 1, column: 1, syntax: false, + source_offset: Some(0), }), }, NativeGapTestCase { @@ -63,6 +66,7 @@ fn given_databricks_model_sql_when_probing_its_parser_then_only_sql_it_reads_who line: 1, column: 1, syntax: false, + source_offset: Some(0), }), }, ]; @@ -359,3 +363,52 @@ fn given_a_declared_and_an_inferred_databricks_type_when_matching_then_a_length_ ); } } + +#[test] +fn given_a_session_type_when_databricks_reads_it_then_only_single_type_families_are_respelt() { + let test_cases = [ + SessionTypeTestCase { + description: "a Databricks spelling is kept", + spelling: "DECIMAL(10, 2)", + expected_type: "DECIMAL(10, 2)", + }, + SessionTypeTestCase { + description: "a sized VARCHAR is a Databricks type, so it is kept", + spelling: "VARCHAR(12)", + expected_type: "VARCHAR(12)", + }, + SessionTypeTestCase { + description: "Polyglot's TEXT for string literals reads as STRING", + spelling: "TEXT", + expected_type: "STRING", + }, + SessionTypeTestCase { + description: "BOOL reads as BOOLEAN", + spelling: "BOOL", + expected_type: "BOOLEAN", + }, + SessionTypeTestCase { + description: "an integer of unknown width has no one Databricks type, so stays unread", + spelling: "INT64", + expected_type: "INT64", + }, + SessionTypeTestCase { + description: "a zoned timestamp stays unread", + spelling: "TIMESTAMPTZ", + expected_type: "TIMESTAMPTZ", + }, + SessionTypeTestCase { + description: "a MAP in another dialect's spelling stays unread", + spelling: "MAP(VARCHAR, INTEGER)", + expected_type: "MAP(VARCHAR, INTEGER)", + }, + ]; + for test_case in test_cases { + assert_eq!( + DatabricksDialect.session_type(test_case.spelling), + test_case.expected_type, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index ce10f4fcb..b352d5ad5 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -266,3 +266,9 @@ pub(super) struct ParseFailureTestCase { /// The byte offset of a syntax error; a limit has none worth comparing. pub(super) expected_offset: Option, } + +pub(super) struct SessionTypeTestCase { + pub(super) description: &'static str, + pub(super) spelling: &'static str, + pub(super) expected_type: &'static str, +} From 3a3dacd50c2255cacc532226f1d26d3ff7737169 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:06:29 +0000 Subject: [PATCH 060/160] WIP (not shipping): validation pin of every sqlquack crate to 02bd392d 02bd392d is the approved Databricks semantics link (the profile and its LATERAL VIEW and set-operation ORDER BY fix) on SQLQuack main 38be86ba; it is not on main yet. Repin every sqlquack crate to the SQLQuack main commit that carries it. --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 188f0f15a..dd33c5e75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -4501,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "serde", "serde_json", @@ -4511,7 +4511,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "regex", "serde", @@ -4525,7 +4525,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "serde", "sqlquack-core", @@ -4534,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "serde", "serde_json", @@ -4547,7 +4547,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-databricks" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "sqlquack-syntax", "stacker", @@ -4556,7 +4556,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "proc-macro2", "quote", @@ -4566,7 +4566,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=a37cc362916e2556c82c8403fcff10ff7b8ed074#a37cc362916e2556c82c8403fcff10ff7b8ed074" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" dependencies = [ "sqlquack-syntax", "stacker", @@ -4663,7 +4663,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5099,7 +5099,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9872e5e17..e7d2618ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,11 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } -sqlquack-syntax-databricks = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "a37cc362916e2556c82c8403fcff10ff7b8ed074" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } +sqlquack-syntax-databricks = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From b7b6cd65c6cc6f5a7bca5ef26fa3235f5221641f Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:07:46 +0000 Subject: [PATCH 061/160] fix(analysis): give native routes the session's functions, quoting and case setting A native route analysed every model against a schema with no function facts, quoted output names lost their quoting, and Snowflake's QUOTED_IDENTIFIERS_IGNORE_CASE was ignored. - The session schema now answers the adapter's declared function return types, respelt for the dialect, and its first_arg, conditional_result and safe_cast nullability rules. - A stored name in double quotes becomes a quoted schema column, with "" read as one quote, wherever the session, revalidation or fast lineage builds one. - The session request carries the profile's quoted-identifier case setting into the cache key and each dialect's analysis, so a dialect can pick a profile for it. - Semantic completion no longer panics when the binding catalog is already borrowed: unknown relations stay per-request. - An adapter's own Python nullability rules are not called on a native route; compile now warns, naming the adapter and the functions. The callback tests run on the Polyglot route, and the payload-only completion check expects a native route to require its session. --- .../_helpers/native_analysis.rs | 5 +- .../analysis_route/_helpers/revalidation.rs | 18 ++-- .../src/analysis_route/main/fast_lineage.rs | 16 ++-- .../src/analysis_route/main/mod.rs | 2 + .../src/analysis_route/main/model_outcome.rs | 3 +- .../main/native_revalidation.rs | 2 +- .../src/analysis_route/main/schema_column.rs | 25 ++++++ .../main/unapplied_nullability_rules.rs | 18 ++++ .../src/analysis_route/models.rs | 2 + .../src/analysis_route/tests/helpers.rs | 16 ++-- .../tests/test_native_dialect.rs | 52 ++++++++++- .../tests/test_native_outcome.rs | 86 +++++++++++++++++-- .../tests/test_native_revalidation.rs | 4 +- .../src/analysis_route/tests/test_types.rs | 13 ++- .../src/analysis_route/types.rs | 6 ++ .../_helpers/analysis_cache.rs | 1 + .../analysis_session/_helpers/mappings.rs | 60 ++++++++++--- .../analysis_session/_helpers/session.rs | 16 +++- .../src/assembly/analysis_session/models.rs | 34 +++++++- .../analysis_session/tests/helpers.rs | 7 +- .../_helpers/analysis/binding_catalog.rs | 15 ++-- .../analysis_session/model_analyses.rs | 1 + .../analysis_session/profile_inputs.rs | 2 +- .../_helpers/analysis_session/session.rs | 3 + .../compile_driver/assembly_analysis.rs | 42 ++++++++- .../src/bindings/_helpers/native_host/host.rs | 9 ++ .../compiler/analysis_session/_test_types.py | 2 +- .../compiler/analysis_session/helpers.py | 7 +- .../compiler/semantic_checks/helpers.py | 12 +++ .../test_native_semantic_checks.py | 11 ++- 30 files changed, 420 insertions(+), 70 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/schema_column.rs create mode 100644 crates/sqlbuild-analysis/src/analysis_route/main/unapplied_nullability_rules.rs diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs index 3f6577207..417cb2def 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs @@ -27,8 +27,9 @@ pub(crate) fn native_analysis( statement: &Expression, input: &AnalysisInput<'_>, ) -> NativeAnalysis { + let profile: &'static dyn DialectProfile = dialect.profile_for(input); let context: LineageContext<'_> = LineageContext { - profile: dialect.profile(), + profile, schema: input.schema, sql: Some(input.sql), }; @@ -42,7 +43,7 @@ pub(crate) fn native_analysis( lineage: lineage .outputs .iter() - .map(|output| lineage_row(dialect.profile(), output, input.references)) + .map(|output| lineage_row(profile, output, input.references)) .collect(), row_sources: lineage .row_sources diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs index 96530c5d2..96c507d37 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; +use crate::analysis_route::main::schema_column::schema_column; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::UNKNOWN_TYPE; @@ -38,7 +39,7 @@ pub(crate) fn revised( dialect: &dyn NativeDialect, outcome: &ModelOutcome, poisoned: &[(String, String)], - declared: &[String], + finished: &FinishedSession, ) -> Result>, String> { let sql: Cow<'_, str> = resolved_cleaned_sql(outcome)?; let relations: SessionSchema = SessionSchema { @@ -53,12 +54,14 @@ pub(crate) fn revised( ) }) .collect::>>(), + functions: finished.functions.clone(), }; let analysed: Result = dialect.analyse(&AnalysisInput { sql: &sql, schema: &relations, references: &[], - extension_functions: declared, + extension_functions: &finished.declared_functions, + quoted_ignore_case: finished.quoted_ignore_case, }); let Ok(analysis) = analysed else { return Ok(None); @@ -94,12 +97,11 @@ fn schema_columns( || poisoned .iter() .any(|(model, output)| model == relation && output == name); - schema.push(SchemaColumn { - name: name.clone(), - quoted: false, - data_type: (!untyped).then(|| dialect.session_type(data_type)), - nullability: Nullability::Unknown, - }); + schema.push(schema_column( + name, + (!untyped).then(|| dialect.session_type(data_type)), + Nullability::Unknown, + )); } schema } diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs index 3f3fbf262..bcde53cba 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs @@ -8,13 +8,14 @@ use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; use crate::analysis_route::_helpers::located_gap::{located, unreadable_reason}; use crate::analysis_route::_helpers::marker_names::{distinct_calls, first_call_names}; +use crate::analysis_route::main::schema_column::schema_column; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, ParserGap}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::{ CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, TRANSFORM_DIRECT, TRANSFORM_STAR, }; -use crate::assembly::analysis_session::models::{LineageRow, SessionSchema}; +use crate::assembly::analysis_session::models::{LineageRow, SessionFunctions, SessionSchema}; use crate::lineage::models::{ FastLineageOutcome, LineageColumn, LineageConfidence, LineageResourceType, LineageSchemaResource, LineageSource, LineageTransformKind, @@ -74,6 +75,7 @@ pub fn fast_lineage( schema: &relations, references: &references, extension_functions: &[], + quoted_ignore_case: false, }); match analysed { Ok(native) => FastLineageOutcome::Built { @@ -106,16 +108,14 @@ fn markers_schema( resource .columns .iter() - .map(|column| SchemaColumn { - name: column.clone(), - quoted: false, - data_type: None, - nullability: Nullability::Unknown, - }) + .map(|column| schema_column(column, None, Nullability::Unknown)) .collect(), ); } - SessionSchema { relations } + SessionSchema { + relations, + functions: SessionFunctions::default(), + } } /// A session lineage row as a fast lineage column; sources of other resource kinds are dropped. diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs index 578305573..d85ed3ce1 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/mod.rs @@ -5,5 +5,7 @@ pub mod fast_lineage; pub(crate) mod model_outcome; pub mod native_gap; pub mod native_revalidation; +pub mod schema_column; pub mod skipped_analysis; +pub mod unapplied_nullability_rules; pub mod unavailable_analysis; diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs index 1395539f7..504ab04f8 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/model_outcome.rs @@ -15,7 +15,7 @@ pub(crate) fn model_outcome( dialect: &dyn NativeDialect, model: &ModelRequest, (schema, relations): (Shapes, &SessionSchema), - extension_functions: &[String], + (extension_functions, quoted_ignore_case): (&[String], bool), ) -> (ModelOutcome, Option) { let (cleaned_sql, analysed): (String, Result) = match normalize_native_sql(&model.query_sql, dialect.name(), dialect.stub_prefix()) { @@ -27,6 +27,7 @@ pub(crate) fn model_outcome( schema: relations, references: &model.lineage_references, extension_functions, + quoted_ignore_case, }) .map_err(|gap: ParserGap| gap.message); (sql, analysed) diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs index 67ae52f5c..35cd3db37 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/native_revalidation.rs @@ -24,7 +24,7 @@ pub fn native_revalidation( .par_iter() .map(|model| { let outcome: &ModelOutcome = session_outcome(finished, outcomes, &model.name)?; - match revised(dialect, outcome, poisoned, &finished.declared_functions) { + match revised(dialect, outcome, poisoned, finished) { Ok(Some(rows)) => Ok(rows), Ok(None) => Ok(unrevised(model)), Err(reason) => Err(format!("{}: {reason}", model.name)), diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/schema_column.rs b/crates/sqlbuild-analysis/src/analysis_route/main/schema_column.rs new file mode 100644 index 000000000..d2a89894a --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/main/schema_column.rs @@ -0,0 +1,25 @@ +use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; + +use crate::assembly::analysis_session::constants::QUOTED_IDENTIFIER_DELIMITER; + +/// A stored column name as SQLQuack reads it: `"…"` is quoted, its inner `""` one quote. +#[must_use] +pub fn schema_column( + name: &str, + data_type: Option, + nullability: Nullability, +) -> SchemaColumn { + let delimiter: String = QUOTED_IDENTIFIER_DELIMITER.to_string(); + let inner: Option<&str> = name + .strip_prefix(QUOTED_IDENTIFIER_DELIMITER) + .and_then(|rest| rest.strip_suffix(QUOTED_IDENTIFIER_DELIMITER)); + SchemaColumn { + name: inner.map_or_else( + || name.to_owned(), + |inner| inner.replace(&delimiter.repeat(2), &delimiter), + ), + quoted: inner.is_some(), + data_type, + nullability, + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/unapplied_nullability_rules.rs b/crates/sqlbuild-analysis/src/analysis_route/main/unapplied_nullability_rules.rs new file mode 100644 index 000000000..1a5b2b593 --- /dev/null +++ b/crates/sqlbuild-analysis/src/analysis_route/main/unapplied_nullability_rules.rs @@ -0,0 +1,18 @@ +use crate::analysis_route::models::AnalysisRoute; + +/// The warning for an adapter's own nullability rules on a native route, which does not call them. +#[must_use] +pub fn unapplied_nullability_rules( + route: &AnalysisRoute, + adapter: &str, + functions: &[String], +) -> Option { + match route { + AnalysisRoute::Native(_) if !functions.is_empty() => Some(format!( + "Adapter '{adapter}' defines its own nullability rule for {}; native SQL analysis \ + does not apply it, so these calls take the dialect's own nullability", + functions.join(", ") + )), + _ => None, + } +} diff --git a/crates/sqlbuild-analysis/src/analysis_route/models.rs b/crates/sqlbuild-analysis/src/analysis_route/models.rs index 838fd60c2..bc886000c 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/models.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/models.rs @@ -89,6 +89,8 @@ pub struct AnalysisInput<'a> { pub references: &'a [(String, String, String)], /// Functions the catalogue cannot know, such as MotherDuck's `md_*`; never reported unknown. pub extension_functions: &'a [String], + /// Whether quoted identifiers compare without case, as Snowflake's session parameter sets. + pub quoted_ignore_case: bool, } /// One query's analysis, in the facts the analysis session keeps for a model. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs index 9dd60dfa2..ed28a13c2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs @@ -2,10 +2,11 @@ use std::collections::HashMap; use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; +use crate::analysis_route::main::schema_column::schema_column as stored_column; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis, StandInSchema}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::models::{ - LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, SessionSchema, + LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, SessionFunctions, SessionSchema, }; use crate::assembly::analysis_session::types::Shapes; use crate::lineage::models::{LineageColumn, LineageSource}; @@ -41,6 +42,7 @@ pub(super) fn analysed( schema: &schema, references: &references, extension_functions: &extension_functions, + quoted_ignore_case: false, }) .expect("the test query is read whole") } @@ -95,17 +97,15 @@ pub(super) fn session_relations(shape: &[(&str, &str, Nullability)]) -> (Shapes, .collect(); ( vec![("raw_orders".to_owned(), pairs)], - SessionSchema { relations }, + SessionSchema { + relations, + functions: SessionFunctions::default(), + }, ) } fn schema_column(&(name, data_type, nullability): &(&str, &str, Nullability)) -> SchemaColumn { - SchemaColumn { - name: name.to_owned(), - quoted: false, - data_type: Some(data_type.to_owned()), - nullability, - } + stored_column(name, Some(data_type.to_owned()), nullability) } /// A finished outcome of `sql` over `orders`, as the session bound it. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index 20eabe24a..bb18b63c6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -1,10 +1,11 @@ +use crate::analysis_route::main::unapplied_nullability_rules::unapplied_nullability_rules; use crate::analysis_route::main::unavailable_analysis::unavailable_analysis; -use crate::analysis_route::models::{NativeAnalysis, TypeMatch}; +use crate::analysis_route::models::{AnalysisRoute, NativeAnalysis, TypeMatch}; use crate::analysis_route::tests::helpers::{analysed, resource_columns}; use crate::analysis_route::tests::test_types::{ CataloguedDialect, ColumnTestCase, FindingTestCase, FoldingDialect, LineageTestCase, MacroBodyTestCase, NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, StarTestCase, - UnavailableAnalysisTestCase, + UnappliedRulesTestCase, UnavailableAnalysisTestCase, }; use crate::analysis_route::types::NativeDialect; @@ -137,6 +138,53 @@ fn given_no_native_parser_when_skipping_analysis_then_p026_names_the_dialect() { } } +#[test] +fn given_adapter_nullability_rules_when_routing_then_only_a_native_route_warns_they_are_unapplied() +{ + let test_cases = [ + UnappliedRulesTestCase { + description: "a native route names the adapter and each function it leaves alone", + route: AnalysisRoute::Native(&PivotlessDialect), + functions: &["UPPER", "IFF"], + expected_warning: Some( + "Adapter 'acme' defines its own nullability rule for UPPER, IFF; native SQL \ + analysis does not apply it, so these calls take the dialect's own nullability", + ), + }, + UnappliedRulesTestCase { + description: "Polyglot calls the adapter's rules, so nothing is unapplied", + route: AnalysisRoute::Legacy, + functions: &["UPPER"], + expected_warning: None, + }, + UnappliedRulesTestCase { + description: "no model is analysed where no parser reads the dialect", + route: AnalysisRoute::Unavailable, + functions: &["UPPER"], + expected_warning: None, + }, + UnappliedRulesTestCase { + description: "an adapter with no rules of its own has nothing to warn about", + route: AnalysisRoute::Native(&PivotlessDialect), + functions: &[], + expected_warning: None, + }, + ]; + for test_case in test_cases { + let functions: Vec = test_case + .functions + .iter() + .map(|f| (*f).to_owned()) + .collect(); + assert_eq!( + unapplied_nullability_rules(&test_case.route, "acme", &functions), + test_case.expected_warning.map(str::to_owned), + "{}", + test_case.description + ); + } +} + #[test] fn given_findings_when_analysing_then_they_take_sqlbuild_codes_and_positions() { let test_cases = [ diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index 095d840e1..e592759a3 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -5,12 +5,14 @@ use crate::analysis_route::tests::test_types::{ CataloguedDialect, FoldingDialect, MarkerLineageTestCase, NativeLineageTestCase, NativeOutcomeTestCase, PivotlessDialect, }; -use crate::assembly::analysis_session::models::{ModelOutcome, ModelRequest, SessionSchema}; +use crate::assembly::analysis_session::models::{ + ModelOutcome, ModelRequest, SessionFunctions, SessionSchema, +}; use crate::assembly::analysis_session::types::Shapes; use crate::lineage::models::{ LineageColumn, LineageOutcomeParts, LineageResourceType, LineageSchemaResource, }; -use sqlquack_semantics::lineage::models::Nullability; +use sqlquack_semantics::lineage::models::{Nullability, NullabilityRule}; #[test] fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_follow_them() { @@ -23,6 +25,7 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll ("status", "VARCHAR", Nullability::Nullable), ], declared_functions: &[], + nullability_rules: &[], expected_lines: &[ "order_id INTEGER non_null", "status VARCHAR nullable", @@ -34,6 +37,7 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll sql: "SELECT * FROM raw_orders", shape: &[], declared_functions: &[], + nullability_rules: &[], expected_lines: &["succeeded=true star=true/false findings=[]"], }, NativeOutcomeTestCase { @@ -41,6 +45,7 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll sql: "SELECT FROM WHERE", shape: &[("order_id", "INTEGER", Nullability::NonNull)], declared_functions: &[], + nullability_rules: &[], expected_lines: &["succeeded=false star=false/false findings=[]"], }, NativeOutcomeTestCase { @@ -48,6 +53,7 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll sql: "SELECT CURRENT_TIMESTAMP() AS at FROM raw_orders", shape: &[("order_id", "INTEGER", Nullability::NonNull)], declared_functions: &[("CURRENT_TIMESTAMP", "TIMESTAMPTZ")], + nullability_rules: &[], expected_lines: &[ "at TIMESTAMPTZ unknown", "succeeded=true star=false/true findings=[]", @@ -58,15 +64,81 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll sql: "SELECT CURRENT_TIMESTAMP() AS at FROM raw_orders", shape: &[("order_id", "INTEGER", Nullability::NonNull)], declared_functions: &[], + nullability_rules: &[], expected_lines: &[ "at TIMESTAMPTZ unknown", "succeeded=true star=false/true findings=[\"B101\"]", ], }, + NativeOutcomeTestCase { + description: "a declared function's return type types its call", + sql: "SELECT shop_day(order_id) AS d FROM raw_orders", + shape: &[("order_id", "INTEGER", Nullability::NonNull)], + declared_functions: &[("SHOP_DAY", "DATE")], + nullability_rules: &[], + expected_lines: &[ + "d DATE unknown", + "succeeded=true star=false/true findings=[]", + ], + }, + NativeOutcomeTestCase { + description: "a declared function without a return type is untyped", + sql: "SELECT shop_day(order_id) AS d FROM raw_orders", + shape: &[("order_id", "INTEGER", Nullability::NonNull)], + declared_functions: &[("SHOP_DAY", "")], + nullability_rules: &[], + expected_lines: &["d - unknown", "succeeded=true star=false/true findings=[]"], + }, + NativeOutcomeTestCase { + description: "an adapter's first-argument rule keeps a non-null argument non-null", + sql: "SELECT shop_label(status) AS s FROM raw_orders", + shape: &[("status", "VARCHAR", Nullability::NonNull)], + declared_functions: &[("SHOP_LABEL", "VARCHAR")], + nullability_rules: &[("SHOP_LABEL", NullabilityRule::FirstArgument)], + expected_lines: &[ + "s VARCHAR non_null", + "succeeded=true star=false/true findings=[]", + ], + }, + NativeOutcomeTestCase { + description: "without the rule the same call's nullability is unknown", + sql: "SELECT shop_label(status) AS s FROM raw_orders", + shape: &[("status", "VARCHAR", Nullability::NonNull)], + declared_functions: &[("SHOP_LABEL", "VARCHAR")], + nullability_rules: &[], + expected_lines: &[ + "s VARCHAR unknown", + "succeeded=true star=false/true findings=[]", + ], + }, + NativeOutcomeTestCase { + description: "a quoted stored name is the quoted column, its doubled quote one quote", + sql: "SELECT \"Say \"\"hi\"\"\" AS c FROM raw_orders", + shape: &[("\"Say \"\"hi\"\"\"", "VARCHAR", Nullability::NonNull)], + declared_functions: &[], + nullability_rules: &[], + expected_lines: &[ + "c VARCHAR non_null", + "succeeded=true star=false/true findings=[]", + ], + }, ]; for test_case in test_cases { - let (schema, relations): (Shapes, SessionSchema) = session_relations(test_case.shape); + let (schema, mut relations): (Shapes, SessionSchema) = session_relations(test_case.shape); + relations.functions = SessionFunctions { + return_types: test_case + .declared_functions + .iter() + .filter(|(_, data_type)| !data_type.is_empty()) + .map(|(name, data_type)| ((*name).to_owned(), (*data_type).to_owned())) + .collect(), + nullability: test_case + .nullability_rules + .iter() + .map(|(name, rule)| ((*name).to_owned(), *rule)) + .collect(), + }; let model: ModelRequest = ModelRequest { name: "orders".to_owned(), query_sql: test_case.sql.to_owned(), @@ -78,8 +150,12 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll .iter() .map(|(name, _)| (*name).to_owned()) .collect(); - let (outcome, failure): (ModelOutcome, Option) = - model_outcome(&CataloguedDialect, &model, (schema, &relations), &declared); + let (outcome, failure): (ModelOutcome, Option) = model_outcome( + &CataloguedDialect, + &model, + (schema, &relations), + (&declared, false), + ); let analysis = &outcome.analysis; let mut lines: Vec = analysis .columns diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs index 0e68c40a8..6ad508bc4 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_revalidation.rs @@ -5,7 +5,7 @@ use crate::analysis_route::main::native_revalidation::native_revalidation; use crate::analysis_route::tests::helpers::{finding_rows, outcome}; use crate::analysis_route::tests::test_types::{PivotlessDialect, RevalidationTestCase}; use crate::assembly::analysis_session::models::{ - FinishedSession, ModelOutcome, PivotOutcome, PivotTables, + FinishedSession, ModelOutcome, PivotOutcome, PivotTables, SessionFunctions, }; use crate::semantic_checks::models::{RawBinding, RecoveryModel}; use crate::semantic_checks::types::RevisedBinding; @@ -43,6 +43,8 @@ fn given_poisoned_outputs_when_revalidating_natively_then_findings_keep_session_ relations: None, model_names: vec!["other".to_owned(), "downstream".to_owned()], declared_functions: Vec::new(), + functions: SessionFunctions::default(), + quoted_ignore_case: false, }; let outcomes: Vec<(ModelOutcome, PivotOutcome)> = vec![ (outcome("SELECT 1"), PivotOutcome::Absent), diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 382cb4133..1a6567c52 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -1,10 +1,10 @@ use sqlquack_semantics::dialects::constants::{DUCKDB, POSTGRES, SNOWFLAKE}; use sqlquack_semantics::dialects::types::DialectProfile; -use sqlquack_semantics::lineage::models::Nullability; +use sqlquack_semantics::lineage::models::{Nullability, NullabilityRule}; use sqlquack_syntax::expressions::statements::models::Command; use sqlquack_syntax::{Dialect, Expression, ParseOptions, Parsed, parse_strict}; -use crate::analysis_route::models::{GapSite, LocatedGap, ParserGap, TypeMatch}; +use crate::analysis_route::models::{AnalysisRoute, GapSite, LocatedGap, ParserGap, TypeMatch}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; use crate::type_system::models::NormalizedType; @@ -208,6 +208,13 @@ pub(super) struct UnavailableAnalysisTestCase { pub(super) expected_message: &'static str, } +pub(super) struct UnappliedRulesTestCase { + pub(super) description: &'static str, + pub(super) route: AnalysisRoute, + pub(super) functions: &'static [&'static str], + pub(super) expected_warning: Option<&'static str>, +} + /// Each output's name, transform code, confidence code and the columns it reads. type ExpectedLineage = &'static [(&'static str, u8, u8, &'static [&'static str])]; @@ -239,6 +246,8 @@ pub(super) struct NativeOutcomeTestCase { pub(super) shape: &'static [(&'static str, &'static str, Nullability)], /// The adapter's declared functions as `(name, return type)`. pub(super) declared_functions: &'static [(&'static str, &'static str)], + /// The adapter's built-in nullability rules as `(function name, rule)`. + pub(super) nullability_rules: &'static [(&'static str, NullabilityRule)], /// Each output column as `name type nullability`, then success, star flags and finding codes. pub(super) expected_lines: &'static [&'static str], } diff --git a/crates/sqlbuild-analysis/src/analysis_route/types.rs b/crates/sqlbuild-analysis/src/analysis_route/types.rs index d22d4c9a3..f64582c20 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/types.rs @@ -26,6 +26,12 @@ pub trait NativeDialect: Sync { /// The dialect's sqlquack-semantics profile: how its names resolve, compare and type. fn profile(&self) -> &'static dyn DialectProfile; + /// The profile `input` is analysed under; a dialect whose name rules follow a setting overrides it. + fn profile_for(&self, input: &AnalysisInput<'_>) -> &'static dyn DialectProfile { + let _ = input; + self.profile() + } + /// A type as written in this dialect, as SQLBuild compares it; `None` when unknown or refused. fn normalize_type(&self, spelling: &str) -> Option; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs index 757a894a9..a814d3bfa 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/analysis_cache.rs @@ -222,6 +222,7 @@ fn session_digest(request: &SessionRequest) -> ContentDigest { key.text(ANALYSIS_CACHE_FORMAT); key.text(&request.dialect); key.flag(request.case_sensitive_shapes); + key.flag(request.quoted_ignore_case); key.pairs(&request.function_return_types); key.optional_pairs(request.nullability_rules.as_ref()); key.flag(request.rich_type_inference); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs index fc2bdaeb8..a4f2495e7 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs @@ -4,11 +4,16 @@ use std::collections::{BTreeSet, HashMap}; use std::sync::OnceLock; use sha2::{Digest, Sha256}; -use sqlquack_semantics::lineage::models::{Nullability, SchemaColumn}; +use sqlquack_semantics::lineage::models::{Nullability, NullabilityRule, SchemaColumn}; +use crate::analysis_route::main::schema_column::schema_column; use crate::analysis_route::types::NativeDialect; -use crate::assembly::analysis_session::constants::{DIALECT_ALIASES, UNKNOWN_TYPE}; -use crate::assembly::analysis_session::models::{ModelRequest, SessionSchema}; +use crate::assembly::analysis_session::constants::{ + CONDITIONAL_RESULT_RULE, DIALECT_ALIASES, FIRST_ARG_RULE, SAFE_CAST_RULE, UNKNOWN_TYPE, +}; +use crate::assembly::analysis_session::models::{ + ModelRequest, SessionFunctions, SessionRequest, SessionSchema, +}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::Columns; use crate::semantic_validation::types::Relations; @@ -241,11 +246,33 @@ pub(crate) fn waves(producers: &[Vec]) -> Option>> { (scheduled == producers.len()).then_some(waves) } -/// Each relation with a known shape, its columns typed and with the nullability the session knows. +/// The request's function return types, and its built-in nullability rules; adapter callbacks are not. +pub(crate) fn session_functions(request: &SessionRequest) -> SessionFunctions { + SessionFunctions { + return_types: request.function_return_types.clone(), + nullability: request + .nullability_rules + .iter() + .flatten() + .filter_map(|(function, id)| { + let rule: NullabilityRule = match id.as_str() { + FIRST_ARG_RULE => NullabilityRule::FirstArgument, + CONDITIONAL_RESULT_RULE => NullabilityRule::ConditionalResult, + SAFE_CAST_RULE => NullabilityRule::SafeCast, + _ => return None, + }; + Some((function.clone(), rule)) + }) + .collect(), + } +} + +/// Each relation with a known shape, typed with the nullability the session knows, and the functions. pub(crate) fn session_schema( dialect: &dyn NativeDialect, schema: &Shapes, nullability: &ShapeTable, + functions: &SessionFunctions, ) -> SessionSchema { let relations: HashMap> = schema .iter() @@ -256,7 +283,17 @@ pub(crate) fn session_schema( (relation.clone(), typed) }) .collect(); - SessionSchema { relations } + SessionSchema { + relations, + functions: SessionFunctions { + return_types: functions + .return_types + .iter() + .map(|(function, data_type)| (function.clone(), dialect.session_type(data_type))) + .collect(), + nullability: functions.nullability.clone(), + }, + } } /// Each column with its known type as `dialect` reads it, and the nullability `known` spells. @@ -267,12 +304,13 @@ fn typed_columns( ) -> Vec { columns .iter() - .map(|(name, data_type)| SchemaColumn { - name: name.clone(), - quoted: false, - data_type: (!data_type.is_empty() && data_type != UNKNOWN_TYPE) - .then(|| dialect.session_type(data_type)), - nullability: column_nullability(known, name), + .map(|(name, data_type)| { + schema_column( + name, + (!data_type.is_empty() && data_type != UNKNOWN_TYPE) + .then(|| dialect.session_type(data_type)), + column_nullability(known, name), + ) }) .collect() } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 97dcb6abc..48999b826 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -31,7 +31,7 @@ use crate::assembly::analysis_session::_helpers::enrichment::{ Enrichment, EnrichmentInput, enrichment, }; use crate::assembly::analysis_session::_helpers::mappings::{ - ShapeTable, dict_from_pairs, same_keys, same_relations, session_schema, + ShapeTable, dict_from_pairs, same_keys, same_relations, session_functions, session_schema, }; use crate::assembly::analysis_session::_helpers::mappings::{producers, waves}; use crate::assembly::analysis_session::constants::{UNKNOWN_NULLABILITY, UNKNOWN_TYPE}; @@ -39,7 +39,8 @@ use crate::assembly::analysis_session::main::resolved_cleaned_sql::resolved_clea use crate::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, ContractProof, DeferredCleaning, FinishedSession, LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, - RelationTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionSchema, SessionStep, + RelationTables, SessionFunctions, SessionModelFacts, SessionOutcome, SessionRequest, + SessionSchema, SessionStep, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::main::normalize_native::normalize_native_sql; @@ -181,6 +182,8 @@ impl AnalysisSession { .iter() .map(|(name, _)| name.clone()) .collect(); + let functions: SessionFunctions = session_functions(&request); + let quoted_ignore_case: bool = request.quoted_ignore_case; let finished = FinishedSession { tables: PivotTables { dialect: request.dialect, @@ -195,6 +198,8 @@ impl AnalysisSession { relations, model_names, declared_functions, + functions, + quoted_ignore_case, }; let outcome = SessionOutcome { models, @@ -331,14 +336,17 @@ impl AnalysisSession { .iter() .map(|(name, _)| name.clone()) .collect(); + let functions: SessionFunctions = session_functions(request); let outcomes: Vec<(ModelOutcome, Option)> = pool.install(|| { misses .par_iter() .map(|position| { let model: &ModelRequest = &request.models[models[*position]]; let schema: Shapes = schemas[*position].clone(); - let relations: SessionSchema = session_schema(dialect, &schema, nullability); - model_outcome(dialect, model, (schema, &relations), &declared) + let relations: SessionSchema = + session_schema(dialect, &schema, nullability, &functions); + let analysis = (declared.as_slice(), request.quoted_ignore_case); + model_outcome(dialect, model, (schema, &relations), analysis) }) .collect() }); diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 537ed0f04..6f79793ac 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use rayon::ThreadPool; use sqlbuild_cache::digest::types::ContentDigest; use sqlbuild_cache::store::models::NativeStore; -use sqlquack_semantics::lineage::models::SchemaColumn; +use sqlquack_semantics::lineage::models::{NullabilityRule, SchemaColumn}; use sqlquack_semantics::lineage::types::LineageSchema; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; @@ -118,6 +118,10 @@ pub struct FinishedSession { pub(crate) model_names: Vec, /// The adapter's declared functions, which the native analysis knew. pub(crate) declared_functions: Vec, + /// The adapter's function return types and nullability rules the native analysis read. + pub(crate) functions: SessionFunctions, + /// Whether quoted identifiers compared without case in the session's analysis. + pub(crate) quoted_ignore_case: bool, } /// The session's typed result for one compiled model, borrowed from the finished session. @@ -176,6 +180,8 @@ pub struct SessionRequest { pub dialect: String, /// Whether published shapes keep authored quoting, Python's `inferred_binding_shape` test. pub case_sensitive_shapes: bool, + /// Whether quoted identifiers compare without case, as Snowflake's session parameter sets. + pub quoted_ignore_case: bool, pub function_return_types: Pairs, /// Adapter nullability rules as `(function name, rule id)`. pub nullability_rules: Option, @@ -342,11 +348,21 @@ pub struct AnalysisCache { pub(crate) stats: AnalysisCacheStats, } +/// The adapter's function facts a native route gives SQLQuack: return types and nullability rules. +#[derive(Clone, Debug, Default)] +pub(crate) struct SessionFunctions { + /// `(function name, type)` of each function whose return type the adapter declares. + pub(crate) return_types: Vec<(String, String)>, + /// `(function name, rule)` of each built-in nullability rule the adapter names. + pub(crate) nullability: Vec<(String, NullabilityRule)>, +} + /// The relations one model reads, as the session knows them, for a native route's analysis. #[derive(Debug, Default)] pub(crate) struct SessionSchema { /// Columns of each relation with a known shape, by the analysis name the query reads. pub(crate) relations: HashMap>, + pub(crate) functions: SessionFunctions, } impl LineageSchema for SessionSchema { @@ -361,6 +377,22 @@ impl LineageSchema for SessionSchema { }) .map(Vec::as_slice) } + + fn function_type(&self, name: &str) -> Option<&str> { + self.functions + .return_types + .iter() + .find(|(function, _)| function.eq_ignore_ascii_case(name)) + .map(|(_, data_type)| data_type.as_str()) + } + + fn function_nullability(&self, name: &str) -> Option { + self.functions + .nullability + .iter() + .find(|(function, _)| function.eq_ignore_ascii_case(name)) + .map(|(_, rule)| *rule) + } } /// One compile's model analysis, advanced phase by phase. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 3597a8c17..7687674a6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -19,8 +19,8 @@ use crate::assembly::analysis_session::main::take_analysis_cache::take_analysis_ use crate::assembly::analysis_session::models::{ AnalysisCacheStats, AnalysisSession, ColumnFact, ContractProof, DynamicFamily, FinishedSession, LineageRow, ModelColumns, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, - PivotModel, PivotOutcome, PivotTables, SessionModelAnalysis, SessionModelFacts, SessionOutcome, - SessionRequest, SessionStep, + PivotModel, PivotOutcome, PivotTables, SessionFunctions, SessionModelAnalysis, + SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::tests::test_types::{ CachedRun, ColumnView, ModelSpec, RecoveredFacts, @@ -121,6 +121,7 @@ pub(crate) fn orders_request(models: Vec) -> SessionRequest { SessionRequest { dialect: "duckdb".to_owned(), case_sensitive_shapes: false, + quoted_ignore_case: false, function_return_types: Vec::new(), nullability_rules: Some(Vec::new()), nullability_callback: None, @@ -402,6 +403,8 @@ pub(crate) fn finished_session(tables: PivotTables) -> FinishedSession { relations: None, model_names: Vec::new(), declared_functions: Vec::new(), + functions: SessionFunctions::default(), + quoted_ignore_case: false, } } diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis/binding_catalog.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis/binding_catalog.rs index 5a0dab6dd..3aa2be4ba 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis/binding_catalog.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis/binding_catalog.rs @@ -109,7 +109,7 @@ impl BindingCatalog { }) } - /// `BindingCatalog.prepare`: add unknown closed relations, then override changed ones. + /// `BindingCatalog.prepare`: add unknown closed relations (overrides while in use), then changed ones. fn prepared( &self, py: Python<'_>, @@ -121,16 +121,21 @@ impl BindingCatalog { let schema: Bound<'_, PyAny> = native_field(request, "schema")?; for item in mapping_items(&schema)?.try_iter()? { let (name, columns): (Bound<'_, PyAny>, Bound<'_, PyAny>) = item?.extract()?; - if columns.is_truthy()? && !schemas.contains(&name)? { + if columns.is_truthy()? + && !schemas.contains(&name)? + && !additions.contains(&name)? + { additions.set_item(&name, &columns)?; - schemas.set_item(&name, &columns)?; } } } - if !additions.is_empty() { + if !additions.is_empty() + && let Ok(mut native) = self.native.try_borrow_mut(py) + { self.extended.store(true, Ordering::Relaxed); + schemas.update(additions.as_mapping())?; let relations: Relations = conversions::relations(additions.as_any())?; - self.native.borrow_mut(py).inner.update_relations(relations); + native.inner.update_relations(relations); } let mut prepared: Vec = Vec::with_capacity(requests.len()); for request in requests { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/model_analyses.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/model_analyses.rs index e964962a2..cd4096e76 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/model_analyses.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/model_analyses.rs @@ -129,6 +129,7 @@ fn session_request<'py>( profile, &native_field(profile, "sql_analysis_dialect")?, )?, + quoted_ignore_case: native_field(profile, "quoted_identifiers_ignore_case")?.is_truthy()?, function_return_types: return_types(profile)?, nullability_rules: Some(rules), rich_type_inference: request.rich_type_inference, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/profile_inputs.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/profile_inputs.rs index 94a47c4c0..045cc2861 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/profile_inputs.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/profile_inputs.rs @@ -15,7 +15,7 @@ use crate::bindings::_helpers::records::fields::mapping_items; use crate::bindings::_helpers::records::ids::RecordId; const GENERIC_DIALECT: &str = "generic"; -const ADAPTER_RULE_ID: &str = "python"; +pub(crate) const ADAPTER_RULE_ID: &str = "python"; /// The nullability rules the native analysis knows, with their native rule ids. const KNOWN_RULES: [(AdapterHost, &str); 3] = [ (AdapterHost::FirstArgNullability, "first_arg"), diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 83e702475..8716b69b5 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -99,6 +99,7 @@ pub(crate) struct ModelRow { pub(crate) struct RequestRow { pub(crate) dialect: String, pub(crate) case_sensitive_shapes: bool, + pub(crate) quoted_ignore_case: bool, pub(crate) function_return_types: Pairs, pub(crate) nullability_rules: Option, pub(crate) rich_type_inference: bool, @@ -456,6 +457,7 @@ fn session_request(request: RequestRow) -> SessionRequest { let RequestRow { dialect, case_sensitive_shapes, + quoted_ignore_case, function_return_types, nullability_rules, rich_type_inference, @@ -469,6 +471,7 @@ fn session_request(request: RequestRow) -> SessionRequest { SessionRequest { dialect, case_sensitive_shapes, + quoted_ignore_case, function_return_types, nullability_rules, nullability_callback: None, diff --git a/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_analysis.rs b/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_analysis.rs index 642ba9517..562800f14 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_analysis.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/compile_driver/assembly_analysis.rs @@ -4,6 +4,9 @@ use pyo3::IntoPyObjectExt; use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyResult, Python}; use pyo3::types::{PyBool, PyDict, PyDictMethods, PyFrozenSet, PyString, PyTuple, PyTupleMethods}; +use sqlbuild_analysis::analysis_route::main::analysis_route::analysis_route; +use sqlbuild_analysis::analysis_route::main::unapplied_nullability_rules::unapplied_nullability_rules; +use sqlbuild_analysis::analysis_route::models::AnalysisRoute; use sqlbuild_analysis::assembly::analysis_session::main::native_dialect::native_dialect; use sqlbuild_analysis::assembly::analysis_session::types::Pairs; use sqlbuild_host::compile_context::main::record_compile_cpu_timing::record_compile_cpu_timing; @@ -16,7 +19,9 @@ use crate::bindings::_helpers::analysis::binding_catalog::{ use crate::bindings::_helpers::analysis_session::model_analyses::{ ModelAnalyses, ModelAnalysisRequest, analyze_models, }; -use crate::bindings::_helpers::analysis_session::profile_inputs::text_pairs; +use crate::bindings::_helpers::analysis_session::profile_inputs::{ + ADAPTER_RULE_ID, nullability_rules, text_pairs, +}; use crate::bindings::_helpers::analysis_session::stage_functions::expression_source_shapes; use crate::bindings::_helpers::boundary::python_calls::keywords; use crate::bindings::_helpers::compile_context::compile_timings::PythonProcessClock; @@ -27,6 +32,7 @@ use crate::bindings::_helpers::compile_driver::stage_work::{ }; use crate::bindings::_helpers::compiled_views::model_input_view::attached_schema; use crate::bindings::_helpers::compiled_views::source_views::read_source_view; +use crate::bindings::_helpers::contracts::spec_resolution::resolve_effective_adapter_name; use crate::bindings::_helpers::native_host::host; use crate::bindings::_helpers::project_assembly::declared_relations::{ DeclaredRelations, DeclaredShapes, declared_relations, @@ -45,6 +51,7 @@ use std::collections::HashMap; const GENERIC_DIALECT: &str = "generic"; const UDF_PREFIX: &str = "__sqlbuild_udf_"; +const COMPILE_LOGGER: &str = "sqlbuild.compile"; /// The analysis state every assembled model and the project share. pub(crate) struct AnalysisContext<'py> { @@ -278,6 +285,38 @@ fn function_return_types<'py>( Ok(types) } +/// Warn that a native route leaves the adapter's own nullability rules uncalled. +fn warn_unapplied_nullability_rules( + inputs: &Bound<'_, PyAny>, + profile: &Bound<'_, PyAny>, +) -> PyResult<()> { + let (rows, adapter_rules) = nullability_rules(profile)?; + if adapter_rules.is_none() { + return Ok(()); + } + let mut functions: Vec = rows + .into_iter() + .filter(|(_, id)| id == ADAPTER_RULE_ID) + .map(|(function, _)| function) + .collect(); + functions.sort(); + let dialect: Option = native_field(profile, "sql_analysis_dialect")?.extract()?; + let adapter: Option = resolve_effective_adapter_name( + &native_field(inputs, "project_config")?, + &native_field(inputs, "local_config")?, + )? + .extract()?; + let route: AnalysisRoute = analysis_route(dialect.as_deref()); + let named: &str = adapter + .as_deref() + .or(dialect.as_deref()) + .unwrap_or(GENERIC_DIALECT); + match unapplied_nullability_rules(&route, named, &functions) { + Some(warning) => host::log_warning_message(inputs.py(), COMPILE_LOGGER, &warning), + None => Ok(()), + } +} + /// Record each expression source's analysed shape as its declared relation. fn record_expression_shapes<'py>( request: &AssemblyRequest, @@ -391,6 +430,7 @@ fn model_analyses<'py>( return Ok(None); } let inputs: &Bound<'py, PyAny> = request.inputs.bind(py); + warn_unapplied_nullability_rules(inputs, &context.profile)?; let names: &Bound<'py, PyAny> = request.analysis_model_names.bind(py); let mode: &Bound<'py, PyAny> = request.column_lineage_mode.bind(py); let lineage_mode: Bound<'py, PyAny> = record_class(py, RecordId::ColumnLineageMode)?; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/native_host/host.rs b/crates/sqlbuild-python/src/bindings/_helpers/native_host/host.rs index babcc38c9..84d87dcdc 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/native_host/host.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/native_host/host.rs @@ -97,3 +97,12 @@ pub(crate) fn log_debug_message(py: Python<'_>, logger: &str, message: &str) -> .call((), Some(&kwargs)) .map(|_| ()) } + +/// `logging.getLogger(logger).warning(message)`. +pub(crate) fn log_warning_message(py: Python<'_>, logger: &str, message: &str) -> PyResult<()> { + py.import("logging")? + .getattr("getLogger")? + .call1((logger,))? + .call_method1("warning", (message,)) + .map(|_| ()) +} diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 350086450..3d3515d7c 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -88,7 +88,7 @@ class CteFactRecoveryParityTestCase: @dataclass(frozen=True) class AdapterRuleCallbackTestCase: - """A project-local adapter rule the native session calls back into, and what it raises.""" + """A project-local adapter rule the Polyglot route calls back into, and what it raises.""" description: str seed: int diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index a4f2c1993..7ffe4d1f0 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -757,7 +757,10 @@ def __call__(self, arguments: tuple[InferredNullability, ...]) -> InferredNullab def adapter_rule_project( *, test_case: AdapterRuleCallbackTestCase, rule: RecordingNullabilityRule, tmp_path: Path ) -> tuple[CompileInputsView, ExpressionInferenceProfile]: - """A generated project with the adapter-rule models, and a profile calling `rule`.""" + """A generated project with the adapter-rule models, and a Polyglot profile calling `rule`. + + A native route does not call an adapter's own rules; compile warns instead. + """ return ( compile_inputs( @@ -770,7 +773,7 @@ def adapter_rule_project( }, ), ExpressionInferenceProfile( - sql_analysis_dialect="duckdb", + sql_analysis_dialect=None, function_nullability_rules={"UPPER": rule, "IFF": rule}, ), ) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index a0488f1fa..9aba77650 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -26,6 +26,7 @@ if TYPE_CHECKING: from sqlbuild._native import DiagnosticRow +_NATIVE_SESSION_REQUIRED: str = "revalidating a native route without its session" _RELATIONS: dict[str, tuple[str, ...]] = { '__source("raw_orders")': ( "order_id", @@ -262,6 +263,17 @@ def without_session(inputs: SemanticInputs) -> SemanticInputs: return replace(inputs, session=None) +def payload_completion_view(inputs: SemanticInputs) -> tuple[object, ...] | None: + """The completion from payload facts alone; `None` where a native route requires the session.""" + + try: + completed: CompiledProjectView = completed_natively(without_session(inputs)) + except ValueError as error: + assert _NATIVE_SESSION_REQUIRED in str(error) + return None + return completion_view(completed) + + def session_fact_models(inputs: SemanticInputs) -> frozenset[str]: """Models whose output names and lineage the finished session supplies to the stage.""" diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index 1f2c4f7dc..da2259b15 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -38,12 +38,12 @@ native_completion, note_kinds, outcome_label, + payload_completion_view, proven_output_count, semantic_outcome, session_corpus_files, session_fact_models, with_dialect, - without_session, ) _NOTE_KINDS: tuple[str, ...] = ("downstream output uses", "downstream uses of", " has: ") @@ -261,14 +261,14 @@ def test_given_compiles_when_completing_from_the_session_then_matches_payload_an selections: list[frozenset[str]] = [session_fact_models(inputs) for inputs in captured] codes: Counter[str] = Counter() golden: list[GoldenEntry] = [] - payload_views: list[tuple[object, ...]] = [] + payload_views: list[tuple[object, ...] | None] = [] session_views: list[tuple[object, ...]] = [] for index, inputs in enumerate(captured): completed: CompiledProjectView = completed_natively(inputs) codes.update(item.code for item in completed.diagnostics) codes.update(note_kinds(completed, _NOTE_KINDS)) session_views.append(completion_view(completed)) - payload_views.append(completion_view(completed_natively(without_session(inputs)))) + payload_views.append(payload_completion_view(inputs)) golden.append( golden_entry( "completion", session_views[-1], masked=(str(tmp_path / f"project_{index}"),) @@ -284,7 +284,10 @@ def test_given_compiles_when_completing_from_the_session_then_matches_payload_an ) == [] ) - assert payload_views == session_views + assert all( + payload in (None, session) + for payload, session in zip(payload_views, session_views, strict=True) + ) assert ( session_models, model_count - session_models, From 99785e65eb0ea607f711934457378ac4c6d3902f Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:12:32 +0000 Subject: [PATCH 062/160] fix(analysis): quote an unknown quoted column as written SQLQuack quotes an unknown column's name in lower case, so a quoted "MiSsPeLlEd" was reported as 'misspelled'. A native route now puts the authored name back into the message. --- .../_helpers/native_analysis.rs | 18 ++++++++-- .../src/analysis_route/constants.rs | 2 ++ .../tests/test_native_dialect.rs | 35 +++++++++++++++++-- .../src/analysis_route/tests/test_types.rs | 6 ++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs index 417cb2def..9e2730994 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/native_analysis.rs @@ -8,7 +8,9 @@ use sqlquack_semantics::lineage::models::{ use sqlquack_semantics::{statement_lineage, statement_name_findings}; use sqlquack_syntax::Expression; -use crate::analysis_route::constants::{ERROR_SEVERITY, UNKNOWN_FUNCTION_CODE}; +use crate::analysis_route::constants::{ + ERROR_SEVERITY, UNKNOWN_COLUMN_CODE, UNKNOWN_FUNCTION_CODE, +}; use crate::analysis_route::models::{AnalysisInput, NativeAnalysis}; use crate::analysis_route::types::NativeDialect; use crate::assembly::analysis_session::constants::{ @@ -156,7 +158,7 @@ fn diagnostic_row(input: &AnalysisInput<'_>, finding: NameFinding) -> Option, finding: NameFinding) -> Option String { + if finding.code != UNKNOWN_COLUMN_CODE || finding.written.is_empty() { + return finding.message.clone(); + } + finding.message.replacen( + &format!("'{}'", finding.written.to_lowercase()), + &format!("'{}'", finding.written), + 1, + ) +} + /// The function an unknown-function finding names, as written or as its message quotes it. fn function_name(finding: &NameFinding) -> &str { if finding.written.is_empty() { diff --git a/crates/sqlbuild-analysis/src/analysis_route/constants.rs b/crates/sqlbuild-analysis/src/analysis_route/constants.rs index 9e622059a..70cba12be 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/constants.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/constants.rs @@ -20,6 +20,8 @@ pub(crate) const NULLABLE_WRAPPER: &str = "Nullable"; pub(crate) const LOW_CARDINALITY_WRAPPER: &str = "LowCardinality"; /// The finding of a function the dialect's catalogue does not know. pub(crate) const UNKNOWN_FUNCTION_CODE: &str = "E202"; +/// The finding of a column no referenced relation has. +pub(crate) const UNKNOWN_COLUMN_CODE: &str = "E201"; /// The severity of every finding the native analysis reports, as the Polyglot path blocks them. pub(crate) const ERROR_SEVERITY: &str = "error"; /// What `macro_body` puts before a body to read it as the one projection of a query. diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs index bb18b63c6..8dbdd7990 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_dialect.rs @@ -3,9 +3,9 @@ use crate::analysis_route::main::unavailable_analysis::unavailable_analysis; use crate::analysis_route::models::{AnalysisRoute, NativeAnalysis, TypeMatch}; use crate::analysis_route::tests::helpers::{analysed, resource_columns}; use crate::analysis_route::tests::test_types::{ - CataloguedDialect, ColumnTestCase, FindingTestCase, FoldingDialect, LineageTestCase, - MacroBodyTestCase, NativeTypeMatchTestCase, PivotlessDialect, ReadWholeTestCase, StarTestCase, - UnappliedRulesTestCase, UnavailableAnalysisTestCase, + CataloguedDialect, ColumnTestCase, FindingMessageTestCase, FindingTestCase, FoldingDialect, + LineageTestCase, MacroBodyTestCase, NativeTypeMatchTestCase, PivotlessDialect, + ReadWholeTestCase, StarTestCase, UnappliedRulesTestCase, UnavailableAnalysisTestCase, }; use crate::analysis_route::types::NativeDialect; @@ -138,6 +138,35 @@ fn given_no_native_parser_when_skipping_analysis_then_p026_names_the_dialect() { } } +#[test] +fn given_an_unknown_quoted_column_when_analysing_then_its_message_quotes_the_name_as_written() { + let test_cases = [ + FindingMessageTestCase { + description: "a quoted name keeps its authored case", + sql: "SELECT \"MiSsPeLlEd\" FROM orders", + expected_messages: &[("B002", "Unknown column 'MiSsPeLlEd' in table 'orders'")], + }, + FindingMessageTestCase { + description: "a qualified quoted name keeps its authored case", + sql: "SELECT o.\"Missing\" FROM orders AS o", + expected_messages: &[("B002", "Unknown column 'Missing' in table 'orders'")], + }, + ]; + for test_case in test_cases { + let analysis: NativeAnalysis = analysed(&PivotlessDialect, test_case.sql, &[]); + let messages: Vec<(&str, &str)> = analysis + .diagnostics + .iter() + .map(|row| (row.code.as_str(), row.message.as_str())) + .collect(); + assert_eq!( + messages, test_case.expected_messages, + "{}", + test_case.description + ); + } +} + #[test] fn given_adapter_nullability_rules_when_routing_then_only_a_native_route_warns_they_are_unapplied() { diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 1a6567c52..9caa9ddf2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -208,6 +208,12 @@ pub(super) struct UnavailableAnalysisTestCase { pub(super) expected_message: &'static str, } +pub(super) struct FindingMessageTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + pub(super) expected_messages: &'static [(&'static str, &'static str)], +} + pub(super) struct UnappliedRulesTestCase { pub(super) description: &'static str, pub(super) route: AnalysisRoute, From e64c6b553466d64718031a45e7d8df21e5f935fd Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:15:59 +0000 Subject: [PATCH 063/160] feat(analysis): fold quoted Snowflake names under QUOTED_IDENTIFIERS_IGNORE_CASE With the session parameter set, the Snowflake arm analyses through SQLQuack's quoted-ignoring-case profile, so a quoted name resolves to upper case like an unquoted one, as Snowflake does. --- .../src/analysis_route/_helpers/snowflake_dialect.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs index 1eff9c3fd..128c19261 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs @@ -1,6 +1,6 @@ //! Snowflake's native module: SQLQuack's Snowflake parser reads its SQL. Validation only. -use sqlquack_semantics::dialects::constants::SNOWFLAKE; +use sqlquack_semantics::dialects::constants::{SNOWFLAKE, SNOWFLAKE_QUOTED_IGNORING_CASE}; use sqlquack_semantics::dialects::types::DialectProfile; use sqlquack_syntax::{ Dialect, Error, Expression, ParseOptions, Parsed, StrictFailure, TokenizeFailure, parse_strict, @@ -10,7 +10,7 @@ use crate::analysis_route::_helpers::type_spelling::respelt; use crate::analysis_route::constants::{ SNOWFLAKE_DIALECT, SNOWFLAKE_TYPE_SPELLING, UNQUOTED_STUB_PREFIX, UNSCANNED_CONSTRUCT, }; -use crate::analysis_route::models::{ParserGap, SnowflakeDialect}; +use crate::analysis_route::models::{AnalysisInput, ParserGap, SnowflakeDialect}; use crate::analysis_route::types::NativeDialect; use crate::type_system::main::normalize_text_type::normalize_text_type; use crate::type_system::models::{NormalizedType, TypeDialect}; @@ -32,6 +32,14 @@ impl NativeDialect for SnowflakeDialect { &SNOWFLAKE } + /// Snowflake's profile, with quoted names folded as well under QUOTED_IDENTIFIERS_IGNORE_CASE. + fn profile_for(&self, input: &AnalysisInput<'_>) -> &'static dyn DialectProfile { + match input.quoted_ignore_case { + true => &SNOWFLAKE_QUOTED_IGNORING_CASE, + false => &SNOWFLAKE, + } + } + /// The type as Polyglot normalised it, or `None` (unknown) where the text fallback can't match it. fn normalize_type(&self, spelling: &str) -> Option { respelt(spelling, &SNOWFLAKE_TYPE_SPELLING) From 03e62ffdfdbbb4358946ca249b11dfed5a00053b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:15:59 +0000 Subject: [PATCH 064/160] test(analysis): re-record CREATE TABLE AS and INSERT ... SELECT columns as unknown Native analysis reads only queries, so a model whose SQL is a CREATE TABLE AS or an INSERT ... SELECT gets no columns rather than its inner SELECT's. --- .../analysis_session/test_native_query_columns.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py index 07c8955d1..9485a7ce0 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_query_columns.py @@ -30,14 +30,14 @@ "test_case", [ FallbackColumnsTestCase( - description="CREATE TABLE AS takes its inner select's columns", + description="CREATE TABLE AS, not a query, has unknown columns", query_sql="CREATE TABLE t AS SELECT 1 AS a", - expected_columns=(("a", None, "non_null"),), + expected_columns=(), ), FallbackColumnsTestCase( - description="INSERT ... SELECT takes its select's columns", + description="INSERT ... SELECT, not a query, has unknown columns", query_sql="INSERT INTO t SELECT 1 AS a", - expected_columns=(("a", None, "non_null"),), + expected_columns=(), ), FallbackColumnsTestCase( description="calls nested 100 deep", From 9e9d911b794605f222ce84883ffed6e6942ebbff Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:18:21 +0000 Subject: [PATCH 065/160] test(analysis): expect the native refusal to complete without a session as a compiler error --- .../src/sqlbuild/compiler/semantic_checks/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index 9aba77650..80cd8c7dd 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -13,6 +13,7 @@ import sqlbuild._native as native_module from sqlbuild._native import ( CompilerDiagnostic, + NativeCompilerError, project_with, ) from sqlbuild.adapter.contract.models import ExpressionInferenceProfile @@ -268,7 +269,7 @@ def payload_completion_view(inputs: SemanticInputs) -> tuple[object, ...] | None try: completed: CompiledProjectView = completed_natively(without_session(inputs)) - except ValueError as error: + except NativeCompilerError as error: assert _NATIVE_SESSION_REQUIRED in str(error) return None return completion_view(completed) From d0a0af2d4367de63bd4bee1a20f8c237bfeefa1e Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:29:35 +0000 Subject: [PATCH 066/160] test(analysis): capture each generated semantics project under its own dialect The generated failing-projects test compiled every project under DuckDB and re-read the captured inputs as each other dialect, so a dialect's golden held DuckDB's analysis of its models. Each dialect now compiles the project under its own analysis dialect, with DuckDB's function rules kept for all. --- .../compiler/semantic_checks/helpers.py | 19 +++++++++++++++++++ .../test_native_semantic_checks.py | 13 +++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index 80cd8c7dd..92fbd3b21 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -199,6 +199,25 @@ def captured_semantic_inputs( return SemanticInputs(**stage) +def captured_under_dialect( + *, + project_dir: Path, + files: dict[str, str], + dialect: str, +) -> SemanticInputs: + """Compile under `dialect`, with DuckDB's function rules, and keep the stage's inputs.""" + + stage: dict[str, Any] | None = native_module.assembly_stage_inputs( + "semantic_completion", + inputs=compile_inputs(project_dir=project_dir, files=files), + inference_profile=replace( + DuckDbAdapter().expression_inference_profile(), sql_analysis_dialect=dialect + ), + ) + assert stage is not None + return SemanticInputs(**stage) + + def with_dialect(inputs: SemanticInputs, dialect: str | None) -> SemanticInputs: """The same stage inputs read under another SQL dialect.""" diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index da2259b15..821585873 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -30,6 +30,7 @@ from tests.integration.src.sqlbuild.compiler.semantic_checks.helpers import ( SemanticInputs, captured_semantic_inputs, + captured_under_dialect, completed_natively, completion_view, files_with_non_ascii, @@ -84,15 +85,15 @@ def test_given_generated_failing_projects_when_completing_natively_then_matches_ golden: list[GoldenEntry] = [] for index in range(test_case.count): project_dir: Path = tmp_path / f"project_{index}" - captured: SemanticInputs = captured_semantic_inputs( - project_dir=project_dir, - files=generated_semantic_files( - rng=rng, model_count=test_case.model_count, require_analysis=index % 2 == 0 - ), + files: dict[str, str] = generated_semantic_files( + rng=rng, model_count=test_case.model_count, require_analysis=index % 2 == 0 ) views: dict[str, object] = {} for dialect in test_case.dialects: - native: CompiledProjectView = completed_natively(with_dialect(captured, dialect)) + captured: SemanticInputs = captured_under_dialect( + project_dir=project_dir, files=files, dialect=dialect + ) + native: CompiledProjectView = completed_natively(captured) statuses["completion_native"] += 1 statuses["diagnostics_native"] += len(native.diagnostics) statuses["type_recovery_planned"] += has_blocking_binding(captured.project) From f4e75951dffc6e62801d4a12b4550f74baf8a692 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 17:01:51 +0000 Subject: [PATCH 067/160] WIP (not shipping): pin every sqlquack crate to SQLQuack main 3b497dfa SQLQuack main 3b497dfa carries the Databricks semantics link 02bd392d, so the validation pin is replaced by a main commit. --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd33c5e75..75b068c6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4091,7 +4091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4501,7 +4501,7 @@ dependencies = [ [[package]] name = "sqlquack-core" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "serde", "serde_json", @@ -4511,7 +4511,7 @@ dependencies = [ [[package]] name = "sqlquack-semantics" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "regex", "serde", @@ -4525,7 +4525,7 @@ dependencies = [ [[package]] name = "sqlquack-snowflake" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "serde", "sqlquack-core", @@ -4534,7 +4534,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "serde", "serde_json", @@ -4547,7 +4547,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-databricks" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "sqlquack-syntax", "stacker", @@ -4556,7 +4556,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-derive" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "proc-macro2", "quote", @@ -4566,7 +4566,7 @@ dependencies = [ [[package]] name = "sqlquack-syntax-postgres" version = "0.2.0" -source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=02bd392d5d250656b606ee13cc65b4e639d95fb7#02bd392d5d250656b606ee13cc65b4e639d95fb7" +source = "git+https://github.com/sqlstack-io/sqlquack.git?rev=3b497dfadeb5ff7ed96efb5478197f1d7878ddd1#3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" dependencies = [ "sqlquack-syntax", "stacker", @@ -4663,7 +4663,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -5099,7 +5099,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e7d2618ba..92a0e1489 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,11 +177,11 @@ sqlbuild-state = { path = "crates/sqlbuild-state" } # SQL lint Rules, quality Rules and the formatter. sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "cf79656d247277a30bc81ef39b6dee420e721221" } # Scopes, name resolution, types and lineage for the native analysis route. -sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } +sqlquack-semantics = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" } # The lossless SQL tokenizer behind query fingerprints. -sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } -sqlquack-syntax-databricks = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } -sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "02bd392d5d250656b606ee13cc65b4e639d95fb7" } +sqlquack-syntax = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" } +sqlquack-syntax-databricks = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" } +sqlquack-syntax-postgres = { git = "https://github.com/sqlstack-io/sqlquack.git", rev = "3b497dfadeb5ff7ed96efb5478197f1d7878ddd1" } stacker = "0.1" syn = { version = "2", features = ["full", "visit", "extra-traits"] } subtle = "2" From 3879482ba60f9509f5a6dda81d5a27647effa931 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 17:06:53 +0000 Subject: [PATCH 068/160] test(analysis): re-record the native digest goldens and pin the open native gaps Re-record the Python-oracle digest goldens the native DuckDB, Snowflake and SQLite routes change: engine type spellings and result types, nullability proven by filters and literals, unknown columns for stars over relations without an authoritative shape, per-output types for duplicate output names, and messages that keep the authored spelling. Keep the open gaps as strict expected failures beside passing controls: Snowflake's invalid coercible literal and date arithmetic, and declared source types that native analysis does not read. The adapter-rule case is renamed, because adapter callbacks are not called on a native route. Native routes record no legacy lineage enrichment, so those minimums are 0 where every profile is native. --- .../_helpers/snowflake_dialect.rs | 3 +- ...self_and_a_star_consumer_of_the_cycle.json | 32 +- ...ullability_rules_called_back_natively.json | 626 --------- ...ty_rules_not_called_on_a_native_route.json | 626 +++++++++ ...ntyped_inputs_cte_facts_and_contracts.json | 1134 ++++++++--------- ...and_duckdb_adapter_rules_fast_lineage.json | 460 +++---- ...and_duckdb_adapter_rules_rich_lineage.json | 472 +++---- ...tions_untyped_inputs_contracts_pivots.json | 1074 ++++++++-------- ...g_column_readers_and_a_unique_summary.json | 52 +- ...and_comments_new_and_unknown_dialects.json | 28 +- ...ferences_cursors_audits_and_sql_tests.json | 44 +- ...ects_added_to_the_native_parser_build.json | 24 +- ...ii_names_comments_and_line_separators.json | 32 +- ..._comparisons_type_errors_and_opt_outs.json | 62 +- ..._with_pivots_stars_and_set_operations.json | 8 +- ...ntic_session_failing_semantics_corpus.json | 12 +- ...r_a_model_with_a_non_ascii_name_after.json | 6 +- .../compiler/analysis_session/_test_types.py | 10 + .../compiler/analysis_session/helpers.py | 20 + .../test_native_model_analysis.py | 83 +- .../compiler/semantic_checks/_test_types.py | 11 + .../compiler/semantic_checks/helpers.py | 18 + .../test_native_semantic_checks.py | 67 +- 23 files changed, 2549 insertions(+), 2355 deletions(-) delete mode 100644 tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json create mode 100644 tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_not_called_on_a_native_route.json diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs index 128c19261..bdc39499e 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/snowflake_dialect.rs @@ -47,8 +47,7 @@ impl NativeDialect for SnowflakeDialect { } } -/// Every statement the shared parser reads from `sql` in `dialect`, or its first refusal; it -/// cannot tell invalid syntax from an unsupported construct, so no refusal is marked syntax. +/// Every statement the shared parser reads from `sql`, or its first refusal, never marked syntax. pub(crate) fn shared_parsed(sql: &str, dialect: Dialect) -> Result { let statements: Vec = parse_strict(sql, dialect, &ParseOptions::default()) .map_err(|failure| match failure { diff --git a/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json b/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json index 84e00f8f2..423c4c4a4 100644 --- a/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json +++ b/tests/goldens/python_oracle/analysis_cyclic_a_ref_cycle_a_model_reading_itself_and_a_star_consumer_of_the_cycle.json @@ -6,15 +6,15 @@ [ "models", { -"order_audit": "102a144114b4a8a3e464d565", +"order_audit": "58e599de7d3c99f821c9f47e", "order_summary": "ca21b0318683cc6b7506af3d", -"orders": "412acfefd9b029b3a46752c7", -"returns": "0d43c64379f12801c6a42e57" +"orders": "b9b6f79d642bfc6a539644a3", +"returns": "81bcb1e61a24c0c7f9cbcd47" } ], [ "catalog", -"2b8932e9fd162ab0b58d4e8f" +"3fc9fa4898ce446dc54a5ec9" ], [ "proof", @@ -39,15 +39,15 @@ [ "models", { -"order_audit": "102a144114b4a8a3e464d565", +"order_audit": "58e599de7d3c99f821c9f47e", "order_summary": "ca21b0318683cc6b7506af3d", -"orders": "6a1622e4397f1eef49fe9b3b", -"returns": "0d43c64379f12801c6a42e57" +"orders": "b9b6f79d642bfc6a539644a3", +"returns": "81bcb1e61a24c0c7f9cbcd47" } ], [ "catalog", -"2b8932e9fd162ab0b58d4e8f" +"3fc9fa4898ce446dc54a5ec9" ], [ "proof", @@ -72,15 +72,15 @@ [ "models", { -"order_audit": "102a144114b4a8a3e464d565", +"order_audit": "58e599de7d3c99f821c9f47e", "order_summary": "ca21b0318683cc6b7506af3d", -"orders": "412acfefd9b029b3a46752c7", -"returns": "0d43c64379f12801c6a42e57" +"orders": "b9b6f79d642bfc6a539644a3", +"returns": "81bcb1e61a24c0c7f9cbcd47" } ], [ "catalog", -"2b8932e9fd162ab0b58d4e8f" +"3fc9fa4898ce446dc54a5ec9" ], [ "proof", @@ -105,15 +105,15 @@ [ "models", { -"order_audit": "102a144114b4a8a3e464d565", +"order_audit": "58e599de7d3c99f821c9f47e", "order_summary": "ca21b0318683cc6b7506af3d", -"orders": "6a1622e4397f1eef49fe9b3b", -"returns": "50c61ef317c36e693b607753" +"orders": "b9b6f79d642bfc6a539644a3", +"returns": "81bcb1e61a24c0c7f9cbcd47" } ], [ "catalog", -"2b8932e9fd162ab0b58d4e8f" +"3fc9fa4898ce446dc54a5ec9" ], [ "proof", diff --git a/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json b/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json deleted file mode 100644 index 489f0beb0..000000000 --- a/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_called_back_natively.json +++ /dev/null @@ -1,626 +0,0 @@ -[ -[ -"shapes", -"78e5cd70ce361d823798634b" -], -[ -"models", -{ -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "df6e4a58863118447ccd6a56", -"orders_model_10": "bcbd615c256c35337aed2db2", -"orders_model_11": "47923badaec7c00915e5b4bb", -"orders_model_12": "1cc095a7cdb49d517637e721", -"orders_model_13": "800b7f2cfae2d608b60c7ece", -"orders_model_14": "22c002e97f6de1fe4b9cdccd", -"orders_model_15": "eebd1c2c0322fa13fdc75504", -"orders_model_16": "2717ab2a04d761545073308b", -"orders_model_17": "8255e9b0e9b9fb715e5d1f58", -"orders_model_18": "28541fce67c204773859236c", -"orders_model_19": "9f23b26f2c338b8fc3e3e838", -"orders_model_2": "5a185b79563abbd167d23c9e", -"orders_model_20": "d594752e20c6f55a9ca9a40e", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "50215bcfc3981855df918ff1", -"orders_model_23": "2c1c11368b18534a2e433f73", -"orders_model_3": "f530981ae0183c1746020b51", -"orders_model_4": "e29709e8e7acb44cc782304f", -"orders_model_5": "0c6b7057337af7825d4f5905", -"orders_model_6": "4308568a0bb439fb417623b4", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "be94f9981f346459dd08eb00", -"orders_model_9": "7a96226f82f52625a17f825f", -"payment_totals": "8cf82ad30e9cce84bc8a3529", -"payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "18ecbe5e93fe3ced5cbfd24a", -"pivot_1": "8ffb88803fba96f5e1a0cc14", -"pivot_2": "af151ba34f16c141fdee1ac3", -"pivot_3": "8d7a74ec77a418a6a2641b84", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" -} -], -[ -"catalog", -"d360424e65e4f8f287f9a397" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"20e6b6374d63663154468bed" -], -[ -"proof", -"20e6b6374d63663154468bed" -], -[ -"proof", -"b9689124495085d917d65ebf" -], -[ -"proof", -"0d91dfe8eefbbb36998db250" -], -[ -"proof", -"28c524727756a048843237ef" -], -[ -"proof", -"99b1ccea692774bb61737217" -], -[ -"shapes", -"78e5cd70ce361d823798634b" -], -[ -"models", -{ -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "dc7f3dec778fd7cc3b47de47", -"orders_model_1": "372f6fed52ac7b76d5703db0", -"orders_model_10": "e0d9b7a41394deecb38e3086", -"orders_model_11": "a74efc4485502093e7925022", -"orders_model_12": "159cce49ef0548e95a5b46fe", -"orders_model_13": "03539cfdeb6da7bf1e9fe042", -"orders_model_14": "3fdd19b40a943161f6c6cc9e", -"orders_model_15": "ca04925bf918b53768e0398d", -"orders_model_16": "e29709e8e7acb44cc782304f", -"orders_model_17": "b98505b8785820593e460502", -"orders_model_18": "86967e1e1505ff93a0965cef", -"orders_model_19": "966cc0c652215c4b0d249ec4", -"orders_model_2": "835e887650bbf9b124ae26b1", -"orders_model_20": "84ef045e4fbe323e09d9d96b", -"orders_model_21": "ab446571e5d18dc83a4b3cf5", -"orders_model_22": "f1de8641e65deae07a766a44", -"orders_model_23": "8c59f6a9f31545711675187a", -"orders_model_3": "000b4e41decb10da91684d6e", -"orders_model_4": "bf62990d9480cf71956fd3c9", -"orders_model_5": "0a4ba3dd4c01cce9c3179015", -"orders_model_6": "36c156c5a6601badf8124464", -"orders_model_7": "2cc4699fcfbfc50f29d34e4d", -"orders_model_8": "ebd5177365eb50337f61b39a", -"orders_model_9": "a0b1dba8f656eaa0bfb2c9dd", -"payment_totals": "8cf82ad30e9cce84bc8a3529", -"payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "af151ba34f16c141fdee1ac3", -"pivot_1": "db45532b4aa0746bb6155579", -"pivot_2": "60093df965b22d2f7c809ce4", -"pivot_3": "335dfa47b776524b62ee26e6", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" -} -], -[ -"catalog", -"05058c2d6069f67034746afd" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"b9689124495085d917d65ebf" -], -[ -"proof", -"0d91dfe8eefbbb36998db250" -], -[ -"proof", -"4994bb2a572acbed6354a2bb" -], -[ -"proof", -"c3cb3226e55a418829fd059c" -], -[ -"proof", -"28c524727756a048843237ef" -], -[ -"proof", -"99b1ccea692774bb61737217" -], -[ -"shapes", -"78e5cd70ce361d823798634b" -], -[ -"models", -{ -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "172c86dd8351e7740e167082", -"orders_model_10": "1348402b074ad555891e4bef", -"orders_model_11": "e0a1bcae33bbfb164c9468f6", -"orders_model_12": "0fbd9a675ff8d8560b0540a4", -"orders_model_13": "c414c20d1ede816d6b319333", -"orders_model_14": "d1550c8ea359faa939c59092", -"orders_model_15": "e0a6f9cf7645c417bdbdb2f3", -"orders_model_16": "6a3eeb4e49931817e15eec3a", -"orders_model_17": "d2f93646b187586cdc2ad5b6", -"orders_model_18": "252b1614ef32811f90405d55", -"orders_model_19": "20835381aab849fd35068525", -"orders_model_2": "541aad8bbf10a165003da4fa", -"orders_model_20": "9bb3b6be8506e6c3e809177f", -"orders_model_21": "c87751fbd20922b30ba3d847", -"orders_model_22": "264e63bb81a8673659bbb9db", -"orders_model_23": "ed8580ec8dc574a2041c68ee", -"orders_model_3": "4b659372639f55ac9ed99d36", -"orders_model_4": "c1496cb11408177cb813ba21", -"orders_model_5": "2d52cab3dcd8a4c198f59410", -"orders_model_6": "5ccadf27b4bf1a37e0f30682", -"orders_model_7": "16fc9c0348ca5eadfd147e95", -"orders_model_8": "1a1778883746da694b7922e3", -"orders_model_9": "7a96226f82f52625a17f825f", -"payment_totals": "8cf82ad30e9cce84bc8a3529", -"payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "d7685388e49eb5b076c776ac", -"pivot_1": "60093df965b22d2f7c809ce4", -"pivot_2": "b4bb4c1927b422d4b8b9ebb9", -"pivot_3": "a3b575dd05964ac460755bbd", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" -} -], -[ -"catalog", -"249032d056c80b1ec4ee3cea" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"74234e98afe7498fb5daf1f3" -], -[ -"proof", -"0d91dfe8eefbbb36998db250" -], -[ -"proof", -"4994bb2a572acbed6354a2bb" -], -[ -"proof", -"28c524727756a048843237ef" -], -[ -"proof", -"15cac1a9d4a0c399d20eec68" -], -[ -"proof", -"28c524727756a048843237ef" -], -[ -"proof", -"99b1ccea692774bb61737217" -] -] diff --git a/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_not_called_on_a_native_route.json b/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_not_called_on_a_native_route.json new file mode 100644 index 000000000..ada2e828e --- /dev/null +++ b/tests/goldens/python_oracle/analysis_generated_a_project_local_adapter_s_own_nullability_rules_not_called_on_a_native_route.json @@ -0,0 +1,626 @@ +[ +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "098010c5fda0aaacd463edfa", +"orders_model_15": "b8d23f08f59324fac2af511c", +"orders_model_18": "adb0a20a33e34fcf22e9dece", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_3": "ac14d6498f3827e560832222", +"orders_model_6": "cce383c29948d269c6512620", +"orders_model_9": "8061cad6eea8e358bc36ea9f", +"orders_model_1": "9e861fd9c6a6ee02f8cd9e6f", +"orders_model_10": "e2474a8016a05a8fcc2ec76f", +"orders_model_13": "cee4f52a30d55c4bf5b61902", +"orders_model_16": "1f6eacb4b853c105b065224a", +"orders_model_19": "e510adf7ef7e5becf44521bd", +"orders_model_22": "246d8a59293249a117c59e24", +"orders_model_4": "cad99cc00c4b5d4cf42f7b64", +"orders_model_7": "849e77b8ea8ae735aff06dd8", +"orders_model_11": "48c39b786c49f94c1bdfc245", +"orders_model_14": "484c4a29525317c48d82a1fc", +"orders_model_17": "7bf4dd10c9b258b193d13206", +"orders_model_2": "4b7cf40564849791a2910d0f", +"orders_model_20": "7f318b6e7ef3a655e3bd376b", +"orders_model_23": "e74e97ece725d7b78b1841a1", +"orders_model_5": "813668a42a542dc65d16d8d9", +"orders_model_8": "6ef4151966ba46cd7b4787fd", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "70e69b1adee7eb5488b1f3a6", +"pivot_1": "780cccafd942913ef74a86dc", +"pivot_2": "3023c184a01a785e2b99dd76", +"pivot_3": "98b8f3e8c523bf756d6a2bf7", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" +} +], +[ +"catalog", +"6b62b5735b9ed6e6e71ab2bb" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"20e6b6374d63663154468bed" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "ce5efbe45ba9958d07abe4be", +"orders_model_15": "71c3c40b94129b126dcdbb81", +"orders_model_18": "a0b539522577fbc223ae67b2", +"orders_model_21": "67ae6af409703b6ec6a4ba22", +"orders_model_3": "fd8ea928d0c948f37488b850", +"orders_model_6": "e64a458d11ef75ef9e32b2fb", +"orders_model_9": "699a9768ed5f28982916730d", +"orders_model_1": "2e657a2a732202f603d838cc", +"orders_model_10": "1657f9d386129215a3757337", +"orders_model_13": "0392e3abc9686a089a2269ed", +"orders_model_16": "cad99cc00c4b5d4cf42f7b64", +"orders_model_19": "31101e0e04f084d9c4956599", +"orders_model_22": "5c9a5664690fd035f64bcc1d", +"orders_model_4": "d99e41de1f2f5367123a86f9", +"orders_model_7": "1614a626eb417037485880e8", +"orders_model_11": "eb250514b8cec8a0c17de091", +"orders_model_14": "b100040bbda2543791356e9d", +"orders_model_17": "ebf05ef31b7e8ad3459595f4", +"orders_model_2": "a1a708f0e0e9cbf14f7ba3a7", +"orders_model_20": "ac25583f44012d99edee3be6", +"orders_model_23": "64f5c9e396d6cbf225ba7714", +"orders_model_5": "509da13380b8db206b5bbf72", +"orders_model_8": "656b6b3035654f5de79c039f", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "3023c184a01a785e2b99dd76", +"pivot_1": "9c1e23f4518b773b613dce93", +"pivot_2": "f557338abf3ddad3a6cb4336", +"pivot_3": "111edafe3adf02dec49c33d9", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" +} +], +[ +"catalog", +"6b62b5735b9ed6e6e71ab2bb" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"b9689124495085d917d65ebf" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"c3cb3226e55a418829fd059c" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "e4f6f6732e3ac678e83050d1", +"orders_model_15": "59a879b544632a1e9dbfccc8", +"orders_model_18": "4d3d2362cfabf1142b115ec8", +"orders_model_21": "9c5579c9b54079d1d76454f5", +"orders_model_3": "ac83e503763d54af353b88bd", +"orders_model_6": "ab2d50eea2c1144d8f9b5b91", +"orders_model_9": "8061cad6eea8e358bc36ea9f", +"orders_model_1": "6ed2f97a3a2b66b68ece8cfc", +"orders_model_10": "89c6a304cf5abdad93da59c9", +"orders_model_13": "9e177727feae83b2e543693a", +"orders_model_16": "70301685144d60d2e6460e2d", +"orders_model_19": "e0e5ba5c4bd951712a7cfb0c", +"orders_model_22": "ae1b61cb770b95ca99f3e37d", +"orders_model_4": "04360c72514e6a50e268ac56", +"orders_model_7": "ca188e5303c858aafc792993", +"orders_model_11": "28aa418cc8ed14a56873b90a", +"orders_model_14": "2185f7f47d0209d454123e0c", +"orders_model_17": "71757963de52c32334787883", +"orders_model_2": "580e8c893963f4f88a27bd44", +"orders_model_20": "56c4a9019347d421247f5767", +"orders_model_23": "6b428b0d6fd0d6a39babee02", +"orders_model_5": "cc7160489d8c1d3296517bae", +"orders_model_8": "14350982101ab47c588e4472", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "460dfe7ef04e1d6b458c104e", +"pivot_1": "f557338abf3ddad3a6cb4336", +"pivot_2": "31102e4a876501f9ff2fb5e9", +"pivot_3": "fa817a4878f55dc151440bb6", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" +} +], +[ +"catalog", +"6b62b5735b9ed6e6e71ab2bb" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"74234e98afe7498fb5daf1f3" +], +[ +"proof", +"0d91dfe8eefbbb36998db250" +], +[ +"proof", +"4994bb2a572acbed6354a2bb" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"15cac1a9d4a0c399d20eec68" +], +[ +"proof", +"28c524727756a048843237ef" +], +[ +"proof", +"99b1ccea692774bb61737217" +] +] diff --git a/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json b/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json index 3a75d3870..fb93c905b 100644 --- a/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json +++ b/tests/goldens/python_oracle/analysis_generated_rich_lineage_over_untyped_inputs_cte_facts_and_contracts.json @@ -6,43 +6,43 @@ [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "6f741725ae5781425358bc3b", -"orders_model_10": "c7e95ff7d90362e121ab238f", -"orders_model_11": "05479b1f55d5c854f9729a97", -"orders_model_12": "c380ae05f3bd0627628ab6fc", -"orders_model_13": "15537f706967682b808641f6", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", -"orders_model_16": "3dbaddb57a2ed5f8db44feda", -"orders_model_17": "ae6be31dbe60a1c0d17b30bc", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_19": "4782036882204544bf9b8cc0", -"orders_model_2": "2e71b399a66b78700dd48317", -"orders_model_20": "00d5932c856360eb282ca536", -"orders_model_21": "7a96226f82f52625a17f825f", -"orders_model_22": "74a4ef50a4b351777410e3ae", -"orders_model_23": "14b86bcb1a8edc065f4c7405", -"orders_model_3": "b1c72c923aea0b59f8745317", -"orders_model_4": "526ce0a1d0a274582f9d35f1", -"orders_model_5": "f9183611419f32c78a12fbc4", -"orders_model_6": "7a0c234309501030bdc80d53", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "2e035b59097bd4547b20a7ca", -"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "335dfa47b776524b62ee26e6", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "3767f4902ed5f3ab1acc83a1", -"pivot_3": "826e0ad9aec9b04097d58b37", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "4c1fd04cd9c309cbdf527da8", +"orders_model_15": "84591d33538539fa86025db9", +"orders_model_18": "aea7224659a833d20f777865", +"orders_model_21": "8061cad6eea8e358bc36ea9f", +"orders_model_3": "39ddff27a397d2edc7681f49", +"orders_model_6": "8ef746a82d7aa2a0923807c0", +"orders_model_9": "632ce5a31c3bae13f0f4a7e9", +"orders_model_1": "fb2a01d710b2f83776b14354", +"orders_model_10": "40be7e0d35ce48e94f7c8a80", +"orders_model_13": "157856cfd2af496e6ea0ea50", +"orders_model_16": "a6ea5be270f7a6c18c7000cf", +"orders_model_19": "1e0bb6e85de85db44da77c5a", +"orders_model_22": "43cf6c5727de2c16cbf69295", +"orders_model_4": "904fb7a9751e94dd87e783a8", +"orders_model_7": "849e77b8ea8ae735aff06dd8", +"orders_model_11": "f642fc0dca03bb5d7b50e972", +"orders_model_14": "4fecc090e99be8bb0842c172", +"orders_model_17": "c7480e9c22e83b4805e7773a", +"orders_model_2": "018be1a46a90b11d68dcf9cd", +"orders_model_20": "13e27987e4afb380b1744e12", +"orders_model_23": "f65bf576c4c56164a290a3ad", +"orders_model_5": "d291f5b458b8a3eb5f24e55c", +"orders_model_8": "713b06e49c2e0e60e556e009", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "111edafe3adf02dec49c33d9", +"pivot_1": "1b5af616e9ae9638438cff5c", +"pivot_2": "802f8187adaf13eca50630f3", +"pivot_3": "57067ca2b1293776e2d4ca98", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"7dae5e8f83fbe068e10fa8a1" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -180,29 +180,29 @@ "models", { "orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_15": "6fe127037935cdca6ceb3f6e", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_21": "7a96226f82f52625a17f825f", +"orders_model_3": "b1c72c923aea0b59f8745317", +"orders_model_6": "526c167cb476b3553de71042", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", "orders_model_1": "6f741725ae5781425358bc3b", "orders_model_10": "c7e95ff7d90362e121ab238f", -"orders_model_11": "05479b1f55d5c854f9729a97", -"orders_model_12": "c380ae05f3bd0627628ab6fc", "orders_model_13": "c7fa6f64bedfc1611fad26ba", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "6fe127037935cdca6ceb3f6e", "orders_model_16": "3dbaddb57a2ed5f8db44feda", -"orders_model_17": "7cc87d8c32df82057dc6057d", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", "orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_22": "74a4ef50a4b351777410e3ae", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_17": "7cc87d8c32df82057dc6057d", "orders_model_2": "27b686e0958c19d151cd6319", "orders_model_20": "13b36d543da620ba9a151138", -"orders_model_21": "7a96226f82f52625a17f825f", -"orders_model_22": "74a4ef50a4b351777410e3ae", "orders_model_23": "14b86bcb1a8edc065f4c7405", -"orders_model_3": "b1c72c923aea0b59f8745317", -"orders_model_4": "75f6764c319047022968f0b3", "orders_model_5": "f4c3ccd3c76f843ac4bced8a", -"orders_model_6": "526c167cb476b3553de71042", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", "orders_model_8": "55ea0527456ec442813d63d5", -"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", "payment_totals": "e75b3088ecccbf453f472518", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "335dfa47b776524b62ee26e6", @@ -352,43 +352,43 @@ [ "models", { -"orders_model_0": "541418fef292ad4a9c6f8dab", -"orders_model_1": "ff37aa9e31c0ddbc7f05cba2", -"orders_model_10": "315f07530cae631f3e373e9d", -"orders_model_11": "05479b1f55d5c854f9729a97", -"orders_model_12": "94f95487333b2561382c2981", -"orders_model_13": "f73e95ff8968d95ed11dc4f3", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", -"orders_model_16": "1a6c68f8be75e24bf3304cdb", -"orders_model_17": "ae6be31dbe60a1c0d17b30bc", -"orders_model_18": "79cac6cd1f2e605da47e460d", -"orders_model_19": "2d58b808566d04184cf9ab29", -"orders_model_2": "2e71b399a66b78700dd48317", -"orders_model_20": "13b36d543da620ba9a151138", -"orders_model_21": "c669dc21fed757d7809fc424", -"orders_model_22": "066b8f5bf2be63a888045fe7", -"orders_model_23": "14b86bcb1a8edc065f4c7405", -"orders_model_3": "b1c72c923aea0b59f8745317", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "f9183611419f32c78a12fbc4", -"orders_model_6": "f3d71446fe7a0576e2049a7a", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "55ea0527456ec442813d63d5", -"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "335dfa47b776524b62ee26e6", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "3767f4902ed5f3ab1acc83a1", -"pivot_3": "826e0ad9aec9b04097d58b37", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "4c1fd04cd9c309cbdf527da8", +"orders_model_15": "84591d33538539fa86025db9", +"orders_model_18": "a966d5cb9da6bf9bf87a7043", +"orders_model_21": "8061cad6eea8e358bc36ea9f", +"orders_model_3": "39ddff27a397d2edc7681f49", +"orders_model_6": "adb7fba2dee76ecb6f89c7ed", +"orders_model_9": "632ce5a31c3bae13f0f4a7e9", +"orders_model_1": "7fe52a2e56fd2728f1d917e1", +"orders_model_10": "40be7e0d35ce48e94f7c8a80", +"orders_model_13": "de8904a0b91b813475635afd", +"orders_model_16": "2adb1fa75191ad27a5e5aaa8", +"orders_model_19": "77f6d40b2301347436ce5deb", +"orders_model_22": "43cf6c5727de2c16cbf69295", +"orders_model_4": "948cc75006d0d6881dcd671b", +"orders_model_7": "28eff25ffabb34f29939d834", +"orders_model_11": "ba98da3a0550982e09af6345", +"orders_model_14": "d2975b92cd134a4ce1bb54f6", +"orders_model_17": "01ee43e0b40835cf94ec53f1", +"orders_model_2": "06e4ba9c2cd3f52747cf0c7c", +"orders_model_20": "2c582a63beeabc38339e22cf", +"orders_model_23": "8761351fa25ea985304bf968", +"orders_model_5": "c71516dc9ea50f798f2fc042", +"orders_model_8": "e31ffd5ea8ae8ebbe58bcfb3", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "8c525598d55de3fed0ddbbde", +"pivot_1": "0860ef89e9ce4066470edeac", +"pivot_2": "868863626dfd7f4708e819ed", +"pivot_3": "38ecf55ef23e6f922714c0b2", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"9f8ce1f318b9262fd1b830ff" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -526,29 +526,29 @@ "models", { "orders_model_0": "dc4da2070db56afde4157773", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_15": "26974efa98703503eb817f97", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_21": "9c26876fe71c235386932362", +"orders_model_3": "3ed083bce693c01d151c8081", +"orders_model_6": "a903f36871fea6d8ac818922", +"orders_model_9": "67dc0f1b5ce04c8989ad1aca", "orders_model_1": "9b0cc36921b5b8b8a8dea025", "orders_model_10": "d4f810ff3ba14295385d6fb9", -"orders_model_11": "f7cdb34524d45acad3b71f7d", -"orders_model_12": "c380ae05f3bd0627628ab6fc", "orders_model_13": "11f35c6f0b5918adfca2c40f", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "26974efa98703503eb817f97", "orders_model_16": "c5a645b8cfb02ddc927fc297", -"orders_model_17": "669b30fefd09621ed0f2ad55", -"orders_model_18": "60e19e6908bb5c06d38eeb01", "orders_model_19": "5318b049f2f651a689fb5e87", +"orders_model_22": "86880e31d61c6c5522256c62", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_7": "a4c400c5fa2da4107098339e", +"orders_model_11": "f7cdb34524d45acad3b71f7d", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_17": "669b30fefd09621ed0f2ad55", "orders_model_2": "361de61cfd22f09af946862f", "orders_model_20": "407c766e409ebec28d0c232a", -"orders_model_21": "9c26876fe71c235386932362", -"orders_model_22": "86880e31d61c6c5522256c62", "orders_model_23": "209048028aa76bf4c2bd28c6", -"orders_model_3": "3ed083bce693c01d151c8081", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", "orders_model_5": "a89cb10ab6dfef246cc36b07", -"orders_model_6": "a903f36871fea6d8ac818922", -"orders_model_7": "a4c400c5fa2da4107098339e", "orders_model_8": "5400ca61f1ed2f29d89e2d23", -"orders_model_9": "67dc0f1b5ce04c8989ad1aca", "payment_totals": "692528d52a5623a0e90d7fb4", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "335dfa47b776524b62ee26e6", @@ -570,43 +570,43 @@ [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "73d5e99497d80b919e0d5491", -"orders_model_10": "31ba43d1ed6f0701481c543b", -"orders_model_11": "e68784174562f0ac8afbc614", -"orders_model_12": "7c1f7afa32f56956c9bb5806", -"orders_model_13": "3676088fa7438b6033da7334", -"orders_model_14": "c5aa1f63d80b5d306477c290", -"orders_model_15": "31fb137256076ba0126683aa", -"orders_model_16": "3122402ab671f4438c8209d7", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "7ccdde571d7f688040f1f47c", -"orders_model_19": "f9be9d8e81fbd30196a2ba14", -"orders_model_2": "b9162cfbc1ce5044c01cca42", -"orders_model_20": "1bb9c3971b4488e0b7023645", -"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", -"orders_model_22": "5f2c3523ba8300fb944cc577", -"orders_model_23": "b760f8a3ed2b65af8ff70abc", -"orders_model_3": "f0f64c1baf69c4d7f0c75612", -"orders_model_4": "526ce0a1d0a274582f9d35f1", -"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", -"orders_model_6": "38187a959e7cbbef799b5777", -"orders_model_7": "20835381aab849fd35068525", -"orders_model_8": "72717ea55695667a7b7d8b39", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "db45532b4aa0746bb6155579", -"pivot_1": "826e0ad9aec9b04097d58b37", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "af151ba34f16c141fdee1ac3", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "7f4e57099dcd7d7c826a1720", +"orders_model_15": "2c80312c2723165af581e438", +"orders_model_18": "0cdaa5055afca4199842df67", +"orders_model_21": "632ce5a31c3bae13f0f4a7e9", +"orders_model_3": "c5d1f198c23a94d1c8c91b87", +"orders_model_6": "417212138029e32a8ed8cca2", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "e614f4493609a3173e1e2f26", +"orders_model_10": "5319ea0ee8d7d215ae85ea93", +"orders_model_13": "4e339dcb27e741a618eef93f", +"orders_model_16": "d04b6cec5e72d70e8fe73f16", +"orders_model_19": "849e77b8ea8ae735aff06dd8", +"orders_model_22": "b44879224a1bec6ebe6172b3", +"orders_model_4": "904fb7a9751e94dd87e783a8", +"orders_model_7": "e0e5ba5c4bd951712a7cfb0c", +"orders_model_11": "5c5bddb4083ebb82397a0656", +"orders_model_14": "da7384ddbf922aaef7f6f423", +"orders_model_17": "876e9e4f776962478c832387", +"orders_model_2": "ad4686c0757fff59d6a6e1dd", +"orders_model_20": "7ad13656ef279929394009ab", +"orders_model_23": "9965a7c90d3cbc180f48295c", +"orders_model_5": "c08a69fbca107fef0028e482", +"orders_model_8": "36ef9fbcf0c8b8874028e4e3", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "9c1e23f4518b773b613dce93", +"pivot_1": "57067ca2b1293776e2d4ca98", +"pivot_2": "1b5af616e9ae9638438cff5c", +"pivot_3": "3023c184a01a785e2b99dd76", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"c17fe05b252117c41defdc1e" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -744,29 +744,29 @@ "models", { "orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_12": "7c1f7afa32f56956c9bb5806", +"orders_model_15": "31fb137256076ba0126683aa", +"orders_model_18": "099cb926af524f10d27cc108", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_3": "f0f64c1baf69c4d7f0c75612", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_9": "d34e456ade2ced8792534866", "orders_model_1": "73d5e99497d80b919e0d5491", "orders_model_10": "31ba43d1ed6f0701481c543b", -"orders_model_11": "3efd8cbce60b5adde2346a56", -"orders_model_12": "7c1f7afa32f56956c9bb5806", "orders_model_13": "cc456ea2e83298c9cd8206fb", -"orders_model_14": "59968e869226ce12e62c6b5e", -"orders_model_15": "31fb137256076ba0126683aa", "orders_model_16": "a6740f24eddec216a1afc653", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "099cb926af524f10d27cc108", "orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_22": "5f2c3523ba8300fb944cc577", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_11": "3efd8cbce60b5adde2346a56", +"orders_model_14": "59968e869226ce12e62c6b5e", +"orders_model_17": "769e25e737cf7832171d8837", "orders_model_2": "3a3bfc6019ae1cf18c714dd0", "orders_model_20": "1bb9c3971b4488e0b7023645", -"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", -"orders_model_22": "5f2c3523ba8300fb944cc577", "orders_model_23": "4afbe759a7d6ab796db5ab1c", -"orders_model_3": "f0f64c1baf69c4d7f0c75612", -"orders_model_4": "75f6764c319047022968f0b3", "orders_model_5": "b0f2950e6d0c6c8ba60db1ca", -"orders_model_6": "38187a959e7cbbef799b5777", -"orders_model_7": "20835381aab849fd35068525", "orders_model_8": "63ebe9cbc63523f4c4adddb2", -"orders_model_9": "d34e456ade2ced8792534866", "payment_totals": "e75b3088ecccbf453f472518", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "db45532b4aa0746bb6155579", @@ -916,43 +916,43 @@ [ "models", { -"orders_model_0": "541418fef292ad4a9c6f8dab", -"orders_model_1": "828d747058993aeefdbae18a", -"orders_model_10": "9aefea09a1d167dbf49a2c69", -"orders_model_11": "e68784174562f0ac8afbc614", -"orders_model_12": "2cbf8e290082ab49db794db4", -"orders_model_13": "3676088fa7438b6033da7334", -"orders_model_14": "59968e869226ce12e62c6b5e", -"orders_model_15": "31fb137256076ba0126683aa", -"orders_model_16": "b67d418ac9424b16909d6a26", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "099cb926af524f10d27cc108", -"orders_model_19": "f9be9d8e81fbd30196a2ba14", -"orders_model_2": "b9162cfbc1ce5044c01cca42", -"orders_model_20": "1bb9c3971b4488e0b7023645", -"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", -"orders_model_22": "0b4ee9c2c4584613df0c4f3e", -"orders_model_23": "b760f8a3ed2b65af8ff70abc", -"orders_model_3": "f0f64c1baf69c4d7f0c75612", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", -"orders_model_6": "38187a959e7cbbef799b5777", -"orders_model_7": "20835381aab849fd35068525", -"orders_model_8": "72717ea55695667a7b7d8b39", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "db45532b4aa0746bb6155579", -"pivot_1": "826e0ad9aec9b04097d58b37", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "b2268fc29a60313e3b8c34ed", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "7f4e57099dcd7d7c826a1720", +"orders_model_15": "2c80312c2723165af581e438", +"orders_model_18": "0cdaa5055afca4199842df67", +"orders_model_21": "632ce5a31c3bae13f0f4a7e9", +"orders_model_3": "c5d1f198c23a94d1c8c91b87", +"orders_model_6": "417212138029e32a8ed8cca2", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "ede07cb8a81f9ce56f00f42f", +"orders_model_10": "5319ea0ee8d7d215ae85ea93", +"orders_model_13": "e12b2151a9cc473604b38078", +"orders_model_16": "5df128e8d57d789d3741b926", +"orders_model_19": "28eff25ffabb34f29939d834", +"orders_model_22": "b44879224a1bec6ebe6172b3", +"orders_model_4": "948cc75006d0d6881dcd671b", +"orders_model_7": "1c3f8e6db38452fbe711c134", +"orders_model_11": "9d420cd659ba93c596847354", +"orders_model_14": "9f70128e0f0e8caf9642b52f", +"orders_model_17": "85950947160d5a9ce51e5f8b", +"orders_model_2": "da63c044b64f206c8779a11a", +"orders_model_20": "7ad13656ef279929394009ab", +"orders_model_23": "8f696b8e7abb2c488b62f043", +"orders_model_5": "387b9e73946184ed49951e99", +"orders_model_8": "36ef9fbcf0c8b8874028e4e3", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "9c1e23f4518b773b613dce93", +"pivot_1": "38ecf55ef23e6f922714c0b2", +"pivot_2": "0860ef89e9ce4066470edeac", +"pivot_3": "06c892c6cba0e4f3ba822660", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"5f5c09917fe031a630ac277b" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1090,29 +1090,29 @@ "models", { "orders_model_0": "dc4da2070db56afde4157773", +"orders_model_12": "b06fcf5168d718f9042eb9f0", +"orders_model_15": "2a978f96fe42e545ae0e5f8e", +"orders_model_18": "8a322bf0beb93c0034bd28d5", +"orders_model_21": "67dc0f1b5ce04c8989ad1aca", +"orders_model_3": "cdd3d071e7283000e9b176dd", +"orders_model_6": "b8c7c62baf6528acb751f82c", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", "orders_model_1": "3e1948d706ff1cecf42f7863", "orders_model_10": "476ca953b1cb041d05ab9663", -"orders_model_11": "78eeedc510efd8cc89dfea31", -"orders_model_12": "b06fcf5168d718f9042eb9f0", "orders_model_13": "25952bed7c264fceaeec357c", -"orders_model_14": "c5aa1f63d80b5d306477c290", -"orders_model_15": "2a978f96fe42e545ae0e5f8e", "orders_model_16": "b67d418ac9424b16909d6a26", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "8a322bf0beb93c0034bd28d5", "orders_model_19": "a4c400c5fa2da4107098339e", +"orders_model_22": "c838c05a0db9b7bdb5a1e793", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", +"orders_model_11": "78eeedc510efd8cc89dfea31", +"orders_model_14": "c5aa1f63d80b5d306477c290", +"orders_model_17": "769e25e737cf7832171d8837", "orders_model_2": "2f584b5185d90ee4c66c8339", "orders_model_20": "1bb9c3971b4488e0b7023645", -"orders_model_21": "67dc0f1b5ce04c8989ad1aca", -"orders_model_22": "c838c05a0db9b7bdb5a1e793", "orders_model_23": "18c951cb594f929547ec6527", -"orders_model_3": "cdd3d071e7283000e9b176dd", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", "orders_model_5": "2f2cbf190f07bea2687a7a1e", -"orders_model_6": "b8c7c62baf6528acb751f82c", -"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", "orders_model_8": "63ebe9cbc63523f4c4adddb2", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", "payment_totals": "692528d52a5623a0e90d7fb4", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "db45532b4aa0746bb6155579", @@ -1134,43 +1134,43 @@ [ "models", { -"orders_model_0": "dc7f3dec778fd7cc3b47de47", -"orders_model_1": "98e73c916923e6ad3dbb4fe8", -"orders_model_10": "bba235f3ba5a071d31c89cfc", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "7dde13e57109cfb992bc623d", -"orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "6d1049bdcad88b007d5f111c", -"orders_model_15": "f31a90a0505705305536f618", -"orders_model_16": "bf62990d9480cf71956fd3c9", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "288bd78cd1025c86da017d6d", -"orders_model_19": "bc46ad8533a003d33c266a57", -"orders_model_2": "f165784d55122e5e5f859766", -"orders_model_20": "f2329324ed72baeb2911ce6a", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "9cc8cf8a102827df55622a21", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "d545cb7b8ca26b55d32439c0", -"orders_model_5": "c21e33abd058c0db4ed1aa0a", -"orders_model_6": "529410c5a69b04b7172c6dda", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "9a483c2d5942e8857be41233", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "94e60cd6b1a7fcc43f4df828", +"orders_model_15": "c79b92a8c39dafe29b9d3c1b", +"orders_model_18": "4a633de68d595f1c1db5b7ed", +"orders_model_21": "a676dde53da776de16f4fae9", +"orders_model_3": "e248bccac4fe7448d6ffefce", +"orders_model_6": "60f13cc7cb1efade26df391f", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "fc1488a09bf5a315c878509a", +"orders_model_10": "f785e3f7a7bf04d69fe258ff", +"orders_model_13": "060429a246a0568d696e2884", +"orders_model_16": "d99e41de1f2f5367123a86f9", +"orders_model_19": "583d29f78c901b46711eee3f", +"orders_model_22": "f035710c20d8ec38a6895234", +"orders_model_4": "d42726f7b0c3f0d38b8cbf29", +"orders_model_7": "7cb2666289d5475fb8e59816", +"orders_model_11": "fff956d158a47691e012109c", +"orders_model_14": "e0cd6596800c6d659bdb4632", +"orders_model_17": "d5658f434b0e634b215f460b", +"orders_model_2": "3329cf3b13f15869e552faf6", +"orders_model_20": "136b7aa7edf7c54843f89a27", +"orders_model_23": "9e899e72fafb086e86373175", +"orders_model_5": "a64dd7eb640e84a539b03b76", +"orders_model_8": "49aa23b1b8900497327ba778", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "3023c184a01a785e2b99dd76", +"pivot_2": "47dc75892e7ace4f0b7e4d11", +"pivot_3": "802f8187adaf13eca50630f3", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"b2b2221f2da5d47b86b56575" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1308,29 +1308,29 @@ "models", { "orders_model_0": "34986e60aa464378baaa7f25", +"orders_model_12": "7dde13e57109cfb992bc623d", +"orders_model_15": "f31a90a0505705305536f618", +"orders_model_18": "288bd78cd1025c86da017d6d", +"orders_model_21": "af5f39f23b7c2b58d3f12930", +"orders_model_3": "7faf6388dfebc22483c72417", +"orders_model_6": "529410c5a69b04b7172c6dda", +"orders_model_9": "0b71c12f30a7617191bc2e17", "orders_model_1": "98e73c916923e6ad3dbb4fe8", "orders_model_10": "9949660e9a41b3a11b1d646a", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "7dde13e57109cfb992bc623d", "orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "f31a90a0505705305536f618", "orders_model_16": "e931c288a0cc5a80e1d59ebe", -"orders_model_17": "4cb4a5fe4f98fcb58b9aa0a8", -"orders_model_18": "288bd78cd1025c86da017d6d", "orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_4": "d545cb7b8ca26b55d32439c0", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_17": "4cb4a5fe4f98fcb58b9aa0a8", "orders_model_2": "2c4a0d5f37d1033435dc44b8", "orders_model_20": "f2329324ed72baeb2911ce6a", -"orders_model_21": "af5f39f23b7c2b58d3f12930", -"orders_model_22": "9cc8cf8a102827df55622a21", "orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "7faf6388dfebc22483c72417", -"orders_model_4": "d545cb7b8ca26b55d32439c0", "orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "529410c5a69b04b7172c6dda", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", "orders_model_8": "e0e0e8448cdcfb7bbf411186", -"orders_model_9": "0b71c12f30a7617191bc2e17", "payment_totals": "e75b3088ecccbf453f472518", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -1480,43 +1480,43 @@ [ "models", { -"orders_model_0": "c0a4a49f398790b6968ded9b", -"orders_model_1": "f8df97502ce8987ade2b8ae6", -"orders_model_10": "d9e87a16e347f6f0316452d6", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "541418fef292ad4a9c6f8dab", -"orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "6d1049bdcad88b007d5f111c", -"orders_model_15": "f31a90a0505705305536f618", -"orders_model_16": "0eca13897e0abb4131837a39", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "fb0385ba59d9d254edc4f53d", -"orders_model_19": "bc46ad8533a003d33c266a57", -"orders_model_2": "f165784d55122e5e5f859766", -"orders_model_20": "f2329324ed72baeb2911ce6a", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "83e085c3265373904422aad9", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "a249acc516a880c9fbe25e88", -"orders_model_5": "c21e33abd058c0db4ed1aa0a", -"orders_model_6": "c55e2f85ad9de69066fc965c", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "ec0818249ce5c1a3abdbba12", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "b2268fc29a60313e3b8c34ed", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "94e60cd6b1a7fcc43f4df828", +"orders_model_15": "c79b92a8c39dafe29b9d3c1b", +"orders_model_18": "f1077a241bf5da058ecea8e2", +"orders_model_21": "a676dde53da776de16f4fae9", +"orders_model_3": "e248bccac4fe7448d6ffefce", +"orders_model_6": "e97a70a6d29697dc7a5bb026", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "8c55008327247d32fc23abdc", +"orders_model_10": "f785e3f7a7bf04d69fe258ff", +"orders_model_13": "8af3805074512d064fb865a5", +"orders_model_16": "f4e41df89fd84a43e52ce29a", +"orders_model_19": "ae659831ce6aa9820e6e8a9d", +"orders_model_22": "f035710c20d8ec38a6895234", +"orders_model_4": "34b0a9652b09c53e5aa314b0", +"orders_model_7": "babbbcc1ecf5c297157a9cd5", +"orders_model_11": "62cfce559364ea76891d3353", +"orders_model_14": "bb6b56bc63c9f77c726d9626", +"orders_model_17": "ebe63fadd57b7d3b95afb29b", +"orders_model_2": "9c31136ccdc1bdd3517206e6", +"orders_model_20": "136b7aa7edf7c54843f89a27", +"orders_model_23": "bd51523ed414294d0d2b1d32", +"orders_model_5": "5f23178435c57cdd114faec7", +"orders_model_8": "9463c8cf10ef90862accbdfb", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "06c892c6cba0e4f3ba822660", +"pivot_2": "d1bbd23f4694ce0d97f5c165", +"pivot_3": "868863626dfd7f4708e819ed", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"0675d0c35333ce73c1709f47" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1654,87 +1654,87 @@ "models", { "orders_model_0": "279aba0c75055164f61b65f8", +"orders_model_12": "dc4da2070db56afde4157773", +"orders_model_15": "18cc8b92ab2adfb7e7e160a3", +"orders_model_18": "8cf38d3ff37cd8143561920a", +"orders_model_21": "af51dc83785211c066cbb51f", +"orders_model_3": "d43b073d40eb97e87277c137", +"orders_model_6": "0541825c1a3bbc51c23f3a53", +"orders_model_9": "0b71c12f30a7617191bc2e17", "orders_model_1": "45de3ae2081edb1155411120", "orders_model_10": "ce0bc9f834e9623dddad0b3a", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "dc4da2070db56afde4157773", "orders_model_13": "87a319b5caf7cc32bc6aa50e", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "18cc8b92ab2adfb7e7e160a3", "orders_model_16": "28610c39619a490a015886ec", -"orders_model_17": "664cb83e35a8ecea0758a450", -"orders_model_18": "8cf38d3ff37cd8143561920a", "orders_model_19": "dcb9d89d4b588b53682fb0b4", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_7": "526a9e7adadbf085a39764b5", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_17": "664cb83e35a8ecea0758a450", "orders_model_2": "2c4a0d5f37d1033435dc44b8", "orders_model_20": "f2329324ed72baeb2911ce6a", -"orders_model_21": "af51dc83785211c066cbb51f", -"orders_model_22": "9cc8cf8a102827df55622a21", "orders_model_23": "857c9a58882bddd68cec3bd5", -"orders_model_3": "d43b073d40eb97e87277c137", -"orders_model_4": "a249acc516a880c9fbe25e88", "orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "0541825c1a3bbc51c23f3a53", -"orders_model_7": "526a9e7adadbf085a39764b5", "orders_model_8": "fcc5ac474974dee716908c65", -"orders_model_9": "0b71c12f30a7617191bc2e17", "payment_totals": "692528d52a5623a0e90d7fb4", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "0b5af099963f99173031a52f", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "26ba5a13355d37dfa8e67820" -} -], -[ -"catalog", -"7f3cc4630798c6d611e9c144" -], -[ -"shapes", -"78e5cd70ce361d823798634b" -], -[ -"models", -{ -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "d7498e8ac1b0afeb189ff727", -"orders_model_10": "60ab36ff1481d98f182141fc", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "dc7f3dec778fd7cc3b47de47", -"orders_model_13": "35b1097a860faac43e81b044", -"orders_model_14": "28b94fa5aaa6225fd8809740", -"orders_model_15": "1a083ad888e035969bd5f5fe", -"orders_model_16": "061d35dfeab6243e15890c04", -"orders_model_17": "458d9725be39144ca5e0c33d", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", -"orders_model_2": "959e49dd41f7bcc7e9edde19", -"orders_model_20": "40a0a6e57912d17b00b26274", -"orders_model_21": "fbd144b873208a2bd80a35a4", -"orders_model_22": "4e068f2ba3ce583e4d5b6006", -"orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "e1986e4dea1c1d381bf078ff", -"orders_model_4": "526ce0a1d0a274582f9d35f1", -"orders_model_5": "458d9725be39144ca5e0c33d", -"orders_model_6": "d236f592f3b7c9328bb402ea", -"orders_model_7": "ef0eb5ded0428f49ac6cb411", -"orders_model_8": "19280031a53ac7db42aa7136", -"orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "bd6145283beb468ce86b460b", -"pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "af151ba34f16c141fdee1ac3", +"pivot_1": "0b5af099963f99173031a52f", +"pivot_2": "5cf2515c32c9c98b0a9d8315", +"pivot_3": "3767f4902ed5f3ab1acc83a1", "status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"status_passthrough": "26ba5a13355d37dfa8e67820" +} +], +[ +"catalog", +"7f3cc4630798c6d611e9c144" +], +[ +"shapes", +"78e5cd70ce361d823798634b" +], +[ +"models", +{ +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "b02026cdc6a38ff5f06fe2a9", +"orders_model_15": "ef6ebc916ab6d71b7525d23f", +"orders_model_18": "aea7224659a833d20f777865", +"orders_model_21": "496ef0c368c89fe73a7b69ff", +"orders_model_3": "0d11445c74305b9100560c9c", +"orders_model_6": "e64a458d11ef75ef9e32b2fb", +"orders_model_9": "0a80765a57a9744602000dff", +"orders_model_1": "c5b54699c7681cdeeebe6d97", +"orders_model_10": "554ba2fe9ec0480f2c848f60", +"orders_model_13": "7bac87596d1672636fbfc359", +"orders_model_16": "106aadd0ff457f2f228fec77", +"orders_model_19": "a10d88c3055fd182742c409e", +"orders_model_22": "93f305f8baf363528a9b1f5f", +"orders_model_4": "904fb7a9751e94dd87e783a8", +"orders_model_7": "1931ea05cdcf5fb03a08a45a", +"orders_model_11": "fff956d158a47691e012109c", +"orders_model_14": "cc0aba8ad07238f59b805db1", +"orders_model_17": "c289af2f3331d5c54ff0dfb1", +"orders_model_2": "e1603185bab5288bd9cad27b", +"orders_model_20": "6b41843732caf5f5b95f0579", +"orders_model_23": "df13d253f68d3d484b06fcb8", +"orders_model_5": "c289af2f3331d5c54ff0dfb1", +"orders_model_8": "2bf96e35794854fa861c4c7b", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "5fb7483692a07a7a31a2ef9b", +"pivot_1": "712ce9a95b98693096395334", +"pivot_2": "70e69b1adee7eb5488b1f3a6", +"pivot_3": "3023c184a01a785e2b99dd76", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"a749305bcf58d93756b4c9de" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1872,29 +1872,29 @@ "models", { "orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_12": "34986e60aa464378baaa7f25", +"orders_model_15": "1a083ad888e035969bd5f5fe", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_3": "e1986e4dea1c1d381bf078ff", +"orders_model_6": "36c156c5a6601badf8124464", +"orders_model_9": "338aef528aa5f199e2ffa973", "orders_model_1": "d7498e8ac1b0afeb189ff727", "orders_model_10": "60ab36ff1481d98f182141fc", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "34986e60aa464378baaa7f25", "orders_model_13": "35b1097a860faac43e81b044", -"orders_model_14": "28b94fa5aaa6225fd8809740", -"orders_model_15": "1a083ad888e035969bd5f5fe", "orders_model_16": "061d35dfeab6243e15890c04", -"orders_model_17": "458d9725be39144ca5e0c33d", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", "orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_7": "80a1f1b7b38c1afa802d7625", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_17": "458d9725be39144ca5e0c33d", "orders_model_2": "3c07dff3140b081ee33fd13e", "orders_model_20": "40a0a6e57912d17b00b26274", -"orders_model_21": "fbd144b873208a2bd80a35a4", -"orders_model_22": "4e068f2ba3ce583e4d5b6006", "orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "e1986e4dea1c1d381bf078ff", -"orders_model_4": "75f6764c319047022968f0b3", "orders_model_5": "458d9725be39144ca5e0c33d", -"orders_model_6": "36c156c5a6601badf8124464", -"orders_model_7": "80a1f1b7b38c1afa802d7625", "orders_model_8": "19280031a53ac7db42aa7136", -"orders_model_9": "338aef528aa5f199e2ffa973", "payment_totals": "e75b3088ecccbf453f472518", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "bd6145283beb468ce86b460b", @@ -2044,43 +2044,43 @@ [ "models", { -"orders_model_0": "541418fef292ad4a9c6f8dab", -"orders_model_1": "673e75c341c3f2437091de09", -"orders_model_10": "8314d3a80b3a31f5a7b3b87c", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "c0a4a49f398790b6968ded9b", -"orders_model_13": "bc666dca3ca9016f640dfe68", -"orders_model_14": "7c3ab71786810446d45e97a3", -"orders_model_15": "1a083ad888e035969bd5f5fe", -"orders_model_16": "39b572633eb377051c189c82", -"orders_model_17": "7a9fabf5b3d3ab9e1fc63a99", -"orders_model_18": "79cac6cd1f2e605da47e460d", -"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", -"orders_model_2": "959e49dd41f7bcc7e9edde19", -"orders_model_20": "40a0a6e57912d17b00b26274", -"orders_model_21": "59228560ec9692b57930d8b5", -"orders_model_22": "b8238ed1e26320d8f8912882", -"orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "e1986e4dea1c1d381bf078ff", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "7a9fabf5b3d3ab9e1fc63a99", -"orders_model_6": "748592fef86e52c625afd1e0", -"orders_model_7": "ef0eb5ded0428f49ac6cb411", -"orders_model_8": "19280031a53ac7db42aa7136", -"orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "bd6145283beb468ce86b460b", -"pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "b2268fc29a60313e3b8c34ed", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "b02026cdc6a38ff5f06fe2a9", +"orders_model_15": "ef6ebc916ab6d71b7525d23f", +"orders_model_18": "a966d5cb9da6bf9bf87a7043", +"orders_model_21": "496ef0c368c89fe73a7b69ff", +"orders_model_3": "0d11445c74305b9100560c9c", +"orders_model_6": "3bc5580c26c91e8c5a2c7ed7", +"orders_model_9": "0a80765a57a9744602000dff", +"orders_model_1": "050954c57ddb4444f9cc1103", +"orders_model_10": "a2617416d1e573e91ad6d556", +"orders_model_13": "91de85a50b3e7d95285e9ef5", +"orders_model_16": "7c196cbcfd446eaf0650186e", +"orders_model_19": "42554e8420c2bd13558da43e", +"orders_model_22": "f0152946381d8c2aaf939503", +"orders_model_4": "948cc75006d0d6881dcd671b", +"orders_model_7": "44dc5de3912695d88a818a03", +"orders_model_11": "62cfce559364ea76891d3353", +"orders_model_14": "a162dc6cd78d2f8ce0da45bb", +"orders_model_17": "cec4287287b33c590d00a0bb", +"orders_model_2": "af07bae49ecbfb94bebce4ef", +"orders_model_20": "715261ceed0a226d62b43387", +"orders_model_23": "3be8631ce21962bab983795c", +"orders_model_5": "cec4287287b33c590d00a0bb", +"orders_model_8": "6c91b6c3227c14ab77f77f0e", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "b630f6bbcf705fcab4076ffb", +"pivot_1": "f76cad7a83d99f2c3ad7754f", +"pivot_2": "4cd97646890bc4d292590af0", +"pivot_3": "06c892c6cba0e4f3ba822660", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"988cc4307f8cdbfd7c4e95f9" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -2218,29 +2218,29 @@ "models", { "orders_model_0": "dc4da2070db56afde4157773", +"orders_model_12": "279aba0c75055164f61b65f8", +"orders_model_15": "e643f7ef7c9f860822ad8ba7", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_21": "176a688a47e60b55e1ce0673", +"orders_model_3": "f22120166b4143e8fd193e3d", +"orders_model_6": "c2b91215ea727d9313a22740", +"orders_model_9": "283c165f2da9102ffec377c4", "orders_model_1": "b6345243b4bb0009ec2dfb5d", "orders_model_10": "408cbd03f775698d2714eb62", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "279aba0c75055164f61b65f8", "orders_model_13": "87a1dbf609e37b67c90e35b2", -"orders_model_14": "544d358c786985653996181d", -"orders_model_15": "e643f7ef7c9f860822ad8ba7", "orders_model_16": "2ee8d0a917d274c8d55f4370", -"orders_model_17": "bac0193c7d43a68153ce544e", -"orders_model_18": "60e19e6908bb5c06d38eeb01", "orders_model_19": "ab249088d32d341a013be687", +"orders_model_22": "c6255585e2f5156964d8f8f8", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_7": "afb5b3102eaac7fa202355c0", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_14": "544d358c786985653996181d", +"orders_model_17": "bac0193c7d43a68153ce544e", "orders_model_2": "3c07dff3140b081ee33fd13e", "orders_model_20": "40a0a6e57912d17b00b26274", -"orders_model_21": "176a688a47e60b55e1ce0673", -"orders_model_22": "c6255585e2f5156964d8f8f8", "orders_model_23": "e0ec1521e19c47996421ec94", -"orders_model_3": "f22120166b4143e8fd193e3d", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", "orders_model_5": "bac0193c7d43a68153ce544e", -"orders_model_6": "c2b91215ea727d9313a22740", -"orders_model_7": "afb5b3102eaac7fa202355c0", "orders_model_8": "19280031a53ac7db42aa7136", -"orders_model_9": "283c165f2da9102ffec377c4", "payment_totals": "692528d52a5623a0e90d7fb4", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "bd6145283beb468ce86b460b", @@ -2262,43 +2262,43 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "abadf69a0572399b6536a2c1", -"orders_model_10": "c46ffece29c5df800b8f74df", -"orders_model_11": "be02726b764e7cc94f5df56c", -"orders_model_12": "edf57c58d750226a747455ba", -"orders_model_13": "278d4fd453aea2eca14894ea", -"orders_model_14": "0759e673a754006cbe1427de", -"orders_model_15": "5e266036ddde9cb398819134", -"orders_model_16": "eeaefdbd50fdc13681d8653a", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "dd2512e1e685426785444ae5", -"orders_model_19": "5ab5711e085cc014f3e44950", -"orders_model_2": "b1cffebb5797fe684c20e885", -"orders_model_20": "9b30de5669877213eebaf0e5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "91d92b539582dec7737d1704", -"orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "e29709e8e7acb44cc782304f", -"orders_model_5": "afd6d68f0a0a6689404e0260", -"orders_model_6": "7ccdde571d7f688040f1f47c", -"orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "2307138f76b5c3d80ec84a7a", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "6792391ba81748e33b725b02", +"orders_model_18": "ab2d50eea2c1144d8f9b5b91", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "0cdaa5055afca4199842df67", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "a01f8e4ec7b620ce17dfeb9a", +"orders_model_10": "43cd3fa513c204d3a57fa901", +"orders_model_13": "d6da1af6620de8c0ed4518e5", +"orders_model_16": "d40501d996ca880cd8f3bad0", +"orders_model_19": "6e2108808bec1c8e26780f24", +"orders_model_22": "d31e6dac331e1b53f7b0c1a5", +"orders_model_4": "cad99cc00c4b5d4cf42f7b64", +"orders_model_7": "68b2a84748bad7d3a4c3f42b", +"orders_model_11": "930d3b19fc9c4090e18bb8aa", +"orders_model_14": "d05c196335877deb6b2eb46e", +"orders_model_17": "32e11315a2d0ae43b0f30c56", +"orders_model_2": "a1a708f0e0e9cbf14f7ba3a7", +"orders_model_20": "425a516cf49ced70f18550bf", +"orders_model_23": "ff895a8a83c9e758fbc37bf5", +"orders_model_5": "8c62f0737cfba7e68b86ae43", +"orders_model_8": "e0b9f79671eccfa779e360be", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "3023c184a01a785e2b99dd76", +"pivot_2": "4201810ec2a2caba872d3cea", +"pivot_3": "e836d6def127c9a6d051ebda", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"893ba59566e53ebc6aaa2cdc" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -2436,29 +2436,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "a030043d592af380cf44cb9f", +"orders_model_15": "5e266036ddde9cb398819134", +"orders_model_18": "f73c7edc9f35cb7956a40b52", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_3": "e03d256f9a62a63d98fad738", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_9": "d34e456ade2ced8792534866", "orders_model_1": "7f4d684fd731d8fd8e517a52", "orders_model_10": "c46ffece29c5df800b8f74df", -"orders_model_11": "8003e1680dc046032ceea404", -"orders_model_12": "a030043d592af380cf44cb9f", "orders_model_13": "56d09679844a976541cce33c", -"orders_model_14": "e51070f44666248f5b7764b3", -"orders_model_15": "5e266036ddde9cb398819134", "orders_model_16": "eeaefdbd50fdc13681d8653a", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "f73c7edc9f35cb7956a40b52", "orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_22": "e780cb2018f0f8f501e8d2fe", +"orders_model_4": "9a1f8dc30a3cfc10bba60a45", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_17": "3515712bb3c936c534706554", "orders_model_2": "835e887650bbf9b124ae26b1", "orders_model_20": "9b30de5669877213eebaf0e5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "e780cb2018f0f8f501e8d2fe", "orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "e03d256f9a62a63d98fad738", -"orders_model_4": "9a1f8dc30a3cfc10bba60a45", "orders_model_5": "afd6d68f0a0a6689404e0260", -"orders_model_6": "099cb926af524f10d27cc108", -"orders_model_7": "a36493d57a16c1f79a91afd6", "orders_model_8": "e7532f3dad5c3e5750265e5b", -"orders_model_9": "d34e456ade2ced8792534866", "payment_totals": "e75b3088ecccbf453f472518", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -2608,43 +2608,43 @@ [ "models", { -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "1ad8f3013a29b4f16f037b39", -"orders_model_10": "3aec7a059badfa0e968d5216", -"orders_model_11": "be02726b764e7cc94f5df56c", -"orders_model_12": "cb6d3e8487b533349a2bad04", -"orders_model_13": "d55e6b7bb6533cea41bcfeda", -"orders_model_14": "0759e673a754006cbe1427de", -"orders_model_15": "5e266036ddde9cb398819134", -"orders_model_16": "97e322d50d70c30c61fb7a15", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "326d8bbf3c85b38aafb08a98", -"orders_model_19": "5ab5711e085cc014f3e44950", -"orders_model_2": "b1cffebb5797fe684c20e885", -"orders_model_20": "9b30de5669877213eebaf0e5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "368c97dc5eea624b492ce042", -"orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "3af96276ee92b913506618b2", -"orders_model_5": "ee556ea5f790f1d885cee84a", -"orders_model_6": "099cb926af524f10d27cc108", -"orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "ed49fa288ea02d542c88ec94", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "b2268fc29a60313e3b8c34ed", -"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "6792391ba81748e33b725b02", +"orders_model_18": "ff113261ecb1302e1768b19e", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "0cdaa5055afca4199842df67", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "4f8e1dc514d29636e414c5d8", +"orders_model_10": "43cd3fa513c204d3a57fa901", +"orders_model_13": "3cd5e90f96a677b770ecfc3b", +"orders_model_16": "d429b5a2405839eec25c811c", +"orders_model_19": "9e0f3ba46f59d75ad544727a", +"orders_model_22": "3e050ff8096d65ad3bcf9c09", +"orders_model_4": "e776c54bbf64f9efbcffb86a", +"orders_model_7": "1baf594884e8b799007edf2c", +"orders_model_11": "3c52b383636f30ce044a39e6", +"orders_model_14": "d0bf04d1c170cf049059b503", +"orders_model_17": "e73f3fa095e925032a0f493c", +"orders_model_2": "9317eb65f36a14dd5181ef82", +"orders_model_20": "425a516cf49ced70f18550bf", +"orders_model_23": "9b5a54a902e9acadf76a4194", +"orders_model_5": "491a1c5bb2ab02a6131a515b", +"orders_model_8": "e705d9189173555c5e6902ea", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "06c892c6cba0e4f3ba822660", +"pivot_2": "d8cb46c8edcfe87f37163643", +"pivot_3": "8cbdd393ed22a6eee2bfc26a", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"bb15ca51be7f1375a5b43886" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -2782,29 +2782,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "4054517533ad5199a1077065", +"orders_model_15": "90310178ea5674f6669a72f4", +"orders_model_18": "fd5138ac4af02cd168a423e6", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_3": "72f0c384f9c169084cf65392", +"orders_model_6": "8a322bf0beb93c0034bd28d5", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", "orders_model_1": "1070e59d687e0fed0e44b651", "orders_model_10": "fa26ab8510354dd670525ac2", -"orders_model_11": "eb7d097be111399bcd2f940f", -"orders_model_12": "4054517533ad5199a1077065", "orders_model_13": "ab1f9c5b6ef07ffa40eeadaf", -"orders_model_14": "273c339ab90e9a64a7c8f8a5", -"orders_model_15": "90310178ea5674f6669a72f4", "orders_model_16": "e018db5f6e3eaf8888bd8eff", -"orders_model_17": "fe03c6a89b870943b6dd5922", -"orders_model_18": "fd5138ac4af02cd168a423e6", "orders_model_19": "e24f113150b49c7e955cf81b", +"orders_model_22": "56f27ec8513b46291154e8cd", +"orders_model_4": "39a96779a764101f7d1dbae2", +"orders_model_7": "4983a5e9302c8b9e28e75202", +"orders_model_11": "eb7d097be111399bcd2f940f", +"orders_model_14": "273c339ab90e9a64a7c8f8a5", +"orders_model_17": "fe03c6a89b870943b6dd5922", "orders_model_2": "daca571af4a4d1cb1afbce74", "orders_model_20": "9b30de5669877213eebaf0e5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "56f27ec8513b46291154e8cd", "orders_model_23": "e94cdbeab01617cd05a55f82", -"orders_model_3": "72f0c384f9c169084cf65392", -"orders_model_4": "39a96779a764101f7d1dbae2", "orders_model_5": "7af136b7e522ccf14f427e1e", -"orders_model_6": "8a322bf0beb93c0034bd28d5", -"orders_model_7": "4983a5e9302c8b9e28e75202", "orders_model_8": "e7532f3dad5c3e5750265e5b", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", "payment_totals": "692528d52a5623a0e90d7fb4", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -2826,43 +2826,43 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "c909142a40bfa94a0849e672", -"orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "5f4d57a90f61f48a39c3f45c", -"orders_model_12": "159cce49ef0548e95a5b46fe", -"orders_model_13": "ba7bafc812a816c0c0770d82", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", -"orders_model_16": "e29709e8e7acb44cc782304f", -"orders_model_17": "40ca546bd222f4bd18e62461", -"orders_model_18": "2bcb5417ea2e03971c367fbd", -"orders_model_19": "16fc9c0348ca5eadfd147e95", -"orders_model_2": "be51e7ded79e692b0f86ad8e", -"orders_model_20": "0c798fa3e6d807bf94e96eee", -"orders_model_21": "32e2f46b53e4361d34fb2f95", -"orders_model_22": "ca95b1a1313ab6e0c86f22d0", -"orders_model_23": "6423a33f3457191fa1b24540", -"orders_model_3": "52f7f68c35fc1813f86aee3b", -"orders_model_4": "40daca9947aaa0922736d9ca", -"orders_model_5": "efc04632c3bd90f5ca705b61", -"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "ce5efbe45ba9958d07abe4be", +"orders_model_15": "b2fd9a5a8fcc2fd4b501594e", +"orders_model_18": "bcaa1a8b281edaf085e71f99", +"orders_model_21": "8b48e953b842468f8a4b7330", +"orders_model_3": "da6fb6ea5f7e62f158a9171d", +"orders_model_6": "aea7224659a833d20f777865", +"orders_model_9": "4c22b437652fb7f416ad4417", +"orders_model_1": "ca9699ee8d950158abf9275c", +"orders_model_10": "55dfdc7e25361c194ca7a2b2", +"orders_model_13": "072997a4b27ecdc471698510", +"orders_model_16": "cad99cc00c4b5d4cf42f7b64", +"orders_model_19": "ca188e5303c858aafc792993", +"orders_model_22": "d50d6963b3d3c9908843697b", +"orders_model_4": "bb70da9e97096f272dd0edeb", +"orders_model_7": "eb9a44efb89bd577daf4b9bb", +"orders_model_11": "884b5e4619894a1f2dca4e75", +"orders_model_14": "08c324156e8fbdd6ee5b8c34", +"orders_model_17": "77cae24a023f421e79bd8d6b", +"orders_model_2": "8650e077152ed53580daab6e", +"orders_model_20": "b414f89d27f5b1ec36d9bfa1", +"orders_model_23": "706bf82e63aa10076edfd1c5", +"orders_model_5": "db3f24143f0d658fa42a0e8c", "orders_model_8": "653dbee5db7422883147ecf8", -"orders_model_9": "e2d21c84b2b62dea98a1d185", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "af151ba34f16c141fdee1ac3", -"pivot_1": "bd6145283beb468ce86b460b", -"pivot_2": "db45532b4aa0746bb6155579", -"pivot_3": "5cf2515c32c9c98b0a9d8315", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "3023c184a01a785e2b99dd76", +"pivot_1": "5fb7483692a07a7a31a2ef9b", +"pivot_2": "9c1e23f4518b773b613dce93", +"pivot_3": "47dc75892e7ace4f0b7e4d11", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"f877c81e569d46b2f31adb8a" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -3000,29 +3000,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "159cce49ef0548e95a5b46fe", +"orders_model_15": "fcf8d3dfd18bb30aef04a7e0", +"orders_model_18": "f913dcd698fd61a9df4580a9", +"orders_model_21": "32e2f46b53e4361d34fb2f95", +"orders_model_3": "52f7f68c35fc1813f86aee3b", +"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_9": "e2d21c84b2b62dea98a1d185", "orders_model_1": "c909142a40bfa94a0849e672", "orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "5f4d57a90f61f48a39c3f45c", -"orders_model_12": "159cce49ef0548e95a5b46fe", "orders_model_13": "b929a7472b014a9b5b757127", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "fcf8d3dfd18bb30aef04a7e0", "orders_model_16": "9a1f8dc30a3cfc10bba60a45", -"orders_model_17": "40ca546bd222f4bd18e62461", -"orders_model_18": "f913dcd698fd61a9df4580a9", "orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_22": "ca95b1a1313ab6e0c86f22d0", +"orders_model_4": "40daca9947aaa0922736d9ca", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_17": "40ca546bd222f4bd18e62461", "orders_model_2": "be51e7ded79e692b0f86ad8e", "orders_model_20": "0c798fa3e6d807bf94e96eee", -"orders_model_21": "32e2f46b53e4361d34fb2f95", -"orders_model_22": "ca95b1a1313ab6e0c86f22d0", "orders_model_23": "6423a33f3457191fa1b24540", -"orders_model_3": "52f7f68c35fc1813f86aee3b", -"orders_model_4": "40daca9947aaa0922736d9ca", "orders_model_5": "efc04632c3bd90f5ca705b61", -"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_7": "f1d838e10071f9b219bf07c6", "orders_model_8": "588e1b85c85669d7d2f2874a", -"orders_model_9": "e2d21c84b2b62dea98a1d185", "payment_totals": "e75b3088ecccbf453f472518", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "e6d82b48a7a5593bd747ea69", @@ -3172,43 +3172,43 @@ [ "models", { -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "7ebef6bac8d0a4ff15fca702", -"orders_model_10": "07a1b43e2e1aec3a941c7463", -"orders_model_11": "5f4d57a90f61f48a39c3f45c", -"orders_model_12": "f28ca9cbe4f0b82139ce42ff", -"orders_model_13": "8bfac0eb02357c4ef7316ba0", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", -"orders_model_16": "3af96276ee92b913506618b2", -"orders_model_17": "185a865c8cf0d67b3027a313", -"orders_model_18": "f913dcd698fd61a9df4580a9", -"orders_model_19": "16fc9c0348ca5eadfd147e95", -"orders_model_2": "be51e7ded79e692b0f86ad8e", -"orders_model_20": "0c798fa3e6d807bf94e96eee", -"orders_model_21": "88425997d70bb43627dba42c", -"orders_model_22": "083e6ee404f8943487416adb", -"orders_model_23": "6423a33f3457191fa1b24540", -"orders_model_3": "52f7f68c35fc1813f86aee3b", -"orders_model_4": "2cfd6426be9711fc3a185538", -"orders_model_5": "1e772c63e569addeecf15a27", -"orders_model_6": "79cac6cd1f2e605da47e460d", -"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "ce5efbe45ba9958d07abe4be", +"orders_model_15": "b2fd9a5a8fcc2fd4b501594e", +"orders_model_18": "bcaa1a8b281edaf085e71f99", +"orders_model_21": "8b48e953b842468f8a4b7330", +"orders_model_3": "da6fb6ea5f7e62f158a9171d", +"orders_model_6": "a966d5cb9da6bf9bf87a7043", +"orders_model_9": "4c22b437652fb7f416ad4417", +"orders_model_1": "6fb5cb3c2a3da8e849f3c215", +"orders_model_10": "3e279fbf4d2f8b6bb22573c9", +"orders_model_13": "18224653a131c203f5850667", +"orders_model_16": "e776c54bbf64f9efbcffb86a", +"orders_model_19": "cca068c36904ee6a40522cb7", +"orders_model_22": "d50d6963b3d3c9908843697b", +"orders_model_4": "696923119a29a646a62f106a", +"orders_model_7": "0d946e5630e41164201fa20e", +"orders_model_11": "12274bd6b0ca24efa5f74e94", +"orders_model_14": "56417b56a4e7a0b8b49d6f8f", +"orders_model_17": "1943f6b997fb167a34c3cdbb", +"orders_model_2": "fc5c5953ac3044bb535d6835", +"orders_model_20": "b414f89d27f5b1ec36d9bfa1", +"orders_model_23": "2d01394da7a6e9d7b150b211", +"orders_model_5": "20983af890f0296ff103a3cf", "orders_model_8": "653dbee5db7422883147ecf8", -"orders_model_9": "982d87b5a15b54ee70c70755", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "b2268fc29a60313e3b8c34ed", -"pivot_1": "bd6145283beb468ce86b460b", -"pivot_2": "db45532b4aa0746bb6155579", -"pivot_3": "5cf2515c32c9c98b0a9d8315", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "06c892c6cba0e4f3ba822660", +"pivot_1": "b630f6bbcf705fcab4076ffb", +"pivot_2": "9c1e23f4518b773b613dce93", +"pivot_3": "d1bbd23f4694ce0d97f5c165", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"3eb464018675acfdd12a461f" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -3346,29 +3346,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "31d217ac4f1c012c19d14c40", +"orders_model_15": "d1fb37ffa36108edc2a01aba", +"orders_model_18": "02d5138c4e7513182fc7cd52", +"orders_model_21": "08e7a885c1d9662b2678c3ea", +"orders_model_3": "85fda65b19f7375e90342fa9", +"orders_model_6": "60e19e6908bb5c06d38eeb01", +"orders_model_9": "05bfc38d1bf1cc771029edec", "orders_model_1": "18de106324e6bd649e9a0662", "orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "68b962a61fcbd39f299bf7b9", -"orders_model_12": "31d217ac4f1c012c19d14c40", "orders_model_13": "1d827eced23366a37ddba6b8", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "d1fb37ffa36108edc2a01aba", "orders_model_16": "39a96779a764101f7d1dbae2", -"orders_model_17": "185a865c8cf0d67b3027a313", -"orders_model_18": "02d5138c4e7513182fc7cd52", "orders_model_19": "3f36107952adc3d06995d9db", +"orders_model_22": "1b893dcaf72edc288a3b9710", +"orders_model_4": "2cfd6426be9711fc3a185538", +"orders_model_7": "07289e08df177aa672b5da80", +"orders_model_11": "68b962a61fcbd39f299bf7b9", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_17": "185a865c8cf0d67b3027a313", "orders_model_2": "be51e7ded79e692b0f86ad8e", "orders_model_20": "0c798fa3e6d807bf94e96eee", -"orders_model_21": "08e7a885c1d9662b2678c3ea", -"orders_model_22": "1b893dcaf72edc288a3b9710", "orders_model_23": "f35475b4e5e4ae85ae46e91d", -"orders_model_3": "85fda65b19f7375e90342fa9", -"orders_model_4": "2cfd6426be9711fc3a185538", "orders_model_5": "949207159f621de6e270aed5", -"orders_model_6": "60e19e6908bb5c06d38eeb01", -"orders_model_7": "07289e08df177aa672b5da80", "orders_model_8": "c18f5ec77f58bbb47cc78168", -"orders_model_9": "05bfc38d1bf1cc771029edec", "payment_totals": "692528d52a5623a0e90d7fb4", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "0b5af099963f99173031a52f", diff --git a/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json index 58e8e5e04..891f92108 100644 --- a/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json +++ b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_fast_lineage.json @@ -6,50 +6,50 @@ [ "models", { -"adapter_rules_cte": "01c88e0760e09990ec7e7f27", -"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", -"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", -"dotless_i_staging": "0414cf2aabffee9324ca1152", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "0ffc9fd6d1c6d34c2db07849", -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "b1970df938e25e00faeed015", +"adapter_rules_cte": "841a45720257c5ccc265a8dd", +"adapter_rules_staging": "d7ed9e83f75a1acce7b9adef", +"adapter_rules_untyped_cte": "25b5f2031eb7b5c501458c59", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "f43b09f8bea292b9ed926048", +"orders_model_15": "7892762bdf84044392ec032c", +"orders_model_18": "18f9da2cb3153a93b0bad0c5", +"orders_model_21": "61bbd920ddc368eb7f15f23f", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "3c79de3965ca12588d41dc12", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "9e2c93fb53c63305e06b8ada", "orders_model_10": "6a9dd6631afe2aecba420032", -"orders_model_11": "e0a1bcae33bbfb164c9468f6", -"orders_model_12": "926f7b801edc86d0d0ecbac0", -"orders_model_13": "8af6ca52fb84b5b173b2326d", -"orders_model_14": "c1ec6ecf89992261001a255f", -"orders_model_15": "cc32a1a5e07802f1512706b9", -"orders_model_16": "16ae4650da78bf9861f65553", -"orders_model_17": "ee556ea5f790f1d885cee84a", -"orders_model_18": "241cc7616b413ea893ca6829", -"orders_model_19": "92a8e66d811c1497a2216783", -"orders_model_2": "345f166deff852e9116d8801", -"orders_model_20": "97c09ef764382c456089a08f", -"orders_model_21": "29e669ac95b4e44f7d8ea530", -"orders_model_22": "8d9a8370c5bb21b381f42208", -"orders_model_23": "fe73a080f7f7a3e1df6c305c", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "10c324ddebd8f360f30b0a6b", -"orders_model_5": "0209faf6600f5936ca0209a7", -"orders_model_6": "6d3a0df899304a5fff38b3f0", -"orders_model_7": "2cc4699fcfbfc50f29d34e4d", -"orders_model_8": "82c19ad1bdebebf9b9e99f2c", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_13": "621b73b3d5c26dce087b47d3", +"orders_model_16": "15244e80d584b8560df7bb65", +"orders_model_19": "f1a53df313536491ea93c1d0", +"orders_model_22": "e5efe0bb7d262f2d111366b3", +"orders_model_4": "540d8ba0e4f2a3eb6648b918", +"orders_model_7": "f0454968fece57bf50be47c9", +"orders_model_11": "13e957b7cb5550c82c444f53", +"orders_model_14": "2f82b71e0b4161de8468ccba", +"orders_model_17": "491a1c5bb2ab02a6131a515b", +"orders_model_2": "9317eb65f36a14dd5181ef82", +"orders_model_20": "ef1eb3830731ab532a163f2b", +"orders_model_23": "e396b8b30628a0208c1e7794", +"orders_model_5": "dc48a3a4f5d527183ee35842", +"orders_model_8": "a8d6d6bdaf5ff00dd714816d", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ffb88803fba96f5e1a0cc14", -"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "b2268fc29a60313e3b8c34ed", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345", -"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +"pivot_0": "bf081a06e8a2a198511486e0", +"pivot_1": "d8cb46c8edcfe87f37163643", +"pivot_2": "0860ef89e9ce4066470edeac", +"pivot_3": "06c892c6cba0e4f3ba822660", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"3f652f722b3dc3980f07f59d" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -214,50 +214,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "0897bc61ca6a0042539889cd", +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "f43b09f8bea292b9ed926048", +"orders_model_15": "60aa6792c6ac5f20c7300029", +"orders_model_18": "18f9da2cb3153a93b0bad0c5", +"orders_model_21": "61bbd920ddc368eb7f15f23f", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "5583097e3d7507c1048ebaea", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "0d476405ccafa4fbc2ea5db6", "orders_model_10": "6a9dd6631afe2aecba420032", -"orders_model_11": "e0a1bcae33bbfb164c9468f6", -"orders_model_12": "fd707ea6f93871b0dc83c579", -"orders_model_13": "74b30199b5f8cc665f133e68", -"orders_model_14": "c1ec6ecf89992261001a255f", -"orders_model_15": "18719f2a40e9a0d55dce1f6d", -"orders_model_16": "5464f219a5381063a5cf2965", -"orders_model_17": "afd6d68f0a0a6689404e0260", -"orders_model_18": "ff71888baeb777eb8b0d274d", -"orders_model_19": "92a8e66d811c1497a2216783", -"orders_model_2": "835e887650bbf9b124ae26b1", -"orders_model_20": "97c09ef764382c456089a08f", -"orders_model_21": "af5f39f23b7c2b58d3f12930", -"orders_model_22": "3abd2e6a08df2cf8322bf309", -"orders_model_23": "fe73a080f7f7a3e1df6c305c", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "c868aee07ab0f13c50fadca2", -"orders_model_5": "8ca37d6ebd3f7cafc5b226da", -"orders_model_6": "7431806bd67f9e0c952a98ab", -"orders_model_7": "2cc4699fcfbfc50f29d34e4d", -"orders_model_8": "82c19ad1bdebebf9b9e99f2c", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"orders_model_13": "0fa6bc036eccb1bd4067c0fc", +"orders_model_16": "3dc4ee5e67881ec8eb88374f", +"orders_model_19": "68412d0dd098d6f4127f5084", +"orders_model_22": "e5efe0bb7d262f2d111366b3", +"orders_model_4": "f2577830acba88d149818267", +"orders_model_7": "1614a626eb417037485880e8", +"orders_model_11": "28aa418cc8ed14a56873b90a", +"orders_model_14": "5b1b6887611fe96d83e3823f", +"orders_model_17": "8c62f0737cfba7e68b86ae43", +"orders_model_2": "a1a708f0e0e9cbf14f7ba3a7", +"orders_model_20": "ef1eb3830731ab532a163f2b", +"orders_model_23": "d6ff40831aac2339640778c9", +"orders_model_5": "85991f5c77c8754f799c1397", +"orders_model_8": "a8d6d6bdaf5ff00dd714816d", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ffb88803fba96f5e1a0cc14", -"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "af151ba34f16c141fdee1ac3", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" +"pivot_0": "780cccafd942913ef74a86dc", +"pivot_1": "4201810ec2a2caba872d3cea", +"pivot_2": "1b5af616e9ae9638438cff5c", +"pivot_3": "3023c184a01a785e2b99dd76", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"4b773261b99e3dd24d7e41a5" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -422,50 +422,50 @@ [ "models", { -"adapter_rules_cte": "01c88e0760e09990ec7e7f27", -"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", -"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", -"dotless_i_staging": "0414cf2aabffee9324ca1152", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "0ffc9fd6d1c6d34c2db07849", -"orders_model_0": "b09efd1e7181db7773253200", -"orders_model_1": "8f27fcd3be57b9a85a5ade04", -"orders_model_10": "b57ee1c35cbaadb688298310", -"orders_model_11": "1c545871d01b68d8cc5270d4", -"orders_model_12": "a2d666e8a32f9f36556fefca", -"orders_model_13": "356f32e628d61db27bf4a4f3", -"orders_model_14": "5770c08feedcc514f8cdea74", -"orders_model_15": "511a913bdc473948a1d98b00", -"orders_model_16": "7a40989a4e6cf23c222525f6", -"orders_model_17": "74820176e511b43c55d5dcb7", -"orders_model_18": "6162b5c431b89f8917de6793", -"orders_model_19": "2d58b808566d04184cf9ab29", -"orders_model_2": "3d131ea2ed9d90f4404800c4", -"orders_model_20": "24da7733eb4155182f26f868", -"orders_model_21": "afdbc811f2683769c6065622", -"orders_model_22": "1ecee061003eefdd8e7b3e1d", -"orders_model_23": "ebf7fe799455ca0ef8c2a02e", -"orders_model_3": "c3a07511c03df1968f2c5ff9", -"orders_model_4": "0eca13897e0abb4131837a39", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "8ed658a7454aba1b437c36b2", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "a0fc71fac4e12ec905ae953c", -"orders_model_9": "f6f4f76cf2077ee259b60302", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"adapter_rules_cte": "841a45720257c5ccc265a8dd", +"adapter_rules_staging": "d7ed9e83f75a1acce7b9adef", +"adapter_rules_untyped_cte": "25b5f2031eb7b5c501458c59", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "36bff715df460d6a99057b48", +"orders_model_15": "d99e5356957f891964a727f1", +"orders_model_18": "e2ab7b8536a568c9d3d3f586", +"orders_model_21": "4e0bd396f144b882c4f76045", +"orders_model_3": "855cfac8af99cff64e3b39cc", +"orders_model_6": "80c85e009910001ee4bf4573", +"orders_model_9": "2e576dbc2ec9950f748ab040", +"orders_model_1": "719ad3e054aa9391ed9206ca", +"orders_model_10": "04a4df58847b79cc1bc296b5", +"orders_model_13": "1fd65cb552749944892dc152", +"orders_model_16": "40824548bf6231808982c889", +"orders_model_19": "77f6d40b2301347436ce5deb", +"orders_model_22": "aa4709950063fa7bbf47ff53", +"orders_model_4": "f4e41df89fd84a43e52ce29a", +"orders_model_7": "28eff25ffabb34f29939d834", +"orders_model_11": "0d81f117399e963e70aebb95", +"orders_model_14": "a31e47529981efdef405c080", +"orders_model_17": "a4d45def03d7d7666bc06b0c", +"orders_model_2": "d1b6a330b73516f1d4246ecd", +"orders_model_20": "8325a495f5df26f5bb6553f1", +"orders_model_23": "aacfa6141bcba749057aa9c4", +"orders_model_5": "cec4287287b33c590d00a0bb", +"orders_model_8": "cb6b6c0f05f7ae1ec8b6e585", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "78dd4b3d90301ccd1e781295", -"pivot_1": "b2268fc29a60313e3b8c34ed", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "ae6c2278feec076b8e8bc0d9", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345", -"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +"pivot_0": "0860ef89e9ce4066470edeac", +"pivot_1": "06c892c6cba0e4f3ba822660", +"pivot_2": "d1bbd23f4694ce0d97f5c165", +"pivot_3": "c6c8e4f1d9d2048c3338e4d7", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"e9da95982504131dc773c046" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -630,50 +630,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "dc7f3dec778fd7cc3b47de47", -"orders_model_1": "881ca8c2aa83a640eb0c27ab", -"orders_model_10": "0498564b827ff224871c5c16", -"orders_model_11": "1c545871d01b68d8cc5270d4", -"orders_model_12": "a9609afdd975480f9b30d313", -"orders_model_13": "e9a825f1c611bead0b731f83", -"orders_model_14": "5770c08feedcc514f8cdea74", -"orders_model_15": "511a913bdc473948a1d98b00", -"orders_model_16": "00e981e5c6d50e8e603b21d1", -"orders_model_17": "3397814b3d1156e0e851a303", -"orders_model_18": "81452dd48c4fac8e75395e03", -"orders_model_19": "4782036882204544bf9b8cc0", -"orders_model_2": "3d131ea2ed9d90f4404800c4", -"orders_model_20": "d5ad421755fa576851cd7eea", -"orders_model_21": "afdbc811f2683769c6065622", -"orders_model_22": "82ea73f8ec2b48e09123b28a", -"orders_model_23": "ebf7fe799455ca0ef8c2a02e", -"orders_model_3": "c3a07511c03df1968f2c5ff9", -"orders_model_4": "bf62990d9480cf71956fd3c9", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "2694202ab29ccd1ad221d427", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "a0fc71fac4e12ec905ae953c", -"orders_model_9": "dadc55d42681e2d084d2c52b", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "c98195ac460ce6ca7b4010e3", +"orders_model_15": "c8cd55adb66760570ede2dc9", +"orders_model_18": "92b36e152f6c5344d8752970", +"orders_model_21": "5f132332ae3b9369f92873b5", +"orders_model_3": "d8e80bbaf4082a4b84af147a", +"orders_model_6": "231a71307da97c186fceaf29", +"orders_model_9": "20f4b7d9787b1c99bacef80a", +"orders_model_1": "578f308f47a8f1f8c74c85a2", +"orders_model_10": "1480ce664be2bd478f6f13af", +"orders_model_13": "dd18b3c2ed7c7b324d985764", +"orders_model_16": "63d5b8360aca1525e180d656", +"orders_model_19": "1e0bb6e85de85db44da77c5a", +"orders_model_22": "aa4709950063fa7bbf47ff53", +"orders_model_4": "d99e41de1f2f5367123a86f9", +"orders_model_7": "849e77b8ea8ae735aff06dd8", +"orders_model_11": "3967c4719e858bb1e795eb1c", +"orders_model_14": "0339a9398180e39c582dfed3", +"orders_model_17": "3edb225b67323ffbaaf845b5", +"orders_model_2": "22f382fa450be01cb2d19679", +"orders_model_20": "8325a495f5df26f5bb6553f1", +"orders_model_23": "aa316d0b798086ad820eebb2", +"orders_model_5": "acb0c89df32884c9cd916504", +"orders_model_8": "cb6b6c0f05f7ae1ec8b6e585", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "78dd4b3d90301ccd1e781295", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "ae6c2278feec076b8e8bc0d9", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" +"pivot_0": "1b5af616e9ae9638438cff5c", +"pivot_1": "3023c184a01a785e2b99dd76", +"pivot_2": "47dc75892e7ace4f0b7e4d11", +"pivot_3": "f6d97556af34b65315ce96dc", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"d9d2cf58d167ce9056b86b8b" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -838,50 +838,50 @@ [ "models", { -"adapter_rules_cte": "01c88e0760e09990ec7e7f27", -"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", -"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", -"dotless_i_staging": "0414cf2aabffee9324ca1152", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "0ffc9fd6d1c6d34c2db07849", -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "1cbed5d338fb3ae061486bf7", -"orders_model_10": "5319ece7f82596acc6513f91", -"orders_model_11": "ccf2498a5b3526260f88df6f", -"orders_model_12": "cb6d3e8487b533349a2bad04", -"orders_model_13": "6c48a8656e02aa11445643e2", -"orders_model_14": "3c574035ecba3d30a27e46ce", -"orders_model_15": "530fb4a941e847207cba2410", -"orders_model_16": "a249acc516a880c9fbe25e88", -"orders_model_17": "7c698d2731cea40e031c9052", -"orders_model_18": "6d3a0df899304a5fff38b3f0", -"orders_model_19": "2cc4699fcfbfc50f29d34e4d", -"orders_model_2": "59a5d7fd62d3554715ee8ef0", -"orders_model_20": "1475a844903645d3154efcef", -"orders_model_21": "1d0e1910cfa54e2cf1cdf738", -"orders_model_22": "e1c0902ddf75d8f0324e3aeb", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "7db0fac3e405cbc04ce12b67", -"orders_model_4": "3cebd1bd3941ee9edc8ae06e", -"orders_model_5": "0e62e59d1dbc1c842380f957", -"orders_model_6": "6d3a0df899304a5fff38b3f0", -"orders_model_7": "dfa55753fe76745de6964097", +"adapter_rules_cte": "841a45720257c5ccc265a8dd", +"adapter_rules_staging": "d7ed9e83f75a1acce7b9adef", +"adapter_rules_untyped_cte": "25b5f2031eb7b5c501458c59", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "f834ffa0f02cc35587802e94", +"orders_model_18": "3c79de3965ca12588d41dc12", +"orders_model_21": "3c97c1c7363692399b1dda45", +"orders_model_3": "5afc8409712cccb9c27f5577", +"orders_model_6": "3c79de3965ca12588d41dc12", +"orders_model_9": "2e576dbc2ec9950f748ab040", +"orders_model_1": "322f0673f24a323f7ab204f2", +"orders_model_10": "be32f84ba32fc4122499c543", +"orders_model_13": "2fe03b7cf395ed97e4fe1e8e", +"orders_model_16": "34b0a9652b09c53e5aa314b0", +"orders_model_19": "f0454968fece57bf50be47c9", +"orders_model_22": "138ad371f887c37248ccbc76", +"orders_model_4": "21e1a97ec58e053fceb0752f", +"orders_model_7": "7857040aedd1f4a5eeb85aad", +"orders_model_11": "bd51523ed414294d0d2b1d32", +"orders_model_14": "6ce4b9615eacda6b7223cc34", +"orders_model_17": "8de17e28e71a71170af3696f", +"orders_model_2": "d54d5927e4e85660a1ca82bb", +"orders_model_20": "60d60bdadc4851ec2dc7801b", +"orders_model_23": "bd51523ed414294d0d2b1d32", +"orders_model_5": "c71516dc9ea50f798f2fc042", "orders_model_8": "bdc843833774d4e2ce081a50", -"orders_model_9": "f6f4f76cf2077ee259b60302", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8d7a74ec77a418a6a2641b84", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "b2268fc29a60313e3b8c34ed", -"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345", -"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +"pivot_0": "98b8f3e8c523bf756d6a2bf7", +"pivot_1": "0860ef89e9ce4066470edeac", +"pivot_2": "06c892c6cba0e4f3ba822660", +"pivot_3": "d8cb46c8edcfe87f37163643", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"a72c104311c712bf9eada7e3" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1046,50 +1046,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "1cbed5d338fb3ae061486bf7", -"orders_model_10": "d02e5f6991f3149f83f298e7", -"orders_model_11": "ccf2498a5b3526260f88df6f", -"orders_model_12": "edf57c58d750226a747455ba", -"orders_model_13": "c6fe9d63cebac8a82557647b", -"orders_model_14": "3c574035ecba3d30a27e46ce", -"orders_model_15": "530fb4a941e847207cba2410", -"orders_model_16": "d545cb7b8ca26b55d32439c0", -"orders_model_17": "7c698d2731cea40e031c9052", -"orders_model_18": "7431806bd67f9e0c952a98ab", -"orders_model_19": "2cc4699fcfbfc50f29d34e4d", -"orders_model_2": "808d76e03e1b6ed06eb32fe2", -"orders_model_20": "cb78e3704fcaf1883d3f635d", -"orders_model_21": "1d0e1910cfa54e2cf1cdf738", -"orders_model_22": "a431a1c7e46ac0fd16f20095", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "7db0fac3e405cbc04ce12b67", -"orders_model_4": "3d86c54cb0dcd3d2cb0b7792", -"orders_model_5": "9a08a024fd692ca500ba98be", -"orders_model_6": "7431806bd67f9e0c952a98ab", -"orders_model_7": "35db1baf4dd2d3ddfefa9829", +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "f834ffa0f02cc35587802e94", +"orders_model_18": "5583097e3d7507c1048ebaea", +"orders_model_21": "3c97c1c7363692399b1dda45", +"orders_model_3": "5afc8409712cccb9c27f5577", +"orders_model_6": "5583097e3d7507c1048ebaea", +"orders_model_9": "751c4dbbc22801ad8e41b700", +"orders_model_1": "4104fd33507fb9bdb9016fba", +"orders_model_10": "be32f84ba32fc4122499c543", +"orders_model_13": "a910fc1e9b0157c62478246c", +"orders_model_16": "d42726f7b0c3f0d38b8cbf29", +"orders_model_19": "1614a626eb417037485880e8", +"orders_model_22": "cddb983530781622e4a90235", +"orders_model_4": "3149a406a283f607ca35f01b", +"orders_model_7": "81bed31498f03f471038d561", +"orders_model_11": "9e899e72fafb086e86373175", +"orders_model_14": "50305bb62a182ee63be08606", +"orders_model_17": "270491a253ef6f6a5f2d4981", +"orders_model_2": "67f9667db38c07bb272b7025", +"orders_model_20": "60d60bdadc4851ec2dc7801b", +"orders_model_23": "9e899e72fafb086e86373175", +"orders_model_5": "d291f5b458b8a3eb5f24e55c", "orders_model_8": "bdc843833774d4e2ce081a50", -"orders_model_9": "5748db46e2a60c5fb4056bfc", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8d7a74ec77a418a6a2641b84", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "af151ba34f16c141fdee1ac3", -"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" +"pivot_0": "98b8f3e8c523bf756d6a2bf7", +"pivot_1": "1b5af616e9ae9638438cff5c", +"pivot_2": "3023c184a01a785e2b99dd76", +"pivot_3": "4201810ec2a2caba872d3cea", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"9d696237ea0f619ed4dee48f" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", diff --git a/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json index a2f20ee86..891f92108 100644 --- a/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json +++ b/tests/goldens/python_oracle/analysis_generated_snowflake_and_duckdb_adapter_rules_rich_lineage.json @@ -6,50 +6,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", -"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "0c505e7c91961ed939fa9101", +"adapter_rules_cte": "841a45720257c5ccc265a8dd", +"adapter_rules_staging": "d7ed9e83f75a1acce7b9adef", +"adapter_rules_untyped_cte": "25b5f2031eb7b5c501458c59", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "f43b09f8bea292b9ed926048", +"orders_model_15": "7892762bdf84044392ec032c", +"orders_model_18": "18f9da2cb3153a93b0bad0c5", +"orders_model_21": "61bbd920ddc368eb7f15f23f", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "3c79de3965ca12588d41dc12", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "9e2c93fb53c63305e06b8ada", "orders_model_10": "6a9dd6631afe2aecba420032", -"orders_model_11": "e0a1bcae33bbfb164c9468f6", -"orders_model_12": "926f7b801edc86d0d0ecbac0", -"orders_model_13": "8af6ca52fb84b5b173b2326d", -"orders_model_14": "c1ec6ecf89992261001a255f", -"orders_model_15": "b381e86843932813f7ad5b91", -"orders_model_16": "16ae4650da78bf9861f65553", -"orders_model_17": "ee556ea5f790f1d885cee84a", -"orders_model_18": "ff71888baeb777eb8b0d274d", -"orders_model_19": "92a8e66d811c1497a2216783", -"orders_model_2": "b1cffebb5797fe684c20e885", -"orders_model_20": "839fac6b32ebf58d515e9451", -"orders_model_21": "29e669ac95b4e44f7d8ea530", -"orders_model_22": "8d9a8370c5bb21b381f42208", -"orders_model_23": "fe73a080f7f7a3e1df6c305c", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "10c324ddebd8f360f30b0a6b", -"orders_model_5": "3fc282000bb67e74433ffd40", -"orders_model_6": "e813ee34de06d5cae82ac8c2", -"orders_model_7": "2cc4699fcfbfc50f29d34e4d", -"orders_model_8": "82c19ad1bdebebf9b9e99f2c", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8ffb88803fba96f5e1a0cc14", -"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "b2268fc29a60313e3b8c34ed", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345", -"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +"orders_model_13": "621b73b3d5c26dce087b47d3", +"orders_model_16": "15244e80d584b8560df7bb65", +"orders_model_19": "f1a53df313536491ea93c1d0", +"orders_model_22": "e5efe0bb7d262f2d111366b3", +"orders_model_4": "540d8ba0e4f2a3eb6648b918", +"orders_model_7": "f0454968fece57bf50be47c9", +"orders_model_11": "13e957b7cb5550c82c444f53", +"orders_model_14": "2f82b71e0b4161de8468ccba", +"orders_model_17": "491a1c5bb2ab02a6131a515b", +"orders_model_2": "9317eb65f36a14dd5181ef82", +"orders_model_20": "ef1eb3830731ab532a163f2b", +"orders_model_23": "e396b8b30628a0208c1e7794", +"orders_model_5": "dc48a3a4f5d527183ee35842", +"orders_model_8": "a8d6d6bdaf5ff00dd714816d", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "bf081a06e8a2a198511486e0", +"pivot_1": "d8cb46c8edcfe87f37163643", +"pivot_2": "0860ef89e9ce4066470edeac", +"pivot_3": "06c892c6cba0e4f3ba822660", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"e2fa30056701edaf7a347f5b" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -214,50 +214,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "0897bc61ca6a0042539889cd", +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "f43b09f8bea292b9ed926048", +"orders_model_15": "60aa6792c6ac5f20c7300029", +"orders_model_18": "18f9da2cb3153a93b0bad0c5", +"orders_model_21": "61bbd920ddc368eb7f15f23f", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "5583097e3d7507c1048ebaea", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "0d476405ccafa4fbc2ea5db6", "orders_model_10": "6a9dd6631afe2aecba420032", -"orders_model_11": "e0a1bcae33bbfb164c9468f6", -"orders_model_12": "fd707ea6f93871b0dc83c579", -"orders_model_13": "74b30199b5f8cc665f133e68", -"orders_model_14": "c1ec6ecf89992261001a255f", -"orders_model_15": "7f3924ba267d897cc875a294", -"orders_model_16": "5464f219a5381063a5cf2965", -"orders_model_17": "afd6d68f0a0a6689404e0260", -"orders_model_18": "ff71888baeb777eb8b0d274d", -"orders_model_19": "92a8e66d811c1497a2216783", -"orders_model_2": "b1cffebb5797fe684c20e885", -"orders_model_20": "839fac6b32ebf58d515e9451", -"orders_model_21": "af5f39f23b7c2b58d3f12930", -"orders_model_22": "3abd2e6a08df2cf8322bf309", -"orders_model_23": "fe73a080f7f7a3e1df6c305c", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "c868aee07ab0f13c50fadca2", -"orders_model_5": "3fc282000bb67e74433ffd40", -"orders_model_6": "7431806bd67f9e0c952a98ab", -"orders_model_7": "2cc4699fcfbfc50f29d34e4d", -"orders_model_8": "82c19ad1bdebebf9b9e99f2c", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8ffb88803fba96f5e1a0cc14", -"pivot_1": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "af151ba34f16c141fdee1ac3", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" +"orders_model_13": "0fa6bc036eccb1bd4067c0fc", +"orders_model_16": "3dc4ee5e67881ec8eb88374f", +"orders_model_19": "68412d0dd098d6f4127f5084", +"orders_model_22": "e5efe0bb7d262f2d111366b3", +"orders_model_4": "f2577830acba88d149818267", +"orders_model_7": "1614a626eb417037485880e8", +"orders_model_11": "28aa418cc8ed14a56873b90a", +"orders_model_14": "5b1b6887611fe96d83e3823f", +"orders_model_17": "8c62f0737cfba7e68b86ae43", +"orders_model_2": "a1a708f0e0e9cbf14f7ba3a7", +"orders_model_20": "ef1eb3830731ab532a163f2b", +"orders_model_23": "d6ff40831aac2339640778c9", +"orders_model_5": "85991f5c77c8754f799c1397", +"orders_model_8": "a8d6d6bdaf5ff00dd714816d", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "780cccafd942913ef74a86dc", +"pivot_1": "4201810ec2a2caba872d3cea", +"pivot_2": "1b5af616e9ae9638438cff5c", +"pivot_3": "3023c184a01a785e2b99dd76", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"73e2d7913705a099c6c0de21" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -422,50 +422,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", -"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c0a4a49f398790b6968ded9b", -"orders_model_1": "8f27fcd3be57b9a85a5ade04", -"orders_model_10": "b57ee1c35cbaadb688298310", -"orders_model_11": "1c545871d01b68d8cc5270d4", -"orders_model_12": "a7868c059b8f4ff1fe264c2a", -"orders_model_13": "356f32e628d61db27bf4a4f3", -"orders_model_14": "5770c08feedcc514f8cdea74", -"orders_model_15": "6d334b184def14c92a91f6fe", -"orders_model_16": "7a40989a4e6cf23c222525f6", -"orders_model_17": "74820176e511b43c55d5dcb7", -"orders_model_18": "864293c567900a908d92f817", -"orders_model_19": "2d58b808566d04184cf9ab29", -"orders_model_2": "3d131ea2ed9d90f4404800c4", -"orders_model_20": "9ea231ae7342ed67e393353e", -"orders_model_21": "afdbc811f2683769c6065622", -"orders_model_22": "1ecee061003eefdd8e7b3e1d", -"orders_model_23": "ebf7fe799455ca0ef8c2a02e", -"orders_model_3": "2136d13b1291a5e9dd31e887", -"orders_model_4": "0eca13897e0abb4131837a39", -"orders_model_5": "7a9fabf5b3d3ab9e1fc63a99", -"orders_model_6": "ea32e1b4eb22b26be9f440ed", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "a0fc71fac4e12ec905ae953c", -"orders_model_9": "f6f4f76cf2077ee259b60302", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "78dd4b3d90301ccd1e781295", -"pivot_1": "b2268fc29a60313e3b8c34ed", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "ae6c2278feec076b8e8bc0d9", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345", -"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +"adapter_rules_cte": "841a45720257c5ccc265a8dd", +"adapter_rules_staging": "d7ed9e83f75a1acce7b9adef", +"adapter_rules_untyped_cte": "25b5f2031eb7b5c501458c59", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "36bff715df460d6a99057b48", +"orders_model_15": "d99e5356957f891964a727f1", +"orders_model_18": "e2ab7b8536a568c9d3d3f586", +"orders_model_21": "4e0bd396f144b882c4f76045", +"orders_model_3": "855cfac8af99cff64e3b39cc", +"orders_model_6": "80c85e009910001ee4bf4573", +"orders_model_9": "2e576dbc2ec9950f748ab040", +"orders_model_1": "719ad3e054aa9391ed9206ca", +"orders_model_10": "04a4df58847b79cc1bc296b5", +"orders_model_13": "1fd65cb552749944892dc152", +"orders_model_16": "40824548bf6231808982c889", +"orders_model_19": "77f6d40b2301347436ce5deb", +"orders_model_22": "aa4709950063fa7bbf47ff53", +"orders_model_4": "f4e41df89fd84a43e52ce29a", +"orders_model_7": "28eff25ffabb34f29939d834", +"orders_model_11": "0d81f117399e963e70aebb95", +"orders_model_14": "a31e47529981efdef405c080", +"orders_model_17": "a4d45def03d7d7666bc06b0c", +"orders_model_2": "d1b6a330b73516f1d4246ecd", +"orders_model_20": "8325a495f5df26f5bb6553f1", +"orders_model_23": "aacfa6141bcba749057aa9c4", +"orders_model_5": "cec4287287b33c590d00a0bb", +"orders_model_8": "cb6b6c0f05f7ae1ec8b6e585", +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "0860ef89e9ce4066470edeac", +"pivot_1": "06c892c6cba0e4f3ba822660", +"pivot_2": "d1bbd23f4694ce0d97f5c165", +"pivot_3": "c6c8e4f1d9d2048c3338e4d7", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"5566f9b034670ebc7d57c741" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -630,50 +630,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "dc7f3dec778fd7cc3b47de47", -"orders_model_1": "881ca8c2aa83a640eb0c27ab", -"orders_model_10": "0498564b827ff224871c5c16", -"orders_model_11": "1c545871d01b68d8cc5270d4", -"orders_model_12": "1b5b1859d336c6311e9827f1", -"orders_model_13": "e9a825f1c611bead0b731f83", -"orders_model_14": "5770c08feedcc514f8cdea74", -"orders_model_15": "6d334b184def14c92a91f6fe", -"orders_model_16": "00e981e5c6d50e8e603b21d1", -"orders_model_17": "3397814b3d1156e0e851a303", -"orders_model_18": "81452dd48c4fac8e75395e03", -"orders_model_19": "4782036882204544bf9b8cc0", -"orders_model_2": "3d131ea2ed9d90f4404800c4", -"orders_model_20": "b69caa0299e13825ed690cb8", -"orders_model_21": "afdbc811f2683769c6065622", -"orders_model_22": "82ea73f8ec2b48e09123b28a", -"orders_model_23": "ebf7fe799455ca0ef8c2a02e", -"orders_model_3": "2136d13b1291a5e9dd31e887", -"orders_model_4": "bf62990d9480cf71956fd3c9", -"orders_model_5": "458d9725be39144ca5e0c33d", -"orders_model_6": "4359abfef23a7b1e646d2aee", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "a0fc71fac4e12ec905ae953c", -"orders_model_9": "dadc55d42681e2d084d2c52b", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "78dd4b3d90301ccd1e781295", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "ae6c2278feec076b8e8bc0d9", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "c98195ac460ce6ca7b4010e3", +"orders_model_15": "c8cd55adb66760570ede2dc9", +"orders_model_18": "92b36e152f6c5344d8752970", +"orders_model_21": "5f132332ae3b9369f92873b5", +"orders_model_3": "d8e80bbaf4082a4b84af147a", +"orders_model_6": "231a71307da97c186fceaf29", +"orders_model_9": "20f4b7d9787b1c99bacef80a", +"orders_model_1": "578f308f47a8f1f8c74c85a2", +"orders_model_10": "1480ce664be2bd478f6f13af", +"orders_model_13": "dd18b3c2ed7c7b324d985764", +"orders_model_16": "63d5b8360aca1525e180d656", +"orders_model_19": "1e0bb6e85de85db44da77c5a", +"orders_model_22": "aa4709950063fa7bbf47ff53", +"orders_model_4": "d99e41de1f2f5367123a86f9", +"orders_model_7": "849e77b8ea8ae735aff06dd8", +"orders_model_11": "3967c4719e858bb1e795eb1c", +"orders_model_14": "0339a9398180e39c582dfed3", +"orders_model_17": "3edb225b67323ffbaaf845b5", +"orders_model_2": "22f382fa450be01cb2d19679", +"orders_model_20": "8325a495f5df26f5bb6553f1", +"orders_model_23": "aa316d0b798086ad820eebb2", +"orders_model_5": "acb0c89df32884c9cd916504", +"orders_model_8": "cb6b6c0f05f7ae1ec8b6e585", +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "1b5af616e9ae9638438cff5c", +"pivot_1": "3023c184a01a785e2b99dd76", +"pivot_2": "47dc75892e7ace4f0b7e4d11", +"pivot_3": "f6d97556af34b65315ce96dc", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"eff03c0ebcf2c6323671b7ee" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -838,50 +838,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "d244bb9fd736be2a83c1c25f", -"adapter_rules_untyped_cte": "443d6a471f368f61895e97b7", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "1cbed5d338fb3ae061486bf7", -"orders_model_10": "5a31508076b33a63eb5f6f5a", -"orders_model_11": "ccf2498a5b3526260f88df6f", -"orders_model_12": "2d47664882f4e1df073137de", -"orders_model_13": "6c48a8656e02aa11445643e2", -"orders_model_14": "5324123258199836670bd610", -"orders_model_15": "27153ec6b977ca8e32094d2f", -"orders_model_16": "a249acc516a880c9fbe25e88", -"orders_model_17": "b29a2c85abeb6ad80d6890c3", -"orders_model_18": "e813ee34de06d5cae82ac8c2", -"orders_model_19": "2cc4699fcfbfc50f29d34e4d", -"orders_model_2": "3302ce3fd2e8a684f2be9632", -"orders_model_20": "e4ff6e5c3b52a9c5568a3ff1", -"orders_model_21": "1d0e1910cfa54e2cf1cdf738", -"orders_model_22": "e1c0902ddf75d8f0324e3aeb", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "80151fc977ccd29484c79a45", -"orders_model_4": "1fe47e48e7adf0e94261cf1d", -"orders_model_5": "f9183611419f32c78a12fbc4", -"orders_model_6": "e813ee34de06d5cae82ac8c2", -"orders_model_7": "dfa55753fe76745de6964097", +"adapter_rules_cte": "841a45720257c5ccc265a8dd", +"adapter_rules_staging": "d7ed9e83f75a1acce7b9adef", +"adapter_rules_untyped_cte": "25b5f2031eb7b5c501458c59", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "f834ffa0f02cc35587802e94", +"orders_model_18": "3c79de3965ca12588d41dc12", +"orders_model_21": "3c97c1c7363692399b1dda45", +"orders_model_3": "5afc8409712cccb9c27f5577", +"orders_model_6": "3c79de3965ca12588d41dc12", +"orders_model_9": "2e576dbc2ec9950f748ab040", +"orders_model_1": "322f0673f24a323f7ab204f2", +"orders_model_10": "be32f84ba32fc4122499c543", +"orders_model_13": "2fe03b7cf395ed97e4fe1e8e", +"orders_model_16": "34b0a9652b09c53e5aa314b0", +"orders_model_19": "f0454968fece57bf50be47c9", +"orders_model_22": "138ad371f887c37248ccbc76", +"orders_model_4": "21e1a97ec58e053fceb0752f", +"orders_model_7": "7857040aedd1f4a5eeb85aad", +"orders_model_11": "bd51523ed414294d0d2b1d32", +"orders_model_14": "6ce4b9615eacda6b7223cc34", +"orders_model_17": "8de17e28e71a71170af3696f", +"orders_model_2": "d54d5927e4e85660a1ca82bb", +"orders_model_20": "60d60bdadc4851ec2dc7801b", +"orders_model_23": "bd51523ed414294d0d2b1d32", +"orders_model_5": "c71516dc9ea50f798f2fc042", "orders_model_8": "bdc843833774d4e2ce081a50", -"orders_model_9": "f6f4f76cf2077ee259b60302", -"payment_totals": "ddedaaeda0bdb9d9997ac6cd", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8d7a74ec77a418a6a2641b84", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "b2268fc29a60313e3b8c34ed", -"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345", -"umlaut_cte": "52e72621ebb88c5fc2a9fe35" +"payment_totals": "06adf350696650d18756caf9", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "98b8f3e8c523bf756d6a2bf7", +"pivot_1": "0860ef89e9ce4066470edeac", +"pivot_2": "06c892c6cba0e4f3ba822660", +"pivot_3": "d8cb46c8edcfe87f37163643", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"495c0f20724d3290a1990d2d" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1046,50 +1046,50 @@ [ "models", { -"adapter_rules_cte": "dfd1ab68201bde357955ba7f", -"adapter_rules_staging": "1647edbe8ac61356261cfa76", -"adapter_rules_untyped_cte": "6d7a85f4f3ff37fb43fdc0b4", -"dotless_i_staging": "4c25b6514c8939c1579e7729", -"long_s_cte": "22aa6898e4deee5e707a61c8", -"long_s_staging": "c70692ed48ec89dc344df4e0", -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "1cbed5d338fb3ae061486bf7", -"orders_model_10": "d02e5f6991f3149f83f298e7", -"orders_model_11": "ccf2498a5b3526260f88df6f", -"orders_model_12": "a030043d592af380cf44cb9f", -"orders_model_13": "c6fe9d63cebac8a82557647b", -"orders_model_14": "5324123258199836670bd610", -"orders_model_15": "27153ec6b977ca8e32094d2f", -"orders_model_16": "d545cb7b8ca26b55d32439c0", -"orders_model_17": "b29a2c85abeb6ad80d6890c3", -"orders_model_18": "7431806bd67f9e0c952a98ab", -"orders_model_19": "2cc4699fcfbfc50f29d34e4d", -"orders_model_2": "3302ce3fd2e8a684f2be9632", -"orders_model_20": "e4ff6e5c3b52a9c5568a3ff1", -"orders_model_21": "1d0e1910cfa54e2cf1cdf738", -"orders_model_22": "a431a1c7e46ac0fd16f20095", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "80151fc977ccd29484c79a45", -"orders_model_4": "3d86c54cb0dcd3d2cb0b7792", -"orders_model_5": "f9183611419f32c78a12fbc4", -"orders_model_6": "7431806bd67f9e0c952a98ab", -"orders_model_7": "35db1baf4dd2d3ddfefa9829", +"adapter_rules_cte": "56b69355c2ddc2dcd1f2a13c", +"adapter_rules_staging": "f84ba667efd3c8ec2c884647", +"adapter_rules_untyped_cte": "0d85ee95bea1364f3c2867a1", +"dotless_i_staging": "da11179e9d9dc0fe64d2e0b2", +"long_s_cte": "6deb3df1c859c23f5ca923da", +"long_s_staging": "c2a43a7d44995f8c62c32ef8", +"umlaut_cte": "f154c3ae3931efa5d4f93bb2", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "f834ffa0f02cc35587802e94", +"orders_model_18": "5583097e3d7507c1048ebaea", +"orders_model_21": "3c97c1c7363692399b1dda45", +"orders_model_3": "5afc8409712cccb9c27f5577", +"orders_model_6": "5583097e3d7507c1048ebaea", +"orders_model_9": "751c4dbbc22801ad8e41b700", +"orders_model_1": "4104fd33507fb9bdb9016fba", +"orders_model_10": "be32f84ba32fc4122499c543", +"orders_model_13": "a910fc1e9b0157c62478246c", +"orders_model_16": "d42726f7b0c3f0d38b8cbf29", +"orders_model_19": "1614a626eb417037485880e8", +"orders_model_22": "cddb983530781622e4a90235", +"orders_model_4": "3149a406a283f607ca35f01b", +"orders_model_7": "81bed31498f03f471038d561", +"orders_model_11": "9e899e72fafb086e86373175", +"orders_model_14": "50305bb62a182ee63be08606", +"orders_model_17": "270491a253ef6f6a5f2d4981", +"orders_model_2": "67f9667db38c07bb272b7025", +"orders_model_20": "60d60bdadc4851ec2dc7801b", +"orders_model_23": "9e899e72fafb086e86373175", +"orders_model_5": "d291f5b458b8a3eb5f24e55c", "orders_model_8": "bdc843833774d4e2ce081a50", -"orders_model_9": "5748db46e2a60c5fb4056bfc", -"payment_totals": "ce8a97426d53e0acd6c49c2b", -"payments_by_customer": "da1968ec91b55f6f584c7ecd", -"pivot_0": "8d7a74ec77a418a6a2641b84", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "af151ba34f16c141fdee1ac3", -"pivot_3": "5adb7a56c3dfd3d1c3cf58c1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe", -"umlaut_cte": "a4c1810f1e46ef2200a87309" +"payment_totals": "09523c9ed08b78a006a96743", +"payments_by_customer": "9e1265b9f4134636cb508259", +"pivot_0": "98b8f3e8c523bf756d6a2bf7", +"pivot_1": "1b5af616e9ae9638438cff5c", +"pivot_2": "3023c184a01a785e2b99dd76", +"pivot_3": "4201810ec2a2caba872d3cea", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"d2a0f4ec04fb9eac758c1993" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", diff --git a/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json b/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json index 0fe10d8eb..6359af587 100644 --- a/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json +++ b/tests/goldens/python_oracle/analysis_generated_stars_ctes_cte_facts_set_operations_untyped_inputs_contracts_pivots.json @@ -6,43 +6,43 @@ [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "6f741725ae5781425358bc3b", -"orders_model_10": "c7e95ff7d90362e121ab238f", -"orders_model_11": "05479b1f55d5c854f9729a97", -"orders_model_12": "c380ae05f3bd0627628ab6fc", -"orders_model_13": "15537f706967682b808641f6", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", -"orders_model_16": "3dbaddb57a2ed5f8db44feda", -"orders_model_17": "7cc87d8c32df82057dc6057d", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_19": "4782036882204544bf9b8cc0", -"orders_model_2": "27b686e0958c19d151cd6319", -"orders_model_20": "12e4c14a088d64a94a267025", -"orders_model_21": "7a96226f82f52625a17f825f", -"orders_model_22": "74a4ef50a4b351777410e3ae", -"orders_model_23": "14b86bcb1a8edc065f4c7405", -"orders_model_3": "482e641536f8899b756807cf", -"orders_model_4": "526ce0a1d0a274582f9d35f1", -"orders_model_5": "9a08a024fd692ca500ba98be", -"orders_model_6": "526c167cb476b3553de71042", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "800e86b4e6a207eda6a59688", -"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "4c1fd04cd9c309cbdf527da8", +"orders_model_15": "84591d33538539fa86025db9", +"orders_model_18": "aea7224659a833d20f777865", +"orders_model_21": "8061cad6eea8e358bc36ea9f", +"orders_model_3": "39ddff27a397d2edc7681f49", +"orders_model_6": "8ef746a82d7aa2a0923807c0", +"orders_model_9": "632ce5a31c3bae13f0f4a7e9", +"orders_model_1": "fb2a01d710b2f83776b14354", +"orders_model_10": "40be7e0d35ce48e94f7c8a80", +"orders_model_13": "157856cfd2af496e6ea0ea50", +"orders_model_16": "a6ea5be270f7a6c18c7000cf", +"orders_model_19": "1e0bb6e85de85db44da77c5a", +"orders_model_22": "43cf6c5727de2c16cbf69295", +"orders_model_4": "904fb7a9751e94dd87e783a8", +"orders_model_7": "849e77b8ea8ae735aff06dd8", +"orders_model_11": "f642fc0dca03bb5d7b50e972", +"orders_model_14": "4fecc090e99be8bb0842c172", +"orders_model_17": "c7480e9c22e83b4805e7773a", +"orders_model_2": "018be1a46a90b11d68dcf9cd", +"orders_model_20": "13e27987e4afb380b1744e12", +"orders_model_23": "f65bf576c4c56164a290a3ad", +"orders_model_5": "d291f5b458b8a3eb5f24e55c", +"orders_model_8": "713b06e49c2e0e60e556e009", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "335dfa47b776524b62ee26e6", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "3767f4902ed5f3ab1acc83a1", -"pivot_3": "826e0ad9aec9b04097d58b37", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"pivot_0": "111edafe3adf02dec49c33d9", +"pivot_1": "1b5af616e9ae9638438cff5c", +"pivot_2": "802f8187adaf13eca50630f3", +"pivot_3": "57067ca2b1293776e2d4ca98", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"6232f045280d66a7b86567e9" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -180,29 +180,29 @@ "models", { "orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_21": "7a96226f82f52625a17f825f", +"orders_model_3": "482e641536f8899b756807cf", +"orders_model_6": "526c167cb476b3553de71042", +"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", "orders_model_1": "6f741725ae5781425358bc3b", "orders_model_10": "c7e95ff7d90362e121ab238f", -"orders_model_11": "05479b1f55d5c854f9729a97", -"orders_model_12": "c380ae05f3bd0627628ab6fc", "orders_model_13": "462bf85d3521fa64063b2e26", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", "orders_model_16": "3dbaddb57a2ed5f8db44feda", -"orders_model_17": "7cc87d8c32df82057dc6057d", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", "orders_model_19": "2d58b808566d04184cf9ab29", +"orders_model_22": "74a4ef50a4b351777410e3ae", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_7": "f9be9d8e81fbd30196a2ba14", +"orders_model_11": "05479b1f55d5c854f9729a97", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_17": "7cc87d8c32df82057dc6057d", "orders_model_2": "27b686e0958c19d151cd6319", "orders_model_20": "83395d349a292162557b74ba", -"orders_model_21": "7a96226f82f52625a17f825f", -"orders_model_22": "74a4ef50a4b351777410e3ae", "orders_model_23": "14b86bcb1a8edc065f4c7405", -"orders_model_3": "482e641536f8899b756807cf", -"orders_model_4": "75f6764c319047022968f0b3", "orders_model_5": "07dc61a646b8222df8eccb85", -"orders_model_6": "526c167cb476b3553de71042", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", "orders_model_8": "e88481648db54aa4b3e3b0d6", -"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "335dfa47b776524b62ee26e6", @@ -352,43 +352,43 @@ [ "models", { -"orders_model_0": "1c5f7e1a73535a5617fb070a", -"orders_model_1": "ff37aa9e31c0ddbc7f05cba2", -"orders_model_10": "315f07530cae631f3e373e9d", -"orders_model_11": "05479b1f55d5c854f9729a97", -"orders_model_12": "94f95487333b2561382c2981", -"orders_model_13": "f73e95ff8968d95ed11dc4f3", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", -"orders_model_16": "1a6c68f8be75e24bf3304cdb", -"orders_model_17": "2b9d7cab1383a6532589c1ae", -"orders_model_18": "b1a0e72c622ac940a7b5fb00", -"orders_model_19": "2d58b808566d04184cf9ab29", -"orders_model_2": "27b686e0958c19d151cd6319", -"orders_model_20": "207a0ee79521c1a9b4effb31", -"orders_model_21": "c669dc21fed757d7809fc424", -"orders_model_22": "066b8f5bf2be63a888045fe7", -"orders_model_23": "14b86bcb1a8edc065f4c7405", -"orders_model_3": "482e641536f8899b756807cf", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "0e62e59d1dbc1c842380f957", -"orders_model_6": "c72d1a9250666a5ac7e54369", -"orders_model_7": "f9be9d8e81fbd30196a2ba14", -"orders_model_8": "8454628161253c1b281bb22f", -"orders_model_9": "7c6d6c8fb64f5bfb121ebac1", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "4c1fd04cd9c309cbdf527da8", +"orders_model_15": "84591d33538539fa86025db9", +"orders_model_18": "a966d5cb9da6bf9bf87a7043", +"orders_model_21": "8061cad6eea8e358bc36ea9f", +"orders_model_3": "39ddff27a397d2edc7681f49", +"orders_model_6": "adb7fba2dee76ecb6f89c7ed", +"orders_model_9": "632ce5a31c3bae13f0f4a7e9", +"orders_model_1": "7fe52a2e56fd2728f1d917e1", +"orders_model_10": "40be7e0d35ce48e94f7c8a80", +"orders_model_13": "de8904a0b91b813475635afd", +"orders_model_16": "2adb1fa75191ad27a5e5aaa8", +"orders_model_19": "77f6d40b2301347436ce5deb", +"orders_model_22": "43cf6c5727de2c16cbf69295", +"orders_model_4": "948cc75006d0d6881dcd671b", +"orders_model_7": "28eff25ffabb34f29939d834", +"orders_model_11": "ba98da3a0550982e09af6345", +"orders_model_14": "d2975b92cd134a4ce1bb54f6", +"orders_model_17": "01ee43e0b40835cf94ec53f1", +"orders_model_2": "06e4ba9c2cd3f52747cf0c7c", +"orders_model_20": "2c582a63beeabc38339e22cf", +"orders_model_23": "8761351fa25ea985304bf968", +"orders_model_5": "c71516dc9ea50f798f2fc042", +"orders_model_8": "e31ffd5ea8ae8ebbe58bcfb3", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "335dfa47b776524b62ee26e6", -"pivot_1": "78dd4b3d90301ccd1e781295", -"pivot_2": "3767f4902ed5f3ab1acc83a1", -"pivot_3": "826e0ad9aec9b04097d58b37", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"pivot_0": "8c525598d55de3fed0ddbbde", +"pivot_1": "0860ef89e9ce4066470edeac", +"pivot_2": "868863626dfd7f4708e819ed", +"pivot_3": "38ecf55ef23e6f922714c0b2", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"b749d066f2cc882b93817838" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -526,29 +526,29 @@ "models", { "orders_model_0": "dc4da2070db56afde4157773", +"orders_model_12": "c380ae05f3bd0627628ab6fc", +"orders_model_15": "4ea093ff48f97c35927ecbbd", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_21": "9c26876fe71c235386932362", +"orders_model_3": "482e641536f8899b756807cf", +"orders_model_6": "a903f36871fea6d8ac818922", +"orders_model_9": "67dc0f1b5ce04c8989ad1aca", "orders_model_1": "9b0cc36921b5b8b8a8dea025", "orders_model_10": "d4f810ff3ba14295385d6fb9", -"orders_model_11": "f7cdb34524d45acad3b71f7d", -"orders_model_12": "c380ae05f3bd0627628ab6fc", "orders_model_13": "9f0321d97fb59396dfa496d9", -"orders_model_14": "0506ac9813ca91fe571266cd", -"orders_model_15": "4ea093ff48f97c35927ecbbd", "orders_model_16": "c5a645b8cfb02ddc927fc297", -"orders_model_17": "669b30fefd09621ed0f2ad55", -"orders_model_18": "60e19e6908bb5c06d38eeb01", "orders_model_19": "5318b049f2f651a689fb5e87", +"orders_model_22": "86880e31d61c6c5522256c62", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_7": "a4c400c5fa2da4107098339e", +"orders_model_11": "f7cdb34524d45acad3b71f7d", +"orders_model_14": "0506ac9813ca91fe571266cd", +"orders_model_17": "669b30fefd09621ed0f2ad55", "orders_model_2": "361de61cfd22f09af946862f", "orders_model_20": "032990c645506e1e133c4cd0", -"orders_model_21": "9c26876fe71c235386932362", -"orders_model_22": "86880e31d61c6c5522256c62", "orders_model_23": "209048028aa76bf4c2bd28c6", -"orders_model_3": "482e641536f8899b756807cf", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", "orders_model_5": "e672120be8dbea0cda9c4f28", -"orders_model_6": "a903f36871fea6d8ac818922", -"orders_model_7": "a4c400c5fa2da4107098339e", "orders_model_8": "aba445941b1d72b9d3d5acb6", -"orders_model_9": "67dc0f1b5ce04c8989ad1aca", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "335dfa47b776524b62ee26e6", @@ -570,43 +570,43 @@ [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "73d5e99497d80b919e0d5491", -"orders_model_10": "31ba43d1ed6f0701481c543b", -"orders_model_11": "ac182afebaeb1ab5a7b50a67", -"orders_model_12": "7c1f7afa32f56956c9bb5806", -"orders_model_13": "3676088fa7438b6033da7334", -"orders_model_14": "c5aa1f63d80b5d306477c290", -"orders_model_15": "a533a3838eb00372891616ed", -"orders_model_16": "3122402ab671f4438c8209d7", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "099cb926af524f10d27cc108", -"orders_model_19": "f9be9d8e81fbd30196a2ba14", -"orders_model_2": "3a3bfc6019ae1cf18c714dd0", -"orders_model_20": "c2da59cc5e833d3421094ea9", -"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", -"orders_model_22": "866b4de40862e3cc32f54936", -"orders_model_23": "79c750e0d8bd9a28f05f6337", -"orders_model_3": "f922a0153bfa71727dd6241a", -"orders_model_4": "526ce0a1d0a274582f9d35f1", -"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", -"orders_model_6": "38187a959e7cbbef799b5777", -"orders_model_7": "20835381aab849fd35068525", -"orders_model_8": "72717ea55695667a7b7d8b39", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "7f4e57099dcd7d7c826a1720", +"orders_model_15": "2c80312c2723165af581e438", +"orders_model_18": "0cdaa5055afca4199842df67", +"orders_model_21": "632ce5a31c3bae13f0f4a7e9", +"orders_model_3": "c5d1f198c23a94d1c8c91b87", +"orders_model_6": "417212138029e32a8ed8cca2", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "e614f4493609a3173e1e2f26", +"orders_model_10": "5319ea0ee8d7d215ae85ea93", +"orders_model_13": "4e339dcb27e741a618eef93f", +"orders_model_16": "d04b6cec5e72d70e8fe73f16", +"orders_model_19": "849e77b8ea8ae735aff06dd8", +"orders_model_22": "b44879224a1bec6ebe6172b3", +"orders_model_4": "904fb7a9751e94dd87e783a8", +"orders_model_7": "e0e5ba5c4bd951712a7cfb0c", +"orders_model_11": "5c5bddb4083ebb82397a0656", +"orders_model_14": "da7384ddbf922aaef7f6f423", +"orders_model_17": "876e9e4f776962478c832387", +"orders_model_2": "ad4686c0757fff59d6a6e1dd", +"orders_model_20": "7ad13656ef279929394009ab", +"orders_model_23": "9965a7c90d3cbc180f48295c", +"orders_model_5": "c08a69fbca107fef0028e482", +"orders_model_8": "36ef9fbcf0c8b8874028e4e3", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "db45532b4aa0746bb6155579", -"pivot_1": "826e0ad9aec9b04097d58b37", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "af151ba34f16c141fdee1ac3", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"pivot_0": "9c1e23f4518b773b613dce93", +"pivot_1": "57067ca2b1293776e2d4ca98", +"pivot_2": "1b5af616e9ae9638438cff5c", +"pivot_3": "3023c184a01a785e2b99dd76", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"a27824cf887c032c6fee964b" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -744,29 +744,29 @@ "models", { "orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_12": "7c1f7afa32f56956c9bb5806", +"orders_model_15": "a533a3838eb00372891616ed", +"orders_model_18": "099cb926af524f10d27cc108", +"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", +"orders_model_3": "f922a0153bfa71727dd6241a", +"orders_model_6": "38187a959e7cbbef799b5777", +"orders_model_9": "d34e456ade2ced8792534866", "orders_model_1": "73d5e99497d80b919e0d5491", "orders_model_10": "8ef67d7c6546651d2f2e0b45", -"orders_model_11": "ac182afebaeb1ab5a7b50a67", -"orders_model_12": "7c1f7afa32f56956c9bb5806", "orders_model_13": "cc456ea2e83298c9cd8206fb", -"orders_model_14": "59968e869226ce12e62c6b5e", -"orders_model_15": "a533a3838eb00372891616ed", "orders_model_16": "a6740f24eddec216a1afc653", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "099cb926af524f10d27cc108", "orders_model_19": "f9be9d8e81fbd30196a2ba14", +"orders_model_22": "866b4de40862e3cc32f54936", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_7": "20835381aab849fd35068525", +"orders_model_11": "ac182afebaeb1ab5a7b50a67", +"orders_model_14": "59968e869226ce12e62c6b5e", +"orders_model_17": "769e25e737cf7832171d8837", "orders_model_2": "3a3bfc6019ae1cf18c714dd0", "orders_model_20": "c2da59cc5e833d3421094ea9", -"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", -"orders_model_22": "866b4de40862e3cc32f54936", "orders_model_23": "79c750e0d8bd9a28f05f6337", -"orders_model_3": "f922a0153bfa71727dd6241a", -"orders_model_4": "75f6764c319047022968f0b3", "orders_model_5": "b0f2950e6d0c6c8ba60db1ca", -"orders_model_6": "38187a959e7cbbef799b5777", -"orders_model_7": "20835381aab849fd35068525", "orders_model_8": "5e48e2da1954c3893344d4a8", -"orders_model_9": "d34e456ade2ced8792534866", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "db45532b4aa0746bb6155579", @@ -916,43 +916,43 @@ [ "models", { -"orders_model_0": "1c5f7e1a73535a5617fb070a", -"orders_model_1": "828d747058993aeefdbae18a", -"orders_model_10": "83266a86a028c32724e8cd26", -"orders_model_11": "ac182afebaeb1ab5a7b50a67", -"orders_model_12": "2cbf8e290082ab49db794db4", -"orders_model_13": "3676088fa7438b6033da7334", -"orders_model_14": "59968e869226ce12e62c6b5e", -"orders_model_15": "a533a3838eb00372891616ed", -"orders_model_16": "b67d418ac9424b16909d6a26", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "5d2a8162e9742273846f69e3", -"orders_model_19": "f9be9d8e81fbd30196a2ba14", -"orders_model_2": "3a3bfc6019ae1cf18c714dd0", -"orders_model_20": "c2da59cc5e833d3421094ea9", -"orders_model_21": "7c6d6c8fb64f5bfb121ebac1", -"orders_model_22": "6e4d8e3c08d5ad97e417c6f7", -"orders_model_23": "79c750e0d8bd9a28f05f6337", -"orders_model_3": "f922a0153bfa71727dd6241a", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "b0f2950e6d0c6c8ba60db1ca", -"orders_model_6": "ade3350d63ecf8b7583ca5cb", -"orders_model_7": "20835381aab849fd35068525", -"orders_model_8": "e25266f92d285b11b68983a6", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "7f4e57099dcd7d7c826a1720", +"orders_model_15": "2c80312c2723165af581e438", +"orders_model_18": "0cdaa5055afca4199842df67", +"orders_model_21": "632ce5a31c3bae13f0f4a7e9", +"orders_model_3": "c5d1f198c23a94d1c8c91b87", +"orders_model_6": "417212138029e32a8ed8cca2", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "ede07cb8a81f9ce56f00f42f", +"orders_model_10": "5319ea0ee8d7d215ae85ea93", +"orders_model_13": "e12b2151a9cc473604b38078", +"orders_model_16": "5df128e8d57d789d3741b926", +"orders_model_19": "28eff25ffabb34f29939d834", +"orders_model_22": "b44879224a1bec6ebe6172b3", +"orders_model_4": "948cc75006d0d6881dcd671b", +"orders_model_7": "1c3f8e6db38452fbe711c134", +"orders_model_11": "9d420cd659ba93c596847354", +"orders_model_14": "9f70128e0f0e8caf9642b52f", +"orders_model_17": "85950947160d5a9ce51e5f8b", +"orders_model_2": "da63c044b64f206c8779a11a", +"orders_model_20": "7ad13656ef279929394009ab", +"orders_model_23": "8f696b8e7abb2c488b62f043", +"orders_model_5": "387b9e73946184ed49951e99", +"orders_model_8": "36ef9fbcf0c8b8874028e4e3", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "db45532b4aa0746bb6155579", -"pivot_1": "826e0ad9aec9b04097d58b37", -"pivot_2": "78dd4b3d90301ccd1e781295", -"pivot_3": "b2268fc29a60313e3b8c34ed", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"pivot_0": "9c1e23f4518b773b613dce93", +"pivot_1": "38ecf55ef23e6f922714c0b2", +"pivot_2": "0860ef89e9ce4066470edeac", +"pivot_3": "06c892c6cba0e4f3ba822660", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"db139e73f95bffd34e7a6de5" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1090,29 +1090,29 @@ "models", { "orders_model_0": "dc4da2070db56afde4157773", +"orders_model_12": "b06fcf5168d718f9042eb9f0", +"orders_model_15": "a533a3838eb00372891616ed", +"orders_model_18": "8a322bf0beb93c0034bd28d5", +"orders_model_21": "67dc0f1b5ce04c8989ad1aca", +"orders_model_3": "f922a0153bfa71727dd6241a", +"orders_model_6": "b8c7c62baf6528acb751f82c", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", "orders_model_1": "3e1948d706ff1cecf42f7863", "orders_model_10": "fbdacfcd4b561abe157cdfda", -"orders_model_11": "17515e5f7babd19ac587b1d3", -"orders_model_12": "b06fcf5168d718f9042eb9f0", "orders_model_13": "25952bed7c264fceaeec357c", -"orders_model_14": "c5aa1f63d80b5d306477c290", -"orders_model_15": "a533a3838eb00372891616ed", "orders_model_16": "b67d418ac9424b16909d6a26", -"orders_model_17": "769e25e737cf7832171d8837", -"orders_model_18": "8a322bf0beb93c0034bd28d5", "orders_model_19": "a4c400c5fa2da4107098339e", +"orders_model_22": "866b4de40862e3cc32f54936", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", +"orders_model_11": "17515e5f7babd19ac587b1d3", +"orders_model_14": "c5aa1f63d80b5d306477c290", +"orders_model_17": "769e25e737cf7832171d8837", "orders_model_2": "2f584b5185d90ee4c66c8339", "orders_model_20": "c2da59cc5e833d3421094ea9", -"orders_model_21": "67dc0f1b5ce04c8989ad1aca", -"orders_model_22": "866b4de40862e3cc32f54936", "orders_model_23": "1c16922430c6790814d0a7df", -"orders_model_3": "f922a0153bfa71727dd6241a", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", "orders_model_5": "2f2cbf190f07bea2687a7a1e", -"orders_model_6": "b8c7c62baf6528acb751f82c", -"orders_model_7": "0dbccc6cd571bd9ebefb3c2b", "orders_model_8": "70f77b7d63c1bbe6e602e9bb", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "db45532b4aa0746bb6155579", @@ -1134,43 +1134,43 @@ [ "models", { -"orders_model_0": "dc7f3dec778fd7cc3b47de47", -"orders_model_1": "98e73c916923e6ad3dbb4fe8", -"orders_model_10": "bba235f3ba5a071d31c89cfc", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "7dde13e57109cfb992bc623d", -"orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "2ab277361bc756f8553cd4fc", -"orders_model_15": "37833d5d1b748c8811d3704a", -"orders_model_16": "bf62990d9480cf71956fd3c9", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "288bd78cd1025c86da017d6d", -"orders_model_19": "bc46ad8533a003d33c266a57", -"orders_model_2": "c4c391b60a4436e3a6a9105e", -"orders_model_20": "5660541da0116a33de85cf37", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "9cc8cf8a102827df55622a21", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "d545cb7b8ca26b55d32439c0", -"orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "529410c5a69b04b7172c6dda", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "9a483c2d5942e8857be41233", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "94e60cd6b1a7fcc43f4df828", +"orders_model_15": "c79b92a8c39dafe29b9d3c1b", +"orders_model_18": "4a633de68d595f1c1db5b7ed", +"orders_model_21": "a676dde53da776de16f4fae9", +"orders_model_3": "e248bccac4fe7448d6ffefce", +"orders_model_6": "60f13cc7cb1efade26df391f", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "fc1488a09bf5a315c878509a", +"orders_model_10": "f785e3f7a7bf04d69fe258ff", +"orders_model_13": "060429a246a0568d696e2884", +"orders_model_16": "d99e41de1f2f5367123a86f9", +"orders_model_19": "583d29f78c901b46711eee3f", +"orders_model_22": "f035710c20d8ec38a6895234", +"orders_model_4": "d42726f7b0c3f0d38b8cbf29", +"orders_model_7": "7cb2666289d5475fb8e59816", +"orders_model_11": "fff956d158a47691e012109c", +"orders_model_14": "e0cd6596800c6d659bdb4632", +"orders_model_17": "d5658f434b0e634b215f460b", +"orders_model_2": "3329cf3b13f15869e552faf6", +"orders_model_20": "136b7aa7edf7c54843f89a27", +"orders_model_23": "9e899e72fafb086e86373175", +"orders_model_5": "a64dd7eb640e84a539b03b76", +"orders_model_8": "49aa23b1b8900497327ba778", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "3023c184a01a785e2b99dd76", +"pivot_2": "47dc75892e7ace4f0b7e4d11", +"pivot_3": "802f8187adaf13eca50630f3", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"b2b2221f2da5d47b86b56575" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1308,29 +1308,29 @@ "models", { "orders_model_0": "b21bb72c9b7652fb99f52bf8", +"orders_model_12": "7dde13e57109cfb992bc623d", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_18": "288bd78cd1025c86da017d6d", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_6": "529410c5a69b04b7172c6dda", +"orders_model_9": "0b71c12f30a7617191bc2e17", "orders_model_1": "98e73c916923e6ad3dbb4fe8", "orders_model_10": "bba235f3ba5a071d31c89cfc", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "7dde13e57109cfb992bc623d", "orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "37833d5d1b748c8811d3704a", "orders_model_16": "24d75028ccc9de7633328c96", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "288bd78cd1025c86da017d6d", "orders_model_19": "bc46ad8533a003d33c266a57", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_4": "d545cb7b8ca26b55d32439c0", +"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_17": "cb0cef5752047e0f1201dfb4", "orders_model_2": "2c4a0d5f37d1033435dc44b8", "orders_model_20": "5660541da0116a33de85cf37", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "9cc8cf8a102827df55622a21", "orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "d545cb7b8ca26b55d32439c0", "orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "529410c5a69b04b7172c6dda", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", "orders_model_8": "692f18d932cd76c441a79e81", -"orders_model_9": "0b71c12f30a7617191bc2e17", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -1480,43 +1480,43 @@ [ "models", { -"orders_model_0": "b09efd1e7181db7773253200", -"orders_model_1": "f8df97502ce8987ade2b8ae6", -"orders_model_10": "d9e87a16e347f6f0316452d6", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "1c5f7e1a73535a5617fb070a", -"orders_model_13": "528bfc7a27293f659b6eec35", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "37833d5d1b748c8811d3704a", -"orders_model_16": "0eca13897e0abb4131837a39", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "74eb746be2a4278dae684968", -"orders_model_19": "bc46ad8533a003d33c266a57", -"orders_model_2": "2c4a0d5f37d1033435dc44b8", -"orders_model_20": "5660541da0116a33de85cf37", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "83e085c3265373904422aad9", -"orders_model_23": "ccf2498a5b3526260f88df6f", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "a249acc516a880c9fbe25e88", -"orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "c88625738d77bd28714c4086", -"orders_model_7": "a3f7c2aae3ece4d7de21b9ef", -"orders_model_8": "ec0818249ce5c1a3abdbba12", -"orders_model_9": "0b71c12f30a7617191bc2e17", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_0": "b02026cdc6a38ff5f06fe2a9", +"orders_model_12": "94e60cd6b1a7fcc43f4df828", +"orders_model_15": "c79b92a8c39dafe29b9d3c1b", +"orders_model_18": "f1077a241bf5da058ecea8e2", +"orders_model_21": "a676dde53da776de16f4fae9", +"orders_model_3": "e248bccac4fe7448d6ffefce", +"orders_model_6": "e97a70a6d29697dc7a5bb026", +"orders_model_9": "532e66080e2fcc53c4a30af2", +"orders_model_1": "8c55008327247d32fc23abdc", +"orders_model_10": "f785e3f7a7bf04d69fe258ff", +"orders_model_13": "8af3805074512d064fb865a5", +"orders_model_16": "f4e41df89fd84a43e52ce29a", +"orders_model_19": "ae659831ce6aa9820e6e8a9d", +"orders_model_22": "f035710c20d8ec38a6895234", +"orders_model_4": "34b0a9652b09c53e5aa314b0", +"orders_model_7": "babbbcc1ecf5c297157a9cd5", +"orders_model_11": "62cfce559364ea76891d3353", +"orders_model_14": "bb6b56bc63c9f77c726d9626", +"orders_model_17": "ebe63fadd57b7d3b95afb29b", +"orders_model_2": "9c31136ccdc1bdd3517206e6", +"orders_model_20": "136b7aa7edf7c54843f89a27", +"orders_model_23": "bd51523ed414294d0d2b1d32", +"orders_model_5": "5f23178435c57cdd114faec7", +"orders_model_8": "9463c8cf10ef90862accbdfb", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "b2268fc29a60313e3b8c34ed", -"pivot_2": "5cf2515c32c9c98b0a9d8315", -"pivot_3": "3767f4902ed5f3ab1acc83a1", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "06c892c6cba0e4f3ba822660", +"pivot_2": "d1bbd23f4694ce0d97f5c165", +"pivot_3": "868863626dfd7f4708e819ed", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"01ad0f760ce6f305ea550857" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1654,29 +1654,29 @@ "models", { "orders_model_0": "36669f325385a145cfe71921", +"orders_model_12": "dc4da2070db56afde4157773", +"orders_model_15": "37833d5d1b748c8811d3704a", +"orders_model_18": "8cf38d3ff37cd8143561920a", +"orders_model_21": "bbb7494cbb82085e1c034f21", +"orders_model_3": "2acc6b35910a7b7c1e29d661", +"orders_model_6": "0541825c1a3bbc51c23f3a53", +"orders_model_9": "0b71c12f30a7617191bc2e17", "orders_model_1": "45de3ae2081edb1155411120", "orders_model_10": "bba235f3ba5a071d31c89cfc", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "dc4da2070db56afde4157773", "orders_model_13": "87a319b5caf7cc32bc6aa50e", -"orders_model_14": "611ea76d354e7180f2fe0906", -"orders_model_15": "37833d5d1b748c8811d3704a", "orders_model_16": "8ef4aea858f63561f24ec68b", -"orders_model_17": "cb0cef5752047e0f1201dfb4", -"orders_model_18": "8cf38d3ff37cd8143561920a", "orders_model_19": "dcb9d89d4b588b53682fb0b4", +"orders_model_22": "9cc8cf8a102827df55622a21", +"orders_model_4": "a249acc516a880c9fbe25e88", +"orders_model_7": "526a9e7adadbf085a39764b5", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_14": "611ea76d354e7180f2fe0906", +"orders_model_17": "cb0cef5752047e0f1201dfb4", "orders_model_2": "2c4a0d5f37d1033435dc44b8", "orders_model_20": "5660541da0116a33de85cf37", -"orders_model_21": "bbb7494cbb82085e1c034f21", -"orders_model_22": "9cc8cf8a102827df55622a21", "orders_model_23": "857c9a58882bddd68cec3bd5", -"orders_model_3": "2acc6b35910a7b7c1e29d661", -"orders_model_4": "a249acc516a880c9fbe25e88", "orders_model_5": "ad3b7e6c56a628aff0799f39", -"orders_model_6": "0541825c1a3bbc51c23f3a53", -"orders_model_7": "526a9e7adadbf085a39764b5", "orders_model_8": "3bf1aff8cb84ab7999a0b5fd", -"orders_model_9": "0b71c12f30a7617191bc2e17", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -1698,43 +1698,43 @@ [ "models", { -"orders_model_0": "7dde13e57109cfb992bc623d", -"orders_model_1": "d7498e8ac1b0afeb189ff727", -"orders_model_10": "264e63bb81a8673659bbb9db", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "dc7f3dec778fd7cc3b47de47", -"orders_model_13": "35b1097a860faac43e81b044", -"orders_model_14": "28b94fa5aaa6225fd8809740", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", -"orders_model_16": "f386cc5235b54c550f4924f8", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", -"orders_model_2": "3c07dff3140b081ee33fd13e", -"orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "fbd144b873208a2bd80a35a4", -"orders_model_22": "4e068f2ba3ce583e4d5b6006", -"orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "526ce0a1d0a274582f9d35f1", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "36c156c5a6601badf8124464", -"orders_model_7": "ef0eb5ded0428f49ac6cb411", -"orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "b02026cdc6a38ff5f06fe2a9", +"orders_model_15": "ef6ebc916ab6d71b7525d23f", +"orders_model_18": "aea7224659a833d20f777865", +"orders_model_21": "496ef0c368c89fe73a7b69ff", +"orders_model_3": "0d11445c74305b9100560c9c", +"orders_model_6": "e64a458d11ef75ef9e32b2fb", +"orders_model_9": "0a80765a57a9744602000dff", +"orders_model_1": "c5b54699c7681cdeeebe6d97", +"orders_model_10": "554ba2fe9ec0480f2c848f60", +"orders_model_13": "7bac87596d1672636fbfc359", +"orders_model_16": "106aadd0ff457f2f228fec77", +"orders_model_19": "a10d88c3055fd182742c409e", +"orders_model_22": "93f305f8baf363528a9b1f5f", +"orders_model_4": "904fb7a9751e94dd87e783a8", +"orders_model_7": "1931ea05cdcf5fb03a08a45a", +"orders_model_11": "fff956d158a47691e012109c", +"orders_model_14": "cc0aba8ad07238f59b805db1", +"orders_model_17": "c289af2f3331d5c54ff0dfb1", +"orders_model_2": "e1603185bab5288bd9cad27b", +"orders_model_20": "6b41843732caf5f5b95f0579", +"orders_model_23": "df13d253f68d3d484b06fcb8", +"orders_model_5": "c289af2f3331d5c54ff0dfb1", +"orders_model_8": "2bf96e35794854fa861c4c7b", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "bd6145283beb468ce86b460b", -"pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "af151ba34f16c141fdee1ac3", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"pivot_0": "5fb7483692a07a7a31a2ef9b", +"pivot_1": "712ce9a95b98693096395334", +"pivot_2": "70e69b1adee7eb5488b1f3a6", +"pivot_3": "3023c184a01a785e2b99dd76", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"4f98b4e1b4999a33ba1ff099" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -1872,29 +1872,29 @@ "models", { "orders_model_0": "7dde13e57109cfb992bc623d", +"orders_model_12": "b21bb72c9b7652fb99f52bf8", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_21": "fbd144b873208a2bd80a35a4", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_6": "777e18bfdcc2423b67211d39", +"orders_model_9": "338aef528aa5f199e2ffa973", "orders_model_1": "d7498e8ac1b0afeb189ff727", "orders_model_10": "264e63bb81a8673659bbb9db", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "b21bb72c9b7652fb99f52bf8", "orders_model_13": "35b1097a860faac43e81b044", -"orders_model_14": "28b94fa5aaa6225fd8809740", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", "orders_model_16": "f386cc5235b54c550f4924f8", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "6b0c7fdf2bf74107bd96a6ff", "orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", +"orders_model_22": "4e068f2ba3ce583e4d5b6006", +"orders_model_4": "75f6764c319047022968f0b3", +"orders_model_7": "80a1f1b7b38c1afa802d7625", +"orders_model_11": "b92be214f33c9cf71eea3bc2", +"orders_model_14": "28b94fa5aaa6225fd8809740", +"orders_model_17": "60e141a5769c44084f76723b", "orders_model_2": "3c07dff3140b081ee33fd13e", "orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "fbd144b873208a2bd80a35a4", -"orders_model_22": "4e068f2ba3ce583e4d5b6006", "orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "75f6764c319047022968f0b3", "orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "777e18bfdcc2423b67211d39", -"orders_model_7": "80a1f1b7b38c1afa802d7625", "orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "338aef528aa5f199e2ffa973", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "bd6145283beb468ce86b460b", @@ -2044,43 +2044,43 @@ [ "models", { -"orders_model_0": "1c5f7e1a73535a5617fb070a", -"orders_model_1": "673e75c341c3f2437091de09", -"orders_model_10": "0df969272aa19a78a705bc92", -"orders_model_11": "b92be214f33c9cf71eea3bc2", -"orders_model_12": "b09efd1e7181db7773253200", -"orders_model_13": "bc666dca3ca9016f640dfe68", -"orders_model_14": "7c3ab71786810446d45e97a3", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", -"orders_model_16": "cf4db989e60b4ff2ee7b8b90", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "b1a0e72c622ac940a7b5fb00", -"orders_model_19": "ec3c3ec5fbf35ddc59fd77ca", -"orders_model_2": "3c07dff3140b081ee33fd13e", -"orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "59228560ec9692b57930d8b5", -"orders_model_22": "b8238ed1e26320d8f8912882", -"orders_model_23": "8942d18a241e8dcd1831addc", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "1cefa36f28721fe677e6a684", -"orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "3197497d139b9241f4b6e04c", +"orders_model_0": "94e60cd6b1a7fcc43f4df828", +"orders_model_12": "b02026cdc6a38ff5f06fe2a9", +"orders_model_15": "ef6ebc916ab6d71b7525d23f", +"orders_model_18": "a966d5cb9da6bf9bf87a7043", +"orders_model_21": "496ef0c368c89fe73a7b69ff", +"orders_model_3": "0d11445c74305b9100560c9c", +"orders_model_6": "3bc5580c26c91e8c5a2c7ed7", +"orders_model_9": "0a80765a57a9744602000dff", +"orders_model_1": "050954c57ddb4444f9cc1103", +"orders_model_10": "a2617416d1e573e91ad6d556", +"orders_model_13": "91de85a50b3e7d95285e9ef5", +"orders_model_16": "7c196cbcfd446eaf0650186e", +"orders_model_19": "42554e8420c2bd13558da43e", +"orders_model_22": "f0152946381d8c2aaf939503", +"orders_model_4": "948cc75006d0d6881dcd671b", "orders_model_7": "44dc5de3912695d88a818a03", -"orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "338aef528aa5f199e2ffa973", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_11": "62cfce559364ea76891d3353", +"orders_model_14": "a162dc6cd78d2f8ce0da45bb", +"orders_model_17": "cec4287287b33c590d00a0bb", +"orders_model_2": "af07bae49ecbfb94bebce4ef", +"orders_model_20": "715261ceed0a226d62b43387", +"orders_model_23": "3be8631ce21962bab983795c", +"orders_model_5": "cec4287287b33c590d00a0bb", +"orders_model_8": "6c91b6c3227c14ab77f77f0e", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "bd6145283beb468ce86b460b", -"pivot_1": "8eb04463a7e8015fcc9b8a63", -"pivot_2": "18ecbe5e93fe3ced5cbfd24a", -"pivot_3": "b2268fc29a60313e3b8c34ed", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"pivot_0": "b630f6bbcf705fcab4076ffb", +"pivot_1": "f76cad7a83d99f2c3ad7754f", +"pivot_2": "4cd97646890bc4d292590af0", +"pivot_3": "06c892c6cba0e4f3ba822660", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"74156f1f4b47943ba3b769dd" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -2218,29 +2218,29 @@ "models", { "orders_model_0": "dc4da2070db56afde4157773", +"orders_model_12": "36669f325385a145cfe71921", +"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", +"orders_model_18": "60e19e6908bb5c06d38eeb01", +"orders_model_21": "176a688a47e60b55e1ce0673", +"orders_model_3": "527338f30a7ba57175c3a525", +"orders_model_6": "c2b91215ea727d9313a22740", +"orders_model_9": "283c165f2da9102ffec377c4", "orders_model_1": "b6345243b4bb0009ec2dfb5d", "orders_model_10": "264e63bb81a8673659bbb9db", -"orders_model_11": "29c9da29acf1adc409e364df", -"orders_model_12": "36669f325385a145cfe71921", "orders_model_13": "87a1dbf609e37b67c90e35b2", -"orders_model_14": "544d358c786985653996181d", -"orders_model_15": "a2c41a95e7f2097d2e9b9bd1", "orders_model_16": "cf4db989e60b4ff2ee7b8b90", -"orders_model_17": "60e141a5769c44084f76723b", -"orders_model_18": "60e19e6908bb5c06d38eeb01", "orders_model_19": "ab249088d32d341a013be687", +"orders_model_22": "c6255585e2f5156964d8f8f8", +"orders_model_4": "4c809f6cf2e946ba98b1ec73", +"orders_model_7": "afb5b3102eaac7fa202355c0", +"orders_model_11": "29c9da29acf1adc409e364df", +"orders_model_14": "544d358c786985653996181d", +"orders_model_17": "60e141a5769c44084f76723b", "orders_model_2": "3c07dff3140b081ee33fd13e", "orders_model_20": "85ea595b45b20d75c0d8df80", -"orders_model_21": "176a688a47e60b55e1ce0673", -"orders_model_22": "c6255585e2f5156964d8f8f8", "orders_model_23": "e0ec1521e19c47996421ec94", -"orders_model_3": "527338f30a7ba57175c3a525", -"orders_model_4": "4c809f6cf2e946ba98b1ec73", "orders_model_5": "60e141a5769c44084f76723b", -"orders_model_6": "c2b91215ea727d9313a22740", -"orders_model_7": "afb5b3102eaac7fa202355c0", "orders_model_8": "1d2ab23b591c4ca7f5e6359b", -"orders_model_9": "283c165f2da9102ffec377c4", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "bd6145283beb468ce86b460b", @@ -2262,43 +2262,43 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "abadf69a0572399b6536a2c1", -"orders_model_10": "c46ffece29c5df800b8f74df", -"orders_model_11": "8003e1680dc046032ceea404", -"orders_model_12": "edf57c58d750226a747455ba", -"orders_model_13": "278d4fd453aea2eca14894ea", -"orders_model_14": "e51070f44666248f5b7764b3", -"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", -"orders_model_16": "eeaefdbd50fdc13681d8653a", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "5ccadf27b4bf1a37e0f30682", -"orders_model_19": "5ab5711e085cc014f3e44950", -"orders_model_2": "835e887650bbf9b124ae26b1", -"orders_model_20": "12b73be6a6b5985f71de1ca5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "91d92b539582dec7737d1704", -"orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "e29709e8e7acb44cc782304f", -"orders_model_5": "afd6d68f0a0a6689404e0260", -"orders_model_6": "099cb926af524f10d27cc108", -"orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "6be36f8074a475ab3f5bce6c", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "6792391ba81748e33b725b02", +"orders_model_18": "ab2d50eea2c1144d8f9b5b91", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "0cdaa5055afca4199842df67", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "a01f8e4ec7b620ce17dfeb9a", +"orders_model_10": "43cd3fa513c204d3a57fa901", +"orders_model_13": "d6da1af6620de8c0ed4518e5", +"orders_model_16": "d40501d996ca880cd8f3bad0", +"orders_model_19": "6e2108808bec1c8e26780f24", +"orders_model_22": "d31e6dac331e1b53f7b0c1a5", +"orders_model_4": "cad99cc00c4b5d4cf42f7b64", +"orders_model_7": "68b2a84748bad7d3a4c3f42b", +"orders_model_11": "930d3b19fc9c4090e18bb8aa", +"orders_model_14": "d05c196335877deb6b2eb46e", +"orders_model_17": "32e11315a2d0ae43b0f30c56", +"orders_model_2": "a1a708f0e0e9cbf14f7ba3a7", +"orders_model_20": "425a516cf49ced70f18550bf", +"orders_model_23": "ff895a8a83c9e758fbc37bf5", +"orders_model_5": "8c62f0737cfba7e68b86ae43", +"orders_model_8": "e0b9f79671eccfa779e360be", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "af151ba34f16c141fdee1ac3", -"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "3023c184a01a785e2b99dd76", +"pivot_2": "4201810ec2a2caba872d3cea", +"pivot_3": "e836d6def127c9a6d051ebda", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"c7b70a4fc469694d9d64eff8" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -2436,29 +2436,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_18": "f73c7edc9f35cb7956a40b52", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_6": "099cb926af524f10d27cc108", +"orders_model_9": "d34e456ade2ced8792534866", "orders_model_1": "a23763739c27a47ae12ebed2", "orders_model_10": "c46ffece29c5df800b8f74df", -"orders_model_11": "8003e1680dc046032ceea404", -"orders_model_12": "edf57c58d750226a747455ba", "orders_model_13": "56d09679844a976541cce33c", -"orders_model_14": "e51070f44666248f5b7764b3", -"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", "orders_model_16": "eeaefdbd50fdc13681d8653a", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "f73c7edc9f35cb7956a40b52", "orders_model_19": "5ab5711e085cc014f3e44950", +"orders_model_22": "2853fca6c5782a3d8f7f3229", +"orders_model_4": "c8c4091a11671ad7035944d6", +"orders_model_7": "a36493d57a16c1f79a91afd6", +"orders_model_11": "8003e1680dc046032ceea404", +"orders_model_14": "e51070f44666248f5b7764b3", +"orders_model_17": "3515712bb3c936c534706554", "orders_model_2": "835e887650bbf9b124ae26b1", "orders_model_20": "12b73be6a6b5985f71de1ca5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "2853fca6c5782a3d8f7f3229", "orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "c8c4091a11671ad7035944d6", "orders_model_5": "afd6d68f0a0a6689404e0260", -"orders_model_6": "099cb926af524f10d27cc108", -"orders_model_7": "a36493d57a16c1f79a91afd6", "orders_model_8": "8a583d5a0fe333e4583746b2", -"orders_model_9": "d34e456ade2ced8792534866", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -2608,43 +2608,43 @@ [ "models", { -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "ab25f3ba101f081f8a3f936c", -"orders_model_10": "3aec7a059badfa0e968d5216", -"orders_model_11": "8003e1680dc046032ceea404", -"orders_model_12": "cb6d3e8487b533349a2bad04", -"orders_model_13": "d55e6b7bb6533cea41bcfeda", -"orders_model_14": "e51070f44666248f5b7764b3", -"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", -"orders_model_16": "97e322d50d70c30c61fb7a15", -"orders_model_17": "3515712bb3c936c534706554", -"orders_model_18": "32b8af78b243949e0198fda6", -"orders_model_19": "5ab5711e085cc014f3e44950", -"orders_model_2": "345f166deff852e9116d8801", -"orders_model_20": "12b73be6a6b5985f71de1ca5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "368c97dc5eea624b492ce042", -"orders_model_23": "c8ff9a2130c4a7a57073fb6d", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "7565624f8000d1c0566d8ed3", -"orders_model_5": "ee556ea5f790f1d885cee84a", -"orders_model_6": "5d2a8162e9742273846f69e3", -"orders_model_7": "a36493d57a16c1f79a91afd6", -"orders_model_8": "c7d3f9e5025dad577c958a44", -"orders_model_9": "d34e456ade2ced8792534866", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "853a59f0d6c2dc42ed683ea4", +"orders_model_15": "6792391ba81748e33b725b02", +"orders_model_18": "ff113261ecb1302e1768b19e", +"orders_model_21": "532e66080e2fcc53c4a30af2", +"orders_model_3": "eca2536638ffea1aabfc927b", +"orders_model_6": "0cdaa5055afca4199842df67", +"orders_model_9": "caa8198e7e417423bacef68e", +"orders_model_1": "4f8e1dc514d29636e414c5d8", +"orders_model_10": "43cd3fa513c204d3a57fa901", +"orders_model_13": "3cd5e90f96a677b770ecfc3b", +"orders_model_16": "d429b5a2405839eec25c811c", +"orders_model_19": "9e0f3ba46f59d75ad544727a", +"orders_model_22": "3e050ff8096d65ad3bcf9c09", +"orders_model_4": "e776c54bbf64f9efbcffb86a", +"orders_model_7": "1baf594884e8b799007edf2c", +"orders_model_11": "3c52b383636f30ce044a39e6", +"orders_model_14": "d0bf04d1c170cf049059b503", +"orders_model_17": "e73f3fa095e925032a0f493c", +"orders_model_2": "9317eb65f36a14dd5181ef82", +"orders_model_20": "425a516cf49ced70f18550bf", +"orders_model_23": "9b5a54a902e9acadf76a4194", +"orders_model_5": "491a1c5bb2ab02a6131a515b", +"orders_model_8": "e705d9189173555c5e6902ea", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "8ef48104ced62eb9ff31c2b8", -"pivot_1": "b2268fc29a60313e3b8c34ed", -"pivot_2": "5adb7a56c3dfd3d1c3cf58c1", -"pivot_3": "00a890bbfc402b53ea021e5a", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"pivot_0": "f6d0f49ebaa5ca5614d8fdb6", +"pivot_1": "06c892c6cba0e4f3ba822660", +"pivot_2": "d8cb46c8edcfe87f37163643", +"pivot_3": "8cbdd393ed22a6eee2bfc26a", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"08a4d5d6c6bf5e75b2e23d81" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -2782,29 +2782,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "edf57c58d750226a747455ba", +"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", +"orders_model_18": "fd5138ac4af02cd168a423e6", +"orders_model_21": "0b71c12f30a7617191bc2e17", +"orders_model_3": "a5927681bfbf9d44f4723990", +"orders_model_6": "8a322bf0beb93c0034bd28d5", +"orders_model_9": "9677ddb44ed9870eeb1fdac1", "orders_model_1": "c7630eba442c12475a304749", "orders_model_10": "fa26ab8510354dd670525ac2", -"orders_model_11": "eb7d097be111399bcd2f940f", -"orders_model_12": "edf57c58d750226a747455ba", "orders_model_13": "ab1f9c5b6ef07ffa40eeadaf", -"orders_model_14": "273c339ab90e9a64a7c8f8a5", -"orders_model_15": "88ec6fdf0edc3ff0a0acb78c", "orders_model_16": "e018db5f6e3eaf8888bd8eff", -"orders_model_17": "fe03c6a89b870943b6dd5922", -"orders_model_18": "fd5138ac4af02cd168a423e6", "orders_model_19": "e24f113150b49c7e955cf81b", +"orders_model_22": "24bdd574c66fd2f692429cfb", +"orders_model_4": "a908ebc07c10d1423a970a49", +"orders_model_7": "4983a5e9302c8b9e28e75202", +"orders_model_11": "eb7d097be111399bcd2f940f", +"orders_model_14": "273c339ab90e9a64a7c8f8a5", +"orders_model_17": "fe03c6a89b870943b6dd5922", "orders_model_2": "daca571af4a4d1cb1afbce74", "orders_model_20": "12b73be6a6b5985f71de1ca5", -"orders_model_21": "0b71c12f30a7617191bc2e17", -"orders_model_22": "24bdd574c66fd2f692429cfb", "orders_model_23": "e94cdbeab01617cd05a55f82", -"orders_model_3": "a5927681bfbf9d44f4723990", -"orders_model_4": "a908ebc07c10d1423a970a49", "orders_model_5": "7af136b7e522ccf14f427e1e", -"orders_model_6": "8a322bf0beb93c0034bd28d5", -"orders_model_7": "4983a5e9302c8b9e28e75202", "orders_model_8": "8a583d5a0fe333e4583746b2", -"orders_model_9": "9677ddb44ed9870eeb1fdac1", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "8ef48104ced62eb9ff31c2b8", @@ -2826,43 +2826,43 @@ [ "models", { -"orders_model_0": "c380ae05f3bd0627628ab6fc", -"orders_model_1": "c909142a40bfa94a0849e672", -"orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "5f4d57a90f61f48a39c3f45c", -"orders_model_12": "159cce49ef0548e95a5b46fe", -"orders_model_13": "ba7bafc812a816c0c0770d82", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", -"orders_model_16": "e29709e8e7acb44cc782304f", -"orders_model_17": "40ca546bd222f4bd18e62461", -"orders_model_18": "f913dcd698fd61a9df4580a9", -"orders_model_19": "16fc9c0348ca5eadfd147e95", -"orders_model_2": "be51e7ded79e692b0f86ad8e", -"orders_model_20": "4a708ce5c2d7f50400bef81e", -"orders_model_21": "32e2f46b53e4361d34fb2f95", -"orders_model_22": "ca95b1a1313ab6e0c86f22d0", -"orders_model_23": "6423a33f3457191fa1b24540", -"orders_model_3": "fd09d2408d6ef7eceb04f8e3", -"orders_model_4": "40daca9947aaa0922736d9ca", -"orders_model_5": "efc04632c3bd90f5ca705b61", -"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "ce5efbe45ba9958d07abe4be", +"orders_model_15": "b2fd9a5a8fcc2fd4b501594e", +"orders_model_18": "bcaa1a8b281edaf085e71f99", +"orders_model_21": "8b48e953b842468f8a4b7330", +"orders_model_3": "da6fb6ea5f7e62f158a9171d", +"orders_model_6": "aea7224659a833d20f777865", +"orders_model_9": "4c22b437652fb7f416ad4417", +"orders_model_1": "ca9699ee8d950158abf9275c", +"orders_model_10": "55dfdc7e25361c194ca7a2b2", +"orders_model_13": "072997a4b27ecdc471698510", +"orders_model_16": "cad99cc00c4b5d4cf42f7b64", +"orders_model_19": "ca188e5303c858aafc792993", +"orders_model_22": "d50d6963b3d3c9908843697b", +"orders_model_4": "bb70da9e97096f272dd0edeb", +"orders_model_7": "eb9a44efb89bd577daf4b9bb", +"orders_model_11": "884b5e4619894a1f2dca4e75", +"orders_model_14": "08c324156e8fbdd6ee5b8c34", +"orders_model_17": "77cae24a023f421e79bd8d6b", +"orders_model_2": "8650e077152ed53580daab6e", +"orders_model_20": "b414f89d27f5b1ec36d9bfa1", +"orders_model_23": "706bf82e63aa10076edfd1c5", +"orders_model_5": "db3f24143f0d658fa42a0e8c", "orders_model_8": "653dbee5db7422883147ecf8", -"orders_model_9": "e2d21c84b2b62dea98a1d185", -"payment_totals": "8cf82ad30e9cce84bc8a3529", +"payment_totals": "09523c9ed08b78a006a96743", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "af151ba34f16c141fdee1ac3", -"pivot_1": "bd6145283beb468ce86b460b", -"pivot_2": "db45532b4aa0746bb6155579", -"pivot_3": "5cf2515c32c9c98b0a9d8315", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "57dbca31518ecf81d68ec4fe" +"pivot_0": "3023c184a01a785e2b99dd76", +"pivot_1": "5fb7483692a07a7a31a2ef9b", +"pivot_2": "9c1e23f4518b773b613dce93", +"pivot_3": "47dc75892e7ace4f0b7e4d11", +"status_amounts": "9b4977bb6f213498c985226b", +"status_passthrough": "cd8b6c8470557dc1d97cf1b4" } ], [ "catalog", -"f877c81e569d46b2f31adb8a" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -3000,29 +3000,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "eb36534deed90e9e2fb062de", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_18": "f913dcd698fd61a9df4580a9", +"orders_model_21": "32e2f46b53e4361d34fb2f95", +"orders_model_3": "fd09d2408d6ef7eceb04f8e3", +"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", +"orders_model_9": "e2d21c84b2b62dea98a1d185", "orders_model_1": "c909142a40bfa94a0849e672", "orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "5f4d57a90f61f48a39c3f45c", -"orders_model_12": "eb36534deed90e9e2fb062de", "orders_model_13": "b929a7472b014a9b5b757127", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", "orders_model_16": "c8c4091a11671ad7035944d6", -"orders_model_17": "40ca546bd222f4bd18e62461", -"orders_model_18": "f913dcd698fd61a9df4580a9", "orders_model_19": "16fc9c0348ca5eadfd147e95", +"orders_model_22": "ca95b1a1313ab6e0c86f22d0", +"orders_model_4": "40daca9947aaa0922736d9ca", +"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_11": "5f4d57a90f61f48a39c3f45c", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_17": "40ca546bd222f4bd18e62461", "orders_model_2": "be51e7ded79e692b0f86ad8e", "orders_model_20": "4a708ce5c2d7f50400bef81e", -"orders_model_21": "32e2f46b53e4361d34fb2f95", -"orders_model_22": "ca95b1a1313ab6e0c86f22d0", "orders_model_23": "6423a33f3457191fa1b24540", -"orders_model_3": "fd09d2408d6ef7eceb04f8e3", -"orders_model_4": "40daca9947aaa0922736d9ca", "orders_model_5": "efc04632c3bd90f5ca705b61", -"orders_model_6": "6b0c7fdf2bf74107bd96a6ff", -"orders_model_7": "f1d838e10071f9b219bf07c6", "orders_model_8": "85993844a344d5d4408276d0", -"orders_model_9": "e2d21c84b2b62dea98a1d185", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "e406a89772022ea3c32aefef", "pivot_0": "e6d82b48a7a5593bd747ea69", @@ -3172,43 +3172,43 @@ [ "models", { -"orders_model_0": "94f95487333b2561382c2981", -"orders_model_1": "d69e281f2ec58a1c2cfc8e75", -"orders_model_10": "07a1b43e2e1aec3a941c7463", -"orders_model_11": "5f4d57a90f61f48a39c3f45c", -"orders_model_12": "e3d5141e99608c2ca5d0df9b", -"orders_model_13": "8bfac0eb02357c4ef7316ba0", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", -"orders_model_16": "7565624f8000d1c0566d8ed3", -"orders_model_17": "185a865c8cf0d67b3027a313", -"orders_model_18": "4ed6b441d50e250d458a67ec", -"orders_model_19": "16fc9c0348ca5eadfd147e95", -"orders_model_2": "be51e7ded79e692b0f86ad8e", -"orders_model_20": "59129bc02b2fd6f6f8bfb348", -"orders_model_21": "88425997d70bb43627dba42c", -"orders_model_22": "14091564e5091161047f133c", -"orders_model_23": "6423a33f3457191fa1b24540", -"orders_model_3": "fd09d2408d6ef7eceb04f8e3", -"orders_model_4": "2cfd6426be9711fc3a185538", -"orders_model_5": "1e772c63e569addeecf15a27", -"orders_model_6": "b1a0e72c622ac940a7b5fb00", -"orders_model_7": "f1d838e10071f9b219bf07c6", +"orders_model_0": "4c1fd04cd9c309cbdf527da8", +"orders_model_12": "ce5efbe45ba9958d07abe4be", +"orders_model_15": "b2fd9a5a8fcc2fd4b501594e", +"orders_model_18": "bcaa1a8b281edaf085e71f99", +"orders_model_21": "8b48e953b842468f8a4b7330", +"orders_model_3": "da6fb6ea5f7e62f158a9171d", +"orders_model_6": "a966d5cb9da6bf9bf87a7043", +"orders_model_9": "4c22b437652fb7f416ad4417", +"orders_model_1": "6fb5cb3c2a3da8e849f3c215", +"orders_model_10": "3e279fbf4d2f8b6bb22573c9", +"orders_model_13": "18224653a131c203f5850667", +"orders_model_16": "e776c54bbf64f9efbcffb86a", +"orders_model_19": "cca068c36904ee6a40522cb7", +"orders_model_22": "d50d6963b3d3c9908843697b", +"orders_model_4": "696923119a29a646a62f106a", +"orders_model_7": "0d946e5630e41164201fa20e", +"orders_model_11": "12274bd6b0ca24efa5f74e94", +"orders_model_14": "56417b56a4e7a0b8b49d6f8f", +"orders_model_17": "1943f6b997fb167a34c3cdbb", +"orders_model_2": "fc5c5953ac3044bb535d6835", +"orders_model_20": "b414f89d27f5b1ec36d9bfa1", +"orders_model_23": "2d01394da7a6e9d7b150b211", +"orders_model_5": "20983af890f0296ff103a3cf", "orders_model_8": "653dbee5db7422883147ecf8", -"orders_model_9": "982d87b5a15b54ee70c70755", -"payment_totals": "80bd8eb5eab6ed918ec31949", +"payment_totals": "06adf350696650d18756caf9", "payments_by_customer": "9e1265b9f4134636cb508259", -"pivot_0": "b2268fc29a60313e3b8c34ed", -"pivot_1": "bd6145283beb468ce86b460b", -"pivot_2": "db45532b4aa0746bb6155579", -"pivot_3": "5cf2515c32c9c98b0a9d8315", -"status_amounts": "0fbfc8d810e430c5814769d6", -"status_passthrough": "bbd8b7298ce68a222ba84345" +"pivot_0": "06c892c6cba0e4f3ba822660", +"pivot_1": "b630f6bbcf705fcab4076ffb", +"pivot_2": "9c1e23f4518b773b613dce93", +"pivot_3": "d1bbd23f4694ce0d97f5c165", +"status_amounts": "9ad70da471c2070adfce8826", +"status_passthrough": "15c7374b0ebb45265a111112" } ], [ "catalog", -"3eb464018675acfdd12a461f" +"6b62b5735b9ed6e6e71ab2bb" ], [ "proof", @@ -3346,29 +3346,29 @@ "models", { "orders_model_0": "c380ae05f3bd0627628ab6fc", +"orders_model_12": "bd31b96a3fa2e1bb80699d6e", +"orders_model_15": "e83a2c02763b0f51b49dbfe1", +"orders_model_18": "02d5138c4e7513182fc7cd52", +"orders_model_21": "08e7a885c1d9662b2678c3ea", +"orders_model_3": "fd09d2408d6ef7eceb04f8e3", +"orders_model_6": "60e19e6908bb5c06d38eeb01", +"orders_model_9": "05bfc38d1bf1cc771029edec", "orders_model_1": "18de106324e6bd649e9a0662", "orders_model_10": "99f0746f249fd25788f1348a", -"orders_model_11": "68b962a61fcbd39f299bf7b9", -"orders_model_12": "bd31b96a3fa2e1bb80699d6e", "orders_model_13": "1d827eced23366a37ddba6b8", -"orders_model_14": "69a4a23c4590c4354bff5dbf", -"orders_model_15": "e83a2c02763b0f51b49dbfe1", "orders_model_16": "a908ebc07c10d1423a970a49", -"orders_model_17": "185a865c8cf0d67b3027a313", -"orders_model_18": "02d5138c4e7513182fc7cd52", "orders_model_19": "3f36107952adc3d06995d9db", +"orders_model_22": "1b893dcaf72edc288a3b9710", +"orders_model_4": "2cfd6426be9711fc3a185538", +"orders_model_7": "07289e08df177aa672b5da80", +"orders_model_11": "68b962a61fcbd39f299bf7b9", +"orders_model_14": "69a4a23c4590c4354bff5dbf", +"orders_model_17": "185a865c8cf0d67b3027a313", "orders_model_2": "be51e7ded79e692b0f86ad8e", "orders_model_20": "59129bc02b2fd6f6f8bfb348", -"orders_model_21": "08e7a885c1d9662b2678c3ea", -"orders_model_22": "1b893dcaf72edc288a3b9710", "orders_model_23": "f35475b4e5e4ae85ae46e91d", -"orders_model_3": "fd09d2408d6ef7eceb04f8e3", -"orders_model_4": "2cfd6426be9711fc3a185538", "orders_model_5": "949207159f621de6e270aed5", -"orders_model_6": "60e19e6908bb5c06d38eeb01", -"orders_model_7": "07289e08df177aa672b5da80", "orders_model_8": "4fe24f5b9f54838716b41386", -"orders_model_9": "05bfc38d1bf1cc771029edec", "payment_totals": "10740c01d3d11a1de6799033", "payments_by_customer": "c29bf4a09e3c8e9cff564a2f", "pivot_0": "0b5af099963f99173031a52f", diff --git a/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json b/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json index 6deaca5f2..204c1fba1 100644 --- a/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json +++ b/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json @@ -6,23 +6,23 @@ [ "models", { -"missing_east": "aba5ab28f3a97ef8fce3c485", -"missing_north": "777ae26d6fa90296228d39f8", -"missing_west": "8a38648fbc0412a83d2626ef", -"orders_east": "4784ce99683381a7cb1bbeca", -"orders_north": "4784ce99683381a7cb1bbeca", -"orders_south": "4784ce99683381a7cb1bbeca", -"orders_summary": "1bda84c5cb7bff52c8d05569", -"orders_west": "4784ce99683381a7cb1bbeca", -"totals_east": "77d2a746f8facada159be569", -"totals_north": "a8d32a96d12ea19c455c183d", -"totals_south": "3b15726cb112aac93fc49acf", -"totals_west": "2b5d9cbefea62a4be70e381b" +"missing_east": "0cad01674c495e25cdde5496", +"missing_north": "ad2d4666e5bee319472b817a", +"missing_west": "00e7e40d85a9fcbd65ec2ac5", +"orders_summary": "93a3b7738140835b50fa89ad", +"totals_east": "0df7b54702598eadc68b3cbe", +"totals_north": "dcf0b489d347a19712253b52", +"totals_south": "74ca99c21327eea1760e0795", +"totals_west": "3d272c8b4ace2013aac085b6", +"orders_east": "0e0af78cfc32d1f46e85fe8b", +"orders_north": "0e0af78cfc32d1f46e85fe8b", +"orders_south": "0e0af78cfc32d1f46e85fe8b", +"orders_west": "0e0af78cfc32d1f46e85fe8b" } ], [ "catalog", -"9c499b98266c28cea392795d" +"c722537f00872b25f833fca2" ], [ "proof", @@ -79,23 +79,23 @@ [ "models", { -"missing_east": "aba5ab28f3a97ef8fce3c485", -"missing_north": "777ae26d6fa90296228d39f8", -"missing_west": "8a38648fbc0412a83d2626ef", -"orders_east": "4784ce99683381a7cb1bbeca", -"orders_north": "4784ce99683381a7cb1bbeca", -"orders_south": "4784ce99683381a7cb1bbeca", -"orders_summary": "1bda84c5cb7bff52c8d05569", -"orders_west": "4784ce99683381a7cb1bbeca", -"totals_east": "77d2a746f8facada159be569", -"totals_north": "a8d32a96d12ea19c455c183d", -"totals_south": "3b15726cb112aac93fc49acf", -"totals_west": "2b5d9cbefea62a4be70e381b" +"missing_east": "0cad01674c495e25cdde5496", +"missing_north": "ad2d4666e5bee319472b817a", +"missing_west": "00e7e40d85a9fcbd65ec2ac5", +"orders_summary": "437a867a65e38afbcdde4c25", +"totals_east": "aa9ebfbb9277e602f8e59b3e", +"totals_north": "a91a3df807cea8f738066257", +"totals_south": "4a03c9c25fae2ce1a6bff613", +"totals_west": "d8f87f660afc07018bb682ce", +"orders_east": "0e0af78cfc32d1f46e85fe8b", +"orders_north": "0e0af78cfc32d1f46e85fe8b", +"orders_south": "0e0af78cfc32d1f46e85fe8b", +"orders_west": "0e0af78cfc32d1f46e85fe8b" } ], [ "catalog", -"9c499b98266c28cea392795d" +"c722537f00872b25f833fca2" ], [ "proof", diff --git a/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json b/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json index c1e0c0cba..9e32136ea 100644 --- a/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json +++ b/tests/goldens/python_oracle/metadata_formerly_deferred_non_ascii_names_and_comments_new_and_unknown_dialects.json @@ -2,41 +2,41 @@ [ "metadata", { -"None": "257eadf2980dd54d687bb376", -"duckdb": "d1d66a9dd0325471cc034afd", -"mysql": "257eadf2980dd54d687bb376", -"nonsense": "6f6079f31e12e028922d40f2", -"tsql": "257eadf2980dd54d687bb376" +"duckdb": "f94d387a4480c13db319fdad", +"mysql": "cababa3a33d5afcabd8f3fb8", +"tsql": "cababa3a33d5afcabd8f3fb8", +"None": "cababa3a33d5afcabd8f3fb8", +"nonsense": "6f6079f31e12e028922d40f2" } ], [ "metadata", { -"None": "c9f66e2829627aeb5e4728db", "duckdb": "0ed0773a2a4a6fca23c1dcac", "mysql": "c9f66e2829627aeb5e4728db", -"nonsense": "6f6079f31e12e028922d40f2", -"tsql": "c9f66e2829627aeb5e4728db" +"tsql": "c9f66e2829627aeb5e4728db", +"None": "c9f66e2829627aeb5e4728db", +"nonsense": "6f6079f31e12e028922d40f2" } ], [ "metadata", { -"None": "a86fc9ef8fd7f46e7110dff4", "duckdb": "30c78fd5afb3d05479d6c4ca", "mysql": "a86fc9ef8fd7f46e7110dff4", -"nonsense": "6f6079f31e12e028922d40f2", -"tsql": "a86fc9ef8fd7f46e7110dff4" +"tsql": "a86fc9ef8fd7f46e7110dff4", +"None": "a86fc9ef8fd7f46e7110dff4", +"nonsense": "6f6079f31e12e028922d40f2" } ], [ "metadata", { -"None": "9225fd40168382c4100e98c3", "duckdb": "55da090c1795767af58252eb", "mysql": "9225fd40168382c4100e98c3", -"nonsense": "6f6079f31e12e028922d40f2", -"tsql": "9225fd40168382c4100e98c3" +"tsql": "9225fd40168382c4100e98c3", +"None": "9225fd40168382c4100e98c3", +"nonsense": "6f6079f31e12e028922d40f2" } ] ] diff --git a/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json b/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json index 97b71711e..39fd0c942 100644 --- a/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json +++ b/tests/goldens/python_oracle/metadata_generated_function_calls_config_references_cursors_audits_and_sql_tests.json @@ -2,73 +2,73 @@ [ "metadata", { -"bigquery": "3a936fb881b69845cbf281ea", -"duckdb": "3b771f6b2fe8c3590fa58424", -"postgres": "b0a7df99618a9706e15fffef", -"snowflake": "b0a7df99618a9706e15fffef" +"duckdb": "1c2a3eebe1d7ed12373eee03", +"postgres": "8e36fb23554087db46727693", +"snowflake": "8e36fb23554087db46727693", +"bigquery": "03e76c987d9b715a01607418" } ], [ "metadata", { -"bigquery": "0a4ff65b0383da513b81728e", -"duckdb": "0347352e059171438345cdc3", -"postgres": "6fc977a6058882760800a845", -"snowflake": "6fc977a6058882760800a845" +"duckdb": "5b11b1dec11561480d6dc43f", +"postgres": "abcdd5e3fcbe2daefb74f57b", +"snowflake": "abcdd5e3fcbe2daefb74f57b", +"bigquery": "27ba56206f3fd3e08e404003" } ], [ "metadata", { -"bigquery": "6188d55225fc3355ee350d93", "duckdb": "7f34dcf40c6392f554ea91da", "postgres": "2e203d99e28ab5cbdfd8cfb6", -"snowflake": "2e203d99e28ab5cbdfd8cfb6" +"snowflake": "2e203d99e28ab5cbdfd8cfb6", +"bigquery": "6188d55225fc3355ee350d93" } ], [ "metadata", { -"bigquery": "c582c97bec0387c38abb4d4d", "duckdb": "5248ff1ce3518aeed27d1ecd", "postgres": "94e4aae33c04503253db41a4", -"snowflake": "94e4aae33c04503253db41a4" +"snowflake": "94e4aae33c04503253db41a4", +"bigquery": "c582c97bec0387c38abb4d4d" } ], [ "metadata", { -"bigquery": "d1229780b7935d9acac4c6fa", "duckdb": "f58eecbca144a88bbd6c7eef", "postgres": "ad4229629c684797c3e9e363", -"snowflake": "ad4229629c684797c3e9e363" +"snowflake": "ad4229629c684797c3e9e363", +"bigquery": "d1229780b7935d9acac4c6fa" } ], [ "metadata", { -"bigquery": "8a38d3db16cbefe4251c8244", -"duckdb": "d3057408147f96cf1aef3b61", -"postgres": "8bd1331b85712d5a5bed65b2", -"snowflake": "8bd1331b85712d5a5bed65b2" +"duckdb": "6c87da536a646aeb646ad807", +"postgres": "eb74286ed7c0e52a3fe8a992", +"snowflake": "eb74286ed7c0e52a3fe8a992", +"bigquery": "96b011c7d66946c301340908" } ], [ "metadata", { -"bigquery": "76c9a91c06ccf970b8cf8823", "duckdb": "30c095b3a216c510c92743c5", "postgres": "83535238d4bee99c5079d1f1", -"snowflake": "83535238d4bee99c5079d1f1" +"snowflake": "83535238d4bee99c5079d1f1", +"bigquery": "76c9a91c06ccf970b8cf8823" } ], [ "metadata", { -"bigquery": "7b052937b7f3b212908fa289", "duckdb": "31c36219fc8d6894d50fd2a6", "postgres": "c6df91db98ed3db9d24d2dfc", -"snowflake": "c6df91db98ed3db9d24d2dfc" +"snowflake": "c6df91db98ed3db9d24d2dfc", +"bigquery": "7b052937b7f3b212908fa289" } ] ] diff --git a/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json b/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json index 64b357cbe..09b4c17ca 100644 --- a/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json +++ b/tests/goldens/python_oracle/semantic_formerly_deferred_dialects_added_to_the_native_parser_build.json @@ -2,23 +2,23 @@ [ "completion", { -"databricks": "25140233ccf8e7760c91ffd6", -"mysql": "25140233ccf8e7760c91ffd6", -"oracle": "25140233ccf8e7760c91ffd6", -"sqlite": "25140233ccf8e7760c91ffd6", -"trino": "25140233ccf8e7760c91ffd6", -"tsql": "25140233ccf8e7760c91ffd6" +"mysql": "d54ac41b7924d8d3eb4b07de", +"tsql": "d54ac41b7924d8d3eb4b07de", +"databricks": "d54ac41b7924d8d3eb4b07de", +"sqlite": "d54ac41b7924d8d3eb4b07de", +"oracle": "d54ac41b7924d8d3eb4b07de", +"trino": "d54ac41b7924d8d3eb4b07de" } ], [ "completion", { -"databricks": "edd3b1ec05f9481067078045", -"mysql": "edd3b1ec05f9481067078045", -"oracle": "edd3b1ec05f9481067078045", -"sqlite": "edd3b1ec05f9481067078045", -"trino": "edd3b1ec05f9481067078045", -"tsql": "edd3b1ec05f9481067078045" +"mysql": "af4024cbb0b1022278fb0039", +"tsql": "af4024cbb0b1022278fb0039", +"databricks": "af4024cbb0b1022278fb0039", +"sqlite": "af4024cbb0b1022278fb0039", +"oracle": "af4024cbb0b1022278fb0039", +"trino": "af4024cbb0b1022278fb0039" } ] ] diff --git a/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json b/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json index 56f62a248..19e853e4d 100644 --- a/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json +++ b/tests/goldens/python_oracle/semantic_formerly_deferred_non_ascii_names_comments_and_line_separators.json @@ -2,37 +2,37 @@ [ "completion", { -"bigquery": "72198dc725abd42004ae2f92", -"duckdb": "72198dc725abd42004ae2f92", -"postgres": "72198dc725abd42004ae2f92", -"snowflake": "72198dc725abd42004ae2f92" +"duckdb": "a9ceb826a6c9cc3caf987bb7", +"postgres": "a9ceb826a6c9cc3caf987bb7", +"snowflake": "a9ceb826a6c9cc3caf987bb7", +"bigquery": "a9ceb826a6c9cc3caf987bb7" } ], [ "completion", { -"bigquery": "ec92efdcc3f29018b64c7801", -"duckdb": "092551af5dab295b67c05994", -"postgres": "092551af5dab295b67c05994", -"snowflake": "092551af5dab295b67c05994" +"duckdb": "c10085934861ac2acdf66683", +"postgres": "c10085934861ac2acdf66683", +"snowflake": "c10085934861ac2acdf66683", +"bigquery": "ee8f0e9b7ecf29cbf883f458" } ], [ "completion", { -"bigquery": "af3ee8e62ebdf86bc11ea1b1", -"duckdb": "af3ee8e62ebdf86bc11ea1b1", -"postgres": "af3ee8e62ebdf86bc11ea1b1", -"snowflake": "af3ee8e62ebdf86bc11ea1b1" +"duckdb": "d94e71acae817a60f01b0955", +"postgres": "d94e71acae817a60f01b0955", +"snowflake": "d94e71acae817a60f01b0955", +"bigquery": "d94e71acae817a60f01b0955" } ], [ "completion", { -"bigquery": "5d38a54de74c19e9347a5c45", -"duckdb": "5d38a54de74c19e9347a5c45", -"postgres": "5d38a54de74c19e9347a5c45", -"snowflake": "5d38a54de74c19e9347a5c45" +"duckdb": "25bf8e4b2677f3273106dee9", +"postgres": "25bf8e4b2677f3273106dee9", +"snowflake": "25bf8e4b2677f3273106dee9", +"bigquery": "25bf8e4b2677f3273106dee9" } ] ] diff --git a/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json b/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json index b09f4e70a..bba02b570 100644 --- a/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json +++ b/tests/goldens/python_oracle/semantic_generated_typos_aliases_temporal_comparisons_type_errors_and_opt_outs.json @@ -2,73 +2,73 @@ [ "completion", { -"bigquery": "386e47117f671dcf1e99ba79", -"duckdb": "f8a96a6ad1b63bd08886df5e", -"postgres": "f8a96a6ad1b63bd08886df5e", -"snowflake": "f8a96a6ad1b63bd08886df5e" +"duckdb": "ff77b0f7c0cca5db3021485f", +"postgres": "f6cd382284a88cb40ee7119a", +"snowflake": "45200ddc74b94812a0aca571", +"bigquery": "f6ed5f097058d1635d709bd1" } ], [ "completion", { -"bigquery": "6b2a976a568aea0841340272", -"duckdb": "6b2a976a568aea0841340272", +"duckdb": "a88cbfc47aa1a11a62c671bb", "postgres": "6b2a976a568aea0841340272", -"snowflake": "6b2a976a568aea0841340272" +"snowflake": "2c23a32e69460308d4893f41", +"bigquery": "b6fcb7fdea037d60e2702021" } ], [ "completion", { -"bigquery": "da1919b4d28cc12b6aeb0d6a", -"duckdb": "da1919b4d28cc12b6aeb0d6a", -"postgres": "da1919b4d28cc12b6aeb0d6a", -"snowflake": "da1919b4d28cc12b6aeb0d6a" +"duckdb": "559b4f8d9d535a8c20a43a68", +"postgres": "61b4f248f3abd2ba9cd652a5", +"snowflake": "a7a5c49a1a12ea6a55928d58", +"bigquery": "dac3d8e7001cb578e97d6ce4" } ], [ "completion", { -"bigquery": "0f8a7d8a92a2525f96180eab", -"duckdb": "0f8a7d8a92a2525f96180eab", -"postgres": "0f8a7d8a92a2525f96180eab", -"snowflake": "0f8a7d8a92a2525f96180eab" +"duckdb": "43a694212f8a1c90b7c5c429", +"postgres": "50a5615b0792ed59adf9f1d2", +"snowflake": "43a694212f8a1c90b7c5c429", +"bigquery": "6e65889a512f0a3e8dbdbcf6" } ], [ "completion", { -"bigquery": "a0775e9568c53c1c04d03b9e", -"duckdb": "a0775e9568c53c1c04d03b9e", -"postgres": "a0775e9568c53c1c04d03b9e", -"snowflake": "a0775e9568c53c1c04d03b9e" +"duckdb": "7a4e919059d5f2d759a79a0e", +"postgres": "7d6b71a9970d341846a8f745", +"snowflake": "2e5d4d0063e8d8db7563cc53", +"bigquery": "a04f42301146206387e9cab3" } ], [ "completion", { -"bigquery": "56d1dd61d4d03302a8f2934a", -"duckdb": "ec5f01a5076273dcce4865e9", -"postgres": "ec5f01a5076273dcce4865e9", -"snowflake": "ec5f01a5076273dcce4865e9" +"duckdb": "dabfee6327264ab0daad3f28", +"postgres": "6ed1cf041b8be10e061db0f7", +"snowflake": "77d73ac56b19811a2e044eb4", +"bigquery": "582b2ca361eca23875f37828" } ], [ "completion", { -"bigquery": "f9877a22ff91f0e41760f3cc", -"duckdb": "f9877a22ff91f0e41760f3cc", -"postgres": "f9877a22ff91f0e41760f3cc", -"snowflake": "f9877a22ff91f0e41760f3cc" +"duckdb": "4d6653aafceb276d132e3d5a", +"postgres": "5fa95a999d889ee53aa44970", +"snowflake": "49f8133ce7dc3332de1928b8", +"bigquery": "63abe5e3df41cdc10962f43c" } ], [ "completion", { -"bigquery": "89c0ecb9bfa8db140822ca46", -"duckdb": "e8762d8e3f7d18a056f664ad", -"postgres": "e8762d8e3f7d18a056f664ad", -"snowflake": "e8762d8e3f7d18a056f664ad" +"duckdb": "5a5c63115bf816432297ed36", +"postgres": "15298f247b3b7ab16eac060c", +"snowflake": "5a5c63115bf816432297ed36", +"bigquery": "fa3def6e43f007f87ecb1870" } ] ] diff --git a/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json b/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json index 9000a6d33..7097e638c 100644 --- a/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json +++ b/tests/goldens/python_oracle/semantic_session_analysis_session_corpus_with_pivots_stars_and_set_operations.json @@ -1,18 +1,18 @@ [ [ "completion", -"f85970edb78845ff2ce0ea2c" +"c790d72adcfad6c7f910f7e0" ], [ "completion", -"7149dab572362b7f40692734" +"175e773ac911c101636c43a6" ], [ "completion", -"c72855d94cb69680061f4edd" +"100ec44be185b9d4ae5199cb" ], [ "completion", -"808ea581e0aab0f08630bc23" +"9e37f28251c594cacef62eef" ] ] diff --git a/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json b/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json index 292cd9e8e..4f5cebd40 100644 --- a/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json +++ b/tests/goldens/python_oracle/semantic_session_failing_semantics_corpus.json @@ -1,26 +1,26 @@ [ [ "completion", -"1b1aca58c29b7d2a4885cd24" +"659db5c5fe2701a52cd97fc9" ], [ "completion", -"7d957239333982bbc2a73159" +"5174486fe569433a8352e2e4" ], [ "completion", -"17d9bf58e797f8ac09758305" +"13bd1dee1f37387417f64189" ], [ "completion", -"758b2e20f1f61ae3237cc60b" +"66c18726bbd8d3691d66b1cc" ], [ "completion", -"d76482d15b26655a451ba2bc" +"c415c9c7d7720b8a20b96cbc" ], [ "completion", -"28fd253e8477c9fa71e5c357" +"3800f4f18b512e99ed4f6618" ] ] diff --git a/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json b/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json index 2f01e9d45..c25ac28ec 100644 --- a/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json +++ b/tests/goldens/python_oracle/sql_test_assembly_a_macro_test_over_a_model_with_a_non_ascii_name_after.json @@ -2,9 +2,9 @@ [ "sql_test_assembly", { -"diagnostics": "1029c769852df873c49c45ae", -"raised": "74234e98afe7498fb5daf1f3", -"sql_tests": "601883520be21497efb0a9ee" +"sql_tests": "601883520be21497efb0a9ee", +"diagnostics": "6ef2576fb654edcf29f33018", +"raised": "74234e98afe7498fb5daf1f3" } ] ] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 3d3515d7c..5f5979e59 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -39,6 +39,16 @@ class CyclicAnalysisTestCase: expected_analysed: int +@dataclass(frozen=True) +class SourceTypesTestCase: + """A model reading a source with declared types, with or without the source's expression.""" + + description: str + files: dict[str, str] + lineage_mode: ColumnLineageMode + expected_types: tuple[tuple[str, str | None], ...] + + @dataclass(frozen=True) class CyclicCompileTestCase: """A project whose models `ref` each other, compiled through the CLI.""" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 7ffe4d1f0..7cd6ed013 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -656,6 +656,26 @@ def _record_analyses( runs.sessions.append(session) +def first_model_types( + *, files: dict[str, str], lineage_mode: ColumnLineageMode, tmp_path: Path +) -> tuple[tuple[str, str | None], ...]: + """The first analysed model's output names and types, analysed natively as DuckDB.""" + + inputs: CompileInputsView = compile_inputs(project_dir=tmp_path / "project", files=files) + runs: NativeAnalysisRuns = NativeAnalysisRuns() + analyse_natively( + inputs=inputs, + inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb"), + lineage_mode=lineage_mode, + runs=runs, + cache_root=tmp_path / "cache", + ) + analysis: ModelSqlAnalysis = session_outcome_analysis( + session=runs.sessions[0], position=0, placeholders=None + ) + return tuple((column.name, column.type) for column in analysis.polyglot_analysis.columns or ()) + + def custom_nullability_rule(arguments: tuple[InferredNullability, ...]) -> InferredNullability: """An adapter rule SQLBuild does not ship, so native analysis calls it back.""" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 33ddbde8b..268136c70 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -25,6 +25,7 @@ CyclicCompileTestCase, GeneratedAnalysisTestCase, SharedAnalysisTestCase, + SourceTypesTestCase, StandalonePivotProofTestCase, ) from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( @@ -37,6 +38,7 @@ compile_inputs, compiled_project_view, custom_nullability_rule, + first_model_types, generated_analysis_files, native_pivot_proofs, pivot_project_files, @@ -100,6 +102,26 @@ "models/returns.sql": 'MODEL (description "Returns");\n\nSELECT order_id FROM __ref("orders")\n', } +_DECLARED_SOURCE_PROJECT: dict[str, str] = { + "sqlbuild_project.toml": 'name = "orders_sources"\nadapter = "duckdb"\n', + "sources/raw.yml": ( + "sources:\n - name: raw_orders\n description: Raw orders.\n columns:\n" + " - name: order_id\n type: INTEGER\n" + " - name: amount\n type: DOUBLE\n" + ), + "models/orders.sql": ( + 'MODEL (description "Orders");\n\n' + 'SELECT order_id, amount * -1 AS refund FROM __source("raw_orders")\n' + ), +} +_EXPRESSION_SOURCE_PROJECT: dict[str, str] = _DECLARED_SOURCE_PROJECT | { + "sources/raw.yml": _DECLARED_SOURCE_PROJECT["sources/raw.yml"].replace( + " columns:\n", + " expression: >-\n (SELECT 1 AS order_id, CAST(5 AS DOUBLE) AS amount)\n" + " columns:\n", + ), +} + @pytest.mark.parametrize( "test_case", @@ -130,7 +152,7 @@ expected_minimum_expression_shapes=40, expected_minimum_pivot_proofs=80, expected_minimum_proven_pivots=12, - expected_minimum_native_enrichments=90, + expected_minimum_native_enrichments=0, ), GeneratedAnalysisTestCase( description="snowflake and duckdb adapter rules, fast lineage", @@ -144,7 +166,7 @@ expected_minimum_expression_shapes=15, expected_minimum_pivot_proofs=30, expected_minimum_proven_pivots=10, - expected_minimum_native_enrichments=30, + expected_minimum_native_enrichments=0, ), GeneratedAnalysisTestCase( description="snowflake and duckdb adapter rules, rich lineage", @@ -158,10 +180,12 @@ expected_minimum_expression_shapes=15, expected_minimum_pivot_proofs=30, expected_minimum_proven_pivots=10, - expected_minimum_native_enrichments=20, + expected_minimum_native_enrichments=0, ), GeneratedAnalysisTestCase( - description="a project-local adapter's own nullability rules, called back natively", + description=( + "a project-local adapter's own nullability rules, not called on a native route" + ), seed=20261011, count=3, model_count=24, @@ -172,7 +196,7 @@ expected_minimum_expression_shapes=1, expected_minimum_pivot_proofs=1, expected_minimum_proven_pivots=1, - expected_minimum_native_enrichments=1, + expected_minimum_native_enrichments=0, ), ], ids=lambda case: case.description, @@ -332,6 +356,55 @@ def test_given_cyclic_models_when_analysing_natively_then_session_answers_and_ca ) +@pytest.mark.parametrize( + "test_case", + [ + SourceTypesTestCase( + description="a source with its expression types the columns read from it", + files=_EXPRESSION_SOURCE_PROJECT, + lineage_mode=ColumnLineageMode.RICH, + expected_types=(("order_id", "INTEGER"), ("refund", "DOUBLE")), + ), + ], + ids=lambda case: case.description, +) +def test_given_typed_source_when_analysing_natively_then_its_columns_are_typed( + test_case: SourceTypesTestCase, tmp_path: Path +) -> None: + assert ( + first_model_types( + files=test_case.files, lineage_mode=test_case.lineage_mode, tmp_path=tmp_path + ) + == test_case.expected_types + ) + + +@pytest.mark.xfail( + strict=True, reason="native analysis reads only authoritative shapes, not declared types" +) +@pytest.mark.parametrize( + "test_case", + [ + SourceTypesTestCase( + description="a source's declared types type the columns read from it", + files=_DECLARED_SOURCE_PROJECT, + lineage_mode=ColumnLineageMode.RICH, + expected_types=(("order_id", "INTEGER"), ("refund", "DOUBLE")), + ), + ], + ids=lambda case: case.description, +) +def test_given_pending_declared_source_types_when_analysing_natively_then_columns_are_typed( + test_case: SourceTypesTestCase, tmp_path: Path +) -> None: + assert ( + first_model_types( + files=test_case.files, lineage_mode=test_case.lineage_mode, tmp_path=tmp_path + ) + == test_case.expected_types + ) + + @pytest.mark.parametrize( "test_case", [ diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py index eb81e271a..7e8276282 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py @@ -30,6 +30,17 @@ class FormerlyDeferredSemanticTestCase: expected_outcomes: frozenset[str] +@dataclass(frozen=True) +class DialectFindingTestCase: + """One model compiled under one dialect: the findings and output types it must have.""" + + description: str + dialect: str + sql: str + expected_codes: tuple[str, ...] + expected_types: tuple[tuple[str, str | None], ...] + + @dataclass(frozen=True) class MissingCatalogTestCase: """A project without the analysis catalog every compile builds.""" diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index 92fbd3b21..4b2306864 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -218,6 +218,24 @@ def captured_under_dialect( return SemanticInputs(**stage) +def one_model_completion( + *, project_dir: Path, dialect: str, sql: str +) -> tuple[tuple[str, ...], tuple[tuple[str, str | None], ...]]: + """One model over the generated sources, completed under `dialect`: codes and output types.""" + + files: dict[str, str] = generated_semantic_files( + rng=random.Random(0), model_count=0, require_analysis=True + ) + files["models/probe.sql"] = f'MODEL (description "Probe");\n\n{sql}\n' + native: CompiledProjectView = completed_natively( + captured_under_dialect(project_dir=project_dir, files=files, dialect=dialect) + ) + return ( + tuple(diagnostic.code for diagnostic in native.diagnostics), + tuple((column.name, column.type) for column in native.models[0].inferred_columns or ()), + ) + + def with_dialect(inputs: SemanticInputs, dialect: str | None) -> SemanticInputs: """The same stage inputs read under another SQL dialect.""" diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index 821585873..86c4cacfd 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -22,6 +22,7 @@ read_golden, ) from tests.integration.src.sqlbuild.compiler.semantic_checks._test_types import ( + DialectFindingTestCase, FormerlyDeferredSemanticTestCase, GeneratedSemanticParityTestCase, MissingCatalogTestCase, @@ -38,6 +39,7 @@ has_blocking_binding, native_completion, note_kinds, + one_model_completion, outcome_label, payload_completion_view, proven_output_count, @@ -187,6 +189,67 @@ def test_given_formerly_deferred_projects_when_completing_natively_then_matches_ assert outcomes == test_case.expected_outcomes +@pytest.mark.parametrize( + "test_case", + [ + DialectFindingTestCase( + description="DuckDB refuses a text literal compared with a number", + dialect="duckdb", + sql="SELECT o.order_id FROM __source(\"raw_orders\") AS o WHERE o.amount > 'placed'", + expected_codes=("B218",), + expected_types=(("order_id", "INTEGER"),), + ), + DialectFindingTestCase( + description="DuckDB types a date plus whole days as a date", + dialect="duckdb", + sql='SELECT order_date + order_id AS due FROM __source("raw_orders")', + expected_codes=(), + expected_types=(("due", "DATE"),), + ), + ], + ids=lambda case: case.description, +) +def test_given_one_model_under_its_dialect_when_completing_natively_then_has_expected_findings( + test_case: DialectFindingTestCase, + tmp_path: Path, +) -> None: + assert one_model_completion( + project_dir=tmp_path / "project", dialect=test_case.dialect, sql=test_case.sql + ) == (test_case.expected_codes, test_case.expected_types) + + +@pytest.mark.xfail( + strict=True, reason="SQLQuack's Snowflake profile does not yet check or type these" +) +@pytest.mark.parametrize( + "test_case", + [ + DialectFindingTestCase( + description="Snowflake refuses a text literal compared with a number", + dialect="snowflake", + sql="SELECT o.order_id FROM __source(\"raw_orders\") AS o WHERE o.amount > 'placed'", + expected_codes=("B218",), + expected_types=(("order_id", "INTEGER"),), + ), + DialectFindingTestCase( + description="Snowflake types a date plus whole days as a date", + dialect="snowflake", + sql='SELECT order_date + order_id AS due FROM __source("raw_orders")', + expected_codes=(), + expected_types=(("due", "DATE"),), + ), + ], + ids=lambda case: case.description, +) +def test_given_pending_snowflake_semantics_when_completing_natively_then_has_expected_findings( + test_case: DialectFindingTestCase, + tmp_path: Path, +) -> None: + assert one_model_completion( + project_dir=tmp_path / "project", dialect=test_case.dialect, sql=test_case.sql + ) == (test_case.expected_codes, test_case.expected_types) + + @pytest.mark.parametrize( "test_case", [ @@ -237,8 +300,8 @@ def test_given_project_without_catalog_when_completing_then_raises_native_compil seed=20261011, count=4, model_count=16, - expected_session_models=78, - expected_payload_models=18, + expected_session_models=83, + expected_payload_models=13, expected_proven_outputs=10, expected_minimum_codes={"B002": 1}, ), From e72abee90f46476f40f1a6737a7804eccceef0f4 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 17:10:53 +0000 Subject: [PATCH 069/160] test(analysis): expect a native wave to analyse each equal model query A native wave analyses every model on its own, so the equal-query case expects no shared or re-analysed queries. Outcomes are unchanged; only the repeated work differs. --- ...que_summary_each_analysed_by_a_native_wave.json} | 0 .../compiler/analysis_session/_test_types.py | 2 +- .../analysis_session/test_native_model_analysis.py | 13 ++++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) rename tests/goldens/python_oracle/{analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json => analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary_each_analysed_by_a_native_wave.json} (100%) diff --git a/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json b/tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary_each_analysed_by_a_native_wave.json similarity index 100% rename from tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary.json rename to tests/goldens/python_oracle/analysis_shared_equal_regional_queries_missing_column_readers_and_a_unique_summary_each_analysed_by_a_native_wave.json diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 5f5979e59..80425a9ff 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -72,7 +72,7 @@ class StandalonePivotProofTestCase: @dataclass(frozen=True) class SharedAnalysisTestCase: - """Models whose equal queries share one analysis, some inexact and so re-analysed alone.""" + """Models with equal queries, some inexact, and how many analyses the session shares.""" description: str regions: tuple[str, ...] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 268136c70..86f39b2b9 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -246,20 +246,23 @@ def test_given_generated_projects_when_analysing_natively_then_cached_and_proven "test_case", [ SharedAnalysisTestCase( - description="equal regional queries, missing-column readers and a unique summary", + description=( + "equal regional queries, missing-column readers and a unique summary, " + "each analysed by a native wave" + ), regions=("east", "west", "north", "south"), inexact_regions=("east", "west", "north"), dialects=("duckdb", "snowflake"), expected_analysed=24, - expected_shared=22, - expected_reanalysed=6, - expected_unshared=2, + expected_shared=0, + expected_reanalysed=0, + expected_unshared=24, expected_column_values=12, ) ], ids=lambda case: case.description, ) -def test_given_equal_model_queries_when_analysing_natively_then_shares_and_cached_runs_agree( +def test_given_equal_model_queries_when_analysing_natively_then_each_is_analysed_and_runs_agree( test_case: SharedAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: inputs: CompileInputsView = compile_inputs( From fc816af1a8dde5bf958c2c2bfc6c9c02e3335522 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 17:34:44 +0000 Subject: [PATCH 070/160] fix(analysis): type a native route's reads of declared but open relations A native route gave SQLQuack only relations whose full shape was known, so a source or seed with declared column types but no authoritative shape was read untyped: `status + 1` over a declared VARCHAR went unreported, where Polyglot's rich analysis used the declared types. The session schema now passes each such relation's declared columns, with the nullability the session knows, as open columns. They type the columns a query reads without making the relation's column list complete, so star expansion and unknown-column checks are unchanged. Revalidation passes the same open columns. The analysis cache format moves to v4, as outcomes for the same inputs change. --- .../analysis_route/_helpers/revalidation.rs | 39 ++++++--- .../src/analysis_route/main/fast_lineage.rs | 1 + .../src/analysis_route/tests/helpers.rs | 1 + .../tests/test_native_outcome.rs | 87 ++++++++++++++++++- .../src/analysis_route/tests/test_types.rs | 9 ++ .../analysis_session/_helpers/mappings.rs | 28 +++--- .../analysis_session/_helpers/session.rs | 5 +- .../assembly/analysis_session/constants.rs | 2 +- .../src/assembly/analysis_session/models.rs | 14 +++ 9 files changed, 160 insertions(+), 26 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs index 96c507d37..6f5bfde6e 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/revalidation.rs @@ -42,18 +42,25 @@ pub(crate) fn revised( finished: &FinishedSession, ) -> Result>, String> { let sql: Cow<'_, str> = resolved_cleaned_sql(outcome)?; + let mut relations: HashMap> = HashMap::new(); + let mut open: HashMap> = HashMap::new(); + for (relation, columns) in &outcome.validated_schema { + let (table, columns) = if columns.is_empty() { + match declared_columns(finished, relation) { + Some(declared) => (&mut open, declared), + None => continue, + } + } else { + (&mut relations, columns) + }; + let _ = table.insert( + relation.clone(), + schema_columns(dialect, relation, columns, poisoned), + ); + } let relations: SessionSchema = SessionSchema { - relations: outcome - .validated_schema - .iter() - .filter(|(_, columns)| !columns.is_empty()) - .map(|(relation, columns)| { - ( - relation.clone(), - schema_columns(dialect, relation, columns, poisoned), - ) - }) - .collect::>>(), + relations, + open, functions: finished.functions.clone(), }; let analysed: Result = dialect.analyse(&AnalysisInput { @@ -74,6 +81,16 @@ pub(crate) fn revised( .map(Some) } +/// The declared columns of `relation`, whose full shape the session did not know, when it has any. +fn declared_columns<'a>(finished: &'a FinishedSession, relation: &str) -> Option<&'a Pairs> { + finished + .tables + .column_types + .iter() + .find(|(name, columns)| name == relation && !columns.is_empty()) + .map(|(_, columns)| columns) +} + /// The model's own findings, as type recovery matches them, kept as they are. pub(crate) fn unrevised(model: &RecoveryModel) -> Vec { model diff --git a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs index bcde53cba..3b179b4ac 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/main/fast_lineage.rs @@ -114,6 +114,7 @@ fn markers_schema( } SessionSchema { relations, + open: HashMap::new(), functions: SessionFunctions::default(), } } diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs index ed28a13c2..29831d22d 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/helpers.rs @@ -99,6 +99,7 @@ pub(super) fn session_relations(shape: &[(&str, &str, Nullability)]) -> (Shapes, vec![("raw_orders".to_owned(), pairs)], SessionSchema { relations, + open: HashMap::new(), functions: SessionFunctions::default(), }, ) diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs index e592759a3..bbeaa37d6 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_native_outcome.rs @@ -3,8 +3,9 @@ use crate::analysis_route::main::model_outcome::model_outcome; use crate::analysis_route::tests::helpers::{column_line, session_relations}; use crate::analysis_route::tests::test_types::{ CataloguedDialect, FoldingDialect, MarkerLineageTestCase, NativeLineageTestCase, - NativeOutcomeTestCase, PivotlessDialect, + NativeOutcomeTestCase, OpenRelationTestCase, PivotlessDialect, }; +use crate::assembly::analysis_session::_helpers::mappings::{ShapeTable, session_schema}; use crate::assembly::analysis_session::models::{ ModelOutcome, ModelRequest, SessionFunctions, SessionSchema, }; @@ -189,6 +190,90 @@ fn given_session_shapes_when_a_native_route_analyses_a_model_then_its_facts_foll } } +#[test] +fn given_a_relation_with_declared_columns_only_when_analysing_natively_then_they_type_its_reads() { + let test_cases = [ + OpenRelationTestCase { + description: "a declared VARCHAR read in arithmetic is a type error", + sql: "SELECT status + 1 AS bumped FROM raw_orders", + declared: &[("status", "VARCHAR"), ("amount", "DOUBLE")], + expected_lines: &["bumped - unknown", "findings=[\"B212\"]"], + }, + OpenRelationTestCase { + description: "a declared column read as it is keeps its declared type", + sql: "SELECT amount FROM raw_orders", + declared: &[("status", "VARCHAR"), ("amount", "DOUBLE")], + expected_lines: &["amount DOUBLE unknown", "findings=[]"], + }, + OpenRelationTestCase { + description: "an undeclared column of an open relation is still not refused", + sql: "SELECT region FROM raw_orders", + declared: &[("status", "VARCHAR")], + expected_lines: &["region - unknown", "findings=[]"], + }, + OpenRelationTestCase { + description: "with nothing declared, nothing is typed", + sql: "SELECT status + 1 AS bumped FROM raw_orders", + declared: &[], + expected_lines: &["bumped - unknown", "findings=[]"], + }, + ]; + for test_case in test_cases { + let schema: Shapes = vec![("raw_orders".to_owned(), Vec::new())]; + let declared: Shapes = vec![( + "raw_orders".to_owned(), + test_case + .declared + .iter() + .map(|(name, data_type)| ((*name).to_owned(), (*data_type).to_owned())) + .collect(), + )]; + let relations: SessionSchema = session_schema( + &CataloguedDialect, + &schema, + ( + &ShapeTable::from_shapes(&declared), + &ShapeTable::from_shapes(&Vec::new()), + ), + &SessionFunctions::default(), + ); + let model: ModelRequest = ModelRequest { + name: "orders".to_owned(), + query_sql: test_case.sql.to_owned(), + required_names: vec!["raw_orders".to_owned()], + ..ModelRequest::default() + }; + let (outcome, _): (ModelOutcome, Option) = model_outcome( + &CataloguedDialect, + &model, + (schema, &relations), + (&[], false), + ); + let mut lines: Vec = outcome + .analysis + .columns + .iter() + .flatten() + .map(|column| { + format!( + "{} {} {}", + column.name, + column.data_type.as_deref().unwrap_or("-"), + column.nullability + ) + }) + .collect(); + let findings: Vec<&str> = outcome + .analysis + .binding_diagnostics + .iter() + .map(|row| row.code.as_str()) + .collect(); + lines.push(format!("findings={findings:?}")); + assert_eq!(lines, test_case.expected_lines, "{}", test_case.description); + } +} + #[test] fn given_a_model_without_facts_when_fast_lineage_reads_it_natively_then_it_has_lineage_or_a_reason() { diff --git a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs index 9caa9ddf2..0ec0a299b 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/tests/test_types.rs @@ -258,6 +258,15 @@ pub(super) struct NativeOutcomeTestCase { pub(super) expected_lines: &'static [&'static str], } +pub(super) struct OpenRelationTestCase { + pub(super) description: &'static str, + pub(super) sql: &'static str, + /// `raw_orders`' declared columns and types; its full shape is never known. + pub(super) declared: &'static [(&'static str, &'static str)], + /// Each output column as `name type`, then the finding codes. + pub(super) expected_lines: &'static [&'static str], +} + pub(super) struct NativeLineageTestCase { pub(super) description: &'static str, pub(super) sql: &'static str, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs index a4f2495e7..3a76ba7a5 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mappings.rs @@ -267,24 +267,30 @@ pub(crate) fn session_functions(request: &SessionRequest) -> SessionFunctions { } } -/// Each relation with a known shape, typed with the nullability the session knows, and the functions. +/// Each relation's known shape, or else its declared columns, typed, with the session's functions. pub(crate) fn session_schema( dialect: &dyn NativeDialect, schema: &Shapes, - nullability: &ShapeTable, + (types, nullability): (&ShapeTable, &ShapeTable), functions: &SessionFunctions, ) -> SessionSchema { - let relations: HashMap> = schema - .iter() - .filter(|(_, columns)| !columns.is_empty()) - .map(|(relation, columns)| { - let typed: Vec = - typed_columns(dialect, columns, nullability.get(relation)); - (relation.clone(), typed) - }) - .collect(); + let mut relations: HashMap> = HashMap::new(); + let mut open: HashMap> = HashMap::new(); + for (relation, columns) in schema { + let (table, columns) = if columns.is_empty() { + match types.get(relation) { + Some(declared) if !declared.is_empty() => (&mut open, declared), + _ => continue, + } + } else { + (&mut relations, columns) + }; + let typed: Vec = typed_columns(dialect, columns, nullability.get(relation)); + let _ = table.insert(relation.clone(), typed); + } SessionSchema { relations, + open, functions: SessionFunctions { return_types: functions .return_types diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index 48999b826..832853160 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -330,7 +330,8 @@ impl AnalysisSession { ) -> Result<(), String> { let pool = self.catalog.native.analysis_pool()?; let request: &SessionRequest = &self.request; - let nullability: &ShapeTable = &self.available_nullability; + let tables: (&ShapeTable, &ShapeTable) = + (&self.available_types, &self.available_nullability); let declared: Vec = request .function_return_types .iter() @@ -344,7 +345,7 @@ impl AnalysisSession { let model: &ModelRequest = &request.models[models[*position]]; let schema: Shapes = schemas[*position].clone(); let relations: SessionSchema = - session_schema(dialect, &schema, nullability, &functions); + session_schema(dialect, &schema, tables, &functions); let analysis = (declared.as_slice(), request.quoted_ignore_case); model_outcome(dialect, model, (schema, &relations), analysis) }) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index 5da96a56e..0473e9c3f 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -196,7 +196,7 @@ pub(crate) const PYTHON_RULE: &str = "python"; pub(crate) const NON_NULL_NULLABILITY: &str = "non_null"; pub(crate) const NULLABLE_NULLABILITY: &str = "nullable"; /// Bumped whenever the stored outcome or a key's fields change; older entries never match. -pub(crate) const ANALYSIS_CACHE_FORMAT: &str = "native-model-analysis-v3"; +pub(crate) const ANALYSIS_CACHE_FORMAT: &str = "native-model-analysis-v4"; /// A stored varint byte at or above this value continues into the next byte. pub(crate) const VARINT_CONTINUATION: u64 = 0x80; /// The value bits each stored varint byte carries. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 6f79793ac..1752e4358 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -362,6 +362,8 @@ pub(crate) struct SessionFunctions { pub(crate) struct SessionSchema { /// Columns of each relation with a known shape, by the analysis name the query reads. pub(crate) relations: HashMap>, + /// Declared columns of each relation whose full shape is not known, which type the ones read. + pub(crate) open: HashMap>, pub(crate) functions: SessionFunctions, } @@ -378,6 +380,18 @@ impl LineageSchema for SessionSchema { .map(Vec::as_slice) } + fn open_columns(&self, relation: &str) -> Option<&[SchemaColumn]> { + self.open + .get(relation) + .or_else(|| { + self.open + .iter() + .find(|(name, _)| name.eq_ignore_ascii_case(relation)) + .map(|(_, columns)| columns) + }) + .map(Vec::as_slice) + } + fn function_type(&self, name: &str) -> Option<&str> { self.functions .return_types From 2a8051330723db3885aebc52335375c1cbd17a3a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 17:49:48 +0000 Subject: [PATCH 071/160] WIP (not shipping): bind the Databricks timestamp's zone flag in one arm The setting-help scan reads a source line holding both a word followed by ': true' and a string as a loosely named setting. The two timestamp arms become one that matches on the zone flag; the spellings are unchanged. --- .../src/analysis_route/_helpers/databricks_dialect.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs index 3bef82bcd..b81281bb2 100644 --- a/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs +++ b/crates/sqlbuild-analysis/src/analysis_route/_helpers/databricks_dialect.rs @@ -160,8 +160,11 @@ fn canonical(data_type: &DataType) -> Option { scale.unwrap_or(default_scale) ) } - DataType::Timestamp { timezone: true, .. } => "TIMESTAMP".to_owned(), - DataType::Timestamp { .. } => "TIMESTAMPNTZ".to_owned(), + DataType::Timestamp { timezone, .. } => match timezone { + true => "TIMESTAMP", + false => "TIMESTAMPNTZ", + } + .to_owned(), DataType::Time { .. } => "TIME".to_owned(), DataType::Interval { unit, to } => match (unit, to) { (Some(unit), Some(to)) => format!("INTERVAL {unit} TO {to}"), From c2fc54cfea30acbb807c7eb046ef547ab3e86ccf Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:32:05 +0000 Subject: [PATCH 072/160] fix(rules): read relation markers as tables in custom-rule syntax trees Compiled query_sql keeps relation markers such as __ref("orders"). Snowflake does not accept a bare function call in FROM, so newer SQLQuack parsers refuse every Snowflake model that references another. A shared helper in sqlbuild-analysis (relation_markers::markers_as_names) now writes each marker call in code as a short indexed name, padded so all later text keeps its line and column, and returns the calls by index. sql_tree reads SQL through it for every dialect. The rules crate's dialect lexical syntax moves to sqlbuild-sqltext so both use one mapping. --- .../src/sql_tree/main/sql_tree_nodes.rs | 10 +++-- .../src/sql_tree/tests/test_sql_tree.rs | 19 ++++++++ .../src/query_facts/_helpers/shape.rs | 43 +------------------ .../cli/commands/main/rules/_test_types.py | 2 + .../cli/commands/main/rules/helpers.py | 6 ++- .../main/rules/test_rule_positions.py | 25 ++++++++++- 6 files changed, 57 insertions(+), 48 deletions(-) diff --git a/crates/sqlbuild-analysis/src/sql_tree/main/sql_tree_nodes.rs b/crates/sqlbuild-analysis/src/sql_tree/main/sql_tree_nodes.rs index de5e7a8b9..daad7f356 100644 --- a/crates/sqlbuild-analysis/src/sql_tree/main/sql_tree_nodes.rs +++ b/crates/sqlbuild-analysis/src/sql_tree/main/sql_tree_nodes.rs @@ -1,7 +1,9 @@ //! `sql_tree_nodes`: a rule's SQL syntax tree as nodes in pre-order. +use sqlbuild_sqltext::sql_scan::main::dialect_lexical_syntax::dialect_lexical_syntax; use sqlquack_syntax::Expression; +use crate::relation_markers::main::markers_as_names::markers_as_names; use crate::sql_tree::_helpers::parse::parse; use crate::sql_tree::_helpers::positions::line_starts; use crate::sql_tree::_helpers::rules_tree_parser::rules_tree_parser; @@ -11,7 +13,7 @@ use crate::sql_tree::errors::SqlTreeError; use crate::sql_tree::models::SqlTreeNode; use crate::worker_stack::main::run_on_worker_stack::run_on_worker_stack; -/// Every node of `sql` parsed in `dialect`, in pre-order, joins and CTEs owning their expressions. +/// Every node of `sql` parsed in `dialect`, in pre-order, joins and CTEs owning their expressions; relation markers read as tables. pub fn sql_tree_nodes(sql: &str, dialect: &str) -> Result, SqlTreeError> { run_on_worker_stack(TREE_WORKER_STACK_BYTES, "the parser panicked", || { tree_nodes(sql, dialect) @@ -22,6 +24,8 @@ pub fn sql_tree_nodes(sql: &str, dialect: &str) -> Result, SqlT fn tree_nodes(sql: &str, dialect: &str) -> Result, SqlTreeError> { let parser = rules_tree_parser(dialect) .ok_or_else(|| SqlTreeError::UnavailableDialect(dialect.to_owned()))?; - let statements: Vec = parse(sql, parser)?; - walk(&statements, &line_starts(sql)) + let (named, _) = markers_as_names(sql, &dialect_lexical_syntax(dialect)) + .map_err(SqlTreeError::Unanalysable)?; + let statements: Vec = parse(&named, parser)?; + walk(&statements, &line_starts(&named)) } diff --git a/crates/sqlbuild-analysis/src/sql_tree/tests/test_sql_tree.rs b/crates/sqlbuild-analysis/src/sql_tree/tests/test_sql_tree.rs index e1715b2ee..5a0629d70 100644 --- a/crates/sqlbuild-analysis/src/sql_tree/tests/test_sql_tree.rs +++ b/crates/sqlbuild-analysis/src/sql_tree/tests/test_sql_tree.rs @@ -6,6 +6,8 @@ use crate::sql_tree::tests::test_types::{TreeFailureTestCase, TreeShapeTestCase} const CTE_AND_JOIN_SQL: &str = "WITH x AS (SELECT 1 AS id)\nSELECT *\n FROM x JOIN (SELECT 2 AS id) s ON x.id = s.id"; +const SNOWFLAKE_MARKER_SQL: &str = "WITH o AS (SELECT * FROM __ref(\"orders\"))\nSELECT o.*, c.name\nFROM o JOIN __ref(\"customers\") c ON o.id = c.id JOIN (SELECT 2 AS id) s ON s.id = o.id\nWHERE o.id IN (SELECT id FROM __source(\"raw_returns\"))"; + #[test] fn given_sql_when_building_a_rule_tree_then_clauses_own_their_queries_at_their_source_positions() { let test_cases = [ @@ -48,6 +50,23 @@ fn given_sql_when_building_a_rule_tree_then_clauses_own_their_queries_at_their_s ("subquery", 2, 3, Some("alias")), ], }, + TreeShapeTestCase { + description: "Snowflake reads relation markers as tables; nodes after them keep their columns", + dialect: "snowflake", + sql: SNOWFLAKE_MARKER_SQL, + expected_nodes: vec![ + ("select", 1, 1, None), + ("cte", 1, 6, Some("select")), + ("select", 1, 12, Some("cte")), + ("star", 1, 12, Some("select")), + ("star", 2, 8, Some("select")), + ("join", 3, 8, Some("select")), + ("join", 3, 49, Some("select")), + ("select", 3, 54, Some("subquery")), + ("subquery", 3, 54, Some("join")), + ("select", 4, 7, Some("in")), + ], + }, TreeShapeTestCase { description: "a comma-separated FROM item after an explicit join is not a join", dialect: "duckdb", diff --git a/crates/sqlbuild-rules/src/query_facts/_helpers/shape.rs b/crates/sqlbuild-rules/src/query_facts/_helpers/shape.rs index 0f2b3ea81..f9fae3f4b 100644 --- a/crates/sqlbuild-rules/src/query_facts/_helpers/shape.rs +++ b/crates/sqlbuild-rules/src/query_facts/_helpers/shape.rs @@ -5,6 +5,7 @@ use crate::query_facts::constants::{ use crate::query_facts::models::{CallKind, ModelCall, ModelShape}; use crate::rules::main::compact_sql::compact_sql; use sqlbuild_analysis::semantic_validation::main::normalize_mapped::normalize_mapped; +use sqlbuild_sqltext::sql_scan::main::dialect_lexical_syntax::dialect_lexical_syntax; use sqlbuild_sqltext::sql_scan::main::dialect_matching_paren::dialect_matching_paren; use sqlbuild_sqltext::sql_scan::main::dialect_non_code_end::dialect_non_code_end; use sqlbuild_sqltext::sql_scan::main::quote_policy::quote_policy; @@ -15,7 +16,7 @@ pub(crate) fn model_shape(sql: &str, dialect: &str) -> Result = normalized @@ -77,46 +78,6 @@ fn is_word_byte(byte: u8) -> bool { byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'$' } -/// The comment and quoted-text syntax the rules parser tokenized for `dialect`. -fn lexical_syntax(dialect: &str) -> LexicalSyntax { - let strings = |quotes: &[&str]| quotes.iter().map(|quote| (*quote).to_owned()).collect(); - let lines = |prefixes: &[&str]| prefixes.iter().map(|prefix| (*prefix).to_owned()).collect(); - match dialect.to_ascii_lowercase().as_str() { - "bigquery" => LexicalSyntax { - backslash_escape_quotes: strings(&["'", "\""]), - raw_string_prefix: true, - triple_quoted_strings: true, - line_comment_prefixes: lines(&["--", "#"]), - ..LexicalSyntax::default() - }, - "snowflake" => LexicalSyntax { - backslash_escape_quotes: strings(&["'"]), - line_comment_prefixes: lines(&["--", "//"]), - ..LexicalSyntax::default() - }, - "clickhouse" => LexicalSyntax { - backslash_escape_quotes: strings(&["'"]), - line_comment_prefixes: lines(&["--", "#", "//"]), - ..LexicalSyntax::default() - }, - "databricks" | "duckdb" => LexicalSyntax { - line_comment_prefixes: lines(&["--"]), - ..LexicalSyntax::default() - }, - "mssql" | "sqlserver" | "tsql" => LexicalSyntax { - nested_block_comments: true, - line_comment_prefixes: lines(&["--"]), - ..LexicalSyntax::default() - }, - _ => LexicalSyntax { - escape_string_prefix: true, - nested_block_comments: true, - line_comment_prefixes: lines(&["--"]), - ..LexicalSyntax::default() - }, - } -} - fn is_quote_start(byte: u8) -> bool { matches!(byte, b'\'' | b'"' | b'`' | b'$') } diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/rules/_test_types.py b/tests/e2e/src/sqlbuild/cli/commands/main/rules/_test_types.py index f4859f769..2694060a8 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/rules/_test_types.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/rules/_test_types.py @@ -384,3 +384,5 @@ class SyntaxTreeRuleE2ETestCase: description: str model_sql: str expected_messages: tuple[str, ...] + adapter: str = "duckdb" + other_models: dict[str, str] = field(default_factory=dict) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/rules/helpers.py b/tests/e2e/src/sqlbuild/cli/commands/main/rules/helpers.py index 42c1bcbcb..7dc638873 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/rules/helpers.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/rules/helpers.py @@ -73,11 +73,13 @@ def write_custom_rule_project( selected_rules: tuple[str, ...], files: tuple[tuple[str, str], ...], configuration: str = "", + adapter: str = "duckdb", ) -> None: - """Write a DuckDB project whose selected custom Rules live in the given files.""" + """Write a project, DuckDB by default, whose selected custom Rules live in the given files.""" (project_dir / "sqlbuild_project.toml").write_text( - f'name = "orders"\nadapter = "duckdb"\n\n[rules]\nselect = {json.dumps(selected_rules)}\n' + f'name = "orders"\nadapter = "{adapter}"\n\n' + f"[rules]\nselect = {json.dumps(selected_rules)}\n" "\n[rules.thresholds]\nmin_custom_rule_test_cases = 0\n" + configuration, encoding="utf-8", ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/rules/test_rule_positions.py b/tests/e2e/src/sqlbuild/cli/commands/main/rules/test_rule_positions.py index 62f051087..788b5698e 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/rules/test_rule_positions.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/rules/test_rule_positions.py @@ -140,7 +140,24 @@ def test_given_model_query_layout_when_running_rules_then_findings_use_file_posi "Star/3:o.*,", "Subquery/3:(SEL", ), - ) + ), + SyntaxTreeRuleE2ETestCase( + description="Snowflake reads relation markers as tables at their source positions", + adapter="snowflake", + model_sql=( + 'MODEL (description "Orders", database analytics, schema analytics);\n' + 'WITH c AS (SELECT * FROM __ref("customers"))\n' + 'SELECT o.*, c.name FROM __ref("raw_orders") o JOIN c ON o.id = c.id\n' + ), + other_models={ + name: ( + f'MODEL (description "{name}", database analytics, schema analytics);\n' + "SELECT 1 AS id, 'a' AS name\n" + ) + for name in ("customers", "raw_orders") + }, + expected_messages=("CTE/2:c AS", "Join/2:JOIN", "Star/2:SELE", "Star/2:o.*,"), + ), ], ids=lambda case: case.description, ) @@ -150,7 +167,11 @@ def test_given_custom_rule_over_the_syntax_tree_when_compiling_then_nodes_point_ write_custom_rule_project( project_dir=tmp_path, selected_rules=("XSQBRTREE001",), - files=(("rules/tree.py", _TREE_RULE),), + files=( + ("rules/tree.py", _TREE_RULE), + *((f"models/{name}.sql", sql) for name, sql in test_case.other_models.items()), + ), + adapter=test_case.adapter, ) (tmp_path / "models" / "orders.sql").write_text(test_case.model_sql, encoding="utf-8") From 1bba96c79364a18dfc34e66050413596f566fb2f Mon Sep 17 00:00:00 2001 From: kvlonge <273747162+kvlonge@users.noreply.github.com> Date: Sun, 8 Nov 2026 09:46:27 +0000 Subject: [PATCH 073/160] feat(sql-macros): parse SQL macros and SQL-test planning with the native parser SQL macro bodies and SQL-test planning parsed with Polyglot. Both now parse with the native parser that analysis_route gives the project's SQL analysis dialect, and a dialect with no native parser reports the feature as unavailable rather than falling back. - Macro bodies parse with the project's parser instead of trying eight Polyglot dialects. Declarations and USING slot reads are masked as names of the same length, so spans line up. A body that only names a type is read as a cast's type. - Macro probes type through NativeDialect::analyse over stand-in relations; returns, ANY-family, argument and slot checks read its output types, and type findings are SQLBuild's B210-B219. - SQL-test planning parses with sql_test_parser(dialect). The planning template reads relation marker calls as plain names, so Snowflake accepts them in FROM, and a list after IN is read as a list. - Polyglot's output-typing entry points, used only by SQL macros, and the token-based simple-template fast path are removed. --- .../compiler/_helpers/sql_tests/assembly.rs | 5 + .../_helpers/sql_tests/check_independence.rs | 49 ++- .../compiler/_helpers/sql_tests/constants.rs | 2 - .../_helpers/sql_tests/expected_columns.rs | 15 +- .../_helpers/sql_tests/helper_names.rs | 12 +- .../_helpers/sql_tests/helper_scope.rs | 6 +- .../compiler/_helpers/sql_tests/mock_reads.rs | 56 ++- .../src/compiler/_helpers/sql_tests/mod.rs | 2 - .../compiler/_helpers/sql_tests/planning.rs | 96 ++--- .../_helpers/sql_tests/relation_markers.rs | 103 ++++-- .../compiler/_helpers/sql_tests/rendering.rs | 15 +- .../_helpers/sql_tests/scenario_extraction.rs | 4 +- .../_helpers/sql_tests/simple_template.rs | 266 -------------- .../main/scenario_expected_columns.rs | 5 +- .../main/sql_scenario_classification.rs | 7 +- .../sqlbuild-analysis/src/compiler/models.rs | 2 + .../sqlbuild-analysis/src/compiler/tests.rs | 6 +- .../src/compiler/tests/helpers.rs | 150 +------- .../tests/relation_markers/helpers.rs | 145 ++++++++ .../compiler/tests/simple_template/helpers.rs | 114 ------ .../tests/test_sql_test_expected_columns.rs | 11 +- .../tests/test_sql_test_helper_references.rs | 1 + .../tests/test_sql_test_name_isolation.rs | 7 +- .../compiler/tests/test_sql_test_planning.rs | 16 +- .../tests/test_sql_test_relation_markers.rs | 65 +++- .../tests/test_sql_test_simple_template.rs | 340 ------------------ .../src/compiler/tests/test_types.rs | 19 - .../_helpers/annotated_types.rs | 47 --- .../_helpers/compatibility_types.rs | 35 -- .../main/annotated_output_types.rs | 55 --- .../src/query_analysis/main/mod.rs | 2 - .../src/query_analysis/main/output_types.rs | 38 -- .../src/query_analysis/mod.rs | 3 - .../src/query_analysis/types.rs | 6 - .../src/sql_macros/_helpers/parsing/body.rs | 154 +++++--- .../sql_macros/_helpers/parsing/captures.rs | 10 +- .../sql_macros/_helpers/parsing/parameters.rs | 97 ++++- .../_helpers/rendering/cte_rename.rs | 73 ++-- .../_helpers/rendering/expansion.rs | 41 ++- .../src/sql_macros/_helpers/typing/probes.rs | 132 +++++-- .../_helpers/typing/return_types.rs | 108 +++--- .../_helpers/typing/returns_check.rs | 141 +++----- .../sql_macros/_helpers/typing/typed_calls.rs | 31 +- .../src/sql_macros/constants.rs | 19 +- .../src/sql_macros/models.rs | 5 - .../src/sql_macros/tests/helpers.rs | 22 ++ .../src/sql_macros/tests/test_expansion.rs | 122 +++++-- .../src/sql_macros/tests/test_typed_calls.rs | 3 +- .../sqlbuild-analysis/src/sql_macros/types.rs | 7 + .../src/sql_test_attachment/tests/helpers.rs | 3 +- .../compile_driver/assembly_project.rs | 8 +- .../compile_driver/assembly_sql_tests.rs | 5 +- .../_helpers/input_loops/scenario_loop.rs | 11 +- .../_helpers/render/declaration_values.rs | 5 + .../_helpers/render/sql_macro_host.rs | 6 + .../_helpers/sql_test_glue/assembly.rs | 2 + .../_helpers/sql_test_glue/compiled_tests.rs | 4 +- src/sqlbuild/_native.pyi | 2 +- src/sqlbuild/compiler/compile/types.py | 1 + .../sql_macros/test_sql_macro_returns.py | 230 +++++++----- .../sql_macros/test_sql_macro_types.py | 54 ++- .../compiler/sql_test_glue/helpers.py | 2 +- .../compiler/planner/_helpers/helpers.py | 1 + .../sql_test_assembly/test_plan_test.py | 6 + 64 files changed, 1317 insertions(+), 1693 deletions(-) delete mode 100644 crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/constants.rs delete mode 100644 crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/simple_template.rs create mode 100644 crates/sqlbuild-analysis/src/compiler/tests/relation_markers/helpers.rs delete mode 100644 crates/sqlbuild-analysis/src/compiler/tests/simple_template/helpers.rs delete mode 100644 crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_simple_template.rs delete mode 100644 crates/sqlbuild-analysis/src/query_analysis/_helpers/annotated_types.rs delete mode 100644 crates/sqlbuild-analysis/src/query_analysis/main/annotated_output_types.rs delete mode 100644 crates/sqlbuild-analysis/src/query_analysis/main/output_types.rs delete mode 100644 crates/sqlbuild-analysis/src/query_analysis/types.rs diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs index 53a4e5c28..4ea04d9ca 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs @@ -3,11 +3,13 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; +use crate::analysis_route::types::NativeDialect; use crate::compiler::_helpers::sql_tests::case_identity::{FingerprintFailure, case_fingerprint}; use crate::compiler::_helpers::sql_tests::macro_call_names::macro_call_names; use crate::compiler::_helpers::sql_tests::mock_reads::{ MockReadsRequest, mock_reading_helper_diagnostics, }; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; use crate::compiler::models::{ AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyFailure, SqlTestAssemblyModel, SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyTest, SqlTestHelperDiagnostic, @@ -37,6 +39,7 @@ pub(crate) fn assemble_batch(batch: &SqlTestAssemblyBatch) -> Vec { models: &'a [SqlTestAssemblyModel], macro_names: &'a [MacroNames], syntax: &'a LexicalSyntax, + parser: Option<&'static dyn NativeDialect>, } fn assemble( @@ -94,6 +98,7 @@ fn assemble( models, target_model_names: &target_model_names, syntax, + parser: project.parser, })?; target_model_names .iter() diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/check_independence.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/check_independence.rs index ae53d08a1..431bc1ea8 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/check_independence.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/check_independence.rs @@ -4,16 +4,15 @@ use std::collections::{BTreeSet, HashMap}; use sqlbuild_core::graph_walk::main::breadth_first_path::breadth_first_path; -use polyglot_sql::{Dialect, DialectType, Expression, ExpressionWalk}; use serde_json::Value; use sqlbuild_sqltext::sql_scan::main::dialect_non_code_end::dialect_non_code_end; use sqlbuild_sqltext::sql_scan::models::{LexicalSyntax, Unclosed}; +use sqlquack_syntax::{Expression, ExpressionWalk, Parsed}; -use crate::compiler::_helpers::polyglot::parse_options::guarded_parse_options; +use crate::analysis_route::types::NativeDialect; use crate::compiler::constants::{ALIAS_FIELD, CTES_FIELD, NAME_FIELD}; const DIRECT_DEPENDENCY_PATH_LENGTH: usize = 2; -const TABLE_KIND: &str = "table"; const IDENTIFIER_QUOTES: &[u8] = b"\"`"; /// The check CTEs of one file and the labels its messages name. @@ -23,6 +22,8 @@ pub(crate) struct IndependenceCheck<'a> { pub(crate) assertion_prefix: &'a str, pub(crate) file_label: &'a str, pub(crate) context_label: &'a str, + /// The project's native parser; without one, references come from the identifier scan. + pub(crate) parser: Option<&'a dyn NativeDialect>, } /// The first independence error of `check`, if any. @@ -55,7 +56,7 @@ pub(crate) fn independence_error( if !casefold(body).contains(&casefold(prohibited_prefix)) { continue; } - if let Some(nested) = defined_cte_names(body) + if let Some(nested) = defined_cte_names(check.parser, body) .into_iter() .find(|nested| nested.starts_with(prohibited_prefix)) { @@ -71,7 +72,7 @@ pub(crate) fn independence_error( } let mut dependencies: HashMap> = HashMap::new(); for (name, body) in check.ctes { - let references = match known_cte_references(body, &names_by_key, syntax) { + let references = match known_cte_references(check.parser, body, &names_by_key, syntax) { Ok(references) => references, Err(message) => return Some(message), }; @@ -113,21 +114,20 @@ fn casefold(text: &str) -> String { text.to_lowercase() } -/// `parse_one(sql, dialect="generic")` under SQLBuild's complexity guard, or `None` on error. -fn parse_one(sql: &str) -> Option { - let Ok(options) = guarded_parse_options() else { - return None; - }; - let Ok(mut statements) = Dialect::get(DialectType::Generic).parse_with_options(sql, &options) - else { - return None; - }; - (statements.len() == 1).then(|| statements.remove(0)) +/// The one statement the project's parser reads from `sql` whole, or `None`. +fn parse_one(parser: Option<&dyn NativeDialect>, sql: &str) -> Option { + match parser?.parse(sql) { + Ok(Parsed { + mut statements, + gaps, + }) if gaps.is_empty() && statements.len() == 1 => statements.pop(), + _ => None, + } } /// Every CTE alias defined anywhere in `sql`, in the order `to_dict()` lists them. -fn defined_cte_names(sql: &str) -> Vec { - let Some(parsed) = parse_one(sql) else { +fn defined_cte_names(parser: Option<&dyn NativeDialect>, sql: &str) -> Vec { + let Some(parsed) = parse_one(parser, sql) else { return Vec::new(); }; let Ok(value) = serde_json::to_value(&parsed) else { @@ -165,6 +165,7 @@ fn collect_cte_names(value: &Value, mut names: Vec) -> Vec { /// The keys of the file's CTEs that `sql` reads, in reading order. fn known_cte_references( + parser: Option<&dyn NativeDialect>, sql: &str, names_by_key: &HashMap, syntax: &LexicalSyntax, @@ -173,21 +174,19 @@ fn known_cte_references( if !names_by_key.keys().any(|key| folded.contains(key.as_str())) { return Ok(Vec::new()); } - let Some(parsed) = parse_one(sql) else { + let Some(parsed) = parse_one(parser, sql) else { return identifier_references(sql, names_by_key, syntax); }; let mut references: Vec = Vec::new(); - for table in parsed - .dfs() - .skip(1) - .filter(|node| node.variant_name() == TABLE_KIND) - { - references = push_known(references, casefold(table.get_name()), names_by_key); + for node in parsed.dfs().skip(1) { + if let Expression::Table(table) = node { + references = push_known(references, casefold(&table.name.name), names_by_key); + } } Ok(references) } -/// The identifier scan used when Polyglot cannot parse a body. +/// The identifier scan used when the project's parser cannot read a body. fn identifier_references( sql: &str, names_by_key: &HashMap, diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/constants.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/constants.rs deleted file mode 100644 index 5c74d8b07..000000000 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) const SIMPLE_TEMPLATE_DIALECTS: [&str; 2] = ["duckdb", "snowflake"]; -pub(crate) const MAX_TEMPLATE_DEPTH: usize = 32; diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/expected_columns.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/expected_columns.rs index 3a021b67b..cac9bfccf 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/expected_columns.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/expected_columns.rs @@ -2,14 +2,17 @@ use std::collections::HashSet; -use polyglot_sql::expressions::{Identifier, Select}; -use polyglot_sql::{Dialect, Expression}; +use sqlquack_syntax::expressions::names::models::Identifier; +use sqlquack_syntax::expressions::queries::models::Select; +use sqlquack_syntax::{Expression, Parsed}; + +use crate::analysis_route::types::NativeDialect; /// Return the authored output column identifiers of `sql`, or `None` when they are not explicit. -pub(crate) fn expected_columns(sql: &str, dialect: &Dialect) -> Option> { - let mut statements = match dialect.parse(sql) { - Ok(statements) => statements, - Err(_) => return None, +pub(crate) fn expected_columns(sql: &str, parser: &dyn NativeDialect) -> Option> { + let mut statements = match parser.parse(sql) { + Ok(Parsed { statements, gaps }) if gaps.is_empty() => statements, + _ => return None, }; if statements.len() != 1 { return None; diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_names.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_names.rs index 77ed5bcf9..b31844252 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_names.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_names.rs @@ -2,7 +2,9 @@ use std::collections::{HashMap, HashSet}; -use polyglot_sql::{Dialect, Expression, ExpressionWalk}; +use sqlquack_syntax::{Expression, ExpressionWalk, Parsed}; + +use crate::analysis_route::types::NativeDialect; pub(crate) const HELPER_PREFIX: &str = "__helper__"; @@ -15,11 +17,11 @@ pub(crate) fn helper_cte_name(name: &str) -> String { pub(crate) fn rename_helper_references( sql: &str, helpers: &HashMap, - dialect: &Dialect, + parser: &dyn NativeDialect, ) -> Option { - let statements = match dialect.parse(sql) { - Ok(statements) => statements, - Err(_) => return None, + let statements = match parser.parse(sql) { + Ok(Parsed { statements, gaps }) if gaps.is_empty() => statements, + _ => return None, }; let [statement] = statements.as_slice() else { return None; diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_scope.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_scope.rs index 6f042f9d3..0ecf99580 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_scope.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/helper_scope.rs @@ -4,9 +4,9 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use sqlbuild_core::graph_walk::main::depth_first_postorder::depth_first_postorder; -use polyglot_sql::Dialect; use rustc_hash::FxHashSet; +use crate::analysis_route::types::NativeDialect; use crate::compiler::_helpers::sql_tests::cte_rename::defined_cte_keys; use crate::compiler::_helpers::sql_tests::cte_slices::SliceDialect; use crate::compiler::_helpers::sql_tests::cte_sql::{with_leading_ctes, with_unique_ctes}; @@ -84,7 +84,7 @@ pub(crate) struct ScopeRequest<'a> { pub(crate) fixtures: &'a TestFixtures, pub(crate) patterns: &'a SqlTestPatterns, pub(crate) file_label: &'a str, - pub(crate) rename_dialect: Option<&'a Dialect>, + pub(crate) rename_dialect: Option<&'a dyn NativeDialect>, pub(crate) flat_dialect: bool, pub(crate) slice_dialect: SliceDialect, /// Helpers the compiler found the test reads; an unread helper calling a reference is left out. @@ -221,7 +221,7 @@ impl ScopeGraph { fn rewrite_readers( &self, readers: &[(String, &str)], - dialect: &Dialect, + dialect: &dyn NativeDialect, patterns: &SqlTestPatterns, ) -> Option> { let names: HashMap = self diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs index 97e790075..aace735a9 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs @@ -3,10 +3,6 @@ use std::collections::HashSet; use std::convert::Infallible; -use polyglot_sql::expressions::With; -use polyglot_sql::{ - ComplexityGuardOptions, Dialect, DialectType, Expression, ExpressionWalk, ParseOptions, -}; use sqlbuild_core::graph_walk::main::breadth_first_reach::breadth_first_reach; use sqlbuild_core::graph_walk::models::VisitOrder; use sqlbuild_core::graph_walk::types::NodeSet; @@ -15,7 +11,10 @@ use sqlbuild_core::text::main::is_word_char::is_word_char; use sqlbuild_sqltext::sql_references::main::extract_sql_references::extract_sql_references; use sqlbuild_sqltext::sql_references::models::{ReferenceExtraction, SqlReference}; use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; +use sqlquack_syntax::expressions::queries::models::With; +use sqlquack_syntax::{Expression, ExpressionWalk, Parsed}; +use crate::analysis_route::types::NativeDialect; use crate::compiler::_helpers::sql_tests::planning::MOCK_CTE_PREFIXES; use crate::compiler::models::{ SqlTestAssemblyFailure, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, @@ -32,8 +31,6 @@ const RELATION_KINDS: [&str; 5] = [ DBT_REF_KIND, TABLE_FUNCTION_KIND, ]; -/// The function call depth SQLBuild's Polyglot proxy allows `parse_one`. -const MAX_FUNCTION_CALL_DEPTH: usize = 512; type Deferrable = Result; @@ -52,6 +49,8 @@ pub(crate) struct MockReadsRequest<'a> { pub(crate) models: &'a [SqlTestAssemblyModel], pub(crate) target_model_names: &'a [String], pub(crate) syntax: &'a LexicalSyntax, + /// The project's native parser; without one, reads come from the token scan. + pub(crate) parser: Option<&'a dyn NativeDialect>, } /// A Python dict by string key: a repeated key keeps its first position and takes the new value. @@ -155,7 +154,7 @@ pub(crate) fn mock_reading_helper_diagnostics( .iter() .chain(&payload.assertion_ctes) .collect(); - let graph = cte_graph(&payload.authored_ctes, &readers)?; + let graph = cte_graph(request.parser, &payload.authored_ctes, &readers)?; let mut scanned: Vec<&str> = readers.iter().map(|cte| cte.sql_body.as_str()).collect(); for key in read_helper_keys(&graph) { if let Some(cte) = graph.cte(&key) { @@ -241,7 +240,11 @@ fn model_reference(reference: &SqlTestAssemblyReference) -> Reference { } /// Python's `sql_test_cte_graph`. -fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferrable> { +fn cte_graph<'a>( + parser: Option<&dyn NativeDialect>, + authored: &'a [SqlTestCte], + readers: &[&SqlTestCte], +) -> Deferrable> { let mut ctes: OrderedEntries<&'a SqlTestCte> = OrderedEntries::new(); for cte in authored { ctes.assign(fold(&cte.name), cte); @@ -250,7 +253,7 @@ fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferra let mut reads: OrderedEntries> = OrderedEntries::new(); for (key, cte) in &ctes.entries { let others: Vec = keys.iter().filter(|other| *other != key).cloned().collect(); - let cte_reads = match parsed_reads(&cte.sql_body, &others)? { + let cte_reads = match parsed_reads(parser, &cte.sql_body, &others) { Some(parsed) => parsed, None => token_reads(&cte.sql_body, &others), }; @@ -258,7 +261,7 @@ fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferra } let mut reader_reads: Vec = Vec::new(); for reader in readers { - let parsed = match parsed_reads(&reader.sql_body, &keys)? { + let parsed = match parsed_reads(parser, &reader.sql_body, &keys) { Some(parsed) => parsed, None => token_reads(&reader.sql_body, &keys), }; @@ -276,18 +279,24 @@ fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferra } /// Python's `_parsed_reads`: unqualified reads of `keys` no nested CTE shadows, or None unparsed. -fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { +fn parsed_reads( + parser: Option<&dyn NativeDialect>, + sql: &str, + keys: &[String], +) -> Option> { let folded = fold(sql); if !keys.iter().any(|key| folded.contains(key.as_str())) { - return Ok(Some(Vec::new())); + return Some(Vec::new()); } - let options = proxy_parse_options()?; - let Ok(mut statements) = Dialect::get(DialectType::Generic).parse_with_options(sql, &options) + let Ok(Parsed { + mut statements, + gaps, + }) = parser?.parse(sql) else { - return Ok(None); + return None; }; - if statements.len() != 1 { - return Ok(None); + if statements.len() != 1 || !gaps.is_empty() { + return None; } let parsed: Expression = statements.remove(0); let nested = defined_cte_keys(&parsed); @@ -304,18 +313,7 @@ fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { reads.push(key); } } - Ok(Some(reads)) -} - -/// The parse options SQLBuild's Polyglot proxy gives `parse_one`. -fn proxy_parse_options() -> Deferrable { - let guard: ComplexityGuardOptions = serde_json::from_value(serde_json::json!({ - "maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH, - })) - .map_err(|error| SqlTestAssemblyFailure::Internal(error.to_string()))?; - Ok(ParseOptions { - complexity_guard: Some(guard), - }) + Some(reads) } /// Python's `_defined_cte_keys` over `to_dict()`. diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs index 745b163bc..5c0b7d52b 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs @@ -2,7 +2,6 @@ pub mod assembly; pub mod authored_macro_calls; pub mod case_identity; pub(crate) mod check_independence; -pub(crate) mod constants; pub mod cte_rename; pub mod cte_slices; pub mod cte_sql; @@ -22,4 +21,3 @@ pub mod relation_markers; pub mod rendering; pub mod scenario_extraction; pub(crate) mod scenario_scope; -pub(crate) mod simple_template; diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs index cb2d4599a..c774b21cd 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/planning.rs @@ -5,14 +5,15 @@ use std::convert::Infallible; use std::sync::{Arc, Mutex, OnceLock, PoisonError}; use std::time::Instant; -use polyglot_sql::{Dialect, Expression}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use regex::Regex; #[cfg(test)] use serde::Serialize; use sqlbuild_core::graph_walk::main::breadth_first_reach::breadth_first_reach; use sqlbuild_core::graph_walk::main::depth_first_postorder::depth_first_postorder; +use sqlquack_syntax::{Expression, Parsed}; +use crate::analysis_route::types::NativeDialect; use crate::compiler::_helpers::sql_tests::cte_rename::defined_cte_keys; use crate::compiler::_helpers::sql_tests::cte_slices::{SliceDialect, strip_statement_terminators}; use crate::compiler::_helpers::sql_tests::cte_sql::{ @@ -34,9 +35,11 @@ use crate::compiler::_helpers::sql_tests::reader_references::{ use crate::compiler::_helpers::sql_tests::real_model_limit::{ RealModelChain, real_model_limit_message, }; -use crate::compiler::_helpers::sql_tests::relation_markers::relation_marker_calls; +use crate::compiler::_helpers::sql_tests::relation_markers::{ + markers_as_names, named_marker_calls, +}; use crate::compiler::_helpers::sql_tests::rendering::{ - RenderRequest, render_comparison_sql, render_dialect, rendered_chain_steps, + RenderRequest, render_comparison_sql, rendered_chain_steps, sql_test_parser, }; use crate::compiler::models::{ SqlTestAssertionStep, SqlTestChainBatch, SqlTestChainStep, SqlTestCte, SqlTestPlan, @@ -95,7 +98,7 @@ struct ProjectContext { requires_derived_table_aliases: bool, analysis_templates: Arc, patterns: SqlTestPatterns, - render_dialect: Arc, + parser: &'static dyn NativeDialect, rejects_nested_with: bool, max_real_models: Option, /// Each model query's reserved CTE name, since every test reading the model checks it. @@ -111,6 +114,8 @@ pub(crate) struct SqlTestPatterns { table_function: Regex, pub(crate) dbt_reference: Regex, pub(crate) test_reference: Regex, + /// Any relation marker call: `__name("x")`, or `__name("a", "b")` as `__dbt_ref` writes it. + pub(crate) relation_marker: Regex, pub(crate) lexical: LexicalSyntax, pub(crate) identifier: Regex, } @@ -127,6 +132,9 @@ impl SqlTestPatterns { r#"(?i)__dbt_ref\(\s*\"([^\"]+)\"\s*(?:,\s*\"([^\"]+)\"\s*)?\)"#, )?, test_reference: compile_pattern(r#"(?i)__(?:ref|source|seed|dbt_ref|table_fn)\("#)?, + relation_marker: compile_pattern( + r#"(?i)\b(__[a-z_]+)\(\s*"([^"]+)"\s*(?:,\s*"([^"]+)"\s*)?\)"#, + )?, lexical, identifier: compile_pattern(r"[A-Za-z_][A-Za-z0-9_$]*")?, }) @@ -319,7 +327,7 @@ struct AnalysisResolutionRequest<'a> { resolved_chain: &'a HashMap, functions: &'a HashMap, file_label: &'a str, - dialect_name: &'a str, + parser: &'a dyn NativeDialect, templates: &'a AnalysisTemplateCache, patterns: &'a SqlTestPatterns, } @@ -509,7 +517,18 @@ pub(crate) fn plan_and_render_json(request_json: &str) -> Result pub(crate) fn plan_batch(request: SqlTestPlanBatch) -> Result { let render_sql = request.render_sql; let include_plan = request.include_plan; - let render_dialect = Arc::new(render_dialect(request.sql_analysis_dialect.as_deref())); + let Some(parser) = sql_test_parser(request.sql_analysis_dialect.as_deref()) else { + if request.tests.is_empty() { + return Ok(SqlTestPlanBatchOutcome { + plans: Vec::new(), + planning_ns: 0, + rendering_ns: 0, + }); + } + return Err(unavailable_sql_tests( + request.sql_analysis_dialect.as_deref(), + )); + }; let rejects_nested_with = SliceDialect::new(request.sql_analysis_dialect.as_deref()).rejects_nested_with(); let context = ProjectContext { @@ -539,7 +558,7 @@ pub(crate) fn plan_batch(request: SqlTestPlanBatch) -> Result { resolved_chain: self.analysis_resolved, functions: &context.functions, file_label: self.file_label, - dialect_name: &context.dialect, + parser: context.parser, templates: &context.analysis_templates, patterns: &context.patterns, })? @@ -1378,8 +1397,11 @@ fn real_model_inputs(request: &TopoSortRequest<'_>, name: &str) -> Vec { fn analyze_and_resolve_sql( request: AnalysisResolutionRequest<'_>, ) -> Result, String> { - let Some(template) = - analysis_template(request.query_sql, request.dialect_name, request.templates)? + let Some(template) = analysis_template( + request.query_sql, + (request.parser, request.patterns), + request.templates, + )? else { return Ok(None); }; @@ -1470,7 +1492,7 @@ fn analyze_and_resolve_sql( )?; let cte_body_sql = strip_statement_terminators( &functions_resolved, - SliceDialect::new(Some(request.dialect_name)), + SliceDialect::new(Some(request.parser.name())), ) .to_string(); let resolved_sql = with_leading_ctes(&generated_state.generated, &cte_body_sql); @@ -1482,43 +1504,25 @@ fn analyze_and_resolve_sql( })) } -#[cfg(test)] -thread_local! { - static TEMPLATE_AST_PARSES: std::cell::Cell = const { std::cell::Cell::new(0) }; -} - -#[cfg(test)] -pub(crate) fn take_template_ast_parse_count() -> usize { - TEMPLATE_AST_PARSES.with(|count| count.replace(0)) -} - pub(crate) fn analysis_template( query_sql: &str, - dialect_name: &str, + reader: (&dyn NativeDialect, &SqlTestPatterns), templates: &AnalysisTemplateCache, ) -> Result, String> { cached_analysis_template(query_sql, templates, || { - Dialect::get_by_name(dialect_name).and_then(|dialect| { - if let Some(template) = - super::simple_template::simple_analysis_template(query_sql, dialect_name, &dialect) - { - return Some(template); - } - parsed_analysis_template(query_sql, &dialect) - }) + parsed_analysis_template(query_sql, reader) }) } -/// The full-parser path also serves as the differential oracle for the bounded recognizer. +/// The template the parser reads whole from `query_sql`, its relation markers read as names. pub(crate) fn parsed_analysis_template( query_sql: &str, - dialect: &Dialect, + (parser, patterns): (&dyn NativeDialect, &SqlTestPatterns), ) -> Option { - #[cfg(test)] - TEMPLATE_AST_PARSES.with(|count| count.set(count.get() + 1)); - let mut statements = match dialect.parse(query_sql) { - Ok(statements) => statements, - Err(_) => return None, + let (named_sql, named) = markers_as_names(query_sql, patterns); + let mut statements = match parser.parse(&named_sql) { + Ok(Parsed { statements, gaps }) if gaps.is_empty() => statements, + _ => return None, }; if statements.len() != 1 { return None; @@ -1526,10 +1530,20 @@ pub(crate) fn parsed_analysis_template( let expression = statements.remove(0); Some(AnalysisTemplate { existing_cte_names: top_level_cte_names(&expression), - marker_calls: relation_marker_calls(&expression).into(), + marker_calls: named_marker_calls(&expression, &named).into(), }) } +/// Why SQL tests cannot be planned for a project whose SQL analysis dialect has no native parser. +fn unavailable_sql_tests(dialect: Option<&str>) -> String { + match dialect { + Some(dialect) => format!( + "SQL tests are unavailable for SQL analysis dialect '{dialect}', which SQLBuild has no native parser for" + ), + None => "SQL tests are unavailable without a SQL analysis dialect".to_owned(), + } +} + pub(crate) fn cached_analysis_template( query_sql: &str, templates: &AnalysisTemplateCache, diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/relation_markers.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/relation_markers.rs index 8f8e991ae..daaaa50dc 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/relation_markers.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/relation_markers.rs @@ -1,63 +1,122 @@ //! Relation marker calls in a serialized pre-order walk of a parsed statement's relations. -use polyglot_sql::expressions::{From, Join}; -use polyglot_sql::{Expression, ExpressionWalk}; +use sqlquack_syntax::expressions::queries::models::{From, Join}; +use sqlquack_syntax::{Expression, ExpressionWalk}; -use crate::compiler::_helpers::sql_tests::planning::DBT_REF_FUNCTION; +use crate::compiler::_helpers::sql_tests::markers::ProtectedRanges; +use crate::compiler::_helpers::sql_tests::planning::{DBT_REF_FUNCTION, SqlTestPatterns}; pub(crate) type MarkerCall = (String, String); -/// Return marker calls in node order, where Polyglot children follow declared field order. -pub(crate) fn relation_marker_calls(expression: &Expression) -> Vec { - expression.dfs().fold(Vec::new(), node_marker_calls) +/// The name a relation marker call stands as while the planning template is parsed. +const MARKER_NAME_PREFIX: &str = "__sqlbuild_relation_"; + +/// Marker calls in node order, the relations `named` by `markers_as_names` read back as their calls. +pub(crate) fn named_marker_calls(expression: &Expression, named: &[MarkerCall]) -> Vec { + expression.dfs().fold(Vec::new(), |calls, node| { + node_marker_calls(calls, node, named) + }) +} + +/// `sql` with each relation marker call in code as a plain name, which Snowflake reads in FROM. +pub(crate) fn markers_as_names(sql: &str, patterns: &SqlTestPatterns) -> (String, Vec) { + let mut protected: ProtectedRanges<'_> = ProtectedRanges::new(&patterns.lexical, sql); + let mut named: String = String::with_capacity(sql.len()); + let mut calls: Vec = Vec::new(); + let mut cursor: usize = 0; + for captures in patterns.relation_marker.captures_iter(sql) { + let (Some(full), Some(function), Some(first)) = + (captures.get(0), captures.get(1), captures.get(2)) + else { + continue; + }; + if protected.contains(full.start()) { + continue; + } + let function_name: String = function.as_str().to_ascii_lowercase(); + let referenced_name: String = match (captures.get(3), function_name == DBT_REF_FUNCTION) { + (None, _) => first.as_str().to_owned(), + (Some(second), true) => format!("{}__{}", first.as_str(), second.as_str()), + (Some(_), false) => continue, + }; + named.push_str(&sql[cursor..full.start()]); + named.push_str(&format!("{MARKER_NAME_PREFIX}{}", calls.len())); + cursor = full.end(); + calls.push((function_name, referenced_name)); + } + named.push_str(&sql[cursor..]); + (named, calls) } /// FROM then JOIN marker calls of one node, plus the serialized `on` that traversal skips. -fn node_marker_calls(mut calls: Vec, node: &Expression) -> Vec { +fn node_marker_calls( + mut calls: Vec, + node: &Expression, + named: &[MarkerCall], +) -> Vec { match node { Expression::Select(select) => calls.extend( select .from .iter() - .flat_map(from_markers) - .chain(join_markers(&select.joins)), + .flat_map(|from| from_markers(from, named)) + .chain(join_markers(&select.joins, named)), ), - Expression::From(from) => calls.extend(from_markers(from)), - Expression::Delete(delete) => calls.extend(join_markers(&delete.joins)), + Expression::From(from) => calls.extend(from_markers(from, named)), + Expression::Delete(delete) => calls.extend(join_markers(&delete.joins, named)), Expression::JoinedTable(table) => { calls.extend( - marker(&table.left) + marker(&table.left, named) .into_iter() - .chain(join_markers(&table.joins)), + .chain(join_markers(&table.joins, named)), ); } Expression::Update(update) => calls.extend( update .from_clause .iter() - .flat_map(from_markers) - .chain(join_markers(&update.from_joins)), + .flat_map(|from| from_markers(from, named)) + .chain(join_markers(&update.from_joins, named)), ), Expression::DataDeletionProperty(property) => { - return property.on.dfs().fold(calls, node_marker_calls); + return property + .on + .dfs() + .fold(calls, |calls, node| node_marker_calls(calls, node, named)); } _ => {} } calls } -fn from_markers(from: &From) -> impl Iterator + '_ { - from.expressions.iter().filter_map(marker) +fn from_markers<'a>( + from: &'a From, + named: &'a [MarkerCall], +) -> impl Iterator + 'a { + from.expressions + .iter() + .filter_map(|expression| marker(expression, named)) } -fn join_markers(joins: &[Join]) -> impl Iterator + '_ { - joins.iter().filter_map(|join| marker(&join.this)) +fn join_markers<'a>( + joins: &'a [Join], + named: &'a [MarkerCall], +) -> impl Iterator + 'a { + joins.iter().filter_map(|join| marker(&join.this, named)) } -fn marker(expression: &Expression) -> Option { +fn marker(expression: &Expression, named: &[MarkerCall]) -> Option { match expression { - Expression::Alias(alias) => marker(&alias.this), + Expression::Alias(alias) => marker(&alias.this, named), Expression::Function(function) => function_marker(&function.name, &function.args), + Expression::Table(table) if table.schema.is_none() => table + .name + .name + .strip_prefix(MARKER_NAME_PREFIX) + .and_then(|index| match index.parse::() { + Ok(index) => named.get(index).cloned(), + Err(_) => None, + }), _ => None, } } diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/rendering.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/rendering.rs index d1d2e2f72..eff8501ae 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/rendering.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/rendering.rs @@ -1,9 +1,10 @@ //! Deterministic comparison SQL rendering for planned SQL-native tests. +use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::models::AnalysisRoute; +use crate::analysis_route::types::NativeDialect; use std::collections::{HashMap, HashSet}; -use polyglot_sql::{Dialect, DialectType}; - use crate::compiler::_helpers::sql_tests::cte_rename::defined_cte_keys; use crate::compiler::_helpers::sql_tests::cte_rename::{CteRename, rename_ctes}; use crate::compiler::_helpers::sql_tests::cte_slices::{ @@ -602,10 +603,12 @@ fn render_response(request: RenderRequest) -> Result { }) } -/// Resolve the SQL-analysis dialect used to read expected-row columns. -pub(crate) fn render_dialect(name: Option<&str>) -> Dialect { - name.and_then(Dialect::get_by_name) - .unwrap_or_else(|| Dialect::get(DialectType::Generic)) +/// The native parser that reads SQL tests in analysis dialect `name`; `None` leaves them unavailable. +pub(crate) fn sql_test_parser(name: Option<&str>) -> Option<&'static dyn NativeDialect> { + match analysis_route(name) { + AnalysisRoute::Native(parser) => Some(parser), + _ => None, + } } pub(crate) fn render_difference_sample_json(request_json: &str) -> Result { diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/scenario_extraction.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/scenario_extraction.rs index 35b06efd6..0fb49c2f7 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/scenario_extraction.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/scenario_extraction.rs @@ -4,6 +4,7 @@ use sqlbuild_scopes::relationship_names::main::top_level_ctes::scan_top_level_ct use sqlbuild_scopes::relationship_names::models::{RelationshipSource, TopLevelCtes}; use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; +use crate::analysis_route::types::NativeDialect; use crate::compiler::_helpers::sql_tests::check_independence::{ IndependenceCheck, independence_error, }; @@ -24,7 +25,7 @@ const SCENARIO_CONTEXT: &str = "SQL scenario"; pub(crate) fn extract_scenario( sql: &str, file: &str, - syntax: &LexicalSyntax, + (syntax, parser): (&LexicalSyntax, Option<&dyn NativeDialect>), ) -> Result { let ctes = match scan_top_level_ctes(sql, file, RelationshipSource::Scenario, syntax) { TopLevelCtes::Failed(message) => return Err(message), @@ -37,6 +38,7 @@ pub(crate) fn extract_scenario( assertion_prefix: ASSERT_PREFIX, file_label: file, context_label: SCENARIO_CONTEXT, + parser, }, syntax, ) { diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/simple_template.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/simple_template.rs deleted file mode 100644 index 6709e3376..000000000 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/simple_template.rs +++ /dev/null @@ -1,266 +0,0 @@ -//! Closed token productions for test-planning facts; unrecognized input uses the full parser. - -use std::collections::HashSet; - -use polyglot_sql::Dialect; -use polyglot_sql::parser::NO_PAREN_FUNCTION_NAMES; -use polyglot_sql::tokens::{Token, TokenType}; - -use crate::compiler::_helpers::sql_tests::constants::{ - MAX_TEMPLATE_DEPTH, SIMPLE_TEMPLATE_DIALECTS, -}; -use crate::compiler::_helpers::sql_tests::planning::{AnalysisTemplate, DBT_REF_FUNCTION}; -use crate::compiler::_helpers::sql_tests::relation_markers::MarkerCall; - -/// Recognize bounded DuckDB/Snowflake productions; other syntax retains the existing parser. -pub(crate) fn simple_analysis_template( - sql: &str, - dialect_name: &str, - dialect: &Dialect, -) -> Option { - if !SIMPLE_TEMPLATE_DIALECTS.contains(&dialect_name) || sql.contains("/*") { - return None; - } - let tokens = match dialect.tokenize(sql) { - Ok(tokens) => tokens, - Err(_) => return None, - }; - let mut reader = TemplateReader { - tokens: &tokens, - position: 0, - }; - let facts = reader.query(0)?; - reader.take(TokenType::Semicolon); - if reader.position != tokens.len() { - return None; - } - Some(AnalysisTemplate { - existing_cte_names: facts.names, - marker_calls: facts.calls.into(), - }) -} - -struct QueryFacts { - names: HashSet, - calls: Vec, -} - -struct TemplateReader<'a> { - tokens: &'a [Token], - position: usize, -} - -impl<'a> TemplateReader<'a> { - fn take(&mut self, kind: TokenType) -> bool { - if self - .tokens - .get(self.position) - .is_some_and(|token| token.token_type == kind) - { - self.position += 1; - true - } else { - false - } - } - - fn require(&mut self, kind: TokenType) -> Option<()> { - self.take(kind).then_some(()) - } - - fn identifier(&mut self) -> Option<&'a str> { - let token = self.tokens.get(self.position)?; - if !matches!( - token.token_type, - TokenType::Var | TokenType::Identifier | TokenType::QuotedIdentifier | TokenType::Final - ) { - return None; - } - self.position += 1; - Some(&token.text) - } - - fn query(&mut self, depth: usize) -> Option { - if depth > MAX_TEMPLATE_DEPTH { - return None; - } - let mut names: HashSet = HashSet::new(); - let mut cte_calls: Vec = Vec::new(); - let has_with = self.take(TokenType::With); - if has_with { - loop { - names.insert(self.identifier()?.to_owned()); - self.require(TokenType::As)?; - self.require(TokenType::LParen)?; - cte_calls.extend(self.query(depth + 1)?.calls); - self.require(TokenType::RParen)?; - if !self.take(TokenType::Comma) { - break; - } - } - } - let mut calls = self.select(depth)?; - while self.take(TokenType::Union) { - if has_with { - return None; - } - self.require(TokenType::All)?; - if self.take(TokenType::By) { - let name = self.tokens.get(self.position)?; - if name.token_type != TokenType::Var || !name.text.eq_ignore_ascii_case("name") { - return None; - } - self.position += 1; - } - calls.extend(self.select(depth)?); - } - calls.extend(cte_calls); - Some(QueryFacts { names, calls }) - } - - fn select(&mut self, depth: usize) -> Option> { - self.require(TokenType::Select)?; - loop { - if !self.take(TokenType::Star) { - self.scalar(depth + 1)?; - if self.take(TokenType::As) { - self.identifier()?; - } - } - if !self.take(TokenType::Comma) { - break; - } - } - self.require(TokenType::From)?; - let mut calls: Vec = self.relation()?.into_iter().collect(); - loop { - let left = self.take(TokenType::Left); - if !self.take(TokenType::Join) { - if left { - return None; - } - break; - } - calls.extend(self.relation()?); - self.require(TokenType::On)?; - self.condition(depth + 1)?; - } - if self.take(TokenType::Where) { - self.condition(depth + 1)?; - } - Some(calls) - } - - fn relation(&mut self) -> Option> { - let name = self.identifier()?; - let call = if self.take(TokenType::LParen) { - let function = name.to_ascii_lowercase(); - if !matches!( - function.as_str(), - "__ref" | "__source" | "__seed" | "__dbt_ref" - ) { - return None; - } - let mut target = self.marker_argument()?.to_owned(); - if function == DBT_REF_FUNCTION && self.take(TokenType::Comma) { - target.push_str("__"); - target.push_str(self.marker_argument()?); - } - self.require(TokenType::RParen)?; - Some((function, target)) - } else { - None - }; - if self.take(TokenType::As) { - self.identifier()?; - } - Some(call) - } - - /// Require quoted Columns; unquoted arguments can invoke private dialect-specific function rules. - fn marker_argument(&mut self) -> Option<&'a str> { - let token = self.tokens.get(self.position)?; - if token.token_type != TokenType::QuotedIdentifier - || NO_PAREN_FUNCTION_NAMES.contains(token.text.to_ascii_uppercase().as_str()) - { - return None; - } - self.identifier() - } - - fn condition(&mut self, depth: usize) -> Option<()> { - self.scalar(depth)?; - self.require(TokenType::Eq)?; - self.scalar(depth) - } - - fn scalar(&mut self, depth: usize) -> Option<()> { - self.atom(depth)?; - while self.take(TokenType::Plus) - || self.take(TokenType::Dash) - || self.take(TokenType::Star) - || self.take(TokenType::Slash) - { - self.atom(depth)?; - } - Some(()) - } - - fn atom(&mut self, depth: usize) -> Option<()> { - if depth > MAX_TEMPLATE_DEPTH { - return None; - } - if self.take(TokenType::LParen) { - self.scalar(depth + 1)?; - return self.require(TokenType::RParen); - } - if self.take(TokenType::Cast) { - self.require(TokenType::LParen)?; - self.scalar(depth + 1)?; - self.require(TokenType::As)?; - let data_type = self.tokens.get(self.position)?; - if !matches!( - data_type.token_type, - TokenType::Var | TokenType::Int | TokenType::Double - ) || !["int", "integer", "double"] - .iter() - .any(|name| data_type.text.eq_ignore_ascii_case(name)) - { - return None; - } - self.position += 1; - return self.require(TokenType::RParen); - } - if let Some(token) = self.tokens.get(self.position) - && token.token_type == TokenType::Number - { - if !token.text.bytes().any(|byte| byte.is_ascii_digit()) - || !token - .text - .bytes() - .all(|byte| byte.is_ascii_digit() || byte == b'.') - || token.text.bytes().filter(|byte| *byte == b'.').count() > 1 - { - return None; - } - self.position += 1; - return Some(()); - } - let name = self.identifier()?; - if self.take(TokenType::LParen) { - if !name.eq_ignore_ascii_case("coalesce") { - return None; - } - self.scalar(depth + 1)?; - while self.take(TokenType::Comma) { - self.scalar(depth + 1)?; - } - self.require(TokenType::RParen) - } else { - if self.take(TokenType::Dot) { - self.identifier()?; - } - Some(()) - } - } -} diff --git a/crates/sqlbuild-analysis/src/compiler/main/scenario_expected_columns.rs b/crates/sqlbuild-analysis/src/compiler/main/scenario_expected_columns.rs index a1cb1379c..7ff60c155 100644 --- a/crates/sqlbuild-analysis/src/compiler/main/scenario_expected_columns.rs +++ b/crates/sqlbuild-analysis/src/compiler/main/scenario_expected_columns.rs @@ -3,7 +3,7 @@ use crate::compiler::_helpers::sql_tests::expected_columns::{ expected_columns, normalized_identifier, }; -use crate::compiler::_helpers::sql_tests::rendering::render_dialect; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; use crate::compiler::models::ScenarioExpectedColumns; /// The columns an expected CTE lists and how they differ from the known model output. @@ -13,7 +13,8 @@ pub fn scenario_expected_columns( dialect: Option<&str>, model_columns: Option<&[String]>, ) -> ScenarioExpectedColumns { - let listed: Option> = expected_columns(expected_sql, &render_dialect(dialect)); + let listed: Option> = + sql_test_parser(dialect).and_then(|parser| expected_columns(expected_sql, parser)); let (Some(listed_columns), Some(model_columns)) = (listed.as_deref(), model_columns) else { return ScenarioExpectedColumns { listed, diff --git a/crates/sqlbuild-analysis/src/compiler/main/sql_scenario_classification.rs b/crates/sqlbuild-analysis/src/compiler/main/sql_scenario_classification.rs index 96857520d..034fa791a 100644 --- a/crates/sqlbuild-analysis/src/compiler/main/sql_scenario_classification.rs +++ b/crates/sqlbuild-analysis/src/compiler/main/sql_scenario_classification.rs @@ -2,14 +2,15 @@ use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; +use crate::analysis_route::types::NativeDialect; use crate::compiler::_helpers; use crate::compiler::models::SqlScenarioCtes; -/// The scenario's classified CTEs, or its error. +/// The scenario's classified CTEs, or its error; `parser` is the project's native parser. pub fn extract_scenario( sql: &str, file_label: &str, - syntax: &LexicalSyntax, + (syntax, parser): (&LexicalSyntax, Option<&dyn NativeDialect>), ) -> Result { - _helpers::sql_tests::scenario_extraction::extract_scenario(sql, file_label, syntax) + _helpers::sql_tests::scenario_extraction::extract_scenario(sql, file_label, (syntax, parser)) } diff --git a/crates/sqlbuild-analysis/src/compiler/models.rs b/crates/sqlbuild-analysis/src/compiler/models.rs index 37775178d..c6c2e2bfe 100644 --- a/crates/sqlbuild-analysis/src/compiler/models.rs +++ b/crates/sqlbuild-analysis/src/compiler/models.rs @@ -191,6 +191,8 @@ pub struct SqlTestAssemblyBatch { pub models: Vec, pub tests: Vec, pub lexical_syntax: LexicalSyntax, + /// The project's SQL analysis dialect, whose native parser reads helper references. + pub sql_analysis_dialect: Option, } /// What assembly reads of one model: its name, macro dependencies and references. diff --git a/crates/sqlbuild-analysis/src/compiler/tests.rs b/crates/sqlbuild-analysis/src/compiler/tests.rs index 7e1eda417..a2f830e31 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests.rs @@ -2,12 +2,12 @@ mod helpers; #[path = "tests/planning/helpers.rs"] mod planning_helpers; +#[path = "tests/relation_markers/helpers.rs"] +mod relation_markers_helpers; #[path = "tests/test_scenario_expected_columns.rs"] mod scenario_expected_columns; #[path = "tests/test_scenario_helper_ctes.rs"] mod scenario_helper_ctes; -#[path = "tests/simple_template/helpers.rs"] -mod simple_template_helpers; #[path = "tests/single_read/helpers.rs"] mod single_read_helpers; #[path = "tests/test_sql_test_cte_rename.rs"] @@ -32,7 +32,5 @@ mod sql_test_planning; mod sql_test_relation_markers; #[path = "tests/test_sql_test_rendering.rs"] mod sql_test_rendering; -#[path = "tests/test_sql_test_simple_template.rs"] -mod sql_test_simple_template; #[path = "tests/test_types.rs"] mod test_types; diff --git a/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs b/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs index dea0e94b0..2d2a6aa78 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/helpers.rs @@ -2,12 +2,11 @@ use std::collections::{HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Barrier, Mutex}; -use polyglot_sql::Dialect; use serde_json::{Value, json}; use crate::compiler::_helpers::sql_tests::cte_rename::defined_cte_keys; use crate::compiler::_helpers::sql_tests::cte_slices::{SliceDialect, split_top_level_with}; -use crate::compiler::_helpers::sql_tests::relation_markers::relation_marker_calls; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; use crate::compiler::main::sql_test_extraction::extract_batch_json; use crate::compiler::tests::test_types::ExtractionRuleTestCase; use crate::compiler::tests::test_types::{ @@ -673,8 +672,9 @@ pub(crate) fn textual_assertion_with_clause_merges_lifted_ctes() -> bool { ) ); assert!( - polyglot_sql::Dialect::get(polyglot_sql::DialectType::DuckDB) - .parse(resolved_sql) + sql_test_parser(Some("duckdb")) + .expect("DuckDB parser") + .read_whole(resolved_sql) .is_ok(), "{resolved_sql}" ); @@ -1570,143 +1570,6 @@ pub(crate) fn generic_lexical_syntax() -> Value { }) } -type MarkerCalls = Option>; - -/// The original JSON-tree walk, kept as the oracle the direct relation walk must match. -fn json_relation_marker_calls(value: &Value) -> Vec<(String, String)> { - let from_markers = ["from", "from_clause"] - .into_iter() - .filter_map(|key| value.get(key)) - .filter_map(|from| from.get("expressions")) - .filter_map(Value::as_array) - .flatten() - .chain(value.get("left").filter(|_| value.get("joins").is_some())) - .filter_map(json_marker); - let join_markers = ["joins", "from_joins"] - .into_iter() - .filter_map(|key| value.get(key)) - .filter_map(Value::as_array) - .flatten() - .filter_map(|join| join.get("this")) - .filter_map(json_marker); - let children: Vec<&Value> = value - .as_object() - .map(|object| object.values().collect()) - .or_else(|| value.as_array().map(|values| values.iter().collect())) - .unwrap_or_default(); - from_markers - .chain(join_markers) - .chain(children.into_iter().flat_map(json_relation_marker_calls)) - .collect() -} - -fn json_marker(expression: &Value) -> Option<(String, String)> { - let object = expression.as_object()?; - object.get("alias").map_or_else( - || json_function_marker(object), - |alias| alias.get("this").and_then(json_marker), - ) -} - -fn json_function_marker(object: &serde_json::Map) -> Option<(String, String)> { - let function = object.get("function")?.as_object()?; - let function_name = function.get("name")?.as_str()?.to_ascii_lowercase(); - let names: Vec = function - .get("args")? - .as_array()? - .iter() - .map(|arg| { - arg.get("column")? - .get("name")? - .get("name")? - .as_str() - .map(str::to_string) - }) - .collect::>()?; - let referenced_name = (function_name == "__dbt_ref" && names.len() == 2) - .then(|| names.join("__")) - .or_else(|| (names.len() == 1).then(|| names[0].clone()))?; - Some((function_name, referenced_name)) -} - -/// Owned marker calls for comparison with collected calls. -pub(crate) fn owned_marker_calls(calls: &[(&str, &str)]) -> MarkerCalls { - Some( - calls - .iter() - .map(|(function, name)| ((*function).to_string(), (*name).to_string())) - .collect(), - ) -} - -/// Parse one statement and return the direct walk's and the JSON oracle's marker calls. -pub(crate) fn relation_markers_and_json_oracle( - dialect: &str, - sql: &str, -) -> (MarkerCalls, MarkerCalls) { - let mut statements = Dialect::get_by_name(dialect) - .expect("test dialect must exist") - .parse(sql) - .expect("test SQL must parse"); - assert_eq!(statements.len(), 1, "{sql}"); - let expression = statements.remove(0); - ( - Some(relation_marker_calls(&expression)), - serde_json::to_value(&expression) - .ok() - .map(|value| json_relation_marker_calls(&value)), - ) -} - -/// Return every generated relation shape whose collected markers differ from the JSON oracle. -pub(crate) fn relation_marker_oracle_mismatches(dialect: &str) -> Vec { - let relations: &[&str] = &[ - "__ref(\"orders\")", - "__source(\"raw_orders\") AS s", - "__dbt_ref(\"shop\", \"customers\") c", - "(SELECT * FROM __seed(\"regions\") JOIN __ref(\"stores\") ON TRUE) AS d", - "LATERAL (SELECT * FROM __ref(\"items\")) AS l", - "__udf(\"fn_orders\")", - "plain_table", - ]; - let wrappers = [ - "SELECT * FROM {a} JOIN {b} ON TRUE", - "WITH x AS (SELECT * FROM {a}) SELECT (SELECT 1 FROM {b}) AS v FROM x", - "SELECT * FROM {a} WHERE EXISTS (SELECT 1 FROM {b})", - "SELECT * FROM {a} UNION SELECT * FROM {b}", - "SELECT * FROM {a}, {b}", - "SELECT * FROM ({a} JOIN {b} ON TRUE)", - "SELECT * FROM {a} AS x LEFT JOIN LATERAL (SELECT * FROM {b}) AS y ON TRUE", - "SELECT * FROM (SELECT * FROM {a}) AS x CROSS JOIN {b} WHERE x.id IN (SELECT id FROM {a})", - "UPDATE orders SET amount = 1 FROM {a} JOIN {b} ON TRUE", - "DELETE FROM orders USING {a} JOIN {b} ON TRUE", - "INSERT INTO orders SELECT * FROM {a} JOIN {b} ON TRUE", - ]; - let parser = Dialect::get_by_name(dialect).expect("test dialect must exist"); - wrappers - .iter() - .flat_map(|wrapper| { - relations.iter().flat_map(move |first| { - relations - .iter() - .map(move |second| wrapper.replace("{a}", first).replace("{b}", second)) - }) - }) - .filter(|sql| { - parser - .parse(sql) - .unwrap_or_default() - .iter() - .any(|expression| { - Some(relation_marker_calls(expression)) - != serde_json::to_value(expression) - .ok() - .map(|value| json_relation_marker_calls(&value)) - }) - }) - .collect() -} - /// Dialects and their analysis modes; analysis reads BigQuery and Databricks markers as strings. const DIALECT_MODES: [(&str, &[bool]); 6] = [ ("duckdb", &[true, false]), @@ -1719,7 +1582,10 @@ const DIALECT_MODES: [(&str, &[bool]); 6] = [ /// Plan a fixture-read helper beside a model on every dialect and check names stay isolated. pub(crate) fn helper_names_are_isolated(shared: &SharedNameTestCase) -> bool { - for (dialect, modes) in DIALECT_MODES { + let planned = DIALECT_MODES + .into_iter() + .filter(|(dialect, _)| sql_test_parser(Some(dialect)).is_some()); + for (dialect, modes) in planned { for &sql_analysis_enabled in modes { let label = format!( "{} on {dialect}, analysis {sql_analysis_enabled}", diff --git a/crates/sqlbuild-analysis/src/compiler/tests/relation_markers/helpers.rs b/crates/sqlbuild-analysis/src/compiler/tests/relation_markers/helpers.rs new file mode 100644 index 000000000..418e8c599 --- /dev/null +++ b/crates/sqlbuild-analysis/src/compiler/tests/relation_markers/helpers.rs @@ -0,0 +1,145 @@ +//! The relation-marker walk checked against the original JSON-tree walk, its oracle. + +use serde_json::Value; + +use crate::compiler::_helpers::sql_tests::relation_markers::named_marker_calls; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; + +type MarkerCalls = Option>; + +/// The original JSON-tree walk, kept as the oracle the direct relation walk must match. +fn json_relation_marker_calls(value: &Value) -> Vec<(String, String)> { + let from_markers = ["from", "from_clause"] + .into_iter() + .filter_map(|key| value.get(key)) + .filter_map(|from| from.get("expressions")) + .filter_map(Value::as_array) + .flatten() + .chain(value.get("left").filter(|_| value.get("joins").is_some())) + .filter_map(json_marker); + let join_markers = ["joins", "from_joins"] + .into_iter() + .filter_map(|key| value.get(key)) + .filter_map(Value::as_array) + .flatten() + .filter_map(|join| join.get("this")) + .filter_map(json_marker); + let children: Vec<&Value> = value + .as_object() + .map(|object| object.values().collect()) + .or_else(|| value.as_array().map(|values| values.iter().collect())) + .unwrap_or_default(); + from_markers + .chain(join_markers) + .chain(children.into_iter().flat_map(json_relation_marker_calls)) + .collect() +} + +fn json_marker(expression: &Value) -> Option<(String, String)> { + let object = expression.as_object()?; + object.get("alias").map_or_else( + || json_function_marker(object), + |alias| alias.get("this").and_then(json_marker), + ) +} + +fn json_function_marker(object: &serde_json::Map) -> Option<(String, String)> { + let function = object.get("function")?.as_object()?; + let function_name = function.get("name")?.as_str()?.to_ascii_lowercase(); + let names: Vec = function + .get("args")? + .as_array()? + .iter() + .map(|arg| { + arg.get("column")? + .get("name")? + .get("name")? + .as_str() + .map(str::to_string) + }) + .collect::>()?; + let referenced_name = (function_name == "__dbt_ref" && names.len() == 2) + .then(|| names.join("__")) + .or_else(|| (names.len() == 1).then(|| names[0].clone()))?; + Some((function_name, referenced_name)) +} + +/// Owned marker calls for comparison with collected calls. +pub(crate) fn owned_marker_calls(calls: &[(&str, &str)]) -> MarkerCalls { + Some( + calls + .iter() + .map(|(function, name)| ((*function).to_string(), (*name).to_string())) + .collect(), + ) +} + +/// Parse one statement and return the direct walk's and the JSON oracle's marker calls. +pub(crate) fn relation_markers_and_json_oracle( + dialect: &str, + sql: &str, +) -> (MarkerCalls, MarkerCalls) { + let mut statements = sql_test_parser(Some(dialect)) + .expect("test dialect must exist") + .parse(sql) + .expect("test SQL must parse") + .statements; + assert_eq!(statements.len(), 1, "{sql}"); + let expression = statements.remove(0); + ( + Some(named_marker_calls(&expression, &[])), + serde_json::to_value(&expression) + .ok() + .map(|value| json_relation_marker_calls(&value)), + ) +} + +/// Return every generated relation shape whose collected markers differ from the JSON oracle. +pub(crate) fn relation_marker_oracle_mismatches(dialect: &str) -> Vec { + let relations: &[&str] = &[ + "__ref(\"orders\")", + "__source(\"raw_orders\") AS s", + "__dbt_ref(\"shop\", \"customers\") c", + "(SELECT * FROM __seed(\"regions\") JOIN __ref(\"stores\") ON TRUE) AS d", + "LATERAL (SELECT * FROM __ref(\"items\")) AS l", + "__udf(\"fn_orders\")", + "plain_table", + ]; + let wrappers = [ + "SELECT * FROM {a} JOIN {b} ON TRUE", + "WITH x AS (SELECT * FROM {a}) SELECT (SELECT 1 FROM {b}) AS v FROM x", + "SELECT * FROM {a} WHERE EXISTS (SELECT 1 FROM {b})", + "SELECT * FROM {a} UNION SELECT * FROM {b}", + "SELECT * FROM {a}, {b}", + "SELECT * FROM ({a} JOIN {b} ON TRUE)", + "SELECT * FROM {a} AS x LEFT JOIN LATERAL (SELECT * FROM {b}) AS y ON TRUE", + "SELECT * FROM (SELECT * FROM {a}) AS x CROSS JOIN {b} WHERE x.id IN (SELECT id FROM {a})", + "UPDATE orders SET amount = 1 FROM {a} JOIN {b} ON TRUE", + "DELETE FROM orders USING {a} JOIN {b} ON TRUE", + "INSERT INTO orders SELECT * FROM {a} JOIN {b} ON TRUE", + ]; + let parser = sql_test_parser(Some(dialect)).expect("test dialect must exist"); + wrappers + .iter() + .flat_map(|wrapper| { + relations.iter().flat_map(move |first| { + relations + .iter() + .map(move |second| wrapper.replace("{a}", first).replace("{b}", second)) + }) + }) + .filter(|sql| { + parser + .parse(sql) + .map(|parsed| parsed.statements) + .unwrap_or_default() + .iter() + .any(|expression| { + Some(named_marker_calls(expression, &[])) + != serde_json::to_value(expression) + .ok() + .map(|value| json_relation_marker_calls(&value)) + }) + }) + .collect() +} diff --git a/crates/sqlbuild-analysis/src/compiler/tests/simple_template/helpers.rs b/crates/sqlbuild-analysis/src/compiler/tests/simple_template/helpers.rs deleted file mode 100644 index 8fb1f0df7..000000000 --- a/crates/sqlbuild-analysis/src/compiler/tests/simple_template/helpers.rs +++ /dev/null @@ -1,114 +0,0 @@ -use polyglot_sql::Dialect; -use polyglot_sql::parser::NO_PAREN_FUNCTION_NAMES; -use std::collections::HashMap; -use std::sync::Mutex; - -use crate::compiler::_helpers::sql_tests::planning::{ - AnalysisTemplate, analysis_template, parsed_analysis_template, -}; -use crate::compiler::_helpers::sql_tests::simple_template::simple_analysis_template; -use crate::compiler::tests::test_types::SimpleTemplateCorpusOutcome; - -pub(crate) fn no_paren_marker_mismatches(dialect_name: &str) -> Vec { - let dialect = Dialect::get_by_name(dialect_name).expect("supported test dialect"); - let cache = Mutex::new(HashMap::new()); - NO_PAREN_FUNCTION_NAMES - .iter() - .flat_map(|name| [name.to_string(), name.to_ascii_lowercase()]) - .flat_map(|name| [name.clone(), format!("\"{name}\"")]) - .flat_map(|argument| { - [ - format!("__ref({argument})"), - format!("__source({argument})"), - format!("__seed({argument})"), - format!("__dbt_ref({argument})"), - format!("__dbt_ref({argument}, \"orders\")"), - format!("__dbt_ref(\"orders\", {argument})"), - ] - }) - .filter_map(|relation| { - let sql = format!("SELECT id FROM {relation}"); - let fast = simple_analysis_template(&sql, dialect_name, &dialect); - let expected = parsed_analysis_template(&sql, &dialect); - let actual = analysis_template(&sql, dialect_name, &cache).expect("template cache"); - (fast.is_some() - || actual != expected - || expected - .as_ref() - .is_some_and(|facts| !facts.marker_calls.is_empty())) - .then(|| format!("{sql}: fast={fast:?}, actual={actual:?}, expected={expected:?}")) - }) - .collect() -} - -pub(crate) fn simple_template_corpus_outcome(dialect_name: &str) -> SimpleTemplateCorpusOutcome { - let dialect = Dialect::get_by_name(dialect_name).expect("supported test dialect"); - let mut queries: Vec = Vec::new(); - for projection in [ - "id", - "*", - "CAST(id AS INTEGER) AS id", - "COALESCE(id, 0) + id * 2 AS id", - "CAST((id + 1) / 2 AS DOUBLE) AS id", - "1.5 AS id", - "\"__ref(ignored)\" AS id", - "\"a\"\"b\" AS id", - ] { - for relation in [ - "__ref(\"orders\")", - "__source(\"orders\")", - "__seed(\"orders\")", - "__dbt_ref(\"shop\", \"orders\")", - "\"__ref\"(\"orders\")", - "__REF(orders)", - "\"__REF\"(\"orders\")", - "__ref(final)", - "__ref(\"a.b\")", - "__ref(\"a\"\"b\")", - ] { - let body = format!("SELECT {projection} FROM {relation}"); - for sql in [ - body.clone(), - format!("WITH recent AS ({body}) SELECT id FROM recent"), - format!("{body} UNION ALL BY NAME {body}"), - format!( - "WITH recent AS ({body}) SELECT id FROM __ref(\"customers\") AS c JOIN recent AS r ON c.id = r.id" - ), - ] { - let tokens = dialect.tokenize(&sql).expect("generated SQL tokenizes"); - for token in &tokens { - queries.push(format!( - "{} {}", - &sql[..token.span.start], - &sql[token.span.end..] - )); - for inserted in [".", ")", "(", "FROM", "'__ref(\"ignored\")'"] { - queries.push(format!( - "{} {inserted} {}", - &sql[..token.span.start], - &sql[token.span.start..] - )); - } - } - queries.push(sql); - } - } - } - let recognized: Vec<(String, AnalysisTemplate)> = queries - .into_iter() - .filter_map(|sql| { - simple_analysis_template(&sql, dialect_name, &dialect).map(|facts| (sql, facts)) - }) - .collect(); - SimpleTemplateCorpusOutcome { - recognized: recognized.len(), - mismatches: recognized - .into_iter() - .filter_map(|(sql, fast)| { - let parsed = parsed_analysis_template(&sql, &dialect); - (Some(&fast) != parsed.as_ref()) - .then(|| format!("{sql}: fast={fast:?}, parsed={parsed:?}")) - }) - .collect(), - } -} diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_expected_columns.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_expected_columns.rs index 4e1752ed6..b95f97928 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_expected_columns.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_expected_columns.rs @@ -1,5 +1,5 @@ use crate::compiler::_helpers::sql_tests::expected_columns::expected_columns; -use crate::compiler::_helpers::sql_tests::rendering::render_dialect; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; use crate::compiler::tests::test_types::ExpectedColumnsTestCase; #[test] @@ -79,9 +79,12 @@ fn given_expected_cte_bodies_when_reading_columns_then_authored_names_or_none_ar }, ]; - for test_case in test_cases { - let dialect = render_dialect(Some(test_case.dialect)); - let actual: Option> = expected_columns(test_case.sql, &dialect); + for test_case in test_cases + .into_iter() + .filter(|test_case| sql_test_parser(Some(test_case.dialect)).is_some()) + { + let parser = sql_test_parser(Some(test_case.dialect)).expect("native parser"); + let actual: Option> = expected_columns(test_case.sql, parser); let actual_columns: Option> = actual .as_ref() .map(|columns| columns.iter().map(String::as_str).collect()); diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs index b6e1ffeaf..d8de1ba4c 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs @@ -589,6 +589,7 @@ fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python() { models: models(), tests, lexical_syntax: generic_syntax(), + sql_analysis_dialect: Some("duckdb".to_owned()), }; let macro_test = SqlTestAssemblyTest { block_name: None, diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_name_isolation.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_name_isolation.rs index 8c555fa0d..48fa7a353 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_name_isolation.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_name_isolation.rs @@ -1,8 +1,7 @@ use std::collections::HashMap; -use polyglot_sql::Dialect; - use crate::compiler::_helpers::sql_tests::helper_names::rename_helper_references; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; use crate::compiler::tests::helpers::{helper_names_are_isolated, plan_shape_refusal}; use crate::compiler::tests::test_types::{ HelperRenameTestCase, NameRefusalTestCase, PlanShape, SharedNameTestCase, @@ -60,10 +59,10 @@ fn given_helper_references_when_renaming_then_only_relation_references_change() "expected_rows".to_string(), "__helper__expected_rows".to_string(), )]); - let dialect = Dialect::get_by_name("duckdb").expect("DuckDB dialect"); + let dialect = sql_test_parser(Some("duckdb")).expect("DuckDB parser"); for test_case in test_cases { assert_eq!( - rename_helper_references(test_case.sql, &helpers, &dialect).as_deref(), + rename_helper_references(test_case.sql, &helpers, dialect).as_deref(), test_case.expected_sql, "{}", test_case.description diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs index 43e3268d9..5f5403124 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_planning.rs @@ -2,6 +2,7 @@ use serde_json::{Value, json}; use crate::compiler::_helpers::sql_tests::planning::plan_and_render_json; use crate::compiler::_helpers::sql_tests::planning::resolve_chains_json; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; use crate::compiler::main::sql_test_chains::resolve_sql_test_chains; use crate::compiler::main::sql_test_glue::plan_sql_test_batch; use crate::compiler::main::sql_test_plan_errors::sql_test_plan_error_messages; @@ -25,6 +26,7 @@ use crate::compiler::tests::test_types::{ /// A mocked model test, a test missing its source mock and a UDF test over two models. const GLUE_REQUEST: &str = r#"{ + "sqlAnalysisDialect": "duckdb", "lexicalSyntax": { "backslashEscapeQuotes": [], "escapeStringPrefix": false, "rawStringPrefix": false, "tripleQuotedStrings": false, "nestedBlockComments": false, "lineCommentPrefixes": ["--"] @@ -183,8 +185,8 @@ fn given_sql_test_planning_cases_when_exercising_native_planner_then_expected_be fn given_typed_requests_when_planning_then_plans_and_chains_equal_the_json_entry_points() { let test_cases = [ GluePlanParityTestCase { - description: "generic SQL, planned without rendering", - dialect: None, + description: "DuckDB, planned without rendering", + dialect: Some("duckdb"), render_sql: false, expected_plan_count: 3, expected_missing_mock_severity: "error", @@ -217,7 +219,10 @@ fn given_typed_requests_when_planning_then_plans_and_chains_equal_the_json_entry } value }; - for test_case in test_cases { + for test_case in test_cases + .into_iter() + .filter(|test_case| sql_test_parser(test_case.dialect).is_some()) + { let mut request: Value = serde_json::from_str(GLUE_REQUEST).expect("valid request"); request["sqlAnalysisDialect"] = json!(test_case.dialect); request["renderSql"] = json!(test_case.render_sql); @@ -341,7 +346,10 @@ fn given_real_model_calling_udf_in_projection_when_planning_then_call_targets_th }, ]; - for test_case in test_cases { + for test_case in test_cases + .iter() + .filter(|test_case| sql_test_parser(Some(test_case.dialect)).is_some()) + { let sql = projected_udf_call_sql(test_case.dialect); assert_eq!( diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_relation_markers.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_relation_markers.rs index f22adc6a3..dd9f19d1a 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_relation_markers.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_relation_markers.rs @@ -1,4 +1,8 @@ -use crate::compiler::tests::helpers::{ +use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; + +use crate::compiler::_helpers::sql_tests::planning::{SqlTestPatterns, parsed_analysis_template}; +use crate::compiler::_helpers::sql_tests::rendering::sql_test_parser; +use crate::compiler::tests::relation_markers_helpers::{ owned_marker_calls, relation_marker_oracle_mismatches, relation_markers_and_json_oracle, }; use crate::compiler::tests::test_types::{RelationMarkerOracleTestCase, RelationMarkersTestCase}; @@ -26,7 +30,7 @@ fn given_parsed_statements_when_collecting_relation_markers_then_json_walk_order }, RelationMarkersTestCase { description: "aliased joins keep from before joins", - dialect: "snowflake", + dialect: "duckdb", sql: "SELECT o.id FROM __ref(\"orders\") AS o JOIN __source(\"raw_customers\") AS c ON o.id = c.id LEFT JOIN __seed(\"regions\") r ON c.region = r.id", expected_calls: &[ ("__ref", "orders"), @@ -82,7 +86,10 @@ fn given_parsed_statements_when_collecting_relation_markers_then_json_walk_order expected_calls: &[("__ref", "orders"), ("__ref", "archived_orders")], }, ]; - for test_case in test_cases { + for test_case in test_cases + .iter() + .filter(|test_case| sql_test_parser(Some(test_case.dialect)).is_some()) + { let (collected, oracle) = relation_markers_and_json_oracle(test_case.dialect, test_case.sql); assert_eq!( @@ -134,7 +141,10 @@ fn given_generated_relation_shapes_when_collecting_markers_then_json_oracle_matc expected_mismatches: &[], }, ]; - for test_case in test_cases { + for test_case in test_cases + .iter() + .filter(|test_case| sql_test_parser(Some(test_case.dialect)).is_some()) + { assert_eq!( relation_marker_oracle_mismatches(test_case.dialect), test_case.expected_mismatches, @@ -143,3 +153,50 @@ fn given_generated_relation_shapes_when_collecting_markers_then_json_oracle_matc ); } } + +#[test] +fn given_marked_model_sql_when_reading_the_planning_template_then_every_parser_sees_the_markers() { + let sql: &str = "SELECT o.id, '__ref(\"quoted\")' AS q FROM __ref(\"orders\") AS o -- __ref(\"commented\")\nJOIN __source(\"raw_customers\") AS c ON o.id = c.id LEFT JOIN __seed(\"regions\") r ON c.region = r.id JOIN __dbt_ref(\"shop\", \"customers\") d ON TRUE WHERE o.id IN (SELECT id FROM __REF(\"Products\"))"; + let test_cases = [ + RelationMarkersTestCase { + description: "Snowflake, which takes a table function only inside TABLE( )", + dialect: "snowflake", + sql, + expected_calls: &[ + ("__ref", "orders"), + ("__source", "raw_customers"), + ("__seed", "regions"), + ("__dbt_ref", "shop__customers"), + ("__ref", "Products"), + ], + }, + RelationMarkersTestCase { + description: "DuckDB, the same calls in the same order", + dialect: "duckdb", + sql, + expected_calls: &[ + ("__ref", "orders"), + ("__source", "raw_customers"), + ("__seed", "regions"), + ("__dbt_ref", "shop__customers"), + ("__ref", "Products"), + ], + }, + ]; + let patterns: SqlTestPatterns = SqlTestPatterns::new(LexicalSyntax { + line_comment_prefixes: vec!["--".to_owned()], + ..LexicalSyntax::default() + }) + .expect("the SQL-test patterns compile"); + for test_case in test_cases { + let parser = sql_test_parser(Some(test_case.dialect)).expect(test_case.description); + let template = parsed_analysis_template(test_case.sql, (parser, &patterns)) + .expect(test_case.description); + assert_eq!( + Some(template.marker_calls.to_vec()), + owned_marker_calls(test_case.expected_calls), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_simple_template.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_simple_template.rs deleted file mode 100644 index a11546bfe..000000000 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_simple_template.rs +++ /dev/null @@ -1,340 +0,0 @@ -use std::collections::HashMap; -use std::sync::Mutex; - -use polyglot_sql::Dialect; - -use crate::compiler::_helpers::sql_tests::planning::{ - analysis_template, parsed_analysis_template, take_template_ast_parse_count, -}; -use crate::compiler::_helpers::sql_tests::simple_template::simple_analysis_template; -use crate::compiler::tests::simple_template_helpers::{ - no_paren_marker_mismatches, simple_template_corpus_outcome, -}; -use crate::compiler::tests::test_types::{ - RelationMarkerOracleTestCase, SimpleTemplateCorpusTestCase, SimpleTemplateTestCase, -}; - -#[test] -fn given_registered_no_paren_arguments_when_planning_then_all_marker_positions_use_the_parser() { - let test_cases = [ - RelationMarkerOracleTestCase { - description: "all registry names in quoted and unquoted marker argument positions", - dialect: "duckdb", - expected_mismatches: &[], - }, - RelationMarkerOracleTestCase { - description: "all Snowflake registry names in quoted and unquoted marker argument positions", - dialect: "snowflake", - expected_mismatches: &[], - }, - ]; - for test_case in test_cases { - assert_eq!( - no_paren_marker_mismatches(test_case.dialect), - test_case.expected_mismatches, - "{}", - test_case.description - ); - } -} - -#[test] -fn given_bounded_and_fallback_queries_when_planning_then_facts_and_ast_counts_match() { - let test_cases = [ - SimpleTemplateTestCase { - description: "Snowflake dense projections and union remove the template AST", - dialect: "snowflake", - sql: r#"WITH imported AS (SELECT * FROM __ref("orders")), -projected AS (SELECT CAST(b.id AS INTEGER) AS id, -CAST(COALESCE(b.amount, 0) + CAST(b.id AS DOUBLE) AS DOUBLE) AS amount FROM imported AS b), -combined AS (SELECT id, amount FROM projected UNION ALL BY NAME SELECT id, amount FROM projected WHERE id = 0), -final AS (SELECT CAST(id AS INTEGER) AS id, CAST(amount AS DOUBLE) AS amount FROM combined) -SELECT id, amount FROM final"#, - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "Snowflake dense UDF projection retains the template AST", - dialect: "snowflake", - sql: r#"WITH imported AS (SELECT * FROM __ref("orders")), -projected AS (SELECT CAST(b.id AS INTEGER) AS id, -CAST(__udf("amount")(b.amount) AS DOUBLE) AS amount FROM imported AS b) -SELECT id, amount FROM projected"#, - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "Snowflake QUALIFY remains on the full parser", - dialect: "snowflake", - sql: "SELECT id FROM __ref(\"orders\") QUALIFY ROW_NUMBER() OVER (ORDER BY id) = 1", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "quoted no-paren source argument retains parser Function classification", - dialect: "duckdb", - sql: "SELECT id FROM __source(\"now\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "unquoted no-paren reference argument retains parser Function classification", - dialect: "duckdb", - sql: "SELECT id FROM __ref(NOW)", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "first dbt argument with no-paren name retains parser classification", - dialect: "duckdb", - sql: "SELECT id FROM __dbt_ref(\"pi\", \"orders\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "second dbt argument with no-paren name retains parser classification", - dialect: "duckdb", - sql: "SELECT id FROM __dbt_ref(\"orders\", \"getdate\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "unquoted ordinary argument conservatively retains full parser", - dialect: "duckdb", - sql: "SELECT id FROM __seed(orders)", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "unquoted dialect-specific user argument retains full parser", - dialect: "duckdb", - sql: "SELECT id FROM __source(user)", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "quoted user is an ordinary Column in the pinned parser", - dialect: "duckdb", - sql: "SELECT id FROM __source(\"user\")", - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "dense CTE projections joins and union remove one cold template AST", - dialect: "duckdb", - sql: r#"WITH imported AS (SELECT * FROM __ref("orders")), -lookup_0 AS (SELECT * FROM __source("customers")), -projected AS (SELECT CAST(b.id AS INTEGER) AS id, -CAST(b.amount + COALESCE(l0.amount, 0) AS DOUBLE) AS amount, -CAST(COALESCE(b.amount, 0) + CAST(b.id AS DOUBLE) AS DOUBLE) AS quantity -FROM imported AS b LEFT JOIN lookup_0 AS l0 ON l0.id = b.id), -combined AS (SELECT id, amount, quantity FROM projected -UNION ALL BY NAME SELECT id, amount, quantity FROM projected WHERE id = 0), -final AS (SELECT CAST(id AS INTEGER) AS id, CAST(amount AS DOUBLE) AS amount FROM combined) -SELECT id, amount FROM final"#, - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "nested CTEs follow outer relation then WITH traversal order", - dialect: "duckdb", - sql: "WITH recent AS (WITH nested AS (SELECT * FROM __source(\"orders\")) SELECT id FROM nested) SELECT id FROM recent JOIN __ref(\"customers\") AS c ON c.id = recent.id", - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "root union has no Select CTE names", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders\") UNION ALL SELECT id FROM __source(\"orders\")", - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "quoted names escapes and line-comment marker lookalikes", - dialect: "duckdb", - sql: "WITH \"recent\" AS (SELECT * FROM __ref(\"or\"\"ders\")) SELECT id FROM \"recent\" -- __seed(\"ignored\")", - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "two-part dbt references precede joined seeds", - dialect: "duckdb", - sql: "SELECT id FROM __dbt_ref(\"shop\", \"orders\") AS o JOIN __seed(\"regions\") AS r ON o.id = r.id", - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "scalar nested subquery retains full parser", - dialect: "duckdb", - sql: "SELECT (SELECT id FROM __ref(\"orders\")) AS id", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "string marker lookalike retains full parser", - dialect: "duckdb", - sql: "SELECT '__ref(\"ignored\")' AS note FROM __ref(\"orders\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "table function call retains full parser", - dialect: "duckdb", - sql: "SELECT id FROM __table_fn(\"orders\")(1)", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "scalar function marker retains full parser", - dialect: "duckdb", - sql: "SELECT __udf(\"amount\")(id) AS id FROM __ref(\"orders\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "WITH over UNION retains full parser AST attachment", - dialect: "duckdb", - sql: "WITH recent AS (SELECT id FROM orders) SELECT id FROM recent UNION ALL SELECT id FROM recent", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "unterminated block comment retains full parser policy", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders\") /* unclosed", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "nested block comments retain full parser policy", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders\") /* outer /* __ref(\"ignored\") */ inner */", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "missing projection retains parser failure", - dialect: "duckdb", - sql: "SELECT FROM __ref(\"orders\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "incomplete cast retains parser failure", - dialect: "duckdb", - sql: "SELECT CAST(id AS INTEGER FROM __ref(\"orders\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "incomplete WHERE retains parser failure", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders\") WHERE", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "unterminated identifier retains parser failure", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders)", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "unterminated table identifier retains parser failure", - dialect: "duckdb", - sql: "SELECT id FROM \"orders", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "incomplete join retains parser failure", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders\") LEFT", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "multiple statements retain textual fallback", - dialect: "duckdb", - sql: "SELECT id FROM __ref(\"orders\"); SELECT id FROM other", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "literal marker argument is not a Column", - dialect: "duckdb", - sql: "SELECT id FROM __ref('orders')", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "postgres dollar quoting retains its parser", - dialect: "postgres", - sql: "SELECT $$__ref(\"ignored\")$$ AS note FROM __ref(\"orders\")", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "snowflake ordinary quoted names use recognized facts", - dialect: "snowflake", - sql: "SELECT id FROM __ref(\"orders\")", - expected_ast_parses: 0, - }, - SimpleTemplateTestCase { - description: "bigquery backtick names retain its parser", - dialect: "bigquery", - sql: "SELECT id FROM __ref(`orders`)", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "tsql brackets retain its parser", - dialect: "tsql", - sql: "SELECT id FROM __ref([orders])", - expected_ast_parses: 1, - }, - SimpleTemplateTestCase { - description: "databricks quoting retains its parser", - dialect: "databricks", - sql: "SELECT id FROM __ref(`orders`)", - expected_ast_parses: 1, - }, - ]; - for test_case in test_cases { - let dialect = Dialect::get_by_name(test_case.dialect).expect("supported test dialect"); - take_template_ast_parse_count(); - let expected = parsed_analysis_template(test_case.sql, &dialect); - assert_eq!( - take_template_ast_parse_count(), - 1, - "{}", - test_case.description - ); - let fast = simple_analysis_template(test_case.sql, test_case.dialect, &dialect); - assert_eq!( - usize::from(fast.is_none()), - test_case.expected_ast_parses, - "{}", - test_case.description - ); - assert_eq!( - fast.as_ref(), - expected - .as_ref() - .filter(|_| test_case.expected_ast_parses == 0), - "{}", - test_case.description - ); - let cache = Mutex::new(HashMap::new()); - let actual = - analysis_template(test_case.sql, test_case.dialect, &cache).expect("template cache"); - assert_eq!(actual, expected, "{}", test_case.description); - assert_eq!( - take_template_ast_parse_count(), - test_case.expected_ast_parses, - "{}", - test_case.description - ); - } -} - -#[test] -fn given_generated_and_mutated_queries_when_recognized_then_every_fact_matches_the_ast() { - let test_cases = [ - SimpleTemplateCorpusTestCase { - description: "combinations and token deletions preserve parser acceptance and facts", - dialect: "duckdb", - expected_min_recognized: 200, - expected_mismatches: &[], - }, - SimpleTemplateCorpusTestCase { - description: "Snowflake token productions preserve parser acceptance and facts", - dialect: "snowflake", - expected_min_recognized: 200, - expected_mismatches: &[], - }, - ]; - for test_case in test_cases { - let outcome = simple_template_corpus_outcome(test_case.dialect); - assert!( - outcome.recognized >= test_case.expected_min_recognized, - "{}: {} recognized", - test_case.description, - outcome.recognized - ); - assert_eq!( - outcome.mismatches, test_case.expected_mismatches, - "{}", - test_case.description - ); - } -} diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs index bf32b0365..7b561363c 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs @@ -186,25 +186,6 @@ pub(crate) struct SqlTestAssemblyTestCase { pub(crate) batch: crate::compiler::models::SqlTestAssemblyBatch, pub(crate) expected_outcomes: Vec, } -pub(crate) struct SimpleTemplateTestCase { - pub description: &'static str, - pub dialect: &'static str, - pub sql: &'static str, - pub expected_ast_parses: usize, -} - -pub(crate) struct SimpleTemplateCorpusTestCase { - pub description: &'static str, - pub dialect: &'static str, - pub expected_min_recognized: usize, - pub expected_mismatches: &'static [&'static str], -} - -pub(crate) struct SimpleTemplateCorpusOutcome { - pub recognized: usize, - pub mismatches: Vec, -} - #[derive(Debug)] pub(crate) struct ScenarioExpectedColumnsTestCase { pub(crate) description: &'static str, diff --git a/crates/sqlbuild-analysis/src/query_analysis/_helpers/annotated_types.rs b/crates/sqlbuild-analysis/src/query_analysis/_helpers/annotated_types.rs deleted file mode 100644 index 28a8d6b50..000000000 --- a/crates/sqlbuild-analysis/src/query_analysis/_helpers/annotated_types.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Facts of a fully annotated query: which calls annotation could not type, and output types. - -use polyglot_sql::expressions::DataType; -use polyglot_sql::optimizer::annotate_types::function_result_is_known; -use polyglot_sql::traversal::ExpressionWalk; -use polyglot_sql::{DialectType, Expression}; - -/// Whether a call under `node`, outside a typed marker, has an unknown or only guessed type. -pub(crate) fn has_untyped_call(node: &Expression, dialect: DialectType) -> bool { - match node { - Expression::Annotated(annotation) if annotation.type_assertion.is_some() => false, - Expression::Function(function) - if !function_result_is_known(function, dialect) || is_unknown(node) => - { - true - } - Expression::AggregateFunction(_) | Expression::WindowFunction(_) if is_unknown(node) => { - true - } - _ => node - .children() - .into_iter() - .any(|child| has_untyped_call(child, dialect)), - } -} - -/// Whether annotation left `node` without a type, or with a wholly unknown one. -fn is_unknown(node: &Expression) -> bool { - node.inferred_type() - .is_none_or(|data_type| *data_type == DataType::Unknown) -} - -/// The annotated type of each projection of a `SELECT`, past its alias. -pub(crate) fn projection_types(expression: &Expression) -> Vec> { - let Expression::Select(select) = expression else { - return Vec::new(); - }; - let mut types: Vec> = Vec::with_capacity(select.expressions.len()); - for projection in &select.expressions { - let value: &Expression = match projection { - Expression::Alias(alias) => &alias.this, - other => other, - }; - types.push(value.inferred_type().cloned()); - } - types -} diff --git a/crates/sqlbuild-analysis/src/query_analysis/_helpers/compatibility_types.rs b/crates/sqlbuild-analysis/src/query_analysis/_helpers/compatibility_types.rs index ad8781052..a34c196d8 100644 --- a/crates/sqlbuild-analysis/src/query_analysis/_helpers/compatibility_types.rs +++ b/crates/sqlbuild-analysis/src/query_analysis/_helpers/compatibility_types.rs @@ -69,35 +69,11 @@ fn evaluate_outputs( functions, dialect, used_annotations: Cell::new(false), - untyped_source: Cell::new(false), }; let outputs = context.query(expression, &relations); (outputs, context.used_annotations.get()) } -/// Outputs only when every `FROM` and `JOIN` source of the query, CTEs included, was typed. -pub(super) fn infer_complete_outputs( - expression: &Expression, - schema: Option<&ValidationSchema>, - functions: &HashMap, - dialect: DialectType, -) -> Option { - let mut relations = Relations::new(); - if let Some(schema) = schema { - for table in &schema.tables { - relations.insert(table.name.to_lowercase(), Rc::new(table_outputs(table))); - } - } - let context = TypeContext { - functions, - dialect, - used_annotations: Cell::new(false), - untyped_source: Cell::new(false), - }; - let outputs = context.query(expression, &relations); - (!context.untyped_source.get()).then_some(outputs) -} - /// A schema table's columns with their declared types. fn table_outputs(table: &polyglot_sql::SchemaTable) -> Outputs { table @@ -111,8 +87,6 @@ struct TypeContext<'a> { functions: &'a HashMap, dialect: DialectType, used_annotations: Cell, - /// Whether a `FROM` or `JOIN` source could not be typed, so stars and columns may be missing. - untyped_source: Cell, } impl TypeContext<'_> { @@ -188,15 +162,6 @@ impl TypeContext<'_> { .flat_map(|from| &from.expressions) .chain(select.joins.iter().map(|join| &join.this)); for source in tables { - let typed: bool = match unwrapped(source) { - Expression::Values(_) => true, - Expression::Subquery(subquery) => subquery.alias.is_some(), - Expression::Table(table) => relations.contains_key(&table.name.name.to_lowercase()), - _ => false, - }; - if !typed { - self.untyped_source.set(true); - } if let Expression::Values(values) = unwrapped(source) { aliases.items.push(( values diff --git a/crates/sqlbuild-analysis/src/query_analysis/main/annotated_output_types.rs b/crates/sqlbuild-analysis/src/query_analysis/main/annotated_output_types.rs deleted file mode 100644 index 45e65c7c9..000000000 --- a/crates/sqlbuild-analysis/src/query_analysis/main/annotated_output_types.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::collections::HashMap; - -use polyglot_sql::expressions::DataType; -use polyglot_sql::{DialectType, Expression, ParseOptions, ValidationSchema}; - -use crate::query_analysis::annotated_types::{has_untyped_call, projection_types}; -use crate::query_analysis::compatibility_types::{NULL_TYPE, infer_outputs}; -use crate::query_analysis::types::AnnotatedOutput; - -/// Each output of the one query in `sql`, typed by full annotation; `None` if a call is untyped. -#[must_use] -pub fn annotated_output_types( - sql: &str, - dialect: &str, - schema: &ValidationSchema, -) -> Option> { - let dialect: DialectType = match dialect.parse() { - Ok(dialect) => dialect, - Err(_) => return None, - }; - let mut statements: Vec = - match polyglot_sql::parse_with_options(sql, dialect, &ParseOptions::default()) { - Ok(statements) => statements, - Err(_) => return None, - }; - if statements.len() != 1 { - return None; - } - let mut expression: Expression = statements.pop()?; - let mapping = polyglot_sql::validation::mapping_schema_from_validation_schema_with_dialect( - schema, dialect, - ); - polyglot_sql::optimizer::annotate_types::annotate_types( - &mut expression, - Some(&mapping as &dyn polyglot_sql::schema::Schema), - Some(dialect), - ); - if has_untyped_call(&expression, dialect) { - return None; - } - let annotated: Vec> = projection_types(&expression); - let mut outputs: Vec = Vec::new(); - for (index, (name, data_type)) in - infer_outputs(&expression, Some(schema), &HashMap::new(), dialect) - .into_iter() - .enumerate() - { - outputs.push(( - name, - data_type.filter(|value| value != NULL_TYPE), - annotated.get(index).cloned().flatten(), - )); - } - Some(outputs) -} diff --git a/crates/sqlbuild-analysis/src/query_analysis/main/mod.rs b/crates/sqlbuild-analysis/src/query_analysis/main/mod.rs index 5d65d9ca3..926992194 100644 --- a/crates/sqlbuild-analysis/src/query_analysis/main/mod.rs +++ b/crates/sqlbuild-analysis/src/query_analysis/main/mod.rs @@ -1,3 +1 @@ -pub mod annotated_output_types; -pub mod output_types; pub mod prepare_project_catalog; diff --git a/crates/sqlbuild-analysis/src/query_analysis/main/output_types.rs b/crates/sqlbuild-analysis/src/query_analysis/main/output_types.rs deleted file mode 100644 index c7d83d7d8..000000000 --- a/crates/sqlbuild-analysis/src/query_analysis/main/output_types.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::collections::HashMap; - -use polyglot_sql::{DialectType, Expression, ParseOptions, ValidationSchema}; - -use crate::query_analysis::compatibility_types::{NULL_TYPE, infer_complete_outputs}; - -/// The output columns and inferred types of the one query in `sql`; `None` if a source is untyped. -#[must_use] -pub fn output_types( - sql: &str, - dialect: &str, - schema: &ValidationSchema, -) -> Option)>> { - let dialect: DialectType = match dialect.parse() { - Ok(dialect) => dialect, - Err(_) => return None, - }; - let mut statements: Vec = - match polyglot_sql::parse_with_options(sql, dialect, &ParseOptions::default()) { - Ok(statements) => statements, - Err(_) => return None, - }; - if statements.len() != 1 { - return None; - } - let expression: Expression = statements.pop()?; - let mut outputs: Vec<(String, Option)> = Vec::new(); - let inferred = infer_complete_outputs(&expression, Some(schema), &HashMap::new(), dialect)?; - for (name, data_type) in inferred { - outputs.push((name, data_type.filter(|value| !is_null_type(value)))); - } - Some(outputs) -} - -/// Whether `value` is the placeholder inference uses for a `NULL` literal's type. -fn is_null_type(value: &str) -> bool { - value == NULL_TYPE -} diff --git a/crates/sqlbuild-analysis/src/query_analysis/mod.rs b/crates/sqlbuild-analysis/src/query_analysis/mod.rs index 847eabad9..94f93ea62 100644 --- a/crates/sqlbuild-analysis/src/query_analysis/mod.rs +++ b/crates/sqlbuild-analysis/src/query_analysis/mod.rs @@ -1,5 +1,3 @@ -#[path = "_helpers/annotated_types.rs"] -mod annotated_types; #[path = "_helpers/borrowed_facts.rs"] mod borrowed_facts; #[path = "_helpers/compatibility_types.rs"] @@ -7,7 +5,6 @@ mod compatibility_types; #[path = "_helpers/engine.rs"] mod engine; pub mod main; -pub mod types; #[cfg(test)] mod tests; diff --git a/crates/sqlbuild-analysis/src/query_analysis/types.rs b/crates/sqlbuild-analysis/src/query_analysis/types.rs deleted file mode 100644 index ff1b963f8..000000000 --- a/crates/sqlbuild-analysis/src/query_analysis/types.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Shapes of query analysis results. - -use polyglot_sql::expressions::DataType; - -/// One output of a fully annotated query: its name, inferred type, and annotated type. -pub type AnnotatedOutput = (String, Option, Option); diff --git a/crates/sqlbuild-analysis/src/sql_macros/_helpers/parsing/body.rs b/crates/sqlbuild-analysis/src/sql_macros/_helpers/parsing/body.rs index fa92f6a59..3aba3eec4 100644 --- a/crates/sqlbuild-analysis/src/sql_macros/_helpers/parsing/body.rs +++ b/crates/sqlbuild-analysis/src/sql_macros/_helpers/parsing/body.rs @@ -1,13 +1,20 @@ //! A SQL macro body parsed as SQL, its parameter references located and replaced in place. -use polyglot_sql::expressions::{Identifier, Select}; -use polyglot_sql::{DialectType, Expression, ExpressionWalk}; +use sqlquack_syntax::expressions::names::models::{Column, Identifier}; +use sqlquack_syntax::expressions::operators::models::DotAccess; +use sqlquack_syntax::expressions::queries::models::Select; +use sqlquack_syntax::{Expression, ExpressionWalk, Parsed}; + +use crate::analysis_route::main::analysis_route::analysis_route; +use crate::analysis_route::models::AnalysisRoute; +use crate::analysis_route::types::NativeDialect; use crate::sql_macros::_helpers::parsing::captures::{output_selects, slot_captures}; use crate::sql_macros::_helpers::parsing::header::unquoted; use crate::sql_macros::_helpers::parsing::lexing::{Token, tokens}; use crate::sql_macros::_helpers::parsing::parameters::{ - ParameterReference, alias_follows, parameter_references, placeholder, placeholder_parameter, + ParameterReference, alias_follows, declarations_masked, in_lists_parenthesised, + parameter_references, placeholder, placeholder_parameter, using_reads_joined, with_placeholders, }; use crate::sql_macros::_helpers::parsing::slots::{SlotRead, check_group_positions, slot_reads}; @@ -22,18 +29,8 @@ use crate::sql_macros::models::{ SqlMacroDefinition, SqlMacroForm, SqlMacroParameter, TextSplice, TypedCall, }; -/// Dialects tried in turn to parse a body; the first that parses it locates its references. -const PARSE_DIALECTS: [DialectType; 8] = [ - DialectType::Generic, - DialectType::DuckDB, - DialectType::Snowflake, - DialectType::BigQuery, - DialectType::PostgreSQL, - DialectType::Databricks, - DialectType::ClickHouse, - DialectType::TSQL, -]; const EXPRESSION_PREFIX: &str = "SELECT "; +const TYPE_PREFIX: &str = "SELECT CAST(NULL AS "; const CTE_SUFFIX: &str = "\nSELECT 1"; /// The body parsed as SQL. @@ -41,30 +38,57 @@ pub(crate) struct ParsedBody { pub(crate) select: Box