Files
2026-07-13 12:10:05 +08:00

1401 lines
32 KiB
TypeScript

import { createClient } from '@libsql/client';
import chalk from 'chalk';
import { sql } from 'drizzle-orm';
import {
blob,
check,
foreignKey,
getTableConfig,
index,
int,
integer,
numeric,
real,
sqliteTable,
sqliteView,
text,
uniqueIndex,
} from 'drizzle-orm/sqlite-core';
import { diffTestSchemasPushLibSQL } from 'tests/schemaDiffer';
import { expect, test } from 'vitest';
test('nothing changed in schema', async (t) => {
const turso = createClient({
url: ':memory:',
});
const users = sqliteTable('users', {
id: integer('id').primaryKey().notNull(),
name: text('name').notNull(),
email: text('email'),
textJson: text('text_json', { mode: 'json' }),
blobJon: blob('blob_json', { mode: 'json' }),
blobBigInt: blob('blob_bigint', { mode: 'bigint' }),
numeric: numeric('numeric'),
createdAt: integer('created_at', { mode: 'timestamp' }),
createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }),
real: real('real'),
text: text('text', { length: 255 }),
role: text('role', { enum: ['admin', 'user'] }).default('user'),
isConfirmed: integer('is_confirmed', {
mode: 'boolean',
}),
});
const schema1 = {
users,
customers: sqliteTable('customers', {
id: integer('id').primaryKey(),
address: text('address').notNull(),
isConfirmed: integer('is_confirmed', { mode: 'boolean' }),
registrationDate: integer('registration_date', { mode: 'timestamp_ms' })
.notNull()
.$defaultFn(() => new Date()),
userId: integer('user_id')
.references(() => users.id)
.notNull(),
}),
posts: sqliteTable('posts', {
id: integer('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
}),
};
const {
sqlStatements,
statements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(turso, schema1, schema1, [], false);
expect(sqlStatements.length).toBe(0);
expect(statements.length).toBe(0);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
});
test('added, dropped index', async (t) => {
const turso = createClient({
url: ':memory:',
});
const users = sqliteTable('users', {
id: integer('id').primaryKey().notNull(),
name: text('name').notNull(),
email: text('email'),
textJson: text('text_json', { mode: 'json' }),
blobJon: blob('blob_json', { mode: 'json' }),
blobBigInt: blob('blob_bigint', { mode: 'bigint' }),
numeric: numeric('numeric'),
createdAt: integer('created_at', { mode: 'timestamp' }),
createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }),
real: real('real'),
text: text('text', { length: 255 }),
role: text('role', { enum: ['admin', 'user'] }).default('user'),
isConfirmed: integer('is_confirmed', {
mode: 'boolean',
}),
});
const schema1 = {
users,
customers: sqliteTable(
'customers',
{
id: integer('id').primaryKey(),
address: text('address').notNull(),
isConfirmed: integer('is_confirmed', { mode: 'boolean' }),
registrationDate: integer('registration_date', { mode: 'timestamp_ms' })
.notNull()
.$defaultFn(() => new Date()),
userId: integer('user_id').notNull(),
},
(table) => ({
uniqueIndex: uniqueIndex('customers_address_unique').on(table.address),
}),
),
posts: sqliteTable('posts', {
id: integer('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
}),
};
const schema2 = {
users,
customers: sqliteTable(
'customers',
{
id: integer('id').primaryKey(),
address: text('address').notNull(),
isConfirmed: integer('is_confirmed', { mode: 'boolean' }),
registrationDate: integer('registration_date', { mode: 'timestamp_ms' })
.notNull()
.$defaultFn(() => new Date()),
userId: integer('user_id').notNull(),
},
(table) => ({
uniqueIndex: uniqueIndex('customers_is_confirmed_unique').on(
table.isConfirmed,
),
}),
),
posts: sqliteTable('posts', {
id: integer('id').primaryKey(),
content: text('content'),
authorId: integer('author_id'),
}),
};
const {
sqlStatements,
statements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(turso, schema1, schema2, [], false);
expect(statements.length).toBe(2);
expect(statements[0]).toStrictEqual({
type: 'drop_index',
tableName: 'customers',
data: 'customers_address_unique;address;true;',
schema: '',
});
expect(statements[1]).toStrictEqual({
type: 'create_index',
tableName: 'customers',
data: 'customers_is_confirmed_unique;is_confirmed;true;',
schema: '',
internal: { indexes: {} },
});
expect(sqlStatements.length).toBe(2);
expect(sqlStatements[0]).toBe(
`DROP INDEX \`customers_address_unique\`;`,
);
expect(sqlStatements[1]).toBe(
`CREATE UNIQUE INDEX \`customers_is_confirmed_unique\` ON \`customers\` (\`is_confirmed\`);`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('added column not null and without default to table with data', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
companies: sqliteTable('companies', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
}),
};
const schema2 = {
companies: sqliteTable('companies', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
age: integer('age').notNull(),
}),
};
const table = getTableConfig(schema1.companies);
const seedStatements = [
`INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('drizzle');`,
`INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('turso');`,
];
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
false,
seedStatements,
);
expect(statements.length).toBe(1);
expect(statements[0]).toStrictEqual({
type: 'sqlite_alter_table_add_column',
tableName: 'companies',
column: {
name: 'age',
type: 'integer',
primaryKey: false,
notNull: true,
autoincrement: false,
},
referenceData: undefined,
});
expect(sqlStatements.length).toBe(2);
expect(sqlStatements[0]).toBe(`delete from companies;`);
expect(sqlStatements[1]).toBe(
`ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(1);
expect(infoToPrint![0]).toBe(
`· You're about to add not-null ${
chalk.underline(
'age',
)
} column without default value, which contains 2 items`,
);
expect(shouldAskForApprove).toBe(true);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(1);
expect(tablesToTruncate![0]).toBe('companies');
});
test('added column not null and without default to table without data', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
companies: sqliteTable('companies', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
}),
};
const schema2 = {
companies: sqliteTable('companies', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
age: integer('age').notNull(),
}),
};
const {
sqlStatements,
statements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(turso, schema1, schema2, [], false);
expect(statements.length).toBe(1);
expect(statements[0]).toStrictEqual({
type: 'sqlite_alter_table_add_column',
tableName: 'companies',
column: {
name: 'age',
type: 'integer',
primaryKey: false,
notNull: true,
autoincrement: false,
},
referenceData: undefined,
});
expect(sqlStatements.length).toBe(1);
expect(sqlStatements[0]).toBe(
`ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`,
);
expect(infoToPrint!.length).toBe(0);
expect(columnsToRemove!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('drop autoincrement. drop column with data', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
companies: sqliteTable('companies', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name'),
}),
};
const schema2 = {
companies: sqliteTable('companies', {
id: integer('id').primaryKey({ autoIncrement: false }),
}),
};
const table = getTableConfig(schema1.companies);
const seedStatements = [
`INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, 'drizzle');`,
`INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, 'turso');`,
];
const {
sqlStatements,
statements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
false,
seedStatements,
);
expect(statements.length).toBe(1);
expect(statements[0]).toStrictEqual({
type: 'recreate_table',
tableName: 'companies',
columns: [
{
name: 'id',
type: 'integer',
autoincrement: false,
notNull: true,
primaryKey: true,
generated: undefined,
},
],
compositePKs: [],
referenceData: [],
uniqueConstraints: [],
checkConstraints: [],
});
expect(sqlStatements.length).toBe(4);
expect(sqlStatements[0]).toBe(
`CREATE TABLE \`__new_companies\` (
\t\`id\` integer PRIMARY KEY NOT NULL
);\n`,
);
expect(sqlStatements[1]).toBe(`INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`);
expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`);
expect(sqlStatements[3]).toBe(
`ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`,
);
expect(columnsToRemove!.length).toBe(1);
expect(infoToPrint!.length).toBe(1);
expect(infoToPrint![0]).toBe(
`· You're about to delete ${
chalk.underline(
'name',
)
} column in companies table with 2 items`,
);
expect(shouldAskForApprove).toBe(true);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('change autoincrement. table is part of foreign key', async (t) => {
const turso = createClient({
url: ':memory:',
});
const companies1 = sqliteTable('companies', {
id: integer('id').primaryKey({ autoIncrement: true }),
});
const users1 = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').unique(),
companyId: integer('company_id').references(() => companies1.id),
});
const schema1 = {
companies: companies1,
users: users1,
};
const companies2 = sqliteTable('companies', {
id: integer('id').primaryKey({ autoIncrement: false }),
});
const users2 = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').unique(),
companyId: integer('company_id').references(() => companies2.id),
});
const schema2 = {
companies: companies2,
users: users2,
};
const { name: usersTableName } = getTableConfig(users1);
const { name: companiesTableName } = getTableConfig(companies1);
const seedStatements = [
`INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('drizzle');`,
`INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('turso');`,
`INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES (1);`,
`INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES (2);`,
];
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
false,
seedStatements,
);
expect(statements.length).toBe(1);
expect(statements[0]).toStrictEqual({
type: 'recreate_table',
tableName: 'companies',
columns: [
{
name: 'id',
type: 'integer',
autoincrement: false,
notNull: true,
primaryKey: true,
generated: undefined,
},
],
compositePKs: [],
referenceData: [],
uniqueConstraints: [],
checkConstraints: [],
});
expect(sqlStatements.length).toBe(4);
expect(sqlStatements[0]).toBe(
`CREATE TABLE \`__new_companies\` (
\t\`id\` integer PRIMARY KEY NOT NULL
);\n`,
);
expect(sqlStatements[1]).toBe(
`INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`,
);
expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`);
expect(sqlStatements[3]).toBe(
`ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('drop not null, add not null', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
}),
posts: sqliteTable(
'posts',
{
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
userId: int('user_id'),
},
),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
}),
posts: sqliteTable(
'posts',
{
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
userId: int('user_id'),
},
),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements!.length).toBe(2);
expect(statements![0]).toStrictEqual({
columnAutoIncrement: false,
columnDefault: undefined,
columnName: 'name',
columnNotNull: false,
columnOnUpdate: undefined,
columnPk: false,
newDataType: 'text',
schema: '',
tableName: 'users',
type: 'alter_table_alter_column_drop_notnull',
});
expect(statements![1]).toStrictEqual({
columnAutoIncrement: false,
columnDefault: undefined,
columnName: 'name',
columnNotNull: true,
columnOnUpdate: undefined,
columnPk: false,
newDataType: 'text',
schema: '',
tableName: 'posts',
type: 'alter_table_alter_column_set_notnull',
});
expect(sqlStatements!.length).toBe(2);
expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`);
expect(sqlStatements![1]).toBe(`ALTER TABLE \`posts\` ALTER COLUMN "name" TO "name" text NOT NULL;`);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('drop table with data', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
}),
posts: sqliteTable(
'posts',
{
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
userId: int('user_id'),
},
),
};
const schema2 = {
posts: sqliteTable(
'posts',
{
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
userId: int('user_id'),
},
),
};
const seedStatements = [
`INSERT INTO \`users\` ("name") VALUES ('drizzle')`,
];
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
false,
seedStatements,
);
expect(statements!.length).toBe(1);
expect(statements![0]).toStrictEqual({
policies: [],
schema: undefined,
tableName: 'users',
type: 'drop_table',
});
expect(sqlStatements!.length).toBe(1);
expect(sqlStatements![0]).toBe(`DROP TABLE \`users\`;`);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(1);
expect(infoToPrint![0]).toBe(`· You're about to delete ${chalk.underline('users')} table with 1 items`);
expect(shouldAskForApprove).toBe(true);
expect(tablesToRemove!.length).toBe(1);
expect(tablesToRemove![0]).toBe('users');
expect(tablesToTruncate!.length).toBe(0);
});
test('recreate table with nested references', async (t) => {
const turso = createClient({
url: ':memory:',
});
let users = sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
age: integer('age'),
});
let subscriptions = sqliteTable('subscriptions', {
id: int('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').references(() => users.id),
customerId: text('customer_id'),
});
const schema1 = {
users: users,
subscriptions: subscriptions,
subscriptionMetadata: sqliteTable('subscriptions_metadata', {
id: int('id').primaryKey({ autoIncrement: true }),
subscriptionId: text('subscription_id').references(
() => subscriptions.id,
),
}),
};
users = sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
});
const schema2 = {
users: users,
subscriptions: subscriptions,
subscriptionMetadata: sqliteTable('subscriptions_metadata', {
id: int('id').primaryKey({ autoIncrement: true }),
subscriptionId: text('subscription_id').references(
() => subscriptions.id,
),
}),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(turso, schema1, schema2, []);
expect(statements!.length).toBe(1);
expect(statements![0]).toStrictEqual({
columns: [
{
autoincrement: false,
name: 'id',
notNull: true,
generated: undefined,
primaryKey: true,
type: 'integer',
},
{
autoincrement: false,
name: 'name',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'text',
},
{
autoincrement: false,
name: 'age',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'integer',
},
],
compositePKs: [],
referenceData: [],
tableName: 'users',
type: 'recreate_table',
uniqueConstraints: [],
checkConstraints: [],
});
expect(sqlStatements!.length).toBe(4);
expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` (
\t\`id\` integer PRIMARY KEY NOT NULL,
\t\`name\` text,
\t\`age\` integer
);\n`);
expect(sqlStatements![1]).toBe(
`INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`,
);
expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
expect(sqlStatements![3]).toBe(
`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('recreate table with added column not null and without default', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
age: integer('age'),
}),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
newColumn: text('new_column').notNull(),
}),
};
const seedStatements = [
`INSERT INTO \`users\` ("name", "age") VALUES ('drizzle', 12)`,
`INSERT INTO \`users\` ("name", "age") VALUES ('turso', 12)`,
];
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
false,
seedStatements,
);
expect(statements!.length).toBe(1);
expect(statements![0]).toStrictEqual({
columns: [
{
autoincrement: false,
name: 'id',
notNull: true,
generated: undefined,
primaryKey: true,
type: 'integer',
},
{
autoincrement: false,
name: 'name',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'text',
},
{
autoincrement: false,
name: 'age',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'integer',
},
{
autoincrement: false,
name: 'new_column',
notNull: true,
generated: undefined,
primaryKey: false,
type: 'text',
},
],
compositePKs: [],
referenceData: [],
tableName: 'users',
type: 'recreate_table',
uniqueConstraints: [],
checkConstraints: [],
});
expect(sqlStatements!.length).toBe(4);
expect(sqlStatements[0]).toBe('DELETE FROM \`users\`;');
expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` (
\t\`id\` integer PRIMARY KEY NOT NULL,
\t\`name\` text,
\t\`age\` integer,
\t\`new_column\` text NOT NULL
);\n`);
expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
expect(sqlStatements![3]).toBe(
`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(1);
expect(infoToPrint![0]).toBe(
`· You're about to add not-null ${
chalk.underline('new_column')
} column without default value to table, which contains 2 items`,
);
expect(shouldAskForApprove).toBe(true);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(1);
expect(tablesToTruncate![0]).toBe('users');
});
test('set not null with index', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
}, (table) => ({
someIndex: index('users_name_index').on(table.name),
})),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
}, (table) => ({
someIndex: index('users_name_index').on(table.name),
})),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements!.length).toBe(1);
expect(statements![0]).toStrictEqual({
columnAutoIncrement: false,
columnDefault: undefined,
columnName: 'name',
columnNotNull: true,
columnOnUpdate: undefined,
columnPk: false,
newDataType: 'text',
schema: '',
tableName: 'users',
type: 'alter_table_alter_column_set_notnull',
});
expect(statements.length).toBe(1);
expect(statements[0]).toStrictEqual({
type: 'alter_table_alter_column_set_notnull',
tableName: 'users',
columnName: 'name',
schema: '',
newDataType: 'text',
columnDefault: undefined,
columnOnUpdate: undefined,
columnNotNull: true,
columnAutoIncrement: false,
columnPk: false,
});
expect(sqlStatements.length).toBe(3);
expect(sqlStatements[0]).toBe(
`DROP INDEX "users_name_index";`,
);
expect(sqlStatements[1]).toBe(
`ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text NOT NULL;`,
);
expect(sqlStatements[2]).toBe(
`CREATE INDEX \`users_name_index\` ON \`users\` (\`name\`);`,
);
expect(columnsToRemove!.length).toBe(0), expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('drop not null with two indexes', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
age: int('age').notNull(),
}, (table) => ({
someUniqeIndex: uniqueIndex('users_name_unique').on(table.name),
someIndex: index('users_age_index').on(table.age),
})),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: true }),
name: text('name'),
age: int('age').notNull(),
}, (table) => ({
someUniqeIndex: uniqueIndex('users_name_unique').on(table.name),
someIndex: index('users_age_index').on(table.age),
})),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements.length).toBe(1);
expect(statements[0]).toStrictEqual({
type: 'alter_table_alter_column_drop_notnull',
tableName: 'users',
columnName: 'name',
schema: '',
newDataType: 'text',
columnDefault: undefined,
columnOnUpdate: undefined,
columnNotNull: false,
columnAutoIncrement: false,
columnPk: false,
});
expect(sqlStatements.length).toBe(5);
expect(sqlStatements[0]).toBe(
`DROP INDEX "users_name_unique";`,
);
expect(sqlStatements[1]).toBe(
`DROP INDEX "users_age_index";`,
);
expect(sqlStatements[2]).toBe(
`ALTER TABLE \`users\` ALTER COLUMN "name" TO "name" text;`,
);
expect(sqlStatements[3]).toBe(
`CREATE UNIQUE INDEX \`users_name_unique\` ON \`users\` (\`name\`);`,
);
expect(sqlStatements[4]).toBe(
`CREATE INDEX \`users_age_index\` ON \`users\` (\`age\`);`,
);
expect(columnsToRemove!.length).toBe(0), expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('add check constraint to table', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
}),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
}, (table) => ({
someCheck: check('some_check', sql`${table.age} > 21`),
})),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements!.length).toBe(1);
expect(statements![0]).toStrictEqual({
columns: [
{
autoincrement: false,
name: 'id',
notNull: true,
generated: undefined,
primaryKey: true,
type: 'integer',
},
{
autoincrement: false,
name: 'name',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'text',
},
{
autoincrement: false,
name: 'age',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'integer',
},
],
compositePKs: [],
referenceData: [],
tableName: 'users',
type: 'recreate_table',
uniqueConstraints: [],
checkConstraints: ['some_check;"users"."age" > 21'],
});
expect(sqlStatements!.length).toBe(4);
expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` (
\t\`id\` integer PRIMARY KEY NOT NULL,
\t\`name\` text,
\t\`age\` integer,
\tCONSTRAINT "some_check" CHECK("__new_users"."age" > 21)
);\n`);
expect(sqlStatements[1]).toBe(
'INSERT INTO `__new_users`("id", "name", "age") SELECT "id", "name", "age" FROM `users`;',
);
expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
expect(sqlStatements![3]).toBe(
`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('drop check constraint', async (t) => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
}, (table) => ({
someCheck: check('some_check', sql`${table.age} > 21`),
})),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
}),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements!.length).toBe(1);
expect(statements![0]).toStrictEqual({
columns: [
{
autoincrement: false,
name: 'id',
notNull: true,
generated: undefined,
primaryKey: true,
type: 'integer',
},
{
autoincrement: false,
name: 'name',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'text',
},
{
autoincrement: false,
name: 'age',
notNull: false,
generated: undefined,
primaryKey: false,
type: 'integer',
},
],
compositePKs: [],
referenceData: [],
tableName: 'users',
type: 'recreate_table',
uniqueConstraints: [],
checkConstraints: [],
});
expect(sqlStatements!.length).toBe(4);
expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` (
\t\`id\` integer PRIMARY KEY NOT NULL,
\t\`name\` text,
\t\`age\` integer
);\n`);
expect(sqlStatements[1]).toBe(
'INSERT INTO `__new_users`("id", "name", "age") SELECT "id", "name", "age" FROM `users`;',
);
expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
expect(sqlStatements![3]).toBe(
`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('db has checks. Push with same names', async () => {
const turso = createClient({
url: ':memory:',
});
const schema1 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
}, (table) => ({
someCheck: check('some_check', sql`${table.age} > 21`),
})),
};
const schema2 = {
users: sqliteTable('users', {
id: int('id').primaryKey({ autoIncrement: false }),
name: text('name'),
age: integer('age'),
}, (table) => ({
someCheck: check('some_check', sql`some new value`),
})),
};
const {
statements,
sqlStatements,
columnsToRemove,
infoToPrint,
shouldAskForApprove,
tablesToRemove,
tablesToTruncate,
} = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
false,
[],
);
expect(statements).toStrictEqual([]);
expect(sqlStatements).toStrictEqual([]);
expect(columnsToRemove!.length).toBe(0);
expect(infoToPrint!.length).toBe(0);
expect(shouldAskForApprove).toBe(false);
expect(tablesToRemove!.length).toBe(0);
expect(tablesToTruncate!.length).toBe(0);
});
test('create view', async () => {
const turso = createClient({
url: ':memory:',
});
const table = sqliteTable('test', {
id: int('id').primaryKey(),
});
const schema1 = {
test: table,
};
const schema2 = {
test: table,
view: sqliteView('view').as((qb) => qb.select().from(table)),
};
const { statements, sqlStatements } = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements).toStrictEqual([
{
definition: 'select "id" from "test"',
name: 'view',
type: 'sqlite_create_view',
},
]);
expect(sqlStatements).toStrictEqual([
`CREATE VIEW \`view\` AS select "id" from "test";`,
]);
});
test('drop view', async () => {
const turso = createClient({
url: ':memory:',
});
const table = sqliteTable('test', {
id: int('id').primaryKey(),
});
const schema1 = {
test: table,
view: sqliteView('view').as((qb) => qb.select().from(table)),
};
const schema2 = {
test: table,
};
const { statements, sqlStatements } = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements).toStrictEqual([
{
name: 'view',
type: 'drop_view',
},
]);
expect(sqlStatements).toStrictEqual([
'DROP VIEW \`view\`;',
]);
});
test('alter view ".as"', async () => {
const turso = createClient({
url: ':memory:',
});
const table = sqliteTable('test', {
id: int('id').primaryKey(),
});
const schema1 = {
test: table,
view: sqliteView('view').as((qb) => qb.select().from(table).where(sql`${table.id} = 1`)),
};
const schema2 = {
test: table,
view: sqliteView('view').as((qb) => qb.select().from(table)),
};
const { statements, sqlStatements } = await diffTestSchemasPushLibSQL(
turso,
schema1,
schema2,
[],
);
expect(statements.length).toBe(0);
expect(sqlStatements.length).toBe(0);
});