mikro-orm/mikro-orm

View on GitHub
packages/knex/src/dialects/postgresql/PostgreSqlTableCompiler.ts

Summary

Maintainability
A
45 mins
Test Coverage
// @ts-ignore
import PostgresDialectTableCompiler from 'knex/lib/dialects/postgres/schema/pg-tablecompiler';
// @ts-ignore
import TableCompiler from 'knex/lib/schema/tablecompiler';
import type { Configuration, Dictionary } from '@mikro-orm/core';

export class PostgreSqlTableCompiler extends PostgresDialectTableCompiler {

  ormConfig!: Configuration;
  declare alterColumnsPrefix: string;

  addColumns(columns: Dictionary[], prefix: string, colCompilers: Dictionary[]) {
    if (prefix !== this.alterColumnsPrefix) {
      // base class implementation for normal add
      return TableCompiler.prototype.addColumns.call(this, columns, prefix);
    }

    // alter columns
    for (const col of colCompilers) {
      this.addColumn(col);
    }
  }

  private addColumn(this: any, col: Dictionary): void {
    const options = this.ormConfig.get('schemaGenerator');
    const quotedTableName = this.tableName();
    const type = col.getColumnType();
    const colName = this.client.wrapIdentifier(col.getColumnName(), col.columnBuilder.queryContext());
    const constraintName = `${this.tableNameRaw.replace(/^.*\.(.*)$/, '$1')}_${col.getColumnName()}_check`;
    const useNative = col.args?.[2]?.useNative;
    const alterType = col.columnBuilder.alterType;
    const alterNullable = col.columnBuilder.alterNullable;
    const defaultTo = col.modified.defaultTo;

    if (defaultTo != null) {
      this.dropColumnDefault(col, colName);
    }

    /* istanbul ignore next */
    if (col.type === 'enu' && !useNative) {
      if (alterType) {
        this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} type text using (${colName}::text)`, bindings: [] });
      }

      /* istanbul ignore else */
      if (options.createForeignKeyConstraints && alterNullable) {
        this.pushQuery({ sql: `alter table ${quotedTableName} add constraint "${constraintName}" ${type.replace(/^text /, '')}`, bindings: [] });
      }
    } else if (type === 'uuid') {
      // we need to drop the default as it would be invalid
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} drop default`, bindings: [] });
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} type ${type} using (${colName}::text::uuid)`, bindings: [] });
    } else if (alterType) {
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} type ${type} using (${colName}::${type})`, bindings: [] });
    }

    this.addColumnDefault(col, colName);
    this.alterColumnNullable(col, colName);
  }

  private alterColumnNullable(this: any, col: Dictionary, colName: string): void {
    const quotedTableName = this.tableName();
    const nullable = col.modified.nullable;

    if (!nullable) {
      return;
    }

    if (nullable[0] === false) {
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} set not null`, bindings: [] });
    } else {
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} drop not null`, bindings: [] });
    }
  }

  private addColumnDefault(this: any, col: Dictionary, colName: string): void {
    const quotedTableName = this.tableName();
    const defaultTo = col.modified.defaultTo;

    if (!defaultTo) {
      return;
    }

    if (defaultTo[0] !== null) {
      const modifier = col.defaultTo(...defaultTo);
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} set ${modifier}`, bindings: [] });
    }
  }

  private dropColumnDefault(this: any, col: Dictionary, colName: string): void {
    const quotedTableName = this.tableName();
    const defaultTo = col.modified.defaultTo;

    if (defaultTo?.[0] == null) {
      this.pushQuery({ sql: `alter table ${quotedTableName} alter column ${colName} drop default`, bindings: [] });
    }
  }

}