# Data Proxy API 调用指南

> 最后更新：2026-08-10
> 本文是 `nutshell-skills` Data Proxy skill 的调用契约来源。只有已合并、已部署并完成环境验收的接口，才能在 skill 中标记为可用。

## 1. Endpoint、认证与 ID

Data Proxy 的公网 URL 直接基于 `${CLINIC_ENDPOINT}`，不要使用已经包含 `/clinic/api/v1` 的 `${CLINIC_API_BASE}`。

| 环境 | `CLINIC_ENV` | `CLINIC_ENDPOINT` |
| --- | --- | --- |
| Dev | `dev` | `https://dev-clinic.pingcap.com` |
| Staging | `staging` | `https://staging-clinic.pingcap.com` |
| Prod | `prod` | `https://clinic.pingcap.com` |

```bash
# 在已安装的 clinic-api skill 根目录执行。
export CLINIC_ENV=staging
export ENV_FILE=.env.staging
source scripts/clinic-shell.sh
get_clinic_auth

# get_clinic_auth 按 CLINIC_ENV 设置 CLINIC_ENDPOINT，并从显式选择的 ENV_FILE 读取 CLINIC_API_KEY。
# 所有请求使用：Authorization: Bearer ${CLINIC_API_KEY}
```

新 `/data-proxy/api/v1` 路径中的 `{orgID}` 是 **TiDB Cloud/DBAAS org ID**，不是 Clinic 数据库中的 organization 主键。`{clusterID}` 通常是 TiDB Cloud logical cluster ID；BYOC Slow Query、BYOC Top SQL 和 BYOC SQL Statements 也显式支持属于该 org 的 physical `byoc-host` cluster ID。首批 BYOC Continuous Profiling 则只接受 physical `byoc-host` cluster ID，不接受 logical BYOC cluster ID。

通过 legacy Dashboard metadata 解析 ID 时，canonical `{orgID}` 对应响应中的 `tenantID`；响应中的 `orgID` 是 Clinic internal ID，只用于 legacy Clinic path。不要把 Dashboard `orgID` 直接放进 canonical path，否则 ownership 校验会返回 `404`。

仅对本次新增的 `/data-proxy/api/v1` resource API，调用方不得传递 `X-Provider`、`X-Region`、`X-Project-Id`、`X-Deploy-Type`、`X-Cluster-Id` 或 `control_plane_info` 来选择数据源；placement 和 effective target 均由服务端解析。既有 API 的参数保持原契约，例如 serverless parent metrics 仍必填 `control_plane_info`。

当前 `/data-proxy/api/v1` canonical API 面向使用 **internal Clinic key** 的 Investibook/受信服务调用方。它们尚未开放为普通 Clinic user 或 DBAAS credential 的公共租户 API；未来开放前仍需由统一鉴权层完成 principal-to-org 授权。本文中的 Batch 1 warehouse API 继续要求纯数字 `{clusterID}`，以保持现有物理表 identifier 的安全兼容。

## 2. Resource API 契约与环境状态

| 能力 | Method 与 Data Proxy path | Query | 当前环境状态 |
| --- | --- | --- | --- |
| 精确 cluster metadata | `GET /data-proxy/api/v1/clusters/{clusterID}` | 无；internal/global caller 使用 | Staging Pass；Prod Pass |
| Org cluster list | `GET /data-proxy/api/v1/orgs/{orgID}/clusters` | 可选 `page=1`、`pageSize=50`、`query`、`showDeleted=false` | Staging Pass；Prod Pass |
| Org cluster detail | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}` | 无 | Staging Pass；Prod Pass |
| Activity Events | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/activities` | 必填 `begin_ts,end_ts`；可选 `catalog,type,name,severity,limit,cursor` | Staging/Prod Pass；两环境均未覆盖真实 cursor |
| Components | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/components` | 无 | Staging Pass；Prod Dedicated Pass |
| Top RU summary | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/top-ru/summary` | 必填 `start,end,top`；可选 `order_by,user,data_source,step` | Staging Partial Pass；Prod Premium/Essential Pass |
| Top RU summary by plan | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/top-ru/summary-plan` | 必填 `start,end,top`；可选 `digest,order_by,user,data_source,step` | Local Pass；Staging/Prod Pending |
| Top RU user stats | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/top-ru/user-stats` | 必填 `start,end`；可选 `top` | Staging Partial Pass；Prod Premium/Essential Pass |
| BYOC Slow Query fields | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/slow-queries/available-fields` | 无 | Local Pass；Staging/Prod Pending |
| BYOC Slow Query list | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/slow-queries` | 必填 `begin_time,end_time`；其余见下文 | Local Pass；Staging/Prod Pending |
| BYOC Slow Query detail | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/slow-queries/detail` | 必填 `digest,timestamp,connect_id` | Local Pass；Staging/Prod Pending |
| BYOC Top SQL instances | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/top-sql/instances` | 必填 `start,end` | Local Pass；Staging/Prod Pending |
| BYOC Top SQL summary | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/top-sql/summary` | 必填 `start,end,window,top,instance,instance_type` | Local Pass；Staging/Prod Pending |
| BYOC Continuous Profiling groups | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/continuous-profiling/group-profiles` | 必填 `begin_time,end_time` | Local Pass；Staging/Prod Pending |
| BYOC Continuous Profiling group detail | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/continuous-profiling/group-profiles/{ts}` | 无 query；`ts` 在 PATH | Local Pass；Staging/Prod Pending |
| BYOC Statements fields | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/statements/available-fields` | 无 | Local Pass；Staging/Prod E2E Pending |
| BYOC Statements types | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/statements/statement-types` | 无 | Local Pass；Staging/Prod E2E Pending |
| BYOC Statements list | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/statements` | 必填 `begin_time,end_time`；可选 `schemas,stmt_types,text,fields` | Local Pass；Staging/Prod E2E Pending |
| BYOC Statements plans | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/statements/plans` | 必填 `begin_time,end_time,digest`；可选 `schema_name` | Local Pass；Staging/Prod E2E Pending |
| BYOC Statements plan detail | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/statements/plan-detail` | 必填 `begin_time,end_time,digest`；可选 `plans,schema_name` | Local Pass；Staging/Prod E2E Pending |

