Releases: drizzle-team/drizzle-orm
drizzle-kit@0.25.0
Breaking changes and migrate guide for Turso users
If you are using Turso and libsql, you will need to upgrade your drizzle.config
and @libsql/client
package.
- This version of drizzle-orm will only work with
@libsql/client@0.10.0
or higher if you are using themigrate
function. For other use cases, you can continue using previous versions(But the suggestion is to upgrade)
To install the latest version, use the command:
npm i @libsql/client@latest
- Previously, we had a common
drizzle.config
for SQLite and Turso users, which allowed a shared strategy for both dialects. Starting with this release, we are introducing the turso dialect in drizzle-kit. We will evolve and improve Turso as a separate dialect with its own migration strategies.
Before
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "sqlite",
schema: "./schema.ts",
out: "./drizzle",
dbCredentials: {
url: "database.db",
},
breakpoints: true,
verbose: true,
strict: true,
});
After
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "turso",
schema: "./schema.ts",
out: "./drizzle",
dbCredentials: {
url: "database.db",
},
breakpoints: true,
verbose: true,
strict: true,
});
If you are using only SQLite, you can use dialect: "sqlite"
LibSQL/Turso and Sqlite migration updates
SQLite "generate" and "push" statements updates
Starting from this release, we will no longer generate comments like this:
'/*\n SQLite does not support "Changing existing column type" out of the box, we do not generate automatic migration for that, so it has to be done manually'
+ '\n Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php'
+ '\n https://www.sqlite.org/lang_altertable.html'
+ '\n https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3'
+ "\n\n Due to that we don't generate migration automatically and it has to be done manually"
+ '\n*/'
We will generate a set of statements, and you can decide if it's appropriate to create data-moving statements instead. Here is an example of the SQL file you'll receive now:
PRAGMA foreign_keys=OFF;
--> statement-breakpoint
CREATE TABLE `__new_worker` (
`id` integer PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`salary` text NOT NULL,
`job_id` integer,
FOREIGN KEY (`job_id`) REFERENCES `job`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
INSERT INTO `__new_worker`("id", "name", "salary", "job_id") SELECT "id", "name", "salary", "job_id" FROM `worker`;
--> statement-breakpoint
DROP TABLE `worker`;
--> statement-breakpoint
ALTER TABLE `__new_worker` RENAME TO `worker`;
--> statement-breakpoint
PRAGMA foreign_keys=ON;
LibSQL/Turso "generate" and "push" statements updates
Since LibSQL supports more ALTER statements than SQLite, we can generate more statements without recreating your schema and moving all the data, which can be potentially dangerous for production environments.
LibSQL and Turso will now have a separate dialect in the Drizzle config file, meaning that we will evolve Turso and LibSQL independently from SQLite and will aim to support as many features as Turso/LibSQL offer.
With the updated LibSQL migration strategy, you will have the ability to:
- Change Data Type: Set a new data type for existing columns.
- Set and Drop Default Values: Add or remove default values for existing columns.
- Set and Drop NOT NULL: Add or remove the NOT NULL constraint on existing columns.
- Add References to Existing Columns: Add foreign key references to existing columns
You can find more information in the LibSQL documentation
LIMITATIONS
- Dropping foreign key will cause table recreation.
This is because LibSQL/Turso does not support dropping this type of foreign key.
CREATE TABLE `users` (
`id` integer NOT NULL,
`name` integer,
`age` integer PRIMARY KEY NOT NULL
FOREIGN KEY (`name`) REFERENCES `users1`("id") ON UPDATE no action ON DELETE no action
);
-
If the table has indexes, altering columns will cause index recreation:
Drizzle-Kit will drop the indexes, modify the columns, and then create the indexes. -
Adding or dropping composite foreign keys is not supported and will cause table recreation.
-
Primary key columns can not be altered and will cause table recreation.
-
Altering columns that are part of foreign key will cause table recreation.
NOTES
- You can create a reference on any column type, but if you want to insert values, the referenced column must have a unique index or primary key.
CREATE TABLE parent(a PRIMARY KEY, b UNIQUE, c, d, e, f);
CREATE UNIQUE INDEX i1 ON parent(c, d);
CREATE INDEX i2 ON parent(e);
CREATE UNIQUE INDEX i3 ON parent(f COLLATE nocase);
CREATE TABLE child1(f, g REFERENCES parent(a)); -- Ok
CREATE TABLE child2(h, i REFERENCES parent(b)); -- Ok
CREATE TABLE child3(j, k, FOREIGN KEY(j, k) REFERENCES parent(c, d)); -- Ok
CREATE TABLE child4(l, m REFERENCES parent(e)); -- Error!
CREATE TABLE child5(n, o REFERENCES parent(f)); -- Error!
CREATE TABLE child6(p, q, FOREIGN KEY(p, q) REFERENCES parent(b, c)); -- Error!
CREATE TABLE child7(r REFERENCES parent(c)); -- Error!
NOTE: The foreign key for the table child5 is an error because, although the parent key column has a unique index, the index uses a different collating sequence.
See more: https://www.sqlite.org/foreignkeys.html
New casing
param in drizzle-orm
and drizzle-kit
There are more improvements you can make to your schema definition. The most common way to name your variables in a database and in TypeScript code is usually snake_case
in the database and camelCase
in the code. For this case, in Drizzle, you can now define a naming strategy in your database to help Drizzle map column keys automatically. Let's take a table from the previous example and make it work with the new casing API in Drizzle
Table can now become:
import { pgTable } from "drizzle-orm/pg-core";
export const ingredients = pgTable("ingredients", (t) => ({
id: t.uuid().defaultRandom().primaryKey(),
name: t.text().notNull(),
description: t.text(),
inStock: t.boolean().default(true),
}));
As you can see, inStock
doesn't have a database name alias, but by defining the casing configuration at the connection level, all queries will automatically map it to snake_case
const db = await drizzle('node-postgres', { connection: '', casing: 'snake_case' })
For drizzle-kit
migrations generation you should also specify casing
param in drizzle config, so you can be sure you casing strategy will be applied to drizzle-kit as well
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
schema: "./schema.ts",
dbCredentials: {
url: "postgresql://postgres:password@localhost:5432/db",
},
casing: "snake_case",
});
drizzle-kit@0.24.2
New Features
🎉 Support for pglite
driver
You can now use pglite with all drizzle-kit commands, including Drizzle Studio!
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
driver: "pglite",
schema: "./schema.ts",
dbCredentials: {
url: "local-pg.db",
},
verbose: true,
strict: true,
});
Bug fixes
- mysql-kit: fix GENERATED ALWAYS AS ... NOT NULL - #2824
drizzle-kit@0.24.1
Bug fixes
Big thanks to @L-Mario564 for his PR. It conflicted in most cases with a PR that was merged, but we incorporated some of his logic. Merging it would have caused more problems and taken more time to resolve, so we just took a few things from his PR, like removing "::" mappings in introspect and some array type default handlers
What was fixed
- The Drizzle Kit CLI was not working properly for the
introspect
command. - Added the ability to use column names with special characters for all dialects.
- Included PostgreSQL sequences in the introspection process.
- Reworked array type introspection and added all test cases.
- Fixed all (we hope) default issues in PostgreSQL, where
::<type>
was included in the introspected output. preserve
casing option was broken
Tickets that were closed
- [BUG]: invalid schema generation with drizzle-kit introspect:pg
- [BUG][mysql introspection]: TS error when introspect column including colon
- [BUG]: Unhandled defaults when introspecting postgres db
- [BUG]: PostgreSQL Enum Naming and Schema Typing Issue
- [BUG]: drizzle-kit instrospect command generates syntax error on varchar column types
- [BUG]: Introspecting varchar[] type produces syntactically invalid schema.ts
- [BUG]: introspect:pg column not using generated enum name
- [BUG]: drizzle-kit introspect casing "preserve" config not working
- [BUG]: drizzle-kit introspect fails on required param that is defined
- [BUG]: Error when running npx drizzle-kit introspect: "Expected object, received string"
- [BUG]: Missing index names when running introspect command [MYSQL]
- [BUG]: drizzle-kit introspect TypeError: Cannot read properties of undefined (reading 'toLowerCase')
- [BUG]: Wrong column name when using PgEnum.array()
- [BUG]: Incorrect Schema Generated when introspecting extisting pg database
- [
⚠️ 🐞BUG]: index() missing argument after introspection, causes tsc error that fails the build - [BUG]: drizzle-kit introspect small errors
- [BUG]: Missing bigint import in drizzle-kit introspect
0.33.0
Breaking changes (for some of postgres.js users)
Bugs fixed for this breaking change
- [BUG]: jsonb always inserted as a json string when using postgres-js
- [BUG]: jsonb type on postgres implement incorrectly
As we are doing with other drivers, we've changed the behavior of PostgreSQL-JS to pass raw JSON values, the same as you see them in the database. So if you are using the PostgreSQL-JS driver and passing data to Drizzle elsewhere, please check the new behavior of the client after it is passed to Drizzle.
We will update it to ensure it does not override driver behaviors, but this will be done as a complex task for everything in Drizzle in other releases
If you were using postgres-js
with jsonb
fields, you might have seen stringified objects in your database, while drizzle insert and select operations were working as expected.
You need to convert those fields from strings to actual JSON objects. To do this, you can use the following query to update your database:
if you are using jsonb:
update table_name
set jsonb_column = (jsonb_column #>> '{}')::jsonb;
if you are using json:
update table_name
set json_column = (json_column #>> '{}')::json;
We've tested it in several cases, and it worked well, but only if all stringified objects are arrays or objects. If you have primitives like strings, numbers, booleans, etc., you can use this query to update all the fields
if you are using jsonb:
UPDATE table_name
SET jsonb_column = CASE
-- Convert to JSONB if it is a valid JSON object or array
WHEN jsonb_column #>> '{}' LIKE '{%' OR jsonb_column #>> '{}' LIKE '[%' THEN
(jsonb_column #>> '{}')::jsonb
ELSE
jsonb_column
END
WHERE
jsonb_column IS NOT NULL;
if you are using json:
UPDATE table_name
SET json_column = CASE
-- Convert to JSON if it is a valid JSON object or array
WHEN json_column #>> '{}' LIKE '{%' OR json_column #>> '{}' LIKE '[%' THEN
(json_column #>> '{}')::json
ELSE
json_column
END
WHERE json_column IS NOT NULL;
If nothing works for you and you are blocked, please reach out to me @AndriiSherman. I will try to help you!
Bug Fixes
drizzle-kit@0.24.0
Breaking changes (for SQLite users)
Fixed Composite primary key order is not consistent by removing sort
in SQLite and to be consistent with the same logic in PostgreSQL and MySQL
The issue that may arise for SQLite users with any driver using composite primary keys is that the order in the database may differ from the Drizzle schema.
-
If you are using
push
, you MAY be prompted to update your table with a new order of columns in the composite primary key. You will need to either change it manually in the database or push the changes, but this may lead to data loss, etc. -
If you are using
generate
, you MAY also be prompted to update your table with a new order of columns in the composite primary key. You can either keep that migration or skip it by emptying the SQL migration file.
If nothing works for you and you are blocked, please reach out to me @AndriiSherman. I will try to help you!
Bug fixes
- [BUG] When using double type columns, import is not inserted - thanks @Karibash
- [BUG] A number value is specified as the default for a column of type char - thanks @Karibash
- [BUG]: Array default in migrations are wrong - thanks @L-Mario564
- [FEATURE]: Simpler default array fields - thanks @L-Mario564
- [BUG]: drizzle-kit generate succeeds but generates invalid SQL for default([]) - Postgres - thanks @L-Mario564
- [BUG]: Incorrect type for array column default value - thanks @L-Mario564
- [BUG]: error: column is of type integer[] but default expression is of type integer - thanks @L-Mario564
- [BUG]: Default value in array generating wrong migration file - thanks @L-Mario564
- [BUG]: enum as array, not possible? - thanks @L-Mario564
0.32.2
- Fix AWS Data API type hints bugs in RQB
- Fix set transactions in MySQL bug - thanks @roguesherlock
- Add forwaring dependencies within useLiveQuery, fixes #2651 - thanks @anstapol
- Export additional types from SQLite package, like
AnySQLiteUpdate
- thanks @veloii
drizzle-kit@0.23.2
- Fixed a bug in PostgreSQL with push and introspect where the
schemaFilter
object was passed. It was detecting enums even in schemas that were not defined in the schemaFilter. - Fixed the
drizzle-kit up
command to work as expected, starting from the sequences release.
0.32.1
- Fix typings for indexes and allow creating indexes on 3+ columns mixing columns and expressions - thanks @lbguilherme!
- Added support for "limit 0" in all dialects - closes #2011 - thanks @sillvva!
- Make inArray and notInArray accept empty list, closes #1295 - thanks @RemiPeruto!
- fix typo in lt typedoc - thanks @dalechyn!
- fix wrong example in README.md - thanks @7flash!
0.32.0
Release notes for drizzle-orm@0.32.0
and drizzle-kit@0.23.0
It's not mandatory to upgrade both packages, but if you want to use the new features in both queries and migrations, you will need to upgrade both packages
New Features
🎉 MySQL $returningId()
function
MySQL itself doesn't have native support for RETURNING
after using INSERT
. There is only one way to do it for primary keys
with autoincrement
(or serial
) types, where you can access insertId
and affectedRows
fields. We've prepared an automatic way for you to handle such cases with Drizzle and automatically receive all inserted IDs as separate objects
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 }[]
Also with Drizzle, you can specify a primary key
with $default
function that will generate custom primary keys at runtime. We will also return those generated keys for you in the $returningId()
call
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 }[]
If there is no primary keys -> type will be
{}[]
for such queries
🎉 PostgreSQL Sequences
You can now specify sequences in Postgres within any schema you need and define all the available properties
Example
import { pgSchema, pgSequence } from "drizzle-orm/pg-core";
// No params specified
export const customSequence = pgSequence("name");
// Sequence with params
export const customSequence = pgSequence("name", {
startWith: 100,
maxValue: 10000,
minValue: 100,
cycle: true,
cache: 10,
increment: 2
});
// Sequence in custom schema
export const customSchema = pgSchema('custom_schema');
export const customSequence = customSchema.sequence("name");
🎉 PostgreSQL Identity Columns
Source: As mentioned, the serial
type in Postgres is outdated and should be deprecated. Ideally, you should not use it. Identity columns
are the recommended way to specify sequences in your schema, which is why we are introducing the identity columns
feature
Example
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"),
});
You can specify all properties available for sequences in the .generatedAlwaysAsIdentity()
function. Additionally, you can specify custom names for these sequences
PostgreSQL docs reference.
🎉 PostgreSQL Generated Columns
You can now specify generated columns on any column supported by PostgreSQL to use with generated columns
Example with generated column for tsvector
Note: we will add
tsVector
column type before latest release
import { SQL, sql } from "drizzle-orm";
import { customType, index, integer, pgTable, text } from "drizzle-orm/pg-core";
const tsVector = customType<{ data: string }>({
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),
})
);
In case you don't need to reference any columns from your table, you can use just sql
template or a 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
You can now specify generated columns on any column supported by MySQL to use with generated columns
You can specify both stored
and virtual
options, for more info you can check MySQL docs
Also MySQL has a few limitation for such columns usage, which is described here
Drizzle Kit will also have limitations for push
command:
-
You can't change the generated constraint expression and type using
push
. Drizzle-kit will ignore this change. To make it work, you would need todrop the column
,push
, and thenadd a column with a new expression
. This was done due to the complex mapping from the database side, where the schema expression will be modified on the database side and, on introspection, we will get a different string. We can't be sure if you changed this expression or if it was changed and formatted by the database. As long as these are generated columns andpush
is mostly used for prototyping on a local database, it should be fast todrop
andcreate
generated columns. Since these columns aregenerated
, all the data will be restored -
generate
should have no limitations
Example
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" }
),
}),
In case you don't need to reference any columns from your table, you can use just sql
template or a string
in .generatedAlwaysAs()
🎉 SQLite Generated Columns
You can now specify generated columns on any column supported by SQLite to use with generated columns
You can specify both stored
and virtual
options, for more info you can check SQLite docs
Also SQLite has a few limitation for such columns usage, which is described here
Drizzle Kit will also have limitations for push
and generate
command:
-
You can't change the generated constraint expression with the stored type in an existing table. You would need to delete this table and create it again. This is due to SQLite limitations for such actions. We will handle this case in future releases (it will involve the creation of a new table with data migration).
-
You can't add a
stored
generated expression to an existing column for the same reason as above. However, you can add avirtual
expression to an existing column. -
You can't change a
stored
generated expression in an existing column for the same reason as above. However, you can change avirtual
expression. -
You can't change the generated constraint type from
virtual
tostored
for the same reason as above. However, you can change fromstored
tovirtual
.
New Drizzle Kit features
🎉 Migrations support for all the new orm features
PostgreSQL sequences, identity columns and generated columns for all dialects
🎉 New flag --force
for drizzle-kit push
You can auto-accept all data-loss statements using the push command. It's only available in CLI parameters. Make sure you always use it if you are fine with running data-loss statements on your database
🎉 New migrations
flag prefix
You can now customize migration file prefixes to make the format suitable for your migration tools:
index
is the default type and will result in0001_name.sql
file names;supabase
andtimestamp
are equal and will result in20240627123900_name.sql
file names;unix
will result in unix seconds prefixes1719481298_name.sql
file names;none
will omit the prefix completely;
Example: Supabase migrations format
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: "postgresql",
migrations: {
prefix: 'supabase'
}
});