1use serde_json::Value as JSONValue;
27use uqa_core::Value;
28
29use crate::ast::{
30 CreateFunction, Expr, FromClause, FunctionBody, FunctionParamMode, FunctionReturns, MergeWhen,
31 Projection, RoutineColumnTypeReference, SelectStmt, Statement, CTE,
32};
33use crate::error::{Result, SQLError};
34
35#[derive(Debug, Clone)]
42pub struct PLpgSQLFunction {
43 pub datums: Vec<PLpgSQLDatum>,
44 pub action: PLpgSQLBlock,
45 pub found_datum: Option<usize>,
47}
48
49impl PLpgSQLFunction {
50 pub fn fori_variable_datums(&self) -> std::collections::BTreeSet<usize> {
54 let mut out = std::collections::BTreeSet::new();
55 collect_fori_vars_block(&self.action, &mut out);
56 out
57 }
58
59 pub fn cursor_argument_datums(&self) -> std::collections::BTreeSet<usize> {
62 let mut out = std::collections::BTreeSet::new();
63 for datum in &self.datums {
64 let PLpgSQLDatum::Var(var) = datum else {
65 continue;
66 };
67 let Some(argument_row) = var.cursor.as_ref().and_then(|cursor| cursor.argument_row)
68 else {
69 continue;
70 };
71 if let Some(PLpgSQLDatum::Row { fields }) = self.datums.get(argument_row) {
72 out.extend(fields.iter().map(|field| field.varno));
73 }
74 }
75 out
76 }
77}
78
79fn collect_fori_vars_block(block: &PLpgSQLBlock, out: &mut std::collections::BTreeSet<usize>) {
80 collect_fori_vars_stmts(&block.body, out);
81 for arm in &block.exceptions {
82 collect_fori_vars_stmts(&arm.body, out);
83 }
84}
85
86fn collect_fori_vars_stmts(stmts: &[PLpgSQLStmt], out: &mut std::collections::BTreeSet<usize>) {
87 for stmt in stmts {
88 match stmt {
89 PLpgSQLStmt::Block(block) => collect_fori_vars_block(block, out),
90 PLpgSQLStmt::If {
91 then_body,
92 elsifs,
93 else_body,
94 ..
95 } => {
96 collect_fori_vars_stmts(then_body, out);
97 for (_, body) in elsifs {
98 collect_fori_vars_stmts(body, out);
99 }
100 if let Some(body) = else_body {
101 collect_fori_vars_stmts(body, out);
102 }
103 }
104 PLpgSQLStmt::Case {
105 arms, else_body, ..
106 } => {
107 for (_, body) in arms {
108 collect_fori_vars_stmts(body, out);
109 }
110 if let Some(body) = else_body {
111 collect_fori_vars_stmts(body, out);
112 }
113 }
114 PLpgSQLStmt::Loop { body, .. } | PLpgSQLStmt::While { body, .. } => {
115 collect_fori_vars_stmts(body, out);
116 }
117 PLpgSQLStmt::ForI { var, body, .. } => {
118 out.insert(*var);
119 collect_fori_vars_stmts(body, out);
120 }
121 PLpgSQLStmt::ForQuery { body, .. } => collect_fori_vars_stmts(body, out),
122 _ => {}
123 }
124 }
125}
126
127#[derive(Debug, Clone)]
130pub enum PLpgSQLDatum {
131 Var(Box<PLpgSQLVar>),
132 Rec {
134 name: String,
135 },
136 RecField {
138 field: String,
139 parent: usize,
140 },
141 Row {
143 fields: Vec<PLpgSQLRowField>,
144 },
145}
146
147impl PLpgSQLDatum {
148 pub fn name(&self) -> Option<&str> {
149 match self {
150 PLpgSQLDatum::Var(v) => Some(&v.name),
151 PLpgSQLDatum::Rec { name } => Some(name),
152 PLpgSQLDatum::RecField { .. } | PLpgSQLDatum::Row { .. } => None,
153 }
154 }
155}
156
157#[derive(Debug, Clone)]
160pub struct PLpgSQLVar {
161 pub name: String,
162 pub type_name: String,
165 pub type_reference: Option<RoutineColumnTypeReference>,
167 pub default: Option<Expr>,
168 pub constant: bool,
169 pub not_null: bool,
170 pub cursor: Option<PLpgSQLCursor>,
172 pub lineno: Option<i64>,
175}
176
177#[derive(Debug, Clone)]
178pub struct PLpgSQLCursor {
179 pub query: Statement,
180 pub argument_row: Option<usize>,
181}
182
183#[derive(Debug, Clone)]
184pub struct PLpgSQLCursorArgument {
185 pub name: Option<String>,
186 pub expr: Expr,
187}
188
189#[derive(Debug, Clone)]
191pub struct PLpgSQLRowField {
192 pub name: String,
193 pub varno: usize,
194}
195
196#[derive(Debug, Clone)]
198pub struct PLpgSQLBlock {
199 pub label: Option<String>,
200 pub body: Vec<PLpgSQLStmt>,
201 pub exceptions: Vec<PLpgSQLExceptionArm>,
202}
203
204#[derive(Debug, Clone)]
207pub struct PLpgSQLExceptionArm {
208 pub conditions: Vec<String>,
212 pub body: Vec<PLpgSQLStmt>,
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub enum RaiseLevel {
218 Debug,
219 Log,
220 Info,
221 Notice,
222 Warning,
223 Error,
224}
225
226impl RaiseLevel {
227 pub fn as_str(self) -> &'static str {
228 match self {
229 RaiseLevel::Debug => "DEBUG",
230 RaiseLevel::Log => "LOG",
231 RaiseLevel::Info => "INFO",
232 RaiseLevel::Notice => "NOTICE",
233 RaiseLevel::Warning => "WARNING",
234 RaiseLevel::Error => "ERROR",
235 }
236 }
237}
238
239#[derive(Debug, Clone)]
241pub enum IntoTarget {
242 Rec(usize),
244 Row(Vec<PLpgSQLRowField>),
246}
247
248#[derive(Debug, Clone)]
250pub enum PLpgSQLStmt {
251 Block(PLpgSQLBlock),
252 Assign {
254 target: usize,
255 expr: Expr,
256 },
257 If {
258 cond: Expr,
259 then_body: Vec<PLpgSQLStmt>,
260 elsifs: Vec<(Expr, Vec<PLpgSQLStmt>)>,
261 else_body: Option<Vec<PLpgSQLStmt>>,
262 },
263 Case {
266 t_expr: Option<Expr>,
267 t_varno: Option<usize>,
268 arms: Vec<(Expr, Vec<PLpgSQLStmt>)>,
269 else_body: Option<Vec<PLpgSQLStmt>>,
270 },
271 Loop {
272 label: Option<String>,
273 body: Vec<PLpgSQLStmt>,
274 },
275 While {
276 label: Option<String>,
277 cond: Expr,
278 body: Vec<PLpgSQLStmt>,
279 },
280 ForI {
282 label: Option<String>,
283 var: usize,
284 lower: Expr,
285 upper: Expr,
286 step: Option<Expr>,
287 reverse: bool,
288 body: Vec<PLpgSQLStmt>,
289 },
290 ForQuery {
292 label: Option<String>,
293 target: IntoTarget,
294 query: Statement,
295 body: Vec<PLpgSQLStmt>,
296 },
297 Exit {
300 is_exit: bool,
301 label: Option<String>,
302 cond: Option<Expr>,
303 },
304 Return {
305 value: Option<PLpgSQLReturnValue>,
306 },
307 ReturnNext {
310 value: Option<PLpgSQLReturnValue>,
311 },
312 ReturnQuery {
313 query: Statement,
314 },
315 ReturnQueryExecute {
316 query: Expr,
317 params: Vec<Expr>,
318 },
319 Raise {
320 level: RaiseLevel,
321 condition: Option<String>,
322 message: Option<String>,
323 params: Vec<Expr>,
324 },
325 ExecSQL {
327 stmt: Statement,
328 into: Option<IntoTarget>,
329 strict: bool,
330 },
331 DynExecute {
333 query: Expr,
334 params: Vec<Expr>,
335 into: Option<IntoTarget>,
336 strict: bool,
337 },
338 Perform {
339 query: Statement,
340 },
341 OpenCursor {
342 cursor: usize,
343 arguments: Vec<PLpgSQLCursorArgument>,
344 },
345 FetchCursor {
346 cursor: usize,
347 target: IntoTarget,
348 direction: i64,
349 count: i64,
350 },
351 CloseCursor {
352 cursor: usize,
353 },
354 GetDiagnostics {
356 items: Vec<(String, usize)>,
357 },
358}
359
360#[derive(Debug, Clone)]
363pub enum PLpgSQLReturnValue {
364 Expr(Expr),
365 Datum(usize),
366}
367
368mod binding;
376mod conditions;
377mod json_validation;
378mod lowering_expression;
379mod lowering_statement;
380mod parsing;
381
382use json_validation::{
383 ensure_single_tag, expect_tag, json_bool_or_false, json_i64_or_zero, json_kind,
384 json_optional_i64, json_optional_str, json_optional_usize, json_usize_or_zero,
385 normalize_plpgsql_type, optional_array, require, require_i64, require_nonempty_str,
386 validate_assignable_datum, validate_record_datum, validate_scalar_datum,
387};
388use lowering_expression::{lower_expr, lower_expr_list, lower_full_statement};
389use lowering_statement::lower_block;
390use parsing::{lower_row_fields, normalize_condition};
391
392pub use binding::{bind_expr, bind_select, bind_statement, VariableResolver};
393pub use conditions::{condition_sqlstate, condition_sqlstates};
394pub use lowering_expression::compile_expression_text;
395pub use parsing::{parse_do_block, parse_function};
396
397#[cfg(test)]
398mod tests;