名前付き内部ステージをクローン時のファイルコピーの挙動を確認してみた #SnowflakeDB
はじめに
2025年5月のリリースで、データベースまたはスキーマレベルでのクローン作成時に、名前付き内部ステージのクローンを作成するように指定できるようになりました。
ディレクトリテーブルを有効化していれば、ステージに配置されたファイルをコピーすることも可能です。この際の挙動を確認したく試してみたので記事としました。
アップデートの概要
これまで名前付き内部ステージはクローンすることができませんでした。
Snowflake ではオブジェクトをデータベースやスキーマレベルでクローンできますが、この際INCLUDE INTERNAL STAGES
オプションを指定することで、データベースまたはスキーマのクローン時に、名前付き内部ステージのクローンを含めることができるようになりました。
さらに対象のステージでディレクトリテーブルが有効化されている場合、 ステージ上のファイルのコピーも作成されます。ファイルのコピーには COPY FILES が使用されます。
また、ファイルコピー時の主な仕様は以下の通りです。
- ファイルが更新されてもディレクトリテーブルが更新されていない場合、更新されたファイルはコピーされない
- クローン時にタイムトラベルを使用しているかどうかに関係なく、内部ステージの現在の状態がクローンされる
- ステージが作成される前の時点を指定した場合、そのステージはクローンされない
試してみる
ファイルコピー時の仕様について、上述の通りではありますが、実際に試してみます。
事前準備
以下の内容で内部ステージを作成します。ポイントとしてディレクトリテーブルを有効化しています。また、現在 AWS 上の Snowflake アカウントであれば名前付き内部ステージのディレクトリテーブルを自動更新するように構成できます。ここではこのオプションは有効化していません。
USE SCHEMA PUBLIC;
CREATE OR ALTER STAGE my_int_stage
DIRECTORY=(
ENABLE = TRUE
AUTO_REFRESH = FALSE)
;
ステージにファイルを配置します。ここでは Snowflake CLI を使用し、ローカルのファイルを配置しました。
# SQLクエリをファイルに書き込む
echo "SELECT 1;" > test.sql
# ファイルの中身を確認
$ cat test.sql
SELECT 1;
# ステージにファイルを配置
$ snow stage copy test.sql @my_int_stage/ \
--database test_db \
--schema public
+----------------------------------------------------------------------------------------------------------------+
| source | target | source_size | target_size | source_compression | target_compression | status | message |
|----------+----------+-------------+-------------+--------------------+--------------------+----------+---------|
| test.sql | test.sql | 10 | 16 | NONE | NONE | UPLOADED | |
+----------------------------------------------------------------------------------------------------------------+
# ステージのファイルを確認
$ snow stage list-files @my_int_stage/ \
--database test_db \
--schema public
+-------------------------------------------------------------------------------------------------+
| name | size | md5 | last_modified |
|-----------------------+------+----------------------------------+-------------------------------|
| my_int_stage/test.sql | 16 | e13f5ed42911feec34c9954a7cf1360f | Wed, 20 Aug 2025 07:40:45 GMT |
+-------------------------------------------------------------------------------------------------+
ステージ上の SQL ファイルは実行可能です。
>execute immediate from @test_db.public.my_int_stage/test.sql
+---+
| 1 |
|---|
| 1 |
+---+
ディレクトリテーブルを確認します。自動更新は有効化していなので、ALTER STAGE での更新が必要です。
>SELECT * FROM DIRECTORY( @test_db.public.my_int_stage );
+---------------+------+---------------+-----+------+----------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+---------------+-----+------+----------|
+---------------+------+---------------+-----+------+----------+
--更新
ALTER STAGE test_db.public.my_int_stage REFRESH;
>SELECT * FROM DIRECTORY( @test_db.public.my_int_stage );
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------|
| test.sql | 16 | 2025-08-20 16:40:45.000 +0900 | e13f5ed42911feec34c9954a7cf1360f | e13f5ed42911feec34c9954a7cf1360f | https://xxxxx.ap-northeast-1.aws.snowflakecomputing.com/api/files/TEST_DB/PUBLIC/MY_INT_STAGE/test%2esql |
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------+
スキーマレベルのクローンにより名前付き内部ステージをクローン
ここではスキーマをクローンすることで、名前付き内部ステージをクローンに含めます。
--スキーマレベルでクローンを作成
CREATE OR REPLACE SCHEMA clone_schema
CLONE test_db.public
INCLUDE INTERNAL STAGES
;
クローン先のステージを確認します。ディレクトリテーブルを有効化しているので、ステージに含まれていたファイルもコピーされています。
--クローン先のステージを確認
>ls @test_db.clone_schema.my_int_stage;
+-----------------------+------+----------------------------------+-------------------------------+
| name | size | md5 | last_modified |
|-----------------------+------+----------------------------------+-------------------------------|
| my_int_stage/test.sql | 16 | 2f278a463315e7a85f5b1b7429d39ebc | Wed, 20 Aug 2025 08:19:10 GMT |
+-----------------------+------+----------------------------------+-------------------------------+
--ディレクトリテーブルで確認:クローン先でも有効化されている
--LAST MODIFIED はクローンされた時間
>SELECT * FROM DIRECTORY( @test_db.clone_schema.my_int_stage );
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------|
| test.sql | 16 | 2025-08-20 17:19:10.443 +0900 | 2f278a463315e7a85f5b1b7429d39ebc | 2f278a463315e7a85f5b1b7429d39ebc | https://xxxxx.ap-northeast-1.aws.snowflakecomputing.com/api/files/TEST_DB/CLONE_SCHEMA/MY_INT_STAGE/test%2esql |
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------+
SQL ファイルを実行します。クローン元と同じ結果が返ります。
>execute immediate from @test_db.clone_schema.my_int_stage/test.sql;
+---+
| 1 |
|---|
| 1 |
+---+
クローン元でファイルを更新
クローン元の名前付き内部ステージのファイルを以下の通り更新します。
# SQLクエリをファイルに書き込む
echo "SELECT 2;" > test.sql
# ファイルの中身を確認
$ cat test.sql
SELECT 2;
# ステージにファイルを配置
$ snow stage copy test.sql @my_int_stage/ \
--database test_db \
--schema public \
--overwrite
更新後の SQL ファイルを実行してみます。
>execute immediate from @test_db.public.my_int_stage/test.sql;
+---+
| 2 |
|---|
| 2 |
+---+
この状態でディレクトリテーブルを確認します。ポイントとして、ディレクトリテーブルの自動更新は無効なので、[LAST_MODIFIED] などディレクトリテーブル上のファイル情報は更新前の内容となっています。
--ディレクトリテーブルを確認
--更新していないので、LAST_MODIFIEDは更新前と同じ値
>SELECT * FROM DIRECTORY( @test_db.public.my_int_stage );
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------|
| test.sql | 16 | 2025-08-20 16:40:45.000 +0900 | e13f5ed42911feec34c9954a7cf1360f | e13f5ed42911feec34c9954a7cf1360f | https://xxxxx.ap-northeast-1.aws.snowflakecomputing.com/api/files/TEST_DB/PUBLIC/MY_INT_STAGE/test%2esql |
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------+
この状態(ディレクトリテーブルのメタデータは更新せずに)でスキーマレベルでクローンしてみます。
--スキーマレベルでクローン
CREATE OR REPLACE SCHEMA test_db.clone_schema
CLONE test_db.public
INCLUDE INTERNAL STAGES
;
クローン先のステージを確認すると、ファイルがコピーされていません。これは仕様通りで、クローン元でファイルが更新されても、ディレクトリテーブルが更新されていない場合、更新されたファイルはコピーされないためです。
--ファイルがコピーされない
>SELECT * FROM DIRECTORY( @test_db.clone_schema.my_int_stage );
+---------------+------+---------------+-----+------+----------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+---------------+-----+------+----------|
+---------------+------+---------------+-----+------+----------+
ディレクトリテーブルを更新した上でクローンするとファイルもコピーされます。
--クローン元の名前付き内部ステージでディレクトリテーブルを更新
ALTER STAGE test_db.public.my_int_stage REFRESH;
--スキーマレベルでクローン
CREATE OR REPLACE SCHEMA test_db.clone_schema
CLONE test_db.public
INCLUDE INTERNAL STAGES
;
--ファイルがコピーされる
>SELECT * FROM DIRECTORY( @test_db.clone_schema.my_int_stage );
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------|
| test.sql | 16 | 2025-08-20 17:36:06.707 +0900 | 67e2ea18b2f148ee8e7436327e913501 | 67e2ea18b2f148ee8e7436327e913501 | https://xxxxx.ap-northeast-1.aws.snowflakecomputing.com/api/files/TEST_DB/CLONE_SCHEMA/MY_INT_STAGE/test%2esql |
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------+
SQL ファイルを実行
>execute immediate from @test_db.clone_schema.my_int_stage/test.sql;
+---+
| 2 |
|---|
| 2 |
+---+
タイムトラベルでクローンを作成
さいごにタイムトラベルでクローンを作成してみます。ここではファイル更新前のタイムスタンプを指定します。
--タイムトラベルでスキーマレベルでクローンを作成
CREATE OR REPLACE SCHEMA test_db.clone_schema
CLONE test_db.public AT (TIMESTAMP => '2025-08-20 17:00:00'::TIMESTAMP_LTZ)
INCLUDE INTERNAL STAGES
;
--クローン先のディレクトリテーブルを確認
>SELECT * FROM DIRECTORY( @test_db.clone_schema.my_int_stage );
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------+
| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------|
| test.sql | 16 | 2025-08-20 17:39:22.244 +0900 | d3367678bf019cb6f8f81a85069021b3 | d3367678bf019cb6f8f81a85069021b3 | https://xxxxx.ap-northeast-1.aws.snowflakecomputing.com/api/files/TEST_DB/CLONE_SCHEMA/MY_INT_STAGE/test%2esql |
+---------------+------+-------------------------------+----------------------------------+----------------------------------+------------------------------------------------------------------------------------------------------------------+
SQL ファイルを実行すると、更新後の内容となっています。これも仕様通りで、タイムトラベルを使用しているかどうかに関係なく、内部ステージの現在の状態がクローンされるためです。
--タイムトラベル(AT | BEFORE)を使用しているかどうかに関係なく、内部ステージの現在の状態をクローン
>execute immediate from @test_db.clone_schema.my_int_stage/test.sql;
+---+
| 2 |
|---|
| 2 |
+---+
さいごに
名前付き内部ステージのクローンと、ファイルコピー時の仕様を確認しました。ファイルを含める場合、ディレクトリテーブルの有効化と更新も必要なためご注意ください。
こちらの内容が何かの参考になれば幸いです。