fix(validate): fold uppercase metadata to the engine's identifier case; skip existence checks without a schema
Issue for this PR
Closes #1333
Type of change
- Bug fix
What does this PR do?
Two defects in altimate_core_validate, both reproduced with the issue's script:
-
Case. The engine compares an unquoted identifier in lowercase and a quoted one exactly (verified:
"Customer_Region"stays mixed-case). Snowflake metadata fromschema_inspect/snowflake_get_table_statsis UPPERCASE, so a correct query failed withColumnNotFound— with the engine's own DidYouMean pointing at the same column in uppercase at confidence 1 — and uppercasing the SQL did not help.normalizeSchemaContextnow folds an all-uppercase table or column name (the storage form of an identifier created unquoted) to lowercase before the schema reaches the engine. Mixed-case names are left alone: they were created quoted, must be referenced quoted, and the engine matches them exactly — folding them would break that. Lowercase names are already in the engine's form. Applies to bothschema_contextshapes (flat map and SchemaDefinition).schema_pathfiles are not folded (out of scope; the issue is about inline metadata from the warehouse tools). -
No schema. The engine ran against the
_empty_placeholder and reported every table as missing, while the tool's description promises existence checks are skipped. The handler now dropsTableNotFound/ColumnNotFound(E001/E002) findings when no schema was given and recomputesvalidfrom what remains, so syntax and dialect errors still surface.
How did you verify your code works?
test/altimate/validate-identifier-case.test.ts(9 tests): the exact query and uppercase metadata from the issue validate through the dispatcher, in lowercase and uppercase SQL; a genuinely misspelled column is stillColumnNotFound; no schema →valid: truefor the correct query, and a real syntax error (selec x fro t, engineE000) is still reported; unit cases forfoldIdentifierCase(upper → lower, mixed kept, lowercase untouched, digits) and the existence classifier.- Mutation-checked: removing the fold fails 5 tests; keeping existence errors without a schema fails 1.
altimate-core-e2e,sql-validation-adversarial,tool-error-propagation,issue-261-e2eand the new file: 244 pass. Typecheck clean.
One note for reviewers: the fold makes the validator lenient in one direction on case-sensitive-by-default engines (Postgres stores unquoted as lowercase, so an all-uppercase Postgres column must have been created quoted and referenced quoted; after folding, an unquoted lowercase reference would validate). That is a missed error rather than a false positive, and the issue's cost was the false positives.
Screenshots / recordings
N/A.
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
🤖 Generated with Claude Code
https://claude.ai/code/session_012Q51zFUmPg1WwtS5CrGJE6
Summary by cubic
Fixes altimate_core_validate rejecting correct Snowflake queries against warehouse metadata and reporting every table as missing when no schema is given.
- Uppercase metadata (e.g.
ORDER_MONTH) folds to the engine's lowercase form before validation; mixed-case quoted names stay exact, and a fold that would collide with an existing name keeps the entry as written. - Quoted all-uppercase references (
"ORDER_MONTH", dbt/BI style) and BigQuery/Databricks backticks are lowercased inside their quotes when a schema is present; string literals and comments are left alone. - A shared
prepareSqlfolds both halves of the SQL-plus-schema pair for every such operation (altimate_core.*andsql.*), and applies the same preparation to sibling SQL (base_sql, the equal query, lineage batches). - JSON/YAML schema files are folded like inline contexts; DDL files load as-is.
- Generated SQL (rewrites, fixes, optimizations) returns in the caller's spelling;
sql.diffrenders its text diff from the raw inputs and folds only for the equivalence check. - Without a schema,
TableNotFound/ColumnNotFoundfindings are dropped andvalidis recomputed, so syntax and dialect errors still surface; a schema context that normalizes to no table takes the no-schema path, and a schema file that fails to load is an engine failure rather than a valid result.
Summary by CodeRabbit
- Bug Fixes
- Improved SQL validation and analysis for uppercase, quoted, and mixed-case identifiers.
- Improved handling of schema files, inline schemas, schema context, and schema-free validation.
- Preserved original identifier casing in generated SQL and rewrites.
- Case-only SQL differences are now displayed correctly.
- Improved processing across SQL analysis, optimization, fixing, rewriting, and lineage checks.
- Validation now clearly reports engine failures.
- Missing tables and columns continue to be reported when schema information is available.
- Tests
- Added coverage for identifier casing, schema formats, lineage, and schema-free validation.