Skip to main content

earl_protocol_sql/
executor.rs

1use anyhow::{Result, bail};
2use serde_json::Value;
3
4use earl_core::{ExecutionContext, ProtocolExecutor, RawExecutionResult};
5
6use crate::PreparedSqlQuery;
7
8/// Execute a single SQL query via sqlx and return the result.
9pub async fn execute_sql_once(
10    data: &PreparedSqlQuery,
11    ctx: &ExecutionContext,
12) -> Result<RawExecutionResult> {
13    let result = crate::sandbox::execute_query(
14        &data.connection_url,
15        &data.query,
16        &data.params,
17        data.read_only,
18        data.max_rows,
19        ctx.transport.timeout,
20    )
21    .await;
22
23    // Wrap errors through the redactor to prevent credential leakage.
24    let rows = match result {
25        Ok(rows) => rows,
26        Err(err) => {
27            let redacted_msg = ctx.redactor.redact(&format!("{err:#}"));
28            bail!("SQL query failed: {redacted_msg}");
29        }
30    };
31
32    let result_value = Value::Array(rows.into_iter().map(Value::Object).collect());
33    let body = serde_json::to_vec(&result_value)?;
34
35    Ok(RawExecutionResult {
36        status: 0,
37        url: "sql://query".into(),
38        body,
39        content_type: Some("application/json".to_string()),
40    })
41}
42
43/// SQL protocol executor.
44pub struct SqlExecutor;
45
46impl ProtocolExecutor for SqlExecutor {
47    type PreparedData = PreparedSqlQuery;
48
49    async fn execute(
50        &mut self,
51        data: &PreparedSqlQuery,
52        ctx: &ExecutionContext,
53    ) -> anyhow::Result<RawExecutionResult> {
54        execute_sql_once(data, ctx).await
55    }
56}