dm_database_parser_sqllog/
sqllog.rs

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