feat: add support for table comment in MySQL (#10017)

* feat: add table comment

* feat: resolve conflict

* feat: del auroraMysql comment

* feat: add changeTableComment method for MySQL

* feat: QueryRunner subclass add  changeTableComment Implementation

* oracle implement changeTableComment

* add test

* feat: loadTables support comment

* feat: rename

* feat: update mysql changeTableComment

* fix: fix conflict

* feat: add table comment

* feat: resolve conflict

* feat: del auroraMysql comment

* feat: add changeTableComment method for MySQL

* feat: QueryRunner subclass add  changeTableComment Implementation

* oracle implement changeTableComment

* add test

* feat: loadTables support comment

* feat: rename

* feat: update mysql changeTableComment

---------

Co-authored-by: sinkhaha <1468709606@qq.com>
This commit is contained in:
sinkhaha 2024-01-03 16:49:33 +08:00 committed by GitHub
parent 15bc8876f8
commit 338df16439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 329 additions and 3 deletions

View File

@ -41,6 +41,7 @@ export function Entity(
schema: options.schema ? options.schema : undefined,
synchronize: options.synchronize,
withoutRowid: options.withoutRowid,
comment: options.comment ? options.comment : undefined,
} as TableMetadataArgs)
}
}

View File

@ -46,4 +46,9 @@ export interface EntityOptions {
* @see https://www.sqlite.org/withoutrowid.html.
*/
withoutRowid?: boolean
/**
* Table comment. Not supported by all database types.
*/
comment?: string
}

View File

@ -2822,4 +2822,16 @@ export class AuroraMysqlQueryRunner
return false
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`aurora-mysql driver does not support change table comment.`,
)
}
}

View File

@ -6,6 +6,8 @@ import { AuroraPostgresDriver } from "./AuroraPostgresDriver"
import { PostgresQueryRunner } from "../postgres/PostgresQueryRunner"
import { ReplicationMode } from "../types/ReplicationMode"
import { QueryResult } from "../../query-runner/QueryResult"
import { Table } from "../../schema-builder/table/Table"
import { TypeORMError } from "../../error"
class PostgresQueryRunnerWrapper extends PostgresQueryRunner {
driver: any
@ -194,4 +196,16 @@ export class AuroraPostgresQueryRunner
return result
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`aurora-postgres driver does not support change comment.`,
)
}
}

View File

@ -4211,4 +4211,15 @@ export class CockroachQueryRunner
return c
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`cockroachdb driver does not support change table comment.`,
)
}
}

View File

@ -1263,4 +1263,16 @@ export class MongoQueryRunner implements QueryRunner {
.db(this.connection.driver.database!)
.collection(collectionName)
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`mongodb driver does not support change table comment.`,
)
}
}

View File

