1use std::sync::{Arc, Weak};
46
47use radixdb_core::{Error, Result};
48use radixdb_executor::context::ExecutionContext;
49use radixdb_executor::PreparedProgram;
50
51use super::database::{Database, DatabaseInnerHandle, FromValue};
52use super::params::Params;
53use super::rows::Rows;
54
55#[derive(Clone)]
72pub struct Statement {
73 db_weak: Weak<DatabaseInnerHandle>,
75 sql: String,
76 program: PreparedProgram,
77}
78
79impl Statement {
80 pub(crate) fn new(
86 db_weak: Weak<DatabaseInnerHandle>,
87 sql: String,
88 db: &Database,
89 ) -> Result<Self> {
90 let program = {
91 let executor = db
92 .executor()
93 .lock()
94 .map_err(|_| Error::LockAcquisitionFailed("executor".to_string()))?;
95 executor.prepare_program(&sql)?
96 };
97
98 Ok(Self {
99 db_weak,
100 sql,
101 program,
102 })
103 }
104
105 #[inline]
108 fn get_db(&self) -> Result<Database> {
109 let db = self
110 .db_weak
111 .upgrade()
112 .map(Database::from_inner)
113 .ok_or_else(|| Error::internal("Database was dropped"))?;
114 db.ensure_open()?;
115 Ok(db)
116 }
117
118 pub(crate) fn validate_owner(&self, owner: &Arc<DatabaseInnerHandle>) -> Result<()> {
119 let actual = self
120 .db_weak
121 .upgrade()
122 .ok_or_else(|| Error::internal("Database was dropped"))?;
123 if Arc::ptr_eq(&actual, owner) {
124 Database::from_inner(actual).ensure_open()
125 } else {
126 Err(Error::invalid_argument(
127 "prepared Statement belongs to a different Database connection",
128 ))
129 }
130 }
131
132 pub(crate) fn prepared_program(&self) -> &PreparedProgram {
133 &self.program
134 }
135
136 pub fn execute<P: Params>(&self, params: P) -> Result<i64> {
148 let db = self.get_db()?;
149 let executor = db
150 .executor()
151 .lock()
152 .map_err(|_| Error::LockAcquisitionFailed("executor".to_string()))?;
153 let ctx = ExecutionContext::with_params(params.into_params());
154 let result = executor.execute_prepared_program(&self.program, &ctx)?;
155 Ok(result.rows_affected())
156 }
157
158 pub fn query<P: Params>(&self, params: P) -> Result<Rows> {
173 let db = self.get_db()?;
174 let executor = db
175 .executor()
176 .lock()
177 .map_err(|_| Error::LockAcquisitionFailed("executor".to_string()))?;
178 let ctx = ExecutionContext::with_params(params.into_params());
179 let result = executor.execute_prepared_program(&self.program, &ctx)?;
180 Ok(Rows::new(result))
181 }
182
183 #[doc(hidden)]
186 pub fn query_for_server(&self, context: super::ServerExecutionContext) -> Result<Rows> {
187 let db = self.get_db()?;
188 let executor = db
189 .executor()
190 .lock()
191 .map_err(|_| Error::LockAcquisitionFailed("executor".to_string()))?;
192 let result = executor.execute_prepared_program(&self.program, context.inner())?;
193 Ok(Rows::new(result))
194 }
195
196 pub fn query_one<T: FromValue, P: Params>(&self, params: P) -> Result<T> {
205 let row = self.query(params)?.next().ok_or(Error::NoRowsReturned)??;
206 row.get(0)
207 }
208
209 pub fn query_opt<T: FromValue, P: Params>(&self, params: P) -> Result<Option<T>> {
218 match self.query(params)?.next() {
219 None => Ok(None),
220 Some(Err(e)) => Err(e),
221 Some(Ok(row)) => Ok(Some(row.get(0)?)),
222 }
223 }
224
225 pub fn sql(&self) -> &str {
227 &self.sql
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_prepared_statement_execute() {
237 let db = Database::open_in_memory().unwrap();
238 db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", ())
239 .unwrap();
240
241 let stmt = db.prepare("INSERT INTO users VALUES ($1, $2)").unwrap();
242
243 stmt.execute((1, "Alice")).unwrap();
244 stmt.execute((2, "Bob")).unwrap();
245 stmt.execute((3, "Charlie")).unwrap();
246
247 let count: i64 = db.query_one("SELECT COUNT(*) FROM users", ()).unwrap();
248 assert_eq!(count, 3);
249 }
250
251 #[test]
252 fn test_prepared_statement_query() {
253 let db = Database::open_in_memory().unwrap();
254 db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", ())
255 .unwrap();
256 db.execute(
257 "INSERT INTO users VALUES ($1, $2), ($3, $4), ($5, $6)",
258 (1, "Alice", 2, "Bob", 3, "Charlie"),
259 )
260 .unwrap();
261
262 let stmt = db.prepare("SELECT name FROM users WHERE id = $1").unwrap();
263
264 let name: String = stmt.query_one((1,)).unwrap();
265 assert_eq!(name, "Alice");
266
267 let name: String = stmt.query_one((2,)).unwrap();
268 assert_eq!(name, "Bob");
269
270 let name: String = stmt.query_one((3,)).unwrap();
271 assert_eq!(name, "Charlie");
272 }
273
274 #[test]
275 fn test_prepared_statement_query_opt() {
276 let db = Database::open_in_memory().unwrap();
277 db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", ())
278 .unwrap();
279 db.execute("INSERT INTO users VALUES ($1, $2)", (1, "Alice"))
280 .unwrap();
281
282 let stmt = db.prepare("SELECT name FROM users WHERE id = $1").unwrap();
283
284 let name: Option<String> = stmt.query_opt((1,)).unwrap();
285 assert_eq!(name, Some("Alice".to_string()));
286
287 let name: Option<String> = stmt.query_opt((999,)).unwrap();
288 assert_eq!(name, None);
289 }
290
291 #[test]
292 fn test_prepared_statement_sql() {
293 let db = Database::open_in_memory().unwrap();
294 let stmt = db.prepare("SELECT 1").unwrap();
295 assert_eq!(stmt.sql(), "SELECT 1");
296 }
297}