Skip to content

Commit

Permalink
feat(mariadb): implement check constraint support + fix json column d…
Browse files Browse the repository at this point in the history
…iffing

Closes #2151
  • Loading branch information
B4nan committed Mar 27, 2022
1 parent c55e2f7 commit b513b16
Show file tree
Hide file tree
Showing 8 changed files with 566 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/knex/src/schema/DatabaseSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class DatabaseSchema {
table.comment = t.table_comment;
const cols = await platform.getSchemaHelper()!.getColumns(connection, table.name, table.schema);
const indexes = await platform.getSchemaHelper()!.getIndexes(connection, table.name, table.schema);
const checks = await platform.getSchemaHelper()!.getChecks(connection, table.name, table.schema);
const checks = await platform.getSchemaHelper()!.getChecks(connection, table.name, table.schema, cols);
const pks = await platform.getSchemaHelper()!.getPrimaryKeys(connection, indexes, table.name, table.schema);
const fks = await platform.getSchemaHelper()!.getForeignKeys(connection, table.name, table.schema);
const enums = await platform.getSchemaHelper()!.getEnumDefinitions(connection, checks, table.name, table.schema);
Expand Down
3 changes: 2 additions & 1 deletion packages/knex/src/schema/SchemaComparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@ export class SchemaComparator {
}

diffCheck(check1: Check, check2: Check): boolean {
return check1.expression !== check2.expression;
const unquote = (str?: string) => str?.replace(/['"`]/g, '');
return unquote(check1.expression as string) !== unquote(check2.expression as string);
}

hasSameDefaultValue(from: Column, to: Column): boolean {
Expand Down
2 changes: 1 addition & 1 deletion packages/knex/src/schema/SchemaHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export abstract class SchemaHelper {
throw new Error('Not supported by given driver');
}

async getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Check[]> {
async getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string, columns?: Column[]): Promise<Check[]> {
throw new Error('Not supported by given driver');
}

Expand Down
39 changes: 34 additions & 5 deletions packages/mariadb/src/MariaDbSchemaHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,40 @@ export class MariaDbSchemaHelper extends SchemaHelper {
+ `where k.table_name = '${tableName}' and k.table_schema = database() and c.constraint_schema = database() and k.referenced_column_name is not null`;
}

private getChecksSQL(tableName: string, _schemaName: string): string {
return `select tc.constraint_schema as table_schema, tc.table_name as table_name, tc.constraint_name as name, tc.check_clause as expression,
case when tc.level = 'Column' then tc.constraint_name else null end as column_name
from information_schema.check_constraints tc
where tc.table_name = '${tableName}' and tc.constraint_schema = database()`;
}

async getChecks(connection: AbstractSqlConnection, tableName: string, schemaName: string, columns?: Column[]): Promise<Check[]> {
const sql = this.getChecksSQL(tableName, schemaName);
const checks = await connection.execute<{ name: string; column_name: string; expression: string }[]>(sql);
const ret: Check[] = [];

for (const check of checks) {
const match = check.expression.match(/^json_valid\(`(.*)`\)$/i);
const col = columns?.find(col => col.name === match?.[1]);

if (col && match) {
col.type = 'json';
col.mappedType = this.platform.getMappedType('json');
delete col.length;
continue;
}

ret.push({
name: check.name,
columnName: check.column_name,
definition: `check (${check.expression})`,
expression: check.expression,
});
}

return ret;
}

async getEnumDefinitions(connection: AbstractSqlConnection, checks: Check[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>> {
const sql = `select column_name as column_name, column_type as column_type from information_schema.columns
where data_type = 'enum' and table_name = '${tableName}' and table_schema = database()`;
Expand Down Expand Up @@ -168,11 +202,6 @@ export class MariaDbSchemaHelper extends SchemaHelper {
})));
}

async getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Check[]> {
// @todo No support for CHECK constraints in current test MySQL version (minimum version is 8.0.16).
return [];
}

normalizeDefaultValue(defaultValue: string, length: number) {
return super.normalizeDefaultValue(defaultValue, length, MariaDbSchemaHelper.DEFAULT_VALUES);
}
Expand Down
19 changes: 19 additions & 0 deletions tests/features/schema-generator/SchemaGenerator.mysql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@ describe('SchemaGenerator', () => {
await orm.close(true);
});

test('generate schema from metadata [mariadb]', async () => {
const orm = await initORMMySql('mariadb', {}, true);
const generator = orm.getSchemaGenerator();
await generator.ensureDatabase();
const dump = await generator.generate();
expect(dump).toMatchSnapshot('mariadb-schema-dump');

const dropDump = await generator.getDropSchemaSQL();
expect(dropDump).toMatchSnapshot('mariadb-drop-schema-dump');

const createDump = await generator.getCreateSchemaSQL();
expect(createDump).toMatchSnapshot('mariadb-create-schema-dump');

const updateDump = await generator.getUpdateSchemaSQL();
expect(updateDump).toMatchSnapshot('mariadb-update-schema-dump');

await orm.close(true);
});

test('update schema [mysql]', async () => {
const orm = await initORMMySql('mysql', {}, true);
const meta = orm.getMetadata();
Expand Down
Loading

0 comments on commit b513b16

Please sign in to comment.