はじめに
Snowflakeにはゼロコピークローンと呼ばれる機能があり、追加のストレージコストをかけることなくデータベースやスキーマ、テーブルのコピーを作成することができます。
このゼロコピークローンを行った時のオブジェクトの挙動で気になるものがあったので、実際に試して記事にまとめてみました。
概要
この記事ではデータベースやスキーマをクローンした際に、下記のオブジェクト等がどのような挙動をするのかを実際に試しながら説明していきます。
- テーブルの外部キー制約
- VIEW
- 権限
クローンオブジェクトの作成方法
クローンオブジェクトを作成する構文は下記です。
create <オブジェクトタイプ> <クローンオブジェクト名> clone <ソースオブジェクト名>;
例えばTEST_DB
というデータベースをクローンしてTEST_DB_CLONE
というデータベースを作成するためのクエリは下記になります。
create database TEST_DB_CLONE clone TEST_DB;
テーブルの外部キー制約
Snowflakeには他のデータベースシステムと同様に外部キー制約を定義することができます。
(定義することはできても、その制約は強制はされません)
クローン時の外部キー制約の挙動について、ドキュメントにはこのように書いてあります。
外部キー制約のあるテーブルがクローンされると、クローンされたテーブルは、主キーを含むソースまたはクローンされたテーブルを参照します。
- 両方のテーブルを含むデータベースまたはスキーマがクローンされる場合、外部キーを持つクローンされたテーブルは、他のクローンされたテーブルの主キーを参照します。
- テーブルが別々のデータベースまたはスキーマにある場合、クローンされたテーブルは、ソーステーブルの主キーを参照します。
ざっくりとした解釈としては、データベース(or スキーマ)をクローンする時、あるテーブルが外部キーとして参照しているテーブルが「同じデータベース内にある」か「別のデータベース内にある」かで挙動が異なるということみたいです。
実際に挙動を見ていきます。
まずCOMPANY_A
とCOMPANY_B
というデータベースを作り、それぞれにIDカラムをPKとするDEPARTMENT
テーブルを作成します。
create database COMPANY_A;
create database COMPANY_B;
create table COMPANY_A.PUBLIC.DEPARTMENT (
ID INTEGER NOT NULL,
NAME VARCHAR,
CONSTRAINT pk PRIMARY KEY (ID)
);
create table COMPANY_B.PUBLIC.DEPARTMENT (
ID INTEGER NOT NULL,
NAME VARCHAR,
CONSTRAINT pk PRIMARY KEY (ID)
);
その後、COMPANY_A
データベース内に、EMPLOYEE_A
テーブルとEMPLOYEE_B
テーブルを作成します。
外部キーは下記のように定義していきます。
-
EMPLOYEE_A
テーブルは自分と同じCOMPANY_A
データベースにあるDEPARTMENT
テーブルを参照 -
EMPLOYEE_B
テーブルは自分とは別のCOMPANY_B
データベースにあるDEPARTMENT
テーブルを参照
-- 自分と同じデータベース内のテーブルを参照
create table COMPANY_A.PUBLIC.EMPLOYEE_A (
DEPARTMENT_ID INTEGER NOT NULL,
NAME VARCHAR,
CONSTRAINT fk FOREIGN KEY (DEPARTMENT_ID) REFERENCES COMPANY_A.PUBLIC.DEPARTMENT (ID)
);
-- 自分とは別のデータベース内のテーブルを参照
create table COMPANY_A.PUBLIC.EMPLOYEE_B (
DEPARTMENT_ID INTEGER NOT NULL,
NAME VARCHAR,
CONSTRAINT fk FOREIGN KEY (DEPARTMENT_ID) REFERENCES COMPANY_B.PUBLIC.DEPARTMENT (ID)
);
ここでCOMPANY_A
データベースをクローンしました。
create database COMPANY_A_CLONE clone COMPANY_A;
クローンされたデータベースCOMPANY_A_CLONE
内のテーブルを見てみると、元々同じデータベース内のテーブルを参照していたEMPLOYEE_A
は、クローンデータベースであるCOMPANY_A_CLONE
データベース内のテーブルを参照してします。
一方、元々別のデータベース内のテーブルを参照していたEMPLOYEE_B
は、クローン後も変わらずCOMPANY_B
データベース内のテーブルを参照し続けています。
図にまとめると下記のような挙動になります。
つまり、
- 外部キーとして同じデータベース内のテーブルを参照している時はデータベースごとクローンされるとクローンされたデータベース内のテーブルを参照する
- 別のデータベースを参照しているものはクローン後も別のデータベースを参照し続ける
という挙動になります。
これは多くの人のイメージ通りかなと思います。
VIEW
次にVIEWの挙動です。
データベースやスキーマをクローンした時のVIEWの挙動で注意すべきは、VIEW定義のfrom句で指定するテーブルがどの程度修飾されているか です。
下記のようなテーブルの修飾の度合いの違いにより、クローンした時の挙動が異なってきます。
- テーブルが完全修飾されているパターン
select * from <db>.<schema>.<table>
- テーブルが修飾されていないパターン
select * from <table>
実際に試してみます。
まずTEST_DB
データベースを作成し、その中にIDカラムを持つTEST_TABLE
テーブルを作成します。
create database TEST_DB;
create table TEST_DB.PUBLIC.TEST_TABLE (ID NUMBER);
次に参照するテーブルの修飾の度合いを変えた3パターンのVIEWを用意します。
- 修飾なし(
<table>
)
create view TEST_DB.PUBLIC.VIEW_A as
select * from TEST_TABLE;
- スキーマのみ修飾(
<schema>.<table>
)
create view TEST_DB.PUBLIC.VIEW_B as
select * from PUBLIC.TEST_TABLE;
- 完全修飾(
<db>.<schema>.<table>
)
create view TEST_DB.PUBLIC.VIEW_C as
select * from TEST_DB.PUBLIC.TEST_TABLE;
ここでTEST_DB
をクローンします。
create database TEST_DB_CLONE clone TEST_DB;
ソースとなるデータベースのTEST_TABLE
には {1, 2, 3} という値を入れ、クローンしたデータベースのTEST_TABLE
には {100, 200, 300} という値を入れておきます。
insert into TEST_DB.PUBLIC.TEST_TABLE
values (1), (2), (3);
insert into TEST_DB_CLONE.PUBLIC.TEST_TABLE
values (100), (200), (300);
ここでクローンしたデータベースでそれぞれのVIEWを呼び出してみると、
VIEW_A
&VIEW_B
と、VIEW_C
で結果が分かれました。
VIEW_C
(完全修飾(<db>.<schema>.<table>
)のパターン)のみ、ソースデータベースTEST_DB
内のテーブルを参照し続け、それ以外の2つのVIEWはクローンされた方TEST_DB_CLONE
のテーブルを参照するような挙動をしました。
図にまとめるとこのようになります。
イメージとしては、クローンしてもVIEWの定義文自体は変わらず、クローンされたデータベース内ではその定義に忠実に従うという感じかなと思います。
テーブルの修飾を意識せずにVIEWの定義をしていると、クローンした時に予想外の挙動になる可能性もありそうなので注意が必要ですね。
今回はデータベースをクローンしましたが、スキーマをクローンした時もクローン後のVIEWは定義文通りのテーブルを参照するような挙動になります。
(今回の例で言うと、VIEW_A
と、VIEW_B
&VIEW_C
で結果が分かれる)
気になる方は実際に試してみてください。
権限
最後にクローンしたオブジェクトの権限について見ていこうと思います。
ドキュメントによると、クローンされたオブジェクト自体の権限は継承されず、クローンされたオブジェクトの子オブジェクトについての権限は継承されるようです。
(例えばデータベースの子オブジェクトにはスキーマやテーブルなどが相当します。)
ソースオブジェクトがデータベースまたはスキーマである場合、クローンは、ソースオブジェクト含まれている子オブジェクトのクローンに対して付与された すべて の権限を継承します。
実際に確認してみます。
まずTEST_DB
とその中のPUBLIC
スキーマにUSERADMINの権限を付与します。
次にTEST_DB
をクローンしてTEST_DB_CLONE
を作成します。(ACCOUNTADMIN権限を利用)
create or replace database TEST_DB_CLONE clone TEST_DB;
するとデータベース自体は権限が継承されず、TEST_DB_CLONE
にはUSERADMINの権限は付いていません。
一方、子オブジェクトであるPUBLIC
スキーマの権限は維持されたままクローンされています。
クローンの作成といってもコマンド自体はCREATE DATABASE ~
なので、クローンデータベース自体には「データベースを作ったロールのOWNERSHIP権限のみが付く」という挙動は自然かもしれないですね。
ただテーブルのクローンに関しては CREATE ~ CLONE
する時に COPY GRANTS
パラメーターが使えるため、それを使用することでソースオブジェクトの権限を継承した状態でクローンを作成することができます。
まとめ
この記事ではオブジェクトをクローンした時の挙動を試してみました。
特にVIEWはテーブルを修飾しているかどうかで挙動が変わるので、クローンした時に困らないように修飾方法まで意識するのが大事だなと思いました。