### Metadata

```bash
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/clusters/${CLUSTER_ID}"

curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode 'page=1' \
  --data-urlencode 'pageSize=50' \
  --data-urlencode 'showDeleted=false' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters"

curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}"
```

单个 cluster 返回 Investibook 诊断所需的 canonical metadata；list 返回：

```json
{"items":[],"page":1,"pageSize":50,"total":0}
```

### Activity Events

`begin_ts`、`end_ts` 是 Unix seconds。`cursor` 位于 query，不再使用 legacy `cursor` Header。

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "begin_ts=${BEGIN_TS}" \
  --data-urlencode "end_ts=${END_TS}" \
  --data-urlencode 'limit=20' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/activities"
```

成功响应保持 `{activities,partial,cursor}`；activity item 继续使用 legacy snake_case 字段。下一页把响应中的非空 `cursor` 原样作为 query 参数发送。

### Components

```bash
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/components"
```

成功响应是 component array。Data Proxy 不返回 legacy Clinic 在特定无 CSRF 请求中追加的 mocked TiCDC component。

### Top RU

Top RU summary、summary by plan 和 user stats 只承诺 **plain logical Premium 与 plain logical Essential**。Resource Pool（包括名称中带 Premium/Essential 的 resource-pool 变体）、Dedicated、Starter 和 BYOC 均未纳入本批支持与验收范围。Data Proxy 解析 logical org/cluster 的 O11y target，然后调用 allowlisted O11y Top RU API；调用方不需要 placement Header。

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "start=${START_TS}" \
  --data-urlencode "end=${END_TS}" \
  --data-urlencode 'top=10' \
  --data-urlencode 'order_by=ru' \
  --data-urlencode 'data_source=deltalake' \
  --data-urlencode 'step=60' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/top-ru/summary"

curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "start=${START_TS}" \
  --data-urlencode "end=${END_TS}" \
  --data-urlencode 'top=10' \
  --data-urlencode "digest=${SQL_DIGEST}" \
  --data-urlencode 'order_by=ru' \
  --data-urlencode 'data_source=deltalake' \
  --data-urlencode 'step=60' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/top-ru/summary-plan"

curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "start=${START_TS}" \
  --data-urlencode "end=${END_TS}" \
  --data-urlencode 'top=20' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/top-ru/user-stats"
```

Summary 的 `user` 有三种不同语义：

| 调用 | 语义 |
| --- | --- |
| 不传 `user` | 不按 user 过滤 |
| `--data-urlencode 'user='` | 只查询 empty/NULL user |
| `--data-urlencode 'user=alice'` | 查询指定 user |

`summary` 按 SQL digest 聚合；需要执行计划明细时，再把原始 `sql_digest` 作为 `digest` 调用 `summary-plan`。同一 SQL 返回的 plan row 数即该 SQL 的 plan count。不要为列表中的每一行自动扇出 plan 请求。

成功 body 保持 O11y JSON，不增加 Data Proxy success envelope，也不丢弃未知响应字段。当前 Data Service 只限制 query 名称、大小和控制字符；参数值、时间窗及 tier 上限仍由 O11y 契约判定。plain Premium 30 天/Top 100 与 plain Essential 7 天/Top 20 目前是 UI policy，不能作为 Data Proxy 已强制的限制写入 skill。

`GET .../top-ru/config` 仍是 Deferred Candidate，当前不可调用。

### BYOC Slow Query（Local Pass；尚未部署验收）

这三条 API 只接受 logical BYOC（`byoc/byoc`）和 physical BYOC resource pool（`byoc-host/byoc resource pool`）。Dedicated、Premium、Starter、Essential 及 tier metadata 冲突的集群都会在解析数据目标前返回 `400`，不会回退到 Slow Query SQL、S3、Athena 或建表链路。

Data Service 通过服务端解析出的客户侧 BYOC O11y diagnosis target 代理读取数据。List 响应按 `fields` 投影，并额外保留 detail round-trip 必需的 `digest`、`timestamp`、`connection_id`；其他成功 body 保持 O11y JSON。Clinic 不持久化、不复制到对象存储，也不为 BYOC Slow Query 建 S3/Athena 表。Clinic 部署必须配置 `central-o11y.byoc-ds-endpoint`；internal-api 会把 O11y 返回目标精确绑定到这个受信 data-service host，缺失或不匹配时 fail closed，HTTP 只允许用于这个受信私网 endpoint。

