uqa_sql/semantics/
portals.rs1#[cfg(test)]
10mod tests;
11
12use crate::{plan::CommandPlan, SQLError};
13
14#[derive(Clone, Copy, PartialEq, Eq)]
15pub enum PortalDeclarationContext {
16 Sql,
17 PLpgSQL,
18}
19
20pub fn cannot_open_command_cursor(command: &CommandPlan) -> SQLError {
21 let tag = match command {
22 CommandPlan::Insert(_) => "INSERT",
23 CommandPlan::Update(_) => "UPDATE",
24 CommandPlan::Delete(_) => "DELETE",
25 CommandPlan::Merge(_) => "MERGE",
26 CommandPlan::Call { .. } => "CALL",
27 CommandPlan::ShowVariable { .. } => "SHOW",
28 CommandPlan::Explain { .. } => "EXPLAIN",
29 _ => command.name(),
30 };
31 SQLError::Routine {
32 sqlstate: "42P11".into(),
33 message: format!("cannot open {tag} query as cursor"),
34 }
35}
36
37pub fn validate_query_options(
38 context: PortalDeclarationContext,
39 has_row_locks: bool,
40 hold: bool,
41 scroll: Option<bool>,
42) -> Result<(), SQLError> {
43 if has_row_locks && hold {
44 return Err(SQLError::Routine {
45 sqlstate: "0A000".into(),
46 message: "DECLARE CURSOR WITH HOLD ... FOR UPDATE is not supported".into(),
47 });
48 }
49 if has_row_locks && scroll == Some(true) {
50 return Err(SQLError::Routine {
51 sqlstate: "0A000".into(),
52 message: if context == PortalDeclarationContext::PLpgSQL {
53 "DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported".into()
54 } else {
55 "DECLARE SCROLL CURSOR ... FOR UPDATE is not supported".into()
56 },
57 });
58 }
59 Ok(())
60}
61
62pub fn command_scroll_returns_nulls(
63 command: &CommandPlan,
64 scroll: Option<bool>,
65) -> Result<bool, SQLError> {
66 if scroll == Some(true) && matches!(command, CommandPlan::Merge(_)) {
67 return Err(SQLError::Routine {
68 sqlstate: "0A000".into(),
69 message: "DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported".into(),
70 });
71 }
72 let null_returning_values = scroll == Some(true)
73 && matches!(
74 command,
75 CommandPlan::Insert(_) | CommandPlan::Update(_) | CommandPlan::Delete(_)
76 );
77 Ok(null_returning_values)
78}