1use super::Value;
6use std::collections::{HashMap, VecDeque};
7
8#[derive(Clone, Copy, Debug)]
10pub struct Call<'a> {
11 pub program: &'a str,
12 pub ordinal: u32,
14 pub verb: &'a str,
17 pub cursor: Option<&'a str>,
19 pub text: &'a str,
21 pub inputs: &'a [Value],
22}
23
24#[derive(Clone, Debug, PartialEq)]
26pub struct Outcome {
27 pub sqlcode: i32,
28 pub sqlstate: String,
29 pub affected: i64,
31 pub rows: Vec<Vec<Value>>,
32 pub tokens: String,
34 pub columns: Vec<Column>,
36 pub parameters: Vec<Option<Value>>,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct Column {
43 pub name: String,
44 pub ty: ColumnType,
45 pub nullable: bool,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum ColumnType {
51 Char(u16),
52 VarChar(u16),
53 Graphic(u16),
55 VarGraphic(u16),
56 SmallInt,
57 Integer,
58 BigInt,
59 Decimal { precision: u8, scale: u8 },
60 Numeric,
62 Real,
63 Double,
64 Date,
65 Time,
66 Timestamp(u8),
68 Binary(u16),
69 VarBinary(u16),
70 Other(String),
72}
73
74impl Outcome {
75 pub fn ok() -> Self {
76 Self { sqlcode: 0, sqlstate: "00000".into(), affected: 0, rows: Vec::new(), tokens: String::new(), columns: Vec::new(), parameters: Vec::new() }
77 }
78
79 pub fn rows(rows: Vec<Vec<Value>>) -> Self {
80 Self { rows, ..Self::ok() }
81 }
82
83 pub fn error(sqlcode: i32, sqlstate: &str) -> Self {
84 Self { sqlcode, sqlstate: sqlstate.into(), ..Self::ok() }
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq)]
91pub struct Abandoned {
92 pub code: &'static str,
93 pub message: String,
94}
95
96pub type Answer = Result<Outcome, Abandoned>;
97
98pub trait Database {
99 fn execute(&mut self, call: &Call) -> Answer;
102 fn prepare(&mut self, call: &Call) -> Answer;
104 fn open(&mut self, call: &Call) -> Answer;
105 fn fetch(&mut self, call: &Call) -> Answer;
107 fn fetch_rows(&mut self, call: &Call, rows: u32) -> Answer;
109 fn insert_rows(&mut self, call: &Call, rows: &[Vec<Value>], atomic: bool) -> Answer;
113 fn call(&mut self, call: &Call) -> Answer {
117 Err(Abandoned { code: "EXEC", message: format!("EXEC SQL CALL {} was reached: this database runs no stored procedures; a recording of the CALL answers it (--sql-replay)", call.cursor.unwrap_or_default()) })
118 }
119 fn close(&mut self, call: &Call) -> Answer;
120 fn commit(&mut self, call: &Call) -> Answer;
121 fn rollback(&mut self, call: &Call) -> Answer;
122 fn close_all(&mut self) -> Result<(), Abandoned> {
125 Ok(())
126 }
127}
128
129#[derive(Clone, Debug, PartialEq)]
130pub struct OpenCursor {
131 pub with_hold: bool,
132 pub positioned: bool,
134 pub statement: Option<String>,
136 pub held: VecDeque<Vec<Value>>,
140 pub current: usize,
142 pub rowset_size: Option<u32>,
145}
146
147impl OpenCursor {
148 pub fn on_backend_row(&self) -> bool {
150 self.current == 1 && self.held.len() == 1
151 }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct Prepared {
157 pub text: String,
158 pub query: bool,
159 pub markers: usize,
160 pub columns: Vec<Column>,
161}
162
163pub struct Session<'w> {
166 pub database: &'w mut dyn Database,
167 cursors: HashMap<(String, String), OpenCursor>,
169 prepared: HashMap<(String, String), Prepared>,
172 pub pending: bool,
174}
175
176impl<'w> Session<'w> {
177 pub fn new(database: &'w mut (dyn Database + '_)) -> Self {
178 Self { database, cursors: HashMap::new(), prepared: HashMap::new(), pending: false }
179 }
180
181 pub fn cursor(&mut self, program: &str, name: &str) -> Option<&mut OpenCursor> {
182 self.cursors.get_mut(&(program.to_owned(), name.to_owned()))
183 }
184
185 pub fn opened(&mut self, program: &str, name: &str, with_hold: bool, statement: Option<&str>) {
186 let cursor = OpenCursor { with_hold, positioned: false, statement: statement.map(str::to_owned), held: VecDeque::new(), current: 0, rowset_size: None };
187 self.cursors.insert((program.to_owned(), name.to_owned()), cursor);
188 }
189
190 pub fn prepared(&self, program: &str, name: &str) -> Option<&Prepared> {
191 self.prepared.get(&(program.to_owned(), name.to_owned()))
192 }
193
194 pub fn prepare(&mut self, program: &str, name: &str, statement: Option<Prepared>) {
195 let key = (program.to_owned(), name.to_owned());
196 match statement {
197 Some(p) => self.prepared.insert(key, p),
198 None => self.prepared.remove(&key),
199 };
200 }
201
202 pub fn running(&self, program: &str, name: &str) -> bool {
204 self.cursors.iter().any(|((p, _), c)| p == program && c.statement.as_deref() == Some(name))
205 }
206
207 pub fn closed(&mut self, program: &str, name: &str) {
208 self.cursors.remove(&(program.to_owned(), name.to_owned()));
209 }
210
211 pub fn committed(&mut self) {
215 self.cursors.retain(|_, c| c.with_hold);
216 for c in self.cursors.values_mut() {
217 c.positioned = false;
218 c.held.drain(..c.current);
219 c.current = 0;
220 }
221 let cursors = &self.cursors;
222 self.prepared.retain(|(program, name), _| cursors.iter().any(|((p, _), c)| p == program && c.statement.as_deref() == Some(name)));
223 self.pending = false;
224 }
225
226 pub fn rolled_back(&mut self) {
227 self.cursors.clear();
228 self.prepared.clear();
229 self.pending = false;
230 }
231
232 pub fn settle(&mut self, program: &str, commit: bool) -> Answer {
236 let answer = if !self.pending && self.cursors.is_empty() {
237 Outcome::ok()
238 } else {
239 let verb = if commit { "COMMIT" } else { "ROLLBACK" };
240 let call = Call { program, ordinal: 0, verb, cursor: None, text: verb, inputs: &[] };
241 if commit { self.database.commit(&call)? } else { self.database.rollback(&call)? }
242 };
243 if commit && answer.sqlcode >= 0 {
244 self.committed();
245 } else {
246 self.rolled_back();
247 }
248 Ok(answer)
249 }
250
251 pub fn end_task(&mut self, program: &str, commit: bool) -> Answer {
254 let answer = self.settle(program, commit)?;
255 if !self.cursors.is_empty() {
256 self.database.close_all()?;
257 self.cursors.clear();
258 }
259 self.prepared.clear();
260 Ok(answer)
261 }
262}