Skip to main content

earl_protocol_sql/
builder.rs

1use anyhow::{Result, bail};
2use serde_json::Value;
3
4use crate::PreparedSqlQuery;
5use crate::schema::SqlOperationTemplate;
6use earl_core::TemplateRenderer;
7
8/// Build a complete `PreparedSqlQuery` from a SQL operation template.
9///
10/// `connection_url` is passed in already resolved — secret resolution
11/// stays in the main crate.
12pub fn build_sql_request(
13    sql_op: &SqlOperationTemplate,
14    context: &Value,
15    renderer: &dyn TemplateRenderer,
16    connection_url: String,
17) -> Result<PreparedSqlQuery> {
18    let query = renderer.render_str(&sql_op.sql.query, context)?;
19    if query.trim().is_empty() {
20        bail!("operation.sql.query rendered empty");
21    }
22
23    let params = if let Some(param_templates) = &sql_op.sql.params {
24        let mut rendered = Vec::with_capacity(param_templates.len());
25        for p in param_templates {
26            rendered.push(renderer.render_value(p, context)?);
27        }
28        rendered
29    } else {
30        Vec::new()
31    };
32
33    let read_only = sql_op
34        .sql
35        .sandbox
36        .as_ref()
37        .and_then(|s| s.read_only)
38        .unwrap_or(true);
39    let max_rows = sql_op
40        .sql
41        .sandbox
42        .as_ref()
43        .and_then(|s| s.max_rows)
44        .unwrap_or(1000) as usize;
45
46    Ok(PreparedSqlQuery {
47        connection_url,
48        query,
49        params,
50        read_only,
51        max_rows,
52    })
53}