import { RawQueryFragment, Utils } from '@mikro-orm/core';
/** Base class for database-specific schema helpers. Provides SQL generation for DDL operations. */
export class SchemaHelper {
  platform;
  constructor(platform) {
    this.platform = platform;
  }
  /** Returns SQL to prepend to schema migration scripts (e.g., disabling FK checks). */
  getSchemaBeginning(_charset, disableForeignKeys) {
    if (disableForeignKeys) {
      return `${this.disableForeignKeysSQL()}\n`;
    }
    return '';
  }
  /** Returns SQL to disable foreign key checks. */
  disableForeignKeysSQL() {
    return '';
  }
  /** Returns SQL to re-enable foreign key checks. */
  enableForeignKeysSQL() {
    return '';
  }
  /** Returns SQL to append to schema migration scripts (e.g., re-enabling FK checks). */
  getSchemaEnd(disableForeignKeys) {
    if (disableForeignKeys) {
      return `${this.enableForeignKeysSQL()}\n`;
    }
    return '';
  }
  finalizeTable(table, charset, collate) {
    return '';
  }
  appendComments(table) {
    return [];
  }
  supportsSchemaConstraints() {
    return true;
  }
  async getPrimaryKeys(connection, indexes = [], tableName, schemaName) {
    const pks = indexes.filter(i => i.primary).map(pk => pk.columnNames);
    return Utils.flatten(pks);
  }
  inferLengthFromColumnType(type) {
    const match = /^\w+\s*(?:\(\s*(\d+)\s*\)|$)/.exec(type);
    if (!match) {
      return;
    }
    return +match[1];
  }
  getTableKey(t) {
    const unquote = str => str.replace(/['"`]/g, '');
    const parts = t.table_name.split('.');
    if (parts.length > 1) {
      return `${unquote(parts[0])}.${unquote(parts[1])}`;
    }
    if (t.schema_name) {
      return `${unquote(t.schema_name)}.${unquote(t.table_name)}`;
    }
    return unquote(t.table_name);
  }
  getCreateNativeEnumSQL(name, values, schema) {
    throw new Error('Not supported by given driver');
  }
  getDropNativeEnumSQL(name, schema) {
    throw new Error('Not supported by given driver');
  }
  getAlterNativeEnumSQL(name, schema, value, items, oldItems) {
    throw new Error('Not supported by given driver');
  }
  /** Returns the SQL query to list all tables in the database. */
  getListTablesSQL() {
    throw new Error('Not supported by given driver');
  }
  /** Retrieves all tables from the database. */
  async getAllTables(connection, schemas) {
    return connection.execute(this.getListTablesSQL());
  }
  getListViewsSQL() {
    throw new Error('Not supported by given driver');
  }
  async loadViews(schema, connection, schemaName) {
    throw new Error('Not supported by given driver');
  }
  /** Returns SQL to rename a column in a table. */
  getRenameColumnSQL(tableName, oldColumnName, to, schemaName) {
    tableName = this.quote(tableName);
    oldColumnName = this.quote(oldColumnName);
    const columnName = this.quote(to.name);
    const schemaReference = schemaName !== undefined && schemaName !== 'public' ? '"' + schemaName + '".' : '';
    const tableReference = schemaReference + tableName;
    return `alter table ${tableReference} rename column ${oldColumnName} to ${columnName}`;
  }
  /** Returns SQL to create an index on a table. */
  getCreateIndexSQL(tableName, index) {
    /* v8 ignore next */
    if (index.expression) {
      return index.expression;
    }
    if (index.fillFactor != null && (index.fillFactor < 0 || index.fillFactor > 100)) {
      throw new Error(`fillFactor must be between 0 and 100, got ${index.fillFactor} for index '${index.keyName}'`);
    }
    tableName = this.quote(tableName);
    const keyName = this.quote(index.keyName);
    const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
    let sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${tableName}`;
    if (index.unique && index.constraint) {
      sql = `alter table ${tableName} add constraint ${keyName} unique`;
    }
    if (index.columnNames.some(column => column.includes('.'))) {
      // JSON columns can have unique index but not unique constraint, and we need to distinguish those, so we can properly drop them
      sql = `create ${index.unique ? 'unique ' : ''}index ${keyName} on ${tableName}`;
      const columns = this.platform.getJsonIndexDefinition(index);
      return `${sql} (${columns.join(', ')})${this.getCreateIndexSuffix(index)}${defer}`;
    }
    // Build column list with advanced options
    const columns = this.getIndexColumns(index);
    sql += ` (${columns})`;
    // Add INCLUDE clause for covering indexes (PostgreSQL, MSSQL)
    if (index.include?.length) {
      sql += ` include (${index.include.map(c => this.quote(c)).join(', ')})`;
    }
    return sql + this.getCreateIndexSuffix(index) + defer;
  }
  /**
   * Hook for adding driver-specific index options (e.g., fill factor for PostgreSQL).
   */
  getCreateIndexSuffix(_index) {
    return '';
  }
  /**
   * Build the column list for an index, supporting advanced options like sort order, nulls ordering, and collation.
   * Note: Prefix length is only supported by MySQL/MariaDB which override this method.
   */
  getIndexColumns(index) {
    if (index.columns?.length) {
      return index.columns
        .map(col => {
          let colDef = this.quote(col.name);
          // Collation comes after column name (SQLite syntax: column COLLATE name)
          if (col.collation) {
            colDef += ` collate ${col.collation}`;
          }
          // Sort order
          if (col.sort) {
            colDef += ` ${col.sort}`;
          }
          // NULLS ordering (PostgreSQL)
          if (col.nulls) {
            colDef += ` nulls ${col.nulls}`;
          }
          return colDef;
        })
        .join(', ');
    }
    return index.columnNames.map(c => this.quote(c)).join(', ');
  }
  /** Returns SQL to drop an index. */
  getDropIndexSQL(tableName, index) {
    return `drop index ${this.quote(index.keyName)}`;
  }
  getRenameIndexSQL(tableName, index, oldIndexName) {
    return [
      this.getDropIndexSQL(tableName, { ...index, keyName: oldIndexName }),
      this.getCreateIndexSQL(tableName, index),
    ];
  }
  /** Returns SQL statements to apply a table difference (add/drop/alter columns, indexes, foreign keys). */
  alterTable(diff, safe) {
    const ret = [];
    const [schemaName, tableName] = this.splitTableName(diff.name);
    if (this.platform.supportsNativeEnums()) {
      const changedNativeEnums = [];
      for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
        if (!column.nativeEnumName) {
          continue;
        }
        const key =
          schemaName && schemaName !== this.platform.getDefaultSchemaName() && !column.nativeEnumName.includes('.')
            ? schemaName + '.' + column.nativeEnumName
            : column.nativeEnumName;
        if (changedProperties.has('enumItems') && key in diff.fromTable.nativeEnums) {
          changedNativeEnums.push([column.nativeEnumName, column.enumItems, diff.fromTable.nativeEnums[key].items]);
        }
      }
      Utils.removeDuplicates(changedNativeEnums).forEach(([enumName, itemsNew, itemsOld]) => {
        // postgres allows only adding new items
        const newItems = itemsNew.filter(val => !itemsOld.includes(val));
        if (enumName.includes('.')) {
          const [enumSchemaName, rawEnumName] = enumName.split('.');
          ret.push(
            ...newItems.map(val => this.getAlterNativeEnumSQL(rawEnumName, enumSchemaName, val, itemsNew, itemsOld)),
          );
          return;
        }
        ret.push(...newItems.map(val => this.getAlterNativeEnumSQL(enumName, schemaName, val, itemsNew, itemsOld)));
      });
    }
    for (const index of Object.values(diff.removedIndexes)) {
      ret.push(this.dropIndex(diff.name, index));
    }
    for (const index of Object.values(diff.changedIndexes)) {
      ret.push(this.dropIndex(diff.name, index));
    }
    for (const check of Object.values(diff.removedChecks)) {
      ret.push(this.dropConstraint(diff.name, check.name));
    }
    for (const check of Object.values(diff.changedChecks)) {
      ret.push(this.dropConstraint(diff.name, check.name));
    }
    /* v8 ignore next */
    if (!safe && Object.values(diff.removedColumns).length > 0) {
      ret.push(this.getDropColumnsSQL(tableName, Object.values(diff.removedColumns), schemaName));
    }
    if (Object.values(diff.addedColumns).length > 0) {
      this.append(ret, this.getAddColumnsSQL(diff.toTable, Object.values(diff.addedColumns)));
    }
    for (const column of Object.values(diff.addedColumns)) {
      const foreignKey = Object.values(diff.addedForeignKeys).find(
        fk => fk.columnNames.length === 1 && fk.columnNames[0] === column.name,
      );
      if (foreignKey && this.options.createForeignKeyConstraints) {
        delete diff.addedForeignKeys[foreignKey.constraintName];
        ret.push(this.createForeignKey(diff.toTable, foreignKey));
      }
    }
    for (const { column, changedProperties } of Object.values(diff.changedColumns)) {
      if (changedProperties.size === 1 && changedProperties.has('comment')) {
        continue;
      }
      if (changedProperties.size === 1 && changedProperties.has('enumItems') && column.nativeEnumName) {
        continue;
      }
      this.append(ret, this.alterTableColumn(column, diff.fromTable, changedProperties));
    }
    for (const { column, changedProperties } of Object.values(diff.changedColumns).filter(diff =>
      diff.changedProperties.has('comment'),
    )) {
      if (
        ['type', 'nullable', 'autoincrement', 'unsigned', 'default', 'enumItems'].some(t => changedProperties.has(t))
      ) {
        continue; // will be handled via column update
      }
      ret.push(this.getChangeColumnCommentSQL(tableName, column, schemaName));
    }
    for (const [oldColumnName, column] of Object.entries(diff.renamedColumns)) {
      ret.push(this.getRenameColumnSQL(tableName, oldColumnName, column, schemaName));
    }
    for (const foreignKey of Object.values(diff.addedForeignKeys)) {
      ret.push(this.createForeignKey(diff.toTable, foreignKey));
    }
    for (const foreignKey of Object.values(diff.changedForeignKeys)) {
      ret.push(this.createForeignKey(diff.toTable, foreignKey));
    }
    for (const index of Object.values(diff.addedIndexes)) {
      ret.push(this.createIndex(index, diff.toTable));
    }
    for (const index of Object.values(diff.changedIndexes)) {
      ret.push(this.createIndex(index, diff.toTable, true));
    }
    for (const [oldIndexName, index] of Object.entries(diff.renamedIndexes)) {
      if (index.unique) {
        ret.push(this.dropIndex(diff.name, index, oldIndexName));
        ret.push(this.createIndex(index, diff.toTable));
      } else {
        ret.push(...this.getRenameIndexSQL(diff.name, index, oldIndexName));
      }
    }
    for (const check of Object.values(diff.addedChecks)) {
      ret.push(this.createCheck(diff.toTable, check));
    }
    for (const check of Object.values(diff.changedChecks)) {
      ret.push(this.createCheck(diff.toTable, check));
    }
    if ('changedComment' in diff) {
      ret.push(this.alterTableComment(diff.toTable, diff.changedComment));
    }
    return ret;
  }
  /** Returns SQL to add columns to an existing table. */
  getAddColumnsSQL(table, columns) {
    const adds = columns
      .map(column => {
        return `add ${this.createTableColumn(column, table)}`;
      })
      .join(', ');
    return [`alter table ${table.getQuotedName()} ${adds}`];
  }
  getDropColumnsSQL(tableName, columns, schemaName) {
    const name = this.quote(this.getTableName(tableName, schemaName));
    const drops = columns.map(column => `drop column ${this.quote(column.name)}`).join(', ');
    return `alter table ${name} ${drops}`;
  }
  hasNonDefaultPrimaryKeyName(table) {
    const pkIndex = table.getPrimaryKey();
    if (!pkIndex || !this.platform.supportsCustomPrimaryKeyNames()) {
      return false;
    }
    const defaultName = this.platform.getDefaultPrimaryName(table.name, pkIndex.columnNames);
    return pkIndex?.keyName !== defaultName;
  }
  /* v8 ignore next */
  castColumn(name, type) {
    return '';
  }
  alterTableColumn(column, table, changedProperties) {
    const sql = [];
    if (changedProperties.has('default') && column.default == null) {
      sql.push(`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} drop default`);
    }
    if (changedProperties.has('type')) {
      let type = column.type + (column.generated ? ` generated always as ${column.generated}` : '');
      if (column.nativeEnumName) {
        type = this.quote(this.getTableName(type, table.schema));
      }
      sql.push(
        `alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} type ${type + this.castColumn(column.name, type)}`,
      );
    }
    if (changedProperties.has('default') && column.default != null) {
      sql.push(
        `alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} set default ${column.default}`,
      );
    }
    if (changedProperties.has('nullable')) {
      const action = column.nullable ? 'drop' : 'set';
      sql.push(`alter table ${table.getQuotedName()} alter column ${this.quote(column.name)} ${action} not null`);
    }
    return sql;
  }
  createTableColumn(column, table, changedProperties) {
    const compositePK = table.getPrimaryKey()?.composite;
    const primaryKey = !changedProperties && !this.hasNonDefaultPrimaryKeyName(table);
    const columnType = column.type + (column.generated ? ` generated always as ${column.generated}` : '');
    const useDefault = column.default != null && column.default !== 'null' && !column.autoincrement;
    const col = [this.quote(column.name), columnType];
    Utils.runIfNotEmpty(() => col.push('unsigned'), column.unsigned && this.platform.supportsUnsigned());
    Utils.runIfNotEmpty(() => col.push('null'), column.nullable);
    Utils.runIfNotEmpty(() => col.push('not null'), !column.nullable && !column.generated);
    Utils.runIfNotEmpty(() => col.push('auto_increment'), column.autoincrement);
    Utils.runIfNotEmpty(() => col.push('unique'), column.autoincrement && !column.primary);
    if (
      column.autoincrement &&
      !column.generated &&
      !compositePK &&
      (!changedProperties || changedProperties.has('autoincrement') || changedProperties.has('type'))
    ) {
      Utils.runIfNotEmpty(() => col.push('primary key'), primaryKey && column.primary);
    }
    if (useDefault) {
      // https://dev.mysql.com/doc/refman/9.0/en/data-type-defaults.html
      const needsExpression = [
        'blob',
        'text',
        'json',
        'point',
        'linestring',
        'polygon',
        'multipoint',
        'multilinestring',
        'multipolygon',
        'geometrycollection',
      ].some(type => column.type.toLowerCase().startsWith(type));
      const defaultSql = needsExpression && !column.default.startsWith('(') ? `(${column.default})` : column.default;
      col.push(`default ${defaultSql}`);
    }
    Utils.runIfNotEmpty(() => col.push(column.extra), column.extra);
    Utils.runIfNotEmpty(() => col.push(`comment ${this.platform.quoteValue(column.comment)}`), column.comment);
    return col.join(' ');
  }
  getPreAlterTable(tableDiff, safe) {
    return [];
  }
  getPostAlterTable(tableDiff, safe) {
    return [];
  }
  getChangeColumnCommentSQL(tableName, to, schemaName) {
    return '';
  }
  async getNamespaces(connection) {
    return [];
  }
  async mapIndexes(indexes) {
    const map = {};
    indexes.forEach(index => {
      if (map[index.keyName]) {
        if (index.columnNames.length > 0) {
          map[index.keyName].composite = true;
          map[index.keyName].columnNames.push(index.columnNames[0]);
        }
        // Merge columns array for advanced column options (sort, length, collation, etc.)
        if (index.columns?.length) {
          map[index.keyName].columns ??= [];
          map[index.keyName].columns.push(index.columns[0]);
        }
        // Merge INCLUDE columns
        if (index.include?.length) {
          map[index.keyName].include ??= [];
          map[index.keyName].include.push(index.include[0]);
        }
      } else {
        map[index.keyName] = index;
      }
    });
    return Object.values(map);
  }
  mapForeignKeys(fks, tableName, schemaName) {
    return fks.reduce((ret, fk) => {
      if (ret[fk.constraint_name]) {
        ret[fk.constraint_name].columnNames.push(fk.column_name);
        ret[fk.constraint_name].referencedColumnNames.push(fk.referenced_column_name);
      } else {
        ret[fk.constraint_name] = {
          columnNames: [fk.column_name],
          constraintName: fk.constraint_name,
          localTableName: schemaName ? `${schemaName}.${tableName}` : tableName,
          referencedTableName: fk.referenced_schema_name
            ? `${fk.referenced_schema_name}.${fk.referenced_table_name}`
            : fk.referenced_table_name,
          referencedColumnNames: [fk.referenced_column_name],
          updateRule: fk.update_rule.toLowerCase(),
          deleteRule: fk.delete_rule.toLowerCase(),
          deferMode: fk.defer_mode,
        };
      }
      return ret;
    }, {});
  }
  normalizeDefaultValue(defaultValue, length, defaultValues = {}) {
    if (defaultValue == null) {
      return defaultValue;
    }
    if (defaultValue instanceof RawQueryFragment) {
      return this.platform.formatQuery(defaultValue.sql, defaultValue.params);
    }
    const genericValue = defaultValue.replace(/\(\d+\)/, '(?)').toLowerCase();
    const norm = defaultValues[genericValue];
    if (!norm) {
      return defaultValue;
    }
    return norm[0].replace('(?)', length != null ? `(${length})` : '');
  }
  getCreateDatabaseSQL(name) {
    name = this.quote(name);
    // two line breaks to force separate execution
    return `create database ${name};\n\nuse ${name}`;
  }
  getDropDatabaseSQL(name) {
    return `drop database if exists ${this.quote(name)}`;
  }
  /* v8 ignore next */
  getCreateNamespaceSQL(name) {
    return `create schema if not exists ${this.quote(name)}`;
  }
  /* v8 ignore next */
  getDropNamespaceSQL(name) {
    return `drop schema if exists ${this.quote(name)}`;
  }
  getDatabaseExistsSQL(name) {
    return `select 1 from information_schema.schemata where schema_name = '${name}'`;
  }
  getDatabaseNotExistsError(dbName) {
    return `Unknown database '${dbName}'`;
  }
  getManagementDbName() {
    return 'information_schema';
  }
  getDefaultEmptyString() {
    return "''";
  }
  async databaseExists(connection, name) {
    try {
      const res = await connection.execute(this.getDatabaseExistsSQL(name));
      return res.length > 0;
    } catch (e) {
      if (e instanceof Error && e.message.includes(this.getDatabaseNotExistsError(name))) {
        return false;
      }
      /* v8 ignore next */
      throw e;
    }
  }
  append(array, sql, pad = false) {
    const length = array.length;
    for (const row of Utils.asArray(sql)) {
      if (!row) {
        continue;
      }
      let tmp = row.trim();
      if (!tmp.endsWith(';')) {
        tmp += ';';
      }
      array.push(tmp);
    }
    if (pad && array.length > length) {
      array.push('');
    }
  }
  /** Returns SQL statements to create a table with all its columns, primary key, indexes, and checks. */
  createTable(table, alter) {
    let sql = `create table ${table.getQuotedName()} (`;
    const columns = table.getColumns();
    const lastColumn = columns[columns.length - 1].name;
    for (const column of columns) {
      const col = this.createTableColumn(column, table);
      if (col) {
        const comma = column.name === lastColumn ? '' : ', ';
        sql += col + comma;
      }
    }
    const primaryKey = table.getPrimaryKey();
    const createPrimary =
      !table.getColumns().some(c => c.autoincrement && c.primary) || this.hasNonDefaultPrimaryKeyName(table);
    if (createPrimary && primaryKey) {
      const name = this.hasNonDefaultPrimaryKeyName(table) ? `constraint ${this.quote(primaryKey.keyName)} ` : '';
      sql += `, ${name}primary key (${primaryKey.columnNames.map(c => this.quote(c)).join(', ')})`;
    }
    sql += ')';
    sql += this.finalizeTable(
      table,
      this.platform.getConfig().get('charset'),
      this.platform.getConfig().get('collate'),
    );
    const ret = [];
    this.append(ret, sql);
    this.append(ret, this.appendComments(table));
    for (const index of table.getIndexes()) {
      this.append(ret, this.createIndex(index, table));
    }
    if (!alter) {
      for (const check of table.getChecks()) {
        this.append(ret, this.createCheck(table, check));
      }
    }
    return ret;
  }
  alterTableComment(table, comment) {
    return `alter table ${table.getQuotedName()} comment = ${this.platform.quoteValue(comment ?? '')}`;
  }
  /** Returns SQL to create a foreign key constraint on a table. */
  createForeignKey(table, foreignKey, alterTable = true, inline = false) {
    if (!this.options.createForeignKeyConstraints) {
      return '';
    }
    const constraintName = this.quote(foreignKey.constraintName);
    const columnNames = foreignKey.columnNames.map(c => this.quote(c)).join(', ');
    const referencedColumnNames = foreignKey.referencedColumnNames.map(c => this.quote(c)).join(', ');
    const referencedTableName = this.quote(this.getReferencedTableName(foreignKey.referencedTableName, table.schema));
    const sql = [];
    if (alterTable) {
      sql.push(`alter table ${table.getQuotedName()} add`);
    }
    sql.push(`constraint ${constraintName}`);
    if (!inline) {
      sql.push(`foreign key (${columnNames})`);
    }
    sql.push(`references ${referencedTableName} (${referencedColumnNames})`);
    if (foreignKey.localTableName !== foreignKey.referencedTableName || this.platform.supportsMultipleCascadePaths()) {
      if (foreignKey.updateRule) {
        sql.push(`on update ${foreignKey.updateRule}`);
      }
      if (foreignKey.deleteRule) {
        sql.push(`on delete ${foreignKey.deleteRule}`);
      }
    }
    if (foreignKey.deferMode) {
      sql.push(`deferrable initially ${foreignKey.deferMode}`);
    }
    return sql.join(' ');
  }
  splitTableName(name, skipDefaultSchema = false) {
    const parts = name.split('.');
    const tableName = parts.pop();
    let schemaName = parts.pop();
    if (skipDefaultSchema && schemaName === this.platform.getDefaultSchemaName()) {
      schemaName = undefined;
    }
    return [schemaName, tableName];
  }
  getReferencedTableName(referencedTableName, schema) {
    const [schemaName, tableName] = this.splitTableName(referencedTableName);
    schema = schemaName ?? schema ?? this.platform.getConfig().get('schema');
    /* v8 ignore next */
    if (schema && schemaName === '*') {
      return `${schema}.${referencedTableName.replace(/^\*\./, '')}`;
    }
    return this.getTableName(tableName, schema);
  }
  createIndex(index, table, createPrimary = false) {
    if (index.primary && !createPrimary) {
      return '';
    }
    if (index.expression) {
      return index.expression;
    }
    const columns = index.columnNames.map(c => this.quote(c)).join(', ');
    const defer = index.deferMode ? ` deferrable initially ${index.deferMode}` : '';
    if (index.primary) {
      const keyName = this.hasNonDefaultPrimaryKeyName(table) ? `constraint ${index.keyName} ` : '';
      return `alter table ${table.getQuotedName()} add ${keyName}primary key (${columns})${defer}`;
    }
    if (index.type === 'fulltext') {
      const columns = index.columnNames.map(name => ({ name, type: table.getColumn(name).type }));
      if (this.platform.supportsCreatingFullTextIndex()) {
        return this.platform.getFullTextIndexExpression(index.keyName, table.schema, table.name, columns);
      }
    }
    return this.getCreateIndexSQL(table.getShortestName(), index);
  }
  createCheck(table, check) {
    return `alter table ${table.getQuotedName()} add constraint ${this.quote(check.name)} check (${check.expression})`;
  }
  getTableName(table, schema) {
    if (schema && schema !== this.platform.getDefaultSchemaName()) {
      return `${schema}.${table}`;
    }
    return table;
  }
  getTablesGroupedBySchemas(tables) {
    return tables.reduce((acc, table) => {
      const schemaTables = acc.get(table.schema_name);
      if (!schemaTables) {
        acc.set(table.schema_name, [table]);
        return acc;
      }
      schemaTables.push(table);
      return acc;
    }, new Map());
  }
  get options() {
    return this.platform.getConfig().get('schemaGenerator');
  }
  processComment(comment) {
    return comment;
  }
  quote(...keys) {
    return this.platform.quoteIdentifier(keys.filter(Boolean).join('.'));
  }
  dropForeignKey(tableName, constraintName) {
    return `alter table ${this.quote(tableName)} drop foreign key ${this.quote(constraintName)}`;
  }
  dropIndex(table, index, oldIndexName = index.keyName) {
    if (index.primary) {
      return `alter table ${this.quote(table)} drop primary key`;
    }
    return `alter table ${this.quote(table)} drop index ${this.quote(oldIndexName)}`;
  }
  dropConstraint(table, name) {
    return `alter table ${this.quote(table)} drop constraint ${this.quote(name)}`;
  }
  /** Returns SQL to drop a table if it exists. */
  dropTableIfExists(name, schema) {
    let sql = `drop table if exists ${this.quote(this.getTableName(name, schema))}`;
    if (this.platform.usesCascadeStatement()) {
      sql += ' cascade';
    }
    return sql;
  }
  createView(name, schema, definition) {
    const viewName = this.quote(this.getTableName(name, schema));
    return `create view ${viewName} as ${definition}`;
  }
  dropViewIfExists(name, schema) {
    let sql = `drop view if exists ${this.quote(this.getTableName(name, schema))}`;
    if (this.platform.usesCascadeStatement()) {
      sql += ' cascade';
    }
    return sql;
  }
  createMaterializedView(name, schema, definition, withData = true) {
    throw new Error('Not supported by given driver');
  }
  dropMaterializedViewIfExists(name, schema) {
    throw new Error('Not supported by given driver');
  }
  refreshMaterializedView(name, schema, concurrently = false) {
    throw new Error('Not supported by given driver');
  }
  getListMaterializedViewsSQL() {
    throw new Error('Not supported by given driver');
  }
  async loadMaterializedViews(schema, connection, schemaName) {
    throw new Error('Not supported by given driver');
  }
}
