Fast path SELECT execution that bypasses parsing and rewriting
(
conn: &Connection,
query: &str,
schema_cache: &SchemaCache,
)
| 354 | |
| 355 | /// Fast path SELECT execution that bypasses parsing and rewriting |
| 356 | pub fn query_fast_path( |
| 357 | conn: &Connection, |
| 358 | query: &str, |
| 359 | schema_cache: &SchemaCache, |
| 360 | ) -> Result<Option<DbResponse>, rusqlite::Error> { |
| 361 | // Check if query qualifies for fast path |
| 362 | if let Some(table_name) = can_use_fast_path(query) { |
| 363 | // Only handle SELECT queries |
| 364 | if !matches!(crate::query::QueryTypeDetector::detect_query_type(query), crate::query::QueryType::Select) { |
| 365 | return Ok(None); |
| 366 | } |
| 367 | |
| 368 | // Check if table has decimal columns |
| 369 | match table_has_decimal_columns(conn, &table_name, schema_cache) { |
| 370 | Ok(false) => { |
| 371 | // No decimal columns, execute directly |
| 372 | let mut stmt = conn.prepare(query)?; |
| 373 | let column_count = stmt.column_count(); |
| 374 | |
| 375 | // Get column names |
| 376 | let mut columns = Vec::new(); |
| 377 | for i in 0..column_count { |
| 378 | columns.push(sanitize_column_name(stmt.column_name(i)?).to_string()); |
| 379 | } |
| 380 | |
| 381 | // Check for boolean columns in the schema using cache |
| 382 | let mut column_types = Vec::new(); |
| 383 | if let Ok(table_schema) = schema_cache.get_or_load(conn, &table_name) { |
| 384 | for col_name in &columns { |
| 385 | if let Some(col_info) = table_schema.column_map.get(&col_name.to_lowercase()) { |
| 386 | column_types.push(Some(col_info.pg_type.clone())); |
| 387 | } else { |
| 388 | column_types.push(None); |
| 389 | } |
| 390 | } |
| 391 | } else { |
| 392 | // Fallback to None for all columns |
| 393 | column_types.resize(columns.len(), None); |
| 394 | } |
| 395 | |
| 396 | // Get rows - with boolean type conversions |
| 397 | let mut rows = Vec::new(); |
| 398 | let result_rows = stmt.query_map([], |row| { |
| 399 | let mut values = Vec::new(); |
| 400 | for (i, _) in columns.iter().enumerate().take(column_count) { |
| 401 | match row.get_ref(i)? { |
| 402 | ValueRef::Null => values.push(None), |
| 403 | ValueRef::Integer(int_val) => { |
| 404 | // Check column type for proper formatting |
| 405 | let pg_type = column_types.get(i) |
| 406 | .and_then(|opt| opt.as_ref()) |
| 407 | .map(|s| s.to_lowercase()) |
| 408 | .unwrap_or_default(); |
| 409 | |
| 410 | if pg_type == "boolean" || pg_type == "bool" { |
| 411 | // Convert SQLite's 0/1 to PostgreSQL's f/t format |
| 412 | let bool_str = if int_val == 0 { "f" } else { "t" }; |
| 413 | values.push(Some(bool_str.as_bytes().to_vec())); |
no test coverage detected