typeorm/typeorm · error · TypeORMError

Rename table queries are not supported by Spanner driver.

Error message

Rename table queries are not supported by Spanner driver.

What it means

TypeORMError thrown unconditionally by renameTable(). Spanner DDL has no ALTER TABLE ... RENAME TO statement, so renaming a table is not possible in place; the driver refuses rather than emitting invalid DDL.

Source

Thrown at src/driver/spanner/SpannerQueryRunner.ts:816

        const downQueries: Query[] = []
        upQueries.push(await this.deleteViewDefinitionSql(view))
        upQueries.push(this.dropViewSql(view))
        downQueries.push(await this.insertViewDefinitionSql(view))
        downQueries.push(this.createViewSql(view))
        await this.executeQueries(upQueries, downQueries)
    }

    /**
     * Renames the given table.
     *
     * @param oldTableOrName
     * @param newTableName
     */
    async renameTable(
        oldTableOrName: Table | string,
        newTableName: string,
    ): Promise<void> {
        throw new TypeORMError(
            `Rename table queries are not supported by Spanner driver.`,
        )
    }

    /**
     * Creates a new column from the column in the table.
     *
     * @param tableOrName
     * @param column
     */
    async addColumn(
        tableOrName: Table | string,
        column: TableColumn,
    ): Promise<void> {
        const table =
            tableOrName instanceof Table
                ? tableOrName
                : await this.getCachedTable(tableOrName)

View on GitHub (pinned to 04ff4daedc)

Solutions

  1. Replace the rename with an explicit CREATE TABLE new ... + copy data + DROP TABLE old, executed as DDL via updateDDL.
  2. Generate the migration manually rather than relying on renameTable.
  3. Keep table names stable; rename via a new table and a backfill/cutover.

Example fix

// before
await qr.renameTable("old_users", "users");

// after
await qr.updateDDL(`CREATE TABLE users (... ) PRIMARY KEY (id)`);
// copy rows, then:
await qr.updateDDL(`DROP TABLE old_users`);
Defensive patterns

Strategy: try-catch

Validate before calling

async function safeRenameTable(qr: QueryRunner, oldName: string, newName: string, buildNewDdl: string): Promise<void> {
  if ((qr.driver.options as any).type === "spanner") {
    await qr.updateDDL(buildNewDdl);
    // copy data, then drop old
  } else {
    await qr.renameTable(oldName, newName);
  }
}

Type guard

function supportsRenameTable(ds: DataSource): boolean {
  return (ds.options as any).type !== "spanner";
}

Try / catch

try {
  await qr.renameTable(oldName, newName);
} catch (e) {
  if (/Rename table queries are not supported by Spanner/i.test(String(e?.message ?? e))) {
    // fall through to create+copy+drop strategy
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling queryRunner.renameTable(old, new); migrations generated from entity renames that translate to renameTable.

Common situations: Renaming an entity and letting the migration generator emit a rename; cross-driver migration scripts that assume rename support.

Related errors


AI-assisted analysis of typeorm/typeorm@04ff4daedc (2026-08-03). Data as JSON: /data/errors/ab128a9f538c6903.json. Report an issue: GitHub.