dm_database_parser_sqllog/
sqllog.rs

1//! SQL 日志数据结构定义
2//!
3//! 定义了解析后的 SQL 日志记录的数据结构。
4//!
5//! # 用法说明
6//!
7//! Sqllog、MetaParts、IndicatorsParts 仅作为库 API 的数据结构,普通用户无需手动构造。
8
9/// SQL 日志记录
10///
11/// 表示一条完整的 SQL 日志记录,包含时间戳、元数据、SQL 语句体和可选的性能指标。
12///
13
14#[derive(Debug, Clone, PartialEq, Default)]
15pub struct Sqllog {
16    /// 时间戳,格式为 "YYYY-MM-DD HH:MM:SS.mmm"
17    pub ts: String,
18
19    /// 元数据部分,包含会话信息、用户信息等
20    pub meta: MetaParts,
21
22    /// SQL 语句体
23    pub body: String,
24
25    /// 可选的性能指标信息
26    pub indicators: Option<IndicatorsParts>,
27}
28
29/// 元数据部分
30///
31/// 包含日志记录的所有元数据字段,如会话 ID、用户名等。
32#[derive(Debug, Clone, PartialEq, Default)]
33pub struct MetaParts {
34    /// EP(Execution Point)编号,范围 0-255
35    pub ep: u8,
36
37    /// 会话 ID
38    pub sess_id: String,
39
40    /// 线程 ID
41    pub thrd_id: String,
42
43    /// 用户名
44    pub username: String,
45
46    /// 事务 ID
47    pub trxid: String,
48
49    /// 语句 ID
50    pub statement: String,
51
52    /// 应用程序名称
53    pub appname: String,
54
55    /// 客户端 IP 地址(可选)
56    pub client_ip: String,
57}
58
59/// 性能指标部分
60///
61/// 包含 SQL 执行的性能指标,如执行时间、影响行数等。
62///
63
64#[derive(Debug, Clone, Copy, PartialEq, Default)]
65pub struct IndicatorsParts {
66    /// 执行时间(毫秒)
67    pub execute_time: f32,
68
69    /// 影响的行数
70    pub row_count: u32,
71
72    /// 执行 ID
73    pub execute_id: i64,
74}
75
76impl Sqllog {
77    /// 判断是否有性能指标信息
78    ///
79    /// # 返回
80    ///
81    /// 如果存在性能指标返回 `true`,否则返回 `false`
82    #[inline]
83    pub fn has_indicators(&self) -> bool {
84        self.indicators.is_some()
85    }
86
87    /// 获取执行时间(毫秒)
88    ///
89    /// # 返回
90    ///
91    /// 如果存在性能指标返回执行时间,否则返回 `None`
92    #[inline]
93    pub fn execute_time(&self) -> Option<f32> {
94        self.indicators.map(|i| i.execute_time)
95    }
96
97    /// 获取影响行数
98    ///
99    /// # 返回
100    ///
101    /// 如果存在性能指标返回影响行数,否则返回 `None`
102    #[inline]
103    pub fn row_count(&self) -> Option<u32> {
104        self.indicators.map(|i| i.row_count)
105    }
106
107    /// 获取执行 ID
108    ///
109    /// # 返回
110    ///
111    /// 如果存在性能指标返回执行 ID,否则返回 `None`
112    #[inline]
113    pub fn execute_id(&self) -> Option<i64> {
114        self.indicators.map(|i| i.execute_id)
115    }
116}
117
118impl MetaParts {
119    /// 判断是否有客户端 IP 信息
120    ///
121    /// # 返回
122    ///
123    /// 如果存在客户端 IP 返回 `true`,否则返回 `false`
124    #[inline]
125    pub fn has_client_ip(&self) -> bool {
126        !self.client_ip.is_empty()
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_sqllog_has_indicators() {
136        // 测试有指标的情况
137        let sqllog = Sqllog {
138            ts: "2025-08-12 10:57:09.548".to_string(),
139            meta: MetaParts::default(),
140            body: "SELECT 1".to_string(),
141            indicators: Some(IndicatorsParts {
142                execute_time: 10.5,
143                row_count: 100,
144                execute_id: 12345,
145            }),
146        };
147        assert!(sqllog.has_indicators());
148
149        // 测试没有指标的情况
150        let sqllog_no_indicators = Sqllog {
151            ts: "2025-08-12 10:57:09.548".to_string(),
152            meta: MetaParts::default(),
153            body: "SELECT 1".to_string(),
154            indicators: None,
155        };
156        assert!(!sqllog_no_indicators.has_indicators());
157    }
158
159    #[test]
160    fn test_sqllog_execute_time() {
161        // 测试有指标的情况
162        let sqllog = Sqllog {
163            ts: "2025-08-12 10:57:09.548".to_string(),
164            meta: MetaParts::default(),
165            body: "SELECT 1".to_string(),
166            indicators: Some(IndicatorsParts {
167                execute_time: 10.5,
168                row_count: 100,
169                execute_id: 12345,
170            }),
171        };
172        assert_eq!(sqllog.execute_time(), Some(10.5));
173
174        // 测试没有指标的情况
175        let sqllog_no_indicators = Sqllog {
176            ts: "2025-08-12 10:57:09.548".to_string(),
177            meta: MetaParts::default(),
178            body: "SELECT 1".to_string(),
179            indicators: None,
180        };
181        assert_eq!(sqllog_no_indicators.execute_time(), None);
182    }
183
184    #[test]
185    fn test_sqllog_row_count() {
186        // 测试有指标的情况
187        let sqllog = Sqllog {
188            ts: "2025-08-12 10:57:09.548".to_string(),
189            meta: MetaParts::default(),
190            body: "SELECT 1".to_string(),
191            indicators: Some(IndicatorsParts {
192                execute_time: 10.5,
193                row_count: 100,
194                execute_id: 12345,
195            }),
196        };
197        assert_eq!(sqllog.row_count(), Some(100));
198
199        // 测试没有指标的情况
200        let sqllog_no_indicators = Sqllog {
201            ts: "2025-08-12 10:57:09.548".to_string(),
202            meta: MetaParts::default(),
203            body: "SELECT 1".to_string(),
204            indicators: None,
205        };
206        assert_eq!(sqllog_no_indicators.row_count(), None);
207    }
208
209    #[test]
210    fn test_sqllog_execute_id() {
211        // 测试有指标的情况
212        let sqllog = Sqllog {
213            ts: "2025-08-12 10:57:09.548".to_string(),
214            meta: MetaParts::default(),
215            body: "SELECT 1".to_string(),
216            indicators: Some(IndicatorsParts {
217                execute_time: 10.5,
218                row_count: 100,
219                execute_id: 12345,
220            }),
221        };
222        assert_eq!(sqllog.execute_id(), Some(12345));
223
224        // 测试没有指标的情况
225        let sqllog_no_indicators = Sqllog {
226            ts: "2025-08-12 10:57:09.548".to_string(),
227            meta: MetaParts::default(),
228            body: "SELECT 1".to_string(),
229            indicators: None,
230        };
231        assert_eq!(sqllog_no_indicators.execute_id(), None);
232    }
233
234    #[test]
235    fn test_meta_has_client_ip() {
236        // 测试有 IP 的情况
237        let meta_with_ip = MetaParts {
238            ep: 0,
239            sess_id: "123".to_string(),
240            thrd_id: "456".to_string(),
241            username: "alice".to_string(),
242            trxid: "789".to_string(),
243            statement: "999".to_string(),
244            appname: "app".to_string(),
245            client_ip: "192.168.1.1".to_string(),
246        };
247        assert!(meta_with_ip.has_client_ip());
248
249        // 测试没有 IP 的情况
250        let meta_no_ip = MetaParts {
251            ep: 0,
252            sess_id: "123".to_string(),
253            thrd_id: "456".to_string(),
254            username: "alice".to_string(),
255            trxid: "789".to_string(),
256            statement: "999".to_string(),
257            appname: "app".to_string(),
258            client_ip: "".to_string(),
259        };
260        assert!(!meta_no_ip.has_client_ip());
261    }
262
263    #[test]
264    fn test_indicators_copy_trait() {
265        let indicators = IndicatorsParts {
266            execute_time: 10.5,
267            row_count: 100,
268            execute_id: 12345,
269        };
270        let copied = indicators;
271        assert_eq!(indicators.execute_time, copied.execute_time);
272        assert_eq!(indicators.row_count, copied.row_count);
273        assert_eq!(indicators.execute_id, copied.execute_id);
274    }
275
276    #[test]
277    fn test_sqllog_default() {
278        let sqllog = Sqllog::default();
279        assert_eq!(sqllog.ts, "");
280        assert_eq!(sqllog.body, "");
281        assert!(sqllog.indicators.is_none());
282    }
283
284    #[test]
285    fn test_meta_default() {
286        let meta = MetaParts::default();
287        assert_eq!(meta.ep, 0);
288        assert_eq!(meta.sess_id, "");
289        assert_eq!(meta.username, "");
290        assert!(!meta.has_client_ip());
291    }
292
293    #[test]
294    fn test_indicators_default() {
295        let indicators = IndicatorsParts::default();
296        assert_eq!(indicators.execute_time, 0.0);
297        assert_eq!(indicators.row_count, 0);
298        assert_eq!(indicators.execute_id, 0);
299    }
300}