```bash
# O11y 支持字段：响应为 JSON string array。
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/slow-queries/available-fields"

# begin_time/end_time 为 Unix seconds；最大窗口 259500 秒。
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "begin_time=${BEGIN_TS}" \
  --data-urlencode "end_time=${END_TS}" \
  --data-urlencode 'fields=query,timestamp,query_time,memory_max,digest' \
  --data-urlencode 'limit=100' \
  --data-urlencode 'orderBy=query_time' \
  --data-urlencode 'desc=true' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/slow-queries"

# digest、timestamp、connect_id 来自 list item。
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "digest=${DIGEST}" \
  --data-urlencode "timestamp=${TIMESTAMP}" \
  --data-urlencode "connect_id=${CONNECT_ID}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/slow-queries/detail"
```

三条接口均为无 body 的 GET，完整 query string 最大 16 KiB。List 参数白名单为 `begin_time,end_time,fields,db,limit,text,orderBy,desc,plans,digest,show_internal`。只有 `db` 和 `plans` 可以重复，分别最多 64 个值；其余参数不能重复。`limit` 缺省为 100、范围 1～1000；`fields` 缺省为 `query,timestamp,query_time,memory_max` 且最长 4096 bytes；`text` 最长 4096 bytes；`digest` 及每个 `db`/`plans` 值最长 256 bytes。`orderBy` 缺省为 `timestamp`，可选 `timestamp,query_time,memory_max,total_keys,process_keys`。`desc` 与 `show_internal` 只接受小写 `true` 或 `false`。

Detail 的 `timestamp` 必须是 finite positive Unix timestamp，`connect_id` 必须是十进制 `uint64`。调用方不得传递 placement query/Header；O11y target、org、region 和 application routing Header 全部由服务端生成。

### BYOC Top SQL（Local Pass；尚未部署验收）

这两条 API 只接受 logical BYOC（`byoc/byoc`）和属于该 org 的 physical BYOC resource pool（`byoc-host/byoc resource pool`）。其他 Tier 或冲突的 tier metadata 会在访问 O11y 前返回 `400`。Data Service 通过服务端解析的客户侧 BYOC O11y diagnosis target 读取数据，不调用 Top SQL SQL/S3/Athena 链路，不建表、不缓存或持久化客户数据。

```bash
# 先发现该时间段内有 Top SQL 数据的实例。
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "start=${START_TS}" \
  --data-urlencode "end=${END_TS}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/top-sql/instances"

# INSTANCE 与 INSTANCE_TYPE 取自 instances 响应。
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "start=${START_TS}" \
  --data-urlencode "end=${END_TS}" \
  --data-urlencode 'window=60s' \
  --data-urlencode 'top=20' \
  --data-urlencode "instance=${INSTANCE}" \
  --data-urlencode "instance_type=${INSTANCE_TYPE}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/top-sql/summary"
```

两条接口都是无 body 的 GET，完整 query string 最大 16 KiB，所有参数都必须且只能出现一次。`start/end` 是 finite、非负、最大 `253402300799` 的 decimal Unix seconds，`end >= start`，窗口最大 15 天。`window` 使用 Go duration 语法，必须是 1 秒到 15 天之间的整秒时长；单次 summary 最多 4096 个 window。`top` 范围 1～100；`instance` 最长 512 bytes，只允许字母、数字、`_ . : - [ ]`；`instance_type` 只允许 `tidb`、`tikv` 或 `pd`。

成功响应保持 O11y 的单层 `{status,data}` JSON envelope，`data` 必须是 array；Data Proxy 不再包装，也保留 O11y 新增的未知字段。响应最大 16 MiB，target 解析与 O11y 查询整条链路的 deadline 为 120 秒。首批不提供 `/top-sql/config`：现有 NGM config 只是本地常量，不代表客户侧 O11y 的真实能力。

### BYOC Continuous Profiling group list/detail（Local Pass；尚未部署验收）

首批只提供 profile group 列表和单个 group 的 target 明细，并且只接受属于该 org 的 physical BYOC resource pool（`byoc-host/byoc resource pool`）。Logical BYOC 和 Dedicated、Premium、Starter、Essential 等其他 Tier 均会在访问 O11y 前 fail closed；不会回退到旧 NGM 的本地对象存储、S3/Athena、缓存或建表链路。

```bash
# begin_time/end_time 为正整数 Unix seconds；按分钟取整后的窗口最大 3 小时。
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "begin_time=${BEGIN_TS}" \
  --data-urlencode "end_time=${END_TS}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${PHYSICAL_CLUSTER_ID}/continuous-profiling/group-profiles"

# TS 取自 group list item 的 ts 字段；detail 不接受 query 参数。
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${PHYSICAL_CLUSTER_ID}/continuous-profiling/group-profiles/${TS}"
```

两条接口都是无 body 的 GET。List 只接受各出现一次的 `begin_time,end_time`；detail 的 `ts` 只能放在 PATH。调用方不得通过 query 或 Header 提供 org、cluster、provider、region、deploy type、O11y cluster ID 或其他 placement 信息。Data Service 从服务端解析 customer-side O11y diagnosis target，只向固定 Continuous Profiling path 发送服务端生成的路由 Header；不会转发 caller 的 Authorization、Cookie 或自定义 routing Header。

