(
framed: &mut Framed<T, crate::protocol::PostgresCodec>,
db: &Arc<DbHandler>,
session: &Arc<SessionState>,
query: &str,
query_router: Option<&Arc<QueryRouter>>
| 1521 | } |
| 1522 | |
| 1523 | async fn execute_dml<T>( |
| 1524 | framed: &mut Framed<T, crate::protocol::PostgresCodec>, |
| 1525 | db: &Arc<DbHandler>, |
| 1526 | session: &Arc<SessionState>, |
| 1527 | query: &str, |
| 1528 | query_router: Option<&Arc<QueryRouter>>, |
| 1529 | ) -> Result<(), PgSqliteError> |
| 1530 | where |
| 1531 | T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send, |
| 1532 | { |
| 1533 | // SQLAlchemy manages transactions explicitly - don't start implicit transactions |
| 1534 | // This was interfering with SQLAlchemy's unit-of-work dirty detection |
| 1535 | |
| 1536 | debug!("execute_dml called with query: {}", query); |
| 1537 | |
| 1538 | // Check for RETURNING clause |
| 1539 | if ReturningTranslator::has_returning_clause(query) { |
| 1540 | debug!("Query has RETURNING clause, using execute_dml_with_returning: {}", query); |
| 1541 | return Self::execute_dml_with_returning(framed, db, session, query, query_router).await; |
| 1542 | } else { |
| 1543 | debug!("Query does NOT have RETURNING clause: {}", query); |
| 1544 | } |
| 1545 | |
| 1546 | // Validate numeric constraints for INSERT/UPDATE before execution |
| 1547 | use crate::query::{QueryTypeDetector, QueryType}; |
| 1548 | use crate::validator::NumericValidator; |
| 1549 | |
| 1550 | // Validate before executing - do all database work before any await |
| 1551 | let validation_error = match QueryTypeDetector::detect_query_type(query) { |
| 1552 | QueryType::Insert => { |
| 1553 | if let Some(table_name) = extract_table_name_from_insert(query) { |
| 1554 | // Validate numeric constraints using session connection |
| 1555 | match db.with_session_connection(&session.id, |conn| { |
| 1556 | match NumericValidator::validate_insert(conn, query, &table_name) { |
| 1557 | Ok(()) => Ok(()), |
| 1558 | Err(crate::error::PgError::NumericValueOutOfRange { .. }) => { |
| 1559 | Err(rusqlite::Error::SqliteFailure( |
| 1560 | rusqlite::ffi::Error::new(rusqlite::ffi::SQLITE_ERROR), |
| 1561 | Some("NUMERIC_VALUE_OUT_OF_RANGE".to_string()) |
| 1562 | )) |
| 1563 | }, |
| 1564 | Err(e) => Err(rusqlite::Error::SqliteFailure( |
| 1565 | rusqlite::ffi::Error::new(rusqlite::ffi::SQLITE_ERROR), |
| 1566 | Some(format!("Numeric validation failed: {e}")) |
| 1567 | )) |
| 1568 | } |
| 1569 | }).await { |
| 1570 | Ok(()) => None, |
| 1571 | Err(PgSqliteError::Sqlite(rusqlite::Error::SqliteFailure(_, Some(msg)))) if msg == "NUMERIC_VALUE_OUT_OF_RANGE" => { |
| 1572 | // Create a numeric value out of range error |
| 1573 | Some(PgSqliteError::Validation(crate::error::PgError::NumericValueOutOfRange { |
| 1574 | type_name: "numeric".to_string(), |
| 1575 | column_name: String::new(), |
| 1576 | value: String::new(), |
| 1577 | })) |
| 1578 | }, |
| 1579 | Err(e) => Some(e), |
| 1580 | } |
nothing calls this directly
no test coverage detected