(
framed: &mut Framed<T, crate::protocol::PostgresCodec>,
db: &Arc<DbHandler>,
session: &Arc<SessionState>,
query: &str,
_query_router: Option<&Arc<QueryRouter>
| 2217 | } |
| 2218 | |
| 2219 | async fn execute_transaction<T>( |
| 2220 | framed: &mut Framed<T, crate::protocol::PostgresCodec>, |
| 2221 | db: &Arc<DbHandler>, |
| 2222 | session: &Arc<SessionState>, |
| 2223 | query: &str, |
| 2224 | _query_router: Option<&Arc<QueryRouter>>, |
| 2225 | ) -> Result<(), PgSqliteError> |
| 2226 | where |
| 2227 | T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send, |
| 2228 | { |
| 2229 | use crate::query::{QueryTypeDetector, QueryType}; |
| 2230 | use crate::protocol::TransactionStatus; |
| 2231 | |
| 2232 | // Check if we're in a failed transaction |
| 2233 | let current_status = session.get_transaction_status().await; |
| 2234 | if current_status == TransactionStatus::InFailedTransaction { |
| 2235 | // Only ROLLBACK is allowed in a failed transaction |
| 2236 | if !matches!(QueryTypeDetector::detect_query_type(query), QueryType::Rollback) { |
| 2237 | return Err(PgSqliteError::Protocol( |
| 2238 | "current transaction is aborted, commands ignored until end of transaction block".to_string() |
| 2239 | )); |
| 2240 | } |
| 2241 | } |
| 2242 | |
| 2243 | match QueryTypeDetector::detect_query_type(query) { |
| 2244 | QueryType::Begin => { |
| 2245 | // Check if we're already in a transaction |
| 2246 | if current_status == TransactionStatus::InTransaction { |
| 2247 | // PostgreSQL behavior: warn but don't fail |
| 2248 | tracing::warn!("BEGIN command received while already in transaction"); |
| 2249 | // Send a warning notice |
| 2250 | use crate::protocol::messages::NoticeResponse; |
| 2251 | framed.send(BackendMessage::NoticeResponse(NoticeResponse { |
| 2252 | severity: "WARNING".to_string(), |
| 2253 | code: "25001".to_string(), // active_sql_transaction |
| 2254 | message: "there is already a transaction in progress".to_string(), |
| 2255 | detail: None, |
| 2256 | hint: None, |
| 2257 | position: None, |
| 2258 | where_: None, |
| 2259 | })).await.map_err(PgSqliteError::Io)?; |
| 2260 | // Still send CommandComplete, but don't actually execute BEGIN |
| 2261 | framed.send(BackendMessage::CommandComplete { tag: "BEGIN".to_string() }).await |
| 2262 | .map_err(PgSqliteError::Io)?; |
| 2263 | } else { |
| 2264 | tracing::debug!("Executing BEGIN command"); |
| 2265 | db.begin_with_session(&session.id).await?; |
| 2266 | tracing::debug!("BEGIN executed successfully"); |
| 2267 | // Update transaction status to InTransaction |
| 2268 | *session.transaction_status.write().await = TransactionStatus::InTransaction; |
| 2269 | tracing::debug!("Transaction status updated to InTransaction"); |
| 2270 | framed.send(BackendMessage::CommandComplete { tag: "BEGIN".to_string() }).await |
| 2271 | .map_err(PgSqliteError::Io)?; |
| 2272 | } |
| 2273 | } |
| 2274 | QueryType::Commit => { |
| 2275 | // Can't commit a failed transaction |
| 2276 | if current_status == TransactionStatus::InFailedTransaction { |
nothing calls this directly
no test coverage detected