成功响应保持 O11y JSON 字段，不增加 Data Proxy success envelope；空 group list 规范化为 `[]`，detail 无对应 group 时保持 O11y 的 `200` + JSON `null`。响应最大 16 MiB，target 解析与 O11y 查询共享 180 秒 deadline。Clinic 仅在单次请求中有界转发 JSON，不落盘、不缓存、不复制或持久化客户侧 profiling 数据。

本批明确不注册 action token、download、single-profile view 或 config。也就是说它可以发现可用 profile group 和具体 component/address/profile type，但还不能通过 canonical Data Proxy API 下载 pprof bytes；这些能力需要在 token 隔离、下载流式上限和 access-log 脱敏契约单独冻结后再实现。

### BYOC SQL Statements（Local Pass；Prod E2E 待验收）

这五条 API 只接受 logical BYOC（`byoc/byoc`）和属于同一 org 的 physical BYOC resource pool（`byoc-host/byoc resource pool`）。Dedicated、Premium、Starter、Essential 及 tier metadata 冲突的集群都会在发起数据请求前 fail closed，不会回退到其他 tier 的诊断数据链路。

Data Service 只代理服务端解析出的客户侧 BYOC O11y diagnosis target。这条链路不执行 warehouse SQL，不读写 S3 或 Athena、不建表或进入 warehouse；不会落盘、缓存或建立持久副本，只在单次请求内进行有界代理，并按需补充 `binary_plan_text`。现有 Premium Statements warehouse SQL 能力和其 provider 支持矩阵保持不变；这五条 BYOC GET API 不替换、不收紧其它 tier 的现有 API。

logical 和 physical cluster ID 使用同一组 path。下面的占位变量二选一：

```bash
export LOGICAL_BYOC_CLUSTER_ID='<logical BYOC cluster ID>'
export PHYSICAL_BYOC_CLUSTER_ID='<physical byoc-host cluster ID>'

# logical BYOC
export BYOC_STATEMENTS_CLUSTER_ID="${LOGICAL_BYOC_CLUSTER_ID}"
# physical byoc-host：需要时改用下一行
# export BYOC_STATEMENTS_CLUSTER_ID="${PHYSICAL_BYOC_CLUSTER_ID}"
```

Fields 和 statement types 不接受 query 或 body，成功响应均为 JSON string array：

```bash
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${BYOC_STATEMENTS_CLUSTER_ID}/statements/available-fields"

curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${BYOC_STATEMENTS_CLUSTER_ID}/statements/statement-types"
```

List 返回 JSON object array。`begin_time` 和 `end_time` 为 Unix seconds，必填且时间窗口最大为 259500 秒。`schemas` 与 `stmt_types` 是 repeated array 参数，要通过重复 query key 传递，不要拼成 comma-separated 字符串；每组最多 64 个值。`fields` 则是 comma-separated 字段名。

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "begin_time=${BEGIN_TS}" \
  --data-urlencode "end_time=${END_TS}" \
  --data-urlencode 'schemas=app' \
  --data-urlencode 'schemas=reporting' \
  --data-urlencode 'stmt_types=Select' \
  --data-urlencode 'stmt_types=Update' \
  --data-urlencode 'fields=digest_text,digest,exec_count,query_sample_text,plan_digest,binary_plan' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${BYOC_STATEMENTS_CLUSTER_ID}/statements"
```

Plans 按 SQL `digest` 返回 JSON object array；`schema_name` 可选：

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "begin_time=${BEGIN_TS}" \
  --data-urlencode "end_time=${END_TS}" \
  --data-urlencode "digest=${SQL_DIGEST}" \
  --data-urlencode "schema_name=${SCHEMA_NAME}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${BYOC_STATEMENTS_CLUSTER_ID}/statements/plans"
```

Plan detail 按 SQL `digest` 返回一个 JSON object。`plans` 是**可选** repeated array 参数；省略时聚合该 SQL 的所有 plan，指定时每个值是 64 位十六进制 plan digest 或 `N/A`，最多 64 个。未找到数据的成功响应为 `200 {}`，不是 `null` 或 array。

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode "begin_time=${BEGIN_TS}" \
  --data-urlencode "end_time=${END_TS}" \
  --data-urlencode "digest=${SQL_DIGEST}" \
  --data-urlencode "plans=${PLAN_DIGEST_1}" \
  --data-urlencode "plans=${PLAN_DIGEST_2}" \
  --data-urlencode "schema_name=${SCHEMA_NAME}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${BYOC_STATEMENTS_CLUSTER_ID}/statements/plan-detail"
