From 5d836f1db31f71cfca5993bb798e1515d013ee95 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Tue, 14 Jan 2025 19:01:38 +0200 Subject: [PATCH 1/4] feat: singlestore recreate Added: - filtering and mapping statements to recreate - sql generator for recreate - tests on migrate and push --- drizzle-kit/src/api.ts | 3 +- drizzle-kit/src/cli/commands/migrate.ts | 2 +- drizzle-kit/src/cli/commands/push.ts | 33 +- .../src/cli/commands/singlestorePushUtils.ts | 114 ++- drizzle-kit/src/jsonStatements.ts | 9 + drizzle-kit/src/snapshotsDiffer.ts | 10 +- drizzle-kit/src/sqlgenerator.ts | 73 +- drizzle-kit/src/statementCombiner.ts | 151 +++ drizzle-kit/src/utils.ts | 1 - .../tests/push/singlestore-push.test.ts | 630 ++++++++++++- drizzle-kit/tests/push/singlestore.test.ts | 3 - drizzle-kit/tests/schemaDiffer.ts | 59 +- drizzle-kit/tests/singlestore.test.ts | 398 ++++++++ .../singlestore-statements-combiner.test.ts | 875 ++++++++++++++++++ 14 files changed, 2306 insertions(+), 55 deletions(-) create mode 100644 drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts diff --git a/drizzle-kit/src/api.ts b/drizzle-kit/src/api.ts index 18107bd34..930d6f1e9 100644 --- a/drizzle-kit/src/api.ts +++ b/drizzle-kit/src/api.ts @@ -473,7 +473,8 @@ export const pushSingleStoreSchema = async ( const { shouldAskForApprove, statementsToExecute, infoToPrint } = await logSuggestionsAndReturn( db, statements, - validatedCur, + squashedCur, + squashedPrev, ); return { diff --git a/drizzle-kit/src/cli/commands/migrate.ts b/drizzle-kit/src/cli/commands/migrate.ts index 8c62a5edb..3718078cb 100644 --- a/drizzle-kit/src/cli/commands/migrate.ts +++ b/drizzle-kit/src/cli/commands/migrate.ts @@ -667,7 +667,7 @@ export const prepareSingleStorePush = async ( 'push', ); - return { sqlStatements, statements, validatedCur, validatedPrev }; + return { sqlStatements, statements, squashedPrev, squashedCur }; } catch (e) { console.error(e); process.exit(1); diff --git a/drizzle-kit/src/cli/commands/push.ts b/drizzle-kit/src/cli/commands/push.ts index 0c82fe026..eef0b1869 100644 --- a/drizzle-kit/src/cli/commands/push.ts +++ b/drizzle-kit/src/cli/commands/push.ts @@ -188,8 +188,8 @@ export const singlestorePush = async ( const filteredStatements = singleStoreFilterStatements( statements.statements ?? [], - statements.validatedCur, - statements.validatedPrev, + statements.squashedCur, + statements.squashedPrev, ); try { @@ -207,35 +207,17 @@ export const singlestorePush = async ( } = await singleStoreLogSuggestionsAndReturn( db, filteredStatements, - statements.validatedCur, + statements.squashedCur, + statements.squashedPrev, ); - const filteredSqlStatements = fromJson(filteredStatements, 'singlestore'); - - const uniqueSqlStatementsToExecute: string[] = []; - statementsToExecute.forEach((ss) => { - if (!uniqueSqlStatementsToExecute.includes(ss)) { - uniqueSqlStatementsToExecute.push(ss); - } - }); - const uniqueFilteredSqlStatements: string[] = []; - filteredSqlStatements.forEach((ss) => { - if (!uniqueFilteredSqlStatements.includes(ss)) { - uniqueFilteredSqlStatements.push(ss); - } - }); - if (verbose) { console.log(); console.log( withStyle.warning('You are about to execute current statements:'), ); console.log(); - console.log( - [...uniqueSqlStatementsToExecute, ...uniqueFilteredSqlStatements] - .map((s) => chalk.blue(s)) - .join('\n'), - ); + console.log(statementsToExecute.map((s) => chalk.blue(s)).join('\n')); console.log(); } @@ -289,13 +271,10 @@ export const singlestorePush = async ( } } - for (const dStmnt of uniqueSqlStatementsToExecute) { + for (const dStmnt of statementsToExecute) { await db.query(dStmnt); } - for (const statement of uniqueFilteredSqlStatements) { - await db.query(statement); - } if (filteredStatements.length > 0) { render(`[${chalk.green('✓')}] Changes applied`); } else { diff --git a/drizzle-kit/src/cli/commands/singlestorePushUtils.ts b/drizzle-kit/src/cli/commands/singlestorePushUtils.ts index 80fad9b2d..d3c32d789 100644 --- a/drizzle-kit/src/cli/commands/singlestorePushUtils.ts +++ b/drizzle-kit/src/cli/commands/singlestorePushUtils.ts @@ -1,16 +1,16 @@ import chalk from 'chalk'; import { render } from 'hanji'; -import { TypeOf } from 'zod'; +import { fromJson } from 'src/sqlgenerator'; import { JsonAlterColumnTypeStatement, JsonStatement } from '../../jsonStatements'; -import { singlestoreSchema, SingleStoreSquasher } from '../../serializer/singlestoreSchema'; -import type { DB } from '../../utils'; +import { Column, SingleStoreSchemaSquashed, SingleStoreSquasher } from '../../serializer/singlestoreSchema'; +import { type DB, findAddedAndRemoved } from '../../utils'; import { Select } from '../selector-ui'; import { withStyle } from '../validations/outputs'; export const filterStatements = ( statements: JsonStatement[], - currentSchema: TypeOf, - prevSchema: TypeOf, + currentSchema: SingleStoreSchemaSquashed, + prevSchema: SingleStoreSchemaSquashed, ) => { return statements.filter((statement) => { if (statement.type === 'alter_table_alter_column_set_type') { @@ -104,10 +104,30 @@ export const filterStatements = ( }); }; +export function findColumnTypeAlternations( + columns1: Record, + columns2: Record, +): string[] { + const changes: string[] = []; + + for (const key in columns1) { + if (columns1.hasOwnProperty(key) && columns2.hasOwnProperty(key)) { + const col1 = columns1[key]; + const col2 = columns2[key]; + if (col1.type !== col2.type) { + changes.push(col2.name); + } + } + } + + return changes; +} + export const logSuggestionsAndReturn = async ( db: DB, statements: JsonStatement[], - json2: TypeOf, + json2: SingleStoreSchemaSquashed, + json1: SingleStoreSchemaSquashed, ) => { let shouldAskForApprove = false; const statementsToExecute: string[] = []; @@ -337,6 +357,88 @@ export const logSuggestionsAndReturn = async ( shouldAskForApprove = true; } } + } else if (statement.type === 'singlestore_recreate_table') { + const tableName = statement.tableName; + + const prevColumns = json1.tables[tableName].columns; + const currentColumns = json2.tables[tableName].columns; + const { removedColumns, addedColumns } = findAddedAndRemoved( + Object.keys(prevColumns), + Object.keys(currentColumns), + ); + + if (removedColumns.length) { + for (const removedColumn of removedColumns) { + const res = await db.query<{ count: string }>( + `select count(\`${tableName}\`.\`${removedColumn}\`) as count from \`${tableName}\``, + ); + + const count = Number(res[0].count); + if (count > 0) { + infoToPrint.push( + `· You're about to delete ${ + chalk.underline( + removedColumn, + ) + } column in ${tableName} table with ${count} items`, + ); + columnsToRemove.push(removedColumn); + shouldAskForApprove = true; + } + } + } + + if (addedColumns.length) { + for (const addedColumn of addedColumns) { + const [res] = await db.query<{ count: string }>( + `select count(*) as count from \`${tableName}\``, + ); + + const columnConf = json2.tables[tableName].columns[addedColumn]; + + const count = Number(res.count); + if (count > 0 && columnConf.notNull && !columnConf.default) { + infoToPrint.push( + `· You're about to add not-null ${ + chalk.underline( + addedColumn, + ) + } column without default value to table, which contains ${count} items`, + ); + shouldAskForApprove = true; + tablesToTruncate.push(tableName); + + statementsToExecute.push(`TRUNCATE TABLE \`${tableName}\`;`); + } + } + } + + const columnWithChangedType = findColumnTypeAlternations(prevColumns, currentColumns); + for (const column of columnWithChangedType) { + const [res] = await db.query<{ count: string }>( + `select count(*) as count from \`${tableName}\` WHERE \`${tableName}\`.\`${column}\` IS NOT NULL;`, + ); + + const count = Number(res.count); + if (count > 0) { + infoToPrint.push( + `· You're about recreate ${chalk.underline(tableName)} table with data type changing for ${ + chalk.underline( + column, + ) + } column, which contains ${count} items`, + ); + shouldAskForApprove = true; + tablesToTruncate.push(tableName); + + statementsToExecute.push(`TRUNCATE TABLE \`${tableName}\`;`); + } + } + } + + const stmnt = fromJson([statement], 'singlestore', 'push'); + if (typeof stmnt !== 'undefined') { + statementsToExecute.push(...stmnt); } } diff --git a/drizzle-kit/src/jsonStatements.ts b/drizzle-kit/src/jsonStatements.ts index f64020f5a..b70d01b99 100644 --- a/drizzle-kit/src/jsonStatements.ts +++ b/drizzle-kit/src/jsonStatements.ts @@ -73,6 +73,14 @@ export interface JsonRecreateTableStatement { checkConstraints: string[]; } +export interface JsonRecreateSingleStoreTableStatement { + type: 'singlestore_recreate_table'; + tableName: string; + columns: Column[]; + compositePKs: string[]; + uniqueConstraints?: string[]; +} + export interface JsonDropTableStatement { type: 'drop_table'; tableName: string; @@ -794,6 +802,7 @@ export type JsonAlterColumnStatement = | JsonAlterColumnDropIdentityStatement; export type JsonStatement = + | JsonRecreateSingleStoreTableStatement | JsonRecreateTableStatement | JsonAlterColumnStatement | JsonCreateTableStatement diff --git a/drizzle-kit/src/snapshotsDiffer.ts b/drizzle-kit/src/snapshotsDiffer.ts index 2db4ad02c..0fd803288 100644 --- a/drizzle-kit/src/snapshotsDiffer.ts +++ b/drizzle-kit/src/snapshotsDiffer.ts @@ -141,7 +141,7 @@ import { } from './serializer/pgSchema'; import { SingleStoreSchema, SingleStoreSchemaSquashed, SingleStoreSquasher } from './serializer/singlestoreSchema'; import { SQLiteSchema, SQLiteSchemaSquashed, SQLiteSquasher, View as SqliteView } from './serializer/sqliteSchema'; -import { libSQLCombineStatements, sqliteCombineStatements } from './statementCombiner'; +import { libSQLCombineStatements, singleStoreCombineStatements, sqliteCombineStatements } from './statementCombiner'; import { copy, prepareMigrationMeta } from './utils'; const makeChanged = (schema: T) => { @@ -2875,9 +2875,8 @@ export const applySingleStoreSnapshotsDiff = async ( return [viewKey, viewValue]; }, ); - */ - const diffResult = applyJsonDiff(tablesPatchedSnap1, json2); // replace tablesPatchedSnap1 with viewsPatchedSnap1 + const diffResult = applyJsonDiff(columnsPatchedSnap1, json2); // replace columnsPatchedSnap1 with viewsPatchedSnap1 const typedResult: DiffResultSingleStore = diffResultSchemeSingleStore.parse(diffResult); @@ -3177,7 +3176,8 @@ export const applySingleStoreSnapshotsDiff = async ( jsonStatements.push(...jsonAlteredUniqueConstraints); - const sqlStatements = fromJson(jsonStatements, 'singlestore'); + const combinedJsonStatements = singleStoreCombineStatements(jsonStatements, json2); + const sqlStatements = fromJson(combinedJsonStatements, 'singlestore'); const uniqueSqlStatements: string[] = []; sqlStatements.forEach((ss) => { @@ -3193,7 +3193,7 @@ export const applySingleStoreSnapshotsDiff = async ( const _meta = prepareMigrationMeta([], rTables, rColumns); return { - statements: jsonStatements, + statements: combinedJsonStatements, sqlStatements: uniqueSqlStatements, _meta, }; diff --git a/drizzle-kit/src/sqlgenerator.ts b/drizzle-kit/src/sqlgenerator.ts index 26adaf531..703de2a7a 100644 --- a/drizzle-kit/src/sqlgenerator.ts +++ b/drizzle-kit/src/sqlgenerator.ts @@ -69,6 +69,7 @@ import { JsonMoveEnumStatement, JsonMoveSequenceStatement, JsonPgCreateIndexStatement, + JsonRecreateSingleStoreTableStatement, JsonRecreateTableStatement, JsonRenameColumnStatement, JsonRenameEnumStatement, @@ -574,7 +575,7 @@ class MySqlCreateTableConvertor extends Convertor { return statement; } } -class SingleStoreCreateTableConvertor extends Convertor { +export class SingleStoreCreateTableConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'create_table' && dialect === 'singlestore'; } @@ -618,7 +619,7 @@ class SingleStoreCreateTableConvertor extends Convertor { if (typeof compositePKs !== 'undefined' && compositePKs.length > 0) { statement += ',\n'; const compositePK = SingleStoreSquasher.unsquashPK(compositePKs[0]); - statement += `\tCONSTRAINT \`${st.compositePkName}\` PRIMARY KEY(\`${compositePK.columns.join(`\`,\``)}\`)`; + statement += `\tCONSTRAINT \`${compositePK.name}\` PRIMARY KEY(\`${compositePK.columns.join(`\`,\``)}\`)`; } if ( @@ -1531,7 +1532,7 @@ class MySQLDropTableConvertor extends Convertor { } } -class SingleStoreDropTableConvertor extends Convertor { +export class SingleStoreDropTableConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'drop_table' && dialect === 'singlestore'; } @@ -1590,14 +1591,14 @@ class MySqlRenameTableConvertor extends Convertor { } } -class SingleStoreRenameTableConvertor extends Convertor { +export class SingleStoreRenameTableConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'rename_table' && dialect === 'singlestore'; } convert(statement: JsonRenameTableStatement) { const { tableNameFrom, tableNameTo } = statement; - return `RENAME TABLE \`${tableNameFrom}\` TO \`${tableNameTo}\`;`; + return `ALTER TABLE \`${tableNameFrom}\` RENAME TO \`${tableNameTo}\`;`; } } @@ -1641,7 +1642,7 @@ class SingleStoreAlterTableRenameColumnConvertor extends Convertor { convert(statement: JsonRenameColumnStatement) { const { tableName, oldColumnName, newColumnName } = statement; - return `ALTER TABLE \`${tableName}\` RENAME COLUMN \`${oldColumnName}\` TO \`${newColumnName}\`;`; + return `ALTER TABLE \`${tableName}\` CHANGE \`${oldColumnName}\` \`${newColumnName}\`;`; } } @@ -3499,7 +3500,7 @@ class CreateMySqlIndexConvertor extends Convertor { } } -class CreateSingleStoreIndexConvertor extends Convertor { +export class CreateSingleStoreIndexConvertor extends Convertor { can(statement: JsonStatement, dialect: Dialect): boolean { return statement.type === 'create_index' && dialect === 'singlestore'; } @@ -3812,10 +3813,68 @@ class LibSQLRecreateTableConvertor extends Convertor { } } +class SingleStoreRecreateTableConvertor extends Convertor { + can(statement: JsonStatement, dialect: Dialect): boolean { + return ( + statement.type === 'singlestore_recreate_table' + && dialect === 'singlestore' + ); + } + + convert(statement: JsonRecreateSingleStoreTableStatement): string[] { + const { tableName, columns, compositePKs, uniqueConstraints } = statement; + + const columnNames = columns.map((it) => `\`${it.name}\``).join(', '); + const newTableName = `__new_${tableName}`; + + const sqlStatements: string[] = []; + + // create new table + sqlStatements.push( + new SingleStoreCreateTableConvertor().convert({ + type: 'create_table', + tableName: newTableName, + columns, + compositePKs, + uniqueConstraints, + schema: '', + }), + ); + + // migrate data + sqlStatements.push( + `INSERT INTO \`${newTableName}\`(${columnNames}) SELECT ${columnNames} FROM \`${tableName}\`;`, + ); + + // drop table + sqlStatements.push( + new SingleStoreDropTableConvertor().convert({ + type: 'drop_table', + tableName: tableName, + schema: '', + }), + ); + + // rename table + sqlStatements.push( + new SingleStoreRenameTableConvertor().convert({ + fromSchema: '', + tableNameFrom: newTableName, + tableNameTo: tableName, + toSchema: '', + type: 'rename_table', + }), + ); + + return sqlStatements; + } +} + const convertors: Convertor[] = []; convertors.push(new PgCreateTableConvertor()); convertors.push(new MySqlCreateTableConvertor()); convertors.push(new SingleStoreCreateTableConvertor()); +convertors.push(new SingleStoreRecreateTableConvertor()); convertors.push(new SQLiteCreateTableConvertor()); convertors.push(new SQLiteRecreateTableConvertor()); convertors.push(new LibSQLRecreateTableConvertor()); diff --git a/drizzle-kit/src/statementCombiner.ts b/drizzle-kit/src/statementCombiner.ts index f3ca9789c..7d84a2aa8 100644 --- a/drizzle-kit/src/statementCombiner.ts +++ b/drizzle-kit/src/statementCombiner.ts @@ -4,6 +4,7 @@ import { JsonStatement, prepareCreateIndexesJson, } from './jsonStatements'; +import { SingleStoreSchemaSquashed } from './serializer/singlestoreSchema'; import { SQLiteSchemaSquashed, SQLiteSquasher } from './serializer/sqliteSchema'; export const prepareLibSQLRecreateTable = ( @@ -444,3 +445,153 @@ export const sqliteCombineStatements = ( return [...renamedTables, ...renamedColumns, ...rest]; }; + +export const prepareSingleStoreRecreateTable = ( + table: SingleStoreSchemaSquashed['tables'][keyof SingleStoreSchemaSquashed['tables']], +): JsonStatement[] => { + const { name, columns, uniqueConstraints, indexes, compositePrimaryKeys } = table; + + const composites: string[] = Object.values(compositePrimaryKeys); + + const statements: JsonStatement[] = [ + { + type: 'singlestore_recreate_table', + tableName: name, + columns: Object.values(columns), + compositePKs: composites, + uniqueConstraints: Object.values(uniqueConstraints), + }, + ]; + + if (Object.keys(indexes).length) { + statements.push(...prepareCreateIndexesJson(name, '', indexes)); + } + return statements; +}; + +export const singleStoreCombineStatements = ( + statements: JsonStatement[], + json2: SingleStoreSchemaSquashed, +) => { + const newStatements: Record = {}; + + for (const statement of statements) { + if ( + statement.type === 'alter_table_alter_column_set_type' + || statement.type === 'alter_table_alter_column_set_notnull' + || statement.type === 'alter_table_alter_column_drop_notnull' + || statement.type === 'alter_table_alter_column_drop_autoincrement' + || statement.type === 'alter_table_alter_column_set_autoincrement' + || statement.type === 'alter_table_alter_column_drop_pk' + || statement.type === 'alter_table_alter_column_set_pk' + || statement.type === 'create_composite_pk' + || statement.type === 'alter_composite_pk' + || statement.type === 'delete_composite_pk' + ) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareSingleStoreRecreateTable(json2.tables[tableName]); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => + type === 'rename_table' || type === 'alter_table_rename_column' + ); + const preparedStatements = prepareSingleStoreRecreateTable(json2.tables[tableName]); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + if ( + (statement.type === 'alter_table_alter_column_drop_default' + || statement.type === 'alter_table_alter_column_set_default') && statement.columnNotNull + ) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareSingleStoreRecreateTable(json2.tables[tableName]); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareSingleStoreRecreateTable(json2.tables[tableName]); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + if (statement.type === 'alter_table_add_column' && statement.column.primaryKey) { + const tableName = statement.tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = prepareSingleStoreRecreateTable(json2.tables[tableName]); + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'recreate_table')) { + const wasRename = statementsForTable.some(({ type }) => type === 'rename_table'); + const preparedStatements = prepareSingleStoreRecreateTable(json2.tables[tableName]); + + if (wasRename) { + newStatements[tableName].push(...preparedStatements); + } else { + newStatements[tableName] = preparedStatements; + } + + continue; + } + + continue; + } + + const tableName = statement.type === 'rename_table' + ? statement.tableNameTo + : (statement as { tableName: string }).tableName; + + const statementsForTable = newStatements[tableName]; + + if (!statementsForTable) { + newStatements[tableName] = [statement]; + continue; + } + + if (!statementsForTable.some(({ type }) => type === 'singlestore_recreate_table')) { + newStatements[tableName].push(statement); + } + } + + const combinedStatements = Object.values(newStatements).flat(); + + const renamedTables = combinedStatements.filter((it) => it.type === 'rename_table'); + const renamedColumns = combinedStatements.filter((it) => it.type === 'alter_table_rename_column'); + + const rest = combinedStatements.filter((it) => it.type !== 'rename_table' && it.type !== 'alter_table_rename_column'); + + return [...renamedTables, ...renamedColumns, ...rest]; +}; diff --git a/drizzle-kit/src/utils.ts b/drizzle-kit/src/utils.ts index 2638ca4ef..93eb044e0 100644 --- a/drizzle-kit/src/utils.ts +++ b/drizzle-kit/src/utils.ts @@ -4,7 +4,6 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from import { join } from 'path'; import { parse } from 'url'; import type { NamedWithSchema } from './cli/commands/migrate'; -import { CasingType } from './cli/validations/common'; import { info } from './cli/views'; import { assertUnreachable, snapshotVersion } from './global'; import type { Dialect } from './schemaValidator'; diff --git a/drizzle-kit/tests/push/singlestore-push.test.ts b/drizzle-kit/tests/push/singlestore-push.test.ts index 4ad3c6c0e..0bafd5956 100644 --- a/drizzle-kit/tests/push/singlestore-push.test.ts +++ b/drizzle-kit/tests/push/singlestore-push.test.ts @@ -1,5 +1,6 @@ +import chalk from 'chalk'; import Docker from 'dockerode'; -import { int, singlestoreTable } from 'drizzle-orm/singlestore-core'; +import { getTableConfig, index, int, singlestoreTable, text } from 'drizzle-orm/singlestore-core'; import fs from 'fs'; import getPort from 'get-port'; import { Connection, createConnection } from 'mysql2/promise'; @@ -264,3 +265,630 @@ VIEW \`view\` AS (select \`id\` from \`test\`);`, await client.query(`DROP TABLE \`test\`;`); }); */ + +test('added column not null and without default to table with data', async (t) => { + const schema1 = { + companies: singlestoreTable('companies', { + id: int('id'), + name: text('name'), + }), + }; + + const schema2 = { + companies: singlestoreTable('companies', { + id: int('id'), + name: text('name'), + age: int('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 diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + { + after: seedStatements, + }, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_add_column', + tableName: 'companies', + column: { + name: 'age', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + schema: '', + }); + + expect(sqlStatements.length).toBe(2); + expect(sqlStatements[0]).toBe(`truncate table companies;`); + expect(sqlStatements[1]).toBe( + `ALTER TABLE \`companies\` ADD \`age\` int 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'); + + await client.query(`DROP TABLE \`companies\`;`); +}); + +test('added column not null and without default to table without data', async (t) => { + const schema1 = { + companies: singlestoreTable('companies', { + id: int('id').primaryKey(), + name: text('name').notNull(), + }), + }; + + const schema2 = { + companies: singlestoreTable('companies', { + id: int('id').primaryKey(), + name: text('name').notNull(), + age: int('age').notNull(), + }), + }; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + ); + + expect(statements.length).toBe(1); + expect(statements[0]).toStrictEqual({ + type: 'alter_table_add_column', + tableName: 'companies', + column: { + name: 'age', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + schema: '', + }); + + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + `ALTER TABLE \`companies\` ADD \`age\` int 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); + + await client.query(`DROP TABLE \`companies\`;`); +}); + +test('drop not null, add not null', async (t) => { + const schema1 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name').notNull(), + }), + posts: singlestoreTable( + 'posts', + { + id: int('id').primaryKey(), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const schema2 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name'), + }), + posts: singlestoreTable( + 'posts', + { + id: int('id').primaryKey(), + name: text('name').notNull(), + userId: int('user_id'), + }, + ), + }; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + generated: undefined, + name: 'id', + notNull: true, + onUpdate: undefined, + primaryKey: false, + type: 'int', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: true, + onUpdate: undefined, + primaryKey: false, + type: 'text', + }, + { + autoincrement: false, + generated: undefined, + name: 'user_id', + notNull: false, + onUpdate: undefined, + primaryKey: false, + type: 'int', + }, + ], + compositePKs: [ + 'posts_id;id', + ], + tableName: 'posts', + type: 'singlestore_recreate_table', + uniqueConstraints: [], + }); + expect(statements![1]).toStrictEqual({ + columns: [ + { + autoincrement: false, + generated: undefined, + name: 'id', + notNull: true, + onUpdate: undefined, + primaryKey: false, + type: 'int', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + onUpdate: undefined, + primaryKey: false, + type: 'text', + }, + ], + compositePKs: [ + 'users_id;id', + ], + tableName: 'users', + type: 'singlestore_recreate_table', + uniqueConstraints: [], + }); + expect(sqlStatements!.length).toBe(8); + expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_posts\` ( +\t\`id\` int NOT NULL, +\t\`name\` text NOT NULL, +\t\`user_id\` int, +\tCONSTRAINT \`posts_id\` PRIMARY KEY(\`id\`) +);\n`); + expect(sqlStatements![1]).toBe( + `INSERT INTO \`__new_posts\`(\`id\`, \`name\`, \`user_id\`) SELECT \`id\`, \`name\`, \`user_id\` FROM \`posts\`;`, + ); + expect(sqlStatements![2]).toBe(`DROP TABLE \`posts\`;`); + expect(sqlStatements![3]).toBe(`ALTER TABLE \`__new_posts\` RENAME TO \`posts\`;`); + expect(sqlStatements![4]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` int NOT NULL, +\t\`name\` text, +\tCONSTRAINT \`users_id\` PRIMARY KEY(\`id\`) +);\n`); + expect(sqlStatements![5]).toBe( + `INSERT INTO \`__new_users\`(\`id\`, \`name\`) SELECT \`id\`, \`name\` FROM \`users\`;`, + ); + expect(sqlStatements![6]).toBe( + `DROP TABLE \`users\`;`, + ); + expect(sqlStatements![7]).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); + + await client.query(`DROP TABLE \`users\`;`); + await client.query(`DROP TABLE \`posts\`;`); +}); + +test('drop table with data', async (t) => { + const schema1 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name').notNull(), + }), + posts: singlestoreTable( + 'posts', + { + id: int('id').primaryKey(), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const schema2 = { + posts: singlestoreTable( + 'posts', + { + id: int('id').primaryKey(), + name: text('name'), + userId: int('user_id'), + }, + ), + }; + + const seedStatements = [ + `INSERT INTO \`users\` (\`id\`, \`name\`) VALUES (1, 'drizzle')`, + ]; + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + { after: 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); + + await client.query(`DROP TABLE \`users\`;`); + await client.query(`DROP TABLE \`posts\`;`); +}); + +test('change data type. db has indexes. table does not have values', async (t) => { + const schema1 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: int('name').notNull(), + }, (table) => [index('index').on(table.name)]), + }; + + const schema2 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name').notNull(), + }, (table) => [index('index').on(table.name)]), + }; + + const seedStatements = [`INSERT INTO users VALUES (1, 12)`]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + generated: undefined, + name: 'id', + notNull: true, + onUpdate: undefined, + primaryKey: false, + type: 'int', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: true, + onUpdate: undefined, + primaryKey: false, + type: 'text', + }, + ], + compositePKs: [ + 'users_id;id', + ], + tableName: 'users', + type: 'singlestore_recreate_table', + uniqueConstraints: [], + }); + expect(statements![1]).toStrictEqual({ + data: 'index;name;false;;;', + internal: undefined, + schema: '', + tableName: 'users', + type: 'create_index', + }); + + expect(sqlStatements!.length).toBe(5); + expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` int NOT NULL, +\t\`name\` text NOT NULL, +\tCONSTRAINT \`users_id\` PRIMARY KEY(\`id\`) +);\n`); + expect(sqlStatements![1]).toBe( + `INSERT INTO \`__new_users\`(\`id\`, \`name\`) SELECT \`id\`, \`name\` FROM \`users\`;`, + ); + expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![3]).toBe(`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`); + expect(sqlStatements![4]).toBe(`CREATE INDEX \`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); + + await client.query(`DROP TABLE \`users\`;`); +}); + +test('change data type. db has indexes. table has values', async (t) => { + const schema1 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: int('name'), + }, (table) => [index('index').on(table.name)]), + }; + + const schema2 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name'), + }, (table) => [index('index').on(table.name)]), + }; + + const seedStatements = [`INSERT INTO users VALUES (1, 12);`, `INSERT INTO users (id) VALUES (2);`]; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + { after: seedStatements }, + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + columns: [ + { + autoincrement: false, + generated: undefined, + name: 'id', + notNull: true, + onUpdate: undefined, + primaryKey: false, + type: 'int', + }, + { + autoincrement: false, + generated: undefined, + name: 'name', + notNull: false, + onUpdate: undefined, + primaryKey: false, + type: 'text', + }, + ], + compositePKs: [ + 'users_id;id', + ], + tableName: 'users', + type: 'singlestore_recreate_table', + uniqueConstraints: [], + }); + expect(statements![1]).toStrictEqual({ + data: 'index;name;false;;;', + internal: undefined, + schema: '', + tableName: 'users', + type: 'create_index', + }); + + expect(sqlStatements!.length).toBe(6); + expect(sqlStatements![0]).toBe(`TRUNCATE TABLE \`users\`;`); + expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` ( +\t\`id\` int NOT NULL, +\t\`name\` text, +\tCONSTRAINT \`users_id\` PRIMARY KEY(\`id\`) +);\n`); + expect(sqlStatements![2]).toBe( + `INSERT INTO \`__new_users\`(\`id\`, \`name\`) SELECT \`id\`, \`name\` FROM \`users\`;`, + ); + expect(sqlStatements![3]).toBe(`DROP TABLE \`users\`;`); + expect(sqlStatements![4]).toBe(`ALTER TABLE \`__new_users\` RENAME TO \`users\`;`); + expect(sqlStatements![5]).toBe(`CREATE INDEX \`index\` ON \`users\` (\`name\`);`); + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(1); + expect(infoToPrint![0]).toBe( + `· You're about recreate ${chalk.underline('users')} table with data type changing for ${ + chalk.underline('name') + } column, which contains 1 items`, + ); + expect(shouldAskForApprove).toBe(true); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(1); + expect(tablesToTruncate![0]).toBe(`users`); + + await client.query(`DROP TABLE \`users\`;`); +}); + +test('add column. add default to column without not null', async (t) => { + const schema1 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name'), + }), + }; + + const schema2 = { + users: singlestoreTable('users', { + id: int('id').primaryKey(), + name: text('name').default('drizzle'), + age: int('age'), + }), + }; + + const { + statements, + sqlStatements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await diffTestSchemasPushSingleStore( + client, + schema1, + schema2, + [], + 'drizzle', + false, + undefined, + ); + + expect(statements!.length).toBe(2); + expect(statements![0]).toStrictEqual({ + columnAutoIncrement: false, + columnName: 'name', + columnNotNull: false, + columnOnUpdate: undefined, + columnPk: false, + newDataType: 'text', + newDefaultValue: "'drizzle'", + schema: '', + tableName: 'users', + type: 'alter_table_alter_column_set_default', + }); + expect(statements![1]).toStrictEqual({ + type: 'alter_table_add_column', + tableName: 'users', + schema: '', + column: { + notNull: false, + primaryKey: false, + autoincrement: false, + name: 'age', + type: 'int', + }, + }); + expect(sqlStatements!.length).toBe(2); + expect(sqlStatements![0]).toBe(`ALTER TABLE \`users\` MODIFY COLUMN \`name\` text DEFAULT 'drizzle';`); + expect(sqlStatements![1]).toBe(`ALTER TABLE \`users\` ADD \`age\` int;`); + expect(columnsToRemove!.length).toBe(0); + expect(infoToPrint!.length).toBe(0); + expect(shouldAskForApprove).toBe(false); + expect(tablesToRemove!.length).toBe(0); + expect(tablesToTruncate!.length).toBe(0); + + await client.query(`DROP TABLE \`users\`;`); +}); diff --git a/drizzle-kit/tests/push/singlestore.test.ts b/drizzle-kit/tests/push/singlestore.test.ts index 82c72063c..f84e34ce5 100644 --- a/drizzle-kit/tests/push/singlestore.test.ts +++ b/drizzle-kit/tests/push/singlestore.test.ts @@ -5,15 +5,12 @@ import { binary, char, date, - datetime, decimal, double, float, int, - json, mediumint, primaryKey, - serial, singlestoreEnum, singlestoreTable, smallint, diff --git a/drizzle-kit/tests/schemaDiffer.ts b/drizzle-kit/tests/schemaDiffer.ts index 9c7f212aa..256288c24 100644 --- a/drizzle-kit/tests/schemaDiffer.ts +++ b/drizzle-kit/tests/schemaDiffer.ts @@ -38,6 +38,7 @@ import { viewsResolver, } from 'src/cli/commands/migrate'; import { pgSuggestions } from 'src/cli/commands/pgPushUtils'; +import { logSuggestionsAndReturn as singleStoreLogSuggestionsAndReturn } from 'src/cli/commands/singlestorePushUtils'; import { logSuggestionsAndReturn } from 'src/cli/commands/sqlitePushUtils'; import { Entities } from 'src/cli/validations/cli'; import { CasingType } from 'src/cli/validations/common'; @@ -1624,11 +1625,35 @@ export const diffTestSchemasPushSingleStore = async ( schema: string, cli: boolean = false, casing?: CasingType | undefined, + sqlStatementsToRun: { + before?: string[]; + after?: string[]; + runApply?: boolean; + } = { + before: [], + after: [], + runApply: true, + }, ) => { - const { sqlStatements } = await applySingleStoreDiffs(left, casing); - for (const st of sqlStatements) { + const shouldRunApply = sqlStatementsToRun.runApply === undefined + ? true + : sqlStatementsToRun.runApply; + + for (const st of sqlStatementsToRun.before ?? []) { await client.query(st); } + + if (shouldRunApply) { + const res = await applySingleStoreDiffs(left, casing); + for (const st of res.sqlStatements) { + await client.query(st); + } + } + + for (const st of sqlStatementsToRun.after ?? []) { + await client.query(st); + } + // do introspect into PgSchemaInternal const introspectedSchema = await fromSingleStoreDatabase( { @@ -1688,7 +1713,35 @@ export const diffTestSchemasPushSingleStore = async ( validatedCur, 'push', ); - return { sqlStatements, statements }; + + const { + statementsToExecute, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + } = await singleStoreLogSuggestionsAndReturn( + { + query: async (sql: string, params?: any[]) => { + const res = await client.execute(sql, params); + return res[0] as T[]; + }, + }, + statements, + sn1, + sn2, + ); + + return { + sqlStatements: statementsToExecute, + statements, + columnsToRemove, + infoToPrint, + shouldAskForApprove, + tablesToRemove, + tablesToTruncate, + }; } else { const { sqlStatements, statements } = await applySingleStoreSnapshotsDiff( sn1, diff --git a/drizzle-kit/tests/singlestore.test.ts b/drizzle-kit/tests/singlestore.test.ts index 3bdccab81..50f862ce0 100644 --- a/drizzle-kit/tests/singlestore.test.ts +++ b/drizzle-kit/tests/singlestore.test.ts @@ -1,6 +1,7 @@ import { sql } from 'drizzle-orm'; import { index, + int, json, primaryKey, serial, @@ -578,3 +579,400 @@ test('add table with indexes', async () => { 'CREATE INDEX `indexColExpr` ON `users` ((lower(`email`)),`email`);', ]); }); + +test('rename table', async () => { + const from = { + table: singlestoreTable('table', { + json: json('json').default([]), + }), + }; + + const to = { + table1: singlestoreTable('table1', { + json1: json('json').default([]), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, [`public.table->public.table1`]); + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE `table` RENAME TO `table1`;', + ); +}); + +test('rename column', async () => { + const from = { + users: singlestoreTable('table', { + json: json('json').default([]), + }), + }; + + const to = { + users: singlestoreTable('table', { + json1: json('json1').default([]), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, [`public.table.json->public.table.json1`]); + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE `table` CHANGE `json` `json1`;', + ); +}); + +test('change data type', async () => { + const from = { + table: singlestoreTable('table', { + id: int(), + age: text(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('drop not null', async () => { + const from = { + table: singlestoreTable('table', { + id: int().notNull(), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('set not null', async () => { + const from = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int().notNull(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int NOT NULL, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('set default with not null column', async () => { + const from = { + table: singlestoreTable('table', { + id: int().notNull(), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int().notNull().default(1), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int NOT NULL DEFAULT 1, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('drop default with not null column', async () => { + const from = { + table: singlestoreTable('table', { + id: int().notNull().default(1), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int().notNull(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int NOT NULL, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('set default', async () => { + const from = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int().default(1), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE `table` MODIFY COLUMN `id` int DEFAULT 1;', + ); +}); + +test('drop default', async () => { + const from = { + table: singlestoreTable('table', { + id: int().default(1), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(1); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE `table` MODIFY COLUMN `id` int;', + ); +}); + +test('set pk', async () => { + const from = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int().primaryKey(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int NOT NULL, +\t\`age\` int, +\tCONSTRAINT \`table_id\` PRIMARY KEY(\`id\`) +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('drop pk', async () => { + const from = { + table: singlestoreTable('table', { + id: int().primaryKey(), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id: int(), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, []); + expect(sqlStatements.length).toBe(4); + expect(sqlStatements[0]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id\` int, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[1]).toBe( + 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + ); + expect(sqlStatements[2]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[3]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('set not null + rename column on table with indexes', async () => { + const from = { + table: singlestoreTable('table', { + id: int('id').default(1), + age: int(), + }), + }; + + const to = { + table: singlestoreTable('table', { + id3: int('id3').notNull().default(1), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, [`public.table.id->public.table.id3`]); + expect(sqlStatements.length).toBe(5); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE \`table\` CHANGE `id` `id3`;', + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`__new_table\` ( +\t\`id3\` int NOT NULL DEFAULT 1, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[2]).toBe( + 'INSERT INTO `__new_table`("id3", "age") SELECT "id3", "age" FROM `table`;', + ); + expect(sqlStatements[3]).toBe( + 'DROP TABLE `table`;', + ); + expect(sqlStatements[4]).toBe( + 'ALTER TABLE `__new_table` RENAME TO `table`;', + ); +}); + +test('set not null + rename table on table with indexes', async () => { + const from = { + table: singlestoreTable('table', { + id: int('id').default(1), + age: int(), + }), + }; + + const to = { + table1: singlestoreTable('table1', { + id: int('id').notNull().default(1), + age: int(), + }), + }; + + const { sqlStatements } = await diffTestSchemasSingleStore(from, to, [`public.table->public.table1`]); + expect(sqlStatements.length).toBe(5); + expect(sqlStatements[0]).toBe( + 'ALTER TABLE `table` RENAME TO `table1`;', + ); + expect(sqlStatements[1]).toBe( + `CREATE TABLE \`__new_table1\` ( +\t\`id\` int NOT NULL DEFAULT 1, +\t\`age\` int +);\n`, + ); + expect(sqlStatements[2]).toBe( + 'INSERT INTO `__new_table1`("id", "age") SELECT "id", "age" FROM `table1`;', + ); + expect(sqlStatements[3]).toBe( + 'DROP TABLE `table1`;', + ); + expect(sqlStatements[4]).toBe( + 'ALTER TABLE `__new_table1` RENAME TO `table1`;', + ); +}); diff --git a/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts new file mode 100644 index 000000000..191cfa23c --- /dev/null +++ b/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts @@ -0,0 +1,875 @@ +import { JsonStatement } from 'src/jsonStatements'; +import { SingleStoreSchemaSquashed } from 'src/serializer/singlestoreSchema'; +import { singleStoreCombineStatements } from 'src/statementCombiner'; +import { expect, test } from 'vitest'; + +test(`change column data type`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, + { + type: 'alter_table_alter_column_set_type', + tableName: 'user', + columnName: 'lastName123', + newDataType: 'int', + oldDataType: 'text', + schema: '', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + columnIsUnique: false, + } as unknown as JsonStatement, + ]; + const json1: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName123', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + + const newJsonStatements = [ + { + type: 'singlestore_recreate_table', + tableName: 'user', + columns: [ + { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + { + name: 'lastName123', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'test', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`set autoincrement`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_set_autoincrement', + tableName: 'users', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: true, + columnPk: false, + } as unknown as JsonStatement, + ]; + + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + users: { + name: 'users', + columns: { + new_id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: true, + }, + name: { + name: 'name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + email: { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const newJsonStatements = [ + { + type: 'singlestore_recreate_table', + tableName: 'users', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: true, + }, + { + name: 'name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`drop autoincrement`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_drop_autoincrement', + tableName: 'users', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: true, + columnPk: false, + } as unknown as JsonStatement, + ]; + + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + users: { + name: 'users', + columns: { + new_id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + name: { + name: 'name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + email: { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const newJsonStatements = [ + { + type: 'singlestore_recreate_table', + tableName: 'users', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`drop autoincrement`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_drop_autoincrement', + tableName: 'users', + columnName: 'id', + schema: '', + newDataType: 'int', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: true, + columnPk: false, + } as unknown as JsonStatement, + ]; + + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + users: { + name: 'users', + columns: { + new_id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + name: { + name: 'name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + email: { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const newJsonStatements = [ + { + type: 'singlestore_recreate_table', + tableName: 'users', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`set not null`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_set_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: true, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + ]; + + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + users: { + name: 'users', + columns: { + new_id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + name: { + name: 'name', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + email: { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const newJsonStatements = [ + { + type: 'singlestore_recreate_table', + tableName: 'users', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'name', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`drop not null`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_alter_column_drop_notnull', + tableName: 'users', + columnName: 'name', + schema: '', + newDataType: 'text', + columnDefault: undefined, + columnOnUpdate: undefined, + columnNotNull: false, + columnAutoIncrement: false, + columnPk: false, + } as unknown as JsonStatement, + ]; + + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + users: { + name: 'users', + columns: { + new_id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + name: { + name: 'name', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + email: { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const newJsonStatements = [ + { + type: 'singlestore_recreate_table', + tableName: 'users', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'name', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'email', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`renamed column and droped column "test"`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, + { + type: 'alter_table_drop_column', + tableName: 'user', + columnName: 'test', + schema: '', + }, + ]; + const json1: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + user: { + name: 'user', + columns: { + firstName: { + name: 'firstName', + type: 'int', + primaryKey: true, + notNull: true, + autoincrement: false, + }, + lastName: { + name: 'lastName123', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + + const newJsonStatements: JsonStatement[] = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, + { + type: 'alter_table_drop_column', + tableName: 'user', + columnName: 'test', + schema: '', + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`droped column that is part of composite pk`, async (t) => { + const statements: JsonStatement[] = [ + { type: 'delete_composite_pk', tableName: 'user', data: 'id,iq' }, + { + type: 'alter_table_alter_column_set_pk', + tableName: 'user', + schema: '', + columnName: 'id', + }, + { + type: 'alter_table_drop_column', + tableName: 'user', + columnName: 'iq', + schema: '', + }, + ]; + const json1: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + first_nam: { + name: 'first_nam', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + iq: { + name: 'iq', + type: 'int', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: { + user_id_iq_pk: 'id,iq', + }, + uniqueConstraints: {}, + }, + }, + }; + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + user: { + name: 'user', + columns: { + id: { + name: 'id', + type: 'int', + primaryKey: true, + notNull: false, + autoincrement: false, + }, + first_nam: { + name: 'first_name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + + const newJsonStatements: JsonStatement[] = [ + { + type: 'singlestore_recreate_table', + tableName: 'user', + columns: [ + { + name: 'id', + type: 'int', + primaryKey: true, + notNull: false, + autoincrement: false, + }, + { + name: 'first_name', + type: 'text', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); + +test(`add column with pk`, async (t) => { + const statements: JsonStatement[] = [ + { + type: 'alter_table_add_column', + tableName: 'table', + column: { + name: 'test', + type: 'integer', + primaryKey: true, + notNull: false, + autoincrement: false, + }, + schema: '', + }, + ]; + const json2: SingleStoreSchemaSquashed = { + version: '1', + dialect: 'singlestore', + tables: { + table: { + name: 'table', + columns: { + id1: { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + new_age: { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + test: { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + }, + indexes: {}, + compositePrimaryKeys: {}, + uniqueConstraints: {}, + }, + }, + }; + + const newJsonStatements = [ + { + columns: [ + { + name: 'id1', + type: 'text', + primaryKey: false, + notNull: true, + autoincrement: false, + }, + { + name: 'new_age', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + { + name: 'test', + type: 'integer', + primaryKey: false, + notNull: false, + autoincrement: false, + }, + ], + compositePKs: [], + tableName: 'table', + type: 'singlestore_recreate_table', + uniqueConstraints: [], + }, + ]; + expect(singleStoreCombineStatements(statements, json2)).toStrictEqual( + newJsonStatements, + ); +}); From 0a0cdd20c1c1077f0332a5317e22af18fc4b9ee1 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Wed, 15 Jan 2025 12:08:17 +0200 Subject: [PATCH 2/4] updated tests --- drizzle-kit/tests/push/singlestore.test.ts | 2 +- drizzle-kit/tests/singlestore.test.ts | 32 +++++++++---------- .../singlestore-statements-combiner.test.ts | 7 ++++ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drizzle-kit/tests/push/singlestore.test.ts b/drizzle-kit/tests/push/singlestore.test.ts index 4c0c51991..6f58e8ddd 100644 --- a/drizzle-kit/tests/push/singlestore.test.ts +++ b/drizzle-kit/tests/push/singlestore.test.ts @@ -397,7 +397,7 @@ const singlestoreSuite: DialectSuite = { // It's not possible to create/alter/drop primary keys in SingleStore expect(sqlStatements).toStrictEqual([ - 'RENAME TABLE `products_categories` TO `products_to_categories`;', + 'ALTER TABLE `products_categories` RENAME TO `products_to_categories`;', ]); await context.client.query(`DROP TABLE \`products_categories\``); diff --git a/drizzle-kit/tests/singlestore.test.ts b/drizzle-kit/tests/singlestore.test.ts index 50f862ce0..dca99ad2d 100644 --- a/drizzle-kit/tests/singlestore.test.ts +++ b/drizzle-kit/tests/singlestore.test.ts @@ -215,6 +215,13 @@ test('add table #7', async () => { expect(statements.length).toBe(2); expect(statements[0]).toStrictEqual({ + type: 'rename_table', + tableNameFrom: 'users1', + tableNameTo: 'users2', + fromSchema: undefined, + toSchema: undefined, + }); + expect(statements[1]).toStrictEqual({ type: 'create_table', tableName: 'users', schema: undefined, @@ -227,13 +234,6 @@ test('add table #7', async () => { }, compositePkName: '', }); - expect(statements[1]).toStrictEqual({ - type: 'rename_table', - tableNameFrom: 'users1', - tableNameTo: 'users2', - fromSchema: undefined, - toSchema: undefined, - }); }); test('add schema + table #1', async () => { @@ -644,7 +644,7 @@ test('change data type', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -678,7 +678,7 @@ test('drop not null', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -712,7 +712,7 @@ test('set not null', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -746,7 +746,7 @@ test('set default with not null column', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -780,7 +780,7 @@ test('drop default with not null column', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -859,7 +859,7 @@ test('set pk', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -893,7 +893,7 @@ test('drop pk', async () => { );\n`, ); expect(sqlStatements[1]).toBe( - 'INSERT INTO `__new_table`("id", "age") SELECT "id", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id`, `age`) SELECT `id`, `age` FROM `table`;', ); expect(sqlStatements[2]).toBe( 'DROP TABLE `table`;', @@ -930,7 +930,7 @@ test('set not null + rename column on table with indexes', async () => { );\n`, ); expect(sqlStatements[2]).toBe( - 'INSERT INTO `__new_table`("id3", "age") SELECT "id3", "age" FROM `table`;', + 'INSERT INTO `__new_table`(`id3`, `age`) SELECT `id3`, `age` FROM `table`;', ); expect(sqlStatements[3]).toBe( 'DROP TABLE `table`;', @@ -967,7 +967,7 @@ test('set not null + rename table on table with indexes', async () => { );\n`, ); expect(sqlStatements[2]).toBe( - 'INSERT INTO `__new_table1`("id", "age") SELECT "id", "age" FROM `table1`;', + 'INSERT INTO `__new_table1`(\`id\`, \`age\`) SELECT \`id\`, \`age\` FROM `table1`;', ); expect(sqlStatements[3]).toBe( 'DROP TABLE `table1`;', diff --git a/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts b/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts index 191cfa23c..0ba6cf278 100644 --- a/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts +++ b/drizzle-kit/tests/statements-combiner/singlestore-statements-combiner.test.ts @@ -99,6 +99,13 @@ test(`change column data type`, async (t) => { }; const newJsonStatements = [ + { + type: 'alter_table_rename_column', + tableName: 'user', + oldColumnName: 'lastName', + newColumnName: 'lastName123', + schema: '', + }, { type: 'singlestore_recreate_table', tableName: 'user', From 6ebfff465fc32d7959f4351a937a7bc658fdf000 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Thu, 23 Jan 2025 15:18:31 +0200 Subject: [PATCH 3/4] update: returned validated instead of squashed for `filterStatements` and `singleStoreLogSuggestionsAndReturn` --- drizzle-kit/src/api.ts | 4 ++-- drizzle-kit/src/cli/commands/migrate.ts | 2 +- drizzle-kit/src/cli/commands/push.ts | 8 ++++---- drizzle-kit/src/cli/commands/singlestorePushUtils.ts | 10 ++++++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drizzle-kit/src/api.ts b/drizzle-kit/src/api.ts index 930d6f1e9..3af67a042 100644 --- a/drizzle-kit/src/api.ts +++ b/drizzle-kit/src/api.ts @@ -473,8 +473,8 @@ export const pushSingleStoreSchema = async ( const { shouldAskForApprove, statementsToExecute, infoToPrint } = await logSuggestionsAndReturn( db, statements, - squashedCur, - squashedPrev, + validatedCur, + validatedPrev, ); return { diff --git a/drizzle-kit/src/cli/commands/migrate.ts b/drizzle-kit/src/cli/commands/migrate.ts index 3718078cb..8c62a5edb 100644 --- a/drizzle-kit/src/cli/commands/migrate.ts +++ b/drizzle-kit/src/cli/commands/migrate.ts @@ -667,7 +667,7 @@ export const prepareSingleStorePush = async ( 'push', ); - return { sqlStatements, statements, squashedPrev, squashedCur }; + return { sqlStatements, statements, validatedCur, validatedPrev }; } catch (e) { console.error(e); process.exit(1); diff --git a/drizzle-kit/src/cli/commands/push.ts b/drizzle-kit/src/cli/commands/push.ts index eef0b1869..e517e4b0d 100644 --- a/drizzle-kit/src/cli/commands/push.ts +++ b/drizzle-kit/src/cli/commands/push.ts @@ -188,8 +188,8 @@ export const singlestorePush = async ( const filteredStatements = singleStoreFilterStatements( statements.statements ?? [], - statements.squashedCur, - statements.squashedPrev, + statements.validatedCur, + statements.validatedPrev, ); try { @@ -207,8 +207,8 @@ export const singlestorePush = async ( } = await singleStoreLogSuggestionsAndReturn( db, filteredStatements, - statements.squashedCur, - statements.squashedPrev, + statements.validatedCur, + statements.validatedPrev, ); if (verbose) { diff --git a/drizzle-kit/src/cli/commands/singlestorePushUtils.ts b/drizzle-kit/src/cli/commands/singlestorePushUtils.ts index d3c32d789..5a550a239 100644 --- a/drizzle-kit/src/cli/commands/singlestorePushUtils.ts +++ b/drizzle-kit/src/cli/commands/singlestorePushUtils.ts @@ -1,16 +1,18 @@ import chalk from 'chalk'; import { render } from 'hanji'; import { fromJson } from 'src/sqlgenerator'; +import { TypeOf } from 'zod'; import { JsonAlterColumnTypeStatement, JsonStatement } from '../../jsonStatements'; import { Column, SingleStoreSchemaSquashed, SingleStoreSquasher } from '../../serializer/singlestoreSchema'; +import { singlestoreSchema } from '../../serializer/singlestoreSchema'; import { type DB, findAddedAndRemoved } from '../../utils'; import { Select } from '../selector-ui'; import { withStyle } from '../validations/outputs'; export const filterStatements = ( statements: JsonStatement[], - currentSchema: SingleStoreSchemaSquashed, - prevSchema: SingleStoreSchemaSquashed, + currentSchema: TypeOf, + prevSchema: TypeOf, ) => { return statements.filter((statement) => { if (statement.type === 'alter_table_alter_column_set_type') { @@ -126,8 +128,8 @@ export function findColumnTypeAlternations( export const logSuggestionsAndReturn = async ( db: DB, statements: JsonStatement[], - json2: SingleStoreSchemaSquashed, - json1: SingleStoreSchemaSquashed, + json2: TypeOf, + json1: TypeOf, ) => { let shouldAskForApprove = false; const statementsToExecute: string[] = []; From 88fe39671219d67cbac0ba6268b7c1f67857c0b0 Mon Sep 17 00:00:00 2001 From: Aleksandr Sherman Date: Thu, 23 Jan 2025 15:46:37 +0200 Subject: [PATCH 4/4] fixed: drizzle-zod errors and dprint errors --- drizzle-zod/src/column.types.ts | 2 +- drizzle-zod/tests/singlestore.test.ts | 2 +- integration-tests/tests/replicas/singlestore.test.ts | 1 - integration-tests/tests/replicas/sqlite.test.ts | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drizzle-zod/src/column.types.ts b/drizzle-zod/src/column.types.ts index 35d010920..49c12cdbb 100644 --- a/drizzle-zod/src/column.types.ts +++ b/drizzle-zod/src/column.types.ts @@ -28,6 +28,7 @@ export type GetZodType< : TData extends infer TTuple extends [any, ...any[]] ? z.ZodTuple }, [any, ...any[]]>> : TData extends Date ? z.ZodDate + : TData extends Buffer ? z.ZodType : TDataType extends 'array' ? z.ZodArray[number], string, undefined, undefined>> : TData extends infer TDict extends Record ? z.ZodObject<{ [K in keyof TDict]: GetZodType }, 'strip'> @@ -36,7 +37,6 @@ export type GetZodType< : TData extends bigint ? z.ZodBigInt : TData extends boolean ? z.ZodBoolean : TData extends string ? z.ZodString - : TData extends Buffer ? z.ZodType : z.ZodTypeAny; type HandleSelectColumn< diff --git a/drizzle-zod/tests/singlestore.test.ts b/drizzle-zod/tests/singlestore.test.ts index b91c74be8..929bdf4b3 100644 --- a/drizzle-zod/tests/singlestore.test.ts +++ b/drizzle-zod/tests/singlestore.test.ts @@ -1,4 +1,4 @@ -import { type Equal } from 'drizzle-orm'; +import type { Equal } from 'drizzle-orm'; import { customType, int, serial, singlestoreSchema, singlestoreTable, text } from 'drizzle-orm/singlestore-core'; import { test } from 'vitest'; import { z } from 'zod'; diff --git a/integration-tests/tests/replicas/singlestore.test.ts b/integration-tests/tests/replicas/singlestore.test.ts index d4344a3b1..0c1d44f33 100644 --- a/integration-tests/tests/replicas/singlestore.test.ts +++ b/integration-tests/tests/replicas/singlestore.test.ts @@ -813,7 +813,6 @@ describe('[transaction] replicas singlestore', () => { // // }); // }); - describe('[$count] read replicas postgres', () => { it('primary $count', () => { const primaryDb = drizzle.mock(); diff --git a/integration-tests/tests/replicas/sqlite.test.ts b/integration-tests/tests/replicas/sqlite.test.ts index adc74a373..e7fae2e70 100644 --- a/integration-tests/tests/replicas/sqlite.test.ts +++ b/integration-tests/tests/replicas/sqlite.test.ts @@ -800,7 +800,6 @@ describe('[findMany] read replicas sqlite', () => { }); }); - describe('[$count] read replicas postgres', () => { it('primary $count', () => { const primaryDb = drizzle.mock();