drizzle-orm@0.32.0
및 drizzle-kit@0.23.0
릴리스 노트
두 패키지를 모두 업그레이드할 필요는 없지만, 쿼리와 마이그레이션에서 새로운 기능을 사용하려면 두 패키지를 모두 업그레이드해야 합니다.
New Features
🎉 MySQL $returningId()
함수
MySQL 자체적으로는 INSERT
이후에 RETURNING
을 지원하지 않습니다. autoincrement
(또는 serial
) 타입의 primary key
에 대해서만 insertId
와 affectedRows
필드에 접근할 수 있습니다. Drizzle을 사용하면 이러한 경우를 자동으로 처리하고, 삽입된 모든 ID를 별도의 객체로 받을 수 있습니다.
import { boolean, int, text, mysqlTable } from 'drizzle-orm/mysql-core';
const usersTable = mysqlTable('users', {
id: int('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
});
const result = await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]).$returningId();
// ^? { id: number }[]
또한 Drizzle을 사용하면 $default
함수를 통해 런타임에 커스텀 primary key
를 생성할 수 있습니다. 이렇게 생성된 키도 $returningId()
호출 시 반환됩니다.
import { varchar, text, mysqlTable } from 'drizzle-orm/mysql-core';
import { createId } from '@paralleldrive/cuid2';
const usersTableDefFn = mysqlTable('users_default_fn', {
customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(createId),
name: text('name').notNull(),
});
const result = await db.insert(usersTableDefFn).values([{ name: 'John' }, { name: 'John1' }]).$returningId();
// ^? { customId: string }[]
만약
primary key
가 없다면, 해당 쿼리의 타입은{}[]
가 됩니다.
🎉 PostgreSQL 시퀀스
이제 Postgres에서 원하는 스키마 내에 시퀀스를 지정하고, 사용 가능한 모든 속성을 정의할 수 있습니다.
예제
import { pgSchema, pgSequence } from "drizzle-orm/pg-core";
// 파라미터 없이 시퀀스 생성
export const customSequence = pgSequence("name");
// 파라미터를 지정하여 시퀀스 생성
export const customSequence = pgSequence("name", {
startWith: 100, // 시작 값
maxValue: 10000, // 최대 값
minValue: 100, // 최소 값
cycle: true, // 순환 여부
cache: 10, // 캐시 크기
increment: 2 // 증가 값
});
// 커스텀 스키마 내에서 시퀀스 생성
export const customSchema = pgSchema('custom_schema');
export const customSequence = customSchema.sequence("name");
🎉 PostgreSQL IDENTITY 컬럼
출처: 앞서 언급했듯이, Postgres의 serial
타입은 구식이며 더 이상 사용하지 않는 것이 좋습니다. 여러분은 이를 사용하지 않는 것이 이상적입니다. IDENTITY 컬럼이 스키마에서 시퀀스를 지정하는 권장 방식이기 때문에, 이번에 IDENTITY 컬럼
기능을 소개합니다.
import { pgTable, integer, text } from 'drizzle-orm/pg-core'
export const ingredients = pgTable("ingredients", {
id: integer("id").primaryKey().generatedAlwaysAsIdentity({ startWith: 1000 }),
name: text("name").notNull(),
description: text("description"),
});
.generatedAlwaysAsIdentity()
함수에서 시퀀스와 관련된 모든 속성을 지정할 수 있습니다. 또한, 이러한 시퀀스에 커스텀 이름을 지정할 수도 있습니다.
PostgreSQL 문서 참조.
🎉 PostgreSQL 생성된 컬럼(Generated Columns)
PostgreSQL에서 지원하는 모든 컬럼에 대해 생성된 컬럼(Generated Columns)을 지정할 수 있습니다. 이를 통해 특정 컬럼의 값을 자동으로 계산하거나 생성할 수 있습니다.
예를 들어, 다음과 같이 생성된 컬럼을 정의할 수 있습니다:
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
price NUMERIC(10, 2) NOT NULL,
discounted_price NUMERIC(10, 2) GENERATED ALWAYS AS (price * 0.9) STORED
);
위 예제에서 discounted_price
컬럼은 price
컬럼의 값에 0.9를 곱한 결과를 자동으로 저장합니다. 이렇게 생성된 컬럼을 사용하면 데이터의 일관성을 유지하면서도 계산된 값을 쉽게 관리할 수 있습니다.
예제: tsvector
를 위한 생성된 컬럼
참고: 최신 릴리스 전에
tsVector
컬럼 타입을 추가할 예정입니다.
import { SQL, sql } from "drizzle-orm";
import { customType, index, integer, pgTable, text } from "drizzle-orm/pg-core";
const tsVector = customType({
dataType() {
return "tsvector";
},
});
export const test = pgTable(
"test",
{
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
content: text("content"),
contentSearch: tsVector("content_search", {
dimensions: 3,
}).generatedAlwaysAs(
(): SQL => sql`to_tsvector('english', ${test.content})`
),
},
(t) => ({
idx: index("idx_content_search").using("gin", t.contentSearch),
})
);
테이블의 다른 컬럼을 참조할 필요가 없는 경우, sql
템플릿이나 string
을 사용할 수 있습니다.
export const users = pgTable("users", {
id: integer("id"),
name: text("name"),
generatedName: text("gen_name").generatedAlwaysAs(sql`hello world!`),
generatedName1: text("gen_name1").generatedAlwaysAs("hello world!"),
}),
🎉 MySQL 생성된 컬럼(Generated Columns)
이제 MySQL에서 지원하는 모든 컬럼에 생성된 컬럼을 지정할 수 있습니다. 생성된 컬럼을 사용하려면 stored
와 virtual
옵션을 모두 지정할 수 있습니다. 더 자세한 정보는 MySQL 공식 문서를 참고하세요.
또한 MySQL은 이러한 컬럼 사용에 몇 가지 제한 사항이 있습니다. 이에 대한 내용은 여기에서 확인할 수 있습니다.
Drizzle Kit도 push
커맨드 사용 시 몇 가지 제한 사항이 있습니다:
-
push
를 사용하여 생성된 컬럼의 제약 조건 표현식과 타입을 변경할 수 없습니다. Drizzle-kit은 이 변경을 무시합니다. 이를 적용하려면 컬럼을drop
하고,push
를 실행한 후, 새로운 표현식으로 컬럼을 다시add
해야 합니다. 이는 데이터베이스 측에서 스키마 표현식이 수정되고, 인트로스펙션 시 다른 문자열로 반환되는 복잡한 매핑 문제 때문입니다. 여러분이 이 표현식을 변경했는지, 아니면 데이터베이스가 변경하고 포맷팅했는지 확신할 수 없기 때문입니다. 생성된 컬럼은push
가 주로 로컬 데이터베이스에서 프로토타이핑에 사용되므로, 컬럼을drop
하고 다시create
하는 것이 빠를 것입니다. 이 컬럼들은generated
이므로 모든 데이터가 복원됩니다. -
generate
에는 제한 사항이 없어야 합니다.
-- 예시: 생성된 컬럼 사용
CREATE TABLE example (
id INT PRIMARY KEY,
price DECIMAL(10, 2),
quantity INT,
total_price DECIMAL(10, 2) AS (price * quantity) STORED
);
위 예제에서 total_price
는 price
와 quantity
를 곱한 값으로 자동 계산되는 생성된 컬럼입니다.
예제
export const users = mysqlTable("users", {
id: int("id"),
id2: int("id2"),
name: text("name"),
generatedName: text("gen_name").generatedAlwaysAs(
(): SQL => sql`${schema2.users.name} || 'hello'`,
{ mode: "stored" }
),
generatedName1: text("gen_name1").generatedAlwaysAs(
(): SQL => sql`${schema2.users.name} || 'hello'`,
{ mode: "virtual" }
),
});
테이블의 컬럼을 참조할 필요가 없다면, .generatedAlwaysAs()
안에 sql
템플릿이나 string
을 사용할 수 있습니다.
🎉 SQLite 생성된 컬럼(Generated Columns)
이제 SQLite에서 지원하는 모든 컬럼에 생성된 컬럼(Generated Columns)을 지정할 수 있습니다. 생성된 컬럼을 사용하려면 stored
와 virtual
옵션을 모두 지정할 수 있습니다. 더 자세한 정보는 SQLite 공식 문서를 참고하세요.
또한 SQLite는 이러한 컬럼 사용에 몇 가지 제한 사항이 있습니다. 이에 대한 내용은 여기에서 확인할 수 있습니다.
Drizzle Kit도 push
와 generate
커맨드에 대해 몇 가지 제한 사항이 있습니다:
-
기존 테이블에서
stored
타입의 생성된 제약 조건 표현식을 변경할 수 없습니다. 이 경우 테이블을 삭제하고 다시 생성해야 합니다. 이는 SQLite의 제한 사항 때문입니다. 향후 릴리스에서 이 문제를 해결할 예정입니다(새 테이블 생성 및 데이터 마이그레이션 과정이 포함될 예정). -
기존 컬럼에
stored
생성된 표현식을 추가할 수 없습니다. 위와 같은 이유 때문입니다. 그러나virtual
표현식은 기존 컬럼에 추가할 수 있습니다. -
기존 컬럼의
stored
생성된 표현식을 변경할 수 없습니다. 위와 같은 이유 때문입니다. 그러나virtual
표현식은 변경할 수 있습니다. -
생성된 제약 조건 타입을
virtual
에서stored
로 변경할 수 없습니다. 위와 같은 이유 때문입니다. 그러나stored
에서virtual
로는 변경할 수 있습니다.
이러한 제한 사항은 SQLite의 기술적 한계로 인해 발생하며, 향후 업데이트에서 개선될 예정입니다.
New Drizzle Kit features
🎉 새로운 ORM 기능에 대한 마이그레이션 지원
모든 데이터베이스 방언(dialect)에 대해 PostgreSQL 시퀀스, ID 컬럼, 생성된 컬럼 지원
// 예제: 마이그레이션 파일
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddGeneratedColumns1712345678901 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "user"
ADD COLUMN "full_name" VARCHAR GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "user"
DROP COLUMN "full_name"
`);
}
}
이제 여러분은 TypeORM을 사용하여 PostgreSQL 시퀀스, ID 컬럼, 생성된 컬럼과 같은 새로운 ORM 기능을 마이그레이션 파일에서 쉽게 활용할 수 있습니다. 위 예제는 full_name
이라는 생성된 컬럼을 추가하는 방법을 보여줍니다. 이 컬럼은 first_name
과 last_name
을 결합하여 자동으로 생성됩니다.
🎉 drizzle-kit push
에 --force
플래그 추가
데이터 손실이 발생할 수 있는 구문을 자동으로 승인하려면 push 명령어에 --force
플래그를 사용할 수 있습니다. 이 기능은 커맨드라인 파라미터에서만 사용 가능합니다. 데이터베이스에서 데이터 손실이 발생해도 괜찮다면 항상 이 플래그를 사용하세요.
drizzle-kit push --force
🎉 새로운 migrations
플래그 prefix
이제 마이그레이션 파일 접두사를 커스텀하여 여러분의 마이그레이션 도구에 맞는 형식으로 만들 수 있습니다:
index
는 기본 타입으로,0001_name.sql
과 같은 파일 이름이 생성됩니다.supabase
와timestamp
는 동일하며,20240627123900_name.sql
과 같은 파일 이름이 생성됩니다.unix
는 유닉스 초를 접두사로 사용하여1719481298_name.sql
과 같은 파일 이름이 생성됩니다.none
은 접두사를 완전히 생략합니다.
예제: Supabase 마이그레이션 형식
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql", // 데이터베이스 방언 설정
migrations: {
prefix: 'supabase' // 마이그레이션 파일 접두사 설정
}
});