```

`digest` 必须是 64 位十六进制 SQL digest。`text` 和 `fields` 各最长 4096 bytes，`schema_name`、每个 `schemas`、`stmt_types` 及 repeated plan 值各最长 256 bytes。完整 query string 最大 16 KiB；从 target 解析到 O11y 响应共用 120 秒 deadline；成功响应（包括下述执行计划补充后）最大 16 MiB。

Plan detail 中已有非空 `binary_plan_text` 时保持原值。否则 Data Service 会对 `binary_plan` 做 best-effort 解码，成功时补充人类可读的 `binary_plan_text`；该文本用于可读展示，不承诺与旧 NGM/TiDB renderer 字节一致。缺失、格式无效或解码超限时保持原始成功响应，不伪造 plan text。如合法补充后的整体 body 超过 16 MiB，仍按响应上限拒绝。

调用方不得传入 `data_source`、`dataSource`、provider、region、cluster routing 或其他 placement query/Header。只有上述参数白名单会被转发；O11y target 与 routing Header 全部由服务端生成。目前只有本地契约与集成测试通过，staging 与 prod 都尚未做 E2E，不得在 skill 中标记为 Available。

## 3. 规范化 SQL、Schema 与 Capabilities API（Batch 1）

Batch 1 为四个已有能力增加规范化入口；底层仍调用同一 Data Proxy Service/Provider，不复制 SQL、建表、rewrite 或查询逻辑。

| 能力 | Canonical method/path | Code | Staging | Prod |
| --- | --- | --- | --- | --- |
| SQL query | `POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/sql-queries` | Merged / Local Pass | Pending | Pass |
| Same-cluster batch | `POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/sql-query-batches` | Merged / Local Pass | Pending | Pass |
| Table schemas | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/table-schemas` | Merged / Local Pass | Pending | Pass |
| Service capabilities | `GET /data-proxy/api/v1/capabilities` | Merged / Local Pass | Pending | Pass |

这四个入口已完成 prod E2E，可在 prod skill 中标记为 Available；Staging 仍保持 Pending。Canonical API 不接受 caller-selected placement/routing 参数；服务端校验 PATH 中的 org/cluster ownership，并从 Clinic 管控面解析 provider、region、data source 和 Clinic tenant。

### SQL Query

```text
POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/sql-queries
```

JSON body 只允许：

| 参数 | 必需 | 说明 |
| --- | --- | --- |
| `sql` | 是 | Data Proxy logical SQL；仍需满足标准逻辑表和 date partition 约束 |
| `timeout` | 否 | 秒；`0` 使用既有默认值，显式值最大 1800 |

`clusterId`、`cluster_id`、`orgId`、`options` 和其他未知字段都会返回 `400`。字段名大小写敏感，重复 key、显式 `null` 和 trailing JSON 也会被拒绝。单请求 body 最大 1 MiB。

每个实际读取逻辑表的 `SELECT` 都必须在 `WHERE` 中包含自己的 `date` 分区条件。UNION 的每个分支、CTE/子查询内部的表扫描都分别校验；仅在外层或另一个分支添加 `date` 不会放行未受限扫描。JOIN 多张物理表时须用各自 alias 限定 `alias.date`；只把分区条件写在 `JOIN ... ON` 中当前会被保守拒绝。

```bash
curl --fail-with-body \
  -X POST \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  -H 'Content-Type: application/json' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/sql-queries" \
  --data "{
    \"timeout\": 120,
    \"sql\": \"SELECT digest, COUNT(*) AS executions FROM clinic_data_proxy.slow_query_logs WHERE date = '20260721' GROUP BY digest ORDER BY executions DESC LIMIT 20\"
  }"
```

成功响应继续使用既有 `{columns,rows,metadata}` 结构；`rows` 仍可能为 `null`，row values 仍按既有 Data Proxy 契约返回。

### Same-cluster Batch Query

```text
POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/sql-query-batches
```

Body 是最多 100 个 query item 的数组，每个 item 同样只允许 `sql` 和可选 `timeout`。所有 item 使用 PATH 中同一 cluster；不接受 item-level cluster/options。任意 item 非法时整批失败，不执行部分查询；body 最大 4 MiB。Statements batch 继续不支持。

```bash
curl --fail-with-body \
  -X POST \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  -H 'Content-Type: application/json' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/sql-query-batches" \
  --data '[
    {"sql":"SELECT COUNT(*) FROM clinic_data_proxy.slow_query_logs WHERE date = '\''20260721'\''","timeout":120},
    {"sql":"SELECT sql_digest, SUM(topsql_cpu_time_ms) FROM clinic_data_proxy.topsql WHERE date = '\''2026-07-21'\'' GROUP BY sql_digest LIMIT 20","timeout":120}
  ]'
```

成功响应保持既有 `{results,count}` 结构；`results` 按请求顺序包含每个 item 的 `{columns,rows,metadata}`，`count` 等于成功返回的 item 数。

### Table Schemas

```text
GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/table-schemas?tables=...
```

