cube-js/cube · error · Error

${this.constructor} driver supports only rows upload

Error message

${this.constructor} driver supports only rows upload

What it means

MySqlDriver.uploadTableWithIndexes only supports `DownloadTableData` in row form (`rows` array). If the data object is in an unsupported shape (e.g., a stream-like or csv structure), the driver throws this error rather than attempting an incompatible insert path.

Source

Thrown at packages/cubejs-mysql-driver/src/MySqlDriver.ts:438

      if (value.toLowerCase() === 'false') {
        return false;
      }
    }
    return super.toColumnValue(value, genericType);
  }

  protected isDownloadTableDataRow(tableData: DownloadTableData): tableData is DownloadTableMemoryData {
    return (<DownloadTableMemoryData> tableData).rows !== undefined;
  }

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

    await this.createTable(table, columns);

    try {
      const batchSize = 1000; // TODO make dynamic?
      for (let j = 0; j < Math.ceil(tableData.rows.length / batchSize); j++) {
        const currentBatchSize = Math.min(tableData.rows.length - j * batchSize, batchSize);
        const indexArray = Array.from({ length: currentBatchSize }, (v, i) => i);
        const valueParamPlaceholders =
          indexArray.map(i => `(${columns.map((c, paramIndex) => this.param(paramIndex + i * columns.length)).join(', ')})`).join(', ');
        const params = indexArray.map(i => columns
          .map(c => this.toColumnValue(tableData.rows[i + j * batchSize][c.name], c.type)))
          .reduce((a, b) => a.concat(b), []);

        await this.query(
          `INSERT INTO ${table}
        (${columns.map(c => this.quoteIdentifier(c.name)).join(', ')})

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Materialize the source data into a plain `rows` array before uploading (use source driver's downloadQueryResults rows form).
  2. If the source is streamed, buffer rows in batches and upload as row data.
  3. Check the DownloadTableData type to ensure `rows` is populated and not `stream`/`csv`.

Example fix

// before
await mysqlDriver.uploadTableWithIndexes(table, columns, downloaded.stream);
// after
const { rows } = await sourceDriver.downloadQueryResults(query);
await mysqlDriver.uploadTableWithIndexes(table, columns, { rows, types });
Defensive patterns

Strategy: type-guard

Validate before calling

const isRowData = (d) => d && Array.isArray(d.rows);
if (!isRowData(tableData)) throw new Error('MySQL driver requires rows-based DownloadTableData');

Type guard

function isDownloadTableDataRow(d: DownloadTableData): d is { rows: any[] } {
  return 'rows' in d && Array.isArray((d as any).rows);
}

Try / catch

if (!isDownloadTableDataRow(tableData)) {
  tableData = { rows: await materializeRows(tableData) };
}
await driver.uploadTableWithIndexes(table, columns, tableData);

Prevention

When it happens

Trigger: Calling `uploadTableWithIndexes` (via the driver's table-upload API) with a `tableData` whose shape fails `isDownloadTableDataRow` — i.e., data not provided as a `rows` array.

Common situations: Custom driver wrappers or orchestration code feeding streamed/cursor data from a source database into MySQL; building a custom exporter that passes non-row table data.

Related errors


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