fix: add type fallback for MySQL to prevent all-NULL display (#40)

Add i32/i16 type decoding and try_get_unchecked fallback to
mysql_value_to_json(), fixing all columns showing NULL on older
MySQL versions (e.g. 5.6) where sqlx type-checked decoding fails.
This commit is contained in:
t8y2
2026-05-08 17:01:32 +08:00
parent 92fe47b913
commit edda036f3c
2 changed files with 26 additions and 10 deletions
+7
View File
@@ -132,6 +132,8 @@ fn mysql_value_to_json(row: &MySqlRow, idx: usize, type_name: &str) -> serde_jso
.map(serde_json::Value::String)
.or_else(|_| row.try_get::<i64, _>(idx).map(super::safe_i64_to_json))
.or_else(|_| row.try_get::<u64, _>(idx).map(super::safe_u64_to_json))
.or_else(|_| row.try_get::<i32, _>(idx).map(|v| serde_json::Value::Number(v.into())))
.or_else(|_| row.try_get::<i16, _>(idx).map(|v| serde_json::Value::Number(v.into())))
.or_else(|_| {
row.try_get::<f64, _>(idx).map(|v| {
serde_json::Number::from_f64(v).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null)
@@ -142,6 +144,11 @@ fn mysql_value_to_json(row: &MySqlRow, idx: usize, type_name: &str) -> serde_jso
row.try_get::<Vec<u8>, _>(idx).map(|b| serde_json::Value::String(String::from_utf8_lossy(&b).to_string()))
})
.or_else(|e| mysql_temporal_to_json_value(row, idx).ok_or(e))
.or_else(|_| row.try_get_unchecked::<String, _>(idx).map(serde_json::Value::String))
.or_else(|_| {
row.try_get_unchecked::<Vec<u8>, _>(idx)
.map(|b| serde_json::Value::String(String::from_utf8_lossy(&b).to_string()))
})
.unwrap_or(serde_json::Value::Null)
}
+19 -10
View File
@@ -22,16 +22,6 @@ pub async fn connect(
.map_err(|_| format!("Oracle connection timed out ({CONNECTION_TIMEOUT_SECS}s)"))?
.map_err(|e| format!("Oracle connection failed: {e}"))?;
// Verify connection works with a simple query
log::info!("[oracle] connect: verifying with SELECT 1 FROM DUAL...");
conn.query("SELECT 1 FROM DUAL", &[]).await.map_err(|e| format!("Oracle connection verification failed: {e}"))?;
log::info!("[oracle] connect: verification OK, testing multi-row...");
let r = conn
.query("SELECT username FROM all_users ORDER BY username", &[])
.await
.map_err(|e| format!("Oracle multi-row test failed: {e}"))?;
log::info!("[oracle] connect: multi-row OK, {} rows", r.rows.len());
Ok(conn)
}
@@ -270,6 +260,11 @@ pub async fn list_triggers(conn: &OracleClient, schema: &str, table: &str) -> Re
pub async fn execute_query(conn: &OracleClient, sql: &str) -> Result<QueryResult, String> {
let start = Instant::now();
let sql = sql.trim().trim_end_matches(';');
// Rewrite FETCH FIRST N ROWS ONLY → ROWNUM for Oracle 11g compatibility
let sql = rewrite_fetch_first(sql);
let sql = sql.as_ref();
let trimmed = sql.to_uppercase();
log::debug!("[oracle] execute_query: sql={}", &sql[..sql.len().min(200)]);
@@ -324,3 +319,17 @@ pub async fn execute_query(conn: &OracleClient, sql: &str) -> Result<QueryResult
}
}
}
fn rewrite_fetch_first(sql: &str) -> std::borrow::Cow<'_, str> {
let upper = sql.to_uppercase();
if let Some(pos) = upper.find("FETCH FIRST") {
if let Some(end) = upper[pos..].find("ROWS ONLY") {
let between = sql[pos + 11..pos + end].trim();
if let Ok(n) = between.parse::<u64>() {
let base = sql[..pos].trim_end();
return std::borrow::Cow::Owned(format!("SELECT * FROM ({base}) WHERE ROWNUM <= {n}"));
}
}
}
std::borrow::Cow::Borrowed(sql)
}