schemachange icon indicating copy to clipboard operation
schemachange copied to clipboard

Error during initial deploy when dry-run and create-change-history-table set to true

Open chris-gonzalez-321 opened this issue 9 months ago • 6 comments

Describe the bug

Hello,

This is the error in question:

File "/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/schemachange/cli.py", line 71, in main
    deploy(config=config, session=session)
    ~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/schemachange/deploy.py", line 102, in deploy
    script_metadata = versioned_scripts.get(script.name)
                      ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'

I took a quick peek at the code. It looks like if dry-run and create-change-history-table are both set to true, with CHANGE_HISTORY table not yet created, the code errors when attempting to find a versioned script entry in a dictionary set to None.

To Reproduce

Run schemachange command and make sure:

  1. dry-run is set to true
  2. create-change-history-table is set to true
  3. CHANGE_HISTORY table does not yet exist

Expected behavior

This was caught because of how we have our pipeline set up - where a dry-run deploy runs first, then the true deploy after manual approval.

If an initial "true" deploy should be done prior to doing the dry-run, then the documentation should probably include a disclaimer for dry-run option (unless I missed it).

Otherwise, I'm thinking the fix would be:

Return an empty dictionary for already-applied versioned scripts only if:

  1. dry-run is set to true
  2. create-change-history-table is set to true
  3. CHANGE_HISTORY table does not yet exist

Schemachange

  • Version: 4.0.1

chris-gonzalez-321 avatar Mar 07 '25 00:03 chris-gonzalez-321

Also getting this one 👍

carter293 avatar Mar 11 '25 08:03 carter293

Also experiencing issues with this.

AugustTom avatar May 16 '25 13:05 AugustTom

We found out on our end if schemas and tables do not exist in Snowflake, dry-run will fail. First of all, ensure that you have them ready, so then dry-run would work

GarciaPL avatar May 18 '25 15:05 GarciaPL

here is a work around.

if you remove the --create-change-history param, and manually run this sql (more or less) on your snowflake, you'll be good. reference the CHANGE_HISTORY DDL on the main doc page.

CREATE SCHEMA IF NOT EXISTS DEV_INTEGRATION.SCHEMACHANGE;
USE SCHEMA DEV_INTEGRATION.SCHEMACHANGE;

CREATE TABLE IF NOT EXISTS "SCHEMACHANGE"."CHANGE_HISTORY"
(
    VERSION        VARCHAR,
    DESCRIPTION    VARCHAR,
    SCRIPT         VARCHAR,
    SCRIPT_TYPE    VARCHAR,
    CHECKSUM       VARCHAR,
    EXECUTION_TIME NUMBER,
    STATUS         VARCHAR,
    INSTALLED_BY   VARCHAR,
    INSTALLED_ON   TIMESTAMP_LTZ
);

and be sure to point schemachange at the custom path to

"--change-history-table DEV_INTEGRATION.SCHEMACHANGE.CHANGE_HISTORY"

mylons avatar Jul 08 '25 04:07 mylons

The problem is in the get_script_metadata function inside the SnowflakeSession.py

When in --dry_run and the change table does not exists there

        change_history_table_exists = self.change_history_table_exists(
            create_change_history_table=create_change_history_table,
            dry_run=dry_run,
        )
        if not change_history_table_exists:
            return None, None, None

This returns None as versioned_scripts when initialization occurs in the deploy.py and fails when calling get method on None object.

    (
        versioned_scripts,
        r_scripts_checksum,
        max_published_version,
    ) = session.get_script_metadata(
        create_change_history_table=config.create_change_history_table,
        dry_run=config.dry_run,
    )

A quick fix would be return defaultdict(dict)

        change_history_table_exists = self.change_history_table_exists(
            create_change_history_table=create_change_history_table,
            dry_run=dry_run,
        )
        if not change_history_table_exists:
            return defaultdict(dict), None, None

This will return empty dictionary for versioned scripts and --dry_run will behave normally.

PavelPawlowski avatar Sep 25 '25 08:09 PavelPawlowski

For dry-run on initial deployment, use --create-change-history-table to create the table if missing.

schemachange deploy --dry-run --create-change-history-table

Fixed for 4.2.0 (mid-December). Give it a test when it's available to verify it handles your scenario.

sfc-gh-tmathew avatar Nov 18 '25 17:11 sfc-gh-tmathew