1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! Command 序列契约(方案 A 的「core 产出指令、Host 执行」边界)
//!
//! core 不持有 MongoDB 驱动。读/写路径被拆成两步:
//!
//! ```text
//! Host: plan = core.plan_query(gql, params, ctx) // 纯逻辑,产出命令
//! Host: for cmd in plan.commands { driver.execute(cmd) } // 唯一 IO 边界
//! Host: items = core.finalize(plan, items) // 回喂 core 做后处理
//! ```
//!
//! 命令 JSON 形状(语言无关,Node/Python 两侧共用):
//!
//! `source` / `namespace` / `collection` 为**定位三元组**,由生成该命令的 schema 一次填入
//! (`source` 缺省 `"default"`,`namespace` 缺省 `null` = 连接自身默认库/schema)。
//! Host 依据 `cmd.source`(而非 collection 反查)选择连接;`namespace` 的用法见
//! `multi-datasource-routing-plan.md`(Mongo client 型 source / SQL qualified 表名)。
//!
//! ```json
//! { "kind": "find", "source": "default", "namespace": null, "collection": "u", "filter": {...}, "projection": {...}|null }
//! { "kind": "aggregate", "source": "pg1", "namespace": "app", "collection": "u", "pipeline": [ ... ] }
//! { "kind": "countDocuments", "source": "default", "namespace": null, "collection": "u", "filter": {...} }
//! { "kind": "findOne", "source": "default", "namespace": null, "collection": "u", "filter": {...}, "projection": {...}|null }
//! { "kind": "insertOne", "source": "default", "namespace": null, "collection": "u", "doc": {...} }
//! { "kind": "insertMany", "source": "default", "namespace": null, "collection": "u", "docs": [ ... ] }
//! { "kind": "findOneAndUpdate", "source": "default", "namespace": null, "collection": "u", "filter": {...}, "update": {...}, "options": {...} }
//! { "kind": "updateMany", "source": "default", "namespace": null, "collection": "u", "filter": {...}, "update": {...} }
//! { "kind": "deleteMany", "source": "default", "namespace": null, "collection": "u", "filter": {...} }
//! ```
//!
//! 两阶段查询(`$lookup` + `$skip/$limit`)的命令序列里,第二条 aggregate 的
//! `$match._id.$in` 为占位符 [`PHASE1_IDS`],Host 需用第一条命令返回的
//! `_id` 数组(**保持顺序**)替换后再执行;执行完调用 [`restore_sort_order`] 还原排序。
//!
//! 写路径占位符:mutation 步骤间的父子依赖用 `{{step.<N>._id}}`
//! ([`step_id_placeholder`])表达——第 N 步执行结果文档的 `_id`,Host 在该步
//! 执行完成后回填到后续命令。需新生成的 `_id` 不用占位符:由 Host 供给
//! `new_ids` / `new_id` 参数,core 按序消费(core 无随机源)。
//!
//! 子模块划分:
//! - [`cmd`]:命令构造 + 数值工具
//! - [`query`]:读路径计划(分页 / 两阶段 / 排序还原)
//! - [`count`]:列表 + total 计划
//! - [`write`]:插入与简单查询计划 + 写权限探针
//! - [`mutate`]:update / updateMany / remove / upsert / insertMany 计划
//! - [`mutation`]:mutation 递归规划(父子文档步骤序列)
//! - [`finalize`]:结果回喂(后处理 / asyncFn 桥 / 剥离注入)
use crateContext;
use crate;
pub use ;
pub use ;
pub use ;
pub use ;
pub use plan_mutation;
pub use ;
pub use ;
/// 权限拒绝哨兵:Host 需映射为各自的 PermissionError。
///
/// 所有权限类错误统一携带 `ERR_PERMISSION:` 稳定前缀 —— Host 按**前缀**识别
/// 权限错误(构造后剥离前缀),不再对具体中文文案做脆弱匹配(core 文案可自由调整)。
pub const ERR_PERM_PREFIX: &str = "ERR_PERMISSION:";
pub const ERR_PERMISSION: &str = "ERR_PERMISSION:无访问权限";
pub const ERR_NO_WRITE: &str = "ERR_PERMISSION:无写入权限";
pub const ERR_NO_DELETE: &str = "ERR_PERMISSION:无删除权限";
pub const ERR_NO_BATCH_WRITE: &str = "ERR_PERMISSION:无批量写入权限";
/// `require_context` 开启时 ctx 缺失的拒绝哨兵(fail-secure;Host 按前缀识别,
/// 一般映射为 500 配置/契约错误而非 403 —— 这是调用方合约违反,不是用户无权限)。
pub const ERR_NO_CONTEXT: &str =
"ERR_NO_CONTEXT:require_context 已开启,调用必须携带用户上下文(内部调用请传 {\"internal\": true})";
/// fail-secure 门禁:`require_context` 开启时拒绝 `ctx: None`(默认关闭时零开销放行)。
///
/// 挂在所有含 ctx 的 plan 公开入口顶部(读/写/联邦),保证「绑定层无论怎么调,
/// 只要开关开着,缺上下文一律显式报错」——而非静默按系统调用放行。
/// 档位拒绝哨兵:text2query 档命中硬限制/收缩项时的稳定前缀。
///
/// Host 按**前缀**映射为各自的 ProfileViolation(构造后剥离前缀),
/// 不对中文文案做脆弱匹配(core 文案可自由调整)——与 `ERR_PERM_PREFIX` 同构。
pub const ERR_TEXT2QUERY: &str = "ERR_TEXT2QUERY:";
/// text2query 档硬限制(单点定义,Host 可读;取值**严于** standard)
///
/// - 单次取数行数:严于 [`MAX_PAGE_SIZE`](5000)—— AI 问数交互式结果规模;
/// - 关系嵌套深度:严于 `pipeline::MAX_DEPTH`(10)—— AI 生成的关系嵌套实用上限;
/// - 联邦单源行数:严于 `federation::MAX_FEDERATION_ROWS`(100000)。
pub const T2Q_MAX_ROWS: f64 = 1000.0;
pub const T2Q_MAX_DEPTH: usize = 3;
pub const T2Q_MAX_FEDERATION_ROWS: usize = 10_000;
/// text2query 档强制上下文(叠加于 [`ensure_context`] 之上)。
///
/// `require_context` 与档位**正交但叠加**:text2query 档等效强制开启,
/// 退出档位后恢复用户原设置(见 Host 的 `text2query()` 上下文管理器)。
/// text2query 档禁用某项能力(standard 档放行)。
///
/// 用于「DB 独有能力 / 直通 / 受限参数」类收缩项;standard 档不做任何拦截。
/// text2query 档功能收缩 Err(无 `Registry` 场景:仅持 `Profile` 的形状校验函数用)。
///
/// 与 [`forbid_t2q`] 同构、文案形态单点收敛:
///
/// - 以 [`ERR_TEXT2QUERY`] 前缀开头(Host 按前缀映射 ProfileViolation 并 emit
/// `profile_blocked`,不匹配中文文案);
/// - `[<feature>]` 方括号包裹门禁项名(Host 正则 `` \[(.+?)\] `` 提取为告警
/// `feature` 字段,缺括号即 feature 留白);
/// - `detail` 携带原语义句与收缩编号(U1~U4 / 8c-2),供测试与 SKILL 文档子串断言。
/// text2query 档禁止携带 `route_override`(受信来源门禁)。
///
/// `route_override`(`{source, namespace}`)是多租户路由覆盖的**受信服务端参数**,
/// 禁止透传用户输入 / AI 生成(否则可被用于跨源路由,CWE-639)。standard 档维持
/// 受信可用;text2query 档只要携带即拒。判决逻辑单点在此,绑定层(core-py/core-node)
/// 在 `with_route_override` 唯一注入点调用,覆盖读 / 写全部 plan 入口。
/// 两阶段查询中由 Host 替换的阶段一 `_id` 顺序数组
pub const PHASE1_IDS: &str = "{{phase1.ids}}";
/// mutation 第 `idx` 步执行结果文档 `_id` 的占位符
/// 读取上限(pageSize 封顶,防拖库)
pub const MAX_PAGE_SIZE: f64 = 5000.0;