Skip to content

Commit

Permalink
fix: allow json as alias for longtext mariadb (#10018)
Browse files Browse the repository at this point in the history
  • Loading branch information
smith-xyz committed May 9, 2023
1 parent 54f4f89 commit 2a2bb4b
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 2 deletions.
27 changes: 25 additions & 2 deletions src/driver/mysql/MysqlDriver.ts
Expand Up @@ -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 (
Expand Down Expand Up @@ -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 &&
Expand Down Expand Up @@ -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)
}
}
10 changes: 10 additions & 0 deletions 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
}
83 changes: 83 additions & 0 deletions 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`",
)
}
}),
))
})
})

0 comments on commit 2a2bb4b

Please sign in to comment.