본문 바로가기
WIKI 기술 지식 베이스

CREATE DOMAIN

원문 보기 위키 갱신

CREATE DOMAIN은 기본 타입 위에 이름이 붙은 제약 래퍼를 정의해요. STRICT 테이블에서 컬럼 타입으로 쓸 수 있어요.

출처: 문서

본문

**Turso 확장 기능**: CREATE DOMAIN은 표준 SQLite에는 없는 Turso 전용 문이에요. 도메인은 STRICT 테이블과 커스텀 타입 실험 기능이 필요해요. 이 기능은 사용 전에 [활성화](/sql-reference/experimental-features)해야 해요.

CREATE DOMAIN 문은 선택적인 제약을 갖춘 이름 붙은 타입 별칭을 정의해요. CREATE TYPE과 달리, 도메인은 커스텀 ENCODE/DECODE 로직이나 OPERATOR를 지정하지 않아요. 대신 도메인은 기본 타입 위에 NOT NULL과 CHECK 제약을 얹고, 값은 기본 타입의 고유 저장 형식으로 저장돼요.

문법

CREATE DOMAIN [IF NOT EXISTS] domain-name AS base-type
    [DEFAULT default-expr]
    [NOT NULL]
    [[CONSTRAINT constraint-name] CHECK (expr)]
    ...;

설명

도메인은 기본 타입을 검증 제약으로 감싸요. 도메인 타입 컬럼에 값을 쓸 때 CHECK 제약과 NOT NULL 제한(있다면)이 검증돼요. 검증을 통과하면 값은 기본 타입의 고유 형식으로 저장돼요. 값을 읽을 때는 그대로 반환돼요.

도메인은 ORDER BY, 인덱싱, 산술, 집계 같은 연산에서 쿼리 엔진에게 투명해요. 도메인 컬럼은 입력 검증이 더해진 점만 빼면 기본 타입 컬럼과 정확히 똑같이 동작해요.

도메인은 STRICT 테이블에서만 동작해요. STRICT가 아닌 테이블에서 도메인 이름을 써도 효과가 없어요.

절

IF NOT EXISTS

같은 이름의 도메인이 이미 존재할 때 생길 오류를 억제해요. 기존 도메인은 그대로 유지돼요.

CREATE DOMAIN IF NOT EXISTS positive_int AS integer CHECK (value > 0);

AS base-type

기반 타입을 지정해요. 기본 타입은 원시 타입(integer, real, text, blob)이나 다른 도메인이 될 수 있어서, 도메인을 겹겹이 쌓을 수 있어요.

CREATE DOMAIN percentage AS integer
    CHECK (value >= 0)
    CHECK (value <= 100);

DEFAULT

이 도메인 타입 컬럼의 기본값을 설정해요. 컬럼 수준의 DEFAULT가 도메인 수준의 DEFAULT를 이겨요.

CREATE DOMAIN status AS text DEFAULT 'active';

CREATE TABLE accounts (
    id INTEGER PRIMARY KEY,
    state status
) STRICT;

INSERT INTO accounts(id) VALUES (1);
SELECT state FROM accounts;
-- active

NOT NULL

도메인에 NOT NULL 제약을 추가해요. 이 도메인 타입 컬럼에 NULL을 넣거나 갱신하려는 시도는, 컬럼 정의에 NOT NULL이 없어도 실패해요.

CREATE DOMAIN required_text AS text NOT NULL;

CREATE TABLE contacts (
    id INTEGER PRIMARY KEY,
    name required_text
) STRICT;

INSERT INTO contacts VALUES (1, 'Alice');
-- OK

INSERT INTO contacts VALUES (2, NULL);
-- Error: domain required_text does not allow null values

컬럼 수준의 NULL 선언은 도메인의 NOT NULL 제약을 이길 수 없어요.

CHECK

검증 제약을 추가해요. 표현식은 검사 중인 입력 값을 가리킬 때 value를 참조할 수 있어요. CHECK 제약은 여러 개 지정할 수 있고, 모두 통과해야 해요.

CREATE DOMAIN positive_int AS integer CHECK (value > 0);

CREATE TABLE measurements (
    id INTEGER PRIMARY KEY,
    reading positive_int
) STRICT;

INSERT INTO measurements VALUES (1, 42);
-- OK

INSERT INTO measurements VALUES (2, -5);
-- Error: domain positive_int constraint violation

CONSTRAINT name CHECK

CHECK 제약에는 문서화 목적으로 이름을 붙일 수 있어요.

CREATE DOMAIN valid_score AS integer
    CONSTRAINT non_negative CHECK (value >= 0)
    CONSTRAINT max_hundred CHECK (value <= 100);

도메인 체이닝

도메인은 다른 도메인을 기본 타입으로 쓸 수 있어요. 도메인이 체인처럼 이어지면, 체인 안의 모든 제약이 자식에서 조상까지 강제돼요.

CREATE DOMAIN base_amount AS integer CHECK (value > 0);
CREATE DOMAIN small_amount AS base_amount CHECK (value < 1000);

CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    quantity small_amount
) STRICT;

INSERT INTO orders VALUES (1, 50);
-- OK: passes both value > 0 and value < 1000

INSERT INTO orders VALUES (2, -1);
-- Error: fails base_amount's CHECK (value > 0)

INSERT INTO orders VALUES (3, 5000);
-- Error: fails small_amount's CHECK (value < 1000)

세 단계 이상의 체이닝도 지원해요:

