(
framed: &mut Framed<T, crate::protocol::PostgresCodec>,
db: &Arc<DbHandler>,
session: &Arc<SessionState>,
query: &str,
query_router: Option<&Arc<QueryRouter>>
| 178 | } |
| 179 | } |
| 180 | pub async fn execute_query<T>( |
| 181 | framed: &mut Framed<T, crate::protocol::PostgresCodec>, |
| 182 | db: &Arc<DbHandler>, |
| 183 | session: &Arc<SessionState>, |
| 184 | query: &str, |
| 185 | query_router: Option<&Arc<QueryRouter>>, |
| 186 | ) -> Result<(), PgSqliteError> |
| 187 | where |
| 188 | T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send, |
| 189 | { |
| 190 | println!("EXECUTOR: execute_query called with: '{}'", query); |
| 191 | // Executing query |
| 192 | |
| 193 | // Strip SQL comments first to avoid parsing issues |
| 194 | let cleaned_query = crate::query::strip_sql_comments(query); |
| 195 | let query_to_execute = cleaned_query.trim(); |
| 196 | |
| 197 | // Check if query is empty after comment stripping |
| 198 | if query_to_execute.is_empty() { |
| 199 | return Err(PgSqliteError::Protocol(error_message!("Empty query").into_owned())); |
| 200 | } |
| 201 | |
| 202 | // Handle PostgreSQL DEALLOCATE commands (used for prepared statement cleanup) |
| 203 | let query_upper = query_to_execute.to_uppercase(); |
| 204 | if query_upper.starts_with("DEALLOCATE") { |
| 205 | debug!("DEALLOCATE command - treating as successful no-op (SQLite manages prepared statements automatically)"); |
| 206 | // Send CommandComplete for successful DEALLOCATE |
| 207 | let msg = BackendMessage::CommandComplete { tag: error_message!("DEALLOCATE").into_owned() }; |
| 208 | framed.send(msg).await.map_err(PgSqliteError::Io)?; |
| 209 | return Ok(()); |
| 210 | } |
| 211 | |
| 212 | // debug!("Executing query: {}", query_to_execute); |
| 213 | |
| 214 | // Check for Python-style parameters and provide helpful error |
| 215 | use crate::query::parameter_parser::ParameterParser; |
| 216 | let python_params = ParameterParser::find_python_parameters(query_to_execute); |
| 217 | if !python_params.is_empty() { |
| 218 | let error_msg = format!( |
| 219 | "Python-style parameters detected: {python_params:?}. pgsqlite requires parameter values to be substituted before execution. This usually means psycopg2 client-side substitution failed. Please ensure parameters are properly bound when executing the query." |
| 220 | ); |
| 221 | debug!("⚠️ {}", error_msg); |
| 222 | debug!("Query: {}", query_to_execute); |
| 223 | return Err(PgSqliteError::Protocol(error_msg)); |
| 224 | } |
| 225 | |
| 226 | // Check if query contains multiple statements |
| 227 | let trimmed = query_to_execute.trim(); |
| 228 | if trimmed.contains(';') { |
| 229 | // Split by semicolon and execute each statement |
| 230 | let statements: Vec<&str> = trimmed.split(';') |
| 231 | .map(|s| s.trim()) |
| 232 | .filter(|s| !s.is_empty()) |
| 233 | .collect(); |
| 234 | |
| 235 | // Handle empty query case (just semicolon) - SQLAlchemy uses ";" for ping |
| 236 | if statements.is_empty() { |
| 237 | debug!("Empty query (just semicolon) - treating as successful no-op"); |
no test coverage detected