Skip to content

fix: avoid re-running run-once migrations (409)#422

Open
cburyta wants to merge 3 commits intoSnowflake-Labs:masterfrom
cburyta:fix/issue-409-information-schema-case
Open

fix: avoid re-running run-once migrations (409)#422
cburyta wants to merge 3 commits intoSnowflake-Labs:masterfrom
cburyta:fix/issue-409-information-schema-case

Conversation

@cburyta
Copy link
Copy Markdown

@cburyta cburyta commented Feb 13, 2026

Pull Request: Fix INFORMATION_SCHEMA case sensitivity (#409)

Summary

Fixes the bug where schemachange re-deploys all versioned scripts when the change history table is specified with lowercase schema/table names (e.g. mydb.myschema.change_history). Snowflake stores identifiers in uppercase in INFORMATION_SCHEMA; the previous query used a case-sensitive comparison, so the lookup returned no rows and schemachange treated the table as missing.

Problem

  • Issue: #409 – Case sensitivity of the config file
  • Symptom: "When running schemachange it deploys everything every time. I get a lot of duplicate versions in the SCHEMACHANGE_CHANGE_HISTORY table."
  • Root cause: fetch_change_history_metadata() and change_history_schema_exists() compared against INFORMATION_SCHEMA using REPLACE('schema_name','"','') without normalizing case. Snowflake returns TABLE_SCHEMA = 'MYSCHEMA', so 'myschema' != 'MYSCHEMA' and the query returns no rows.

Solution

Use UPPER(REPLACE(...)) in the WHERE clause for:

  1. fetch_change_history_metadata()TABLE_SCHEMA and TABLE_NAME in the INFORMATION_SCHEMA.TABLES query.
  2. change_history_schema_exists()SCHEMA_NAME in the INFORMATION_SCHEMA.SCHEMATA query.

No change to object references (e.g. fully_qualified); Snowflake is already case-insensitive for those. Only the INFORMATION_SCHEMA lookups are fixed.

Testing

  • New test: test_fetch_change_history_metadata_uses_upper_for_information_schema – builds a session with lowercase ChangeHistoryTable and asserts the executed query contains UPPER(REPLACE( so the lookup is case-insensitive.

Checklist

  • Tests pass (pytest)
  • Code formatted (ruff format .)
  • Lint clean (ruff check .)
  • Test added for the fix
  • CHANGELOG.md

…atch (Snowflake-Labs#409)

Snowflake stores schema and table names in uppercase in INFORMATION_SCHEMA.
When change_history_table is specified with lowercase names (e.g. from config),
the previous REPLACE-only comparison failed, so fetch_change_history_metadata()
returned {} and schemachange re-ran all versioned scripts.

- fetch_change_history_metadata: UPPER(REPLACE(...)) for TABLE_SCHEMA and TABLE_NAME
- change_history_schema_exists: UPPER(REPLACE(...)) for SCHEMA_NAME
@cburyta cburyta changed the title Fix/issue 409 information schema case fix: avoid re-running run-once migrations (409) Feb 16, 2026
@sfc-gh-tmathew
Copy link
Copy Markdown
Collaborator

Thanks for the PR! One thing to flag on the UPPER() approach: it breaks quoted mixed-case identifiers. When someone specifies "MySchema", get_snowflake_identifier_string stores it as "MySchema", and REPLACE('"MySchema"', '"', '') correctly yields MySchema. Adding UPPER() turns that into MYSCHEMA, but INFORMATION_SCHEMA stores quoted identifiers in their original case — so the lookup fails.

Rather than changing the SQL, would you be open to fixing this in get_snowflake_identifier_string in config/utils.py? Unquoted identifiers that match the word pattern should be uppercased at parse time, since that's Snowflake's own normalization behavior:

elif snowflake_identifier_pattern.match(input_value):
    return input_value.upper()

That handles both cases correctly without needing any changes to the queries.

@cburyta
Copy link
Copy Markdown
Author

cburyta commented Apr 21, 2026

Glad to go that route editing in the python side - that would feel cleaner.

However a couple notes...

  • if QUOTED_IDENTIFIERS_IGNORE_CASE=TRUE is for some reason set in the Snowflake env, I don't believe that edge case is covered without some extra thought
  • I believe hitting that method will impact more than just this migration. I might approach it different though, I think instead of return input_value.upper()

DeployConfig.py might change the case of other props for example. (Edit: I might consider editing ChangeHistoryTable.py from_str for a python based approach.)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants