Skip to content

fix(snowflake): quote case-sensitive lowercase column identifiers in generated SQL - #43312

Open
sadpandajoe wants to merge 4 commits into
masterfrom
fix-snowflake-case-sensitive-column-quoting
Open

fix(snowflake): quote case-sensitive lowercase column identifiers in generated SQL#43312
sadpandajoe wants to merge 4 commits into
masterfrom
fix-snowflake-case-sensitive-column-quoting

Conversation

@sadpandajoe

Copy link
Copy Markdown
Member

fix(snowflake): quote case-sensitive lowercase column identifiers in generated SQL

SUMMARY

Snowflake folds unquoted identifiers to UPPERCASE. A physical table created with an
explicitly double-quoted, case-sensitive lowercase column (e.g.
create table t ("id" int)) is stored exactly as id, but Superset's generated
chart queries referenced it unquoted (SELECT id ...). Snowflake then resolved the
unquoted reference to ID, which does not match the physical column, and the query
failed with a column-not-found error.

Root cause: Superset's Snowflake integration correctly detects the exact reflected
case of a column via SQLAlchemy's quoted_name(..., quote=True), but that signal is
lost when the name is persisted into a plain string ORM column
(TableColumn.column_name). At query-generation time, Superset falls back to
SQLAlchemy's generic auto-quoting heuristic, which only quotes identifiers containing
uppercase or special characters — the opposite of Snowflake's own case-folding
convention, where it's the all-lowercase identifiers that need quoting to survive.

Fix: add a prepare_identifier() hook to BaseEngineSpec (default: no-op, so every
other engine's SQL generation is unchanged) and override it in SnowflakeEngineSpec
to explicitly force-quote the stored identifier via quoted_name(name, quote=True)
whenever the owning dataset has normalize_columns disabled (the default) — in that
case the stored name is already the exact physical identifier, so quoting it verbatim
is always safe. The hook is applied at the physical-column construction sites used by
chart SELECT/GROUP BY/ORDER BY/WHERE/metric expressions and timestamp columns.

A related but separate symptom — a table whose own name is an explicitly
quoted lowercase Snowflake identifier can block dataset creation — is a different code
path (table/schema existence checks, not column query generation) and is intentionally
out of scope here; it needs its own follow-up.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — backend-only SQL generation fix, no UI change. No Snowflake credentials were
available in this environment to capture a live before/after against a real warehouse;
see TESTING INSTRUCTIONS for how the fix was verified offline.

TESTING INSTRUCTIONS

  1. pytest tests/unit_tests/connectors/sqla/models_test.py -k snowflake_case_sensitive
  2. pytest tests/unit_tests/models/helpers_test.py -k snowflake_case_sensitive
  3. pytest tests/unit_tests/db_engine_specs/test_snowflake.py -k prepare_identifier
  4. pytest tests/unit_tests/db_engine_specs/test_base.py -k prepare_identifier

Manual verification against a real Snowflake instance (not performed here — no
credentials available):

  1. create table bug_test ("id" int, "name" varchar); then insert a few rows.
  2. Connect the database to Superset, create a physical dataset from bug_test.
  3. Build an un-aggregated table chart using the id/name columns.
  4. Before this fix: the chart errors with a column-not-found error. After: it renders.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.67%. Comparing base (8c500cc) to head (68da159).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
superset/db_engine_specs/snowflake.py 50.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43312      +/-   ##
==========================================
- Coverage   66.73%   66.67%   -0.06%     
==========================================
  Files        2876     2876              
  Lines      164194   164133      -61     
  Branches    37883    37849      -34     
==========================================
- Hits       109570   109434     -136     
- Misses      52467    52543      +76     
+ Partials     2157     2156       -1     
Flag Coverage Δ
hive 38.10% <25.00%> (-0.01%) ⬇️
mysql 57.76% <85.00%> (+<0.01%) ⬆️
postgres 57.80% <85.00%> (+<0.01%) ⬆️
presto 40.04% <40.00%> (+<0.01%) ⬆️
python 59.18% <85.00%> (+<0.01%) ⬆️
sqlite 57.44% <85.00%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sadpandajoe
sadpandajoe marked this pull request as ready for review August 18, 2026 23:13
@dosubot dosubot Bot added change:backend Requires changing the backend data:connect:snowflake Related to Snowflake labels Aug 18, 2026
@bito-code-review

bito-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #3918ed

Actionable Suggestions - 0
Review Details
  • Files reviewed - 8 · Commit Range: d5f5c40..d1fd9cc
    • superset/connectors/sqla/models.py
    • superset/db_engine_specs/base.py
    • superset/db_engine_specs/snowflake.py
    • superset/models/helpers.py
    • tests/unit_tests/connectors/sqla/models_test.py
    • tests/unit_tests/db_engine_specs/test_base.py
    • tests/unit_tests/db_engine_specs/test_snowflake.py
    • tests/unit_tests/models/helpers_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:backend Requires changing the backend data:connect:snowflake Related to Snowflake size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant