Skip to content

Commit

Permalink
Merge pull request #3950 from drizzle-team/drizzle-kit/singlestore-re…
Browse files Browse the repository at this point in the history
…create

[Drizzle-kit]: singlestore recreate
  • Loading branch information
AndriiSherman authored Jan 23, 2025
2 parents 0cd20b6 + 90cced1 commit f9c666c
Show file tree
Hide file tree
Showing 17 changed files with 2,316 additions and 58 deletions.
1 change: 1 addition & 0 deletions drizzle-kit/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ export const pushSingleStoreSchema = async (
db,
statements,
validatedCur,
validatedPrev,
);

return {
Expand Down
27 changes: 3 additions & 24 deletions drizzle-kit/src/cli/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,34 +208,16 @@ export const singlestorePush = async (
db,
filteredStatements,
statements.validatedCur,
statements.validatedPrev,
);

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();
}

Expand Down Expand Up @@ -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 {
Expand Down
108 changes: 106 additions & 2 deletions drizzle-kit/src/cli/commands/singlestorePushUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import chalk from 'chalk';
import { render } from 'hanji';
import { fromJson } from 'src/sqlgenerator';
import { TypeOf } from 'zod';
import { JsonAlterColumnTypeStatement, JsonStatement } from '../../jsonStatements';
import { singlestoreSchema, SingleStoreSquasher } from '../../serializer/singlestoreSchema';
import type { DB } from '../../utils';
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';

Expand Down Expand Up @@ -104,10 +106,30 @@ export const filterStatements = (
});
};

export function findColumnTypeAlternations(
columns1: Record<string, Column>,
columns2: Record<string, Column>,
): 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<typeof singlestoreSchema>,
json1: TypeOf<typeof singlestoreSchema>,
) => {
let shouldAskForApprove = false;
const statementsToExecute: string[] = [];
Expand Down Expand Up @@ -337,6 +359,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);
}
}

Expand Down
9 changes: 9 additions & 0 deletions drizzle-kit/src/jsonStatements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -794,6 +802,7 @@ export type JsonAlterColumnStatement =
| JsonAlterColumnDropIdentityStatement;

export type JsonStatement =
| JsonRecreateSingleStoreTableStatement
| JsonRecreateTableStatement
| JsonAlterColumnStatement
| JsonCreateTableStatement
Expand Down
10 changes: 5 additions & 5 deletions drizzle-kit/src/snapshotsDiffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <T extends ZodTypeAny>(schema: T) => {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) => {
Expand All @@ -3193,7 +3193,7 @@ export const applySingleStoreSnapshotsDiff = async (
const _meta = prepareMigrationMeta([], rTables, rColumns);

return {
statements: jsonStatements,
statements: combinedJsonStatements,
sqlStatements: uniqueSqlStatements,
_meta,
};
Expand Down
73 changes: 66 additions & 7 deletions drizzle-kit/src/sqlgenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
JsonMoveEnumStatement,
JsonMoveSequenceStatement,
JsonPgCreateIndexStatement,
JsonRecreateSingleStoreTableStatement,
JsonRecreateTableStatement,
JsonRenameColumnStatement,
JsonRenameEnumStatement,
Expand Down Expand Up @@ -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';
}
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -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';
}
Expand Down Expand Up @@ -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}\`;`;
}
}

Expand Down Expand Up @@ -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}\`;`;
}
}

Expand Down Expand Up @@ -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';
}
Expand Down Expand Up @@ -3816,10 +3817,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());
Expand Down
Loading

0 comments on commit f9c666c

Please sign in to comment.