唯一允许的 query 参数是必填的 `tables`，值为 1～16 个 comma-separated standard logical table；一次请求中的表必须属于同一数据源 family。`clusterId`、dataSource、vendor、region、`options.*` 或其他 query 参数都会返回 `400`；Statements schema 继续不暴露。

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode 'tables=topsql_tidb,topsql_sql_meta,topsql_plan_meta' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/table-schemas"
```

成功响应继续使用既有 `{schemas,count}` 结构，并返回逻辑表名而不是 cluster-suffixed 物理表名。为保持现有 schema payload parity，当前 trusted internal Clinic key audience 仍可看到既有 `data_source`/`location` 字段；未来若开放普通 principal，需单独评审是否收窄这些字段。

### Service Capabilities

```bash
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/capabilities"
```

该接口是服务级资源，因此不增加 org/cluster PATH。响应与 legacy `/data-proxy/info` 的 `service`、`version` 和 `supported_vendors` 信息等价。

### Legacy Compatibility

Canonical API 是增量入口，不 redirect、不删除或收紧任何旧接口：

| 能力 | 保留的 legacy path | 兼容边界 |
| --- | --- | --- |
| SQL query | `POST /data-proxy/query` | 继续接受 body `clusterId`/`cluster_id`、`timeout` 和既有 `options` |
| Batch SQL query | `POST /data-proxy/batch-query` | 继续允许 item-level cluster、caller routing options 和跨集群 batch |
| Schema | `GET /data-proxy/schema` | 继续接受 query `clusterId`、dataSource/vendor/region 和既有 aliases |
| Service info | `GET /data-proxy/info` | 响应保持不变 |
| Serverless parent metrics | `/data-proxy/serverless-parent-metrics/api/v1/{query,query_range,series}` | 本批不修改，仍使用 legacy `control_plane_info` 契约 |

现有 Clinic Slow Query Explorer 与 health-check 可继续调用 legacy path。配套 nutshell skill 以 canonical helper/示例作为默认入口，并用显式 `clinic_legacy_*` helper 保留旧路径兼容。

### Premium SQL Statements warehouse（Prod AWS Pass；GCP/Azure 正向样本待补）

这项 Premium warehouse 能力与上述只读客户侧 O11y 的 BYOC Statements GET API 是两条独立链路，本次 BYOC 支持不改变本节的 tier、provider、存储和查询契约。Premium SQL Statements 使用 canonical `sql-queries` 和逻辑表 `clinic_data_proxy.statements`，与 Slow Query 的 `clinic_data_proxy.slow_query_logs` 区分。必须使用这个完整、小写的逻辑表名；cluster scope 来自 PATH，SQL 中的 `date = 'YYYY-MM-DD'` 分区条件必填，不要在同一条 SQL 中混用 `statements` 与其他 Data Proxy 逻辑表。Legacy `POST /data-proxy/query` 仍保持现有 Statements 契约。

```bash
curl --fail-with-body \
  -X POST \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  -H 'Content-Type: application/json' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/sql-queries" \
  --data "{
    \"timeout\": 300,
    \"sql\": \"SELECT * FROM clinic_data_proxy.statements WHERE date = '2026-07-21' LIMIT 20\"
  }"
```

Canonical body 不接受 `options`。Statements 查询会由服务端强制使用 Clinic cluster 管控面解析的 provider、region 和 `persisted_statements_summary` 数据源，并使用服务端管理的 SQL 重写和物理表。Canonical/legacy batch 和 schema 都暂不暴露 Statements。本批范围为 Premium、Premium Reserved、Premium Resource Pool 和 Premium Reserved Resource Pool；只有集群开启持久化 Statements 后才会产生数据。

| Provider | 查询引擎 | 当前状态 |
| --- | --- | --- |
| AWS | Athena + Delta | Prod Pass：有数据 Premium 样本 legacy/canonical 查询与 authoritative routing 通过 |
| GCP | BigQuery + Delta | Implemented；prod 无 Premium 样本，Not exercised |
| Azure | Synapse + Delta | Implemented；prod 只有 deleted Premium 样本且未配置 Statements 数据源，正向 Not exercised |
| AliCloud | — | Unsupported；当前 MaxCompute/OSS 链路不能安全读取 Delta transaction log |

数据源 URL 已解析但存储根目录为空（或只有目录 marker）时，返回 `200` 和空结果：`columns=[]`、`rows=[]`、`metadata.rowCount=0`。Clinic 没有返回 Statements 数据源 URL 时，返回 `400/DATASOURCE_NOT_CONFIGURED`。存储权限、超时、网络错误，以及“目录有对象但没有 Delta transaction log”都会作为错误返回，不会被折叠为空结果。

这项 SQL 查询能力是新的增量接口，**不是**对旧 NGM Statements `available_fields`、`stmt_types`、`list`、`plans` 和 `plan/detail` GET 契约的一对一替代。这些旧接口未被本批修改，其兼容迁移仍单独待办。

## 4. Cluster Metrics 与 Business Logs（Batch 2）

Batch 2 为现有 Prometheus-compatible metrics 和 Loki-compatible business logs 增加 org/cluster-scoped canonical alias。canonical adapter 只负责 ownership、服务端 target 解析和安全转发；PromQL/LogQL 改写、上游调用与成功响应仍复用 legacy core。当前为 **Prod Pass**，可加入 prod skill；Staging 仍 Pending。

| 能力 | Canonical method/path | 参数 | Code | Staging | Prod |
| --- | --- | --- | --- | --- | --- |
| Metrics instant query | `GET, POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/metrics/query` | 必填 `query`；可选 `time,timeout` | Merged / Local Pass | Pending | Pass |
| Metrics range query | `GET, POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/metrics/query-range` | 必填 `query,start,end,step`；可选 `timeout` | Merged / Local Pass | Pending | Pass |
| Metrics series | `GET, POST /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/metrics/series` | 至少一个 `match[]`；可选 `start,end` | Merged / Local Pass | Pending | Pass |
| Logs range query | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/logs/query-range` | Loki-compatible query；常用 `query,start,end,limit,direction,step,interval` | Merged / Local Pass | Pending | Pass |
| Log labels | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/logs/labels` | Loki-compatible query；常用 `start,end,query` | Merged / Local Pass | Pending | Pass |
| Log label values | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/logs/labels/{name}/values` | label name 在 PATH；常用 `start,end,query` | Merged / Local Pass | Pending | Pass |
| Log series | `GET /data-proxy/api/v1/orgs/{orgID}/clusters/{clusterID}/logs/series` | Loki-compatible `match[]`；可选 `start,end` | Merged / Local Pass | Pending | Pass |