CREATE DOMAIN text_val AS text;
CREATE DOMAIN nonempty AS text_val CHECK (length(value) > 0);
CREATE DOMAIN short_text AS nonempty CHECK (length(value) < 50);

CREATE TABLE labels (
    id INTEGER PRIMARY KEY,
    name short_text
) STRICT;

INSERT INTO labels VALUES (1, 'OK');
-- OK

INSERT INTO labels VALUES (2, '');
-- Error: fails nonempty's CHECK (length(value) > 0)

UPDATE 강제

도메인 제약은 INSERT뿐 아니라 UPDATE에서도 강제돼요.

CREATE DOMAIN positive_int AS integer CHECK (value > 0);

CREATE TABLE items (
    id INTEGER PRIMARY KEY,
    stock positive_int
) STRICT;

INSERT INTO items VALUES (1, 10);
UPDATE items SET stock = 20 WHERE id = 1;
SELECT stock FROM items;
-- 20

UPDATE items SET stock = -1 WHERE id = 1;
-- Error: domain positive_int constraint violation

CAST 지원

CAST는 도메인의 검증 제약을 적용해요:

CREATE DOMAIN positive_int AS integer CHECK (value > 0);

SELECT CAST(42 AS positive_int);
-- 42

SELECT CAST(-1 AS positive_int);
-- Error: domain positive_int constraint violation

NULL을 NOT NULL 도메인으로 캐스팅하는 건 거절돼요:

CREATE DOMAIN notnull_int AS integer NOT NULL;

SELECT CAST(NULL AS notnull_int);
-- Error: domain notnull_int does not allow null values

테이블 수준 CHECK와 함께 쓰기

도메인 CHECK 제약과 테이블 수준 CHECK 제약은 둘 다 적용돼요. 도메인 제약은 인코딩 중에 검사되고, 테이블 CHECK는 별도로 검사돼요.

CREATE DOMAIN positive_int AS integer CHECK (value > 0);

CREATE TABLE bounded (
    id INTEGER PRIMARY KEY,
    val positive_int CHECK (val < 100)
) STRICT;

INSERT INTO bounded VALUES (1, 50);
-- OK: passes domain CHECK (> 0) and table CHECK (< 100)

INSERT INTO bounded VALUES (2, -1);
-- Error: fails domain CHECK

INSERT INTO bounded VALUES (3, 200);
-- Error: fails table CHECK

도메인 컬럼에서의 연산

도메인 컬럼은 기본 타입과 같은 연산을 지원해요: 산술, 비교, 정렬, 집계.

CREATE DOMAIN myint AS integer;

CREATE TABLE data (
    id INTEGER PRIMARY KEY,
    a myint,
    b myint
) STRICT;

INSERT INTO data VALUES (1, 10, 3);
SELECT a + b, a - b, a * b FROM data;
-- 13|7|30
CREATE DOMAIN myint AS integer;

CREATE TABLE scores (
    id INTEGER PRIMARY KEY,
    val myint
) STRICT;

INSERT INTO scores VALUES (1, 30);
INSERT INTO scores VALUES (2, 10);
INSERT INTO scores VALUES (3, 20);
SELECT val FROM scores ORDER BY val;
-- 10
-- 20
-- 30

도메인 삭제하기

도메인은 DROP DOMAIN으로 삭제하지, DROP TYPE으로 삭제하지 않아요. 도메인에 DROP TYPE을 쓰면(또는 타입에 DROP DOMAIN을 쓰면) 오류가 발생해요.

도메인은 어떤 테이블 컬럼이나 다른 도메인이 참조하는 동안에는 삭제할 수 없어요.

CREATE DOMAIN my_domain AS integer;
CREATE TABLE t(x my_domain) STRICT;

DROP DOMAIN my_domain;
-- Error: type 'my_domain' is in use

DROP TABLE t;
DROP DOMAIN my_domain;
-- OK

제한 사항

  • STRICT 테이블 전용: STRICT가 아닌 테이블에서 도메인 타입을 쓰면 CREATE TABLE과 ALTER TABLE ADD COLUMN 시점에 거절돼요. STRICT가 아닌 테이블은 임의의 타입 이름을 어피니티 힌트로 받아들이지만, 도메인 제약(CHECK, NOT NULL, DEFAULT)은 STRICT 테이블에서만 강제되기 때문에 허용하면 오해를 살 수 있어요.
  • 사용 중일 때 삭제 불가: 도메인은 어떤 테이블 컬럼이나 다른 도메인이 참조하는 동안에는 삭제할 수 없어요.
STRICT가 아닌 테이블이 당시 존재하지 않던 타입 이름으로 만들어졌고(예: `CREATE TABLE t(x mydom)`), 그 후에 같은 이름의 도메인이 만들어졌다면(`CREATE DOMAIN mydom AS ...`), 도메인 제약은 기존 테이블에 소급 적용되지 **않아요**. 이건 버그가 아니에요: STRICT가 아닌 테이블은 일치하는 도메인이 존재하는지와 무관하게 모든 타입 이름을 그저 어피니티 힌트로 취급해요. STRICT 테이블은 CREATE TABLE 시점에 알 수 없는 타입 이름을 거절하기 때문에 이런 상황을 예방해요.
  • UNIQUE, PRIMARY KEY, REFERENCES 없음: 이 제약들은 도메인 정의에서 지원되지 않아요. 대신 테이블 수준 제약을 쓰세요.
  • 절 중복 없음: 같은 정의에 DEFAULT나 NOT NULL 절이 여러 개면 거절돼요. 서로 충돌하는 NULL과 NOT NULL 절도 거절돼요.

더 알아보기 (Learn more)