From 6e28322ca65ba739bf0d767075016bc0cae7a48c Mon Sep 17 00:00:00 2001 From: jesussegado Date: Thu, 17 Sep 2020 13:12:51 +0200 Subject: [PATCH] fix: migration:generate issue with onUpdate using mariadb 10.4 (#6714) * Update MysqlQueryRunner.ts In Mariadb the extra information of a DDL is in upper case and in javascript String.indexOf() function is case sensitive, because of that when you generate a new migrations in mariadb it always create a line to update "onUpdate" lines. example: ```sql CREATE TABLE `test` ( `test_id` int(11) NOT NULL AUTO_INCREMENT, `test_update` timestamp() NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), PRIMARY KEY (`test_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ``` When you try to generate a new migration always create the next migration file: ```ts import { MigrationInterface, QueryRunner } from 'typeorm'; export class test261600082802966 implements MigrationInterface { name = 'test261600082802966'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( 'ALTER TABLE `test` CHANGE `test_update` `test_update` timestamp() NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()' ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( 'ALTER TABLE `test` CHANGE `test_update` `test_update` timestamp() NOT NULL DEFAULT CURRENT_TIMESTAMP() ON UPDATE current_timestamp()' ); } } ``` Entity file test.ts ```ts import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class test extends BaseEntity { @PrimaryGeneratedColumn({}) test_id: number; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP()', onUpdate: 'CURRENT_TIMESTAMP()', nullable: false, }) test_update: Date; } ``` * Update MysqlQueryRunner.ts * add test to issue 6714 * Update MysqlQueryRunner.ts * Update issue-6714.ts Co-authored-by: jesusegado --- src/driver/mysql/MysqlQueryRunner.ts | 4 +- test/github-issues/6714/entity/session.ts | 17 ++++++ .../6714/entity/sessionchanged.ts | 15 +++++ test/github-issues/6714/issue-6714.ts | 59 +++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/github-issues/6714/entity/session.ts create mode 100644 test/github-issues/6714/entity/sessionchanged.ts create mode 100644 test/github-issues/6714/issue-6714.ts diff --git a/src/driver/mysql/MysqlQueryRunner.ts b/src/driver/mysql/MysqlQueryRunner.ts index f595487f91..5a7cdb1875 100644 --- a/src/driver/mysql/MysqlQueryRunner.ts +++ b/src/driver/mysql/MysqlQueryRunner.ts @@ -1321,7 +1321,9 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { } if (dbColumn["EXTRA"].indexOf("on update") !== -1) { - tableColumn.onUpdate = dbColumn["EXTRA"].substring(dbColumn["EXTRA"].indexOf("on update") + 10); + // New versions of MariaDB return expressions in lowercase. We need to set it in + // uppercase so the comparison in MysqlDriver#compareExtraValues does not fail. + tableColumn.onUpdate = dbColumn["EXTRA"].substring(dbColumn["EXTRA"].indexOf("on update") + 10).toUpperCase(); } if (dbColumn["GENERATION_EXPRESSION"]) { diff --git a/test/github-issues/6714/entity/session.ts b/test/github-issues/6714/entity/session.ts new file mode 100644 index 0000000000..9ea05da3ac --- /dev/null +++ b/test/github-issues/6714/entity/session.ts @@ -0,0 +1,17 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "../../../../src"; + +@Entity({ name: "Session" }) +export class Session { + + @PrimaryGeneratedColumn() + id?: number; + + @Column({ + type: "timestamp", + precision: 3, + default: () => "CURRENT_TIMESTAMP(3)", + onUpdate: "CURRENT_TIMESTAMP(3)", + }) + ts: Date; + +} diff --git a/test/github-issues/6714/entity/sessionchanged.ts b/test/github-issues/6714/entity/sessionchanged.ts new file mode 100644 index 0000000000..c689295e84 --- /dev/null +++ b/test/github-issues/6714/entity/sessionchanged.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "../../../../src"; + +@Entity({ name: "Session" }) +export class Session { + @PrimaryGeneratedColumn() + id?: number; + + @Column({ + type: "timestamp", + precision: 4, + default: () => "CURRENT_TIMESTAMP(4)", + onUpdate: "CURRENT_TIMESTAMP(4)", + }) + ts: Date; +} diff --git a/test/github-issues/6714/issue-6714.ts b/test/github-issues/6714/issue-6714.ts new file mode 100644 index 0000000000..e51fd4a718 --- /dev/null +++ b/test/github-issues/6714/issue-6714.ts @@ -0,0 +1,59 @@ +import "reflect-metadata"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { Session as baseEntity } from "./entity/session"; +import { Session as changedEntity } from "./entity/sessionchanged"; + +describe("github issues > #6714 Migration:generate issue with onUpdate using mariadb 10.4", () => { + it("dont change anything", async () => { + let connections: Connection[]; + connections = await createTestingConnections({ + entities: [baseEntity], + schemaCreate: false, + dropSchema: true, + enabledDrivers: ["mariadb"], + }); + await reloadTestingDatabases(connections); + await Promise.all( + connections.map(async (connection) => { + const schemaBuilder = connection.driver.createSchemaBuilder(); + const syncQueries = await schemaBuilder.log(); + expect(syncQueries.downQueries).to.be.eql([]); + expect(syncQueries.upQueries).to.be.eql([]); + }) + ); + await closeTestingConnections(connections); + }); + it("recognizing on update changes", async () => { + // this connection create database with a Session entity + const baseConnections = await createTestingConnections({ + entities: [baseEntity], + schemaCreate: true, // create the database + dropSchema: true, + enabledDrivers: ["mariadb"], + }); + // this connection change Session entity on update value + const connections = await createTestingConnections({ + entities: [changedEntity], + schemaCreate: false, // don't change the entity + dropSchema: false, + enabledDrivers: ["mariadb"], + name: "test", + }); + await Promise.all( + connections.map(async (connection) => { + const schemaBuilder = connection.driver.createSchemaBuilder(); + const syncQueries = await schemaBuilder.log(); + expect(syncQueries.downQueries.length).not.to.be.eql(0); + expect(syncQueries.upQueries.length).not.to.be.eql(0); + }) + ); + await closeTestingConnections(baseConnections); + await closeTestingConnections(connections); + }); +});