Daft SQL 标识符(Identifiers)大小写语义全解析:三种 Identifier Mode 的语法、配置与源码实现
2026/9/18 3:59:44 网站建设 项目流程

Daft SQL 标识符(Identifiers)大小写语义全解析:三种 Identifier Mode 的语法、配置与源码实现

【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft

Daft 的 SQL 标识符默认区分大小写,但内置了insensitive(不敏感)、sensitive(敏感)与normalize(归一化)三种解析模式,可通过SessionOptions在创建会话时配置。本指南以 docs/sql/identifiers.md 为主线,结合 daft/session.py、src/daft-session/src/options.rs、src/daft-catalog/src/bindings.rs 与 src/daft-sql/src/planner.rs 等源码,系统讲解 Daft 中标识符的书写语法、三种模式的行为差异、与主流引擎的兼容性对比,以及底层查找与归一化的实现原理,帮助你在跨目录、跨系统场景下正确选择标识符大小写策略。

Daft SQL 标识符的基本概念

在 Daft 中,标识符(Identifier)用于引用 SQL 语句中的各类对象——附加目录(attached catalogs)附加表(attached tables)列(columns)等。Daft 的标识符解析遵循以下核心原则:

  • 标识符默认区分大小写(case-sensitive)。
  • 解析行为受会话(Session)级配置的Identifier Mode控制,可切换为大小写不敏感或大小写归一化模式。
  • 无论何种模式,标识符本身都会被保留原始大小写(case-preserved),模式只影响“匹配”的方式:即用户写下的大小写与已注册名称如何对应。

这一设计在 src/daft-session/src/options.rs 中有直接对应:IdentifierMode枚举的三个变体InsensitiveSensitive(默认)、Normalize,其文档注释明确说明:

  • Insensitive:对ident AS alias中的ident做大小写不敏感查找,绑定到保留大小写的alias
  • Sensitive:对ident做大小写敏感查找,绑定到保留大小写的alias(默认值,标有#[default]);
  • Normalize:对ident做大小写敏感查找,但绑定到小写化的alias

这些模式在解析附加目录、附加表以及通过会话解析的列时统一生效,是理解 Daft SQL 大小写行为的总入口。

一个重要的前提警告

!!! warning "注意"

诸如 Iceberg、Unity 等目录(Catalog)拥有**互不兼容的解析规则**,Daft 无法保证所有目录实现之间的一致性。

这意味着标识符大小写语义无法跨目录完全统一。官方建议:在不同系统间协作时,为命名空间和表统一使用小写名称,并搭配identifier_mode = 'normalize',可以获得最一致的体验。

标识符的书写语法

Daft 支持三种标识符书写形式,规则与 SQL 标准一致:

普通标识符(regular identifier)——不加引号:

abc

带引号的定界标识符(delimited identifier)——使用双引号:

"abc"

限定标识符(qualified identifier)——用点号连接多个部分:

abc.xyz

混合限定标识符——各段可以分别选择是否加引号:

a."b".c

含特殊字符的定界标识符——双引号内可以容纳 emoji、空格、保留字等:

SELECT "🍺" FROM "🍻"

标识符规则

  • 标识符可以是不加引号的普通标识符,也可以是双引号包裹的定界标识符
  • 当文本是关键字(keyword)或包含特殊字符时,必须使用双引号包裹。
  • 普通标识符必须以字母字符或下划线'_'开头(字母字符按 Unicode 派生核心属性判定)。
  • 普通标识符只能包含字母数字、'$''_'字符。

从源码角度看,定界标识符只接受双引号。在 src/daft-sql/src/planner.rs 的normalize函数中,SQL parser 解析出的每个ObjectNamePart会按其quote_style分派:

  • Some('"'):双引号定界标识符,原样保留ident.value.clone()),不经过归一化;
  • None:普通标识符,送入会话的normalizer处理;
  • Some(c):其他引号风格(如反引号、单引号),直接抛出unsupported_sql_err,提示 “Daft only supports delimited identifiers with double-quotes”。

也就是说,Daft 目前只支持双引号作为定界符,SELECT \col`SELECT 'col'` 这类写法会报错。

三种 Identifier Mode 的行为与兼容性

Daft 提供三种标识符模式,各自的行为与主流引擎兼容性总结如下:

模式行为兼容性
insensitive所有标识符按大小写不敏感匹配,名称保留原始大小写duckdbsparkunity
sensitive所有标识符按大小写敏感匹配,名称保留原始大小写pythoniceberg
normalize不加引号的普通标识符归一化为小写;双引号定界标识符保留原始大小写trinopostgresdatafusion

三种模式的底层差异可以直接映射到 src/daft-session/src/session.rs 的normalizer()实现:

  • InsensitiveSensitive模式:normalizer返回恒等函数str::to_string,即不修改任何大小写
  • Normalize模式:normalizer返回str::to_lowercase,即把普通标识符统一小写化

