名前付き内部ステージをクローン時のファイルコピーの挙動を確認してみた #SnowflakeDB

名前付き内部ステージをクローン時のファイルコピーの挙動を確認してみた #SnowflakeDB

2025.08.21

はじめに

2025年5月のリリースで、データベースまたはスキーマレベルでのクローン作成時に、名前付き内部ステージのクローンを作成するように指定できるようになりました。
ディレクトリテーブルを有効化していれば、ステージに配置されたファイルをコピーすることも可能です。この際の挙動を確認したく試してみたので記事としました。

https://docs.snowflake.com/en/release-notes/2025/9_12#support-for-internal-stage-cloning-general-availability

アップデートの概要

これまで名前付き内部ステージはクローンすることができませんでした。

Snowflake ではオブジェクトをデータベースやスキーマレベルでクローンできますが、この際INCLUDE INTERNAL STAGESオプションを指定することで、データベースまたはスキーマのクローン時に、名前付き内部ステージのクローンを含めることができるようになりました。

さらに対象のステージでディレクトリテーブルが有効化されている場合、 ステージ上のファイルのコピーも作成されます。ファイルのコピーには COPY FILES が使用されます。

また、ファイルコピー時の主な仕様は以下の通りです。

  • ファイルが更新されてもディレクトリテーブルが更新されていない場合、更新されたファイルはコピーされない
  • クローン時にタイムトラベルを使用しているかどうかに関係なく、内部ステージの現在の状態がクローンされる
    • ステージが作成される前の時点を指定した場合、そのステージはクローンされない

https://docs.snowflake.com/en/sql-reference/sql/create-clone#label-create-clone-internal-stages-notes

試してみる

ファイルコピー時の仕様について、上述の通りではありますが、実際に試してみます。

事前準備

以下の内容で内部ステージを作成します。ポイントとしてディレクトリテーブルを有効化しています。また、現在 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 |
+-------------------------------------------------------------------------------------------------+

https://docs.snowflake.com/ja/developer-guide/snowflake-cli/command-reference/stage-commands/overview

ステージ上の 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 |
+---+

さいごに

名前付き内部ステージのクローンと、ファイルコピー時の仕様を確認しました。ファイルを含める場合、ディレクトリテーブルの有効化と更新も必要なためご注意ください。
こちらの内容が何かの参考になれば幸いです。

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.