diff --git a/src/driver/mysql/MysqlDriver.ts b/src/driver/mysql/MysqlDriver.ts index 144398bab6..a890fd9a3e 100644 --- a/src/driver/mysql/MysqlDriver.ts +++ b/src/driver/mysql/MysqlDriver.ts @@ -732,12 +732,17 @@ export class MysqlDriver implements Driver { return "tinyint" } else if (column.type === "uuid" && !this.uuidColumnTypeSuported) { return "varchar" - } else if (column.type === "json" && this.options.type === "mariadb") { + } else if ( + column.type === "json" && + this.options.type === "mariadb" && + !VersionUtils.isGreaterOrEqual(this.version ?? "0.0.0", "10.4.3") + ) { /* * MariaDB implements this as a LONGTEXT rather, as the JSON data type contradicts the SQL standard, * and MariaDB's benchmarks indicate that performance is at least equivalent. * * @see https://mariadb.com/kb/en/json-data-type/ + * if Version is 10.4.3 or greater, JSON is an alias for longtext and an automatic check_json(column) constraint is added */ return "longtext" } else if ( @@ -999,7 +1004,7 @@ export class MysqlDriver implements Driver { const isColumnChanged = tableColumn.name !== columnMetadata.databaseName || - tableColumn.type !== this.normalizeType(columnMetadata) || + this.isColumnDataTypeChanged(tableColumn, columnMetadata) || tableColumn.length !== this.getColumnLength(columnMetadata) || tableColumn.width !== columnMetadata.width || (columnMetadata.precision !== undefined && @@ -1358,4 +1363,22 @@ export class MysqlDriver implements Driver { return comment } + + /** + * A helper to check if column data types have changed + * This can be used to manage checking any types the + * database may alias + */ + private isColumnDataTypeChanged( + tableColumn: TableColumn, + columnMetadata: ColumnMetadata, + ) { + // this is an exception for mariadb versions where json is an alias for longtext + if ( + this.normalizeType(columnMetadata) === "json" && + tableColumn.type.toLowerCase() === "longtext" + ) + return false + return tableColumn.type !== this.normalizeType(columnMetadata) + } } diff --git a/test/github-issues/9903/entity/User.ts b/test/github-issues/9903/entity/User.ts new file mode 100644 index 0000000000..b51c04f946 --- /dev/null +++ b/test/github-issues/9903/entity/User.ts @@ -0,0 +1,10 @@ +import { Column, Entity, PrimaryGeneratedColumn } from "../../../../src" + +@Entity() +export class User { + @PrimaryGeneratedColumn("increment") + id?: number + + @Column({ type: "json" }) + jsonData: string +} diff --git a/test/github-issues/9903/issue-9903.ts b/test/github-issues/9903/issue-9903.ts new file mode 100644 index 0000000000..061f92bc22 --- /dev/null +++ b/test/github-issues/9903/issue-9903.ts @@ -0,0 +1,83 @@ +import "../../utils/test-setup" +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils" +import { DataSource } from "../../../src/index" +import { expect } from "chai" +import { User } from "./entity/User" + +describe("github issues > #9903 json data type", () => { + let connections: DataSource[] + + afterEach(() => closeTestingConnections(connections)) + + describe("json supported type for mariadb", () => { + const expectedJsonString = JSON.stringify({ + firstName: "Quality", + lastName: "Tester", + }) + const newUser: User = { + jsonData: expectedJsonString, + } + + const badJsonUser: User = { + jsonData: `'''faux---'''`, + } + + before( + async () => + (connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["mariadb"], + })), + ) + beforeEach(() => reloadTestingDatabases(connections)) + + it("should create table with json constraint", () => + Promise.all( + connections.map(async (connection) => { + const userRepository = connection.getRepository(User) + + await userRepository.save(newUser) + + const savedUser = await userRepository.findOneOrFail({ + where: { id: newUser.id }, + }) + + expect(savedUser).to.not.be.null + expect(savedUser.jsonData).to.equal(expectedJsonString) + + // trying to save bad json + // here when executing the save the value is passed to JSON.stringify(), + // this will ensure its json valid in mariadb so this won't break the constraint + try { + await userRepository.save(badJsonUser) + } catch (err) { + expect.fail( + null, + null, + "Should have not thrown an error", + ) + } + + try { + await userRepository.query( + "INSERT INTO user values (?, ?)", + [3, badJsonUser.jsonData], + ) + expect.fail(null, null, "Should have thrown an error") + } catch (err) { + expect(err).not.to.be.undefined + expect(err.sqlMessage).not.to.be.undefined + expect(err.sqlMessage).to.equal( + "CONSTRAINT `user.jsonData` failed for `test`.`user`", + ) + } + }), + )) + }) +})