dm_database_parser_sqllog/parser/api.rs
1//! 便捷 API 函数
2//!
3//! 提供了一组方便使用的高层 API,用于快速解析 SQL 日志。
4
5use crate::error::ParseError;
6use crate::parser::record_parser::RecordParser;
7use crate::sqllog::Sqllog;
8use std::fs::File;
9use std::io::BufReader;
10use std::path::Path;
11
12// SqllogIterator 已移入 record_parser.rs 并非公共导出
13
14/// 从文件读取并返回 Sqllog 迭代器(流式处理)
15///
16/// 这是一个便捷函数,从文件读取日志并返回 `SqllogIterator` 迭代器。
17/// 使用迭代器可以避免一次性加载所有数据到内存,适合处理大文件。
18///
19/// # 参数
20///
21/// * `path` - 日志文件路径
22///
23/// # 返回
24///
25/// * `Ok(Iterator)` - 返回一个用于流式解析的迭代器,迭代项是 `Result<Sqllog, ParseError>`
26/// * `Err(ParseError)` - 文件打开错误
27///
28/// # 示例
29///
30/// ```no_run
31/// use dm_database_parser_sqllog::iter_records_from_file;
32///
33/// let parser = iter_records_from_file("sqllog.txt")?;
34///
35/// let mut sqllog_count = 0;
36/// let mut error_count = 0;
37///
38/// for result in parser {
39/// match result {
40/// Ok(sqllog) => {
41/// sqllog_count += 1;
42/// println!("Sqllog {}: 用户={}, SQL={}",
43/// sqllog_count, sqllog.meta.username, sqllog.body);
44/// }
45/// Err(err) => {
46/// error_count += 1;
47/// eprintln!("错误 {}: {}", error_count, err);
48/// }
49/// }
50/// }
51///
52/// println!("成功: {} 条, 错误: {} 个", sqllog_count, error_count);
53/// # Ok::<(), Box<dyn std::error::Error>>(())
54/// ```
55pub fn iter_records_from_file<P>(
56 path: P,
57) -> Result<impl Iterator<Item = Result<Sqllog, ParseError>>, ParseError>
58where
59 P: AsRef<Path>,
60{
61 let path_ref = path.as_ref();
62 let file = File::open(path_ref).map_err(|e| ParseError::FileNotFound {
63 path: format!("{}: {}", path_ref.display(), e),
64 })?;
65 let reader = BufReader::new(file);
66 let record_parser = RecordParser::new(reader);
67 // 返回一个隐藏的具体迭代器实现(crate 内部定义)
68 Ok(crate::parser::record_parser::SqllogIterator::new(
69 record_parser,
70 ))
71}
72
73/// 从文件读取并并行解析为 Sqllog(高性能版本)
74///
75/// 此函数使用并行处理,将日志文件解析为 Sqllog 列表。
76/// 适合处理大文件(GB 级别),先识别所有记录,然后并行解析。
77///
78/// # 性能
79///
80/// - 1GB 文件(300万条记录):约 2.5-2.7 秒
81/// - 与流式处理性能相当(流式也使用批量并行优化)
82/// - 内存使用:批量加载所有记录到内存
83///
84/// # 参数
85///
86/// * `path` - 日志文件路径
87///
88/// # 返回
89///
90/// * `Ok((Vec<Sqllog>, Vec<ParseError>))` - 成功解析的 Sqllog 和遇到的错误
91/// * `Err(ParseError)` - 文件打开错误
92///
93/// # 示例
94///
95/// ```no_run
96/// use dm_database_parser_sqllog::parse_records_from_file;
97///
98/// let (sqllogs, errors) = parse_records_from_file("large_log.txt")?;
99///
100/// println!("成功解析 {} 条 SQL 日志", sqllogs.len());
101/// println!("遇到 {} 个错误", errors.len());
102///
103/// for sqllog in sqllogs.iter().take(10) {
104/// println!("用户: {}, SQL: {}", sqllog.meta.username, sqllog.body);
105/// }
106/// # Ok::<(), Box<dyn std::error::Error>>(())
107/// ```
108pub fn parse_records_from_file<P>(path: P) -> Result<(Vec<Sqllog>, Vec<ParseError>), ParseError>
109where
110 P: AsRef<Path>,
111{
112 // 直接使用流式迭代器收集所有结果(内部已经使用批量并行优化)
113 let mut sqllogs = Vec::new();
114 let mut errors = Vec::new();
115
116 for result in iter_records_from_file(path)? {
117 match result {
118 Ok(sqllog) => sqllogs.push(sqllog),
119 Err(err) => errors.push(err),
120 }
121 }
122
123 Ok((sqllogs, errors))
124}