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}