Skip to main content

vibesql_server/
session.rs

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
14/// Session state for a database connection
15pub struct Session {
16    /// Database name
17    #[allow(dead_code)]
18    pub database: String,
19    /// User name
20    #[allow(dead_code)]
21    pub user: String,
22    /// Shared database instance (shared across all connections to the same database)
23    db: SharedDatabase,
24    /// Prepared statement cache for reduced parsing overhead
25    stmt_cache: Arc<PreparedStatementCache>,
26    /// Named prepared statements (from PREPARE SQL syntax)
27    named_statements: HashMap<String, Arc<PreparedStatement>>,
28    /// Cursor storage for DECLARE/OPEN/FETCH/CLOSE operations
29    cursors: CursorStore,
30    /// Session-level transaction manager for READ COMMITTED isolation
31    /// Tracks uncommitted changes that should only be visible to this session
32    txn_manager: SessionTransactionManager,
33}
34
35/// Simplified execution result for wire protocol
36#[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    /// Statement prepared successfully
61    Prepare {
62        statement_name: String,
63    },
64    /// Prepared statement deallocated
65    Deallocate {
66        statement_name: String,
67    },
68    /// Cursor declared successfully
69    DeclareCursor {
70        cursor_name: String,
71    },
72    /// Cursor opened successfully
73    OpenCursor {
74        cursor_name: String,
75    },
76    /// Cursor fetched rows
77    Fetch {
78        rows: Vec<Row>,
79        columns: Vec<Column>,
80    },
81    /// Cursor closed successfully
82    CloseCursor {
83        cursor_name: String,
84    },
85    /// Transaction started
86    Begin,
87    /// Transaction committed
88    Commit,
89    /// Transaction rolled back
90    Rollback,
91    Other {
92        message: String,
93    },
94}
95
96impl ExecutionResult {
97    /// Get the statement type as a string for metrics
98    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    /// Get the number of rows affected
125    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    /// Create a new session with a shared database
149    ///
150    /// This constructor is used for wire protocol connections where multiple
151    /// connections to the same database should share data.
152    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    /// Create a new standalone session with its own isolated database
165    ///
166    /// This constructor is used for HTTP API requests and other stateless
167    /// operations where data isolation between requests is acceptable.
168    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    /// Check if this session is currently in a transaction
174    pub fn in_transaction(&self) -> bool {
175        self.txn_manager.in_transaction()
176    }
177
178    /// Get the shared database handle
179    pub fn shared_database(&self) -> &SharedDatabase {
180        &self.db
181    }
182
183    /// Create a new session with a shared statement cache
184    #[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    /// Prepare a SQL statement for repeated execution
203    ///
204    /// The statement is parsed once and cached. Subsequent calls with the same
205    /// SQL will return the cached prepared statement without re-parsing.
206    ///
207    /// # Example
208    /// ```text
209    /// let stmt = session.prepare("SELECT * FROM users WHERE id = ?")?;
210    /// let result = session.execute_prepared(&stmt, &[SqlValue::Integer(1)]).await?;
211    /// ```
212    #[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    /// Execute a prepared statement with parameters
218    ///
219    /// Binds the provided parameters to the prepared statement and executes it.
220    /// This avoids the parsing overhead of the original SQL.
221    #[allow(dead_code)]
222    pub async fn execute_prepared(
223        &mut self,
224        stmt: &PreparedStatement,
225        params: &[SqlValue],
226    ) -> Result<ExecutionResult> {
227        // Bind parameters to get executable statement
228        let bound_stmt = stmt.bind(params).map_err(|e| anyhow::anyhow!("{}", e))?;
229
230        // Execute the bound statement
231        self.execute_statement(&bound_stmt).await
232    }
233
234    /// Execute a SQL query with auto-caching
235    ///
236    /// This method automatically caches parsed statements for performance.
237    /// For repeated queries, use `prepare()` + `execute_prepared()` for best performance.
238    pub async fn execute(&mut self, sql: &str) -> Result<ExecutionResult> {
239        // Try to get from cache or prepare
240        let prepared = self.stmt_cache.get_or_prepare(sql).map_err(|e| anyhow::anyhow!("{}", e))?;
241
242        // For non-parameterized queries, execute directly from cached AST
243        self.execute_statement(prepared.statement()).await
244    }
245
246    /// Execute a SQL query with parameters (convenience method)
247    ///
248    /// Combines prepare + execute_prepared in one call.
249    #[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    /// Execute a parsed statement
260    async fn execute_statement(
261        &mut self,
262        statement: &vibesql_ast::Statement,
263    ) -> Result<ExecutionResult> {
264        // Acquire write lock for most operations (read-only operations could use read lock,
265        // but for simplicity we use write lock for all to avoid potential deadlocks)
266        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                // Convert to our result format
274                let result_rows: Vec<Row> =
275                    rows.iter().map(|r| Row { values: r.values.to_vec() }).collect();
276
277                // TODO: Get actual column names from select statement
278                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                // Invalidate cache for modified table
293                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                // Invalidate cache for modified table
301                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                // Invalidate cache for modified table
309                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                // Invalidate cache for dropped table
331                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                // Extract table count from message - the executor returns a message like
349                // "ANALYZE completed - N table(s) analyzed"
350                let tables_analyzed =
351                    if analyze_stmt.table_name.is_some() { 1 } else { db.list_tables().len() };
352                let _ = message; // Message is informational, we track count
353                Ok(ExecutionResult::Analyze { tables_analyzed })
354            }
355
356            vibesql_ast::Statement::Prepare(prepare_stmt) => {
357                // Release lock before calling helper (doesn't need db)
358                drop(db);
359                self.execute_prepare(prepare_stmt)
360            }
361
362            vibesql_ast::Statement::Execute(execute_stmt) => {
363                // Release lock before calling helper (will reacquire if needed)
364                drop(db);
365                self.execute_execute(execute_stmt).await
366            }
367
368            vibesql_ast::Statement::Deallocate(deallocate_stmt) => {
369                // Release lock before calling helper (doesn't need db)
370                drop(db);
371                self.execute_deallocate(deallocate_stmt)
372            }
373
374            vibesql_ast::Statement::DeclareCursor(declare_stmt) => {
375                // Release lock - declare doesn't need db
376                drop(db);
377                self.execute_declare_cursor(declare_stmt)
378            }
379
380            vibesql_ast::Statement::OpenCursor(open_stmt) => {
381                // Keep lock for open cursor (needs db)
382                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                // Release lock - fetch doesn't need db
389                drop(db);
390                self.execute_fetch(fetch_stmt)
391            }
392
393            vibesql_ast::Statement::CloseCursor(close_stmt) => {
394                // Release lock - close doesn't need db
395                drop(db);
396                self.execute_close_cursor(close_stmt)
397            }
398
399            vibesql_ast::Statement::BeginTransaction(_) => {
400                // Release lock - transaction management doesn't need db lock
401                drop(db);
402                self.begin_transaction().await
403            }
404
405            vibesql_ast::Statement::Commit(_) => {
406                // Release lock first, commit() will reacquire if needed
407                drop(db);
408                self.commit().await
409            }
410
411            vibesql_ast::Statement::Rollback(_) => {
412                // Release lock - rollback discards buffered changes, no db write needed
413                drop(db);
414                self.rollback().await
415            }
416
417            vibesql_ast::Statement::RollbackToSavepoint(_savepoint_stmt) => {
418                // TODO: Implement savepoints in SessionTransactionManager
419                Ok(ExecutionResult::Other { message: "ROLLBACK TO SAVEPOINT".to_string() })
420            }
421
422            vibesql_ast::Statement::Savepoint(_savepoint_stmt) => {
423                // TODO: Implement savepoints in SessionTransactionManager
424                Ok(ExecutionResult::Other { message: "SAVEPOINT".to_string() })
425            }
426
427            vibesql_ast::Statement::ReleaseSavepoint(_release_stmt) => {
428                // TODO: Implement savepoints in SessionTransactionManager
429                Ok(ExecutionResult::Other { message: "RELEASE SAVEPOINT".to_string() })
430            }
431
432            _ => {
433                // For now, return a generic success for other statements
434                Ok(ExecutionResult::Other { message: "Command completed successfully".to_string() })
435            }
436        }
437    }
438
439    /// Execute PREPARE statement - registers a named prepared statement
440    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        // Get the SQL string to prepare
449        let sql = match &prepare_stmt.statement {
450            PreparedStatementBody::SqlString(s) => s.clone(),
451            PreparedStatementBody::ParsedStatement(_stmt) => {
452                // For parsed statements, we need to re-serialize to SQL for caching
453                // For now, we'll create a prepared statement directly from the AST
454                // This is a simplified approach - a full implementation would
455                // need to serialize the AST back to SQL or work with AST directly
456                return Err(anyhow::anyhow!(
457                    "PREPARE ... AS syntax not yet supported. Use PREPARE ... FROM 'sql_string' instead"
458                ));
459            }
460        };
461
462        // Create the prepared statement
463        let prepared = self
464            .stmt_cache
465            .get_or_prepare(&sql)
466            .map_err(|e| anyhow::anyhow!("Failed to prepare statement: {}", e))?;
467
468        // Store in named statements registry
469        self.named_statements.insert(name.clone(), prepared);
470
471        Ok(ExecutionResult::Prepare { statement_name: name })
472    }
473
474    /// Execute EXECUTE statement - runs a named prepared statement
475    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            // Look up the named statement
485            let prepared = self
486                .named_statements
487                .get(&name)
488                .ok_or_else(|| anyhow::anyhow!("Prepared statement '{}' not found", name))?
489                .clone();
490
491            // Evaluate parameter expressions to get values
492            let params: Vec<SqlValue> =
493                param_exprs.iter().map(evaluate_expression).collect::<Result<Vec<_>>>()?;
494
495            // Execute the prepared statement with parameters
496            self.execute_prepared(&prepared, &params).await
497        })
498    }
499
500    /// Execute DEALLOCATE statement - removes a named prepared statement
501    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    /// Get prepared statement cache statistics
525    #[allow(dead_code)]
526    pub fn cache_stats(&self) -> PreparedStatementCacheStats {
527        self.stmt_cache.stats()
528    }
529
530    /// Clear the prepared statement cache
531    #[allow(dead_code)]
532    pub fn clear_cache(&self) {
533        self.stmt_cache.clear();
534    }
535
536    /// Begin a transaction
537    ///
538    /// Starts a new transaction for this session. While in a transaction:
539    /// - Changes are tracked for potential rollback
540    /// - The storage layer manages transaction state
541    pub async fn begin_transaction(&mut self) -> Result<ExecutionResult> {
542        // Track session-level transaction state
543        self.txn_manager.begin().map_err(|e| anyhow::anyhow!("{}", e))?;
544
545        // Also start transaction in the shared database storage layer
546        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    /// Commit the current transaction
554    ///
555    /// Commits all changes made during this transaction.
556    /// After commit, changes are permanent and visible to all sessions.
557    pub async fn commit(&mut self) -> Result<ExecutionResult> {
558        // Commit session-level transaction state (clears buffered change tracking)
559        let _changes = self.txn_manager.commit().map_err(|e| anyhow::anyhow!("{}", e))?;
560
561        // Commit in the shared database storage layer
562        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    /// Rollback the current transaction
570    ///
571    /// Discards all changes made during this transaction.
572    /// The database state is restored to what it was before BEGIN.
573    pub async fn rollback(&mut self) -> Result<ExecutionResult> {
574        // Rollback session-level transaction state
575        self.txn_manager.rollback().map_err(|e| anyhow::anyhow!("{}", e))?;
576
577        // Rollback in the shared database storage layer
578        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    /// Execute DECLARE CURSOR statement
586    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    /// Execute FETCH statement
595    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        // Convert cursor rows to session rows
600        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    /// Execute CLOSE CURSOR statement
609    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
618/// Evaluate an expression to a SqlValue (for EXECUTE parameters)
619fn evaluate_expression(expr: &vibesql_ast::Expression) -> Result<SqlValue> {
620    use vibesql_ast::Expression;
621
622    match expr {
623        // Expression::Literal wraps SqlValue directly
624        Expression::Literal(val) => Ok(val.clone()),
625        Expression::UnaryOp { op, expr: operand } => {
626            // Handle negative numbers
627            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        // Not in transaction initially
672        assert!(!session.in_transaction());
673
674        // Begin transaction
675        assert!(session.begin_transaction().await.is_ok());
676        assert!(session.in_transaction());
677
678        // Can't begin twice
679        assert!(session.begin_transaction().await.is_err());
680
681        // Commit
682        assert!(session.commit().await.is_ok());
683        assert!(!session.in_transaction());
684
685        // Can't commit when not in transaction
686        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        // Create a table first
695        session.execute("CREATE TABLE users (id INT, name VARCHAR(100))").await.unwrap();
696
697        // Prepare a statement (note: parser doesn't support ?, so use literal value)
698        let stmt = session.prepare("SELECT * FROM users WHERE id = 1").unwrap();
699        assert_eq!(stmt.param_count(), 0);
700
701        // Execute the prepared statement
702        let result = session.execute_prepared(&stmt, &[]).await;
703        assert!(result.is_ok());
704
705        // Verify we get a Select result
706        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        // First prepare - cache miss
718        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        // Second prepare - cache hit
724        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        // First execute - cache miss
736        session.execute("SELECT 1").await.unwrap();
737        let stats = session.cache_stats();
738        assert_eq!(stats.misses, 1);
739
740        // Second execute - cache hit
741        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        // Create a table and insert data
752        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        // Analyze the table
757        let result = session.execute("ANALYZE users").await.unwrap();
758
759        // Verify we get an Analyze result
760        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        // Create multiple tables
774        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        // Analyze all tables (no table name)
780        let result = session.execute("ANALYZE").await.unwrap();
781
782        // Verify we get an Analyze result with 2 tables
783        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        // Create a table
797        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        // Analyze specific columns
801        let result = session.execute("ANALYZE users (id, name)").await.unwrap();
802
803        // Verify we get an Analyze result
804        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        // Create a shared database
821        let db = create_shared_db();
822
823        // Create two sessions using the same shared database
824        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        // Create a table through session 1
828        session1
829            .execute("CREATE TABLE shared_test (id INT, value VARCHAR(100))")
830            .await
831            .unwrap();
832
833        // Insert data through session 1
834        session1
835            .execute("INSERT INTO shared_test VALUES (1, 'from session 1')")
836            .await
837            .unwrap();
838
839        // Should be visible through session 2
840        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        // Insert through session 2
849        session2
850            .execute("INSERT INTO shared_test VALUES (2, 'from session 2')")
851            .await
852            .unwrap();
853
854        // Should be visible through session 1
855        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}