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    /// v3.4.0 M3-T5:Box<str> 优化变体(perf-box-str feature)
82    ///
83    /// 用于不需要修改字符串的场景,`Box<str>` 为 16 字节(指针 + 长度),
84    /// 比 `String`(24 字节:指针 + 长度 + 容量)节省 8 字节/值。
85    #[cfg(feature = "perf-box-str")]
86    BoxedStr(Box<str>),
87}
88
89// ---------------------------------------------------------------------------
90// FromQueryResult trait
91// ---------------------------------------------------------------------------
92
93/// 从 `Value` 反序列化查询结果行的字段值。
94///
95/// 由 `#[derive(FromQueryResult)]` 自动为结构体生成实现,
96/// 也可手动为自定义类型实现。
97///
98/// # 示例
99///
100/// ```ignore
101/// use sz_orm_core::value::{Value, FromQueryResult};
102///
103/// #[derive(FromQueryResult)]
104/// struct User {
105///     id: i64,
106///     name: String,
107/// }
108///
109/// let row = vec![
110///     ("id".to_string(), Value::from(1i64)),
111///     ("name".to_string(), Value::from("Alice")),
112/// ];
113/// let user = User::from_row(&row).unwrap();
114/// assert_eq!(user.id, 1);
115/// ```
116pub trait FromQueryResult: Sized {
117    /// 从单个 `Value` 提取字段值
118    ///
119    /// 对结构体类型无意义(结构体由多列组成,不能从单个 Value 构造),
120    /// 因此提供默认实现返回错误。仅基础标量类型(i64, String, bool 等)
121    /// 需要真正重写此方法。
122    fn from_value(_value: &Value) -> Result<Self, String> {
123        Err(
124            "from_value not implemented for this type; use from_query_result for structs"
125                .to_string(),
126        )
127    }
128
129    /// 从一行数据(列名→值的 HashMap 表示)构建自身
130    fn from_row(row: &std::collections::HashMap<String, Value>) -> Result<Self, String> {
131        Self::from_query_result(row)
132    }
133
134    /// 从一行数据构建自身(主入口,由 `#[derive(FromQueryResult)]` 自动生成)
135    fn from_query_result(_row: &std::collections::HashMap<String, Value>) -> Result<Self, String> {
136        Err("from_query_result not implemented for this type".to_string())
137    }
138
139    /// 返回该类型期望的列名列表(由 `#[derive(FromQueryResult)]` 自动生成)。
140    ///
141    /// 用于 `query_as!` 宏在 `db-verify` 模式下做编译期列名交叉验证:
142    /// 确保 SQL 的 SELECT 列全部出现在结构体字段中。
143    fn row_desc() -> Vec<&'static str> {
144        Vec::new()
145    }
146
147    /// 返回该类型期望的列名 + SQL 类型列表(由 `#[derive(FromQueryResult)]` 自动生成)。
148    ///
149    /// 用于 `query_as!` 宏在 `db-verify` 模式下做编译期列类型匹配验证:
150    /// 确保 SQL 的 SELECT 列类型与结构体字段类型兼容。
151    ///
152    /// 返回格式:`&[(&'static str, &'static str)]`,例如 `[("id", "bigint"), ("name", "varchar")]`。
153    /// SQL 类型名使用 `INFORMATION_SCHEMA.DATA_TYPE`(MySQL)或 `udt_name`(PostgreSQL)
154    /// 的规范化小写形式。
155    fn column_types() -> &'static [(&'static str, &'static str)] {
156        &[]
157    }
158}
159
160/// 列名枚举抽象(P2-2:由 `#[derive(ColumnEnum)]` 自动实现)。
161///
162/// 从结构体字段自动生成 `<StructName>Column` 枚举,每个变体对应一个数据库列:
163/// ```rust,ignore
164/// #[derive(ColumnEnum)]
165/// struct User { id: i64, name: String }
166///
167/// let col = UserColumn::Id;
168/// assert_eq!(col.as_str(), "id");
169/// ```
170///
171/// 支持 `#[column(name = "...")]` 覆盖列名(与 `#[derive(FromQueryResult)]` 一致)。
172pub trait ColumnTrait {
173    /// 返回当前变体对应的数据库列名。
174    fn as_str(&self) -> &'static str;
175    /// 返回全部列变体(保持结构体字段声明顺序)。
176    fn all() -> Vec<Self>
177    where
178        Self: Sized;
179}
180
181/// 编译期字符串相等比较(const 上下文专用,供 `query_as!` 生成的编译期验证代码使用)。
182///
183/// `str == str` 在 const 上下文中不可用(`PartialEq` 非 const),
184/// 因此提供逐字节比较的 const 实现。
185pub const fn __sz_orm_const_str_eq(a: &str, b: &str) -> bool {
186    if a.len() != b.len() {
187        return false;
188    }
189    let ab = a.as_bytes();
190    let bb = b.as_bytes();
191    let mut i = 0;
192    while i < ab.len() {
193        if ab[i] != bb[i] {
194            return false;
195        }
196        i += 1;
197    }
198    true
199}
200
201/// 编译期 SQL 类型兼容性比较(const 上下文专用,供 `query_as!` 生成的编译期验证代码使用)。
202///
203/// 与 `sz-orm-macros` crate 内 `types_compatible()` 保持同一分类逻辑:
204/// 将类型名映射到逻辑分类(整数/浮点/文本/二进制/时间/JSON/UUID 等),
205/// 分类相同即视为兼容(如 `BIGINT` 与 `INT8`)。
206pub const fn __sz_orm_const_types_compatible(
207    actual_db_type: &str,
208    expected_rust_type: &str,
209) -> bool {
210    // 逐字节大小写不敏感比较(const 上下文不支持 to_uppercase()/slice range 索引)
211    const fn ci_eq(t: &str, pat: &[u8]) -> bool {
212        let tb = t.as_bytes();
213        if tb.len() != pat.len() {
214            return false;
215        }
216        let mut i = 0;
217        while i < tb.len() {
218            let c = tb[i];
219            let u = if c >= b'a' && c <= b'z' { c - 32 } else { c };
220            if u != pat[i] {
221                return false;
222            }
223            i += 1;
224        }
225        true
226    }
227    const fn classify(t: &str) -> u8 {
228        if ci_eq(t, b"BOOLEAN") || ci_eq(t, b"BOOL") {
229            1
230        } else if ci_eq(t, b"TINYINT") {
231            2
232        } else if ci_eq(t, b"SMALLINT") || ci_eq(t, b"INT2") {
233            3
234        } else if ci_eq(t, b"INT")
235            || ci_eq(t, b"INT4")
236            || ci_eq(t, b"OID")
237            || ci_eq(t, b"MEDIUMINT")
238            || ci_eq(t, b"INTEGER")
239        {
240            4
241        } else if ci_eq(t, b"BIGINT") || ci_eq(t, b"INT8") {
242            5
243        } else if ci_eq(t, b"TINYINT UNSIGNED") {
244            6
245        } else if ci_eq(t, b"SMALLINT UNSIGNED") {
246            7
247        } else if ci_eq(t, b"INT UNSIGNED") || ci_eq(t, b"MEDIUMINT UNSIGNED") {
248            8
249        } else if ci_eq(t, b"BIGINT UNSIGNED") {
250            9
251        } else if ci_eq(t, b"FLOAT") || ci_eq(t, b"FLOAT4") || ci_eq(t, b"REAL") {
252            10
253        } else if ci_eq(t, b"DOUBLE") || ci_eq(t, b"FLOAT8") {
254            11
255        } else if ci_eq(t, b"DECIMAL")
256            || ci_eq(t, b"NUMERIC")
257            || ci_eq(t, b"NEWDECIMAL")
258            || ci_eq(t, b"MONEY")
259        {
260            12
261        } else if ci_eq(t, b"TEXT")
262            || ci_eq(t, b"VARCHAR")
263            || ci_eq(t, b"CHAR")
264            || ci_eq(t, b"NAME")
265            || ci_eq(t, b"CLOB")
266            || ci_eq(t, b"STRING")
267        {
268            13
269        } else if ci_eq(t, b"BLOB")
270            || ci_eq(t, b"BYTEA")
271            || ci_eq(t, b"BINARY")
272            || ci_eq(t, b"VARBINARY")
273        {
274            14
275        } else if ci_eq(t, b"DATE") {
276            15
277        } else if ci_eq(t, b"DATETIME") || ci_eq(t, b"TIMESTAMP") || ci_eq(t, b"TIMESTAMPTZ") {
278            16
279        } else if ci_eq(t, b"TIME") || ci_eq(t, b"TIMETZ") {
280            17
281        } else if ci_eq(t, b"JSON") || ci_eq(t, b"JSONB") {
282            18
283        } else if ci_eq(t, b"UUID") {
284            19
285        } else {
286            0
287        }
288    }
289    let a = classify(actual_db_type);
290    let b = classify(expected_rust_type);
291    a == b || a == 0 || b == 0
292}
293
294// 基础类型的 FromQueryResult 实现
295
296macro_rules! impl_from_query_result_int {
297    ($t:ty, $variant:ident) => {
298        impl FromQueryResult for $t {
299            fn from_value(value: &Value) -> Result<Self, String> {
300                match value {
301                    // 精确匹配对应变体;数据库整数变体间不做隐式截断转换,
302                    // 需要转换时由调用方显式处理。
303                    Value::$variant(n) => Ok(*n as $t),
304                    Value::Null => Err("NULL value cannot be converted to integer".to_string()),
305                    other => Err(format!("cannot convert {:?} to {}", other, stringify!($t))),
306                }
307            }
308        }
309    };
310}
311
312impl_from_query_result_int!(i64, I64);
313impl_from_query_result_int!(i32, I32);
314impl_from_query_result_int!(i16, I16);
315impl_from_query_result_int!(i8, I8);
316impl_from_query_result_int!(u64, U64);
317impl_from_query_result_int!(u32, U32);
318impl_from_query_result_int!(u16, U16);
319impl_from_query_result_int!(u8, U8);
320
321macro_rules! impl_from_query_result_float {
322    ($t:ty, $variant:ident) => {
323        impl FromQueryResult for $t {
324            fn from_value(value: &Value) -> Result<Self, String> {
325                match value {
326                    Value::$variant(n) => Ok(*n as $t),
327                    Value::Null => Err("NULL value cannot be converted to float".to_string()),
328                    other => Err(format!("cannot convert {:?} to {}", other, stringify!($t))),
329                }
330            }
331        }
332    };
333}
334
335impl_from_query_result_float!(f64, F64);
336impl_from_query_result_float!(f32, F32);
337
338impl FromQueryResult for bool {
339    fn from_value(value: &Value) -> Result<Self, String> {
340        match value {
341            Value::Bool(b) => Ok(*b),
342            Value::I64(n) => Ok(*n != 0),
343            Value::Null => Err("NULL value cannot be converted to bool".to_string()),
344            other => Err(format!("cannot convert {:?} to bool", other)),
345        }
346    }
347}
348
349impl FromQueryResult for String {
350    fn from_value(value: &Value) -> Result<Self, String> {
351        match value {
352            Value::String(s) => Ok(s.clone()),
353            Value::Decimal(s) => Ok(s.clone()),
354            Value::Uuid(s) => Ok(s.clone()),
355            Value::Date(s) => Ok(s.clone()),
356            Value::DateTime(s) => Ok(s.clone()),
357            Value::Time(s) => Ok(s.clone()),
358            Value::Json(s) => Ok(s.clone()),
359            Value::Null => Err("NULL value cannot be converted to String".to_string()),
360            other => Err(format!("cannot convert {:?} to String", other)),
361        }
362    }
363}
364
365impl<T: FromQueryResult> FromQueryResult for Option<T> {
366    fn from_value(value: &Value) -> Result<Self, String> {
367        match value {
368            Value::Null => Ok(None),
369            other => T::from_value(other).map(Some),
370        }
371    }
372}
373
374// 基础类型实现后,为 Option<T> 补充 from_row 不支持的兜底
375impl FromQueryResult for () {
376    fn from_value(_value: &Value) -> Result<Self, String> {
377        Ok(())
378    }
379}
380
381// ---------------------------------------------------------------------------
382// 快捷函数:从 QueryRows 中提取 Vec<T>
383// ---------------------------------------------------------------------------
384
385/// 将 `QueryRows` 转换为 `Vec<T>`,其中 `T: FromQueryResult`。
386///
387/// # 示例
388///
389/// ```ignore
390/// let users: Vec<User> = rows_to::<User>(rows)?;
391/// ```
392pub fn rows_to<T: FromQueryResult>(rows: &crate::pool::QueryRows) -> Result<Vec<T>, String> {
393    rows.iter().map(T::from_query_result).collect()
394}
395
396impl Value {
397    /// 判断是否为 null
398    pub fn is_null(&self) -> bool {
399        matches!(self, Value::Null)
400    }
401
402    /// 判断是否为布尔值
403    pub fn is_bool(&self) -> bool {
404        matches!(self, Value::Bool(_))
405    }
406
407    /// 判断是否为整数
408    pub fn is_i64(&self) -> bool {
409        matches!(self, Value::I64(_))
410    }
411
412    /// 判断是否为浮点数
413    pub fn is_f64(&self) -> bool {
414        matches!(self, Value::F64(_))
415    }
416
417    /// 判断是否为字符串
418    pub fn is_string(&self) -> bool {
419        matches!(self, Value::String(_))
420    }
421
422    /// 判断是否为字节
423    pub fn is_bytes(&self) -> bool {
424        matches!(self, Value::Bytes(_))
425    }
426
427    /// 判断是否为对象
428    pub fn is_object(&self) -> bool {
429        matches!(self, Value::Object(_))
430    }
431
432    /// 从 HashMap 构造 Value
433    pub fn from_map(map: std::collections::HashMap<String, Value>) -> Self {
434        Value::Object(map)
435    }
436
437    /// 若可能,返回 &str 形式的值
438    pub fn as_str(&self) -> Option<&str> {
439        match self {
440            Value::String(s) => Some(s),
441            Value::Decimal(s) => Some(s),
442            _ => None,
443        }
444    }
445
446    /// 若可能,返回 i64 形式的值
447    /// 支持 F32/F64 → i64 的有损转换(数据库 SUM/AVG 等聚合函数常返回浮点类型)
448    /// U64 → i64 使用 `try_from`,超过 `i64::MAX` 时返回 `None`(避免静默截断为负数)
449    pub fn as_i64(&self) -> Option<i64> {
450        match self {
451            Value::I8(v) => Some(*v as i64),
452            Value::I16(v) => Some(*v as i64),
453            Value::I32(v) => Some(*v as i64),
454            Value::I64(v) => Some(*v),
455            Value::U8(v) => Some(*v as i64),
456            Value::U16(v) => Some(*v as i64),
457            Value::U32(v) => Some(*v as i64),
458            Value::U64(v) => i64::try_from(*v).ok(),
459            Value::F32(v) => Some(*v as i64),
460            Value::F64(v) => Some(*v as i64),
461            Value::Bool(v) => Some(if *v { 1 } else { 0 }),
462            Value::String(s) => s.parse::<i64>().ok(),
463            Value::Decimal(s) => s.parse::<i64>().ok(),
464            _ => None,
465        }
466    }
467
468    /// 若可能,返回 f64 形式的值
469    /// 支持整数类型 → f64 的转换
470    pub fn as_f64(&self) -> Option<f64> {
471        match self {
472            Value::F32(v) => Some(*v as f64),
473            Value::F64(v) => Some(*v),
474            Value::I8(v) => Some(*v as f64),
475            Value::I16(v) => Some(*v as f64),
476            Value::I32(v) => Some(*v as f64),
477            Value::I64(v) => Some(*v as f64),
478            Value::U8(v) => Some(*v as f64),
479            Value::U16(v) => Some(*v as f64),
480            Value::U32(v) => Some(*v as f64),
481            Value::U64(v) => Some(*v as f64),
482            Value::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
483            Value::Decimal(s) => s.parse::<f64>().ok(),
484            _ => None,
485        }
486    }
487
488    /// 若可能,返回 bool 形式的值
489    /// 支持整数(非 0 即真)、浮点(非 0.0 即真)、字符串("1"/"true"/"yes"/"on" 为真)的转换
490    pub fn as_bool(&self) -> Option<bool> {
491        match self {
492            Value::Bool(v) => Some(*v),
493            Value::I8(v) => Some(*v != 0),
494            Value::I16(v) => Some(*v != 0),
495            Value::I32(v) => Some(*v != 0),
496            Value::I64(v) => Some(*v != 0),
497            Value::U8(v) => Some(*v != 0),
498            Value::U16(v) => Some(*v != 0),
499            Value::U32(v) => Some(*v != 0),
500            Value::U64(v) => Some(*v != 0),
501            Value::F32(v) => Some(*v != 0.0),
502            Value::F64(v) => Some(*v != 0.0),
503            Value::String(s) => match s.to_lowercase().as_str() {
504                "1" | "true" | "yes" | "on" => Some(true),
505                "0" | "false" | "no" | "off" => Some(false),
506                _ => None,
507            },
508            Value::Null => Some(false),
509            _ => None,
510        }
511    }
512
513    /// 若可能,返回字节切片形式(&[u8])的值
514    /// 字符串类型会返回其 UTF-8 字节
515    pub fn as_bytes(&self) -> Option<&[u8]> {
516        match self {
517            Value::Bytes(v) => Some(v),
518            Value::String(s) => Some(s.as_bytes()),
519            _ => None,
520        }
521    }
522
523    /// 转换为 SQL 参数字符串(用于直接拼接 SQL 语句)
524    /// 字符串类型会进行转义并加引号;字节类型转换为 X'..' 形式
525    ///
526    /// # 安全性警告
527    ///
528    /// 本方法使用简单的 `'` → `''` 转义,对 PostgreSQL/SQLite 默认配置安全,
529    /// 但对 MySQL 默认配置(backslash 是转义字符)不安全:含 `\` 的字符串
530    /// 可能被 MySQL 误解。**生产环境请使用 [`Value::to_param_with_dialect`]**
531    /// 以获得方言感知的转义。
532    pub fn to_param(&self) -> Cow<'_, str> {
533        match self {
534            Value::Null => Cow::Borrowed("NULL"),
535            Value::Bool(b) => Cow::Owned(if *b { "TRUE" } else { "FALSE" }.to_string()),
536            Value::I8(v) => Cow::Owned(v.to_string()),
537            Value::I16(v) => Cow::Owned(v.to_string()),
538            Value::I32(v) => Cow::Owned(v.to_string()),
539            Value::I64(v) => Cow::Owned(v.to_string()),
540            Value::U8(v) => Cow::Owned(v.to_string()),
541            Value::U16(v) => Cow::Owned(v.to_string()),
542            Value::U32(v) => Cow::Owned(v.to_string()),
543            Value::U64(v) => Cow::Owned(v.to_string()),
544            Value::F32(v) => Cow::Owned(v.to_string()),
545            Value::F64(v) => Cow::Owned(v.to_string()),
546            Value::Decimal(s) => Cow::Owned(s.clone()),
547            Value::String(s) => Cow::Owned(format!("'{}'", escape_string(s))),
548            #[cfg(feature = "perf-box-str")]
549            Value::BoxedStr(s) => Cow::Owned(format!("'{}'", escape_string(s))),
550            Value::Bytes(b) => Cow::Owned(format!("X'{}'", hex_encode(b))),
551            Value::Uuid(s) => Cow::Owned(format!("'{}'", escape_string(s))),
552            Value::Date(s) => Cow::Owned(format!("'{}'", escape_string(s))),
553            Value::DateTime(s) => Cow::Owned(format!("'{}'", escape_string(s))),
554            Value::Time(s) => Cow::Owned(format!("'{}'", escape_string(s))),
555            Value::Json(s) => Cow::Owned(format!("'{}'", escape_string(s))),
556            Value::Array(arr) => {
557                let params: Vec<String> = arr.iter().map(|v| v.to_param().into_owned()).collect();
558                Cow::Owned(format!("({})", params.join(", ")))
559            }
560            Value::Object(_) => Cow::Borrowed("NULL"),
561        }
562    }
563
564    /// v0.2.2 修复 H-1:方言感知的 SQL 参数转换
565    ///
566    /// 与 [`to_param`](Self::to_param) 的区别:字符串类型使用 `dialect.escape_string()`
567    /// 而非简单的 `'` → `''` 转义,确保在所有方言下都安全:
568    ///
569    /// - **MySQL**:转义 `\`、`'`、`\0`、`\n`、`\r`、`\t`、`\x1a`
570    /// - **PostgreSQL**:仅转义 `'`(依赖 `standard_conforming_strings=on` 默认配置)
571    /// - **SQLite**:仅转义 `'`
572    ///
573    /// # 推荐用法
574    ///
575    /// ```ignore
576    /// use sz_orm_core::{DbType, get_dialect};
577    /// let dialect = get_dialect(DbType::MySQL)?;
578    /// let v = Value::String("hello\\nworld".to_string());
579    /// let param = v.to_param_with_dialect(&**dialect);
580    /// ```
581    pub fn to_param_with_dialect(&self, dialect: &dyn crate::dialect::Dialect) -> Cow<'_, str> {
582        match self {
583            Value::Null => Cow::Borrowed("NULL"),
584            Value::Bool(b) => Cow::Owned(if *b { "TRUE" } else { "FALSE" }.to_string()),
585            Value::I8(v) => Cow::Owned(v.to_string()),
586            Value::I16(v) => Cow::Owned(v.to_string()),
587            Value::I32(v) => Cow::Owned(v.to_string()),
588            Value::I64(v) => Cow::Owned(v.to_string()),
589            Value::U8(v) => Cow::Owned(v.to_string()),
590            Value::U16(v) => Cow::Owned(v.to_string()),
591            Value::U32(v) => Cow::Owned(v.to_string()),
592            Value::U64(v) => Cow::Owned(v.to_string()),
593            Value::F32(v) => Cow::Owned(v.to_string()),
594            Value::F64(v) => Cow::Owned(v.to_string()),
595            Value::Decimal(s) => Cow::Owned(s.clone()),
596            Value::String(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
597            #[cfg(feature = "perf-box-str")]
598            Value::BoxedStr(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
599            Value::Bytes(b) => Cow::Owned(format!("X'{}'", hex_encode(b))),
600            Value::Uuid(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
601            Value::Date(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
602            Value::DateTime(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
603            Value::Time(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
604            Value::Json(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
605            Value::Array(arr) => {
606                let params: Vec<String> = arr
607                    .iter()
608                    .map(|v| v.to_param_with_dialect(dialect).into_owned())
609                    .collect();
610                Cow::Owned(format!("({})", params.join(", ")))
611            }
612            Value::Object(_) => Cow::Borrowed("NULL"),
613        }
614    }
615
616    /// 从任何实现了 `Into<Value>` 的类型构造 Value
617    pub fn from<T: Into<Value>>(v: T) -> Self {
618        v.into()
619    }
620
621    /// v3.4.0 M3-T5:创建 `BoxedStr` 变体(perf-box-str feature)
622    ///
623    /// `Box<str>` 为 16 字节(指针 + 长度),比 `String`(24 字节)节省 8 字节。
624    /// 适用于不需要修改字符串的场景。
625    #[cfg(feature = "perf-box-str")]
626    pub fn boxed_str(s: impl Into<Box<str>>) -> Self {
627        Value::BoxedStr(s.into())
628    }
629}
630
631impl fmt::Display for Value {
632    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633        match self {
634            Value::Null => write!(f, "NULL"),
635            Value::Bool(b) => write!(f, "{}", b),
636            Value::I8(v) => write!(f, "{}", v),
637            Value::I16(v) => write!(f, "{}", v),
638            Value::I32(v) => write!(f, "{}", v),
639            Value::I64(v) => write!(f, "{}", v),
640            Value::U8(v) => write!(f, "{}", v),
641            Value::U16(v) => write!(f, "{}", v),
642            Value::U32(v) => write!(f, "{}", v),
643            Value::U64(v) => write!(f, "{}", v),
644            Value::F32(v) => write!(f, "{}", v),
645            Value::F64(v) => write!(f, "{}", v),
646            Value::Decimal(v) => write!(f, "{}", v),
647            Value::String(v) => write!(f, "'{}'", v),
648            #[cfg(feature = "perf-box-str")]
649            Value::BoxedStr(v) => write!(f, "'{}'", v),
650            Value::Bytes(v) => write!(f, "X'{}'", hex_encode(v)),
651            Value::Uuid(v) => write!(f, "'{}'", v),
652            Value::Date(v) => write!(f, "'{}'", v),
653            Value::DateTime(v) => write!(f, "'{}'", v),
654            Value::Time(v) => write!(f, "'{}'", v),
655            Value::Json(v) => write!(f, "'{}'", v),
656            Value::Array(v) => {
657                let items: Vec<String> = v.iter().map(|i| format!("{}", i)).collect();
658                write!(f, "({})", items.join(", "))
659            }
660            Value::Object(map) => {
661                let items: Vec<String> = map.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
662                write!(f, "{{{}}}", items.join(", "))
663            }
664        }
665    }
666}
667
668impl From<()> for Value {
669    fn from(_: ()) -> Self {
670        Value::Null
671    }
672}
673
674impl From<bool> for Value {
675    fn from(v: bool) -> Self {
676        Value::Bool(v)
677    }
678}
679
680impl From<i8> for Value {
681    fn from(v: i8) -> Self {
682        Value::I8(v)
683    }
684}
685
686impl From<i16> for Value {
687    fn from(v: i16) -> Self {
688        Value::I16(v)
689    }
690}
691
692impl From<i32> for Value {
693    fn from(v: i32) -> Self {
694        Value::I32(v)
695    }
696}
697
698impl From<i64> for Value {
699    fn from(v: i64) -> Self {
700        Value::I64(v)
701    }
702}
703
704impl From<u8> for Value {
705    fn from(v: u8) -> Self {
706        Value::U8(v)
707    }
708}
709
710impl From<u16> for Value {
711    fn from(v: u16) -> Self {
712        Value::U16(v)
713    }
714}
715
716impl From<u32> for Value {
717    fn from(v: u32) -> Self {
718        Value::U32(v)
719    }
720}
721
722impl From<u64> for Value {
723    fn from(v: u64) -> Self {
724        Value::U64(v)
725    }
726}
727
728impl From<f32> for Value {
729    fn from(v: f32) -> Self {
730        Value::F32(v)
731    }
732}
733
734impl From<f64> for Value {
735    fn from(v: f64) -> Self {
736        Value::F64(v)
737    }
738}
739
740impl From<String> for Value {
741    fn from(v: String) -> Self {
742        Value::String(v)
743    }
744}
745
746impl From<&str> for Value {
747    fn from(v: &str) -> Self {
748        Value::String(v.to_string())
749    }
750}
751
752impl From<Vec<u8>> for Value {
753    fn from(v: Vec<u8>) -> Self {
754        Value::Bytes(v)
755    }
756}
757
758impl From<&[u8]> for Value {
759    fn from(v: &[u8]) -> Self {
760        Value::Bytes(v.to_vec())
761    }
762}
763
764impl From<Vec<Value>> for Value {
765    fn from(v: Vec<Value>) -> Self {
766        Value::Array(v)
767    }
768}
769
770/// 字符串字面量转义(v0.2.1 修复 Critical D-1)
771///
772/// # 旧实现的问题
773///
774/// 旧实现同时使用 `'` → `''`(标准 SQL)和 `\` → `\\`(MySQL 风格)转义,
775/// 导致在 PostgreSQL/SQLite 等不把 `\` 作为转义字符的方言下数据完整性受损
776/// (写入 `\\n` 字面量而非 `\n`)。
777///
778/// # 新实现
779///
780/// 只使用标准 SQL 转义:`'` → `''`。
781///
782/// - **SQL 注入防御**:`'` 被转义为 `''`,攻击者无法突破字符串字面量
783/// - **数据完整性**:在所有方言(MySQL/PG/SQLite/Oracle)下数据保持原样
784/// - **MySQL 兼容性**:MySQL 默认把 `\` 作为转义字符,但我们不主动转义 `\`,
785///   所以写入的 `\` 会被 MySQL 解析为字面 `\`(与 PG/SQLite 一致)
786///
787/// # 注意
788///
789/// 对于需要方言感知转义的场景(如 MySQL 的 `NO_BACKSLASH_ESCAPES` 模式),
790/// 应使用 `Dialect::escape_string()` 方法。
791fn escape_string(s: &str) -> String {
792    let mut escaped = String::with_capacity(s.len() + s.chars().filter(|&c| c == '\'').count());
793    for c in s.chars() {
794        if c == '\'' {
795            escaped.push_str("''");
796        } else {
797            escaped.push(c);
798        }
799    }
800    escaped
801}
802
803fn hex_encode(bytes: &[u8]) -> String {
804    bytes.iter().map(|b| format!("{:02x}", b)).collect()
805}
806
807/// 列类型枚举(v1.1.0 新增)
808///
809/// 用于 `row_to_value_*` 函数的预解析列类型分派,避免每行每列做字符串 `match`。
810/// 适配器在第一行解析列类型为 `Vec<ColType>`,后续行复用枚举分派(编译器优化为跳转表)。
811///
812/// # 性能优势
813///
814/// - 字符串 `match type_name` 无法被 LLVM 优化为跳转表(`&str` 比较)
815/// - 枚举 `match col_type` 编译为跳转表,O(1) 且缓存友好
816/// - 在 SELECT ALL 大结果集场景下,每行每列节省 1 次字符串比较
817#[derive(Debug, Clone, Copy, PartialEq, Eq)]
818#[non_exhaustive]
819pub enum ColType {
820    /// 布尔类型(SQLite BOOLEAN / MySQL BOOLEAN/TINYINT(1) / PG BOOL / Oracle Boolean)
821    Bool,
822    /// 8 位有符号整数(MySQL TINYINT)
823    I8,
824    /// 16 位有符号整数(MySQL SMALLINT / PG INT2)
825    I16,
826    /// 32 位有符号整数(MySQL INT/MEDIUMINT / PG INT4)
827    I32,
828    /// 64 位有符号整数(MySQL BIGINT / PG INT8 / SQLite INTEGER / Oracle NUMBER)
829    I64,
830    /// 8 位无符号整数(MySQL TINYINT UNSIGNED)
831    U8,
832    /// 16 位无符号整数(MySQL SMALLINT UNSIGNED)
833    U16,
834    /// 32 位无符号整数(MySQL INT UNSIGNED/MEDIUMINT UNSIGNED)
835    U32,
836    /// 64 位无符号整数(MySQL BIGINT UNSIGNED)
837    U64,
838    /// 32 位浮点数(MySQL FLOAT / PG FLOAT4 / SQLite REAL)
839    F32,
840    /// 64 位浮点数(MySQL DOUBLE / PG FLOAT8 / Oracle BinaryDouble)
841    F64,
842    /// 高精度十进制数(MySQL DECIMAL/NUMERIC/NEWDECIMAL / PG NUMERIC / Oracle NUMBER(p,s))
843    Decimal,
844    /// 字符串类型(TEXT/VARCHAR/CHAR/CLOB 等)
845    String,
846    /// 字节类型(BLOB/BYTEA/RAW 等)
847    Bytes,
848    /// 日期类型(DATE)
849    Date,
850    /// 日期时间类型(DATETIME/TIMESTAMP)
851    DateTime,
852    /// 时间类型(TIME)
853    Time,
854    /// JSON 类型
855    Json,
856    /// UUID 类型
857    Uuid,
858    /// 未知类型(回退到 i64 → f64 → bool → String 顺序尝试)
859    Unknown,
860}
861
862impl ColType {
863    /// 从数据库类型名解析为 ColType(通用回退实现)
864    ///
865    /// 各适配器应优先使用自己专门的 `parse_col_type_<db>` 函数(覆盖数据库特有类型名),
866    /// 此函数作为通用回退,覆盖最常见的标准 SQL 类型名。
867    ///
868    /// # 注意
869    ///
870    /// "INTEGER" 在通用映射中被归为 I32(与 MySQL INT/PG INT4 一致)。
871    /// **SQLite 适配器必须使用 [`ColType::parse_sqlite`]**:SQLite 的 INTEGER
872    /// 类型采用动态存储,可容纳 64 位整数(sqlx 默认按 i64 解码),若按 I32
873    /// 解码会在数值超过 i32::MAX 时截断。
874    pub fn from_type_name(type_name: &str) -> Self {
875        match type_name {
876            "BOOLEAN" | "BOOL" => Self::Bool,
877            "TINYINT" => Self::I8,
878            "SMALLINT" | "INT2" => Self::I16,
879            "INT" | "INT4" | "OID" | "MEDIUMINT" | "INTEGER" => Self::I32,
880            "BIGINT" | "INT8" => Self::I64,
881            "TINYINT UNSIGNED" => Self::U8,
882            "SMALLINT UNSIGNED" => Self::U16,
883            "INT UNSIGNED" | "MEDIUMINT UNSIGNED" => Self::U32,
884            "BIGINT UNSIGNED" => Self::U64,
885            "FLOAT" | "FLOAT4" | "REAL" => Self::F32,
886            "DOUBLE" | "FLOAT8" => Self::F64,
887            "DECIMAL" | "NUMERIC" | "NEWDECIMAL" | "MONEY" => Self::Decimal,
888            "TEXT" | "VARCHAR" | "CHAR" | "NAME" => Self::String,
889            "BLOB" | "BYTEA" => Self::Bytes,
890            "DATE" => Self::Date,
891            "DATETIME" | "TIMESTAMP" => Self::DateTime,
892            "TIME" => Self::Time,
893            "JSON" => Self::Json,
894            "UUID" => Self::Uuid,
895            _ => Self::Unknown,
896        }
897    }
898
899    /// SQLite 专用列类型解析
900    ///
901    /// SQLite 使用动态类型系统(type affinity),同一列可存储 INT/REAL/TEXT/BLOB 任意类型。
902    /// sqlx 报告的类型名遵循 SQLite 的"声明类型"(declared type)规则:
903    ///
904    /// - **INTEGER**:实际可容纳 8 字节整数(最大 2^63-1),sqlx 默认按 `i64` 解码。
905    ///   若按 I32 解码,数值超过 `i32::MAX` 会静默截断。
906    /// - **INT/INTEGER/BIGINT** 等:在 SQLite 中都按 INTEGER 亲和性处理,应统一映射为 I64。
907    /// - **REAL/FLOAT/DOUBLE**:映射为 F64(SQLite REAL 是 8 字节 IEEE 754)。
908    /// - **TEXT/CLOB**:映射为 String。
909    /// - **BLOB**:映射为 Bytes。
910    /// - **NUMERIC/DECIMAL**:保留为 Decimal(按字符串解码避免精度丢失)。
911    /// - **BOOLEAN**:SQLite 无原生 BOOLEAN,存为 INTEGER 0/1,但声明 BOOLEAN 时按 Bool 解码。
912    /// - **DATETIME/TIMESTAMP/DATE/TIME**:SQLite 通常以 TEXT 存储,按 String 解码。
913    /// - **JSON**:SQLite 4.x 后有 JSON 类型,按 String 解码(保留原始 JSON 文本)。
914    pub fn parse_sqlite(type_name: &str) -> Self {
915        // SQLite type_info 可能返回空字符串(NULL 或表达式结果),按 Unknown 处理
916        if type_name.is_empty() {
917            return Self::Unknown;
918        }
919        match type_name.to_uppercase().as_str() {
920            // SQLite INTEGER 亲和性:实际为 64 位有符号整数
921            "INTEGER" | "INT" | "BIGINT" | "INT8" | "INT4" | "INT2" | "TINYINT" | "SMALLINT"
922            | "MEDIUMINT" => Self::I64,
923            "BOOLEAN" | "BOOL" => Self::Bool,
924            "REAL" | "FLOAT" | "DOUBLE" | "FLOAT8" | "DOUBLE PRECISION" => Self::F64,
925            "DECIMAL" | "NUMERIC" => Self::Decimal,
926            "TEXT" | "CLOB" | "VARCHAR" | "CHAR" | "NAME" => Self::String,
927            "BLOB" => Self::Bytes,
928            "DATE" => Self::Date,
929            "DATETIME" | "TIMESTAMP" => Self::DateTime,
930            "TIME" => Self::Time,
931            "JSON" => Self::Json,
932            _ => Self::Unknown,
933        }
934    }
935
936    /// MySQL 专用列类型解析
937    ///
938    /// MySQL 类型名来自 `Column::type_info().name()`,遵循 MySQL 协议报告的类型名。
939    pub fn parse_mysql(type_name: &str) -> Self {
940        match type_name.to_uppercase().as_str() {
941            "TINYINT" => Self::I8,
942            "SMALLINT" => Self::I16,
943            "INT" | "INTEGER" | "MEDIUMINT" => Self::I32,
944            "BIGINT" => Self::I64,
945            "TINYINT UNSIGNED" => Self::U8,
946            "SMALLINT UNSIGNED" => Self::U16,
947            "INT UNSIGNED" | "MEDIUMINT UNSIGNED" => Self::U32,
948            "BIGINT UNSIGNED" => Self::U64,
949            "FLOAT" => Self::F32,
950            "DOUBLE" => Self::F64,
951            "DECIMAL" | "NUMERIC" | "NEWDECIMAL" => Self::Decimal,
952            "VARCHAR" | "CHAR" | "TEXT" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM"
953            | "SET" => Self::String,
954            "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BINARY" | "VARBINARY" => Self::Bytes,
955            "DATE" => Self::Date,
956            "DATETIME" | "TIMESTAMP" => Self::DateTime,
957            "TIME" => Self::Time,
958            "YEAR" => Self::I16,
959            "JSON" => Self::Json,
960            "BOOLEAN" | "BOOL" => Self::Bool,
961            _ => Self::from_type_name(type_name),
962        }
963    }
964
965    /// PostgreSQL 专用列类型解析
966    ///
967    /// PostgreSQL 类型名来自 `Column::type_info().name()`,使用 PG 内部类型名(如 INT4/INT8/FLOAT8)。
968    pub fn parse_postgres(type_name: &str) -> Self {
969        match type_name.to_uppercase().as_str() {
970            "BOOL" => Self::Bool,
971            "INT2" | "SMALLINT" => Self::I16,
972            "INT4" | "INTEGER" | "INT" => Self::I32,
973            "INT8" | "BIGINT" => Self::I64,
974            "FLOAT4" | "REAL" => Self::F32,
975            "FLOAT8" | "DOUBLE PRECISION" => Self::F64,
976            "NUMERIC" | "DECIMAL" | "MONEY" => Self::Decimal,
977            "TEXT" | "VARCHAR" | "CHAR" | "BPCHAR" | "NAME" | "CITEXT" => Self::String,
978            "BYTEA" => Self::Bytes,
979            "DATE" => Self::Date,
980            "TIMESTAMP" | "TIMESTAMPTZ" => Self::DateTime,
981            "TIME" | "TIMETZ" => Self::Time,
982            "JSON" | "JSONB" => Self::Json,
983            "UUID" => Self::Uuid,
984            "OID" => Self::I32,
985            _ => Self::from_type_name(type_name),
986        }
987    }
988}
989
990/// 位置式查询结果类型
991///
992/// 用于 `Connection::query_values` / `query_values_with_params`,绕过
993/// `HashMap<String, Value>` 行映射的开销,直接返回列名 + 按列顺序的值矩阵。
994///
995/// # 性能优势
996///
997/// - 普通 `query` 返回 `Vec<HashMap<String, Value>>`,每行每列需哈希计算 + 字符串克隆
998/// - `QueryValues` 返回 `(Vec<String>, Vec<Vec<Value>>)`,列名只分配一次,
999///   每行值按列序号直接 `Vec::push`,无哈希计算
1000/// - 在 SELECT ALL 大结果集场景下,比 `query` 提升 30%~50%
1001///
1002/// # 用法
1003///
1004/// ```rust,ignore
1005/// let (names, values_matrix): QueryValues = conn.query_values("SELECT id, name FROM users").await?;
1006/// // names = ["id", "name"]
1007/// // values_matrix[0] = [Value::I64(1), Value::String("Alice".into())]
1008/// ```
1009pub type QueryValues = (Vec<String>, Vec<Vec<Value>>);
1010
1011#[cfg(test)]
1012mod tests {
1013    use super::*;
1014
1015    #[test]
1016    fn test_value_is_null() {
1017        assert!(Value::Null.is_null());
1018        assert!(!Value::I64(0).is_null());
1019    }
1020
1021    // ---- P0-2:编译期 const 辅助函数测试 ----
1022
1023    #[test]
1024    fn test_const_str_eq() {
1025        assert!(__sz_orm_const_str_eq("id", "id"));
1026        assert!(__sz_orm_const_str_eq("user_id", "user_id"));
1027        assert!(!__sz_orm_const_str_eq("id", "ID"));
1028        assert!(!__sz_orm_const_str_eq("id", "idd"));
1029        assert!(!__sz_orm_const_str_eq("", "id"));
1030        assert!(__sz_orm_const_str_eq("", ""));
1031    }
1032
1033    #[test]
1034    fn test_const_types_compatible_same_category() {
1035        // 同一逻辑分类 → 兼容
1036        assert!(__sz_orm_const_types_compatible("BIGINT", "BIGINT"));
1037        assert!(__sz_orm_const_types_compatible("bigint", "BIGINT"));
1038        assert!(__sz_orm_const_types_compatible("INT8", "BIGINT")); // PG 风格
1039        assert!(__sz_orm_const_types_compatible("varchar", "TEXT"));
1040        assert!(__sz_orm_const_types_compatible("VARCHAR", "VARCHAR"));
1041        assert!(__sz_orm_const_types_compatible("timestamp", "DATETIME"));
1042        assert!(__sz_orm_const_types_compatible("int4", "INT"));
1043        assert!(__sz_orm_const_types_compatible("jsonb", "JSON"));
1044        assert!(__sz_orm_const_types_compatible("numeric", "DECIMAL"));
1045    }
1046
1047    #[test]
1048    fn test_const_types_compatible_different_category() {
1049        // 不同逻辑分类 → 不兼容
1050        assert!(!__sz_orm_const_types_compatible("BIGINT", "TEXT"));
1051        assert!(!__sz_orm_const_types_compatible("VARCHAR", "INT"));
1052        assert!(!__sz_orm_const_types_compatible("JSON", "BIGINT"));
1053        assert!(!__sz_orm_const_types_compatible("BLOB", "DATE"));
1054        assert!(!__sz_orm_const_types_compatible("DOUBLE", "INT"));
1055    }
1056
1057    #[test]
1058    fn test_const_types_compatible_unknown_tolerant() {
1059        // 未知类型分类为 0 → 容忍(不误报)
1060        assert!(__sz_orm_const_types_compatible("CUSTOM_TYPE", "BIGINT"));
1061        assert!(__sz_orm_const_types_compatible("BIGINT", "CUSTOM_TYPE"));
1062        assert!(__sz_orm_const_types_compatible("UNKNOWN1", "UNKNOWN2"));
1063    }
1064
1065    #[test]
1066    fn test_col_type_from_type_name() {
1067        // 标准类型
1068        assert_eq!(ColType::from_type_name("BOOLEAN"), ColType::Bool);
1069        assert_eq!(ColType::from_type_name("TINYINT"), ColType::I8);
1070        assert_eq!(ColType::from_type_name("SMALLINT"), ColType::I16);
1071        assert_eq!(ColType::from_type_name("INT"), ColType::I32);
1072        assert_eq!(ColType::from_type_name("BIGINT"), ColType::I64);
1073        assert_eq!(ColType::from_type_name("INT UNSIGNED"), ColType::U32);
1074        assert_eq!(ColType::from_type_name("FLOAT"), ColType::F32);
1075        assert_eq!(ColType::from_type_name("DOUBLE"), ColType::F64);
1076        assert_eq!(ColType::from_type_name("TEXT"), ColType::String);
1077        assert_eq!(ColType::from_type_name("BLOB"), ColType::Bytes);
1078        assert_eq!(ColType::from_type_name("DATE"), ColType::Date);
1079        assert_eq!(ColType::from_type_name("TIMESTAMP"), ColType::DateTime);
1080        assert_eq!(ColType::from_type_name("JSON"), ColType::Json);
1081        // PG 风格
1082        assert_eq!(ColType::from_type_name("INT2"), ColType::I16);
1083        assert_eq!(ColType::from_type_name("INT4"), ColType::I32);
1084        assert_eq!(ColType::from_type_name("INT8"), ColType::I64);
1085        assert_eq!(ColType::from_type_name("FLOAT4"), ColType::F32);
1086        assert_eq!(ColType::from_type_name("FLOAT8"), ColType::F64);
1087        assert_eq!(ColType::from_type_name("BYTEA"), ColType::Bytes);
1088        // 未知类型
1089        assert_eq!(ColType::from_type_name("UNKNOWN_TYPE"), ColType::Unknown);
1090        assert_eq!(ColType::from_type_name(""), ColType::Unknown);
1091    }
1092
1093    #[test]
1094    fn test_value_as_i64() {
1095        assert_eq!(Value::I64(42).as_i64(), Some(42));
1096        assert_eq!(Value::I32(42).as_i64(), Some(42));
1097        assert_eq!(Value::Bool(true).as_i64(), Some(1));
1098        assert!(Value::String("test".to_string()).as_i64().is_none());
1099    }
1100
1101    #[test]
1102    fn test_value_as_f64() {
1103        assert_eq!(Value::F64(2.5).as_f64(), Some(2.5));
1104        assert_eq!(Value::I64(42).as_f64(), Some(42.0));
1105    }
1106
1107    #[test]
1108    fn test_value_as_str() {
1109        assert_eq!(Value::String("hello".to_string()).as_str(), Some("hello"));
1110    }
1111
1112    #[test]
1113    fn test_value_to_param() {
1114        assert_eq!(Value::Null.to_param(), "NULL");
1115        assert_eq!(Value::Bool(true).to_param(), "TRUE");
1116        assert_eq!(Value::I64(42).to_param(), "42");
1117        assert_eq!(Value::String("test".to_string()).to_param(), "'test'");
1118        assert_eq!(Value::String("it's".to_string()).to_param(), "'it''s'");
1119    }
1120
1121    #[test]
1122    fn test_value_into() {
1123        let v: Value = 42i64.into();
1124        assert_eq!(v, Value::I64(42));
1125
1126        let v: Value = "hello".into();
1127        assert_eq!(v, Value::String("hello".to_string()));
1128
1129        let arr: Vec<Value> = vec![Value::I64(1), Value::I64(2)];
1130        let v: Value = arr.into();
1131        assert_eq!(v, Value::Array(vec![Value::I64(1), Value::I64(2)]));
1132    }
1133
1134    #[test]
1135    fn test_value_display() {
1136        assert_eq!(format!("{}", Value::Null), "NULL");
1137        assert_eq!(format!("{}", Value::Bool(true)), "true");
1138        assert_eq!(format!("{}", Value::I64(42)), "42");
1139        assert_eq!(format!("{}", Value::String("test".to_string())), "'test'");
1140    }
1141
1142    /// M3-T5.2:Box<str> vs String 内存占用对比
1143    #[test]
1144    fn test_box_str_size() {
1145        let string_size = std::mem::size_of::<String>();
1146        let box_str_size = std::mem::size_of::<Box<str>>();
1147        assert_eq!(string_size, 24);
1148        assert_eq!(box_str_size, 16);
1149        assert_eq!(string_size - box_str_size, 8);
1150    }
1151
1152    /// M3-T5:BoxedStr 变体行为验证
1153    #[cfg(feature = "perf-box-str")]
1154    #[test]
1155    fn test_boxed_str_variant() {
1156        let v = Value::boxed_str("hello");
1157        assert_eq!(format!("{}", v), "'hello'");
1158        assert_eq!(v.to_param(), "'hello'");
1159    }
1160}