Gel/EdgeDB 运算符内省指南:用 EdgeQL 探查 schema::Operator 的完整结构
Gel/EdgeDB 运算符内省指南:用 EdgeQL 探查 schema::Operator 的完整结构
本指南讲解如何通过 Gel(原 EdgeDB)的 EdgeQL 内省能力,从系统 Schema 中查询运算符(Operator)的定义结构。你将掌握 schema::Operator 与 schema::Parameter 两个内省对象类型的字段构成,学会用 with module schema 查询 std::AND 等内置运算符的参数、类型修饰符与返回类型,并理解这些字段在源码中的底层语义。本文以 docs/reference/datamodel/introspection/operators.rst 为骨架展开,并结合仓库源码补充实现细节。
运算符内省概览
在 Gel 的 Schema 中,运算符(Operator)是一类可调用对象(Callable Object)。与函数(Function)类似,运算符同样拥有参数(params)和返回类型(return type),此外还带有运算符种类(operator kind)等独有特征。通过内省(Introspection),开发者可以用纯 EdgeQL 查询这些元数据,动态生成 API 文档、构建类型系统工具或在运行时验证某个运算符是否存在及如何调用。
内省的核心入口是系统模块 schema 中的 schema::Operator 对象类型。下列查询先用 ObjectType 探查 schema::Operator 自身的结构——包括它拥有的链接(links)与属性(properties):
with module schema
select ObjectType {
name,
links: {
name,
},
properties: {
name,
}
}
filter .name = 'schema::Operator';
执行结果:
{
Object {
name: 'schema::Operator',
links: {
Object { name: '__type__' },
Object { name: 'annotations' },
Object { name: 'params' },
Object { name: 'return_type' }
},
properties: {
Object { name: 'id' },
Object { name: 'name' },
Object { name: 'operator_kind' },
Object { name: 'return_typemod' }
}
}
}
可见 schema::Operator 的完整形状:
- links(链接):
__type__(对象自身类型)、annotations(注解)、params(参数列表)、return_type(返回类型); - properties(属性):
id、name(限定名,如std::AND)、operator_kind(运算符种类)、return_typemod(返回类型的类型修饰符)。
这四类元数据与函数的内省结构高度一致(可对照 functions.rst),区别仅在于多出了 operator_kind 属性。
参数结构:schema::Parameter
运算符与函数的另一个共通点是 params 链接。由于参数对运算符的签名(signature)至关重要,这里单独内省 schema::Parameter 的结构:
with module schema
select ObjectType {
name,
links: {
name,
},
properties: {
name,
}
}
filter .name = 'schema::Parameter';
执行结果:
{
Object {
name: 'schema::Parameter',
links: {
Object { name: '__type__' },
Object { name: 'type' }
},
properties: {
Object { name: 'default' },
Object { name: 'id' },
Object { name: 'kind' },
Object { name: 'name' },
Object { name: 'num' },
Object { name: 'typemod' }
}
}
}
schema::Parameter 的字段含义如下:
| 字段 | 类型 | 含义 |
|---|---|---|
id |
属性 | 参数对象的唯一标识 |
name |
属性 | 参数名(如 a、b) |
num |
属性 | 参数在签名中的序号(从 0 开始) |
kind |
属性 | 参数种类,见下文 ParameterKind 枚举 |
typemod |
属性 | 参数的类型修饰符,见下文 TypeModifier 枚举 |
default |
属性 | 默认值表达式,无默认值时返回空集 {} |
type |
链接 | 参数的类型(指向 schema::Type,可继续取 name) |
__type__ |
链接 | 参数对象自身的类型 |
在源码层面,参数对象对应 edb/schema/functions.py 中的 Parameter 类,它同时继承 so.ObjectFragment 与 so.Object,其 num、default、type、typemod、kind 均以 SchemaField 声明,与内省输出一一对应。
实战:内省 std::AND 运算符
将上述结构组合起来,即可完整探查一个具体运算符的全部元数据。以下查询内省内置的布尔与运算符 std::AND,同时获取运算符种类、注解、每个参数的细节以及返回类型:
with module schema
select Operator {
name,
operator_kind,
annotations: { name, @value },
params: {
kind,
name,
num,
typemod,
type: { name },
default,
},
return_typemod,
return_type: { name },
}
filter .name = 'std::AND';
执行结果:
{
Object {
name: 'std::AND',
operator_kind: 'Infix',
annotations: {},
params: {
Object {
kind: 'PositionalParam',
name: 'a',
num: 0,
typemod: 'SingletonType',
type: Object { name: 'std::bool' },
default: {}
},
Object {
kind: 'PositionalParam',
name: 'b',
num: 1,
typemod: 'SingletonType',
type: Object { name: 'std::bool' },
default: {}
}
},
return_typemod: 'SingletonType',
return_type: Object { name: 'std::bool' }
}
}
从结果可以解读出 std::AND 的完整定义:
name为限定名std::AND,即标准库模块std中的AND运算符;operator_kind为Infix(中缀),即写作a AND b的二元写法;annotations为空集合,说明该运算符未附加注解;- 两个参数
a、b均为PositionalParam(位置参数),序号分别为 0 和 1,typemod均为SingletonType(单例类型,即参数必须为单个值而非集合),类型均为std::bool,且都没有默认值; return_typemod为SingletonType,返回类型为std::bool。
这意味着 std::AND 的签名可写作 std::bool AND std::bool -> std::bool,与 EdgeQL 中布尔逻辑的语义完全一致。
源码视角:枚举与字段的底层语义
内省输出中的字符串值并非随意命名,而是直接来自源码中的枚举定义,位于 edb/edgeql/qltypes.py。
OperatorKind(运算符种类)——qltypes.py:
| 枚举值 | 说明 | 书写形态示例 |
|---|---|---|
Infix |
中缀运算符 | a AND b |
Prefix |
前缀运算符 | NOT a |
Postfix |
后缀运算符 | a IS NULL 等 |
Ternary |
三元运算符 | a ?? b 之外的 if/else 形式 |
在 edb/schema/operators.py 的 get_display_signature() 方法中,这四种 kind 被转换为可读的显示签名:中缀为 f'{params[0]} {name} {params[1]}',前缀为 f'{name} {params[0]}',后缀为 f'{params[0]} {name}',三元为 f'{name} ({", ".join(params)})'。
ParameterKind(参数种类)——qltypes.py:
PositionalParam:位置参数,按位置传入;NamedOnlyParam:仅限命名参数(NAMED ONLY);VariadicParam:可变参数(VARIADIC,在底层被包装为array类型,见 functions.py 的from_ast处理)。
TypeModifier(类型修饰符)——qltypes.py:
SingletonType:单例类型,要求输入/输出为单个值;SetOfType:集合类型(SET OF),对应如count(s)这类接受集合的参数;OptionalType:可选类型(OPTIONAL)。
Operator 对象的更多内部字段
除了内省可见的字段,Operator 类在源码中还携带了用于编译与优化运算符调用的内部字段,见 edb/schema/operators.py:
language:运算符实现所用的语言;from_operator/from_function:派生运算符的来源运算符或函数名;from_expr:是否由表达式派生;force_return_cast:是否强制对返回值做类型转换;code:运算符的实现代码;derivative_of:派生运算符的源运算符名(例如std::IN以std::=为源);commutator/negator:可交换运算符与否定运算符的关联名,用于查询优化;recursive:是否为递归运算符。
这些字段多为内部实现细节,不会全部暴露在 schema::Operator 的内省属性中,但它们决定了运算符如何被解析、编译与执行。在 edb/pgsql/compiler/expr.py 中,编译器正是依据 operator_kind 等字段将 EdgeQL 运算符翻译为底层 SQL 表达式。
使用注意事项
- 运算符内省查询需要
with module schema显式引入系统模块,否则Operator、ObjectType等类型名无法解析; params是链接而非属性,查询参数细节时必须展开为params: { ... }形状,且type本身也是一条链接,需继续取type: { name }才能获得类型名;default在无默认值时返回空集{},并非null;- 从源码看,用户自定义运算符目前不受支持:
OperatorCommand._cmd_tree_from_ast在非标准库、非测试模式下会抛出UnsupportedFeatureError('user-defined operators are not supported')(见 edb/schema/operators.py)。因此内省查询通常针对标准库与扩展中内置的运算符(如std::AND、std::=、std::+等)。
掌握上述模式后,你可以通过组合 name 过滤、params 展开与 operator_kind 筛选,对任意内置运算符做程序化内省,进而构建类型检查、文档生成或 IDE 补全等工具。