1use crate::error::Result;
2use crate::types::Value;
3
4#[derive(Debug, Clone, PartialEq)]
6pub struct ColumnInfo {
8 pub name: String,
10 pub data_type: u32,
12 pub nullable: bool,
14 pub precision: Option<u32>,
16 pub scale: Option<u32>,
18}
19
20#[derive(Debug, Clone)]
22pub struct QueryResult {
23 rows: Vec<Vec<Value>>,
24 columns: Vec<ColumnInfo>,
25 affected_rows: u64,
26 command_tag: Option<String>,
27}
28
29impl QueryResult {
30 pub fn from_rows(rows: Vec<Vec<Value>>, columns: Vec<ColumnInfo>) -> Self {
32 Self {
33 rows,
34 columns,
35 affected_rows: 0,
36 command_tag: None,
37 }
38 }
39
40 pub fn from_command(affected_rows: u64, command_tag: Option<String>, _query: String) -> Self {
42 Self {
43 rows: Vec::new(),
44 columns: Vec::new(),
45 affected_rows,
46 command_tag,
47 }
48 }
49
50 pub fn rows(&self) -> &[Vec<Value>] {
52 &self.rows
53 }
54
55 pub fn columns(&self) -> &[ColumnInfo] {
57 &self.columns
58 }
59
60 pub fn affected_rows(&self) -> u64 {
62 self.affected_rows
63 }
64
65 pub fn command_tag(&self) -> Option<&str> {
67 self.command_tag.as_deref()
68 }
69
70 pub fn len(&self) -> usize {
72 self.rows.len()
73 }
74
75 pub fn is_empty(&self) -> bool {
77 self.rows.is_empty()
78 }
79
80 pub fn get(&self, index: usize) -> Option<&[Value]> {
82 self.rows.get(index).map(|row| row.as_slice())
83 }
84
85 pub fn iter(&self) -> std::slice::Iter<'_, Vec<Value>> {
87 self.rows.iter()
88 }
89
90 pub fn empty() -> Self {
92 Self {
93 rows: Vec::new(),
94 columns: Vec::new(),
95 affected_rows: 0,
96 command_tag: None,
97 }
98 }
99}
100
101#[derive(Debug, Clone)]
103pub struct Statement {
104 client: crate::client::Client,
105 id: String,
106 query: String,
107 parameters: Vec<Value>,
108}
109
110impl Statement {
111 pub fn new(client: crate::client::Client, id: String, query: String) -> Self {
113 Self {
114 client,
115 id,
116 query,
117 parameters: Vec::new(),
118 }
119 }
120
121 pub fn id(&self) -> &str {
123 &self.id
124 }
125
126 pub fn sql(&self) -> &str {
128 &self.query
129 }
130
131 pub fn parameters(&self) -> &[Value] {
133 &self.parameters
134 }
135
136 pub fn add_param(&mut self, value: Value) {
138 self.parameters.push(value);
139 }
140
141 pub fn clear_params(&mut self) {
143 self.parameters.clear();
144 }
145
146 pub async fn execute(&self, params: &[Value]) -> Result<u64> {
148 let client = &self.client;
149 let mut conn = client.connection.lock().await;
150 let result = conn.execute_prepared(&self.id, params).await?;
151 Ok(result.affected_rows())
152 }
153}
154
155#[derive(Debug)]
157pub struct Transaction {
158 client: crate::client::Client,
159 committed: bool,
160 rolled_back: bool,
161}
162
163impl Transaction {
164 pub fn new(client: crate::client::Client) -> Self {
166 Self {
167 client,
168 committed: false,
169 rolled_back: false,
170 }
171 }
172
173 pub async fn commit(&mut self) -> Result<()> {
175 if !self.committed && !self.rolled_back {
176 self.client.query("COMMIT").await?;
177 self.committed = true;
178 }
179 Ok(())
180 }
181
182 pub async fn rollback(&mut self) -> Result<()> {
184 if !self.committed && !self.rolled_back {
185 self.client.query("ROLLBACK").await?;
186 self.rolled_back = true;
187 }
188 Ok(())
189 }
190
191 pub async fn query(&self, query: &str) -> Result<QueryResult> {
193 self.client.query(query).await
194 }
195
196 pub async fn execute(&self, query: &str, params: &[Value]) -> Result<u64> {
198 let stmt = self.client.prepare(query).await?;
199 stmt.execute(params).await
200 }
201}
202impl Drop for Transaction {
203 fn drop(&mut self) {
204 if !self.committed && !self.rolled_back {
205 log::warn!("Transaction dropped without explicit commit or rollback. Consider using explicit rollback.");
208
209 }
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_query_result_from_rows() {
221 let rows = vec![vec![Value::Int(1), Value::String("test".to_string())]];
222 let columns = vec![
223 ColumnInfo {
224 name: "id".to_string(),
225 data_type: 23,
226 nullable: false,
227 precision: None,
228 scale: None,
229 },
230 ColumnInfo {
231 name: "name".to_string(),
232 data_type: 25,
233 nullable: true,
234 precision: None,
235 scale: None,
236 },
237 ];
238
239 let result = QueryResult::from_rows(rows.clone(), columns.clone());
240 assert_eq!(result.rows().len(), 1);
241 assert_eq!(result.columns().len(), 2);
242 assert_eq!(result.affected_rows(), 0);
243 }
244
245 #[test]
246 fn test_query_result_from_command() {
247 let result = QueryResult::from_command(5, Some("INSERT 5".to_string()), "INSERT INTO users VALUES (1, 'test')".to_string());
248 assert_eq!(result.affected_rows(), 5);
249 assert_eq!(result.command_tag(), Some("INSERT 5"));
250 assert!(result.is_empty());
251 }
252
253
254}