❄️

Terraform作成したSnowflakeテーブルで、サポートされていないデータ型変更を行う方法

2024/12/22に公開

はじめに

SnowflakeテーブルをIaCで作成することがありますが、テーブル作成後にカラムのデータ型定義を変更したくなる時があるかもしれません。
この際、単純にデータ型記載部分のコードを変更して適用すると、サポート対象外の型変更となっていた場合にTerraformではapplyエラーとなります。
どうすればエラーを回避して型変更ができるのかを考えました。

sf型変更仕様

こちらにデータ型を変更する際のサポート対象/サポート対象外のアクションが記載されています。
例えばNUMBER -> VARCHARや、NUMBER(38,0) -> NUMBER(38,4)などの変更はエラーになります。

CREATE OR REPLACE TABLE tbl (COL NUMBER)
name type kind
COL NUMBER(38,0) COLUMN
ALTER TABLE tbl ALTER COLUMN COL VARCHAR
-- SQLコンパイルエラー:列 COL を型 NUMBER(38,0) から VARCHAR(16777216)に変更することはできません
ALTER TABLE tbl ALTER COLUMN COL NUMBER(38,4)
-- SQLコンパイルエラー:数値のスケールの変更はサポートされていないため、列 COL を型 NUMBER(38,0) から NUMBER(38,4) に変更することはできません。

tf型変更仕様

上記と同様のテーブルリソースを作成し、NUMBER -> VARCHARに変更してみます。

resource "snowflake_table" "table" {
  database                    = "DB"
  schema                      = "SCHEMA"
  name                        = "TBL"
  column {
    name     = "COL"
-   type     = "NUMBER"
+   type     = "VARCHAR"
  }

これを単にterraform applyすると、サポートされていない型変更が走り、エラーでコケます。

  # snowflake_table.table will be updated in-place
  ~ resource "snowflake_table" "table" {
        id                          = "DB|SCHEMA|TBL"
        name                        = "TBL"
        # (8 unchanged attributes hidden)
      ~ column {
            name                    = "COL"
          ~ type                    = "NUMBER(38,0)" -> "VARCHAR"
            # (5 unchanged attributes hidden)
        }
Plan: 0 to add, 1 to change, 0 to destroy.
...
│ Error: error changing property on DB|SCHEMA|TBL: err 002108 (22000): SQLコンパイルエラー:列 COL を型 NUMBER(38,0) から VARCHAR(16777216)に変更することはできません

型変更する方法

大きく分けて、テーブルにデータが入っていない場合と既に入っている場合で対応が異なると思います。

データが入っていない場合

テーブルをリプレースして良いかと思いますので、tfでは2通りの筋の良い実行方法が考えられます。

1. -replaceオプションによるapply

destroy -> createが1コマンドでできる便利なオプションです。

terraform apply -target=snowflake_table.table
  # snowflake_table.table will be replaced, as requested
-/+ resource "snowflake_table" "table" {
      - cluster_by                  = [] -> null
      ~ fully_qualified_name        = "\"DB\".\"SCHEMA\".\"TBL\"" -> (known after apply)
      ~ id                          = "DB|SCHEMA|TBL" -> (known after apply)
        name                        = "TBL"
      ~ owner                       = "SYSADMIN" -> (known after apply)
        # (5 unchanged attributes hidden)
      ~ column {
            name                    = "COL"
          + schema_evolution_record = (known after apply)
          ~ type                    = "NUMBER(38,0)" -> "VARCHAR"
            # (4 unchanged attributes hidden)
        }
Plan: 1 to add, 0 to change, 1 to destroy.
...
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

2. テーブルリソースをコメントアウトしてapply -> 修正後のリソースをコメントインしてapply

CI/CDでapplyすることを前提に組んでいる等のケースでは、target指定のreplaceやdestroyを行うことが難しい場合があるかもしれません。
その場合は該当テーブルをコメントアウトしてapplyすることでdestroyができ、修正後に再度applyすることでリプレースできます。
※カラムだけをコメントアウトしてapplyすると、DROP COLUMNではなく複数カラムへのRENAME+ALTERが走ってしまうのでNG。テーブルリソースごと再作成する必要がある。(stateでカラム順まで管理されているためこのような挙動になると考えられる。)

データが入っている場合

単純なテーブルのリプレースができないので、以下のような実行手順になるかと思います。

  1. 正しい型定義のtmpテーブルリソースを作成
  2. 元テーブルのデータをCASTなどしてtmpテーブルに挿入するクエリを実行
  3. 元テーブルのリソースをリプレースして、型定義を修正した正規のテーブルを作成
  4. tmpテーブルのデータを正規のテーブルに挿入
  5. (tmpテーブルリソースを削除)

結論

  • データがない場合
    1. target指定でapplyできるなら、-replaceオプションでテーブルをリプレース
    2. target指定でapplyできないなら、テーブルリソースのコメントアウト・インでdestory -> create
  • データがある場合
    • tmpテーブルリソース作成 -> データ移行 -> 元テーブルリソース再作成 -> データ移行
感想

なるべくデータ定義変更が起こらないように頑張りたいです。

余談

ALTER COLUMNでは、サポート対象外の型変更時にはエラーメッセージに型が表示されるのですが、地理空間データ型(GEOGRAPHY,GEOMETRY)への変更だけ型がUNKNOWNで表示されるようです。


利用シーンが想定されていないからでしょうかね。

Discussion