Skip to content

Commit

Permalink
feat: custom name for typeorm_metadata table (#8528)
Browse files Browse the repository at this point in the history
This new feature adds a new connection option `metadataTableName`
that allows for renaming of the `typeorm_metadata` table

Closes: #7266

Co-authored-by: Tadas Varanauskas <tadas@bitlocus.com>
  • Loading branch information
varanauskas and tadasbtl committed Jan 31, 2022
1 parent ece0da0 commit f8154eb
Show file tree
Hide file tree
Showing 13 changed files with 104 additions and 5 deletions.
3 changes: 3 additions & 0 deletions docs/connection-options.md
Expand Up @@ -95,6 +95,9 @@ By default this table is called "migrations".

* `migrationsTransactionMode` - Control transactions for migrations (default: `all`), can be one of `all` | `none` | `each`

* `metadataTableName` - Name of the table in the database which is going to contain information about table metadata.
By default this table is called "typeorm_metadata".

* `cache` - Enables entity result caching. You can also configure cache type and other cache options here.
Read more about caching [here](./caching.md).

Expand Down
1 change: 1 addition & 0 deletions docs/using-ormconfig.md
Expand Up @@ -134,6 +134,7 @@ List of available env variables you can set:
* TYPEORM_MIGRATIONS_DIR
* TYPEORM_MIGRATIONS_RUN
* TYPEORM_MIGRATIONS_TABLE_NAME
* TYPEORM_METADATA_TABLE_NAME
* TYPEORM_PASSWORD
* TYPEORM_PORT
* TYPEORM_SCHEMA
Expand Down
1 change: 1 addition & 0 deletions docs/zh_CN/using-ormconfig.md
Expand Up @@ -120,6 +120,7 @@ TYPEORM_ENTITIES = entity/.*js,modules/**/entity/.*js
- TYPEORM_MIGRATIONS_DIR
- TYPEORM_MIGRATIONS_RUN
- TYPEORM_MIGRATIONS_TABLE_NAME
- TYPEORM_METADATA_TABLE_NAME
- TYPEORM_PASSWORD
- TYPEORM_PORT
- TYPEORM_SCHEMA
Expand Down
6 changes: 6 additions & 0 deletions src/connection/BaseConnectionOptions.ts
Expand Up @@ -54,6 +54,12 @@ export interface BaseConnectionOptions {
*/
readonly migrationsTransactionMode?: "all" | "none" | "each";

/**
* Typeorm metadata table name, in case of different name from "typeorm_metadata".
* Accepts single string name.
*/
readonly metadataTableName?: string;

/**
* Naming strategy to be used to name tables and columns in the database.
*/
Expand Down
6 changes: 6 additions & 0 deletions src/connection/Connection.ts
Expand Up @@ -81,6 +81,11 @@ export class Connection {
*/
readonly namingStrategy: NamingStrategyInterface;

/**
* Name for the metadata table
*/
readonly metadataTableName: string;

/**
* Logger used to log orm events.
*/
Expand Down Expand Up @@ -122,6 +127,7 @@ export class Connection {
this.driver = new DriverFactory().create(this);
this.manager = this.createEntityManager();
this.namingStrategy = options.namingStrategy || new DefaultNamingStrategy();
this.metadataTableName = options.metadataTableName || "typeorm_metadata";
this.queryResultCache = options.cache ? new QueryResultCacheFactory(this).create() : undefined;
this.relationLoader = new RelationLoader(this);
this.isConnected = false;
Expand Down
Expand Up @@ -34,6 +34,7 @@ export class ConnectionOptionsEnvReader {
entities: this.stringToArray(PlatformTools.getEnvVariable("TYPEORM_ENTITIES")),
migrations: this.stringToArray(PlatformTools.getEnvVariable("TYPEORM_MIGRATIONS")),
migrationsTableName: PlatformTools.getEnvVariable("TYPEORM_MIGRATIONS_TABLE_NAME"),
metadataTableName: PlatformTools.getEnvVariable("TYPEORM_METADATA_TABLE_NAME"),
subscribers: this.stringToArray(PlatformTools.getEnvVariable("TYPEORM_SUBSCRIBERS")),
logging: this.transformLogging(PlatformTools.getEnvVariable("TYPEORM_LOGGING")),
logger: PlatformTools.getEnvVariable("TYPEORM_LOGGER"),
Expand Down
2 changes: 1 addition & 1 deletion src/driver/postgres/PostgresQueryRunner.ts
Expand Up @@ -1937,7 +1937,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
// In postgres there is no VIRTUAL generated column type
tableColumn.generatedType = "STORED";
// We cannot relay on information_schema.columns.generation_expression, because it is formatted different.
const asExpressionQuery = `SELECT * FROM "typeorm_metadata" `
const asExpressionQuery = `SELECT * FROM "${this.connection.metadataTableName}" `
+ ` WHERE "table" = '${dbTable["table_name"]}'`
+ ` AND "name" = '${tableColumn.name}'`
+ ` AND "schema" = '${dbTable["table_schema"]}'`
Expand Down
6 changes: 3 additions & 3 deletions src/query-runner/BaseQueryRunner.ts
Expand Up @@ -299,11 +299,11 @@ export abstract class BaseQueryRunner {

protected getTypeormMetadataTableName(): string {
const options = <SqlServerConnectionOptions|PostgresConnectionOptions>this.connection.driver.options;
return this.connection.driver.buildTableName("typeorm_metadata", options.schema, options.database);
return this.connection.driver.buildTableName(this.connection.metadataTableName, options.schema, options.database);
}

/**
* Generates SQL query to insert a record into "typeorm_metadata" table.
* Generates SQL query to insert a record into typeorm metadata table.
*/
protected insertTypeormMetadataSql({
database,
Expand All @@ -330,7 +330,7 @@ export abstract class BaseQueryRunner {
}

/**
* Generates SQL query to delete a record from "typeorm_metadata" table.
* Generates SQL query to delete a record from typeorm metadata table.
*/
protected deleteTypeormMetadataSql({
database,
Expand Down
2 changes: 1 addition & 1 deletion src/schema-builder/RdbmsSchemaBuilder.ts
Expand Up @@ -833,7 +833,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder {
protected async createTypeormMetadataTable(queryRunner: QueryRunner) {
const schema = this.currentSchema;
const database = this.currentDatabase;
const typeormMetadataTable = this.connection.driver.buildTableName("typeorm_metadata", schema, database);
const typeormMetadataTable = this.connection.driver.buildTableName(this.connection.metadataTableName, schema, database);

await queryRunner.createTable(new Table(
{
Expand Down
12 changes: 12 additions & 0 deletions test/github-issues/7266/entities/Foo.ts
@@ -0,0 +1,12 @@
import { PrimaryGeneratedColumn, UpdateDateColumn } from "../../../../src";

import { Entity } from "../../../../src/decorator/entity/Entity";

@Entity("foo")
export class Foo {
@PrimaryGeneratedColumn({ name: "id" })
id: number;

@UpdateDateColumn({ name: "updated_at" })
updatedAt: Date;
}
13 changes: 13 additions & 0 deletions test/github-issues/7266/entities/FooView.ts
@@ -0,0 +1,13 @@
import { Connection, ViewColumn, ViewEntity } from "../../../../src";

import { Foo } from "./Foo";

@ViewEntity({
name: "foo_view",
expression: (connection: Connection) =>
connection.createQueryBuilder(Foo, "foo").select(`foo.updatedAt`),
})
export class FooView {
@ViewColumn()
updatedAt: Date;
}
48 changes: 48 additions & 0 deletions test/github-issues/7266/issue-7266.ts
@@ -0,0 +1,48 @@
import "reflect-metadata";
import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils";
import { Connection } from "../../../src/connection/Connection";
import { expect } from "chai";
import { Foo } from "./entities/Foo";
import { FooView } from "./entities/FooView";

const customTypeormMetadataTableName = "custom_typeorm_metadata_table_name";

describe("github issues > #7266 rename table typeorm_metadata name.", () => {
let connections: Connection[];

before(async () => connections = await createTestingConnections({
entities: [Foo, FooView],
enabledDrivers: ["postgres", "mysql", "mariadb"],
metadataTableName: customTypeormMetadataTableName,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));

it("should create the typeorm metadata table with a custom name when provided", () => Promise.all(connections.map(async (connection) => {
const queryRunner = connection.createQueryRunner();

expect(connection.metadataTableName).to.equal(customTypeormMetadataTableName);

const hasCustomMetadataTableName =
await queryRunner.hasTable(customTypeormMetadataTableName);

expect(hasCustomMetadataTableName).to.be.true;

const hasDefaultMetadataTableName =
await queryRunner.hasTable("typeorm_metadata");

expect(hasDefaultMetadataTableName).to.be.false;

await queryRunner.release();
})));

it("should have correct views using the custom named metadata table", () => Promise.all(connections.map(async (connection) => {
const queryRunner = connection.createQueryRunner();

const fooView = await queryRunner.getView("foo_view");

expect(fooView).to.be.exist;

await queryRunner.release();
})));
});
8 changes: 8 additions & 0 deletions test/utils/test-utils.ts
Expand Up @@ -89,6 +89,12 @@ export interface TestingOptions {
*/
namingStrategy?: NamingStrategyInterface;

/**
* Typeorm metadata table name, in case of different name from "typeorm_metadata".
* Accepts single string name.
*/
metadataTableName?: string;

/**
* Schema name used for postgres driver.
*/
Expand Down Expand Up @@ -233,6 +239,8 @@ export function setupTestingConnections(options?: TestingOptions): ConnectionOpt
newOptions.migrations = [options.__dirname + "/migration/*{.js,.ts}"];
if (options && options.namingStrategy)
newOptions.namingStrategy = options.namingStrategy;
if (options && options.metadataTableName)
newOptions.metadataTableName = options.metadataTableName;
return newOptions;
});
}
Expand Down

0 comments on commit f8154eb

Please sign in to comment.