Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion crates/dbx-core/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1861,7 +1861,7 @@ pub async fn execute_statements(
match result {
Ok(result) => return Ok(db::QueryResult { execution_time_ms: start.elapsed().as_millis(), ..result }),
Err(err) => {
if err.contains("Unknown method: execute_batch") {
if is_agent_execute_batch_unsupported(&err) {
log::warn!(
"Agent does not support execute_batch; falling back to statement-by-statement execution"
);
Expand Down Expand Up @@ -1928,6 +1928,11 @@ pub async fn execute_statements(
})
}

fn is_agent_execute_batch_unsupported(error: &str) -> bool {
let lower = error.to_lowercase();
lower.contains("execute_batch") && (lower.contains("unknown method") || lower.contains("method not found"))
}

/// Execute multiple SQL statements within a single transaction.
/// For pooled drivers (Postgres/MySQL), uses the driver transaction API.
/// For SQLite and already-single-connection drivers (ClickHouse/SqlServer/Agent),
Expand Down Expand Up @@ -2404,6 +2409,19 @@ mod tests {
}
}

#[test]
fn agent_execute_batch_unsupported_detects_case_insensitive_method_errors() {
assert!(is_agent_execute_batch_unsupported("Agent RPC error (-1): unknown method: execute_batch"));
assert!(is_agent_execute_batch_unsupported("Agent RPC error (-1): Unknown method: execute_batch"));
assert!(is_agent_execute_batch_unsupported("Agent RPC error (-32601): Method not found: execute_batch"));
}

#[test]
fn agent_execute_batch_unsupported_ignores_unrelated_errors() {
assert!(!is_agent_execute_batch_unsupported("ORA-00955: name is already used by an existing object"));
assert!(!is_agent_execute_batch_unsupported("Agent RPC error (-1): unknown method: execute_query"));
}

#[tokio::test]
async fn wait_for_query_returns_cancelled_when_token_is_cancelled() {
let token = CancellationToken::new();
Expand Down
Loading