Metrics 的 GET 参数放在 query；POST 使用 `application/x-www-form-urlencoded` 和相同字段，不使用 JSON body。时间值继续接受现有 Prometheus-compatible RFC3339 或 Unix timestamp，`step` 继续接受 duration 或秒数。成功响应保持现有 Prometheus `{status,data,errorType,error,warnings}` 契约。

```bash
curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode 'query=sum(rate(tidb_server_query_total[5m]))' \
  --data-urlencode "start=${START_TS}" \
  --data-urlencode "end=${END_TS}" \
  --data-urlencode 'step=60' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/metrics/query-range"

curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode 'query={namespace="example"}' \
  --data-urlencode "start=${START_NS}" \
  --data-urlencode "end=${END_NS}" \
  --data-urlencode 'limit=100' \
  --data-urlencode 'direction=backward' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/orgs/${ORG_ID}/clusters/${CLUSTER_ID}/logs/query-range"
```

Business Logs 的业务 query 继续由既有 Loki proxy 处理；canonical path 不新增另一套 LogQL 逻辑。调用方不得在 canonical query/form/Header 中提供 cluster、org、provider、region、data source 或 placement 信息来覆盖 PATH 和服务端 target。

Batch 2 只新增 alias，不 redirect、不删除或收紧以下 legacy API：

| 能力 | 保留的 legacy path | 兼容边界 |
| --- | --- | --- |
| Metrics instant | `GET, POST /data-proxy/metrics/{cluster_id}/api/v1/query` | 继续使用 query/form 的 Prometheus 参数和 cluster-only scope |
| Metrics range | `GET, POST /data-proxy/metrics/{cluster_id}/api/v1/query_range` | `query_range` underscore path 和现有响应/错误保持 |
| Metrics series | `GET, POST /data-proxy/metrics/{cluster_id}/api/v1/series` | `match[]`、可选时间参数和现有 routing 保持 |
| Logs range | `GET /data-proxy/loki/{cluster_id}/api/v1/query_range` | `query_range` underscore path 和现有 Loki proxy 契约保持 |
| Log labels | `GET /data-proxy/loki/{cluster_id}/api/v1/labels` | 保持 |
| Log label values | `GET /data-proxy/loki/{cluster_id}/api/v1/label/{name}/values` | legacy singular `label` path 保持 |
| Log series | `GET /data-proxy/loki/{cluster_id}/api/v1/series` | 保持 |

## 5. Datasource-scoped System Logs（Batch 3）

System Logs 是 Clinic control-plane 日志，不属于 TiDB business org/cluster。canonical API 因此把 datasource path ID 作为真实资源放入 PATH，不增加虚假的 `{orgID}` 或 `{clusterID}`。当前为 **Prod Pass**，可在 prod 使用；Staging 仍 Pending。

| 能力 | Canonical method/path | 参数 | Code | Staging | Prod |
| --- | --- | --- | --- | --- | --- |
| Datasource catalog | `GET /data-proxy/api/v1/system-log-datasources` | 不接受 query 或 body | Merged / Local Pass | Pending | Pass |
| Logs range query | `GET /data-proxy/api/v1/system-log-datasources/{datasourceUID}/logs/query-range` | Loki-compatible `query,start,end,limit,direction,since,step,interval` | Merged / Local Pass | Pending | Pass |
| Labels | `GET /data-proxy/api/v1/system-log-datasources/{datasourceUID}/logs/labels` | 常用 `start,end,query` | Merged / Local Pass | Pending | Pass |
| Label values | `GET /data-proxy/api/v1/system-log-datasources/{datasourceUID}/logs/labels/{name}/values` | label name 在 PATH；常用 `start,end,query` | Merged / Local Pass | Pending | Pass |
| Series | `GET /data-proxy/api/v1/system-log-datasources/{datasourceUID}/logs/series` | Loki-compatible `match[]`；可选 `start,end` | Merged / Local Pass | Pending | Pass |

先调用 datasource catalog，并从返回的 `data.datasources[].uid` 选择 `{datasourceUID}`。新配置使用真实 UID；兼容仅配置 legacy `datasource` 名称的部署时，catalog 会把该稳定名称作为 path ID 返回；`data.default_datasource` 也使用同一种可直接放入 PATH 的 ID。catalog 保持 `{status,data}` envelope，只返回 datasource 标识、provider/region 元数据和 default 标记，不返回上游 URL。后续请求只接受 PATH 中的该 ID；`datasource`、`datasource_uid`、`datasourceUid`、`datasourceUID`、`uid`、`provider`、`region` 等 routing query 不属于 canonical 契约。

