13 个版本 (破坏性更新)

0.10.0 2022年10月20日
0.8.0 2022年5月30日
0.7.0 2021年12月24日
0.6.0 2021年5月28日
0.2.0 2020年7月31日

#1806 in 数据库接口

MIT 以及可能 GPL-3.0-or-later

72KB
1.5K SLoC

parse-mediawiki-sql

这是一个用于快速解析来自 Wikimedia 导出 的 SQL 文件的库。它经常与来自英语 Wiktionary 导出的某些文件一起使用,但也应该适用于其他维基的导出。

crates.io docs.rs

背景

维基媒体提供可以由数据库服务器执行的 SQL 文件,以创建各种 MediaWiki 数据库表 的副本。但执行创建一些大表的脚本非常慢,而对于重复性工作,运行一个通过解析脚本提取信息的程序会快得多。例如,解析所有 page.sqltemplate_redirects 示例程序大约需要 20 秒,而使用 mariadb 执行 page.sql 创建 page 需要更长的时间,我尝试的时候超过了一个小时。

这个库某种程度上是我之前 Lua 库(parse_sql_dump)的改写,而那个库又受到WikiUtils 的启发,一个链接到维基百科帮助页面 的库,它使用正则表达式解析 SQL 文件。我的 Lua 库使用 LPeg(我非常喜欢)解析文件。像 Rust 库一样,它有一个迭代器接口,但当我用它解析 page.sql 时,它经常消耗我微薄的 RAM 供应,导致我的计算机进入交换空间并出现故障,不得不重启。

因此,我最终创建了一个更节省内存的 Rust 库。在 Rust 中,通过让解析器的输出从输入借用,可以相对容易地最小化解析器的内存使用。使用内存映射,操作系统处理分配和释放解析器输入的内存。

入口点是 iterate_sql_insertions,它接收一个 SQL 脚本字节数组切片(&[u8])并生成一个结构体,该结构体作为表示 INSERT 语句中的行的结构体的迭代器。这些结构体位于 parse_mediawiki_sql::schemas 中,它们的字段类型可以在 parse_mediawiki_sql::types 中找到。从 iterate_sql_insertions 生成的结构体从字节数组切片中借用,因此在 for 循环中必须作为可变引用进行迭代:for _ in &mut parse_mediawiki_sql::iterate_sql_insertions(&sql_script_byte_slice) { /* ... */ }

结构体字段的名称基于数据库表中的字段名称,但去除了前缀。同一表中与另一表中的字段相关的字段使用相同的数据类型,并且数据库中为 intbinary 类型的几个字段由更具体的 Rust 类型表示。

例如,Page 结构体表示 page中的一行。其字段 idnamespacetitle(其类型分别是 PageIdPageNamespacePageTitle))表示 page_idpage_namespacepage_title 字段。在 Redirect 结构体中的 from 字段(表示 rd_from)引用了由其 page_id 字段标识的 page 表中的一行,因此它同样是一个 PageId

如果可能,字段将从输入中借用。如果一个 binary 类型包含有效的 UTF-8,它将表示为 String&str,否则表示为一个 Vec<u8>。如果一个 binary 字段是有效的 UTF-8 并且不会包含任何转义(如 \'),则将其解析为从输入 &[u8] 借用的 &str

由于一些SQL转储文件,如page.sql,可能非常大,我使用了一个便利函数来将文件内存映射,以避免将其完全读入内存,并为迭代器的项目提供借用的东西。示例使用utils::memory_map(由功能utils启用),该功能使用memmap存储库,但具有更友好的错误类型。

示例

生成包含所有重定向页面标题的Vec

use parse_mediawiki_sql::{
    iterate_sql_insertions,
    schemas::Page,
    field_types::{PageNamespace, PageTitle},
    utils::memory_map,
};
use std::fs::File;
let page_sql = unsafe { memory_map("page.sql")? };
let redirects: Vec<(PageNamespace, PageTitle)> =
    iterate_sql_insertions(&page_sql)
        .filter_map(
            |Page { namespace, title, is_redirect, .. }| {
                if is_redirect {
                    Some((namespace, title))
                } else {
                    None
                }
            },
        )
        .collect();

当前用途

template_redirect 示例,可以使用以下命令运行: cargo run --release --example template_redirect path/to/page.sql path/to/redirect.sql > template_redirects.json,生成一个JSON对象,其中包含特定转储版本的所有模板重定向。此程序在Toolforge上的Templatehoard 工具中使用,该工具提供来自英语维基词典的模板实例的转储文件,包括模板及其重定向。

待办事项

  • 允许解析直接提供的.sql.gz文件(目前,它们必须先解压)
  • parse_sql_insertions返回的迭代器中获得更友好的错误。
  • 检查迭代器是否解析了整个SQL插入集。

依赖关系

~2.5–3.5MB
~58K SLoC