risingwavelabs/risingwave · error · BatchError
failed to get row stream from mysql query
Error message
failed to get row stream from mysql query
What it means
In the MySQL batch query executor, `mysql_async`'s `query_iter(...).stream::<Row>()` returned `None`, meaning the connection did not yield a binary/text row stream for the executed statement (e.g. the statement produced no rowset). Since the executor expects result rows for the query, it fails with this error.
Solutions
- Verify the generated query returns a result set by running it directly against MySQL with the same credentials.
- Check for proxies/load balancers between RisingWave and MySQL and test with a direct connection.
- Confirm the MySQL user has SELECT privileges and the server supports streaming results (binary protocol).
- Check the MySQL server error log for aborted statements around the time of the failure.
Defensive patterns
Strategy: try-catch
Validate before calling
-- run the expected query manually before wiring the external table SELECT 1 FROM information_schema.tables WHERE table_schema = 'db' AND table_name = 't'; -- then run the actual query shape to confirm it returns a result set
Try / catch
// wrap batch query execution and classify the failure
match executor.execute().await {
Err(e) if e.to_string().contains("failed to get row stream from mysql query") => {
// check query returns a result set; verify proxy/privileges; retry with direct connection
}
other => other?,
} Prevention
- Test the exact query shape directly against MySQL with the same credentials before wiring it into RisingWave.
- Avoid MySQL proxies/middleware in the data path, or verify streaming (binary protocol) works through them.
- Grant the RisingWave MySQL user explicit SELECT privileges on the queried tables.
- Monitor MySQL server logs for aborted statements or connection resets.
When it happens
Trigger: Executing a batch MySQL external-table query where the statement does not return a row stream — e.g. the query was rewritten or routed to a statement that returns no resultset (non-SELECT), or the server closed/reset the result set.
Common situations: MySQL proxy/middleware (e.g. ProxySQL, MaxScale) interfering with result-set streaming; server-side errors surfaced as empty streams; pointing a RisingWave MySQL external table at a non-query endpoint or using a driver/proxy version mismatch.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- all request confluent registry all timeout
- bad result from mysql
- {batch write failure, with context()}
- bigquery insert error
- bigquery insert error: end of resp stream
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/be74e2f04e468076.
Report an issue: GitHub.
Appendix: source
Thrown at src/batch/executors/src/executor/mysql_query.rs:124
.tcp_port(self.port.parse::<u16>().unwrap()) // FIXME
.user(Some(self.username))
.pass(Some(self.password))
.db_name(Some(self.database))
.into();
let pool = mysql_async::Pool::new(database_opts);
let mut conn = pool
.get_conn()
.await
.context("failed to connect to mysql in batch executor")?;
let query = self.query;
let mut query_iter = conn
.query_iter(query)
.await
.context("failed to execute my_sql_query in batch executor")?;
let Some(row_stream) = query_iter.stream::<mysql_async::Row>().await? else {
bail!("failed to get row stream from mysql query")
};
let mut builder = DataChunkBuilder::new(self.schema.data_types(), self.chunk_size);
tracing::debug!("mysql_query_executor: query executed, start deserializing rows");
// deserialize the rows
#[for_await]
for row in row_stream {
let row = row?;
let owned_row = mysql_row_to_owned_row(row, &self.schema)?;
if let Some(chunk) = builder.append_one_row(owned_row) {
yield chunk;
}
}
if let Some(chunk) = builder.consume_all() {
yield chunk;
}
return Ok(());
}View on GitHub (pinned to 6469eb736d)