9 个重大版本

0.10.0 2024年6月24日
0.8.0 2024年4月2日
0.7.0 2024年3月7日
0.6.0 2023年11月14日

#1224解析器实现


parquetry-sort 中使用

自定义许可

19KB
199

Rust Parquet 代码生成

Rust build status Coverage status

本项目提供了使用 Rust 实现的 Parquet 文件工具,并使用 Arrow。它包括一个代码生成包 (parquetry-gen) 和一个必需的小型运行库 (parquetry)。

请注意,此软件不是“开源”的,但源代码可供个人、非营利组织和工人拥有的企业使用和修改(有关详细信息,请参阅下面的许可部分)。

目录

示例

给定如下架构

message user {
    required int64 id (integer(64, false));
    required int64 ts (timestamp(millis, true));
    optional int32 status;

    optional group user_info {
        required byte_array screen_name (string);

        optional group user_name_info {
            required byte_array name (string);

            optional group user_profile_info {
                required int64 created_at (timestamp(millis, true));
                required byte_array location (string);
                required byte_array description (string);
                optional byte_array url (string);

                required int32 followers_count;
                required int32 friends_count;
                required int32 favourites_count;
                required int32 statuses_count;

                optional group withheld_in_countries (list) {
                    repeated group list {
                        required byte_array element (string);
                    }
                }
            }
        }
    }
}

代码生成器将生成以下 Rust 结构体

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct User {
    pub id: u64,
    #[serde(with = "chrono::serde::ts_milliseconds")]
    pub ts: chrono::DateTime<chrono::Utc>,
    pub status: Option<i32>,
    pub user_info: Option<UserInfo>,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct UserInfo {
    pub screen_name: String,
    pub user_name_info: Option<UserNameInfo>,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct UserNameInfo {
    pub name: String,
    pub user_profile_info: Option<UserProfileInfo>,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct UserProfileInfo {
    #[serde(with = "chrono::serde::ts_milliseconds")]
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub location: String,
    pub description: String,
    pub url: Option<String>,
    pub followers_count: i32,
    pub friends_count: i32,
    pub favourites_count: i32,
    pub statuses_count: i32,
    pub withheld_in_countries: Option<Vec<String>>,
}

它还将为 User 生成一个 parquetry::Schema trait 实例,并包含读取和写入 Parquet 文件值的代码。

依赖项

所有使用都需要使用 parquetryparquetchronolazy_static 作为运行时依赖项。

如果配置中启用了 serde_support 标志(默认情况下是启用的),则需要依赖 serde 并启用 derive 功能。

如果配置中启用了 tests 标志(也是默认设置),您需要将 bincode(最近版本的 2.0.0-rc.x,并启用 serde 功能)、tempdirquickcheck 添加到您的 dev-dependencies

使用方法

example 目录提供了一个相当简化的示例,生成的代码也在这里进行检查。在大多数情况下,您可能只需要像以下这样的 build.rs

use std::{fs::File, io::Write};

fn main() -> Result<(), parquetry_gen::error::Error> {
    for schema in parquetry_gen::ParsedFileSchema::open_dir(
        "src/schemas/",
        Default::default(),
        Some(".parquet.txt"),
    )? {
        println!("cargo:rerun-if-changed={}", schema.absolute_path_str()?);
        let mut output = File::create(format!("src/{}.rs", schema.name))?;
        write!(output, "{}", schema.code()?)?;
    }

    Ok(())
}

默认情况下,生成的代码使用 prettyplease 格式化,并注释说明不应使用 Rustfmt 格式化,但如果您想自己使用 Rustfmt,可以在配置中将 format 设置为 false。

测试

默认配置将生成使用 QuickCheck 生成任意值并确认它们正确序列化和反序列化的测试代码。

测试代码生成当前不支持某些类型。具体来说,不支持浮点数字列、固定长度的字节数组和时间戳。如果您的模式包含这些类型中的任何一种,生成的测试代码将无法编译,您需要在配置中禁用 tests 标志(如果您愿意,也可以提出一个问题)。

生成的测试代码不会为浮点类型生成 NaN 值。如果您想确认您的系统正确处理这些值,您需要手动进行。

默认情况下,为您的类型生成的任意值可能非常大。如果您的测试速度太慢,您可能需要将 QUICKCHECK_GENERATOR_SIZE 环境变量设置为一个较小的值(例如 1020)。

状态和范围

这些工具支持具有大多数物理和逻辑类型的模式,以及列表、可选字段和结构的任意嵌套。

我可能在某个时候添加的功能

  • 8 位和 16 位逻辑整数类型(很简单,我只是还没有需要它们)
  • DATETIMEINTERVALUUID(与之前相同)
  • DECIMAL(至少对于 INT32INT64 表示形式)
  • ENUM(在这个上下文中实际上并不很有用,因为模式没有枚举变体?)
  • 映射(需要更多的工作,但可能值得拥有)

可能永远不会支持的功能

此项目与 parquet_derive 在几个方面有所不同

  • 两者都生成读写代码,但这个项目是从模式生成Rust结构体,而不是相反。
  • 本项目不使用parquet::record::RecordWriter(它似乎并不那么有用,而且我希望有更多的灵活性)。
  • 本项目支持嵌套结构。

通常这两个项目有不同的用途,如果你只是想将一些Rust值存储在Parquet中,我建议选择parquet_derive

警告

名称冲突

目前还没有对与Rust关键字、标准库中的名称等冲突的字段名称进行特殊处理。组名也应在模式内唯一。代码生成器还会生成非公共结构体,其名称可能在理论上与用户生成的代码冲突。

在这些情况中,问题应该是立即明显的,因为生成的代码通常根本无法编译。检查这些冲突并提供更好的错误或允许用户有更多控制权来避免这些问题并不困难,但这并不是我的优先事项。

构造函数

生成的代码包括每个结构体的fn new构造函数,这些构造函数会将任何DateTime<Utc>的精度截断到列表示支持的子秒位数。这些构造函数还会检查任何字符串参数是否包含空字节,并在发现时返回错误。

如果你不希望有任何一种行为,你可以手动构造结构体,因为所有字段始终都是公开的。

Serde实例

默认情况下,生成的代码将包括用于序列化的派生Serde实例。这些实例将使用由模式指定的时间单位(毫秒或微秒)为类型为DateTime<Utc>Option<DateTime<Utc>>的字段使用,但类型为Vec<DateTime<Utc>>的字段将使用DateTime<Utc>的默认Serde序列化编码。

这没有特别的原因,只是因为chrono::serde只提供了例如ts_millisecondsts_milliseconds_option函数,而运行时库可以轻松提供自己的ts_milliseconds_vec,这些情况将使用它。

性能

我没有尝试优化生成的代码,除非性能成为我的用例的问题。

许可

本软件根据反资本主义软件许可(v. 1.4)发布。

依赖项

~16–23MB
~481K SLoC