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 new_datum: Option<usize>,
47 pub old_datum: Option<usize>,
49 pub found_datum: Option<usize>,
51}
52
53impl PLpgSQLFunction {
54 pub fn fori_variable_datums(&self) -> std::collections::BTreeSet<usize> {
58 let mut out = std::collections::BTreeSet::new();
59 collect_fori_vars_block(&self.action, &mut out);
60 out
61 }
62
63 pub fn cursor_argument_datums(&self) -> std::collections::BTreeSet<usize> {
66 let mut out = std::collections::BTreeSet::new();
67 for datum in &self.datums {
68 let PLpgSQLDatum::Var(var) = datum else {
69 continue;
70 };
71 let Some(argument_row) = var.cursor.as_ref().and_then(|cursor| cursor.argument_row)
72 else {
73 continue;
74 };
75 if let Some(PLpgSQLDatum::Row { fields }) = self.datums.get(argument_row) {
76 out.extend(fields.iter().map(|field| field.varno));
77 }
78 }
79 out
80 }
81}
82
83fn collect_fori_vars_block(block: &PLpgSQLBlock, out: &mut std::collections::BTreeSet<usize>) {
84 collect_fori_vars_stmts(&block.body, out);
85 for arm in &block.exceptions {
86 collect_fori_vars_stmts(&arm.body, out);
87 }
88}
89
90fn collect_fori_vars_stmts(stmts: &[PLpgSQLStmt], out: &mut std::collections::BTreeSet<usize>) {
91 for stmt in stmts {
92 match stmt {
93 PLpgSQLStmt::Block(block) => collect_fori_vars_block(block, out),
94 PLpgSQLStmt::If {
95 then_body,
96 elsifs,
97 else_body,
98 ..
99 } => {
100 collect_fori_vars_stmts(then_body, out);
101 for (_, body) in elsifs {
102 collect_fori_vars_stmts(body, out);
103 }
104 if let Some(body) = else_body {
105 collect_fori_vars_stmts(body, out);
106 }
107 }
108 PLpgSQLStmt::Case {
109 arms, else_body, ..
110 } => {
111 for (_, body) in arms {
112 collect_fori_vars_stmts(body, out);
113 }
114 if let Some(body) = else_body {
115 collect_fori_vars_stmts(body, out);
116 }
117 }
118 PLpgSQLStmt::Loop { body, .. } | PLpgSQLStmt::While { body, .. } => {
119 collect_fori_vars_stmts(body, out);
120 }
121 PLpgSQLStmt::ForI { var, body, .. } => {
122 out.insert(*var);
123 collect_fori_vars_stmts(body, out);
124 }
125 PLpgSQLStmt::ForQuery { body, .. } => collect_fori_vars_stmts(body, out),
126 _ => {}
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
134pub enum PLpgSQLDatum {
135 Var(Box<PLpgSQLVar>),
136 Rec {
138 name: String,
139 },
140 RecField {
142 field: String,
143 parent: usize,
144 },
145 Row {
147 fields: Vec<PLpgSQLRowField>,
148 },
149}
150
151impl PLpgSQLDatum {
152 pub fn name(&self) -> Option<&str> {
153 match self {
154 PLpgSQLDatum::Var(v) => Some(&v.name),
155 PLpgSQLDatum::Rec { name } => Some(name),
156 PLpgSQLDatum::RecField { .. } | PLpgSQLDatum::Row { .. } => None,
157 }
158 }
159}
160
161#[derive(Debug, Clone)]
164pub struct PLpgSQLVar {
165 pub name: String,
166 pub type_name: String,
169 pub type_reference: Option<RoutineColumnTypeReference>,
171 pub default: Option<Expr>,
172 pub constant: bool,
173 pub not_null: bool,
174 pub cursor: Option<PLpgSQLCursor>,
176 pub lineno: Option<i64>,
179}
180
181#[derive(Debug, Clone)]
182pub struct PLpgSQLCursor {
183 pub query: Statement,
184 pub argument_row: Option<usize>,
185}
186
187#[derive(Debug, Clone)]
188pub struct PLpgSQLCursorArgument {
189 pub name: Option<String>,
190 pub expr: Expr,
191}
192
193#[derive(Debug, Clone)]
195pub struct PLpgSQLRowField {
196 pub name: String,
197 pub varno: usize,
198}
199
200#[derive(Debug, Clone)]
202pub struct PLpgSQLBlock {
203 pub label: Option<String>,
204 pub body: Vec<PLpgSQLStmt>,
205 pub exceptions: Vec<PLpgSQLExceptionArm>,
206}
207
208#[derive(Debug, Clone)]
211pub struct PLpgSQLExceptionArm {
212 pub conditions: Vec<String>,
216 pub body: Vec<PLpgSQLStmt>,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub enum RaiseLevel {
222 Debug,
223 Log,
224 Info,
225 Notice,
226 Warning,
227 Error,
228}
229
230impl RaiseLevel {
231 pub fn as_str(self) -> &'static str {
232 match self {
233 RaiseLevel::Debug => "DEBUG",
234 RaiseLevel::Log => "LOG",
235 RaiseLevel::Info => "INFO",
236 RaiseLevel::Notice => "NOTICE",
237 RaiseLevel::Warning => "WARNING",
238 RaiseLevel::Error => "ERROR",
239 }
240 }
241}
242
243#[derive(Debug, Clone)]
245pub enum IntoTarget {
246 Rec(usize),
248 Row(Vec<PLpgSQLRowField>),
250}
251
252#[derive(Debug, Clone)]
254pub enum PLpgSQLStmt {
255 Block(PLpgSQLBlock),
256 Assign {
258 target: usize,
259 expr: Expr,
260 },
261 If {
262 cond: Expr,
263 then_body: Vec<PLpgSQLStmt>,
264 elsifs: Vec<(Expr, Vec<PLpgSQLStmt>)>,
265 else_body: Option<Vec<PLpgSQLStmt>>,
266 },
267 Case {
270 t_expr: Option<Expr>,
271 t_varno: Option<usize>,
272 arms: Vec<(Expr, Vec<PLpgSQLStmt>)>,
273 else_body: Option<Vec<PLpgSQLStmt>>,
274 },
275 Loop {
276 label: Option<String>,
277 body: Vec<PLpgSQLStmt>,
278 },
279 While {
280 label: Option<String>,
281 cond: Expr,
282 body: Vec<PLpgSQLStmt>,
283 },
284 ForI {
286 label: Option<String>,
287 var: usize,
288 lower: Expr,
289 upper: Expr,
290 step: Option<Expr>,
291 reverse: bool,
292 body: Vec<PLpgSQLStmt>,
293 },
294 ForQuery {
296 label: Option<String>,
297 target: IntoTarget,
298 query: Statement,
299 body: Vec<PLpgSQLStmt>,
300 },
301 Exit {
304 is_exit: bool,
305 label: Option<String>,
306 cond: Option<Expr>,
307 },
308 Return {
309 value: Option<PLpgSQLReturnValue>,
310 },
311 ReturnNext {
314 value: Option<PLpgSQLReturnValue>,
315 },
316 ReturnQuery {
317 query: Statement,
318 },
319 ReturnQueryExecute {
320 query: Expr,
321 params: Vec<Expr>,
322 },
323 Raise {
324 level: RaiseLevel,
325 condition: Option<String>,
326 message: Option<String>,
327 params: Vec<Expr>,
328 },
329 ExecSQL {
331 stmt: Statement,
332 into: Option<IntoTarget>,
333 strict: bool,
334 },
335 DynExecute {
337 query: Expr,
338 params: Vec<Expr>,
339 into: Option<IntoTarget>,
340 strict: bool,
341 },
342 Perform {
343 query: Statement,
344 },
345 OpenCursor {
346 cursor: usize,
347 arguments: Vec<PLpgSQLCursorArgument>,
348 },
349 FetchCursor {
350 cursor: usize,
351 target: IntoTarget,
352 direction: i64,
353 count: i64,
354 },
355 CloseCursor {
356 cursor: usize,
357 },
358 GetDiagnostics {
360 items: Vec<(String, usize)>,
361 },
362}
363
364#[derive(Debug, Clone)]
367pub enum PLpgSQLReturnValue {
368 Expr(Expr),
369 Datum(usize),
370}
371
372mod binding;
380mod conditions;
381mod json_validation;
382mod lowering_expression;
383mod lowering_statement;
384mod parsing;
385
386use json_validation::{
387 ensure_single_tag, expect_tag, json_bool_or_false, json_i64_or_zero, json_kind,
388 json_optional_i64, json_optional_str, json_optional_usize, json_usize_or_zero,
389 normalize_plpgsql_type, optional_array, require, require_i64, require_nonempty_str,
390 validate_assignable_datum, validate_record_datum, validate_scalar_datum,
391};
392use lowering_expression::{lower_expr, lower_expr_list, lower_full_statement};
393use lowering_statement::lower_block;
394use parsing::{lower_row_fields, normalize_condition};
395
396pub use binding::{bind_expr, bind_select, bind_statement, ResolvedVariable, VariableResolver};
397pub use conditions::{condition_sqlstate, condition_sqlstates};
398pub use lowering_expression::compile_expression_text;
399pub use parsing::{parse_do_block, parse_function};
400
401#[cfg(test)]
402mod tests;