1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::Result;
5use vibesql_executor::{
6 CursorExecutor, CursorStore, FetchResult as CursorFetchResult, PreparedStatement,
7 PreparedStatementCache, PreparedStatementCacheStats,
8};
9use vibesql_types::SqlValue;
10
11use crate::registry::SharedDatabase;
12use crate::transaction::SessionTransactionManager;
13
14pub struct Session {
16 #[allow(dead_code)]
18 pub database: String,
19 #[allow(dead_code)]
21 pub user: String,
22 db: SharedDatabase,
24 stmt_cache: Arc<PreparedStatementCache>,
26 named_statements: HashMap<String, Arc<PreparedStatement>>,
28 cursors: CursorStore,
30 txn_manager: SessionTransactionManager,
33}
34
35#[derive(Debug)]
37pub enum ExecutionResult {
38 Select {
39 rows: Vec<Row>,
40 columns: Vec<Column>,
41 },
42 Insert {
43 rows_affected: usize,
44 },
45 Update {
46 rows_affected: usize,
47 },
48 Delete {
49 rows_affected: usize,
50 },
51 CreateTable,
52 CreateIndex,
53 CreateView,
54 DropTable,
55 DropIndex,
56 DropView,
57 Analyze {
58 tables_analyzed: usize,
59 },
60 Prepare {
62 statement_name: String,
63 },
64 Deallocate {
66 statement_name: String,
67 },
68 DeclareCursor {
70 cursor_name: String,
71 },
72 OpenCursor {
74 cursor_name: String,
75 },
76 Fetch {
78 rows: Vec<Row>,
79 columns: Vec<Column>,
80 },
81 CloseCursor {
83 cursor_name: String,
84 },
85 Begin,
87 Commit,
89 Rollback,
91 Other {
92 message: String,
93 },
94}
95
96impl ExecutionResult {
97 pub fn statement_type(&self) -> &str {
99 match self {
100 ExecutionResult::Select { .. } => "SELECT",
101 ExecutionResult::Insert { .. } => "INSERT",
102 ExecutionResult::Update { .. } => "UPDATE",
103 ExecutionResult::Delete { .. } => "DELETE",
104 ExecutionResult::CreateTable => "CREATE_TABLE",
105 ExecutionResult::CreateIndex => "CREATE_INDEX",
106 ExecutionResult::CreateView => "CREATE_VIEW",
107 ExecutionResult::DropTable => "DROP_TABLE",
108 ExecutionResult::DropIndex => "DROP_INDEX",
109 ExecutionResult::DropView => "DROP_VIEW",
110 ExecutionResult::Analyze { .. } => "ANALYZE",
111 ExecutionResult::Prepare { .. } => "PREPARE",
112 ExecutionResult::Deallocate { .. } => "DEALLOCATE",
113 ExecutionResult::DeclareCursor { .. } => "DECLARE_CURSOR",
114 ExecutionResult::OpenCursor { .. } => "OPEN_CURSOR",
115 ExecutionResult::Fetch { .. } => "FETCH",
116 ExecutionResult::CloseCursor { .. } => "CLOSE_CURSOR",
117 ExecutionResult::Begin => "BEGIN",
118 ExecutionResult::Commit => "COMMIT",
119 ExecutionResult::Rollback => "ROLLBACK",
120 ExecutionResult::Other { .. } => "OTHER",
121 }
122 }
123
124 pub fn rows_affected(&self) -> u64 {
126 match self {
127 ExecutionResult::Select { rows, .. } => rows.len() as u64,
128 ExecutionResult::Insert { rows_affected } => *rows_affected as u64,
129 ExecutionResult::Update { rows_affected } => *rows_affected as u64,
130 ExecutionResult::Delete { rows_affected } => *rows_affected as u64,
131 ExecutionResult::Fetch { rows, .. } => rows.len() as u64,
132 _ => 0,
133 }
134 }
135}
136
137#[derive(Debug, Clone)]
138pub struct Column {
139 pub name: String,
140}
141
142#[derive(Debug, Clone)]
143pub struct Row {
144 pub values: Vec<vibesql_types::SqlValue>,
145}
146
147impl Session {
148 pub fn new(database: String, user: String, db: SharedDatabase) -> Self {
153 Self {
154 database,
155 user,
156 db,
157 stmt_cache: Arc::new(PreparedStatementCache::default_cache()),
158 named_statements: HashMap::new(),
159 cursors: CursorStore::new(),
160 txn_manager: SessionTransactionManager::new(),
161 }
162 }
163
164 pub fn new_standalone(database: String, user: String) -> Self {
169 let db = Arc::new(tokio::sync::RwLock::new(vibesql_storage::Database::new()));
170 Self::new(database, user, db)
171 }
172
173 pub fn in_transaction(&self) -> bool {
175 self.txn_manager.in_transaction()
176 }
177
178 pub fn shared_database(&self) -> &SharedDatabase {
180 &self.db
181 }
182
183 #[allow(dead_code)]
185 pub fn with_cache(
186 database: String,
187 user: String,
188 db: SharedDatabase,
189 cache: Arc<PreparedStatementCache>,
190 ) -> Self {
191 Self {
192 database,
193 user,
194 db,
195 stmt_cache: cache,
196 named_statements: HashMap::new(),
197 cursors: CursorStore::new(),
198 txn_manager: SessionTransactionManager::new(),
199 }
200 }
201
202 #[allow(dead_code)]
213 pub fn prepare(&self, sql: &str) -> Result<Arc<PreparedStatement>> {
214 self.stmt_cache.get_or_prepare(sql).map_err(|e| anyhow::anyhow!("{}", e))
215 }
216
217 #[allow(dead_code)]
222 pub async fn execute_prepared(
223 &mut self,
224 stmt: &PreparedStatement,
225 params: &[SqlValue],
226 ) -> Result<ExecutionResult> {
227 let bound_stmt = stmt.bind(params).map_err(|e| anyhow::anyhow!("{}", e))?;
229
230 self.execute_statement(&bound_stmt).await
232 }
233
234 pub async fn execute(&mut self, sql: &str) -> Result<ExecutionResult> {
239 let prepared = self.stmt_cache.get_or_prepare(sql).map_err(|e| anyhow::anyhow!("{}", e))?;
241
242 self.execute_statement(prepared.statement()).await
244 }
245
246 #[allow(dead_code)]
250 pub async fn execute_with_params(
251 &mut self,
252 sql: &str,
253 params: &[SqlValue],
254 ) -> Result<ExecutionResult> {
255 let prepared = self.prepare(sql)?;
256 self.execute_prepared(&prepared, params).await
257 }
258
259 async fn execute_statement(
261 &mut self,
262 statement: &vibesql_ast::Statement,
263 ) -> Result<ExecutionResult> {
264 let mut db = self.db.write().await;
267
268 match statement {
269 vibesql_ast::Statement::Select(select_stmt) => {
270 let executor = vibesql_executor::SelectExecutor::new(&db);
271 let rows = executor.execute(select_stmt)?;
272
273 let result_rows: Vec<Row> =
275 rows.iter().map(|r| Row { values: r.values.to_vec() }).collect();
276
277 let columns = if !rows.is_empty() {
279 (0..rows[0].values.len())
280 .map(|i| Column { name: format!("col{}", i) })
281 .collect()
282 } else {
283 Vec::new()
284 };
285
286 Ok(ExecutionResult::Select { rows: result_rows, columns })
287 }
288
289 vibesql_ast::Statement::Insert(insert_stmt) => {
290 let affected =
291 vibesql_executor::InsertExecutor::execute(&mut db, insert_stmt)?;
292 self.stmt_cache.invalidate_table(&insert_stmt.table_name);
294 Ok(ExecutionResult::Insert { rows_affected: affected })
295 }
296
297 vibesql_ast::Statement::Update(update_stmt) => {
298 let affected =
299 vibesql_executor::UpdateExecutor::execute(update_stmt, &mut db)?;
300 self.stmt_cache.invalidate_table(&update_stmt.table_name);
302 Ok(ExecutionResult::Update { rows_affected: affected })
303 }
304
305 vibesql_ast::Statement::Delete(delete_stmt) => {
306 let affected =
307 vibesql_executor::DeleteExecutor::execute(delete_stmt, &mut db)?;
308 self.stmt_cache.invalidate_table(&delete_stmt.table_name);
310 Ok(ExecutionResult::Delete { rows_affected: affected })
311 }
312
313 vibesql_ast::Statement::CreateTable(create_stmt) => {
314 vibesql_executor::CreateTableExecutor::execute(create_stmt, &mut db)?;
315 Ok(ExecutionResult::CreateTable)
316 }
317
318 vibesql_ast::Statement::CreateIndex(index_stmt) => {
319 vibesql_executor::CreateIndexExecutor::execute(index_stmt, &mut db)?;
320 Ok(ExecutionResult::CreateIndex)
321 }
322
323 vibesql_ast::Statement::CreateView(view_stmt) => {
324 vibesql_executor::advanced_objects::execute_create_view(view_stmt, &mut db)?;
325 Ok(ExecutionResult::CreateView)
326 }
327
328 vibesql_ast::Statement::DropTable(drop_stmt) => {
329 vibesql_executor::DropTableExecutor::execute(drop_stmt, &mut db)?;
330 self.stmt_cache.invalidate_table(&drop_stmt.table_name);
332 Ok(ExecutionResult::DropTable)
333 }
334
335 vibesql_ast::Statement::DropIndex(drop_stmt) => {
336 vibesql_executor::DropIndexExecutor::execute(drop_stmt, &mut db)?;
337 Ok(ExecutionResult::DropIndex)
338 }
339
340 vibesql_ast::Statement::DropView(drop_stmt) => {
341 vibesql_executor::advanced_objects::execute_drop_view(drop_stmt, &mut db)?;
342 Ok(ExecutionResult::DropView)
343 }
344
345 vibesql_ast::Statement::Analyze(analyze_stmt) => {
346 let message =
347 vibesql_executor::AnalyzeExecutor::execute(analyze_stmt, &mut db)?;
348 let tables_analyzed =
351 if analyze_stmt.table_name.is_some() { 1 } else { db.list_tables().len() };
352 let _ = message; Ok(ExecutionResult::Analyze { tables_analyzed })
354 }
355
356 vibesql_ast::Statement::Prepare(prepare_stmt) => {
357 drop(db);
359 self.execute_prepare(prepare_stmt)
360 }
361
362 vibesql_ast::Statement::Execute(execute_stmt) => {
363 drop(db);
365 self.execute_execute(execute_stmt).await
366 }
367
368 vibesql_ast::Statement::Deallocate(deallocate_stmt) => {
369 drop(db);
371 self.execute_deallocate(deallocate_stmt)
372 }
373
374 vibesql_ast::Statement::DeclareCursor(declare_stmt) => {
375 drop(db);
377 self.execute_declare_cursor(declare_stmt)
378 }
379
380 vibesql_ast::Statement::OpenCursor(open_stmt) => {
381 CursorExecutor::open(&mut self.cursors, open_stmt, &db)
383 .map_err(|e| anyhow::anyhow!("{}", e))?;
384 Ok(ExecutionResult::OpenCursor { cursor_name: open_stmt.cursor_name.clone() })
385 }
386
387 vibesql_ast::Statement::Fetch(fetch_stmt) => {
388 drop(db);
390 self.execute_fetch(fetch_stmt)
391 }
392
393 vibesql_ast::Statement::CloseCursor(close_stmt) => {
394 drop(db);
396 self.execute_close_cursor(close_stmt)
397 }
398
399 vibesql_ast::Statement::BeginTransaction(_) => {
400 drop(db);
402 self.begin_transaction().await
403 }
404
405 vibesql_ast::Statement::Commit(_) => {
406 drop(db);
408 self.commit().await
409 }
410
411 vibesql_ast::Statement::Rollback(_) => {
412 drop(db);
414 self.rollback().await
415 }
416
417 vibesql_ast::Statement::RollbackToSavepoint(_savepoint_stmt) => {
418 Ok(ExecutionResult::Other { message: "ROLLBACK TO SAVEPOINT".to_string() })
420 }
421
422 vibesql_ast::Statement::Savepoint(_savepoint_stmt) => {
423 Ok(ExecutionResult::Other { message: "SAVEPOINT".to_string() })
425 }
426
427 vibesql_ast::Statement::ReleaseSavepoint(_release_stmt) => {
428 Ok(ExecutionResult::Other { message: "RELEASE SAVEPOINT".to_string() })
430 }
431
432 _ => {
433 Ok(ExecutionResult::Other { message: "Command completed successfully".to_string() })
435 }
436 }
437 }
438
439 fn execute_prepare(
441 &mut self,
442 prepare_stmt: &vibesql_ast::PrepareStmt,
443 ) -> Result<ExecutionResult> {
444 use vibesql_ast::PreparedStatementBody;
445
446 let name = prepare_stmt.name.clone();
447
448 let sql = match &prepare_stmt.statement {
450 PreparedStatementBody::SqlString(s) => s.clone(),
451 PreparedStatementBody::ParsedStatement(_stmt) => {
452 return Err(anyhow::anyhow!(
457 "PREPARE ... AS syntax not yet supported. Use PREPARE ... FROM 'sql_string' instead"
458 ));
459 }
460 };
461
462 let prepared = self
464 .stmt_cache
465 .get_or_prepare(&sql)
466 .map_err(|e| anyhow::anyhow!("Failed to prepare statement: {}", e))?;
467
468 self.named_statements.insert(name.clone(), prepared);
470
471 Ok(ExecutionResult::Prepare { statement_name: name })
472 }
473
474 fn execute_execute(
476 &mut self,
477 execute_stmt: &vibesql_ast::ExecuteStmt,
478 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<ExecutionResult>> + Send + '_>>
479 {
480 let name = execute_stmt.name.clone();
481 let param_exprs = execute_stmt.params.clone();
482
483 Box::pin(async move {
484 let prepared = self
486 .named_statements
487 .get(&name)
488 .ok_or_else(|| anyhow::anyhow!("Prepared statement '{}' not found", name))?
489 .clone();
490
491 let params: Vec<SqlValue> =
493 param_exprs.iter().map(evaluate_expression).collect::<Result<Vec<_>>>()?;
494
495 self.execute_prepared(&prepared, ¶ms).await
497 })
498 }
499
500 fn execute_deallocate(
502 &mut self,
503 deallocate_stmt: &vibesql_ast::DeallocateStmt,
504 ) -> Result<ExecutionResult> {
505 use vibesql_ast::DeallocateTarget;
506
507 match &deallocate_stmt.target {
508 DeallocateTarget::Name(name) => {
509 if self.named_statements.remove(name).is_none() {
510 return Err(anyhow::anyhow!("Prepared statement '{}' not found", name));
511 }
512 Ok(ExecutionResult::Deallocate { statement_name: name.clone() })
513 }
514 DeallocateTarget::All => {
515 let count = self.named_statements.len();
516 self.named_statements.clear();
517 Ok(ExecutionResult::Other {
518 message: format!("Deallocated {} prepared statement(s)", count),
519 })
520 }
521 }
522 }
523
524 #[allow(dead_code)]
526 pub fn cache_stats(&self) -> PreparedStatementCacheStats {
527 self.stmt_cache.stats()
528 }
529
530 #[allow(dead_code)]
532 pub fn clear_cache(&self) {
533 self.stmt_cache.clear();
534 }
535
536 pub async fn begin_transaction(&mut self) -> Result<ExecutionResult> {
542 self.txn_manager.begin().map_err(|e| anyhow::anyhow!("{}", e))?;
544
545 let mut db = self.db.write().await;
547 db.begin_transaction()
548 .map_err(|e| anyhow::anyhow!("Failed to begin transaction: {}", e))?;
549
550 Ok(ExecutionResult::Begin)
551 }
552
553 pub async fn commit(&mut self) -> Result<ExecutionResult> {
558 let _changes = self.txn_manager.commit().map_err(|e| anyhow::anyhow!("{}", e))?;
560
561 let mut db = self.db.write().await;
563 db.commit_transaction()
564 .map_err(|e| anyhow::anyhow!("Failed to commit transaction: {}", e))?;
565
566 Ok(ExecutionResult::Commit)
567 }
568
569 pub async fn rollback(&mut self) -> Result<ExecutionResult> {
574 self.txn_manager.rollback().map_err(|e| anyhow::anyhow!("{}", e))?;
576
577 let mut db = self.db.write().await;
579 db.rollback_transaction()
580 .map_err(|e| anyhow::anyhow!("Failed to rollback transaction: {}", e))?;
581
582 Ok(ExecutionResult::Rollback)
583 }
584
585 fn execute_declare_cursor(
587 &mut self,
588 stmt: &vibesql_ast::DeclareCursorStmt,
589 ) -> Result<ExecutionResult> {
590 CursorExecutor::declare(&mut self.cursors, stmt).map_err(|e| anyhow::anyhow!("{}", e))?;
591 Ok(ExecutionResult::DeclareCursor { cursor_name: stmt.cursor_name.clone() })
592 }
593
594 fn execute_fetch(&mut self, stmt: &vibesql_ast::FetchStmt) -> Result<ExecutionResult> {
596 let fetch_result: CursorFetchResult =
597 CursorExecutor::fetch(&mut self.cursors, stmt).map_err(|e| anyhow::anyhow!("{}", e))?;
598
599 let rows: Vec<Row> =
601 fetch_result.rows.iter().map(|r| Row { values: r.values.to_vec() }).collect();
602 let columns: Vec<Column> =
603 fetch_result.columns.iter().map(|name| Column { name: name.clone() }).collect();
604
605 Ok(ExecutionResult::Fetch { rows, columns })
606 }
607
608 fn execute_close_cursor(
610 &mut self,
611 stmt: &vibesql_ast::CloseCursorStmt,
612 ) -> Result<ExecutionResult> {
613 CursorExecutor::close(&mut self.cursors, stmt).map_err(|e| anyhow::anyhow!("{}", e))?;
614 Ok(ExecutionResult::CloseCursor { cursor_name: stmt.cursor_name.clone() })
615 }
616}
617
618fn evaluate_expression(expr: &vibesql_ast::Expression) -> Result<SqlValue> {
620 use vibesql_ast::Expression;
621
622 match expr {
623 Expression::Literal(val) => Ok(val.clone()),
625 Expression::UnaryOp { op, expr: operand } => {
626 if let vibesql_ast::UnaryOperator::Minus = op {
628 let val = evaluate_expression(operand)?;
629 match val {
630 SqlValue::Integer(n) => Ok(SqlValue::Integer(-n)),
631 SqlValue::Bigint(n) => Ok(SqlValue::Bigint(-n)),
632 SqlValue::Float(n) => Ok(SqlValue::Float(-n)),
633 SqlValue::Double(n) => Ok(SqlValue::Double(-n)),
634 SqlValue::Numeric(n) => Ok(SqlValue::Numeric(-n)),
635 _ => Err(anyhow::anyhow!("Cannot negate non-numeric value")),
636 }
637 } else {
638 Err(anyhow::anyhow!("Unsupported unary operator in EXECUTE parameter"))
639 }
640 }
641 _ => Err(anyhow::anyhow!(
642 "Unsupported expression type in EXECUTE parameters. Only literals are currently supported."
643 )),
644 }
645}
646
647#[cfg(test)]
648mod tests {
649 use super::*;
650 use tokio::sync::RwLock;
651 use vibesql_storage::Database;
652
653 fn create_shared_db() -> SharedDatabase {
654 Arc::new(RwLock::new(Database::new()))
655 }
656
657 #[test]
658 fn test_session_creation() {
659 let db = create_shared_db();
660 let session = Session::new("testdb".to_string(), "testuser".to_string(), db);
661 assert_eq!(session.database, "testdb");
662 assert_eq!(session.user, "testuser");
663 assert!(!session.in_transaction());
664 }
665
666 #[tokio::test]
667 async fn test_transaction_state() {
668 let db = create_shared_db();
669 let mut session = Session::new("testdb".to_string(), "testuser".to_string(), db);
670
671 assert!(!session.in_transaction());
673
674 assert!(session.begin_transaction().await.is_ok());
676 assert!(session.in_transaction());
677
678 assert!(session.begin_transaction().await.is_err());
680
681 assert!(session.commit().await.is_ok());
683 assert!(!session.in_transaction());
684
685 assert!(session.commit().await.is_err());
687 }
688
689 #[tokio::test]
690 async fn test_prepare_and_execute() {
691 let db = create_shared_db();
692 let mut session = Session::new("testdb".to_string(), "testuser".to_string(), db);
693
694 session.execute("CREATE TABLE users (id INT, name VARCHAR(100))").await.unwrap();
696
697 let stmt = session.prepare("SELECT * FROM users WHERE id = 1").unwrap();
699 assert_eq!(stmt.param_count(), 0);
700
701 let result = session.execute_prepared(&stmt, &[]).await;
703 assert!(result.is_ok());
704
705 match result.unwrap() {
707 ExecutionResult::Select { .. } => (),
708 _ => panic!("Expected Select result"),
709 }
710 }
711
712 #[test]
713 fn test_cache_hit() {
714 let db = create_shared_db();
715 let session = Session::new("testdb".to_string(), "testuser".to_string(), db);
716
717 let _stmt1 = session.prepare("SELECT 1").unwrap();
719 let stats = session.cache_stats();
720 assert_eq!(stats.misses, 1);
721 assert_eq!(stats.hits, 0);
722
723 let _stmt2 = session.prepare("SELECT 1").unwrap();
725 let stats = session.cache_stats();
726 assert_eq!(stats.misses, 1);
727 assert_eq!(stats.hits, 1);
728 }
729
730 #[tokio::test]
731 async fn test_auto_caching_in_execute() {
732 let db = create_shared_db();
733 let mut session = Session::new("testdb".to_string(), "testuser".to_string(), db);
734
735 session.execute("SELECT 1").await.unwrap();
737 let stats = session.cache_stats();
738 assert_eq!(stats.misses, 1);
739
740 session.execute("SELECT 1").await.unwrap();
742 let stats = session.cache_stats();
743 assert_eq!(stats.hits, 1);
744 }
745
746 #[tokio::test]
747 async fn test_analyze_single_table() {
748 let db = create_shared_db();
749 let mut session = Session::new("testdb".to_string(), "testuser".to_string(), db);
750
751 session.execute("CREATE TABLE users (id INT, name VARCHAR(100))").await.unwrap();
753 session.execute("INSERT INTO users VALUES (1, 'Alice')").await.unwrap();
754 session.execute("INSERT INTO users VALUES (2, 'Bob')").await.unwrap();
755
756 let result = session.execute("ANALYZE users").await.unwrap();
758
759 match result {
761 ExecutionResult::Analyze { tables_analyzed } => {
762 assert_eq!(tables_analyzed, 1);
763 }
764 other => panic!("Expected Analyze result, got {:?}", other),
765 }
766 }
767
768 #[tokio::test]
769 async fn test_analyze_all_tables() {
770 let db = create_shared_db();
771 let mut session = Session::new("testdb".to_string(), "testuser".to_string(), db);
772
773 session.execute("CREATE TABLE users (id INT, name VARCHAR(100))").await.unwrap();
775 session.execute("CREATE TABLE products (id INT, price INT)").await.unwrap();
776 session.execute("INSERT INTO users VALUES (1, 'Alice')").await.unwrap();
777 session.execute("INSERT INTO products VALUES (1, 100)").await.unwrap();
778
779 let result = session.execute("ANALYZE").await.unwrap();
781
782 match result {
784 ExecutionResult::Analyze { tables_analyzed } => {
785 assert_eq!(tables_analyzed, 2);
786 }
787 other => panic!("Expected Analyze result, got {:?}", other),
788 }
789 }
790
791 #[tokio::test]
792 async fn test_analyze_with_columns() {
793 let db = create_shared_db();
794 let mut session = Session::new("testdb".to_string(), "testuser".to_string(), db);
795
796 session.execute("CREATE TABLE users (id INT, name VARCHAR(100), age INT)").await.unwrap();
798 session.execute("INSERT INTO users VALUES (1, 'Alice', 30)").await.unwrap();
799
800 let result = session.execute("ANALYZE users (id, name)").await.unwrap();
802
803 match result {
805 ExecutionResult::Analyze { tables_analyzed } => {
806 assert_eq!(tables_analyzed, 1);
807 }
808 other => panic!("Expected Analyze result, got {:?}", other),
809 }
810 }
811
812 #[test]
813 fn test_analyze_statement_type() {
814 let result = ExecutionResult::Analyze { tables_analyzed: 1 };
815 assert_eq!(result.statement_type(), "ANALYZE");
816 }
817
818 #[tokio::test]
819 async fn test_shared_database_across_sessions() {
820 let db = create_shared_db();
822
823 let mut session1 = Session::new("testdb".to_string(), "user1".to_string(), Arc::clone(&db));
825 let mut session2 = Session::new("testdb".to_string(), "user2".to_string(), Arc::clone(&db));
826
827 session1
829 .execute("CREATE TABLE shared_test (id INT, value VARCHAR(100))")
830 .await
831 .unwrap();
832
833 session1
835 .execute("INSERT INTO shared_test VALUES (1, 'from session 1')")
836 .await
837 .unwrap();
838
839 let result = session2.execute("SELECT * FROM shared_test").await.unwrap();
841 match result {
842 ExecutionResult::Select { rows, .. } => {
843 assert_eq!(rows.len(), 1);
844 }
845 _ => panic!("Expected Select result"),
846 }
847
848 session2
850 .execute("INSERT INTO shared_test VALUES (2, 'from session 2')")
851 .await
852 .unwrap();
853
854 let result = session1.execute("SELECT * FROM shared_test").await.unwrap();
856 match result {
857 ExecutionResult::Select { rows, .. } => {
858 assert_eq!(rows.len(), 2);
859 }
860 _ => panic!("Expected Select result"),
861 }
862 }
863}