diff --git a/src/driver/postgres/PostgresQueryRunner.ts b/src/driver/postgres/PostgresQueryRunner.ts index 47e8b1110..50d64e34c 100644 --- a/src/driver/postgres/PostgresQueryRunner.ts +++ b/src/driver/postgres/PostgresQueryRunner.ts @@ -601,6 +601,11 @@ export class PostgresQueryRunner downQueries.push(this.dropIndexSql(table, index)) }) } + + if (table.comment) { + upQueries.push(new Query("COMMENT ON TABLE " + this.escapePath(table) + " IS '" + table.comment + "'")); + downQueries.push(new Query("COMMENT ON TABLE " + this.escapePath(table) + " IS NULL")); + } await this.executeQueries(upQueries, downQueries) } @@ -3276,10 +3281,14 @@ export class PostgresQueryRunner const currentSchema = await this.getCurrentSchema() const currentDatabase = await this.getCurrentDatabase() - const dbTables: { table_schema: string; table_name: string }[] = [] + const dbTables: { + table_schema: string + table_name: string + table_comment: string + }[] = [] if (!tableNames) { - const tablesSql = `SELECT "table_schema", "table_name" FROM "information_schema"."tables"` + const tablesSql = `SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables"` dbTables.push(...(await this.query(tablesSql))) } else { const tablesCondition = tableNames @@ -3292,7 +3301,7 @@ export class PostgresQueryRunner .join(" OR ") const tablesSql = - `SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE ` + + `SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ` + tablesCondition dbTables.push(...(await this.query(tablesSql))) } @@ -3416,6 +3425,7 @@ export class PostgresQueryRunner const schema = getSchemaFromKey(dbTable, "table_schema") table.database = currentDatabase table.schema = dbTable["table_schema"] + table.comment = dbTable["table_comment"] table.name = this.driver.buildTableName( dbTable["table_name"], schema, @@ -4318,7 +4328,7 @@ export class PostgresQueryRunner table: Table | View, indexOrName: TableIndex | string, ): Query { - let indexName = InstanceChecker.isTableIndex(indexOrName) + const indexName = InstanceChecker.isTableIndex(indexOrName) ? indexOrName.name : indexOrName const concurrent = InstanceChecker.isTableIndex(indexOrName) @@ -4721,12 +4731,43 @@ export class PostgresQueryRunner /** * Change table comment. */ - changeTableComment( + async changeTableComment( tableOrName: Table | string, - comment?: string, + newComment?: string, ): Promise { - throw new TypeORMError( - `postgres driver does not support change table comment.`, + 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( + `COMMENT ON TABLE ${this.escapePath( + newTable, + )} IS ${newComment}`, + ), ) + + downQueries.push( + new Query( + `COMMENT ON TABLE ${this.escapePath(table)} IS ${comment}`, + ), + ) + + await this.executeQueries(upQueries, downQueries) + + table.comment = newTable.comment + this.replaceCachedTable(table, newTable) } } diff --git a/src/schema-builder/RdbmsSchemaBuilder.ts b/src/schema-builder/RdbmsSchemaBuilder.ts index 90eee092a..9b22d9535 100644 --- a/src/schema-builder/RdbmsSchemaBuilder.ts +++ b/src/schema-builder/RdbmsSchemaBuilder.ts @@ -602,7 +602,10 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ) if (!table) continue - if (DriverUtils.isMySQLFamily(this.connection.driver)) { + if ( + DriverUtils.isMySQLFamily(this.connection.driver) || + this.connection.driver.options.type === 'postgres' + ) { const newComment = metadata.comment await this.queryRunner.changeTableComment(table, newComment) } diff --git a/test/github-issues/10612/entity/ExampleEntity.ts b/test/github-issues/10612/entity/ExampleEntity.ts new file mode 100644 index 000000000..9ce5ae839 --- /dev/null +++ b/test/github-issues/10612/entity/ExampleEntity.ts @@ -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 +} diff --git a/test/github-issues/10612/issue-10612.ts b/test/github-issues/10612/issue-10612.ts new file mode 100644 index 000000000..a44d1ed68 --- /dev/null +++ b/test/github-issues/10612/issue-10612.ts @@ -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 > #10612", () => { + let dataSources: DataSource[] + + before(async () => { + dataSources = await createTestingConnections({ + entities: [ExampleEntity], + enabledDrivers: ["postgres"], + }) + }) + + beforeEach(() => reloadTestingDatabases(dataSources)) + after(() => closeTestingConnections(dataSources)) + + it("add table comment", async () => { + await Promise.all( + dataSources.map(async (dataSource) => { + const sql = + "SELECT obj_description('example'::regclass::oid, 'pg_class') AS table_comment" + + 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() + const table = await queryRunner.getTable("example") + + await queryRunner.changeTableComment( + table!, + "new table comment", + ) + const sql = + "SELECT obj_description('example'::regclass::oid, 'pg_class') AS table_comment" + + 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 obj_description('example'::regclass::oid, 'pg_class') AS table_comment" + + const result = await dataSource.manager.query(sql) + expect(result).to.be.eql([ + { table_comment: "change table comment" }, + ]) + + await queryRunner.release() + }), + ) + }) +})