postrust_core/query/
mod.rs

1//! SQL query generation from execution plans.
2//!
3//! This module converts execution plans into parameterized SQL queries.
4
5mod builder;
6
7pub use builder::QueryBuilder;
8
9use crate::error::Result;
10use crate::plan::{ActionPlan, DbActionPlan};
11use postrust_sql::{SqlFragment, SqlParam};
12
13/// Build SQL from an action plan.
14pub fn build_query(plan: &ActionPlan, role: Option<&str>) -> Result<MainQuery> {
15    match plan {
16        ActionPlan::Db(db_plan) => build_db_query(db_plan, role),
17        ActionPlan::Info(_) => Ok(MainQuery::empty()),
18    }
19}
20
21/// Build SQL from a database action plan.
22fn build_db_query(plan: &DbActionPlan, role: Option<&str>) -> Result<MainQuery> {
23    let mut query = MainQuery::new();
24
25    // Add role switch if specified
26    if let Some(role) = role {
27        query.pre_statements.push(format!(
28            "SET LOCAL ROLE {}",
29            postrust_sql::escape_ident(role)
30        ));
31    }
32
33    match plan {
34        DbActionPlan::Read(read_tree) => {
35            query.main = QueryBuilder::build_read(read_tree)?;
36        }
37        DbActionPlan::MutateRead { mutate, read } => {
38            query.main = QueryBuilder::build_mutate(mutate)?;
39            if let Some(read_tree) = read {
40                query.read = Some(QueryBuilder::build_read(read_tree)?);
41            }
42        }
43        DbActionPlan::Call { call, read } => {
44            query.main = QueryBuilder::build_call(call)?;
45            if let Some(read_tree) = read {
46                query.read = Some(QueryBuilder::build_read(read_tree)?);
47            }
48        }
49    }
50
51    Ok(query)
52}
53
54/// A complete query with setup and main statement.
55#[derive(Clone, Debug, Default)]
56pub struct MainQuery {
57    /// Pre-query statements (SET commands)
58    pub pre_statements: Vec<String>,
59    /// Main query
60    pub main: SqlFragment,
61    /// Read query (for mutations with RETURNING)
62    pub read: Option<SqlFragment>,
63    /// Count query (for pagination)
64    pub count: Option<SqlFragment>,
65}
66
67impl MainQuery {
68    /// Create a new empty query.
69    pub fn new() -> Self {
70        Self::default()
71    }
72
73    /// Create an empty query (for info-only plans).
74    pub fn empty() -> Self {
75        Self::default()
76    }
77
78    /// Check if this query has a main statement.
79    pub fn has_main(&self) -> bool {
80        !self.main.is_empty()
81    }
82
83    /// Get the main SQL and parameters.
84    pub fn build_main(self) -> (String, Vec<SqlParam>) {
85        self.main.build()
86    }
87}