Plan SQL with bound parameters (prepared statement execution). Parses the SQL (which may contain `$1`, `$2`, ... placeholders), substitutes placeholder AST nodes with concrete literal values from `params`, then plans normally. This avoids SQL text substitution entirely — parameters are bound at the AST level, not the string level.
(
sql: &str,
params: &[ParamValue],
catalog: &dyn SqlCatalog,
)
| 96 | /// normally. This avoids SQL text substitution entirely — parameters are bound |
| 97 | /// at the AST level, not the string level. |
| 98 | pub fn plan_sql_with_params( |
| 99 | sql: &str, |
| 100 | params: &[ParamValue], |
| 101 | catalog: &dyn SqlCatalog, |
| 102 | ) -> Result<Vec<SqlPlan>> { |
| 103 | // Array DDL/DML never carries `$N` placeholders, but be defensive: |
| 104 | // intercept the same way as the no-params path so the array surface |
| 105 | // is reachable even if a client uses extended-query mode. |
| 106 | if let Some(stmt) = try_parse_array_statement(sql)? { |
| 107 | let _ = params; // arrays don't accept bound params today |
| 108 | return plan_array_statement(stmt, catalog); |
| 109 | } |
| 110 | let preprocessed = preprocess::preprocess(sql)?; |
| 111 | let effective_sql = preprocessed.as_ref().map_or(sql, |p| p.sql.as_str()); |
| 112 | let is_upsert = preprocessed.as_ref().is_some_and(|p| p.is_upsert); |
| 113 | let temporal = preprocessed |
| 114 | .as_ref() |
| 115 | .map(|p| p.temporal) |
| 116 | .unwrap_or_default(); |
| 117 | |
| 118 | let mut statements = parse_sql(effective_sql)?; |
| 119 | for stmt in &mut statements { |
| 120 | params::bind_params(stmt, params); |
| 121 | } |
| 122 | plan_statements(&statements, is_upsert, temporal, catalog) |
| 123 | } |
| 124 | |
| 125 | /// Plan a list of parsed statements. |
| 126 | fn plan_statements( |
no test coverage detected