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 Real,
61 Double,
62 Date,
63 Time,
64 Timestamp(u8),
66 Binary(u16),
67 VarBinary(u16),
68 Other(String),
70}
71
72impl Outcome {
73 pub fn ok() -> Self {
74 Self { sqlcode: 0, sqlstate: "00000".into(), affected: 0, rows: Vec::new(), tokens: String::new(), columns: Vec::new(), parameters: Vec::new() }
75 }
76
77 pub fn rows(rows: Vec<Vec<Value>>) -> Self {
78 Self { rows, ..Self::ok() }
79 }
80
81 pub fn error(sqlcode: i32, sqlstate: &str) -> Self {
82 Self { sqlcode, sqlstate: sqlstate.into(), ..Self::ok() }
83 }
84}
85
86#[derive(Clone, Debug, PartialEq, Eq)]
89pub struct Abandoned {
90 pub code: &'static str,
91 pub message: String,
92}
93
94pub type Answer = Result<Outcome, Abandoned>;
95
96pub trait Database {
97 fn execute(&mut self, call: &Call) -> Answer;
100 fn prepare(&mut self, call: &Call) -> Answer;
102 fn open(&mut self, call: &Call) -> Answer;
103 fn fetch(&mut self, call: &Call) -> Answer;
105 fn fetch_rows(&mut self, call: &Call, rows: u32) -> Answer;
107 fn insert_rows(&mut self, call: &Call, rows: &[Vec<Value>], atomic: bool) -> Answer;
111 fn call(&mut self, call: &Call) -> Answer {
115 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()) })
116 }
117 fn close(&mut self, call: &Call) -> Answer;
118 fn commit(&mut self, call: &Call) -> Answer;
119 fn rollback(&mut self, call: &Call) -> Answer;
120 fn close_all(&mut self) -> Result<(), Abandoned> {
123 Ok(())
124 }
125}
126
127#[derive(Clone, Debug, PartialEq)]
128pub struct OpenCursor {
129 pub with_hold: bool,
130 pub positioned: bool,
132 pub statement: Option<String>,
134 pub held: VecDeque<Vec<Value>>,
138 pub current: usize,
140 pub rowset_size: Option<u32>,
143}
144
145impl OpenCursor {
146 pub fn on_backend_row(&self) -> bool {
148 self.current == 1 && self.held.len() == 1
149 }
150}
151
152#[derive(Clone, Debug, PartialEq, Eq)]
154pub struct Prepared {
155 pub text: String,
156 pub query: bool,
157 pub markers: usize,
158 pub columns: Vec<Column>,
159}
160
161pub struct Session<'w> {
164 pub database: &'w mut dyn Database,
165 cursors: HashMap<(String, String), OpenCursor>,
167 prepared: HashMap<(String, String), Prepared>,
170 pub pending: bool,
172}
173
174impl<'w> Session<'w> {
175 pub fn new(database: &'w mut (dyn Database + '_)) -> Self {
176 Self { database, cursors: HashMap::new(), prepared: HashMap::new(), pending: false }
177 }
178
179 pub fn cursor(&mut self, program: &str, name: &str) -> Option<&mut OpenCursor> {
180 self.cursors.get_mut(&(program.to_owned(), name.to_owned()))
181 }
182
183 pub fn opened(&mut self, program: &str, name: &str, with_hold: bool, statement: Option<&str>) {
184 let cursor = OpenCursor { with_hold, positioned: false, statement: statement.map(str::to_owned), held: VecDeque::new(), current: 0, rowset_size: None };
185 self.cursors.insert((program.to_owned(), name.to_owned()), cursor);
186 }
187
188 pub fn prepared(&self, program: &str, name: &str) -> Option<&Prepared> {
189 self.prepared.get(&(program.to_owned(), name.to_owned()))
190 }
191
192 pub fn prepare(&mut self, program: &str, name: &str, statement: Option<Prepared>) {
193 let key = (program.to_owned(), name.to_owned());
194 match statement {
195 Some(p) => self.prepared.insert(key, p),
196 None => self.prepared.remove(&key),
197 };
198 }
199
200 pub fn running(&self, program: &str, name: &str) -> bool {
202 self.cursors.iter().any(|((p, _), c)| p == program && c.statement.as_deref() == Some(name))
203 }
204
205 pub fn closed(&mut self, program: &str, name: &str) {
206 self.cursors.remove(&(program.to_owned(), name.to_owned()));
207 }
208
209 pub fn committed(&mut self) {
213 self.cursors.retain(|_, c| c.with_hold);
214 for c in self.cursors.values_mut() {
215 c.positioned = false;
216 c.held.drain(..c.current);
217 c.current = 0;
218 }
219 let cursors = &self.cursors;
220 self.prepared.retain(|(program, name), _| cursors.iter().any(|((p, _), c)| p == program && c.statement.as_deref() == Some(name)));
221 self.pending = false;
222 }
223
224 pub fn rolled_back(&mut self) {
225 self.cursors.clear();
226 self.prepared.clear();
227 self.pending = false;
228 }
229
230 pub fn settle(&mut self, program: &str, commit: bool) -> Answer {
234 let answer = if !self.pending && self.cursors.is_empty() {
235 Outcome::ok()
236 } else {
237 let verb = if commit { "COMMIT" } else { "ROLLBACK" };
238 let call = Call { program, ordinal: 0, verb, cursor: None, text: verb, inputs: &[] };
239 if commit { self.database.commit(&call)? } else { self.database.rollback(&call)? }
240 };
241 if commit && answer.sqlcode >= 0 {
242 self.committed();
243 } else {
244 self.rolled_back();
245 }
246 Ok(answer)
247 }
248
249 pub fn end_task(&mut self, program: &str, commit: bool) -> Answer {
252 let answer = self.settle(program, commit)?;
253 if !self.cursors.is_empty() {
254 self.database.close_all()?;
255 self.cursors.clear();
256 }
257 self.prepared.clear();
258 Ok(answer)
259 }
260}