Skip to main content

rust_store_core/dialect/
ir.rs

1//! SQL 中间表示(IR)
2
3use serde_json::{json, Value};
4
5/// 一条参数化 SQL 语句(参数位置用 `?` / `$n` 占位,params 顺序对应)
6#[derive(Debug, Clone, PartialEq)]
7pub struct SqlStmt {
8    /// SQL 文本(标识符已按后端加引号,值全部参数化)
9    pub text: String,
10    /// 绑定参数(按占位顺序)
11    pub params: Vec<Value>,
12    /// 是否为写语句
13    pub is_write: bool,
14    /// SELECT 时的「列 → JSON 还原路径」元数据(见 `row_shape`)
15    pub row_shape: Option<RowShape>,
16    /// RETURNING 列(PG/SQLite 写语句回读用;MySQL 无)
17    pub returning: Vec<String>,
18}
19
20impl SqlStmt {
21    pub fn select(text: String, params: Vec<Value>, row_shape: RowShape) -> Self {
22        SqlStmt {
23            text,
24            params,
25            is_write: false,
26            row_shape: Some(row_shape),
27            returning: Vec::new(),
28        }
29    }
30
31    pub fn write(text: String, params: Vec<Value>) -> Self {
32        SqlStmt {
33            text,
34            params,
35            is_write: true,
36            row_shape: None,
37            returning: Vec::new(),
38        }
39    }
40
41    pub fn to_value(&self) -> Value {
42        json!({
43            "text": self.text,
44            "params": self.params,
45            "isWrite": self.is_write,
46            "rowShape": self.row_shape.as_ref().map(|r| r.to_value()).unwrap_or(Value::Null),
47            "returning": self.returning,
48        })
49    }
50}
51
52/// SELECT 输出的一列:SQL 别名(如 `t1_title` / `rel0_amount`)→ JSON 里的嵌套还原路径
53#[derive(Debug, Clone, PartialEq)]
54pub struct RowShape {
55    /// 列 → { jsonPath, isArray }。jsonPath 形如 `["title"]` 或 `["items","amount"]`,
56    /// 表示该列应嵌套还原到文档的哪个位置;isArray 表示该字段是关系数组(聚合还原)。
57    pub columns: Vec<RowCol>,
58    /// `__present` 哨兵列的别名(缺失 vs null 三态,F-07/H-01):该列存每行
59    /// 「哪些标量字段显式存在」的集合。读取时标量列值为 null,仅当字段存在于
60    /// 该集合才还原为 `key: null`,否则(缺失)不产出该键。`None` 表示本条语句
61    /// 未查该列(如 count / 关系聚合),还原时不做存在性判定。
62    pub present_alias: Option<String>,
63}
64
65#[derive(Debug, Clone, PartialEq)]
66pub struct RowCol {
67    pub alias: String,
68    pub json_path: Vec<String>,
69    /// 是否为关系聚合数组字段(`many` 关系的子文档)
70    pub is_array: bool,
71    /// 是否为 `one` 关系(LEFT JOIN 后还原为对象或 `null`,而非数组)
72    pub one: bool,
73    /// 沿 `json_path` 每一级关系的基数(`true` = `one`)。长度 = 关系级数
74    /// (`json_path.len() - 1`);根标量列为空。嵌套关系靠它逐级决定「对象 / 数组」。
75    pub ones: Vec<bool>,
76    /// 若为空:标量直接取该列;若非空:构造子文档
77    pub sub_shape: Option<RowShape>,
78    /// 值恒「存在」:为 `null` 时也强制写入(不依赖 `__present` 哨兵)。用于归一聚合
79    /// 计算列(§9.2(2))—— 空集 `$sum/$avg/$min/$max` 的语义是显式 `null`(§9.7),
80    /// 与 Mongo 输出逐行对齐。
81    pub always: bool,
82    /// §9.7「布尔归一」:该输出列对应 schema 的 `boolean` 字段 → 行还原时把 SQL 的
83    /// `0/1` 归一为 JSON `bool`(对齐 Mongo;PG 原生 `BOOLEAN` 已是 bool,归一为 no-op)。
84    /// 依据**schema 声明类型**而非驱动元数据,故对任意用户 DDL 都成立。
85    pub is_bool: bool,
86    /// JSON 列(object/array 字段落单列存 JSON 文本):行还原时把列值(JSON 文本字符串)
87    /// 解析为嵌套 JSON 值,跨后端对齐 Mongo 的嵌套文档(见执行文档 §4.5 / 不足清单 #3)。
88    pub is_json: bool,
89}
90
91impl RowShape {
92    pub fn empty() -> Self {
93        RowShape {
94            columns: Vec::new(),
95            present_alias: None,
96        }
97    }
98
99    pub fn to_value(&self) -> Value {
100        let cols: Vec<Value> = self
101            .columns
102            .iter()
103            .map(|c| {
104                json!({
105                    "alias": c.alias,
106                    "path": c.json_path,
107                    "isArray": c.is_array,
108                    "one": c.one,
109                    "ones": c.ones,
110                    "subShape": c.sub_shape.as_ref().map(|s| s.to_value()).unwrap_or(Value::Null),
111                    "always": c.always,
112                    "bool": c.is_bool,
113                    "json": c.is_json,
114                })
115            })
116            .collect();
117        json!({
118            "columns": cols,
119            "present": self.present_alias.clone().unwrap_or_default(),
120        })
121    }
122}
123
124impl RowCol {
125    pub fn scalar(alias: &str, path: &[&str]) -> Self {
126        RowCol {
127            alias: alias.to_string(),
128            json_path: path.iter().map(|s| s.to_string()).collect(),
129            is_array: false,
130            one: false,
131            ones: Vec::new(),
132            sub_shape: None,
133            always: false,
134            is_bool: false,
135            is_json: false,
136        }
137    }
138
139    /// 标量列 + 布尔标记(§9.7):来自 schema `boolean` 字段的输出列
140    pub fn scalar_bool(alias: &str, path: &[&str], is_bool: bool) -> Self {
141        let mut c = RowCol::scalar(alias, path);
142        c.is_bool = is_bool;
143        c
144    }
145
146    /// JSON 列(object/array 字段):还原时把 JSON 文本解析为嵌套值
147    pub fn json(alias: &str, path: &[&str]) -> Self {
148        let mut c = RowCol::scalar(alias, path);
149        c.is_json = true;
150        c
151    }
152
153    /// 归一聚合计算列(§9.2(2)):根标量,且 `null` 也强制写入(见 [`RowCol::always`])
154    pub fn computed(alias: &str, path: &[&str]) -> Self {
155        RowCol {
156            alias: alias.to_string(),
157            json_path: path.iter().map(|s| s.to_string()).collect(),
158            is_array: false,
159            one: false,
160            ones: Vec::new(),
161            sub_shape: None,
162            always: true,
163            is_bool: false,
164            is_json: false,
165        }
166    }
167
168    pub fn array(alias: &str, sub_shape: RowShape) -> Self {
169        RowCol {
170            alias: alias.to_string(),
171            json_path: vec![alias.to_string()],
172            is_array: true,
173            one: false,
174            ones: vec![false],
175            sub_shape: Some(sub_shape),
176            always: false,
177            is_bool: false,
178            is_json: false,
179        }
180    }
181}