fix: allow json as alias for longtext mariadb (#10018)

This commit is contained in:
smith-xyz 2023-05-09 05:54:52 -04:00 committed by GitHub
parent 54f4f8986a
commit 2a2bb4bdc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 2 deletions

View File

@ -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)
}
}

View File

@ -0,0 +1,10 @@
import { Column, Entity, PrimaryGeneratedColumn } from "../../../../src"
@Entity()
export class User {
@PrimaryGeneratedColumn("increment")
id?: number
@Column({ type: "json" })
jsonData: string
}

View File

@ -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`",
)
}
}),
))
})
})