Skip to main content

sz_orm_core/
value.rs

1//! Value 类型定义
2//!
3//! 数据库操作的统一值表示
4
5use std::borrow::Cow;
6use std::fmt;
7
8use serde::{Deserialize, Serialize};
9
10/// 数据库值类型
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
12#[non_exhaustive]
13pub enum Value {
14    /// Null 值
15    #[default]
16    Null,
17
18    /// 布尔值
19    Bool(bool),
20
21    /// 8 位有符号整数
22    I8(i8),
23
24    /// 16 位有符号整数
25    I16(i16),
26
27    /// 32 位有符号整数
28    I32(i32),
29
30    /// 64 位有符号整数
31    I64(i64),
32
33    /// 8 位无符号整数
34    U8(u8),
35
36    /// 16 位无符号整数
37    U16(u16),
38
39    /// 32 位无符号整数
40    U32(u32),
41
42    /// 64 位无符号整数
43    U64(u64),
44
45    /// 32 位浮点数
46    F32(f32),
47
48    /// 64 位浮点数
49    F64(f64),
50
51    /// 高精度十进制数(NUMERIC/DECIMAL),以字符串形式存储避免 f64 精度丢失
52    Decimal(String),
53
54    /// 字符串值
55    String(String),
56
57    /// 字节值
58    Bytes(Vec<u8>),
59
60    /// UUID 值(以字符串形式存储)
61    Uuid(String),
62
63    /// 日期值(ISO 8601 格式)
64    Date(String),
65
66    /// 日期时间值(ISO 8601 格式)
67    DateTime(String),
68
69    /// 时间值
70    Time(String),
71
72    /// JSON 值
73    Json(String),
74
75    /// 值数组
76    Array(Vec<Value>),
77
78    /// 基于 HashMap 的对象值,用于存储关系数据
79    Object(std::collections::HashMap<String, Value>),
80}
81
82impl Value {
83    /// 判断是否为 null
84    pub fn is_null(&self) -> bool {
85        matches!(self, Value::Null)
86    }
87
88    /// 判断是否为布尔值
89    pub fn is_bool(&self) -> bool {
90        matches!(self, Value::Bool(_))
91    }
92
93    /// 判断是否为整数
94    pub fn is_i64(&self) -> bool {
95        matches!(self, Value::I64(_))
96    }
97
98    /// 判断是否为浮点数
99    pub fn is_f64(&self) -> bool {
100        matches!(self, Value::F64(_))
101    }
102
103    /// 判断是否为字符串
104    pub fn is_string(&self) -> bool {
105        matches!(self, Value::String(_))
106    }
107
108    /// 判断是否为字节
109    pub fn is_bytes(&self) -> bool {
110        matches!(self, Value::Bytes(_))
111    }
112
113    /// 判断是否为对象
114    pub fn is_object(&self) -> bool {
115        matches!(self, Value::Object(_))
116    }
117
118    /// 从 HashMap 构造 Value
119    pub fn from_map(map: std::collections::HashMap<String, Value>) -> Self {
120        Value::Object(map)
121    }
122
123    /// 若可能,返回 &str 形式的值
124    pub fn as_str(&self) -> Option<&str> {
125        match self {
126            Value::String(s) => Some(s),
127            Value::Decimal(s) => Some(s),
128            _ => None,
129        }
130    }
131
132    /// 若可能,返回 i64 形式的值
133    /// 支持 F32/F64 → i64 的有损转换(数据库 SUM/AVG 等聚合函数常返回浮点类型)
134    /// U64 → i64 使用 `try_from`,超过 `i64::MAX` 时返回 `None`(避免静默截断为负数)
135    pub fn as_i64(&self) -> Option<i64> {
136        match self {
137            Value::I8(v) => Some(*v as i64),
138            Value::I16(v) => Some(*v as i64),
139            Value::I32(v) => Some(*v as i64),
140            Value::I64(v) => Some(*v),
141            Value::U8(v) => Some(*v as i64),
142            Value::U16(v) => Some(*v as i64),
143            Value::U32(v) => Some(*v as i64),
144            Value::U64(v) => i64::try_from(*v).ok(),
145            Value::F32(v) => Some(*v as i64),
146            Value::F64(v) => Some(*v as i64),
147            Value::Bool(v) => Some(if *v { 1 } else { 0 }),
148            Value::String(s) => s.parse::<i64>().ok(),
149            Value::Decimal(s) => s.parse::<i64>().ok(),
150            _ => None,
151        }
152    }
153
154    /// 若可能,返回 f64 形式的值
155    /// 支持整数类型 → f64 的转换
156    pub fn as_f64(&self) -> Option<f64> {
157        match self {
158            Value::F32(v) => Some(*v as f64),
159            Value::F64(v) => Some(*v),
160            Value::I8(v) => Some(*v as f64),
161            Value::I16(v) => Some(*v as f64),
162            Value::I32(v) => Some(*v as f64),
163            Value::I64(v) => Some(*v as f64),
164            Value::U8(v) => Some(*v as f64),
165            Value::U16(v) => Some(*v as f64),
166            Value::U32(v) => Some(*v as f64),
167            Value::U64(v) => Some(*v as f64),
168            Value::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
169            Value::Decimal(s) => s.parse::<f64>().ok(),
170            _ => None,
171        }
172    }
173
174    /// 若可能,返回 bool 形式的值
175    /// 支持整数(非 0 即真)、浮点(非 0.0 即真)、字符串("1"/"true"/"yes"/"on" 为真)的转换
176    pub fn as_bool(&self) -> Option<bool> {
177        match self {
178            Value::Bool(v) => Some(*v),
179            Value::I8(v) => Some(*v != 0),
180            Value::I16(v) => Some(*v != 0),
181            Value::I32(v) => Some(*v != 0),
182            Value::I64(v) => Some(*v != 0),
183            Value::U8(v) => Some(*v != 0),
184            Value::U16(v) => Some(*v != 0),
185            Value::U32(v) => Some(*v != 0),
186            Value::U64(v) => Some(*v != 0),
187            Value::F32(v) => Some(*v != 0.0),
188            Value::F64(v) => Some(*v != 0.0),
189            Value::String(s) => match s.to_lowercase().as_str() {
190                "1" | "true" | "yes" | "on" => Some(true),
191                "0" | "false" | "no" | "off" => Some(false),
192                _ => None,
193            },
194            Value::Null => Some(false),
195            _ => None,
196        }
197    }
198
199    /// 若可能,返回字节切片形式(&[u8])的值
200    /// 字符串类型会返回其 UTF-8 字节
201    pub fn as_bytes(&self) -> Option<&[u8]> {
202        match self {
203            Value::Bytes(v) => Some(v),
204            Value::String(s) => Some(s.as_bytes()),
205            _ => None,
206        }
207    }
208
209    /// 转换为 SQL 参数字符串(用于直接拼接 SQL 语句)
210    /// 字符串类型会进行转义并加引号;字节类型转换为 X'..' 形式
211    ///
212    /// # 安全性警告
213    ///
214    /// 本方法使用简单的 `'` → `''` 转义,对 PostgreSQL/SQLite 默认配置安全,
215    /// 但对 MySQL 默认配置(backslash 是转义字符)不安全:含 `\` 的字符串
216    /// 可能被 MySQL 误解。**生产环境请使用 [`Value::to_param_with_dialect`]**
217    /// 以获得方言感知的转义。
218    pub fn to_param(&self) -> Cow<'_, str> {
219        match self {
220            Value::Null => Cow::Borrowed("NULL"),
221            Value::Bool(b) => Cow::Owned(if *b { "TRUE" } else { "FALSE" }.to_string()),
222            Value::I8(v) => Cow::Owned(v.to_string()),
223            Value::I16(v) => Cow::Owned(v.to_string()),
224            Value::I32(v) => Cow::Owned(v.to_string()),
225            Value::I64(v) => Cow::Owned(v.to_string()),
226            Value::U8(v) => Cow::Owned(v.to_string()),
227            Value::U16(v) => Cow::Owned(v.to_string()),
228            Value::U32(v) => Cow::Owned(v.to_string()),
229            Value::U64(v) => Cow::Owned(v.to_string()),
230            Value::F32(v) => Cow::Owned(v.to_string()),
231            Value::F64(v) => Cow::Owned(v.to_string()),
232            Value::Decimal(s) => Cow::Owned(s.clone()),
233            Value::String(s) => Cow::Owned(format!("'{}'", escape_string(s))),
234            Value::Bytes(b) => Cow::Owned(format!("X'{}'", hex_encode(b))),
235            Value::Uuid(s) => Cow::Owned(format!("'{}'", escape_string(s))),
236            Value::Date(s) => Cow::Owned(format!("'{}'", escape_string(s))),
237            Value::DateTime(s) => Cow::Owned(format!("'{}'", escape_string(s))),
238            Value::Time(s) => Cow::Owned(format!("'{}'", escape_string(s))),
239            Value::Json(s) => Cow::Owned(format!("'{}'", escape_string(s))),
240            Value::Array(arr) => {
241                let params: Vec<String> = arr.iter().map(|v| v.to_param().into_owned()).collect();
242                Cow::Owned(format!("({})", params.join(", ")))
243            }
244            Value::Object(_) => Cow::Borrowed("NULL"),
245        }
246    }
247
248    /// v0.2.2 修复 H-1:方言感知的 SQL 参数转换
249    ///
250    /// 与 [`to_param`](Self::to_param) 的区别:字符串类型使用 `dialect.escape_string()`
251    /// 而非简单的 `'` → `''` 转义,确保在所有方言下都安全:
252    ///
253    /// - **MySQL**:转义 `\`、`'`、`\0`、`\n`、`\r`、`\t`、`\x1a`
254    /// - **PostgreSQL**:仅转义 `'`(依赖 `standard_conforming_strings=on` 默认配置)
255    /// - **SQLite**:仅转义 `'`
256    ///
257    /// # 推荐用法
258    ///
259    /// ```ignore
260    /// use sz_orm_core::{DbType, get_dialect};
261    /// let dialect = get_dialect(DbType::MySQL)?;
262    /// let v = Value::String("hello\\nworld".to_string());
263    /// let param = v.to_param_with_dialect(&**dialect);
264    /// ```
265    pub fn to_param_with_dialect(&self, dialect: &dyn crate::dialect::Dialect) -> Cow<'_, str> {
266        match self {
267            Value::Null => Cow::Borrowed("NULL"),
268            Value::Bool(b) => Cow::Owned(if *b { "TRUE" } else { "FALSE" }.to_string()),
269            Value::I8(v) => Cow::Owned(v.to_string()),
270            Value::I16(v) => Cow::Owned(v.to_string()),
271            Value::I32(v) => Cow::Owned(v.to_string()),
272            Value::I64(v) => Cow::Owned(v.to_string()),
273            Value::U8(v) => Cow::Owned(v.to_string()),
274            Value::U16(v) => Cow::Owned(v.to_string()),
275            Value::U32(v) => Cow::Owned(v.to_string()),
276            Value::U64(v) => Cow::Owned(v.to_string()),
277            Value::F32(v) => Cow::Owned(v.to_string()),
278            Value::F64(v) => Cow::Owned(v.to_string()),
279            Value::Decimal(s) => Cow::Owned(s.clone()),
280            Value::String(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
281            Value::Bytes(b) => Cow::Owned(format!("X'{}'", hex_encode(b))),
282            Value::Uuid(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
283            Value::Date(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
284            Value::DateTime(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
285            Value::Time(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
286            Value::Json(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
287            Value::Array(arr) => {
288                let params: Vec<String> = arr
289                    .iter()
290                    .map(|v| v.to_param_with_dialect(dialect).into_owned())
291                    .collect();
292                Cow::Owned(format!("({})", params.join(", ")))
293            }
294            Value::Object(_) => Cow::Borrowed("NULL"),
295        }
296    }
297
298    /// 从任何实现了 `Into<Value>` 的类型构造 Value
299    pub fn from<T: Into<Value>>(v: T) -> Self {
300        v.into()
301    }
302}
303
304impl fmt::Display for Value {
305    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306        match self {
307            Value::Null => write!(f, "NULL"),
308            Value::Bool(b) => write!(f, "{}", b),
309            Value::I8(v) => write!(f, "{}", v),
310            Value::I16(v) => write!(f, "{}", v),
311            Value::I32(v) => write!(f, "{}", v),
312            Value::I64(v) => write!(f, "{}", v),
313            Value::U8(v) => write!(f, "{}", v),
314            Value::U16(v) => write!(f, "{}", v),
315            Value::U32(v) => write!(f, "{}", v),
316            Value::U64(v) => write!(f, "{}", v),
317            Value::F32(v) => write!(f, "{}", v),
318            Value::F64(v) => write!(f, "{}", v),
319            Value::Decimal(v) => write!(f, "{}", v),
320            Value::String(v) => write!(f, "'{}'", v),
321            Value::Bytes(v) => write!(f, "X'{}'", hex_encode(v)),
322            Value::Uuid(v) => write!(f, "'{}'", v),
323            Value::Date(v) => write!(f, "'{}'", v),
324            Value::DateTime(v) => write!(f, "'{}'", v),
325            Value::Time(v) => write!(f, "'{}'", v),
326            Value::Json(v) => write!(f, "'{}'", v),
327            Value::Array(v) => {
328                let items: Vec<String> = v.iter().map(|i| format!("{}", i)).collect();
329                write!(f, "({})", items.join(", "))
330            }
331            Value::Object(map) => {
332                let items: Vec<String> = map.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
333                write!(f, "{{{}}}", items.join(", "))
334            }
335        }
336    }
337}
338
339impl From<()> for Value {
340    fn from(_: ()) -> Self {
341        Value::Null
342    }
343}
344
345impl From<bool> for Value {
346    fn from(v: bool) -> Self {
347        Value::Bool(v)
348    }
349}
350
351impl From<i8> for Value {
352    fn from(v: i8) -> Self {
353        Value::I8(v)
354    }
355}
356
357impl From<i16> for Value {
358    fn from(v: i16) -> Self {
359        Value::I16(v)
360    }
361}
362
363impl From<i32> for Value {
364    fn from(v: i32) -> Self {
365        Value::I32(v)
366    }
367}
368
369impl From<i64> for Value {
370    fn from(v: i64) -> Self {
371        Value::I64(v)
372    }
373}
374
375impl From<u8> for Value {
376    fn from(v: u8) -> Self {
377        Value::U8(v)
378    }
379}
380
381impl From<u16> for Value {
382    fn from(v: u16) -> Self {
383        Value::U16(v)
384    }
385}
386
387impl From<u32> for Value {
388    fn from(v: u32) -> Self {
389        Value::U32(v)
390    }
391}
392
393impl From<u64> for Value {
394    fn from(v: u64) -> Self {
395        Value::U64(v)
396    }
397}
398
399impl From<f32> for Value {
400    fn from(v: f32) -> Self {
401        Value::F32(v)
402    }
403}
404
405impl From<f64> for Value {
406    fn from(v: f64) -> Self {
407        Value::F64(v)
408    }
409}
410
411impl From<String> for Value {
412    fn from(v: String) -> Self {
413        Value::String(v)
414    }
415}
416
417impl From<&str> for Value {
418    fn from(v: &str) -> Self {
419        Value::String(v.to_string())
420    }
421}
422
423impl From<Vec<u8>> for Value {
424    fn from(v: Vec<u8>) -> Self {
425        Value::Bytes(v)
426    }
427}
428
429impl From<&[u8]> for Value {
430    fn from(v: &[u8]) -> Self {
431        Value::Bytes(v.to_vec())
432    }
433}
434
435impl From<Vec<Value>> for Value {
436    fn from(v: Vec<Value>) -> Self {
437        Value::Array(v)
438    }
439}
440
441/// 字符串字面量转义(v0.2.1 修复 Critical D-1)
442///
443/// # 旧实现的问题
444///
445/// 旧实现同时使用 `'` → `''`(标准 SQL)和 `\` → `\\`(MySQL 风格)转义,
446/// 导致在 PostgreSQL/SQLite 等不把 `\` 作为转义字符的方言下数据完整性受损
447/// (写入 `\\n` 字面量而非 `\n`)。
448///
449/// # 新实现
450///
451/// 只使用标准 SQL 转义:`'` → `''`。
452///
453/// - **SQL 注入防御**:`'` 被转义为 `''`,攻击者无法突破字符串字面量
454/// - **数据完整性**:在所有方言(MySQL/PG/SQLite/Oracle)下数据保持原样
455/// - **MySQL 兼容性**:MySQL 默认把 `\` 作为转义字符,但我们不主动转义 `\`,
456///   所以写入的 `\` 会被 MySQL 解析为字面 `\`(与 PG/SQLite 一致)
457///
458/// # 注意
459///
460/// 对于需要方言感知转义的场景(如 MySQL 的 `NO_BACKSLASH_ESCAPES` 模式),
461/// 应使用 `Dialect::escape_string()` 方法。
462fn escape_string(s: &str) -> String {
463    let mut escaped = String::with_capacity(s.len() + s.chars().filter(|&c| c == '\'').count());
464    for c in s.chars() {
465        if c == '\'' {
466            escaped.push_str("''");
467        } else {
468            escaped.push(c);
469        }
470    }
471    escaped
472}
473
474fn hex_encode(bytes: &[u8]) -> String {
475    bytes.iter().map(|b| format!("{:02x}", b)).collect()
476}
477
478/// 列类型枚举(v1.1.0 新增)
479///
480/// 用于 `row_to_value_*` 函数的预解析列类型分派,避免每行每列做字符串 `match`。
481/// 适配器在第一行解析列类型为 `Vec<ColType>`,后续行复用枚举分派(编译器优化为跳转表)。
482///
483/// # 性能优势
484///
485/// - 字符串 `match type_name` 无法被 LLVM 优化为跳转表(`&str` 比较)
486/// - 枚举 `match col_type` 编译为跳转表,O(1) 且缓存友好
487/// - 在 SELECT ALL 大结果集场景下,每行每列节省 1 次字符串比较
488#[derive(Debug, Clone, Copy, PartialEq, Eq)]
489#[non_exhaustive]
490pub enum ColType {
491    /// 布尔类型(SQLite BOOLEAN / MySQL BOOLEAN/TINYINT(1) / PG BOOL / Oracle Boolean)
492    Bool,
493    /// 8 位有符号整数(MySQL TINYINT)
494    I8,
495    /// 16 位有符号整数(MySQL SMALLINT / PG INT2)
496    I16,
497    /// 32 位有符号整数(MySQL INT/MEDIUMINT / PG INT4)
498    I32,
499    /// 64 位有符号整数(MySQL BIGINT / PG INT8 / SQLite INTEGER / Oracle NUMBER)
500    I64,
501    /// 8 位无符号整数(MySQL TINYINT UNSIGNED)
502    U8,
503    /// 16 位无符号整数(MySQL SMALLINT UNSIGNED)
504    U16,
505    /// 32 位无符号整数(MySQL INT UNSIGNED/MEDIUMINT UNSIGNED)
506    U32,
507    /// 64 位无符号整数(MySQL BIGINT UNSIGNED)
508    U64,
509    /// 32 位浮点数(MySQL FLOAT / PG FLOAT4 / SQLite REAL)
510    F32,
511    /// 64 位浮点数(MySQL DOUBLE / PG FLOAT8 / Oracle BinaryDouble)
512    F64,
513    /// 高精度十进制数(MySQL DECIMAL/NUMERIC/NEWDECIMAL / PG NUMERIC / Oracle NUMBER(p,s))
514    Decimal,
515    /// 字符串类型(TEXT/VARCHAR/CHAR/CLOB 等)
516    String,
517    /// 字节类型(BLOB/BYTEA/RAW 等)
518    Bytes,
519    /// 日期类型(DATE)
520    Date,
521    /// 日期时间类型(DATETIME/TIMESTAMP)
522    DateTime,
523    /// 时间类型(TIME)
524    Time,
525    /// JSON 类型
526    Json,
527    /// UUID 类型
528    Uuid,
529    /// 未知类型(回退到 i64 → f64 → bool → String 顺序尝试)
530    Unknown,
531}
532
533impl ColType {
534    /// 从数据库类型名解析为 ColType(通用回退实现)
535    ///
536    /// 各适配器应优先使用自己专门的 `parse_col_type_<db>` 函数(覆盖数据库特有类型名),
537    /// 此函数作为通用回退,覆盖最常见的标准 SQL 类型名。
538    ///
539    /// # 注意
540    ///
541    /// "INTEGER" 在通用映射中被归为 I32(与 MySQL INT/PG INT4 一致)。
542    /// **SQLite 适配器必须使用 [`ColType::parse_sqlite`]**:SQLite 的 INTEGER
543    /// 类型采用动态存储,可容纳 64 位整数(sqlx 默认按 i64 解码),若按 I32
544    /// 解码会在数值超过 i32::MAX 时截断。
545    pub fn from_type_name(type_name: &str) -> Self {
546        match type_name {
547            "BOOLEAN" | "BOOL" => Self::Bool,
548            "TINYINT" => Self::I8,
549            "SMALLINT" | "INT2" => Self::I16,
550            "INT" | "INT4" | "OID" | "MEDIUMINT" | "INTEGER" => Self::I32,
551            "BIGINT" | "INT8" => Self::I64,
552            "TINYINT UNSIGNED" => Self::U8,
553            "SMALLINT UNSIGNED" => Self::U16,
554            "INT UNSIGNED" | "MEDIUMINT UNSIGNED" => Self::U32,
555            "BIGINT UNSIGNED" => Self::U64,
556            "FLOAT" | "FLOAT4" | "REAL" => Self::F32,
557            "DOUBLE" | "FLOAT8" => Self::F64,
558            "DECIMAL" | "NUMERIC" | "NEWDECIMAL" | "MONEY" => Self::Decimal,
559            "TEXT" | "VARCHAR" | "CHAR" | "NAME" => Self::String,
560            "BLOB" | "BYTEA" => Self::Bytes,
561            "DATE" => Self::Date,
562            "DATETIME" | "TIMESTAMP" => Self::DateTime,
563            "TIME" => Self::Time,
564            "JSON" => Self::Json,
565            "UUID" => Self::Uuid,
566            _ => Self::Unknown,
567        }
568    }
569
570    /// SQLite 专用列类型解析
571    ///
572    /// SQLite 使用动态类型系统(type affinity),同一列可存储 INT/REAL/TEXT/BLOB 任意类型。
573    /// sqlx 报告的类型名遵循 SQLite 的"声明类型"(declared type)规则:
574    ///
575    /// - **INTEGER**:实际可容纳 8 字节整数(最大 2^63-1),sqlx 默认按 `i64` 解码。
576    ///   若按 I32 解码,数值超过 `i32::MAX` 会静默截断。
577    /// - **INT/INTEGER/BIGINT** 等:在 SQLite 中都按 INTEGER 亲和性处理,应统一映射为 I64。
578    /// - **REAL/FLOAT/DOUBLE**:映射为 F64(SQLite REAL 是 8 字节 IEEE 754)。
579    /// - **TEXT/CLOB**:映射为 String。
580    /// - **BLOB**:映射为 Bytes。
581    /// - **NUMERIC/DECIMAL**:保留为 Decimal(按字符串解码避免精度丢失)。
582    /// - **BOOLEAN**:SQLite 无原生 BOOLEAN,存为 INTEGER 0/1,但声明 BOOLEAN 时按 Bool 解码。
583    /// - **DATETIME/TIMESTAMP/DATE/TIME**:SQLite 通常以 TEXT 存储,按 String 解码。
584    /// - **JSON**:SQLite 4.x 后有 JSON 类型,按 String 解码(保留原始 JSON 文本)。
585    pub fn parse_sqlite(type_name: &str) -> Self {
586        // SQLite type_info 可能返回空字符串(NULL 或表达式结果),按 Unknown 处理
587        if type_name.is_empty() {
588            return Self::Unknown;
589        }
590        match type_name.to_uppercase().as_str() {
591            // SQLite INTEGER 亲和性:实际为 64 位有符号整数
592            "INTEGER" | "INT" | "BIGINT" | "INT8" | "INT4" | "INT2" | "TINYINT" | "SMALLINT"
593            | "MEDIUMINT" => Self::I64,
594            "BOOLEAN" | "BOOL" => Self::Bool,
595            "REAL" | "FLOAT" | "DOUBLE" | "FLOAT8" | "DOUBLE PRECISION" => Self::F64,
596            "DECIMAL" | "NUMERIC" => Self::Decimal,
597            "TEXT" | "CLOB" | "VARCHAR" | "CHAR" | "NAME" => Self::String,
598            "BLOB" => Self::Bytes,
599            "DATE" => Self::Date,
600            "DATETIME" | "TIMESTAMP" => Self::DateTime,
601            "TIME" => Self::Time,
602            "JSON" => Self::Json,
603            _ => Self::Unknown,
604        }
605    }
606
607    /// MySQL 专用列类型解析
608    ///
609    /// MySQL 类型名来自 `Column::type_info().name()`,遵循 MySQL 协议报告的类型名。
610    pub fn parse_mysql(type_name: &str) -> Self {
611        match type_name.to_uppercase().as_str() {
612            "TINYINT" => Self::I8,
613            "SMALLINT" => Self::I16,
614            "INT" | "INTEGER" | "MEDIUMINT" => Self::I32,
615            "BIGINT" => Self::I64,
616            "TINYINT UNSIGNED" => Self::U8,
617            "SMALLINT UNSIGNED" => Self::U16,
618            "INT UNSIGNED" | "MEDIUMINT UNSIGNED" => Self::U32,
619            "BIGINT UNSIGNED" => Self::U64,
620            "FLOAT" => Self::F32,
621            "DOUBLE" => Self::F64,
622            "DECIMAL" | "NUMERIC" | "NEWDECIMAL" => Self::Decimal,
623            "VARCHAR" | "CHAR" | "TEXT" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM"
624            | "SET" => Self::String,
625            "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BINARY" | "VARBINARY" => Self::Bytes,
626            "DATE" => Self::Date,
627            "DATETIME" | "TIMESTAMP" => Self::DateTime,
628            "TIME" => Self::Time,
629            "YEAR" => Self::I16,
630            "JSON" => Self::Json,
631            "BOOLEAN" | "BOOL" => Self::Bool,
632            _ => Self::from_type_name(type_name),
633        }
634    }
635
636    /// PostgreSQL 专用列类型解析
637    ///
638    /// PostgreSQL 类型名来自 `Column::type_info().name()`,使用 PG 内部类型名(如 INT4/INT8/FLOAT8)。
639    pub fn parse_postgres(type_name: &str) -> Self {
640        match type_name.to_uppercase().as_str() {
641            "BOOL" => Self::Bool,
642            "INT2" | "SMALLINT" => Self::I16,
643            "INT4" | "INTEGER" | "INT" => Self::I32,
644            "INT8" | "BIGINT" => Self::I64,
645            "FLOAT4" | "REAL" => Self::F32,
646            "FLOAT8" | "DOUBLE PRECISION" => Self::F64,
647            "NUMERIC" | "DECIMAL" | "MONEY" => Self::Decimal,
648            "TEXT" | "VARCHAR" | "CHAR" | "BPCHAR" | "NAME" | "CITEXT" => Self::String,
649            "BYTEA" => Self::Bytes,
650            "DATE" => Self::Date,
651            "TIMESTAMP" | "TIMESTAMPTZ" => Self::DateTime,
652            "TIME" | "TIMETZ" => Self::Time,
653            "JSON" | "JSONB" => Self::Json,
654            "UUID" => Self::Uuid,
655            "OID" => Self::I32,
656            _ => Self::from_type_name(type_name),
657        }
658    }
659}
660
661/// 位置式查询结果类型
662///
663/// 用于 `Connection::query_values` / `query_values_with_params`,绕过
664/// `HashMap<String, Value>` 行映射的开销,直接返回列名 + 按列顺序的值矩阵。
665///
666/// # 性能优势
667///
668/// - 普通 `query` 返回 `Vec<HashMap<String, Value>>`,每行每列需哈希计算 + 字符串克隆
669/// - `QueryValues` 返回 `(Vec<String>, Vec<Vec<Value>>)`,列名只分配一次,
670///   每行值按列序号直接 `Vec::push`,无哈希计算
671/// - 在 SELECT ALL 大结果集场景下,比 `query` 提升 30%~50%
672///
673/// # 用法
674///
675/// ```rust,ignore
676/// let (names, values_matrix): QueryValues = conn.query_values("SELECT id, name FROM users").await?;
677/// // names = ["id", "name"]
678/// // values_matrix[0] = [Value::I64(1), Value::String("Alice".into())]
679/// ```
680pub type QueryValues = (Vec<String>, Vec<Vec<Value>>);
681
682#[cfg(test)]
683mod tests {
684    use super::*;
685
686    #[test]
687    fn test_value_is_null() {
688        assert!(Value::Null.is_null());
689        assert!(!Value::I64(0).is_null());
690    }
691
692    #[test]
693    fn test_col_type_from_type_name() {
694        // 标准类型
695        assert_eq!(ColType::from_type_name("BOOLEAN"), ColType::Bool);
696        assert_eq!(ColType::from_type_name("TINYINT"), ColType::I8);
697        assert_eq!(ColType::from_type_name("SMALLINT"), ColType::I16);
698        assert_eq!(ColType::from_type_name("INT"), ColType::I32);
699        assert_eq!(ColType::from_type_name("BIGINT"), ColType::I64);
700        assert_eq!(ColType::from_type_name("INT UNSIGNED"), ColType::U32);
701        assert_eq!(ColType::from_type_name("FLOAT"), ColType::F32);
702        assert_eq!(ColType::from_type_name("DOUBLE"), ColType::F64);
703        assert_eq!(ColType::from_type_name("TEXT"), ColType::String);
704        assert_eq!(ColType::from_type_name("BLOB"), ColType::Bytes);
705        assert_eq!(ColType::from_type_name("DATE"), ColType::Date);
706        assert_eq!(ColType::from_type_name("TIMESTAMP"), ColType::DateTime);
707        assert_eq!(ColType::from_type_name("JSON"), ColType::Json);
708        // PG 风格
709        assert_eq!(ColType::from_type_name("INT2"), ColType::I16);
710        assert_eq!(ColType::from_type_name("INT4"), ColType::I32);
711        assert_eq!(ColType::from_type_name("INT8"), ColType::I64);
712        assert_eq!(ColType::from_type_name("FLOAT4"), ColType::F32);
713        assert_eq!(ColType::from_type_name("FLOAT8"), ColType::F64);
714        assert_eq!(ColType::from_type_name("BYTEA"), ColType::Bytes);
715        // 未知类型
716        assert_eq!(ColType::from_type_name("UNKNOWN_TYPE"), ColType::Unknown);
717        assert_eq!(ColType::from_type_name(""), ColType::Unknown);
718    }
719
720    #[test]
721    fn test_value_as_i64() {
722        assert_eq!(Value::I64(42).as_i64(), Some(42));
723        assert_eq!(Value::I32(42).as_i64(), Some(42));
724        assert_eq!(Value::Bool(true).as_i64(), Some(1));
725        assert!(Value::String("test".to_string()).as_i64().is_none());
726    }
727
728    #[test]
729    fn test_value_as_f64() {
730        assert_eq!(Value::F64(2.5).as_f64(), Some(2.5));
731        assert_eq!(Value::I64(42).as_f64(), Some(42.0));
732    }
733
734    #[test]
735    fn test_value_as_str() {
736        assert_eq!(Value::String("hello".to_string()).as_str(), Some("hello"));
737    }
738
739    #[test]
740    fn test_value_to_param() {
741        assert_eq!(Value::Null.to_param(), "NULL");
742        assert_eq!(Value::Bool(true).to_param(), "TRUE");
743        assert_eq!(Value::I64(42).to_param(), "42");
744        assert_eq!(Value::String("test".to_string()).to_param(), "'test'");
745        assert_eq!(Value::String("it's".to_string()).to_param(), "'it''s'");
746    }
747
748    #[test]
749    fn test_value_into() {
750        let v: Value = 42i64.into();
751        assert_eq!(v, Value::I64(42));
752
753        let v: Value = "hello".into();
754        assert_eq!(v, Value::String("hello".to_string()));
755
756        let arr: Vec<Value> = vec![Value::I64(1), Value::I64(2)];
757        let v: Value = arr.into();
758        assert_eq!(v, Value::Array(vec![Value::I64(1), Value::I64(2)]));
759    }
760
761    #[test]
762    fn test_value_display() {
763        assert_eq!(format!("{}", Value::Null), "NULL");
764        assert_eq!(format!("{}", Value::Bool(true)), "true");
765        assert_eq!(format!("{}", Value::I64(42)), "42");
766        assert_eq!(format!("{}", Value::String("test".to_string())), "'test'");
767    }
768}