@ -746,6 +746,49 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
this.replaceCachedTable(oldTable, newTable)
}
/**
* Change table comment.
*/
async changeTableComment(
tableOrName: Table | string,
newComment?: string,
): Promise<void> {
const upQueries: Query[] = []
const downQueries: Query[] = []
const table = InstanceChecker.isTable(tableOrName)
? tableOrName
: await this.getCachedTable(tableOrName)
newComment = this.escapeComment(newComment)
const comment = this.escapeComment(table.comment)
if (newComment === comment) {
return
}
const newTable = table.clone()
upQueries.push(
new Query(
`ALTER TABLE ${this.escapePath(
newTable,
)} COMMENT ${newComment}`,
),
)
downQueries.push(
new Query(
`ALTER TABLE ${this.escapePath(table)} COMMENT ${comment}`,
),
)
await this.executeQueries(upQueries, downQueries)
// change table comment and replace it in cached tabled;
table.comment = newTable.comment
this.replaceCachedTable(table, newTable)
}
/**
* Creates a new column from the column in the table.
*/
@ -2346,11 +2389,15 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
// will cause the query to not hit the optimizations & do full scans. This is why
// a number of queries below do `UNION`s of single `WHERE` clauses.
const dbTables: { TABLE_SCHEMA: string; TABLE_NAME: string }[] = []
const dbTables: {
TABLE_SCHEMA: string
TABLE_NAME: string
TABLE_COMMENT: string
}[] = []
if (!tableNames) {
// Since we don't have any of this data we have to do a scan
const tablesSql = `SELECT \`TABLE_SCHEMA\`, \`TABLE_NAME\` FROM \`INFORMATION_SCHEMA\`.\`TABLES\``
const tablesSql = `SELECT \`TABLE_SCHEMA\`, \`TABLE_NAME\`, \`TABLE_COMMENT\` FROM \`INFORMATION_SCHEMA\`.\`TABLES\``
dbTables.push(...(await this.query(tablesSql)))
} else {
@ -2367,7 +2414,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
database = currentDatabase
}
return `SELECT \`TABLE_SCHEMA\`, \`TABLE_NAME\` FROM \`INFORMATION_SCHEMA\`.\`TABLES\` WHERE \`TABLE_SCHEMA\` = '${database}' AND \`TABLE_NAME\` = '${name}'`
return `SELECT \`TABLE_SCHEMA\`, \`TABLE_NAME\`, \`TABLE_COMMENT\` FROM \`INFORMATION_SCHEMA\`.\`TABLES\` WHERE \`TABLE_SCHEMA\` = '${database}' AND \`TABLE_NAME\` = '${name}'`
})
.join(" UNION ")
@ -2925,6 +2972,8 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
})
})
table.comment = dbTable["TABLE_COMMENT"]
return table
}),
)
@ -3058,6 +3107,10 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
sql += `) ENGINE=${table.engine || "InnoDB"}`
if (table.comment) {
sql += ` COMMENT="${table.comment}"`
}
return new Query(sql)
}

View File

@ -3181,4 +3181,16 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
return `"${tableName}"`
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`oracle driver does not support change table comment.`,
)
}
}

View File

@ -4717,4 +4717,16 @@ export class PostgresQueryRunner
)
return result.length ? true : false
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`postgres driver does not support change table comment.`,
)
}
}

View File

@ -3354,4 +3354,16 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner {
return c
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`spa driver does not support change table comment.`,
)
}
}

View File

@ -2227,4 +2227,16 @@ export class SpannerQueryRunner extends BaseQueryRunner implements QueryRunner {
query.startsWith("DELETE")
)
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`spanner driver does not support change table comment.`,
)
}
}

View File

@ -2242,4 +2242,14 @@ export abstract class AbstractSqliteQueryRunner
.map((i) => (disableEscape ? i : `"${i}"`))
.join(".")
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(`sqlit driver does not support change comment.`)
}
}

View File

@ -4161,4 +4161,16 @@ export class SqlServerQueryRunner
return ISOLATION_LEVEL.READ_COMMITTED
}
}
/**
* Change table comment.
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void> {
throw new TypeORMError(
`sqlserver driver does not support change table comment.`,
)
}
}

View File

@ -70,4 +70,9 @@ export interface TableMetadataArgs {
* an integer primary key column named 'rowid' on table creation.
*/
withoutRowid?: boolean
/**
* Table comment. Not supported by all database types.
*/
comment?: string
}

View File

@ -516,6 +516,11 @@ export class EntityMetadata {
*/
propertiesMap: ObjectLiteral
/**
* Table comment. Not supported by all database types.
*/
comment?: string
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
@ -1065,6 +1070,8 @@ export class EntityMetadata {
this.tableMetadataArgs.type === "junction"
this.isClosureJunction =
this.tableMetadataArgs.type === "closure-junction"
this.comment = this.tableMetadataArgs.comment
}
/**

View File

@ -320,6 +320,7 @@ export abstract class BaseQueryRunner {
foundTable.checks = changedTable.checks
foundTable.justCreated = changedTable.justCreated
foundTable.engine = changedTable.engine
foundTable.comment = changedTable.comment
}
}

View File

@ -271,6 +271,14 @@ export interface QueryRunner {
newTableName: string,
): Promise<void>
/**
* Change table comment. Only supports MySQL and MariaDB
*/
changeTableComment(
tableOrName: Table | string,
comment?: string,
): Promise<void>
/**
* Adds a new column.
*/

View File

@ -221,6 +221,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder {
await this.dropCompositeUniqueConstraints()
// await this.renameTables();
await this.renameColumns()
await this.changeTableComment()
await this.createNewTables()
await this.dropRemovedColumns()
await this.addNewColumns()
@ -590,6 +591,24 @@ export class RdbmsSchemaBuilder implements SchemaBuilder {
}
}
/**
* change table comment
*/
protected async changeTableComment(): Promise<void> {
for (const metadata of this.entityToSyncMetadatas) {
const table = this.queryRunner.loadedTables.find(
(table) =>
this.getTablePath(table) === this.getTablePath(metadata),
)
if (!table) continue
if (DriverUtils.isMySQLFamily(this.connection.driver)) {
const newComment = metadata.comment
await this.queryRunner.changeTableComment(table, newComment)
}
}
}
/**
* Creates tables that do not exist in the database yet.
* New tables are created without foreign and primary keys.

View File

@ -74,4 +74,9 @@ export interface TableOptions {
* Table engine.
*/
engine?: string
/**
* Table comment. Not supported by all database types.
*/
comment?: string
}

