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
- Materialize the source data into a plain `rows` array before uploading (use source driver's downloadQueryResults rows form).
- If the source is streamed, buffer rows in batches and upload as row data.
- 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
- Always build DownloadTableData with a `rows` array for MySQL targets.
- Convert streams/cursors to arrays before upload.
- Reuse driver helpers (downloadQueryResults rows form) rather than custom shapes.
- Write a unit test for the upload path with row data.
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
- Unsupported method: ${message.method}
- Query must be defined in options
- Method is not supported for a '${this.queryType}' query type
- MySQL can not work with table names longer than 64 symbols.
- ${this.constructor} driver supports only rows upload
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/08471953784608d0.
Report an issue: GitHub.