cube-js/cube · error · PostgresError

${this.constructor} driver supports only rows upload

Error message

${this.constructor} driver supports only rows upload

What it means

PostgresDriver.uploadTableWithIndexes only supports uploading data held in memory as rows. If the DownloadTableMemoryData passed in has no 'rows' array (e.g. it only holds a CSV file reference or other lazy form), the driver throws because it cannot stream non-row data into PostgreSQL.

Source

Thrown at packages/cubejs-postgres-driver/src/PostgresDriver.ts:489

    return this.tableColumnTypesWithPrecision(table);
  }

  protected override toGenericType(columnType: string, precision?: number | null, scale?: number | null): GenericDataBaseType {
    return PostgresToGenericType[columnType.toLowerCase()] || super.toGenericType(columnType, precision, scale);
  }

  public readOnly() {
    return !!this.config.readOnly;
  }

  public async uploadTableWithIndexes(
    table: string,
    columns: TableStructure,
    tableData: DownloadTableMemoryData,
    indexesSql: IndexesSQL
  ) {
    if (!tableData.rows) {
      throw new PostgresError(`${this.constructor} driver supports only rows upload`);
    }

    await this.createTable(table, columns);

    try {
      await this.query(
        `INSERT INTO ${table}
      (${columns.map(c => this.quoteIdentifier(c.name)).join(', ')})
      SELECT * FROM UNNEST (${columns.map((c, columnIndex) => `${this.param(columnIndex)}::${this.fromGenericType(c.type)}[]`).join(', ')})`,
        columns.map(c => tableData.rows.map(r => r[c.name]))
      );

      for (let i = 0; i < indexesSql.length; i++) {
        const [query, p] = indexesSql[i].sql;
        await this.query(query, p);
      }
    } catch (e) {
      await this.dropTable(table);

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Ensure the data source populates tableData.rows before calling uploadTableWithIndexes
  2. Use a download/driver pipeline that produces in-memory rows (e.g. DownloadTableMemoryData with rows loaded)
  3. If using unload/CSV flows, use a driver path that supports them instead of the rows-only Postgres upload

Example fix

// before
await driver.uploadTableWithIndexes(table, columns, { files: csvFiles }, indexes);
// after
const tableData = await sourceDriver.downloadTable(table); // materializes rows
await driver.uploadTableWithIndexes(table, columns, tableData, indexes);
Defensive patterns

Strategy: validation

Validate before calling

function assertRowsUpload(tableData) {
  if (!tableData || !Array.isArray(tableData.rows)) {
    throw new Error('uploadTableWithIndexes requires DownloadTableMemoryData with rows');
  }
}
assertRowsUpload(tableData);

Type guard

const hasRows = (td) => typeof td === 'object' && td !== null && Array.isArray(td.rows) && td.rows.length >= 0;

Try / catch

try {
  await driver.uploadTableWithIndexes(table, columns, tableData, indexes);
} catch (e) {
  if (String(e.message).includes('supports only rows upload')) {
    tableData = await sourceDriver.downloadTable(table); // materialize rows
    await driver.uploadTableWithIndexes(table, columns, tableData, indexes);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling uploadTableWithIndexes (directly or via the pre-aggregation upload pipeline) with tableData whose rows property is undefined/null — e.g. data downloaded in a non-row format or an incompatible driver produced the DownloadTableMemoryData.

Common situations: Custom or misconfigured download drivers returning data without materialized rows; rolling up pre-aggregations across drivers where the source driver's unload output is passed to the Postgres driver; older code paths that populate DownloadTableMemoryData differently.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/0ea29a13bd11e693. Report an issue: GitHub.