Skip to main content

sz_orm_sqlx/
any.rs

1//! sqlx 后端适配器实现
2//!
3//! 为 MySQL、PostgreSQL、SQLite 分别实现 Connection 和 ConnectionFactory。
4//! 不使用 sqlx::Any 以避免其类型限制和生命周期问题。
5//!
6//! 关键设计:
7//! Connection trait 已手动解糖(不使用 `#[async_trait]`),所有 async 方法
8//! 使用单一生命周期 `'a`(绑定 `&'a mut self` 和 `&'a str`),而非 HRTB。
9//! 这样 sqlx::Executor 对 `&'c mut XxxConnection` 的 impl(针对具体 `'c`)
10//! 即可满足约束,避免 "implementation of Executor is not general enough" 错误。
11
12use async_trait::async_trait;
13use futures::StreamExt;
14use sqlx::{Column, Executor, Row, TypeInfo};
15use std::collections::HashMap;
16use std::future::Future;
17use std::pin::Pin;
18use std::str::FromStr;
19use std::sync::Arc;
20use sz_orm_core::{ColType, Connection, ConnectionFactory, DbError, QueryRows, QueryValues, Value};
21
22use crate::error::map_sqlx_error;
23
24/// 判断 SQL 是否需要走 raw_sql 路径
25/// MySQL prepared statement 协议不支持 BEGIN/COMMIT/ROLLBACK/SAVEPOINT 等命令
26fn needs_raw_sql(sql: &str) -> bool {
27    let trimmed = sql.trim_start();
28    let upper = trimmed.to_uppercase();
29    upper.starts_with("BEGIN")
30        || upper.starts_with("COMMIT")
31        || upper.starts_with("ROLLBACK")
32        || upper.starts_with("SAVEPOINT")
33        || upper.starts_with("RELEASE")
34        || upper.starts_with("SET ")
35        || upper.starts_with("USE ")
36        || upper.starts_with("START TRANSACTION")
37}
38
39// ===================== SQLite 适配器 =====================
40
41// 注:sqlx 0.9 起 Executor trait 要求 'static lifetime,
42// 原先的 execute_sqlite_boxed / query_sqlite_boxed 已内联到调用点。
43// 见 SqlxSqliteConnection::execute / query 实现。
44
45// 注:原 row_to_value_sqlite(按 type_info().name() 字符串 match)已被
46// row_to_value_with_coltype_sqlite(按预解析 ColType 枚举分派)取代,
47// 性能更优且避免每行字符串比较。详见该函数。
48
49/// SQLite: 使用预解析的 ColType 进行类型分派(避免每行字符串 match)
50///
51/// 调用方预先通过 [`ColType::parse_sqlite`] 解析列类型,
52/// 后续行直接用枚举分派(编译器优化为跳转表),避免每行每列的字符串比较。
53///
54/// **注意**:必须使用 `parse_sqlite` 而非通用 `from_type_name`:SQLite 的 INTEGER 类型
55/// 实际为 64 位动态存储,通用映射会错误地归类为 I32,导致数值截断。
56fn row_to_value_with_coltype_sqlite(
57    row: &sqlx::sqlite::SqliteRow,
58    ordinal: usize,
59    col_type: ColType,
60) -> Value {
61    match col_type {
62        ColType::Bool => match row.try_get::<Option<bool>, usize>(ordinal) {
63            Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
64            Err(_) => Value::Null,
65        },
66        ColType::I8 => match row.try_get::<Option<i8>, usize>(ordinal) {
67            Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
68            Err(_) => Value::Null,
69        },
70        ColType::I16 => match row.try_get::<Option<i16>, usize>(ordinal) {
71            Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
72            Err(_) => Value::Null,
73        },
74        ColType::I32 => match row.try_get::<Option<i32>, usize>(ordinal) {
75            Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
76            Err(_) => Value::Null,
77        },
78        ColType::I64 => match row.try_get::<Option<i64>, usize>(ordinal) {
79            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
80            Err(_) => Value::Null,
81        },
82        ColType::U8 => match row.try_get::<Option<u8>, usize>(ordinal) {
83            Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
84            Err(_) => Value::Null,
85        },
86        ColType::U16 => match row.try_get::<Option<u16>, usize>(ordinal) {
87            Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
88            Err(_) => Value::Null,
89        },
90        ColType::U32 => match row.try_get::<Option<u32>, usize>(ordinal) {
91            Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
92            Err(_) => Value::Null,
93        },
94        // SQLite 无原生 u64,按 i64 解码
95        ColType::U64 => match row.try_get::<Option<i64>, usize>(ordinal) {
96            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
97            Err(_) => Value::Null,
98        },
99        ColType::F32 => match row.try_get::<Option<f32>, usize>(ordinal) {
100            Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
101            Err(_) => Value::Null,
102        },
103        ColType::F64 => match row.try_get::<Option<f64>, usize>(ordinal) {
104            Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
105            Err(_) => Value::Null,
106        },
107        ColType::Decimal => match row.try_get::<Option<String>, usize>(ordinal) {
108            Ok(v) => v.map(Value::Decimal).unwrap_or(Value::Null),
109            Err(_) => Value::Null,
110        },
111        ColType::String => match row.try_get::<Option<String>, usize>(ordinal) {
112            Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
113            Err(_) => Value::Null,
114        },
115        ColType::Bytes => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
116            Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
117            Err(_) => Value::Null,
118        },
119        // SQLite 中 DATE/DATETIME/TIME/JSON/UUID 通常以 TEXT 存储
120        ColType::Date | ColType::DateTime | ColType::Time | ColType::Json | ColType::Uuid => {
121            match row.try_get::<Option<String>, usize>(ordinal) {
122                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
123                Err(_) => Value::Null,
124            }
125        }
126        ColType::Unknown => {
127            // 未知类型(表达式/聚合列,SQLite type_info 返回空串),按
128            // i64 → f64 → bool → String 顺序回退(与 MySQL/PG 臂一致)。
129            // 修复前 bool 优先:任意非零整数被解码为 Bool(true),下游
130            // as_i64() 恒为 1 —— COUNT(*)=50 被读成 1(2026-09-25 实测
131            // 复现的静默读损坏)。表达式布尔值读为 I64(0/1),as_bool()
132            // 对整数类型语义不变。
133            if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
134                return v.map(Value::I64).unwrap_or(Value::Null);
135            }
136            if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
137                return v.map(Value::F64).unwrap_or(Value::Null);
138            }
139            if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
140                return v.map(Value::Bool).unwrap_or(Value::Null);
141            }
142            if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
143                return v.map(Value::String).unwrap_or(Value::Null);
144            }
145            Value::Null
146        }
147        // ColType 标记为 #[non_exhaustive],未来新增变体按 Unknown 处理
148        _ => Value::Null,
149    }
150}
151
152pub struct SqlitePoolHandle {
153    pool: sqlx::SqlitePool,
154}
155
156impl SqlitePoolHandle {
157    pub async fn connect(url: &str) -> Result<Self, DbError> {
158        // SQLite 生产配置:WAL + NORMAL 同步 + 5s 忙等 + 256MB mmap
159        // - WAL:写不阻塞读,崩溃后通过 WAL 恢复,远比 DELETE 模式安全
160        // - Synchronous=Normal:WAL 模式下仅在 checkpoint 时 fsync,性能 ~2x 于 Full
161        // - busy_timeout=5s:避免 "database is locked" 误报
162        // - mmap_size=256MB:大结果集减少 syscall,提升读取吞吐
163        let opts = sqlx::sqlite::SqliteConnectOptions::from_str(url)
164            .map_err(map_sqlx_error)?
165            .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
166            .synchronous(sqlx::sqlite::SqliteSynchronous::Normal)
167            .busy_timeout(std::time::Duration::from_secs(5))
168            .pragma("mmap_size", "268435456");
169        let pool = sqlx::sqlite::SqlitePoolOptions::new()
170            .max_connections(10)
171            .acquire_timeout(std::time::Duration::from_secs(30))
172            .idle_timeout(Some(std::time::Duration::from_secs(600)))
173            .max_lifetime(Some(std::time::Duration::from_secs(1800)))
174            .connect_with(opts)
175            .await
176            .map_err(map_sqlx_error)?;
177        Ok(Self { pool })
178    }
179
180    pub fn from_pool(pool: sqlx::SqlitePool) -> Self {
181        Self { pool }
182    }
183
184    pub fn pool(&self) -> &sqlx::SqlitePool {
185        &self.pool
186    }
187}
188
189pub struct SqlxSqliteConnectionFactory {
190    pool: Arc<SqlitePoolHandle>,
191}
192
193impl SqlxSqliteConnectionFactory {
194    pub fn new(pool: Arc<SqlitePoolHandle>) -> Self {
195        Self { pool }
196    }
197}
198
199#[async_trait]
200impl ConnectionFactory for SqlxSqliteConnectionFactory {
201    async fn create(&self) -> Result<Box<dyn Connection>, DbError> {
202        let conn = self.pool.pool.acquire().await.map_err(map_sqlx_error)?;
203        Ok(Box::new(SqlxSqliteConnection {
204            conn: Some(conn),
205            connected: true,
206            in_transaction: false,
207        }))
208    }
209}
210
211pub struct SqlxSqliteConnection {
212    conn: Option<sqlx::pool::PoolConnection<sqlx::Sqlite>>,
213    connected: bool,
214    in_transaction: bool,
215}
216
217impl Connection for SqlxSqliteConnection {
218    fn execute<'a>(
219        &'a mut self,
220        sql: &'a str,
221    ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
222        Box::pin(async move {
223            let mut pool_conn = self
224                .conn
225                .take()
226                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
227            // sqlx 0.9: PoolConnection 不再实现 Executor,需通过 DerefMut 解引用到内部连接
228            // sqlx 0.9: SqlSafeStr 只对 &'static str 直接实现,非 'static 的 &str 需用 AssertSqlSafe 包装
229            let result = if needs_raw_sql(sql) {
230                (&mut *pool_conn)
231                    .execute(sqlx::raw_sql(sqlx::AssertSqlSafe(sql)))
232                    .await
233            } else {
234                (&mut *pool_conn).execute(sqlx::AssertSqlSafe(sql)).await
235            };
236            self.conn = Some(pool_conn);
237
238            match result {
239                Ok(r) => Ok(r.rows_affected()),
240                Err(e) => {
241                    let db_err = map_sqlx_error(e);
242                    if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
243                        self.connected = false;
244                    }
245                    Err(db_err)
246                }
247            }
248        })
249    }
250
251    fn query<'a>(
252        &'a mut self,
253        sql: &'a str,
254    ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>, DbError>> + Send + 'a>>
255    {
256        Box::pin(async move {
257            let mut pool_conn = self
258                .conn
259                .take()
260                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
261            // sqlx 0.9: PoolConnection 不再实现 Executor,需通过 DerefMut 解引用到内部连接
262            // sqlx 0.9: SqlSafeStr 只对 &'static str 直接实现,非 'static 的 &str 需用 AssertSqlSafe 包装
263            let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
264            self.conn = Some(pool_conn);
265
266            let rows = rows_result.map_err(map_sqlx_error)?;
267            if rows.is_empty() {
268                return Ok(Vec::new());
269            }
270            // 预解析列类型(只在第一行解析,后续行复用,避免每行字符串 match)
271            // SQLite 专用解析:INTEGER 映射为 I64(SQLite 整数动态存储,可容纳 64 位)
272            let col_types: Vec<ColType> = rows[0]
273                .columns()
274                .iter()
275                .map(|col| ColType::parse_sqlite(col.type_info().name()))
276                .collect();
277            let col_names: Vec<String> = rows[0]
278                .columns()
279                .iter()
280                .map(|col| col.name().to_string())
281                .collect();
282            let mut result = Vec::with_capacity(rows.len());
283            for row in &rows {
284                let mut record = HashMap::with_capacity(col_types.len());
285                for (i, name) in col_names.iter().enumerate() {
286                    let value = row_to_value_with_coltype_sqlite(row, i, col_types[i]);
287                    record.insert(name.clone(), value);
288                }
289                result.push(record);
290            }
291            Ok(result)
292        })
293    }
294
295    fn begin_transaction<'a>(
296        &'a mut self,
297    ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
298        Box::pin(async move {
299            if self.in_transaction {
300                return Err(DbError::Internal("transaction already started".to_string()));
301            }
302            self.execute("BEGIN").await?;
303            self.in_transaction = true;
304            Ok(())
305        })
306    }
307
308    fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
309        Box::pin(async move {
310            if self.in_transaction {
311                self.execute("COMMIT").await?;
312                self.in_transaction = false;
313            }
314            Ok(())
315        })
316    }
317
318    fn rollback<'a>(
319        &'a mut self,
320    ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
321        Box::pin(async move {
322            if self.in_transaction {
323                let result = self.execute("ROLLBACK").await;
324                self.in_transaction = false;
325                result.map(|_| ())
326            } else {
327                Ok(())
328            }
329        })
330    }
331
332    fn is_connected(&self) -> bool {
333        // 纳入 conn.is_some():query_stream 提前弃读时 PoolConnection 已从包装器
334        // take() 出去(self.conn = None),毒化状态不得被判定为可用连接
335        // (否则回池后下次 acquire 借出即报 "connection already closed")
336        self.connected && self.conn.is_some()
337    }
338
339    fn in_transaction(&self) -> bool {
340        self.in_transaction
341    }
342
343    fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
344        Box::pin(async move {
345            match self.execute("SELECT 1").await {
346                Ok(_) => true,
347                Err(_) => {
348                    self.connected = false;
349                    false
350                }
351            }
352        })
353    }
354
355    fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
356        Box::pin(async move {
357            if let Some(conn) = self.conn.take() {
358                drop(conn);
359            }
360            self.connected = false;
361            self.in_transaction = false;
362            Ok(())
363        })
364    }
365
366    /// SQLite 参数绑定执行(INSERT/UPDATE/DELETE)
367    ///
368    /// 使用 sqlx prepared statement 绑定参数,避免 SQL 注入与字符串转义开销。
369    /// 对 `Value::Bool`/`I8`..=`I64`/`U8`..=`U64`/`F32`/`F64`/`String`/`Bytes` 直接 bind;
370    /// 其他类型(Date/DateTime/Json/Array/Object)回退为 `to_string()` 后以 TEXT 绑定。
371    fn execute_with_params<'a>(
372        &'a mut self,
373        sql: &'a str,
374        params: &'a [Value],
375    ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
376        Box::pin(async move {
377            if needs_raw_sql(sql) || params.is_empty() {
378                return self.execute(sql).await;
379            }
380            let mut pool_conn = self
381                .conn
382                .take()
383                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
384            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
385            for v in params {
386                q = match v {
387                    Value::Null => q.bind(None::<i64>),
388                    Value::Bool(b) => q.bind(*b),
389                    Value::I8(n) => q.bind(*n),
390                    Value::I16(n) => q.bind(*n),
391                    Value::I32(n) => q.bind(*n),
392                    Value::I64(n) => q.bind(*n),
393                    Value::U8(n) => q.bind(*n),
394                    Value::U16(n) => q.bind(*n),
395                    Value::U32(n) => q.bind(*n),
396                    Value::U64(n) => q.bind(*n as i64),
397                    Value::F32(f) => q.bind(*f),
398                    Value::F64(f) => q.bind(*f),
399                    Value::String(s) => q.bind(s.as_str()),
400                    Value::Decimal(s) => q.bind(s.as_str()),
401                    Value::Bytes(b) => q.bind(b.as_slice()),
402                    other => q.bind(other.to_string()),
403                };
404            }
405            let result = q.execute(&mut *pool_conn).await;
406            self.conn = Some(pool_conn);
407            match result {
408                Ok(r) => Ok(r.rows_affected()),
409                Err(e) => {
410                    let db_err = map_sqlx_error(e);
411                    if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
412                        self.connected = false;
413                    }
414                    Err(db_err)
415                }
416            }
417        })
418    }
419
420    /// SQLite 参数绑定查询(SELECT,HashMap 映射)
421    ///
422    /// 使用 sqlx prepared statement 绑定参数,结果按预解析 ColType 解码为
423    /// `HashMap<String, Value>`。与 `query` 的区别仅在于参数绑定路径。
424    fn query_with_params<'a>(
425        &'a mut self,
426        sql: &'a str,
427        params: &'a [Value],
428    ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
429        Box::pin(async move {
430            if params.is_empty() {
431                return self.query(sql).await;
432            }
433            let mut pool_conn = self
434                .conn
435                .take()
436                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
437            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
438            for v in params {
439                q = match v {
440                    Value::Null => q.bind(None::<i64>),
441                    Value::Bool(b) => q.bind(*b),
442                    Value::I8(n) => q.bind(*n),
443                    Value::I16(n) => q.bind(*n),
444                    Value::I32(n) => q.bind(*n),
445                    Value::I64(n) => q.bind(*n),
446                    Value::U8(n) => q.bind(*n),
447                    Value::U16(n) => q.bind(*n),
448                    Value::U32(n) => q.bind(*n),
449                    Value::U64(n) => q.bind(*n as i64),
450                    Value::F32(f) => q.bind(*f),
451                    Value::F64(f) => q.bind(*f),
452                    Value::String(s) => q.bind(s.as_str()),
453                    Value::Decimal(s) => q.bind(s.as_str()),
454                    Value::Bytes(b) => q.bind(b.as_slice()),
455                    other => q.bind(other.to_string()),
456                };
457            }
458            let rows_result = q.fetch_all(&mut *pool_conn).await;
459            self.conn = Some(pool_conn);
460            let rows = rows_result.map_err(map_sqlx_error)?;
461            if rows.is_empty() {
462                return Ok(Vec::new());
463            }
464            // 预解析列类型(只在第一行解析,后续行复用)
465            // SQLite 专用解析:INTEGER → I64
466            let col_types: Vec<ColType> = rows[0]
467                .columns()
468                .iter()
469                .map(|col| ColType::parse_sqlite(col.type_info().name()))
470                .collect();
471            let col_names: Vec<String> = rows[0]
472                .columns()
473                .iter()
474                .map(|col| col.name().to_string())
475                .collect();
476            let mut result = Vec::with_capacity(rows.len());
477            for row in &rows {
478                let mut record = HashMap::with_capacity(col_types.len());
479                for (i, name) in col_names.iter().enumerate() {
480                    let value = row_to_value_with_coltype_sqlite(row, i, col_types[i]);
481                    record.insert(name.clone(), value);
482                }
483                result.push(record);
484            }
485            Ok(result)
486        })
487    }
488
489    /// SQLite 位置式查询(SELECT,无参数)
490    ///
491    /// 绕过 `HashMap<String, Value>` 行映射,返回 `(列名列表, 按列序号的值矩阵)`。
492    /// 列名与 ColType 仅 `to_string`/解析一次,后续行复用;每行值按列序号直接 `Vec::push`,
493    /// 无哈希计算与字符串克隆。适用于 SELECT ALL 大结果集场景。
494    fn query_values<'a>(
495        &'a mut self,
496        sql: &'a str,
497    ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
498        Box::pin(async move {
499            let mut pool_conn = self
500                .conn
501                .take()
502                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
503            let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
504            self.conn = Some(pool_conn);
505            let rows = rows_result.map_err(map_sqlx_error)?;
506            if rows.is_empty() {
507                return Ok((Vec::new(), Vec::new()));
508            }
509            let cols = rows[0].columns();
510            let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
511            let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
512            for col in cols {
513                col_names.push(col.name().to_string());
514                col_types.push(ColType::parse_sqlite(col.type_info().name()));
515            }
516            let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
517            for row in &rows {
518                let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
519                for (idx, _) in col_names.iter().enumerate() {
520                    row_values.push(row_to_value_with_coltype_sqlite(row, idx, col_types[idx]));
521                }
522                result_rows.push(row_values);
523            }
524            Ok((col_names, result_rows))
525        })
526    }
527
528    /// SQLite 参数绑定位置式查询(SELECT)
529    ///
530    /// 叠加 prepared statement + 位置式映射 + ColType 预解析三重优化。
531    fn query_values_with_params<'a>(
532        &'a mut self,
533        sql: &'a str,
534        params: &'a [Value],
535    ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
536        Box::pin(async move {
537            if params.is_empty() {
538                return self.query_values(sql).await;
539            }
540            let mut pool_conn = self
541                .conn
542                .take()
543                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
544            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
545            for v in params {
546                q = match v {
547                    Value::Null => q.bind(None::<i64>),
548                    Value::Bool(b) => q.bind(*b),
549                    Value::I8(n) => q.bind(*n),
550                    Value::I16(n) => q.bind(*n),
551                    Value::I32(n) => q.bind(*n),
552                    Value::I64(n) => q.bind(*n),
553                    Value::U8(n) => q.bind(*n),
554                    Value::U16(n) => q.bind(*n),
555                    Value::U32(n) => q.bind(*n),
556                    Value::U64(n) => q.bind(*n as i64),
557                    Value::F32(f) => q.bind(*f),
558                    Value::F64(f) => q.bind(*f),
559                    Value::String(s) => q.bind(s.as_str()),
560                    Value::Decimal(s) => q.bind(s.as_str()),
561                    Value::Bytes(b) => q.bind(b.as_slice()),
562                    other => q.bind(other.to_string()),
563                };
564            }
565            let rows_result = q.fetch_all(&mut *pool_conn).await;
566            self.conn = Some(pool_conn);
567            let rows = rows_result.map_err(map_sqlx_error)?;
568            if rows.is_empty() {
569                return Ok((Vec::new(), Vec::new()));
570            }
571            let cols = rows[0].columns();
572            let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
573            let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
574            for col in cols {
575                col_names.push(col.name().to_string());
576                col_types.push(ColType::parse_sqlite(col.type_info().name()));
577            }
578            let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
579            for row in &rows {
580                let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
581                for (idx, _) in col_names.iter().enumerate() {
582                    row_values.push(row_to_value_with_coltype_sqlite(row, idx, col_types[idx]));
583                }
584                result_rows.push(row_values);
585            }
586            Ok((col_names, result_rows))
587        })
588    }
589
590    /// SQLite 流式查询:逐行返回结果,避免大结果集 `fetch_all` 内存峰值
591    ///
592    /// 使用 `sqlx::query::fetch` 获取行流,逐行映射为 `HashMap<String, Value>`。
593    /// 流正常消费完毕后连接归还 `self.conn`;若提前 drop 流,连接通过
594    /// `PoolConnection::drop` 归还到 sqlx 池,但 `self.conn` 变为 `None`。
595    fn query_stream<'a>(
596        &'a mut self,
597        sql: &'a str,
598    ) -> Pin<Box<dyn futures::Stream<Item = Result<HashMap<String, Value>, DbError>> + Send + 'a>>
599    {
600        Box::pin(async_stream::try_stream! {
601            let mut pool_conn = self
602                .conn
603                .take()
604                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
605            let mut row_stream = sqlx::query(sqlx::AssertSqlSafe(sql)).fetch(&mut *pool_conn);
606            let mut col_types: Vec<ColType> = Vec::new();
607            let mut col_names: Vec<String> = Vec::new();
608            let mut first_row = true;
609            while let Some(row_result) = row_stream.next().await {
610                let row = row_result.map_err(map_sqlx_error)?;
611                if first_row {
612                    for col in row.columns() {
613                        col_names.push(col.name().to_string());
614                        col_types.push(ColType::parse_sqlite(col.type_info().name()));
615                    }
616                    first_row = false;
617                }
618                let mut record = HashMap::with_capacity(col_names.len());
619                for (i, name) in col_names.iter().enumerate() {
620                    let value = row_to_value_with_coltype_sqlite(&row, i, col_types[i]);
621                    record.insert(name.clone(), value);
622                }
623                yield record;
624            }
625            // 显式释放 row_stream 对 pool_conn 的可变借用,否则借用检查器
626            // 无法证明 row_stream 在移动 pool_conn 前已被销毁(E0505)
627            drop(row_stream);
628            self.conn = Some(pool_conn);
629        })
630    }
631}
632
633impl Drop for SqlxSqliteConnection {
634    fn drop(&mut self) {
635        if let Some(conn) = self.conn.take() {
636            drop(conn);
637        }
638    }
639}
640
641/// SQLite 在线备份(使用 VACUUM INTO,要求 SQLite 3.27+)
642///
643/// 将当前数据库完整复制到目标路径,原数据库可继续读写(在线备份)。
644/// 目标文件若已存在会报错。路径中的单引号会被转义以防 SQL 注入。
645pub async fn sqlite_backup(
646    conn: &mut SqlxSqliteConnection,
647    dest_path: &str,
648) -> Result<(), DbError> {
649    let escaped_path = dest_path.replace('\'', "''");
650    let sql = format!("VACUUM INTO '{}'", escaped_path);
651    conn.execute(&sql).await?;
652    Ok(())
653}
654
655// ===================== MySQL 适配器 =====================
656
657// 注:sqlx 0.9 起 Executor trait 要求 'static lifetime,
658// 原先的 execute_mysql_boxed / query_mysql_boxed 已内联到调用点。
659
660fn row_to_value_mysql(row: &sqlx::mysql::MySqlRow, ordinal: usize) -> Value {
661    use sqlx::TypeInfo;
662    let type_name = row.columns()[ordinal].type_info().name();
663    match type_name {
664        "BOOLEAN" => match row.try_get::<Option<bool>, usize>(ordinal) {
665            Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
666            Err(_) => Value::Null,
667        },
668        "TINYINT" | "TINYINT UNSIGNED" => match row.try_get::<Option<i8>, usize>(ordinal) {
669            Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
670            Err(_) => match row.try_get::<Option<u8>, usize>(ordinal) {
671                Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
672                Err(_) => Value::Null,
673            },
674        },
675        "SMALLINT" | "SMALLINT UNSIGNED" => match row.try_get::<Option<i16>, usize>(ordinal) {
676            Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
677            Err(_) => match row.try_get::<Option<u16>, usize>(ordinal) {
678                Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
679                Err(_) => Value::Null,
680            },
681        },
682        "INT" | "INT UNSIGNED" | "MEDIUMINT" | "MEDIUMINT UNSIGNED" => {
683            match row.try_get::<Option<i32>, usize>(ordinal) {
684                Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
685                Err(_) => match row.try_get::<Option<u32>, usize>(ordinal) {
686                    Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
687                    Err(_) => Value::Null,
688                },
689            }
690        }
691        "BIGINT" | "BIGINT UNSIGNED" => match row.try_get::<Option<i64>, usize>(ordinal) {
692            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
693            Err(_) => match row.try_get::<Option<u64>, usize>(ordinal) {
694                Ok(v) => v.map(Value::U64).unwrap_or(Value::Null),
695                Err(_) => Value::Null,
696            },
697        },
698        "FLOAT" => match row.try_get::<Option<f32>, usize>(ordinal) {
699            Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
700            Err(_) => Value::Null,
701        },
702        "DOUBLE" => match row.try_get::<Option<f64>, usize>(ordinal) {
703            Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
704            Err(_) => Value::Null,
705        },
706        "VARCHAR" | "TEXT" | "CHAR" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM" => {
707            match row.try_get::<Option<String>, usize>(ordinal) {
708                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
709                Err(_) => Value::Null,
710            }
711        }
712        "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BINARY" | "VARBINARY" => {
713            match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
714                Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
715                Err(_) => Value::Null,
716            }
717        }
718        // DECIMAL/NUMERIC 使用 rust_decimal 解码,以字符串形式保留精度
719        "DECIMAL" | "NUMERIC" | "NEWDECIMAL" => {
720            match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
721                Ok(Some(v)) => Value::Decimal(v.to_string()),
722                Ok(None) => Value::Null,
723                Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
724                    Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
725                    Err(_) => Value::Null,
726                },
727            }
728        }
729        _ => {
730            // 未知类型回退:i64 → f64 → bool → String
731            if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
732                return v.map(Value::I64).unwrap_or(Value::Null);
733            }
734            if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
735                return v.map(Value::F64).unwrap_or(Value::Null);
736            }
737            if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
738                return v.map(Value::Bool).unwrap_or(Value::Null);
739            }
740            if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
741                return v.map(Value::String).unwrap_or(Value::Null);
742            }
743            Value::Null
744        }
745    }
746}
747
748/// MySQL: 使用预解析的 ColType 进行类型分派(避免每行字符串 match)
749///
750/// 与 [`row_to_value_mysql`] 的区别:调用方预先通过 [`ColType::parse_mysql`] 解析列类型,
751/// 后续行直接用枚举分派(编译器优化为跳转表),避免每行每列的字符串比较。
752///
753/// **注意**:必须使用 `parse_mysql` 而非通用 `from_type_name`:MySQL 协议报告的类型名
754/// 包含 NEWDECIMAL/YEAR/ENUM/SET 等特有类型,通用映射无法识别。
755fn row_to_value_with_coltype_mysql(
756    row: &sqlx::mysql::MySqlRow,
757    ordinal: usize,
758    col_type: ColType,
759) -> Value {
760    match col_type {
761        ColType::Bool => match row.try_get::<Option<bool>, usize>(ordinal) {
762            Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
763            Err(_) => Value::Null,
764        },
765        ColType::I8 => match row.try_get::<Option<i8>, usize>(ordinal) {
766            Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
767            Err(_) => match row.try_get::<Option<u8>, usize>(ordinal) {
768                Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
769                Err(_) => Value::Null,
770            },
771        },
772        ColType::I16 => match row.try_get::<Option<i16>, usize>(ordinal) {
773            Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
774            Err(_) => match row.try_get::<Option<u16>, usize>(ordinal) {
775                Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
776                Err(_) => Value::Null,
777            },
778        },
779        ColType::I32 => match row.try_get::<Option<i32>, usize>(ordinal) {
780            Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
781            Err(_) => match row.try_get::<Option<u32>, usize>(ordinal) {
782                Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
783                Err(_) => Value::Null,
784            },
785        },
786        ColType::I64 => match row.try_get::<Option<i64>, usize>(ordinal) {
787            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
788            Err(_) => match row.try_get::<Option<u64>, usize>(ordinal) {
789                Ok(v) => v.map(Value::U64).unwrap_or(Value::Null),
790                Err(_) => Value::Null,
791            },
792        },
793        ColType::U8 => match row.try_get::<Option<u8>, usize>(ordinal) {
794            Ok(v) => v.map(Value::U8).unwrap_or(Value::Null),
795            Err(_) => Value::Null,
796        },
797        ColType::U16 => match row.try_get::<Option<u16>, usize>(ordinal) {
798            Ok(v) => v.map(Value::U16).unwrap_or(Value::Null),
799            Err(_) => Value::Null,
800        },
801        ColType::U32 => match row.try_get::<Option<u32>, usize>(ordinal) {
802            Ok(v) => v.map(Value::U32).unwrap_or(Value::Null),
803            Err(_) => Value::Null,
804        },
805        ColType::U64 => match row.try_get::<Option<u64>, usize>(ordinal) {
806            Ok(v) => v.map(Value::U64).unwrap_or(Value::Null),
807            Err(_) => Value::Null,
808        },
809        ColType::F32 => match row.try_get::<Option<f32>, usize>(ordinal) {
810            Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
811            Err(_) => Value::Null,
812        },
813        ColType::F64 => match row.try_get::<Option<f64>, usize>(ordinal) {
814            Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
815            Err(_) => Value::Null,
816        },
817        // DECIMAL/NUMERIC 使用 rust_decimal 解码,以字符串形式保留精度
818        ColType::Decimal => match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
819            Ok(Some(v)) => Value::Decimal(v.to_string()),
820            Ok(None) => Value::Null,
821            Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
822                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
823                Err(_) => Value::Null,
824            },
825        },
826        ColType::String => match row.try_get::<Option<String>, usize>(ordinal) {
827            Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
828            Err(_) => Value::Null,
829        },
830        ColType::Bytes => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
831            Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
832            Err(_) => Value::Null,
833        },
834        // DATE/DATETIME/TIME/JSON/UUID 在 MySQL 中通常以字符串解码
835        ColType::Date | ColType::DateTime | ColType::Time | ColType::Json | ColType::Uuid => {
836            match row.try_get::<Option<String>, usize>(ordinal) {
837                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
838                Err(_) => Value::Null,
839            }
840        }
841        ColType::Unknown => {
842            // 未知类型回退:i64 → f64 → bool → String
843            if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
844                return v.map(Value::I64).unwrap_or(Value::Null);
845            }
846            if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
847                return v.map(Value::F64).unwrap_or(Value::Null);
848            }
849            if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
850                return v.map(Value::Bool).unwrap_or(Value::Null);
851            }
852            if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
853                return v.map(Value::String).unwrap_or(Value::Null);
854            }
855            Value::Null
856        }
857        // ColType 标记为 #[non_exhaustive],未来新增变体按 Unknown 处理
858        _ => Value::Null,
859    }
860}
861
862pub struct MySqlPoolHandle {
863    pool: sqlx::MySqlPool,
864}
865
866impl MySqlPoolHandle {
867    pub async fn connect(url: &str) -> Result<Self, DbError> {
868        let pool = sqlx::pool::PoolOptions::<sqlx::MySql>::new()
869            .max_connections(10)
870            .acquire_timeout(std::time::Duration::from_secs(30))
871            .idle_timeout(Some(std::time::Duration::from_secs(600)))
872            .max_lifetime(Some(std::time::Duration::from_secs(1800)))
873            .connect(url)
874            .await
875            .map_err(map_sqlx_error)?;
876        Ok(Self { pool })
877    }
878
879    pub fn from_pool(pool: sqlx::MySqlPool) -> Self {
880        Self { pool }
881    }
882
883    pub fn pool(&self) -> &sqlx::MySqlPool {
884        &self.pool
885    }
886}
887
888pub struct SqlxMySqlConnectionFactory {
889    pool: Arc<MySqlPoolHandle>,
890}
891
892impl SqlxMySqlConnectionFactory {
893    pub fn new(pool: Arc<MySqlPoolHandle>) -> Self {
894        Self { pool }
895    }
896}
897
898#[async_trait]
899impl ConnectionFactory for SqlxMySqlConnectionFactory {
900    async fn create(&self) -> Result<Box<dyn Connection>, DbError> {
901        let conn = self.pool.pool.acquire().await.map_err(map_sqlx_error)?;
902        Ok(Box::new(SqlxMySqlConnection {
903            conn: Some(conn),
904            connected: true,
905            in_transaction: false,
906        }))
907    }
908}
909
910pub struct SqlxMySqlConnection {
911    conn: Option<sqlx::pool::PoolConnection<sqlx::MySql>>,
912    connected: bool,
913    in_transaction: bool,
914}
915
916impl Connection for SqlxMySqlConnection {
917    fn execute<'a>(
918        &'a mut self,
919        sql: &'a str,
920    ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
921        Box::pin(async move {
922            let mut pool_conn = self
923                .conn
924                .take()
925                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
926            // sqlx 0.9: PoolConnection 不再实现 Executor,需通过 DerefMut 解引用到内部连接
927            // sqlx 0.9: SqlSafeStr 只对 &'static str 直接实现,非 'static 的 &str 需用 AssertSqlSafe 包装
928            let result = if needs_raw_sql(sql) {
929                (&mut *pool_conn)
930                    .execute(sqlx::raw_sql(sqlx::AssertSqlSafe(sql)))
931                    .await
932            } else {
933                (&mut *pool_conn).execute(sqlx::AssertSqlSafe(sql)).await
934            };
935            self.conn = Some(pool_conn);
936
937            match result {
938                Ok(r) => Ok(r.rows_affected()),
939                Err(e) => {
940                    let db_err = map_sqlx_error(e);
941                    if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
942                        self.connected = false;
943                    }
944                    Err(db_err)
945                }
946            }
947        })
948    }
949
950    fn query<'a>(
951        &'a mut self,
952        sql: &'a str,
953    ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>, DbError>> + Send + 'a>>
954    {
955        Box::pin(async move {
956            let mut pool_conn = self
957                .conn
958                .take()
959                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
960            // sqlx 0.9: PoolConnection 不再实现 Executor,需通过 DerefMut 解引用到内部连接
961            // sqlx 0.9: SqlSafeStr 只对 &'static str 直接实现,非 'static 的 &str 需用 AssertSqlSafe 包装
962            let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
963            self.conn = Some(pool_conn);
964
965            let rows = rows_result.map_err(map_sqlx_error)?;
966            if rows.is_empty() {
967                return Ok(Vec::new());
968            }
969            // 预解析列类型(只在第一行解析,后续行复用,避免每行字符串 match)
970            // MySQL 专用解析:覆盖 MySQL 协议特有类型名(NEWDECIMAL/YEAR/ENUM/SET 等)
971            let col_types: Vec<ColType> = rows[0]
972                .columns()
973                .iter()
974                .map(|col| ColType::parse_mysql(col.type_info().name()))
975                .collect();
976            let col_names: Vec<String> = rows[0]
977                .columns()
978                .iter()
979                .map(|col| col.name().to_string())
980                .collect();
981            let mut result = Vec::with_capacity(rows.len());
982            for row in &rows {
983                let mut record = HashMap::with_capacity(col_types.len());
984                for (i, name) in col_names.iter().enumerate() {
985                    let value = row_to_value_with_coltype_mysql(row, i, col_types[i]);
986                    record.insert(name.clone(), value);
987                }
988                result.push(record);
989            }
990            Ok(result)
991        })
992    }
993
994    fn begin_transaction<'a>(
995        &'a mut self,
996    ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
997        Box::pin(async move {
998            if self.in_transaction {
999                return Err(DbError::Internal("transaction already started".to_string()));
1000            }
1001            self.execute("BEGIN").await?;
1002            self.in_transaction = true;
1003            Ok(())
1004        })
1005    }
1006
1007    fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1008        Box::pin(async move {
1009            if self.in_transaction {
1010                self.execute("COMMIT").await?;
1011                self.in_transaction = false;
1012            }
1013            Ok(())
1014        })
1015    }
1016
1017    fn rollback<'a>(
1018        &'a mut self,
1019    ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1020        Box::pin(async move {
1021            if self.in_transaction {
1022                let result = self.execute("ROLLBACK").await;
1023                self.in_transaction = false;
1024                result.map(|_| ())
1025            } else {
1026                Ok(())
1027            }
1028        })
1029    }
1030
1031    fn is_connected(&self) -> bool {
1032        // 纳入 conn.is_some():query_stream 提前弃读时 PoolConnection 已从包装器
1033        // take() 出去(self.conn = None),毒化状态不得被判定为可用连接
1034        // (否则回池后下次 acquire 借出即报 "connection already closed")
1035        self.connected && self.conn.is_some()
1036    }
1037
1038    fn in_transaction(&self) -> bool {
1039        self.in_transaction
1040    }
1041
1042    fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
1043        Box::pin(async move {
1044            match self.execute("SELECT 1").await {
1045                Ok(_) => true,
1046                Err(_) => {
1047                    self.connected = false;
1048                    false
1049                }
1050            }
1051        })
1052    }
1053
1054    fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1055        Box::pin(async move {
1056            if let Some(conn) = self.conn.take() {
1057                drop(conn);
1058            }
1059            self.connected = false;
1060            self.in_transaction = false;
1061            Ok(())
1062        })
1063    }
1064
1065    /// MySQL 参数绑定执行(INSERT/UPDATE/DELETE)
1066    ///
1067    /// 使用 sqlx prepared statement 绑定参数。MySQL 协议原生支持 `?` 占位符。
1068    /// 对 `Value::Bool`/`I8`..=`I64`/`U8`..=`U64`/`F32`/`F64`/`String`/`Bytes` 直接 bind;
1069    /// 其他类型回退为 `to_string()` 后以 TEXT 绑定。
1070    fn execute_with_params<'a>(
1071        &'a mut self,
1072        sql: &'a str,
1073        params: &'a [Value],
1074    ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
1075        Box::pin(async move {
1076            if needs_raw_sql(sql) || params.is_empty() {
1077                return self.execute(sql).await;
1078            }
1079            let mut pool_conn = self
1080                .conn
1081                .take()
1082                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1083            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1084            for v in params {
1085                q = match v {
1086                    Value::Null => q.bind(None::<i64>),
1087                    Value::Bool(b) => q.bind(*b),
1088                    Value::I8(n) => q.bind(*n),
1089                    Value::I16(n) => q.bind(*n),
1090                    Value::I32(n) => q.bind(*n),
1091                    Value::I64(n) => q.bind(*n),
1092                    Value::U8(n) => q.bind(*n),
1093                    Value::U16(n) => q.bind(*n),
1094                    Value::U32(n) => q.bind(*n),
1095                    Value::U64(n) => q.bind(*n as i64),
1096                    Value::F32(f) => q.bind(*f),
1097                    Value::F64(f) => q.bind(*f),
1098                    Value::String(s) => q.bind(s.as_str()),
1099                    Value::Decimal(s) => q.bind(s.as_str()),
1100                    Value::Bytes(b) => q.bind(b.as_slice()),
1101                    other => q.bind(other.to_string()),
1102                };
1103            }
1104            let result = q.execute(&mut *pool_conn).await;
1105            self.conn = Some(pool_conn);
1106            match result {
1107                Ok(r) => Ok(r.rows_affected()),
1108                Err(e) => {
1109                    let db_err = map_sqlx_error(e);
1110                    if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
1111                        self.connected = false;
1112                    }
1113                    Err(db_err)
1114                }
1115            }
1116        })
1117    }
1118
1119    /// MySQL 参数绑定查询(SELECT,HashMap 映射)
1120    fn query_with_params<'a>(
1121        &'a mut self,
1122        sql: &'a str,
1123        params: &'a [Value],
1124    ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
1125        Box::pin(async move {
1126            if params.is_empty() {
1127                return self.query(sql).await;
1128            }
1129            let mut pool_conn = self
1130                .conn
1131                .take()
1132                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1133            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1134            for v in params {
1135                q = match v {
1136                    Value::Null => q.bind(None::<i64>),
1137                    Value::Bool(b) => q.bind(*b),
1138                    Value::I8(n) => q.bind(*n),
1139                    Value::I16(n) => q.bind(*n),
1140                    Value::I32(n) => q.bind(*n),
1141                    Value::I64(n) => q.bind(*n),
1142                    Value::U8(n) => q.bind(*n),
1143                    Value::U16(n) => q.bind(*n),
1144                    Value::U32(n) => q.bind(*n),
1145                    Value::U64(n) => q.bind(*n as i64),
1146                    Value::F32(f) => q.bind(*f),
1147                    Value::F64(f) => q.bind(*f),
1148                    Value::String(s) => q.bind(s.as_str()),
1149                    Value::Decimal(s) => q.bind(s.as_str()),
1150                    Value::Bytes(b) => q.bind(b.as_slice()),
1151                    other => q.bind(other.to_string()),
1152                };
1153            }
1154            let rows_result = q.fetch_all(&mut *pool_conn).await;
1155            self.conn = Some(pool_conn);
1156            let rows = rows_result.map_err(map_sqlx_error)?;
1157            if rows.is_empty() {
1158                return Ok(Vec::new());
1159            }
1160            let col_names: Vec<String> = rows[0]
1161                .columns()
1162                .iter()
1163                .map(|col| col.name().to_string())
1164                .collect();
1165            let mut result = Vec::with_capacity(rows.len());
1166            for row in &rows {
1167                let mut record = HashMap::with_capacity(col_names.len());
1168                for (i, name) in col_names.iter().enumerate() {
1169                    let value = row_to_value_mysql(row, i);
1170                    record.insert(name.clone(), value);
1171                }
1172                result.push(record);
1173            }
1174            Ok(result)
1175        })
1176    }
1177
1178    /// MySQL 位置式查询(SELECT,无参数)
1179    ///
1180    /// 绕过 HashMap 行映射,返回 `(列名, 按列序号的值矩阵)`。适用于 SELECT ALL 大结果集。
1181    fn query_values<'a>(
1182        &'a mut self,
1183        sql: &'a str,
1184    ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1185        Box::pin(async move {
1186            let mut pool_conn = self
1187                .conn
1188                .take()
1189                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1190            let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
1191            self.conn = Some(pool_conn);
1192            let rows = rows_result.map_err(map_sqlx_error)?;
1193            if rows.is_empty() {
1194                return Ok((Vec::new(), Vec::new()));
1195            }
1196            let cols = rows[0].columns();
1197            let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1198            let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
1199            for col in cols {
1200                col_names.push(col.name().to_string());
1201                col_types.push(ColType::parse_mysql(col.type_info().name()));
1202            }
1203            let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1204            for row in &rows {
1205                let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1206                for (idx, _) in col_names.iter().enumerate() {
1207                    row_values.push(row_to_value_with_coltype_mysql(row, idx, col_types[idx]));
1208                }
1209                result_rows.push(row_values);
1210            }
1211            Ok((col_names, result_rows))
1212        })
1213    }
1214
1215    /// MySQL 参数绑定位置式查询(SELECT)
1216    fn query_values_with_params<'a>(
1217        &'a mut self,
1218        sql: &'a str,
1219        params: &'a [Value],
1220    ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1221        Box::pin(async move {
1222            if params.is_empty() {
1223                return self.query_values(sql).await;
1224            }
1225            let mut pool_conn = self
1226                .conn
1227                .take()
1228                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1229            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1230            for v in params {
1231                q = match v {
1232                    Value::Null => q.bind(None::<i64>),
1233                    Value::Bool(b) => q.bind(*b),
1234                    Value::I8(n) => q.bind(*n),
1235                    Value::I16(n) => q.bind(*n),
1236                    Value::I32(n) => q.bind(*n),
1237                    Value::I64(n) => q.bind(*n),
1238                    Value::U8(n) => q.bind(*n),
1239                    Value::U16(n) => q.bind(*n),
1240                    Value::U32(n) => q.bind(*n),
1241                    Value::U64(n) => q.bind(*n as i64),
1242                    Value::F32(f) => q.bind(*f),
1243                    Value::F64(f) => q.bind(*f),
1244                    Value::String(s) => q.bind(s.as_str()),
1245                    Value::Decimal(s) => q.bind(s.as_str()),
1246                    Value::Bytes(b) => q.bind(b.as_slice()),
1247                    other => q.bind(other.to_string()),
1248                };
1249            }
1250            let rows_result = q.fetch_all(&mut *pool_conn).await;
1251            self.conn = Some(pool_conn);
1252            let rows = rows_result.map_err(map_sqlx_error)?;
1253            if rows.is_empty() {
1254                return Ok((Vec::new(), Vec::new()));
1255            }
1256            let cols = rows[0].columns();
1257            let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1258            let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
1259            for col in cols {
1260                col_names.push(col.name().to_string());
1261                col_types.push(ColType::parse_mysql(col.type_info().name()));
1262            }
1263            let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1264            for row in &rows {
1265                let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1266                for (idx, _) in col_names.iter().enumerate() {
1267                    row_values.push(row_to_value_with_coltype_mysql(row, idx, col_types[idx]));
1268                }
1269                result_rows.push(row_values);
1270            }
1271            Ok((col_names, result_rows))
1272        })
1273    }
1274
1275    /// MySQL 流式查询:逐行返回结果,避免大结果集 `fetch_all` 内存峰值
1276    ///
1277    /// 使用 `sqlx::query::fetch` 获取行流,逐行映射为 `HashMap<String, Value>`。
1278    /// 流正常消费完毕后连接归还 `self.conn`;若提前 drop 流,连接通过
1279    /// `PoolConnection::drop` 归还到 sqlx 池,但 `self.conn` 变为 `None`。
1280    fn query_stream<'a>(
1281        &'a mut self,
1282        sql: &'a str,
1283    ) -> Pin<Box<dyn futures::Stream<Item = Result<HashMap<String, Value>, DbError>> + Send + 'a>>
1284    {
1285        Box::pin(async_stream::try_stream! {
1286            let mut pool_conn = self
1287                .conn
1288                .take()
1289                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1290            let mut row_stream = sqlx::query(sqlx::AssertSqlSafe(sql)).fetch(&mut *pool_conn);
1291            let mut col_types: Vec<ColType> = Vec::new();
1292            let mut col_names: Vec<String> = Vec::new();
1293            let mut first_row = true;
1294            while let Some(row_result) = row_stream.next().await {
1295                let row = row_result.map_err(map_sqlx_error)?;
1296                if first_row {
1297                    for col in row.columns() {
1298                        col_names.push(col.name().to_string());
1299                        col_types.push(ColType::parse_mysql(col.type_info().name()));
1300                    }
1301                    first_row = false;
1302                }
1303                let mut record = HashMap::with_capacity(col_names.len());
1304                for (i, name) in col_names.iter().enumerate() {
1305                    let value = row_to_value_with_coltype_mysql(&row, i, col_types[i]);
1306                    record.insert(name.clone(), value);
1307                }
1308                yield record;
1309            }
1310            // 显式释放 row_stream 对 pool_conn 的可变借用,否则借用检查器
1311            // 无法证明 row_stream 在移动 pool_conn 前已被销毁(E0505)
1312            drop(row_stream);
1313            self.conn = Some(pool_conn);
1314        })
1315    }
1316}
1317
1318impl Drop for SqlxMySqlConnection {
1319    fn drop(&mut self) {
1320        if let Some(conn) = self.conn.take() {
1321            drop(conn);
1322        }
1323    }
1324}
1325
1326/// MySQL 批量导入(多行 INSERT,作为 LOAD DATA LOCAL INFILE 的安全替代)
1327///
1328/// 构建多行 INSERT 语句:`INSERT INTO t (c1, c2) VALUES (?, ?), (?, ?), ...`
1329/// 使用参数绑定避免 SQL 注入。MySQL 原生支持 `?` 占位符。
1330/// 当数据量极大时建议分批调用(每批 1000~10000 行),避免 SQL 过长或超出
1331/// `max_allowed_packet` 限制。
1332pub async fn mysql_bulk_insert(
1333    conn: &mut SqlxMySqlConnection,
1334    table: &str,
1335    columns: &[&str],
1336    rows: &[Vec<Value>],
1337) -> Result<u64, DbError> {
1338    if rows.is_empty() {
1339        return Ok(0);
1340    }
1341    let col_list = columns.join(", ");
1342    let cols_per_row = columns.len();
1343    // MySQL 占位符 ? 每列一个,跨行复用相同模式
1344    let row_placeholder = format!("({})", vec!["?"; cols_per_row].join(", "));
1345    let placeholders = vec![row_placeholder; rows.len()].join(", ");
1346    let sql = format!(
1347        "INSERT INTO {} ({}) VALUES {}",
1348        table, col_list, placeholders
1349    );
1350    // 展平所有行的值为单一参数数组
1351    let mut params: Vec<Value> = Vec::with_capacity(rows.len() * cols_per_row);
1352    for row in rows {
1353        for v in row {
1354            params.push(v.clone());
1355        }
1356    }
1357    conn.execute_with_params(&sql, &params).await
1358}
1359
1360// ===================== PostgreSQL 适配器 =====================
1361
1362// 注:sqlx 0.9 起 Executor trait 要求 'static lifetime,
1363// 原先的 execute_pg_boxed / query_pg_boxed 已内联到调用点。
1364
1365fn row_to_value_pg(row: &sqlx::postgres::PgRow, ordinal: usize) -> Value {
1366    use sqlx::TypeInfo;
1367    let type_name = row.columns()[ordinal].type_info().name();
1368    match type_name {
1369        "BOOL" => match row.try_get::<Option<bool>, usize>(ordinal) {
1370            Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
1371            Err(_) => Value::Null,
1372        },
1373        "INT2" => match row.try_get::<Option<i16>, usize>(ordinal) {
1374            Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
1375            Err(_) => Value::Null,
1376        },
1377        "INT4" | "OID" => match row.try_get::<Option<i32>, usize>(ordinal) {
1378            Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
1379            Err(_) => Value::Null,
1380        },
1381        "INT8" => match row.try_get::<Option<i64>, usize>(ordinal) {
1382            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1383            Err(_) => Value::Null,
1384        },
1385        "FLOAT4" => match row.try_get::<Option<f32>, usize>(ordinal) {
1386            Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
1387            Err(_) => Value::Null,
1388        },
1389        "FLOAT8" => match row.try_get::<Option<f64>, usize>(ordinal) {
1390            Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
1391            Err(_) => Value::Null,
1392        },
1393        "TEXT" | "VARCHAR" | "CHAR" | "NAME" => match row.try_get::<Option<String>, usize>(ordinal)
1394        {
1395            Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1396            Err(_) => Value::Null,
1397        },
1398        "BYTEA" => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
1399            Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
1400            Err(_) => Value::Null,
1401        },
1402        "NUMERIC" => match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
1403            Ok(Some(v)) => Value::Decimal(v.to_string()),
1404            Ok(None) => Value::Null,
1405            Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1406                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1407                Err(_) => Value::Null,
1408            },
1409        },
1410        // UUID:使用 sqlx::types::Uuid(16 字节)解码,避免 36 字符字符串的内存浪费
1411        "UUID" => match row.try_get::<Option<sqlx::types::Uuid>, usize>(ordinal) {
1412            Ok(v) => v
1413                .map(|uuid| Value::String(uuid.to_string()))
1414                .unwrap_or(Value::Null),
1415            Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1416                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1417                Err(_) => Value::Null,
1418            },
1419        },
1420        _ => {
1421            // 未知类型回退
1422            if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
1423                return v.map(Value::I64).unwrap_or(Value::Null);
1424            }
1425            if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
1426                return v.map(Value::F64).unwrap_or(Value::Null);
1427            }
1428            if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
1429                return v.map(Value::Bool).unwrap_or(Value::Null);
1430            }
1431            if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
1432                return v.map(Value::String).unwrap_or(Value::Null);
1433            }
1434            Value::Null
1435        }
1436    }
1437}
1438
1439/// PostgreSQL: 使用预解析的 ColType 进行类型分派(避免每行字符串 match)
1440///
1441/// 与 [`row_to_value_pg`] 的区别:调用方预先通过 [`ColType::parse_postgres`] 解析列类型,
1442/// 后续行直接用枚举分派(编译器优化为跳转表),避免每行每列的字符串比较。
1443///
1444/// **注意**:必须使用 `parse_postgres` 而非通用 `from_type_name`:PostgreSQL 使用
1445/// PG 内部类型名(INT4/INT8/FLOAT8/BPCHAR/JSONB/TIMESTAMPTZ 等),通用映射无法识别。
1446fn row_to_value_with_coltype_pg(
1447    row: &sqlx::postgres::PgRow,
1448    ordinal: usize,
1449    col_type: ColType,
1450) -> Value {
1451    match col_type {
1452        ColType::Bool => match row.try_get::<Option<bool>, usize>(ordinal) {
1453            Ok(v) => v.map(Value::Bool).unwrap_or(Value::Null),
1454            Err(_) => Value::Null,
1455        },
1456        ColType::I8 => match row.try_get::<Option<i8>, usize>(ordinal) {
1457            Ok(v) => v.map(Value::I8).unwrap_or(Value::Null),
1458            Err(_) => Value::Null,
1459        },
1460        ColType::I16 => match row.try_get::<Option<i16>, usize>(ordinal) {
1461            Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
1462            Err(_) => Value::Null,
1463        },
1464        // PostgreSQL OID 在 parse_postgres 中归为 I32
1465        ColType::I32 => match row.try_get::<Option<i32>, usize>(ordinal) {
1466            Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
1467            Err(_) => Value::Null,
1468        },
1469        ColType::I64 => match row.try_get::<Option<i64>, usize>(ordinal) {
1470            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1471            Err(_) => Value::Null,
1472        },
1473        // PostgreSQL 无原生无符号类型(u8/u16/u32):
1474        // - ColType::U8/U16/U32 仅在 parse_postgres 误判时出现(PG 无 UNSIGNED 关键字)
1475        // - 退化为最小可容纳的有符号类型:U8→i16、U16→i32、U32→i64
1476        // - 数值正确性保持,仅在 Value 枚举上为有符号变体(与 sqlx Type<Postgres> 实现一致)
1477        ColType::U8 => match row.try_get::<Option<i16>, usize>(ordinal) {
1478            Ok(v) => v.map(Value::I16).unwrap_or(Value::Null),
1479            Err(_) => Value::Null,
1480        },
1481        ColType::U16 => match row.try_get::<Option<i32>, usize>(ordinal) {
1482            Ok(v) => v.map(Value::I32).unwrap_or(Value::Null),
1483            Err(_) => Value::Null,
1484        },
1485        ColType::U32 => match row.try_get::<Option<i64>, usize>(ordinal) {
1486            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1487            Err(_) => Value::Null,
1488        },
1489        // PostgreSQL 无原生 U64,按 i64 解码
1490        ColType::U64 => match row.try_get::<Option<i64>, usize>(ordinal) {
1491            Ok(v) => v.map(Value::I64).unwrap_or(Value::Null),
1492            Err(_) => Value::Null,
1493        },
1494        ColType::F32 => match row.try_get::<Option<f32>, usize>(ordinal) {
1495            Ok(v) => v.map(Value::F32).unwrap_or(Value::Null),
1496            Err(_) => Value::Null,
1497        },
1498        ColType::F64 => match row.try_get::<Option<f64>, usize>(ordinal) {
1499            Ok(v) => v.map(Value::F64).unwrap_or(Value::Null),
1500            Err(_) => Value::Null,
1501        },
1502        // NUMERIC/DECIMAL/MONEY 使用 rust_decimal 解码,以字符串形式保留精度
1503        ColType::Decimal => match row.try_get::<Option<rust_decimal::Decimal>, usize>(ordinal) {
1504            Ok(Some(v)) => Value::Decimal(v.to_string()),
1505            Ok(None) => Value::Null,
1506            Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1507                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1508                Err(_) => Value::Null,
1509            },
1510        },
1511        ColType::String => match row.try_get::<Option<String>, usize>(ordinal) {
1512            Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1513            Err(_) => Value::Null,
1514        },
1515        ColType::Bytes => match row.try_get::<Option<Vec<u8>>, usize>(ordinal) {
1516            Ok(v) => v.map(Value::Bytes).unwrap_or(Value::Null),
1517            Err(_) => Value::Null,
1518        },
1519        // DATE/DATETIME/TIME/JSON 在 PostgreSQL 中通常以字符串解码
1520        ColType::Date | ColType::DateTime | ColType::Time | ColType::Json => {
1521            match row.try_get::<Option<String>, usize>(ordinal) {
1522                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1523                Err(_) => Value::Null,
1524            }
1525        }
1526        // UUID:使用 sqlx::types::Uuid(16 字节)解码,避免 36 字符字符串的内存浪费
1527        ColType::Uuid => match row.try_get::<Option<sqlx::types::Uuid>, usize>(ordinal) {
1528            Ok(v) => v
1529                .map(|uuid| Value::String(uuid.to_string()))
1530                .unwrap_or(Value::Null),
1531            Err(_) => match row.try_get::<Option<String>, usize>(ordinal) {
1532                Ok(v) => v.map(Value::String).unwrap_or(Value::Null),
1533                Err(_) => Value::Null,
1534            },
1535        },
1536        ColType::Unknown => {
1537            // 未知类型回退:i64 → f64 → bool → String
1538            if let Ok(v) = row.try_get::<Option<i64>, usize>(ordinal) {
1539                return v.map(Value::I64).unwrap_or(Value::Null);
1540            }
1541            if let Ok(v) = row.try_get::<Option<f64>, usize>(ordinal) {
1542                return v.map(Value::F64).unwrap_or(Value::Null);
1543            }
1544            if let Ok(v) = row.try_get::<Option<bool>, usize>(ordinal) {
1545                return v.map(Value::Bool).unwrap_or(Value::Null);
1546            }
1547            if let Ok(v) = row.try_get::<Option<String>, usize>(ordinal) {
1548                return v.map(Value::String).unwrap_or(Value::Null);
1549            }
1550            Value::Null
1551        }
1552        // ColType 标记为 #[non_exhaustive],未来新增变体按 Unknown 处理
1553        _ => Value::Null,
1554    }
1555}
1556
1557pub struct PgPoolHandle {
1558    pool: sqlx::PgPool,
1559}
1560
1561impl PgPoolHandle {
1562    pub async fn connect(url: &str) -> Result<Self, DbError> {
1563        let pool = sqlx::pool::PoolOptions::<sqlx::Postgres>::new()
1564            .max_connections(10)
1565            .acquire_timeout(std::time::Duration::from_secs(30))
1566            .idle_timeout(Some(std::time::Duration::from_secs(600)))
1567            .max_lifetime(Some(std::time::Duration::from_secs(1800)))
1568            .connect(url)
1569            .await
1570            .map_err(map_sqlx_error)?;
1571        Ok(Self { pool })
1572    }
1573
1574    pub fn from_pool(pool: sqlx::PgPool) -> Self {
1575        Self { pool }
1576    }
1577
1578    pub fn pool(&self) -> &sqlx::PgPool {
1579        &self.pool
1580    }
1581}
1582
1583pub struct SqlxPgConnectionFactory {
1584    pool: Arc<PgPoolHandle>,
1585}
1586
1587impl SqlxPgConnectionFactory {
1588    pub fn new(pool: Arc<PgPoolHandle>) -> Self {
1589        Self { pool }
1590    }
1591}
1592
1593#[async_trait]
1594impl ConnectionFactory for SqlxPgConnectionFactory {
1595    async fn create(&self) -> Result<Box<dyn Connection>, DbError> {
1596        let conn = self.pool.pool.acquire().await.map_err(map_sqlx_error)?;
1597        Ok(Box::new(SqlxPgConnection {
1598            conn: Some(conn),
1599            connected: true,
1600            in_transaction: false,
1601        }))
1602    }
1603}
1604
1605pub struct SqlxPgConnection {
1606    conn: Option<sqlx::pool::PoolConnection<sqlx::Postgres>>,
1607    connected: bool,
1608    in_transaction: bool,
1609}
1610
1611impl Connection for SqlxPgConnection {
1612    fn execute<'a>(
1613        &'a mut self,
1614        sql: &'a str,
1615    ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
1616        Box::pin(async move {
1617            let mut pool_conn = self
1618                .conn
1619                .take()
1620                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1621            // sqlx 0.9: PoolConnection 不再实现 Executor,需通过 DerefMut 解引用到内部连接
1622            // sqlx 0.9: SqlSafeStr 只对 &'static str 直接实现,非 'static 的 &str 需用 AssertSqlSafe 包装
1623            let result = if needs_raw_sql(sql) {
1624                (&mut *pool_conn)
1625                    .execute(sqlx::raw_sql(sqlx::AssertSqlSafe(sql)))
1626                    .await
1627            } else {
1628                (&mut *pool_conn).execute(sqlx::AssertSqlSafe(sql)).await
1629            };
1630            self.conn = Some(pool_conn);
1631
1632            match result {
1633                Ok(r) => Ok(r.rows_affected()),
1634                Err(e) => {
1635                    let db_err = map_sqlx_error(e);
1636                    if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
1637                        self.connected = false;
1638                    }
1639                    Err(db_err)
1640                }
1641            }
1642        })
1643    }
1644
1645    fn query<'a>(
1646        &'a mut self,
1647        sql: &'a str,
1648    ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>, DbError>> + Send + 'a>>
1649    {
1650        Box::pin(async move {
1651            let mut pool_conn = self
1652                .conn
1653                .take()
1654                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1655            // sqlx 0.9: PoolConnection 不再实现 Executor,需通过 DerefMut 解引用到内部连接
1656            // sqlx 0.9: SqlSafeStr 只对 &'static str 直接实现,非 'static 的 &str 需用 AssertSqlSafe 包装
1657            let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
1658            self.conn = Some(pool_conn);
1659
1660            let rows = rows_result.map_err(map_sqlx_error)?;
1661            if rows.is_empty() {
1662                return Ok(Vec::new());
1663            }
1664            // 预解析列类型(只在第一行解析,后续行复用,避免每行字符串 match)
1665            // PostgreSQL 专用解析:覆盖 PG 内部类型名(INT4/INT8/FLOAT8/BPCHAR/JSONB 等)
1666            let col_types: Vec<ColType> = rows[0]
1667                .columns()
1668                .iter()
1669                .map(|col| ColType::parse_postgres(col.type_info().name()))
1670                .collect();
1671            let col_names: Vec<String> = rows[0]
1672                .columns()
1673                .iter()
1674                .map(|col| col.name().to_string())
1675                .collect();
1676            let mut result = Vec::with_capacity(rows.len());
1677            for row in &rows {
1678                let mut record = HashMap::with_capacity(col_types.len());
1679                for (i, name) in col_names.iter().enumerate() {
1680                    let value = row_to_value_with_coltype_pg(row, i, col_types[i]);
1681                    record.insert(name.clone(), value);
1682                }
1683                result.push(record);
1684            }
1685            Ok(result)
1686        })
1687    }
1688
1689    fn begin_transaction<'a>(
1690        &'a mut self,
1691    ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1692        Box::pin(async move {
1693            if self.in_transaction {
1694                return Err(DbError::Internal("transaction already started".to_string()));
1695            }
1696            self.execute("BEGIN").await?;
1697            self.in_transaction = true;
1698            Ok(())
1699        })
1700    }
1701
1702    fn commit<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1703        Box::pin(async move {
1704            if self.in_transaction {
1705                self.execute("COMMIT").await?;
1706                self.in_transaction = false;
1707            }
1708            Ok(())
1709        })
1710    }
1711
1712    fn rollback<'a>(
1713        &'a mut self,
1714    ) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1715        Box::pin(async move {
1716            if self.in_transaction {
1717                let result = self.execute("ROLLBACK").await;
1718                self.in_transaction = false;
1719                result.map(|_| ())
1720            } else {
1721                Ok(())
1722            }
1723        })
1724    }
1725
1726    fn is_connected(&self) -> bool {
1727        // 纳入 conn.is_some():query_stream 提前弃读时 PoolConnection 已从包装器
1728        // take() 出去(self.conn = None),毒化状态不得被判定为可用连接
1729        // (否则回池后下次 acquire 借出即报 "connection already closed")
1730        self.connected && self.conn.is_some()
1731    }
1732
1733    fn in_transaction(&self) -> bool {
1734        self.in_transaction
1735    }
1736
1737    fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
1738        Box::pin(async move {
1739            match self.execute("SELECT 1").await {
1740                Ok(_) => true,
1741                Err(_) => {
1742                    self.connected = false;
1743                    false
1744                }
1745            }
1746        })
1747    }
1748
1749    fn close<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), DbError>> + Send + 'a>> {
1750        Box::pin(async move {
1751            if let Some(conn) = self.conn.take() {
1752                drop(conn);
1753            }
1754            self.connected = false;
1755            self.in_transaction = false;
1756            Ok(())
1757        })
1758    }
1759
1760    /// PostgreSQL 参数绑定执行(INSERT/UPDATE/DELETE)
1761    ///
1762    /// 使用 sqlx prepared statement 绑定参数。PostgreSQL 协议使用 `$1, $2, ...` 占位符。
1763    ///
1764    /// # 类型映射说明
1765    ///
1766    /// PostgreSQL 不支持无符号整数类型(u8/u16/u32/u64),因此无符号 Value 绑定
1767    /// 时按"最小可容纳的有符号类型"转换:
1768    /// - `U8`  → `i16`(PostgreSQL `SMALLINT`)
1769    /// - `U16` → `i32`(PostgreSQL `INTEGER`)
1770    /// - `U32` → `i64`(PostgreSQL `BIGINT`)
1771    /// - `U64` → `i64`(可能截断,仅适用于 < `i64::MAX` 的值)
1772    ///
1773    /// 其他类型直接 bind;未知类型回退为 `to_string()` 后以 TEXT 绑定。
1774    fn execute_with_params<'a>(
1775        &'a mut self,
1776        sql: &'a str,
1777        params: &'a [Value],
1778    ) -> Pin<Box<dyn Future<Output = Result<u64, DbError>> + Send + 'a>> {
1779        Box::pin(async move {
1780            if needs_raw_sql(sql) || params.is_empty() {
1781                return self.execute(sql).await;
1782            }
1783            let mut pool_conn = self
1784                .conn
1785                .take()
1786                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1787            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1788            for v in params {
1789                q = match v {
1790                    Value::Null => q.bind(None::<i64>),
1791                    Value::Bool(b) => q.bind(*b),
1792                    Value::I8(n) => q.bind(*n),
1793                    Value::I16(n) => q.bind(*n),
1794                    Value::I32(n) => q.bind(*n),
1795                    Value::I64(n) => q.bind(*n),
1796                    // PostgreSQL 无 u8/u16/u32/u64:按最小可容纳有符号类型转换
1797                    Value::U8(n) => q.bind(*n as i16),
1798                    Value::U16(n) => q.bind(*n as i32),
1799                    Value::U32(n) => q.bind(*n as i64),
1800                    Value::U64(n) => q.bind(*n as i64),
1801                    Value::F32(f) => q.bind(*f),
1802                    Value::F64(f) => q.bind(*f),
1803                    Value::String(s) => q.bind(s.as_str()),
1804                    Value::Decimal(s) => q.bind(s.as_str()),
1805                    Value::Bytes(b) => q.bind(b.as_slice()),
1806                    other => q.bind(other.to_string()),
1807                };
1808            }
1809            let result = q.execute(&mut *pool_conn).await;
1810            self.conn = Some(pool_conn);
1811            match result {
1812                Ok(r) => Ok(r.rows_affected()),
1813                Err(e) => {
1814                    let db_err = map_sqlx_error(e);
1815                    if matches!(db_err, DbError::ConnectionError(_) | DbError::IoError(_)) {
1816                        self.connected = false;
1817                    }
1818                    Err(db_err)
1819                }
1820            }
1821        })
1822    }
1823
1824    /// PostgreSQL 参数绑定查询(SELECT,HashMap 映射)
1825    ///
1826    /// 类型映射规则与 [`Self::execute_with_params`] 相同。
1827    fn query_with_params<'a>(
1828        &'a mut self,
1829        sql: &'a str,
1830        params: &'a [Value],
1831    ) -> Pin<Box<dyn Future<Output = Result<QueryRows, DbError>> + Send + 'a>> {
1832        Box::pin(async move {
1833            if params.is_empty() {
1834                return self.query(sql).await;
1835            }
1836            let mut pool_conn = self
1837                .conn
1838                .take()
1839                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1840            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1841            for v in params {
1842                q = match v {
1843                    Value::Null => q.bind(None::<i64>),
1844                    Value::Bool(b) => q.bind(*b),
1845                    Value::I8(n) => q.bind(*n),
1846                    Value::I16(n) => q.bind(*n),
1847                    Value::I32(n) => q.bind(*n),
1848                    Value::I64(n) => q.bind(*n),
1849                    Value::U8(n) => q.bind(*n as i16),
1850                    Value::U16(n) => q.bind(*n as i32),
1851                    Value::U32(n) => q.bind(*n as i64),
1852                    Value::U64(n) => q.bind(*n as i64),
1853                    Value::F32(f) => q.bind(*f),
1854                    Value::F64(f) => q.bind(*f),
1855                    Value::String(s) => q.bind(s.as_str()),
1856                    Value::Decimal(s) => q.bind(s.as_str()),
1857                    Value::Bytes(b) => q.bind(b.as_slice()),
1858                    other => q.bind(other.to_string()),
1859                };
1860            }
1861            let rows_result = q.fetch_all(&mut *pool_conn).await;
1862            self.conn = Some(pool_conn);
1863            let rows = rows_result.map_err(map_sqlx_error)?;
1864            if rows.is_empty() {
1865                return Ok(Vec::new());
1866            }
1867            // 预解析列类型(只在第一行解析,后续行复用,避免每行字符串 match)
1868            let col_types: Vec<ColType> = rows[0]
1869                .columns()
1870                .iter()
1871                .map(|col| ColType::parse_postgres(col.type_info().name()))
1872                .collect();
1873            let col_names: Vec<String> = rows[0]
1874                .columns()
1875                .iter()
1876                .map(|col| col.name().to_string())
1877                .collect();
1878            let mut result = Vec::with_capacity(rows.len());
1879            for row in &rows {
1880                let mut record = HashMap::with_capacity(col_types.len());
1881                for (i, name) in col_names.iter().enumerate() {
1882                    let value = row_to_value_with_coltype_pg(row, i, col_types[i]);
1883                    record.insert(name.clone(), value);
1884                }
1885                result.push(record);
1886            }
1887            Ok(result)
1888        })
1889    }
1890
1891    /// PostgreSQL 位置式查询(SELECT,无参数)
1892    ///
1893    /// 绕过 HashMap 行映射,返回 `(列名, 按列序号的值矩阵)`。适用于 SELECT ALL 大结果集。
1894    fn query_values<'a>(
1895        &'a mut self,
1896        sql: &'a str,
1897    ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1898        Box::pin(async move {
1899            let mut pool_conn = self
1900                .conn
1901                .take()
1902                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1903            let rows_result = (&mut *pool_conn).fetch_all(sqlx::AssertSqlSafe(sql)).await;
1904            self.conn = Some(pool_conn);
1905            let rows = rows_result.map_err(map_sqlx_error)?;
1906            if rows.is_empty() {
1907                return Ok((Vec::new(), Vec::new()));
1908            }
1909            let cols = rows[0].columns();
1910            let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1911            let mut col_types: Vec<ColType> = Vec::with_capacity(cols.len());
1912            for col in cols {
1913                col_names.push(col.name().to_string());
1914                col_types.push(ColType::parse_postgres(col.type_info().name()));
1915            }
1916            let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1917            for row in &rows {
1918                let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1919                for (idx, _) in col_names.iter().enumerate() {
1920                    row_values.push(row_to_value_with_coltype_pg(row, idx, col_types[idx]));
1921                }
1922                result_rows.push(row_values);
1923            }
1924            Ok((col_names, result_rows))
1925        })
1926    }
1927
1928    /// PostgreSQL 参数绑定位置式查询(SELECT)
1929    ///
1930    /// 类型映射规则与 [`Self::execute_with_params`] 相同。
1931    fn query_values_with_params<'a>(
1932        &'a mut self,
1933        sql: &'a str,
1934        params: &'a [Value],
1935    ) -> Pin<Box<dyn Future<Output = Result<QueryValues, DbError>> + Send + 'a>> {
1936        Box::pin(async move {
1937            if params.is_empty() {
1938                return self.query_values(sql).await;
1939            }
1940            let mut pool_conn = self
1941                .conn
1942                .take()
1943                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
1944            let mut q = sqlx::query(sqlx::AssertSqlSafe(sql));
1945            for v in params {
1946                q = match v {
1947                    Value::Null => q.bind(None::<i64>),
1948                    Value::Bool(b) => q.bind(*b),
1949                    Value::I8(n) => q.bind(*n),
1950                    Value::I16(n) => q.bind(*n),
1951                    Value::I32(n) => q.bind(*n),
1952                    Value::I64(n) => q.bind(*n),
1953                    Value::U8(n) => q.bind(*n as i16),
1954                    Value::U16(n) => q.bind(*n as i32),
1955                    Value::U32(n) => q.bind(*n as i64),
1956                    Value::U64(n) => q.bind(*n as i64),
1957                    Value::F32(f) => q.bind(*f),
1958                    Value::F64(f) => q.bind(*f),
1959                    Value::String(s) => q.bind(s.as_str()),
1960                    Value::Decimal(s) => q.bind(s.as_str()),
1961                    Value::Bytes(b) => q.bind(b.as_slice()),
1962                    other => q.bind(other.to_string()),
1963                };
1964            }
1965            let rows_result = q.fetch_all(&mut *pool_conn).await;
1966            self.conn = Some(pool_conn);
1967            let rows = rows_result.map_err(map_sqlx_error)?;
1968            if rows.is_empty() {
1969                return Ok((Vec::new(), Vec::new()));
1970            }
1971            let cols = rows[0].columns();
1972            let mut col_names: Vec<String> = Vec::with_capacity(cols.len());
1973            for col in cols {
1974                col_names.push(col.name().to_string());
1975            }
1976            let mut result_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1977            for row in rows {
1978                let mut row_values: Vec<Value> = Vec::with_capacity(col_names.len());
1979                for (idx, _) in col_names.iter().enumerate() {
1980                    let ordinal = row.columns()[idx].ordinal();
1981                    row_values.push(row_to_value_pg(&row, ordinal));
1982                }
1983                result_rows.push(row_values);
1984            }
1985            Ok((col_names, result_rows))
1986        })
1987    }
1988
1989    /// PostgreSQL 流式查询:逐行返回结果,避免大结果集 `fetch_all` 内存峰值
1990    ///
1991    /// 使用 `sqlx::query::fetch` 获取行流,逐行映射为 `HashMap<String, Value>`。
1992    /// 流正常消费完毕后连接归还 `self.conn`;若提前 drop 流,连接通过
1993    /// `PoolConnection::drop` 归还到 sqlx 池,但 `self.conn` 变为 `None`。
1994    ///
1995    /// 注意:PostgreSQL 的 `fetch` 返回 `Pin<Box<dyn Stream<...>>>`(boxed trait object),
1996    /// 其析构函数可能持有 `pool_conn` 的借用,导致无法直接 `self.conn = Some(pool_conn)`。
1997    /// 通过显式 `drop(row_stream)` 提前结束借用,再归还连接。
1998    fn query_stream<'a>(
1999        &'a mut self,
2000        sql: &'a str,
2001    ) -> Pin<Box<dyn futures::Stream<Item = Result<HashMap<String, Value>, DbError>> + Send + 'a>>
2002    {
2003        Box::pin(async_stream::try_stream! {
2004            let mut pool_conn = self
2005                .conn
2006                .take()
2007                .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
2008            let mut row_stream = sqlx::query(sqlx::AssertSqlSafe(sql)).fetch(&mut *pool_conn);
2009            // 流式查询无法预解析第一行列类型(流首行未知),使用每行 columns() 长度预分配 HashMap
2010            // 仍然使用 row_to_value_pg(按列类型字符串 match)以保证正确性
2011            while let Some(row_result) = row_stream.next().await {
2012                let row = row_result.map_err(map_sqlx_error)?;
2013                let cols = row.columns();
2014                let mut record = HashMap::with_capacity(cols.len());
2015                for (i, col) in cols.iter().enumerate() {
2016                    let name = col.name().to_string();
2017                    // 流式场景下每行都解析 ColType 反而增加开销,直接使用 row_to_value_pg
2018                    let value = row_to_value_pg(&row, i);
2019                    record.insert(name, value);
2020                }
2021                yield record;
2022            }
2023            // 显式 drop row_stream 以释放对 pool_conn 的借用
2024            // PostgreSQL 的 fetch 流是 boxed trait object,析构函数可能持有借用
2025            drop(row_stream);
2026            self.conn = Some(pool_conn);
2027        })
2028    }
2029}
2030
2031impl Drop for SqlxPgConnection {
2032    fn drop(&mut self) {
2033        if let Some(conn) = self.conn.take() {
2034            drop(conn);
2035        }
2036    }
2037}
2038
2039// ===================== PostgreSQL 扩展功能 =====================
2040
2041/// PostgreSQL 扩展功能 trait
2042///
2043/// 提供 PostgreSQL 特有的 LISTEN/NOTIFY 通道通信与 COPY FROM STDIN 批量导入功能。
2044/// 这些功能在其他数据库(MySQL/SQLite)中没有对应实现,因此单独定义为扩展 trait,
2045/// 仅由 [`SqlxPgConnection`] 实现。
2046///
2047/// # LISTEN/NOTIFY
2048///
2049/// PostgreSQL 的轻量级进程间通信机制:
2050/// - `LISTEN channel`:订阅通道
2051/// - `NOTIFY channel, payload`:向通道发送通知
2052///
2053/// 接收通知需配合 `PgListener` 或轮询 `pg_notification` 系列函数。
2054///
2055/// # COPY FROM STDIN
2056///
2057/// PostgreSQL 的高性能批量导入协议,比逐行 INSERT 快 10~100 倍。
2058/// 数据通过专用协议流式传输,绕过 SQL 解析器。
2059#[async_trait]
2060pub trait PgExtensions: Send + Sync {
2061    /// LISTEN 通道:订阅指定通道的通知
2062    ///
2063    /// # 参数
2064    ///
2065    /// - `channel`: 通道名(仅允许字母、数字、下划线,防止 SQL 注入)
2066    ///
2067    /// # 错误
2068    ///
2069    /// - 通道名包含非法字符时返回 `DbError::Internal`
2070    /// - 连接已关闭时返回 `DbError::Internal`
2071    async fn listen(&mut self, channel: &str) -> Result<(), DbError>;
2072
2073    /// NOTIFY 通道:向指定通道发送通知
2074    ///
2075    /// # 参数
2076    ///
2077    /// - `channel`: 通道名(仅允许字母、数字、下划线)
2078    /// - `payload`: 通知载荷字符串(单引号自动转义)
2079    ///
2080    /// # 错误
2081    ///
2082    /// - 通道名包含非法字符时返回 `DbError::Internal`
2083    async fn notify(&mut self, channel: &str, payload: &str) -> Result<(), DbError>;
2084
2085    /// COPY FROM STDIN:批量导入数据
2086    ///
2087    /// # 参数
2088    ///
2089    /// - `sql`: COPY 语句,如 `COPY mytable (col1, col2) FROM STDIN WITH (FORMAT csv, HEADER true)`
2090    /// - `data`: 完整的导入数据字节流
2091    ///
2092    /// # 返回
2093    ///
2094    /// 返回受影响的行数。
2095    ///
2096    /// # 性能
2097    ///
2098    /// 比逐行 INSERT 快 10~100 倍,适用于大批量数据导入(10 万行以上)。
2099    async fn copy_from_stdin(&mut self, sql: &str, data: &[u8]) -> Result<u64, DbError>;
2100}
2101
2102/// 校验 PostgreSQL 通道名合法性(仅允许字母、数字、下划线)
2103///
2104/// LISTEN/NOTIFY 的通道名不能通过参数化绑定,必须拼接 SQL,
2105/// 因此需严格校验防止 SQL 注入。
2106fn validate_pg_channel_name(channel: &str) -> Result<(), DbError> {
2107    if channel.is_empty() {
2108        return Err(DbError::Internal(
2109            "PG channel name must not be empty".to_string(),
2110        ));
2111    }
2112    if !channel
2113        .chars()
2114        .all(|c| c.is_ascii_alphanumeric() || c == '_')
2115    {
2116        return Err(DbError::Internal(format!(
2117            "invalid PG channel name: {} (only alphanumeric and underscore allowed)",
2118            channel
2119        )));
2120    }
2121    Ok(())
2122}
2123
2124#[async_trait]
2125impl PgExtensions for SqlxPgConnection {
2126    async fn listen(&mut self, channel: &str) -> Result<(), DbError> {
2127        validate_pg_channel_name(channel)?;
2128        // LISTEN 是简单 SQL 命令,channel 已校验,可安全拼接
2129        self.execute(&format!("LISTEN {}", channel)).await?;
2130        Ok(())
2131    }
2132
2133    async fn notify(&mut self, channel: &str, payload: &str) -> Result<(), DbError> {
2134        validate_pg_channel_name(channel)?;
2135        // payload 通过 PostgreSQL 字符串字面量转义(单引号加倍)防止注入
2136        let escaped_payload = payload.replace('\'', "''");
2137        self.execute(&format!("NOTIFY {}, '{}'", channel, escaped_payload))
2138            .await?;
2139        Ok(())
2140    }
2141
2142    async fn copy_from_stdin(&mut self, sql: &str, data: &[u8]) -> Result<u64, DbError> {
2143        let mut pool_conn = self
2144            .conn
2145            .take()
2146            .ok_or_else(|| DbError::Internal("connection already closed".to_string()))?;
2147        // sqlx 0.9: 通过 DerefMut 访问 PgConnection 的 copy_in_raw 方法
2148        // 启动 COPY 协议,返回 PgCopyIn 流式写入句柄
2149        // 注意:直接使用 *pool_conn 解引用,避免 clippy::needless_borrow 警告
2150        let mut copy = (*pool_conn)
2151            .copy_in_raw(sql)
2152            .await
2153            .map_err(map_sqlx_error)?;
2154        // 一次性发送全部数据(非流式,适用于数据可完整载入内存的场景)
2155        copy.send(data).await.map_err(map_sqlx_error)?;
2156        // 完成 COPY 操作,sqlx 0.9 的 PgCopyIn::finish() 直接返回 u64(受影响行数)
2157        let result = copy.finish().await.map_err(map_sqlx_error)?;
2158        self.conn = Some(pool_conn);
2159        Ok(result)
2160    }
2161}
2162
2163/// PostgreSQL 批量导入(多行 INSERT,作为 COPY 协议的简化替代)
2164///
2165/// 构建多行 INSERT 语句:`INSERT INTO t (c1, c2) VALUES ($1, $2), ($3, $4), ...`
2166/// 使用参数绑定避免 SQL 注入。PostgreSQL 占位符 `$N` 跨行递增。
2167///
2168/// 当数据量极大(>10 万行)时,建议分批调用或使用
2169/// [`PgExtensions::copy_from_stdin`] 走 COPY 协议以获得更高吞吐。
2170pub async fn pg_bulk_insert(
2171    conn: &mut SqlxPgConnection,
2172    table: &str,
2173    columns: &[&str],
2174    rows: &[Vec<Value>],
2175) -> Result<u64, DbError> {
2176    if rows.is_empty() {
2177        return Ok(0);
2178    }
2179    let col_list = columns.join(", ");
2180    let cols_per_row = columns.len();
2181    // PostgreSQL 占位符 $1, $2, ... 跨行递增
2182    let placeholders: Vec<String> = rows
2183        .iter()
2184        .enumerate()
2185        .map(|(row_idx, _)| {
2186            let base = row_idx * cols_per_row;
2187            let ph: Vec<String> = (0..cols_per_row)
2188                .map(|i| format!("${}", base + i + 1))
2189                .collect();
2190            format!("({})", ph.join(", "))
2191        })
2192        .collect();
2193    let sql = format!(
2194        "INSERT INTO {} ({}) VALUES {}",
2195        table,
2196        col_list,
2197        placeholders.join(", ")
2198    );
2199    // 展平所有行的值为单一参数数组
2200    let mut params: Vec<Value> = Vec::with_capacity(rows.len() * cols_per_row);
2201    for row in rows {
2202        for v in row {
2203            params.push(v.clone());
2204        }
2205    }
2206    conn.execute_with_params(&sql, &params).await
2207}
2208#[cfg(test)]
2209mod tests {
2210    use super::*;
2211
2212    #[test]
2213    fn test_needs_raw_sql_begin() {
2214        assert!(needs_raw_sql("BEGIN"));
2215        assert!(needs_raw_sql("begin"));
2216        assert!(needs_raw_sql("  BEGIN"));
2217        assert!(needs_raw_sql("\tbegin"));
2218    }
2219
2220    #[test]
2221    fn test_needs_raw_sql_commit() {
2222        assert!(needs_raw_sql("COMMIT"));
2223        assert!(needs_raw_sql("commit"));
2224        assert!(needs_raw_sql(" COMMIT WORK"));
2225    }
2226
2227    #[test]
2228    fn test_needs_raw_sql_rollback() {
2229        assert!(needs_raw_sql("ROLLBACK"));
2230        assert!(needs_raw_sql("rollback"));
2231        assert!(needs_raw_sql(" ROLLBACK TO SAVEPOINT sp1"));
2232    }
2233
2234    #[test]
2235    fn test_needs_raw_sql_savepoint() {
2236        assert!(needs_raw_sql("SAVEPOINT sp1"));
2237        assert!(needs_raw_sql("savepoint s1"));
2238    }
2239
2240    #[test]
2241    fn test_needs_raw_sql_release() {
2242        assert!(needs_raw_sql("RELEASE sp1"));
2243        assert!(needs_raw_sql("release savepoint s1"));
2244    }
2245
2246    #[test]
2247    fn test_needs_raw_sql_set() {
2248        assert!(needs_raw_sql("SET autocommit = 0"));
2249        assert!(needs_raw_sql("set names utf8"));
2250    }
2251
2252    #[test]
2253    fn test_needs_raw_sql_use() {
2254        assert!(needs_raw_sql("USE mydb"));
2255        assert!(needs_raw_sql("use mydb"));
2256    }
2257
2258    #[test]
2259    fn test_needs_raw_sql_start_transaction() {
2260        assert!(needs_raw_sql("START TRANSACTION"));
2261        assert!(needs_raw_sql("start transaction"));
2262        assert!(needs_raw_sql("  START TRANSACTION READ ONLY"));
2263    }
2264
2265    #[test]
2266    fn test_needs_raw_sql_normal_queries() {
2267        assert!(!needs_raw_sql("SELECT * FROM users"));
2268        assert!(!needs_raw_sql("INSERT INTO t VALUES (1)"));
2269        assert!(!needs_raw_sql("UPDATE t SET x = 1"));
2270        assert!(!needs_raw_sql("DELETE FROM t"));
2271        assert!(!needs_raw_sql("CREATE TABLE t (id INT)"));
2272        assert!(!needs_raw_sql("DROP TABLE t"));
2273        assert!(!needs_raw_sql("ALTER TABLE t ADD COLUMN x INT"));
2274    }
2275
2276    #[test]
2277    fn test_needs_raw_sql_edge_cases() {
2278        assert!(!needs_raw_sql(""));
2279        assert!(!needs_raw_sql("   "));
2280        assert!(!needs_raw_sql("SELECT"));
2281        assert!(needs_raw_sql("BEGINNING"));
2282        assert!(needs_raw_sql("COMMITTEE"));
2283        assert!(!needs_raw_sql("INSERT"));
2284        assert!(!needs_raw_sql("DELETE"));
2285    }
2286}