执行、结果与错误
公开执行入口:
const result = await Logicform.execute(logicform, options);
它返回 ExecuteSuccessResult | ExecuteFailureResult。公开入口会把执行错误转换为失败结果,而不是要求调用方依赖异常捕获判断业务执行是否成功。
成功结果
interface ExecuteSuccessResult {
result: DataRow[];
schema: ResolvedSemanticSchema;
normedLogicform: LogicformType;
sqls: ExecutedSql[];
lineage: QueryLineage;
provider: 'mysql' | 'doris' | 'starrocks' | 'postgresql'
| 'clickhouse' | 'snowflake' | 'oracle';
}
result
最终数据行数组。每行都包含执行器拥有的 _id:
- 有 groupby 时,由所有分组输出值构成稳定行身份;
- 明细查询可使用允许输出的 ID Property 形成身份;
- 单聚合行固定为
'0';
- 无可用稳定身份的明细行使用随机 UUID。
_id 不会加入 schema.properties,因为它不是业务结果列。
object/entity 输出已经被填充为实体对象;boolean、外部 Semantic Type 等结果 hook 也已执行完成。
schema
描述本次动态结果列,而不是原始输入 Schema。它包含每个输出的:
interface ResultProperty {
name: string;
type: string;
primal_type: 'string' | 'number' | 'boolean' | 'date' | 'object';
isArray: boolean;
ref?: string;
granularity?: TimeGranularity;
comparable?: ComparableCategoryDefinition;
temporal?: ResultPropertyTemporalMetadata;
}
Operator 产生的计算列也会出现在这里。周期相关结果可能包含:
interface ResultPropertyTemporalMetadata {
kind: 'period-boundary' | 'period-shift' | 'date-bucket' | 'lifecycle';
boundary?: 'start' | 'end';
sourceProperty?: string;
requestedRange?: { gte?: string; lte?: string };
effectiveRange?: { gte?: string; lte?: string };
resolvedAt?: string;
shiftedBy?: { granularity: TimeGranularity; offset: number };
resolvedAtByGroup?: Array<{
group: Record<string, unknown>;
date: string;
}>;
}
系统实际采用的规范化 LogicForm。它可能与输入有以下可预期差异:
- Property ID 转换为规范引用;
- 补充缺失的 PredItem
name;
- 日期值展开为明确上下界;
entity_id 转换为身份 Query;
- 关系嵌套 Query 转换为路径条件;
- 权限 Query 与用户 Query 合并;
- 分页默认
page: 1;
limit: -1 被移除。
需要审计查询口径时,应保存这个字段。
sqls
interface ExecutedSql {
type: 'computation' | 'semantic-resolution';
sql: string;
parameters: readonly unknown[];
displaySql: string;
}
computation:计算 LogicForm 结果的 SQL;
semantic-resolution:解析语义输入/输出的辅助 SQL,例如最新数据日期和实体填充;
sql + parameters:Provider 实际执行的参数化请求;
displaySql:把参数按当前方言渲染后的诊断文本,永远不会被 SemanticDB 执行。
displaySql 可能包含敏感值,只能写入受控的诊断或审计渠道。命中 Query Cache 时,对应 SQL 仍会保留在 sqls 中。
lineage
interface QueryLineage {
source: {
schemaId: string;
schemaName: string;
physicalTable: string;
provider: string;
};
outputs: Array<{
name: string;
sources: Array<{
schemaId: string;
propertyId: string;
propertyName: string;
physicalColumn: string;
}>;
transformations: string[];
}>;
filters: LineageColumnSource[];
}
它说明每个结果列和过滤条件来自哪些 Schema/Property/物理列,以及经历了哪些 Operator 或组合变换。
provider
本次执行实际使用的数据库 Provider kind。
失败结果
interface ExecuteFailureResult {
errorCode: '100';
error: string;
result: [];
schema?: ResolvedSemanticSchema;
normedLogicform: LogicformType;
sqls: ExecutedSql[];
provider?: string;
lineage?: QueryLineage;
}
已经产生的规范化结果、动态 Schema、SQL、Provider 和 Lineage 会尽量保留,便于定位失败阶段。error 当前包含错误消息和 stack,可能暴露内部路径,不应原样展示给终端用户。
判断方式:
const response = await Logicform.execute(logicform, options);
if ('errorCode' in response) {
logger.error({
error: response.error,
normedLogicform: response.normedLogicform,
sqls: response.sqls,
lineage: response.lineage,
});
return;
}
render(response.result, response.schema);
SemanticDBError
扩展 Hook 和底层 API 可以抛出:
class SemanticDBError extends Error {
code: string;
details?: Record<string, unknown>;
}
code 是稳定的机器可读分类,details 提供字段、Schema、Mapping 或 Operator 等上下文。公开 Logicform.execute() 最终仍按上述失败结果返回;onProgress 的 failed 事件可以收到原始 Error 实例。
常见错误类别:
进度回调
onProgress(event) {
console.log(event.stage, event.partialResult);
}
事件顺序:
每个事件都有累计的 partialResult,因此观察者不需要自己合并前序事件。回调可返回 Promise,执行会等待它完成;回调抛错也会使本次执行失败。
取消
const controller = new AbortController();
const pending = Logicform.execute(logicform, {
...options,
signal: controller.signal,
});
controller.abort();
系统会在关键阶段检查 signal,并将取消作为失败结果返回。Provider 能否立即中断正在进行的网络请求取决于具体数据库驱动。
数据权限契约
type Permissions = Record<string, {
rowPermissions?: QueryType;
columnPermissions?: { whitelist: string[] };
}>;
- 传入 Permissions 后,它的 key 是可访问 Schema allowlist。
rowPermissions 与用户 Query 使用 AND 合并。
columnPermissions.whitelist 限制直接或间接引用的字段。
- 关系查询、子查询、周期组件和实体填充都会继续应用相应 Schema 的权限。
- 权限不能被 LogicForm 中的同名字段覆盖。
输出安全建议
- 将
normedLogicform 和 lineage 用于审计与复现。
- 将
sql 与 parameters 分开保存;仅在可信环境查看 displaySql。
- 不向终端用户原样返回 stack、连接信息或物理表名。
- 对
result.schema 驱动展示和格式化,不根据某个 Operator 名硬编码列类型。
- 业务调用方应显式提供输出
name,避免自动名称变化影响下游字段绑定。