View File

@ -83,6 +83,11 @@ export class Table {
*/
engine?: string
/**
* Table comment. Not supported by all database types.
*/
comment?: string
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
@ -137,6 +142,8 @@ export class Table {
if (options.withoutRowid) this.withoutRowid = options.withoutRowid
this.engine = options.engine
this.comment = options.comment
}
}
@ -171,6 +178,7 @@ export class Table {
justCreated: this.justCreated,
withoutRowid: this.withoutRowid,
engine: this.engine,
comment: this.comment,
})
}
@ -411,6 +419,7 @@ export class Table {
exclusions: entityMetadata.exclusions.map((exclusion) =>
TableExclusion.create(exclusion),
),
comment: entityMetadata.comment,
}
return new Table(options)

View File

@ -0,0 +1,8 @@
import { Entity } from "../../../../src/decorator/entity/Entity"
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"
@Entity({ name: "example", comment: "table comment" })
export class ExampleEntity {
@PrimaryGeneratedColumn()
id: number
}

View File

@ -0,0 +1,86 @@
import { DataSource } from "../../../src"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { ExampleEntity } from "./entity/ExampleEntity"
import { expect } from "chai"
describe("github issues > #9991", () => {
let dataSources: DataSource[]
before(async () => {
dataSources = await createTestingConnections({
entities: [ExampleEntity],
enabledDrivers: ["mysql", "mariadb"],
})
})
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
it("add table comment", async () => {
await Promise.all(
dataSources.map(async (dataSource) => {
const sql =
'SELECT `TABLE_COMMENT` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE TABLE_NAME = "example"'
const result = await dataSource.manager.query(sql)
expect(result).to.be.eql([{ TABLE_COMMENT: "table comment" }])
}),
)
})
it("should correctly change table comment and change", async () => {
await Promise.all(
dataSources.map(async (dataSource) => {
const queryRunner = dataSource.createQueryRunner()
let table = await queryRunner.getTable("example")
await queryRunner.changeTableComment(
table!,
"new table comment",
)
const sql =
'SELECT `TABLE_COMMENT` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE TABLE_NAME = "example"'
let result = await dataSource.manager.query(sql)
expect(result).to.be.eql([
{ TABLE_COMMENT: "new table comment" },
])
// revert changes
await queryRunner.executeMemoryDownSql()
result = await dataSource.manager.query(sql)
expect(result).to.be.eql([{ TABLE_COMMENT: "table comment" }])
await queryRunner.release()
}),
)
})
it("should correctly synchronize when table comment change", async () => {
await Promise.all(
dataSources.map(async (dataSource) => {
const queryRunner = dataSource.createQueryRunner()
const exampleMetadata = dataSource.getMetadata(ExampleEntity)
exampleMetadata.comment = "change table comment"
await dataSource.synchronize()
const sql =
'SELECT `TABLE_COMMENT` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE TABLE_NAME = "example"'
const result = await dataSource.manager.query(sql)
expect(result).to.be.eql([
{ TABLE_COMMENT: "change table comment" },
])
await queryRunner.release()
}),
)
})
})