模式的底层实现:Bindings 的双重映射

大小写匹配的核心数据结构是 src/daft-catalog/src/bindings.rs 中的Bindings<T>,它维护了两套映射:

  • bindings: HashMap<String, T>:以原始大小写保存的绑定(lvalue,即对象被注册时的名称);
  • aliases: HashMap<String, HashSet<String>>:以小写化别名name.to_lowercase(),即alias()函数)为键,指向所有匹配该别名的原始名称。

查找(rvalue,即用户写下的标识符)时依据LookupMode分派(见 bindings.rs):

  • LookupMode::Insensitive:通过小写别名集合查找到所有候选绑定并全部返回;
  • LookupMode::Sensitive:直接在bindings中按精确大小写查找。

LookupModeIdentifierMode的映射关系定义在 options.rs:

match self.identifier_mode { IdentifierMode::Insensitive => LookupMode::Insensitive, IdentifierMode::Sensitive => LookupMode::Sensitive, IdentifierMode::Normalize => LookupMode::Sensitive, // 归一化在 SQL 规划阶段完成 }

注意:Normalize模式的查找本身仍是大小写敏感的,小写归一化发生在 SQL 规划阶段——即 planner.rs 的normalize()先把普通标识符转成小写,再以这个已小写的名字去执行敏感查找。正因为如此,normalize模式下双引号定界标识符会被跳过归一化(见上文),从而实现“普通标识符小写化、定界标识符保留大小写”的混合语义。

查找结果唯一性:歧义处理

由于insensitive模式可能命中多个大小写不同的绑定,src/daft-session/src/session.rs 中对目录、Provider、表的查找都做了统一处理:

  • 命中 0 个:返回None(未找到);
  • 命中 1 个:直接返回该对象;
  • 命中多个:抛出ambiguous_identifier_err!歧义错误。

例如同一命名空间下同时注册了Ordersorders两张表,在insensitive模式下查询orders会因命中两个候选而报歧义错误。一个值得注意的例外是函数名get_functionget_aggregate_function(见 session.rs)始终使用LookupMode::Insensitive查找,注释明确说明 “Functions always use case-insensitive lookup (SQL convention)”,即 SQL 函数名大小写不敏感是 Daft 的固定约定,不受 Identifier Mode 影响。

配置方式与当前默认值

标识符模式在会话(Session)级别配置,当前版本默认使用case-sensitive(大小写敏感)模式。

!!! note "未来配置"

用于配置标识符模式的 Python API 计划在后续版本提供;目前统一使用默认的大小写敏感模式。

也就是说,当前仓库代码(options.rs 中Sensitive带有#[default]属性)中,会话创建时Options派生Defaultidentifier_mode即为SensitiveSessionOptions中可配置标识符模式的能力已经为未来版本预留(src/daft-session/src/options.rs 顶部还有 “TODO make env variables” 的注释,暗示未来可能通过环境变量开放配置)。

如果你需要了解 Daft SQL 会话与目录的完整用法,可以参考 会话与目录指南;Session类的 Python 接口定义在 daft/session.py。

多系统协作的实践建议

结合三种模式的语义与兼容性,官方给出了跨系统协作的最佳实践:

  • 首选normalize模式:当需要同时对接 Trino、PostgreSQL、DataFusion 等“小写归一化”生态,或者需要保证多系统行为一致时,identifier_mode = 'normalize'是最稳妥的选择;
  • 命名空间与表统一小写:在跨多个系统的场景下,为命名空间(namespace)和表(table)统一使用全小写名称,能获得最一致的体验;
  • 按生态选模式:如果主要对接 DuckDB / Spark / Unity Catalog,insensitive更贴近其行为;如果主要使用 Python 数据类库或 Iceberg,默认的sensitive与其语义一致;
  • 注意目录差异:Iceberg、Unity 等目录有自己的解析规则,与 Daft 会话级模式无法保证完全一致,因此“模式只是起点,目录实现才是最终裁决者”。

同时可以留意:因为insensitive模式下大小写不同的重名对象会触发歧义错误,在开启该模式前应检查是否已存在仅大小写区分的同名对象。

总结

Daft 的标识符体系围绕“大小写保留 + 模式化匹配”设计:sensitive(默认)做精确匹配,insensitive借助小写别名做不敏感匹配,normalize在 SQL 规划阶段把普通标识符统一小写、同时尊重双引号定界标识符。从 bindings.rs 的双映射结构,到 options.rs 的模式默认值,再到 planner.rs 的归一化分派,这套机制贯穿了 Daft SQL 从解析到绑定的完整链路。理解三种模式的差异与适用生态,是你在多目录、多引擎场景下写出行为可预期的 SQL 的关键一步。

【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询