Skip to content

Commit 88e491c

Browse files
[sqlite]: push - explain
1 parent 6e4e345 commit 88e491c

File tree

4 files changed

+71
-102
lines changed

4 files changed

+71
-102
lines changed

drizzle-kit/src/cli/commands/push-sqlite.ts

Lines changed: 31 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import { fromDrizzleSchema, prepareFromSchemaFiles } from 'src/dialects/sqlite/d
99
import type { JsonStatement } from 'src/dialects/sqlite/statements';
1010
import type { SQLiteDB } from '../../utils';
1111
import { prepareFilenames } from '../../utils/utils-node';
12+
import { highlightSQL } from '../highlighter';
1213
import { resolver } from '../prompts';
1314
import { Select } from '../selector-ui';
1415
import type { EntitiesFilterConfig } from '../validations/cli';
1516
import type { CasingType } from '../validations/common';
16-
import { withStyle } from '../validations/outputs';
1717
import type { SqliteCredentials } from '../validations/sqlite';
18-
import { ProgressView } from '../views';
18+
import { explain, ProgressView } from '../views';
1919

2020
export const handle = async (
2121
schemaPath: string | string[],
@@ -24,6 +24,7 @@ export const handle = async (
2424
filters: EntitiesFilterConfig,
2525
force: boolean,
2626
casing: CasingType | undefined,
27+
explainFlag: boolean,
2728
) => {
2829
const { connectToSQLite } = await import('../connections');
2930
const { introspect: sqliteIntrospect } = await import('./pull-sqlite');
@@ -43,7 +44,7 @@ export const handle = async (
4344

4445
const { ddl: ddl1 } = await sqliteIntrospect(db, filter, progress);
4546

46-
const { sqlStatements, statements } = await ddlDiff(
47+
const { sqlStatements, statements, groupedStatements } = await ddlDiff(
4748
ddl1,
4849
ddl2,
4950
resolver<Table>('table'),
@@ -56,40 +57,14 @@ export const handle = async (
5657
return;
5758
}
5859

59-
const { hints } = await suggestions(db, statements);
60+
const hints = await suggestions(db, statements);
6061

61-
if (verbose && sqlStatements.length > 0) {
62-
console.log();
63-
console.log(
64-
withStyle.warning('You are about to execute current statements:'),
65-
);
66-
console.log();
67-
console.log(sqlStatements.map((s) => chalk.blue(s)).join('\n'));
68-
console.log();
69-
}
62+
const explainMessage = explain('sqlite', groupedStatements, explainFlag, hints);
7063

71-
if (!force && sqlStatements.length > 0) {
72-
const { data } = await render(
73-
new Select(['No, abort', `Yes, I want to execute all statements`]),
74-
);
75-
if (data?.index === 0) {
76-
render(`[${chalk.red('x')}] All changes were aborted`);
77-
process.exit(0);
78-
}
79-
}
64+
if (explainMessage) console.log(explainMessage);
65+
if (explainFlag) return;
8066

8167
if (!force && hints.length > 0) {
82-
console.log(withStyle.warning('Found data-loss statements:'));
83-
console.log(hints.join('\n'));
84-
console.log();
85-
console.log(
86-
chalk.red.bold(
87-
'THIS ACTION WILL CAUSE DATA LOSS AND CANNOT BE REVERTED\n',
88-
),
89-
);
90-
91-
console.log(chalk.white('Do you still want to push changes?'));
92-
9368
const { data } = await render(new Select(['No, abort', 'Yes, I want to execute all statements']));
9469

9570
if (data?.index === 0) {
@@ -98,6 +73,8 @@ export const handle = async (
9873
}
9974
}
10075

76+
const lossStatements = hints.map((x) => x.statement).filter((x) => typeof x !== 'undefined');
77+
10178
if (sqlStatements.length === 0) {
10279
render(`\n[${chalk.blue('i')}] No changes detected`);
10380
} else {
@@ -108,8 +85,10 @@ export const handle = async (
10885
const isD1 = 'driver' in credentials && credentials.driver === 'd1-http';
10986
if (!isD1) await db.run('begin');
11087
try {
111-
for (const dStmnt of sqlStatements) {
112-
await db.run(dStmnt);
88+
for (const statement of [...lossStatements, ...sqlStatements]) {
89+
if (verbose) console.log(highlightSQL(statement));
90+
91+
await db.run(statement);
11392
}
11493
if (!isD1) await db.run('commit');
11594
} catch (e) {
@@ -127,36 +106,38 @@ export const suggestions = async (
127106
connection: SQLiteDB,
128107
jsonStatements: JsonStatement[],
129108
) => {
130-
const statements: string[] = [];
131-
const hints = [] as string[];
109+
const grouped: { hint: string; statement?: string }[] = [];
132110

133111
// TODO: generate truncations/recreates ??
134112
for (const statement of jsonStatements) {
135113
if (statement.type === 'drop_table') {
136114
const name = statement.tableName;
137115
const res = await connection.query(`select 1 from "${name}" limit 1;`);
138116

139-
if (res.length > 0) hints.push(`· You're about to delete non-empty '${name}' table`);
117+
if (res.length > 0) grouped.push({ hint: `· You're about to delete non-empty '${name}' table` });
140118
continue;
141119
}
142120

143121
if (statement.type === 'drop_column') {
144122
const { table, name } = statement.column;
145123

146124
const res = await connection.query(`select 1 from "${table}" limit 1;`);
147-
if (res.length > 0) hints.push(`· You're about to delete '${name}' column in a non-empty '${table}' table`);
125+
if (res.length > 0) {
126+
grouped.push({ hint: `· You're about to delete '${name}' column in a non-empty '${table}' table` });
127+
}
148128
continue;
149129
}
150130

151131
if (statement.type === 'add_column' && (statement.column.notNull && !statement.column.default)) {
152132
const { table, name } = statement.column;
153133
const res = await connection.query(`select 1 from "${table}" limit 1`);
154134
if (res.length > 0) {
155-
hints.push(
156-
`· You're about to add not-null '${name}' column without default value to non-empty '${table}' table`,
135+
grouped.push(
136+
{
137+
hint: `· You're about to add not-null '${name}' column without default value to non-empty '${table}' table`,
138+
statement: `DELETE FROM "${table}" where true;`,
139+
},
157140
);
158-
159-
statements.push(`DELETE FROM "${table}" where true;`);
160141
}
161142

162143
continue;
@@ -170,14 +151,16 @@ export const suggestions = async (
170151

171152
const res = await connection.query(`select 1 from "${statement.from.name}" limit 1`);
172153
if (res.length > 0) {
173-
hints.push(
174-
`· You're about to drop ${
175-
droppedColumns.map((col) => `'${col.name}'`).join(', ')
176-
} column(s) in a non-empty '${statement.from.name}' table`,
154+
grouped.push(
155+
{
156+
hint: `· You're about to drop ${
157+
droppedColumns.map((col) => `'${col.name}'`).join(', ')
158+
} column(s) in a non-empty '${statement.from.name}' table`,
159+
},
177160
);
178161
}
179162
}
180163
}
181164

182-
return { statements, hints };
165+
return grouped;
183166
};

drizzle-kit/src/cli/schema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,10 @@ export const push = command({
333333
await handle(schemaPath, verbose, credentials, filters, force, casing, explain);
334334
} else if (dialect === 'sqlite') {
335335
const { handle: sqlitePush } = await import('./commands/push-sqlite');
336-
await sqlitePush(schemaPath, verbose, credentials, filters, force, casing);
336+
await sqlitePush(schemaPath, verbose, credentials, filters, force, casing, explain);
337337
} else if (dialect === 'turso') {
338338
const { handle: libSQLPush } = await import('./commands/push-libsql');
339-
await libSQLPush(schemaPath, verbose, credentials, filters, force, casing);
339+
await libSQLPush(schemaPath, verbose, credentials, filters, force, casing, explain);
340340
} else if (dialect === 'singlestore') {
341341
const { handle } = await import('./commands/push-singlestore');
342342
await handle(schemaPath, credentials, filters, verbose, force, casing);

drizzle-kit/src/cli/views.ts

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,19 @@ function formatOptionChanges(
9999
}
100100

101101
export const explain = (
102-
dialect: 'mysql' | 'postgres',
103-
grouped: { jsonStatement: StatementPostgres; sqlStatements: string[] }[],
102+
dialect: 'postgres' | 'mysql' | 'sqlite' | 'singlestore' | 'mssql' | 'common' | 'gel' | 'cockroach',
103+
grouped: { jsonStatement: StatementPostgres | StatementSqlite; sqlStatements: string[] }[],
104104
explain: boolean,
105105
hints: { hint: string; statement?: string }[],
106106
) => {
107107
const res = [];
108108
const explains = [];
109109
for (const { jsonStatement, sqlStatements } of grouped) {
110-
const res = dialect === 'postgres' ? psqlExplain(jsonStatement as StatementPostgres) : null;
110+
const res = dialect === 'postgres'
111+
? psqlExplain(jsonStatement as StatementPostgres)
112+
: dialect === 'sqlite'
113+
? sqliteExplain(jsonStatement as StatementSqlite)
114+
: null;
111115

112116
if (res) {
113117
let msg = `┌─── ${res.title}\n`;
@@ -618,27 +622,22 @@ export const sqliteExplain = (
618622
}
619623

620624
if (checkDiffs.length) {
621-
let res: string = '';
622625
const createdChecks = checkDiffs.filter((it) => it.$diffType === 'create');
623626
const droppedChecks = checkDiffs.filter((it) => it.$diffType === 'drop');
624627

625628
if (createdChecks.length) {
626-
res += `| Check constraints added: ${createdChecks.map((it) => `${it.name}`).join(', ')}\n`;
629+
blocks.push([`| Check constraints added: ${createdChecks.map((it) => `${it.name}`).join(', ')}\n`]);
627630
}
628631

629-
if (droppedChecks) {
630-
res += `| Check constraints dropped: ${droppedChecks.map((it) => `${it.name}`).join(', ')}\n`;
632+
if (droppedChecks.length) {
633+
blocks.push([`| Check constraints dropped: ${droppedChecks.map((it) => `${it.name}`).join(', ')}\n`]);
631634
}
632-
633-
res += `| It is not possible to create/drop check constraints on existing table\n`;
634-
blocks.push([res]);
635635
}
636636

637637
if (checksAlters.length) {
638638
blocks.push([
639639
`│ Check constraints altered definition:\n`,
640640
`│ ${checksAlters.map((it) => `${it.name}: ${it.$left.value} -> ${it.$right.value}`).join(',\n')}\n`,
641-
`│ It is not possible to alter definition\n`,
642641
]);
643642
}
644643

@@ -679,49 +678,40 @@ export const sqliteExplain = (
679678
}
680679

681680
if (uniquesDiff.length) {
682-
let res: string = '';
683-
684681
const uniquesCreated = uniquesDiff.filter((it) => it.$diffType === 'create');
685682
const uniquesDropped = uniquesDiff.filter((it) => it.$diffType === 'drop');
686683
if (uniquesCreated.length) {
687-
res += `│ Unique constraints added: ${uniquesCreated.map((it) => `${it.name}`).join(', ')}\n`;
684+
blocks.push([`│ Unique constraints added: ${uniquesCreated.map((it) => `${it.name}`).join(', ')}\n`]);
688685
}
689686
if (uniquesDropped.length) {
690-
res += `│ Unique constraints dropped: ${uniquesDropped.map((it) => `${it.name}`).join(', ')}\n`;
687+
blocks.push([`│ Unique constraints dropped: ${uniquesDropped.map((it) => `${it.name}`).join(', ')}\n`]);
691688
}
692-
693-
res += `│ It is not possible to create/drop unique constraints on existing table\n`;
694-
695-
blocks.push([res]);
696689
}
697690

698691
if (pksDiff.length) {
699-
let res: string = '';
700692
const pksCreated = pksDiff.filter((it) => it.$diffType === 'create');
701693
const pksDropped = pksDiff.filter((it) => it.$diffType === 'drop');
702694

703695
if (pksCreated.length) {
704-
res += `│ Primary key constraints added: ${pksCreated.map((it) => `${it.name}`).join(', ')}\n`;
696+
blocks.push([`│ Primary key constraints added: ${pksCreated.map((it) => `${it.name}`).join(', ')}\n`]);
697+
}
698+
if (pksDropped.length) {
699+
blocks.push([`│ Primary key constraints dropped: ${pksDropped.map((it) => `${it.name}`).join(', ')}\n`]);
705700
}
706-
if (pksDropped) res += `│ Primary key constraints dropped: ${pksDropped.map((it) => `${it.name}`).join(', ')}\n`;
707-
708-
res += `│ It is not possible to create/drop primary key constraints on existing table\n`;
709-
blocks.push([res]);
710701
}
711702

712703
if (newStoredColumns.length) {
713704
blocks.push([
714705
`| Stored columns added: ${newStoredColumns.map((it) => `${it.name}`).join(', ')}\n`,
715-
`| It is not possible to ALTER TABLE ADD COLUMN a STORED column. One can add a VIRTUAL column, however\n`,
716706
]);
717707
}
718708

719709
if (pksAlters.length) {
720710
blocks.push([
721711
`│ Primary key was altered:\n`,
722-
`│ columns: ${
712+
`${
723713
pksAlters.filter((it) => it.columns).map((it) =>
724-
`${it.name}: [${it.columns?.from.join(',')}] -> [${it.columns?.to.join(',')}]\n`
714+
`[${it.columns?.from.join(',')}] -> [${it.columns?.to.join(',')}]\n`
725715
)
726716
}\n`,
727717
]);
@@ -730,9 +720,9 @@ export const sqliteExplain = (
730720
if (uniquesAlters.length) {
731721
blocks.push([
732722
`│ Unique constraint was altered:\n`,
733-
`│ columns: ${
723+
`${
734724
uniquesAlters.filter((it) => it.columns).map((it) =>
735-
`${it.name}, [${it.columns?.from.join(',')}] -> [${it.columns?.to.join(',')}]\n`
725+
`│ name: ${it.name} => columns: [${it.columns?.from.join(',')}] -> [${it.columns?.to.join(',')}]\n`
736726
)
737727
}\n`,
738728
]);
@@ -749,56 +739,49 @@ export const sqliteExplain = (
749739
if (columnsAltered) {
750740
res += `${
751741
columnsAltered.map((it) =>
752-
`│ ${it.name} => columns: [${it.columns?.from.join(',')}] -> [${it.columns?.to.join(',')}]`
742+
`│ name: ${it.name} => columns: [${it.columns?.from.join(',')}] -> [${it.columns?.to.join(',')}]`
753743
)
754744
}\n`;
755745
}
756746
if (columnsToAltered) {
757747
res += ` ${
758748
columnsToAltered.map((it) =>
759-
`│ ${it.name} => columnsTo: [${it.columnsTo?.from.join(',')}] -> [${it.columnsTo?.to.join(',')}]`
749+
`│ name: ${it.name} => columnsTo: [${it.columnsTo?.from.join(',')}] -> [${it.columnsTo?.to.join(',')}]`
760750
)
761751
}\n`;
762752
}
763753
if (tablesToAltered) {
764754
res += `${
765-
tablesToAltered.map((it) => `│ ${it.name} => tableTo: [${it.tableTo?.from}] -> [${it.tableTo?.to}]`)
755+
tablesToAltered.map((it) => `│ name: ${it.name} => tableTo: [${it.tableTo?.from}] -> [${it.tableTo?.to}]`)
766756
}\n`;
767757
}
768758

769759
blocks.push([res]);
770760
}
771761

772762
if (fksDiff.length) {
773-
let res: string = '';
774-
775763
const fksCreated = fksDiff.filter((it) => it.$diffType === 'create');
776764
const fksDropped = fksDiff.filter((it) => it.$diffType === 'drop');
777-
if (fksCreated) res += `| Foreign key constraints added: ${fksCreated.map((it) => `${it.name}`).join(', ')}\n`;
778-
if (fksDropped) res += `| Unique constraints dropped: ${fksDropped.map((it) => `${it.name}`).join(', ')}\n`;
779-
780-
res += `| It is not possible to create/drop foreign key constraints on existing table\n`;
781-
782-
blocks.push([res]);
765+
if (fksCreated.length) {
766+
blocks.push([`| Foreign key constraints added: ${fksCreated.map((it) => `${it.name}`).join(', ')}\n`]);
767+
}
768+
if (fksDropped.length) {
769+
blocks.push([`| Foreign key constraints dropped: ${fksDropped.map((it) => `${it.name}`).join(', ')}\n`]);
770+
}
783771
}
784772

785773
if (indexesDiff.filter((it) => it.isUnique && it.origin === 'auto').length) {
786-
let res: string = '';
787774
const indexCreated = indexesDiff.filter((it) => it.$diffType === 'create');
788775
const indexDropped = indexesDiff.filter((it) => it.$diffType === 'drop');
789-
if (indexCreated) res += `| System generated index added: ${indexCreated.map((it) => `${it.name}`).join(', ')}\n`;
790-
if (indexDropped) {
791-
res += `| System generated index dropped: ${indexDropped.map((it) => `${it.name}`).join(', ')}\n`;
776+
if (indexCreated.length) {
777+
blocks.push([`| System generated index added: ${indexCreated.map((it) => `${it.name}`).join(', ')}\n`]);
778+
}
779+
if (indexDropped.length) {
780+
blocks.push([`| System generated index dropped: ${indexDropped.map((it) => `${it.name}`).join(', ')}\n`]);
792781
}
793-
794-
res += `| It is not possible to drop/create auto generated unique indexes\n`;
795-
796-
blocks.push([res]);
797782
}
798783

799-
if (blocks.filter((it) => Boolean(it))) {
800-
cause += blocks.map((it) => it.join('')).join('|-\n');
801-
}
784+
cause += blocks.map((it) => it.join('')).join('├─\n');
802785
}
803786

804787
if (st.type === 'recreate_column') {

drizzle-kit/src/dialects/sqlite/diff.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ export const ddlDiff = async (
270270
for (const it of createdTables) {
271271
setOfTablesToRecereate.delete(it.name);
272272
}
273+
for (const it of deletedTables) {
274+
setOfTablesToRecereate.delete(it.name);
275+
}
273276

274277
for (const it of updates) {
275278
if (

0 commit comments

Comments
 (0)