Skip to content

Commit

Permalink
- added sqlite, turso checks
Browse files Browse the repository at this point in the history
- added and updated tests
  • Loading branch information
AleksandrSherman committed Sep 10, 2024
1 parent 7075a0f commit e3cc54e
Show file tree
Hide file tree
Showing 27 changed files with 1,753 additions and 44 deletions.
9 changes: 9 additions & 0 deletions drizzle-kit/src/cli/commands/libSqlPushUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,17 @@ export const _moveDataStatements = (
const compositePKs = Object.values(
json.tables[tableName].compositePrimaryKeys,
).map((it) => SQLiteSquasher.unsquashPK(it));
const checkConstraints = Object.values(json.tables[tableName].checkConstraints);

const fks = referenceData.map((it) => SQLiteSquasher.unsquashPushFK(it));

const mappedCheckConstraints: string[] = checkConstraints.map((it) =>
it.replaceAll(`"${tableName}".`, `"${newTableName}".`)
.replaceAll(`\`${tableName}\`.`, `\`${newTableName}\`.`)
.replaceAll(`${tableName}.`, `${newTableName}.`)
.replaceAll(`'${tableName}'.`, `\`${newTableName}\`.`)
);

// create new table
statements.push(
new SQLiteCreateTableConvertor().convert({
Expand All @@ -51,6 +59,7 @@ export const _moveDataStatements = (
columns: tableColumns,
referenceData: fks,
compositePKs,
checkConstraints: mappedCheckConstraints,
}),
);

Expand Down
9 changes: 9 additions & 0 deletions drizzle-kit/src/cli/commands/sqlitePushUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ export const _moveDataStatements = (
const compositePKs = Object.values(
json.tables[tableName].compositePrimaryKeys,
).map((it) => SQLiteSquasher.unsquashPK(it));
const checkConstraints = Object.values(json.tables[tableName].checkConstraints);

const mappedCheckConstraints: string[] = checkConstraints.map((it) =>
it.replaceAll(`"${tableName}".`, `"${newTableName}".`)
.replaceAll(`\`${tableName}\`.`, `\`${newTableName}\`.`)
.replaceAll(`${tableName}.`, `${newTableName}.`)
.replaceAll(`'${tableName}'.`, `\`${newTableName}\`.`)
);

const fks = referenceData.map((it) => SQLiteSquasher.unsquashPushFK(it));

Expand All @@ -38,6 +46,7 @@ export const _moveDataStatements = (
columns: tableColumns,
referenceData: fks,
compositePKs,
checkConstraints: mappedCheckConstraints,
}),
);

Expand Down
28 changes: 28 additions & 0 deletions drizzle-kit/src/introspect-sqlite.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import './@types/utils';
import type { Casing } from './cli/validations/common';
import { CheckConstraint } from './serializer/mysqlSchema';
import type {
Column,
ForeignKey,
Expand Down Expand Up @@ -78,11 +79,15 @@ export const schemaToTypeScript = (
const uniqueImports = Object.values(it.uniqueConstraints).map(
(it) => 'unique',
);
const checkImports = Object.values(it.checkConstraints).map(
(it) => 'check',
);

res.sqlite.push(...idxImports);
res.sqlite.push(...fkImpots);
res.sqlite.push(...pkImports);
res.sqlite.push(...uniqueImports);
res.sqlite.push(...checkImports);

const columnImports = Object.values(it.columns)
.map((col) => {
Expand Down Expand Up @@ -127,6 +132,7 @@ export const schemaToTypeScript = (
|| filteredFKs.length > 0
|| Object.keys(table.compositePrimaryKeys).length > 0
|| Object.keys(table.uniqueConstraints).length > 0
|| Object.keys(table.checkConstraints).length > 0
) {
statement += ',\n';
statement += '(table) => {\n';
Expand All @@ -145,6 +151,10 @@ export const schemaToTypeScript = (
Object.values(table.uniqueConstraints),
casing,
);
statement += createTableChecks(
Object.values(table.checkConstraints),
casing,
);
statement += '\t}\n';
statement += '}';
}
Expand Down Expand Up @@ -401,6 +411,24 @@ const createTableUniques = (

return statement;
};
const createTableChecks = (
checks: CheckConstraint[],
casing: Casing,
): string => {
let statement = '';

checks.forEach((it) => {
const checkKey = withCasing(it.name, casing);

statement += `\t\t${checkKey}: `;
statement += 'check(';
statement += `"${it.name}", `;
statement += `sql\`${it.value}\`)`;
statement += `,\n`;
});

return statement;
};

const createTablePKs = (pks: PrimaryKey[], casing: Casing): string => {
let statement = '';
Expand Down
5 changes: 4 additions & 1 deletion drizzle-kit/src/jsonStatements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface JsonSqliteCreateTableStatement {
}[];
compositePKs: string[][];
uniqueConstraints?: string[];
checkConstraints?: string[];
}

export interface JsonCreateTableStatement {
Expand Down Expand Up @@ -56,6 +57,7 @@ export interface JsonRecreateTableStatement {
}[];
compositePKs: string[][];
uniqueConstraints?: string[];
checkConstraints: string[];
}

export interface JsonDropTableStatement {
Expand Down Expand Up @@ -661,7 +663,7 @@ export const prepareSQLiteCreateTable = (
table: Table,
action?: 'push' | undefined,
): JsonSqliteCreateTableStatement => {
const { name, columns, uniqueConstraints } = table;
const { name, columns, uniqueConstraints, checkConstraints } = table;

const references: string[] = Object.values(table.foreignKeys);

Expand All @@ -682,6 +684,7 @@ export const prepareSQLiteCreateTable = (
referenceData: fks,
compositePKs: composites,
uniqueConstraints: Object.values(uniqueConstraints),
checkConstraints: Object.values(checkConstraints),
};
};

Expand Down
1 change: 1 addition & 0 deletions drizzle-kit/src/serializer/mysqlSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const tableSquashed = object({
foreignKeys: record(string(), string()),
compositePrimaryKeys: record(string(), string()),
uniqueConstraints: record(string(), string()).default({}),
checkConstraints: record(string(), string()).default({}),
}).strict();

export const schemaSquashed = object({
Expand Down
29 changes: 28 additions & 1 deletion drizzle-kit/src/serializer/sqliteSchema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { any, boolean, enum as enumType, literal, object, record, string, TypeOf, union } from 'zod';
import { customMapEntries, mapEntries, mapValues, originUUID } from '../global';
import { customMapEntries, mapValues, originUUID } from '../global';

// ------- V3 --------
const index = object({
Expand Down Expand Up @@ -49,13 +49,19 @@ const uniqueConstraint = object({
columns: string().array(),
}).strict();

const checkConstraint = object({
name: string(),
value: string(),
}).strict();

const table = object({
name: string(),
columns: record(string(), column),
indexes: record(string(), index),
foreignKeys: record(string(), fk),
compositePrimaryKeys: record(string(), compositePK),
uniqueConstraints: record(string(), uniqueConstraint).default({}),
checkConstraints: record(string(), checkConstraint).default({}),
}).strict();

// use main dialect
Expand Down Expand Up @@ -128,6 +134,7 @@ const tableSquashed = object({
foreignKeys: record(string(), string()),
compositePrimaryKeys: record(string(), string()),
uniqueConstraints: record(string(), string()).default({}),
checkConstraints: record(string(), string()).default({}),
}).strict();

export const schemaSquashed = object({
Expand All @@ -150,6 +157,7 @@ export type Index = TypeOf<typeof index>;
export type ForeignKey = TypeOf<typeof fk>;
export type PrimaryKey = TypeOf<typeof compositePK>;
export type UniqueConstraint = TypeOf<typeof uniqueConstraint>;
export type CheckConstraint = TypeOf<typeof checkConstraint>;

export const SQLiteSquasher = {
squashIdx: (idx: Index) => {
Expand Down Expand Up @@ -233,6 +241,17 @@ export const SQLiteSquasher = {
unsquashPK: (pk: string) => {
return pk.split(',');
},
squashCheck: (check: CheckConstraint) => {
return `${check.name};${check.value}`;
},
unsquashCheck: (input: string): CheckConstraint => {
const [
name,
value,
] = input.split(';');

return { name, value };
},
};

export const squashSqliteScheme = (
Expand Down Expand Up @@ -268,6 +287,13 @@ export const squashSqliteScheme = (
},
);

const squashedCheckConstraints = mapValues(
it[1].checkConstraints,
(check) => {
return SQLiteSquasher.squashCheck(check);
},
);

return [
it[0],
{
Expand All @@ -277,6 +303,7 @@ export const squashSqliteScheme = (
foreignKeys: squashedFKs,
compositePrimaryKeys: squashedPKs,
uniqueConstraints: squashedUniqueConstraints,
checkConstraints: squashedCheckConstraints,
},
];
}),
Expand Down
89 changes: 89 additions & 0 deletions drizzle-kit/src/serializer/sqliteSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { withStyle } from '../cli/validations/outputs';
import type { IntrospectStage, IntrospectStatus } from '../cli/views';
import type {
CheckConstraint,
Column,
ForeignKey,
Index,
Expand All @@ -37,11 +38,15 @@ export const generateSqliteSnapshot = (
const foreignKeysObject: Record<string, ForeignKey> = {};
const primaryKeysObject: Record<string, PrimaryKey> = {};
const uniqueConstraintObject: Record<string, UniqueConstraint> = {};
const checkConstraintObject: Record<string, CheckConstraint> = {};

const checksInTable: Record<string, string[]> = {};

const {
name: tableName,
columns,
indexes,
checks,
foreignKeys: tableForeignKeys,
primaryKeys,
uniqueConstraints,
Expand Down Expand Up @@ -245,13 +250,46 @@ export const generateSqliteSnapshot = (
}
});

checks.forEach((check) => {
const checkName = check.name;
if (typeof checksInTable[tableName] !== 'undefined') {
if (checksInTable[tableName].includes(check.name)) {
console.log(
`\n${
withStyle.errorWarning(
`We\'ve found duplicated check constraint name in ${
chalk.underline.blue(
tableName,
)
}. Please rename your check constraint in the ${
chalk.underline.blue(
tableName,
)
} table`,
)
}`,
);
process.exit(1);
}
checksInTable[tableName].push(checkName);
} else {
checksInTable[tableName] = [check.name];
}

checkConstraintObject[checkName] = {
name: checkName,
value: dialect.sqlToQuery(check.value).sql,
};
});

result[tableName] = {
name: tableName,
columns: columnsObject,
indexes: indexesObject,
foreignKeys: foreignKeysObject,
compositePrimaryKeys: primaryKeysObject,
uniqueConstraints: uniqueConstraintObject,
checkConstraints: checkConstraintObject,
};
}

Expand Down Expand Up @@ -500,6 +538,7 @@ export const fromDatabase = async (
indexes: {},
foreignKeys: {},
uniqueConstraints: {},
checkConstraints: {},
};
} else {
result[tableName]!.columns[columnName] = newColumn;
Expand Down Expand Up @@ -670,6 +709,56 @@ WHERE
progressCallback('enums', 0, 'done');
}

const namedCheckPattern = /CONSTRAINT\s*["']?(\w+)["']?\s*CHECK\s*\((.*?)\)/gi;
const unnamedCheckPattern = /CHECK\s*\((.*?)\)/gi;
let checkCounter = 0;
const checkConstraints: Record<string, CheckConstraint> = {};
const checks = await db.query<{ tableName: string; sql: string }>(`SELECT name as "tableName", sql as "sql"
FROM sqlite_master
WHERE type = 'table' AND name != 'sqlite_sequence';`);
for (const check of checks) {
if (!tablesFilter(check.tableName)) continue;

const { tableName, sql } = check;

// Find named CHECK constraints
let namedChecks = [...sql.matchAll(namedCheckPattern)];
if (namedChecks.length > 0) {
namedChecks.forEach(([_, checkName, checkValue]) => {
checkConstraints[checkName] = {
name: checkName,
value: checkValue.trim(),
};
});
} else {
// If no named constraints, find unnamed CHECK constraints and assign names
let unnamedChecks = [...sql.matchAll(unnamedCheckPattern)];
unnamedChecks.forEach(([_, checkValue]) => {
let checkName = `${tableName}_check_${++checkCounter}`;
checkConstraints[checkName] = {
name: checkName,
value: checkValue.trim(),
};
});
}

const table = result[tableName];

if (!table) {
result[tableName] = {
name: tableName,
columns: {},
compositePrimaryKeys: {},
indexes: {},
foreignKeys: {},
uniqueConstraints: {},
checkConstraints: checkConstraints,
};
} else {
result[tableName]!.checkConstraints = checkConstraints;
}
}

return {
version: '6',
dialect: 'sqlite',
Expand Down
Loading

0 comments on commit e3cc54e

Please sign in to comment.