```bash
curl --fail-with-body \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/system-log-datasources"

curl --fail-with-body --get \
  -H "Authorization: Bearer ${CLINIC_API_KEY}" \
  --data-urlencode 'query={namespace="example-control-plane"}' \
  --data-urlencode "start=${START_NS}" \
  --data-urlencode "end=${END_NS}" \
  --data-urlencode 'limit=100' \
  "${CLINIC_ENDPOINT}/data-proxy/api/v1/system-log-datasources/${DATASOURCE_UID}/logs/query-range"
```

Batch 3 保持以下特权 legacy API 原样可用：

| 能力 | 保留的 legacy path | 兼容边界 |
| --- | --- | --- |
| Datasource catalog | `GET /data-proxy/system-logs/api/v1/datasources` | 现有响应保持 |
| Logs range | `GET /data-proxy/system-logs/api/v1/query_range` | 继续接受既有 datasource query aliases；既有 provider/region 参数行为不变 |
| Labels | `GET /data-proxy/system-logs/api/v1/labels` | 同上 |
| Label values | `GET /data-proxy/system-logs/api/v1/label/{name}/values` | legacy singular `label` path 保持 |
| Series | `GET /data-proxy/system-logs/api/v1/series` | 保持 |

Canonical alias 不改变 System Logs 的 privileged audience，也不会扩大 Gateway 权限。配套 nutshell skill 默认使用 datasource UID canonical path，并用显式 legacy helper 保留旧入口兼容。

## 6. 错误与环境可用性

新 resource API 的错误响应为：

```json
{"code":"NotFound","message":"cluster not found","requestId":"..."}
```

常见状态：`400` 参数错误、`401` 未认证、`403` 无权限、`404` org/cluster 不匹配、`413` 请求体过大、`429` 上游限流、`502` 上游无效响应、`503` 暂时不可用、`504` 超时。调用方不得依赖内部上游错误 body。

| 批次 | Staging | Prod |
| --- | --- | --- |
| Metadata / Activity / Components | Pass；Activity cursor 与 dedicated Components 有已记录的 live-data 覆盖缺口 | Pass；Activity cursor 未覆盖，Dedicated Components 已覆盖 |
| Top RU summary / user stats | Partial Pass；Premium 通过，Essential 无 O11y target | Pass；active Premium/Essential 与 legacy 完整 JSON parity |
| SQL Statements via `/data-proxy/query` | Pending；staging 无 Statements 数据源，不能作为正向数据验收环境 | Partial Pass；AWS 有数据 E2E 通过，GCP 无样本、Azure 无 datasource，AliCloud unsupported 通过 |
| BYOC Statements GET（customer-side O11y） | Pending | Pending；prod BYOC logical/physical E2E 尚未执行 |
| REST Batch 1：SQL query / batch / schema / capabilities | Pending | Pass；四组 legacy/canonical parity 与安全边界通过 |
| REST Batch 2：cluster metrics / business logs | Pending | Pass；Metrics 6 项、Logs 4 项稳定 payload parity |
| REST Batch 3：datasource-scoped system logs | Pending | Pass；默认 AWS us-west-2 datasource 的 catalog 与四项 Loki parity |
| BYOC Continuous Profiling group list/detail | Pending | Pending；仅 physical byoc-host，部署后验证 |

详细证据见[迁移与验收状态](./data-proxy-api-migration-acceptance.md)。Top RU 已可在 prod 对承诺的 plain Premium/Essential 标记为 Available；Statements 只能把 AWS 标记为 prod E2E Pass，GCP/Azure 仍需正向样本。

## 7. 尚未可调用的后续迁移

**Batch 4 Serverless Parent Metrics / Logs：Blocked by resolver。** 当前没有权威的 child `{orgID}/{clusterID}` → live parent metrics/logs target resolver；已有 parent resolver 仍依赖 caller `control_plane_info`。因此不注册 `/parent-metrics/...` 或 `/parent-logs/...` canonical alias。现有 `/data-proxy/serverless-parent-metrics/api/v1/...` 与 `/data-proxy/serverless-parent-logs/api/v1/...` 继续按 legacy `control_plane_info` 契约使用。

**Batch 5 `/clinic-internal`：Semantic review complete / no aliases。** Traffic comparison 尚缺明确的 same-org/cross-org 模型和双集群授权；slow-query/metrics healthcheck 仍含 caller routing/URL 与多种 org/tenant ID 语义；AI 请求中的 org ID domain 也需确认。本批没有新增 canonical path，所有既有 `/clinic-internal/...` method/path 与 body 保持不变。

Continuous Profiling 的 action token、download、single-profile view 和 Plan Replayer/OOM files 仍处于 Proposed/Planned；本批只有 physical BYOC group list/detail 达到 Local Pass。BYOC Statements 的五条 canonical GET 已完成本地实现与测试，但 staging/prod E2E 仍 Pending；除这组 BYOC-only 路由外，旧 NGM Statements 及其他 tier 行为均未修改。Premium SQL Statements warehouse 已完成 AWS prod 验收，可按 provider 状态加入 skill；GCP/Azure 不得标记为 prod E2E Pass。候选的 ConProf config、OOM top-memory、跨 org search 与 Top RU config 同样保持 Deferred。

每批迁移完成后同步更新：本文的 endpoint/query/response 示例、环境状态、`data-proxy/SUBSKILL.md`、shell/Python helpers、上层 `SKILL.md` 与 subskill index/changelog，并在全新 workspace 验证实际安装 revision。
