{"record":{"id":"2b505d5fcdfa19b6","repo":"dbt-labs/dbt-core","slug":"should-treat-qualified-vs-unqualified-dbt-tmp-in","errorCode":null,"errorMessage":"should treat qualified vs unqualified __dbt_tmp in MERGE USING as equivalent","messagePattern":"should treat qualified vs unqualified __dbt_tmp in MERGE USING as equivalent","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/sql/diff.rs","lineNumber":4512,"sourceCode":"\n\n    merge\n    into\n        `dbt`.`dbt_dbt_audit`.`seed_executions` as DBT_INTERNAL_DEST\n    using\n        `seed_executions__dbt_tmp` as DBT_INTERNAL_SOURCE\n    on\n        FALSE\n    when matched\n        then update set\n            *\n    when not matched\n        then insert\n            *\n\"#;\n\n        compare_sql(merge_actual, merge_expected, AdapterType::Databricks)\n            .expect(\"should treat qualified vs unqualified __dbt_tmp in MERGE USING as equivalent\");\n    }\n\n    #[test]\n    fn test_compare_sql_query_tag_payload_ignored() {\n        let actual = r#\"    alter session set query_tag = '{\"\"model_name\"\":\"\"stg_base_orders\"\",\"\"env\"\":\"\"PRD\"\",\"\"job\"\":{\"\"run_id\"\":\"\"\"\",\"\"execution_date\"\":\"\"\"\",\"\"start_date\"\":\"\"\"\"}}'\"#;\n        let expected = r#\"    alter session set query_tag = '{\"\"env\"\": \"\"PRD\"\", \"\"job\"\": {\"\"execution_date\"\": \"\"\"\", \"\"run_id\"\": \"\"\"\", \"\"start_date\"\": \"\"\"\"}, \"\"model_name\"\": \"\"stg_base_orders\"\"}'\"#;\n        let result = compare_sql(actual, expected, AdapterType::Snowflake);\n        assert!(\n            result.is_ok(),\n            \"Query tag payload differences should be ignored\"\n        );\n    }\n\n    #[test]\n    fn test_compare_sql_uuid_literals_ignored() {\n        let actual = r#\"\nINSERT INTO\n    PROD_SSAP_AUDIT.ABAC.ABAC_JOB_RUN","sourceCodeStart":4494,"sourceCodeEnd":4530,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/sql/diff.rs#L4494-L4530","documentation":"Test panic from .expect at crates/dbt-adapter/src/sql/diff.rs:4512: compare_sql(merge_actual, merge_expected, AdapterType::Databricks) returned Err because the USING clause referenced the __dbt_tmp temp table with a three-part qualified name in one SQL and a bare identifier in the other. The differ should canonicalize relation qualification so these compare as equal for Databricks MERGE statements.","triggerScenarios":"cargo test -p dbt-adapter with the MERGE half of the test at diff.rs:4512: merge_actual uses `catalog.schema.__dbt_tmp` (three-part) in the MERGE USING clause while merge_expected uses just `__dbt_tmp`, and compare_sql under AdapterType::Databricks reports a mismatch, panicking the expect.","commonSituations":"Fusion qualifies temp relations fully while Mantle emits bare identifiers (or vice versa) in MERGE USING; adapter relation-rendering changes alter qualification depth; MERGE-style incremental models replayed against recorded SQL fail only on the USING relation name.","solutions":["Inspect the Err to confirm the only divergence is relation qualification depth in USING","Normalize relation names in the canonicalizer: qualify/strip to a common form (e.g. compare by trailing identifier when one side is a temp __dbt_tmp relation)","Ensure this normalization is enabled for AdapterType::Databricks and shared with the view-DDL path (diff.rs:4464)","If qualification differences are semantically meaningful (different relations), fix the SQL generator instead"],"exampleFix":"// before\ncompare_sql(merge_actual, merge_expected, AdapterType::Databricks)\n    .expect(\"should treat qualified vs unqualified __dbt_tmp in MERGE USING as equivalent\");\n// after (library-side: unify relation qualification before compare)\nlet norm = |s: &str| unify_relation_qualification(canonicalize(s, AdapterType::Databricks));\nassert_eq!(norm(merge_actual), norm(merge_expected));","handlingStrategy":"validation","validationCode":"// check qualification depth of the USING relation before diff\nlet depth_actual = relation_parts(merge_actual).len();\nlet depth_expected = relation_parts(merge_expected).len();\nif depth_actual != depth_expected && relation_name(merge_actual) == relation_name(merge_expected) {\n    // pure qualification drift — normalize before comparing\n}","typeGuard":null,"tryCatchPattern":"compare_sql(merge_actual, merge_expected, AdapterType::Databricks)\n    .unwrap_or_else(|e| eprintln!(\"MERGE USING relation drift: {e}\"));","preventionTips":["Canonicalize relation qualification (2-part vs 3-part names) before diffing","Share the qualification normalization between view DDL and MERGE paths","Fixture-test MERGE USING with both qualified and bare __dbt_tmp references","Avoid emitting fully qualified temp-table names in generated MERGE statements when the target convention is bare identifiers"],"tags":["rust","sql-diff","test-panic","databricks","merge","relation-qualification"],"backgroundTag":"sql-diff-mismatch","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}