303 lines
8.5 KiB
Markdown
303 lines
8.5 KiB
Markdown
# SQL 数据源处理器技术文档
|
||
|
||
## 概述
|
||
|
||
`SQLDataSourceProcessor` 是一个基于 `DataSourceProcessor` 的子类,用于处理以 `.sqlds` 格式定义的 SQL 数据源。它允许通过配置文件描述 SQL 查询、参数和数据库连接,并动态执行查询获取数据。
|
||
|
||
该模块主要用于从数据库中提取结构化数据,支持普通查询、分页查询以及自动推导返回字段结构(`datadesc`),并能将推导结果持久化回源文件。
|
||
|
||
---
|
||
|
||
## 依赖模块
|
||
|
||
```python
|
||
import codecs
|
||
from .dsProcessor import DataSourceProcessor
|
||
from appPublic.jsonConfig import getConfig
|
||
from sqlor.dbpools import DBPools
|
||
import json
|
||
```
|
||
|
||
- `codecs`: 用于安全地读写带编码的文件(如 UTF-8)。
|
||
- `DataSourceProcessor`: 抽象基类,定义了数据源处理器的标准接口。
|
||
- `getConfig` (from `appPublic.jsonConfig`): 获取全局配置对象。
|
||
- `DBPools` (from `sqlor.dbpools`): 提供异步数据库连接池及 SQL 执行装饰器。
|
||
- `json`: 用于序列化/反序列化 JSON 数据。
|
||
|
||
---
|
||
|
||
## 配置文件格式(`.sqlds`)
|
||
|
||
`.sqlds` 文件是标准的 JSON 格式,描述了一个 SQL 查询的数据源信息:
|
||
|
||
```json
|
||
{
|
||
"sqldesc": {
|
||
"sql_string": "select * from dbo.stock_daily_hist where stock_num=${stock_num}$ order by trade_date desc",
|
||
"db": "mydb",
|
||
"sortfield": "trade_date"
|
||
},
|
||
"arguments": [
|
||
{
|
||
"name": "stock_num",
|
||
"type": "str",
|
||
"iotype": "text",
|
||
"default": "600804"
|
||
}
|
||
],
|
||
"datadesc": []
|
||
}
|
||
```
|
||
|
||
### 字段说明
|
||
|
||
| 字段 | 类型 | 必需 | 描述 |
|
||
|------|------|------|------|
|
||
| `sqldesc.sql_string` | string | 是 | 实际执行的 SQL 查询语句,支持 `${param}$` 形式的参数占位符。 |
|
||
| `sqldesc.db` | string | 是 | 数据库连接名称(在 DBPools 中注册过的别名)。 |
|
||
| `sqldesc.sortfield` | string | 否 | 排序字段,可用于前端排序或分页逻辑参考。 |
|
||
| `arguments` | array | 否 | 定义传入 SQL 的参数列表,每个参数包含:`name`, `type`, `iotype`, `default`。 |
|
||
| `datadesc` | array | 否 | 描述查询结果字段结构的数组。若为空,则首次访问时自动推导并写回文件。 |
|
||
|
||
> ⚠️ 注意:`${param}$` 是模板语法,运行时会被 `ns` 上下文中的对应值替换。
|
||
|
||
---
|
||
|
||
## 类定义
|
||
|
||
```python
|
||
class SQLDataSourceProcessor(DataSourceProcessor):
|
||
```
|
||
|
||
继承自 `DataSourceProcessor`,实现针对 `.sqlds` 类型数据源的具体行为。
|
||
|
||
---
|
||
|
||
## 方法说明
|
||
|
||
### `isMe(name) -> bool`
|
||
|
||
判断当前处理器是否适用于指定类型的数据源。
|
||
|
||
#### 参数
|
||
- `name` (`str`):数据源类型名。
|
||
|
||
#### 返回值
|
||
- `True` 当且仅当 `name == 'sqlds'`。
|
||
|
||
#### 示例
|
||
```python
|
||
if SQLDataSourceProcessor.isMe('sqlds'):
|
||
processor = SQLDataSourceProcessor(...)
|
||
```
|
||
|
||
---
|
||
|
||
### `getArgumentsDesc(dict_data, ns, request) -> list or None`
|
||
|
||
获取数据源所需的输入参数描述。
|
||
|
||
#### 参数
|
||
- `dict_data` (`dict`):解析后的 `.sqlds` 文件内容。
|
||
- `ns` (`dict`):命名空间(通常为请求参数)。
|
||
- `request` (`Request`):HTTP 请求对象(可选用途扩展)。
|
||
|
||
#### 返回值
|
||
- `list`:参数描述数组(来自 `arguments` 字段)。
|
||
- `None`:若未定义 `arguments`。
|
||
|
||
#### 示例返回
|
||
```python
|
||
[
|
||
{
|
||
"name": "stock_num",
|
||
"type": "str",
|
||
"iotype": "text",
|
||
"default": "600804"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### `async getDataDesc(dict_data, ns, request) -> list`
|
||
|
||
异步获取查询结果的字段元信息(即 `datadesc`)。如果尚未生成,则自动从数据库中推导并保存到原文件。
|
||
|
||
#### 参数
|
||
- `dict_data` (`dict`):原始数据源配置。
|
||
- `ns` (`dict`):参数上下文,用于填充 SQL 占位符。
|
||
- `request` (`Request`):请求对象。
|
||
|
||
#### 行为流程
|
||
1. 若 `datadesc` 已存在且非空,直接返回。
|
||
2. 否则:
|
||
- 调用 `runSQLResultFields` 获取 SQL 查询的实际返回字段(不含 `_row_id`)。
|
||
- 将推导出的字段结构写入 `dict_data['datadesc']`。
|
||
- 序列化整个 `dict_data` 回源文件(路径为 `self.src_file`),保持缩进与中文不转义。
|
||
|
||
#### 使用的装饰器
|
||
- `@pool.runSQLResultFields`:执行 SQL 并返回字段元信息(列名、类型等)。
|
||
|
||
#### 返回值
|
||
- `list`:字段描述列表,每项为字段元数据字典(如 `{ "name": "trade_date", "type": "datetime" }`)。
|
||
|
||
#### 示例输出
|
||
```python
|
||
[
|
||
{"name": "stock_num", "type": "string"},
|
||
{"name": "trade_date", "type": "date"},
|
||
{"name": "close_price", "type": "float"}
|
||
]
|
||
```
|
||
|
||
> ✅ 自动持久化:一旦推导完成,会更新 `.sqlds` 文件以避免重复分析。
|
||
|
||
---
|
||
|
||
### `async getData(dict_data, ns, request) -> list`
|
||
|
||
执行完整 SQL 查询并返回所有结果记录。
|
||
|
||
#### 参数
|
||
- `dict_data` (`dict`):数据源定义。
|
||
- `ns` (`dict`):参数上下文。
|
||
- `request` (`Request`):请求对象。
|
||
|
||
#### 行为
|
||
- 使用 `@pool.runSQL` 执行 `sqldesc.sql_string`。
|
||
- 替换 `${}` 中的参数。
|
||
- 返回所有行组成的列表。
|
||
|
||
#### 使用的装饰器
|
||
- `@pool.runSQL`:执行查询并返回异步迭代器,转换为列表。
|
||
|
||
#### 返回值
|
||
- `list[dict]`:每条记录为一个字典。
|
||
|
||
#### 示例返回
|
||
```python
|
||
[
|
||
{"stock_num": "600804", "trade_date": "2023-09-01", "close_price": 12.5},
|
||
{"stock_num": "600804", "trade_date": "2023-08-31", "close_price": 12.3}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### `async getPagingData(dict_data, ns, request) -> dict`
|
||
|
||
执行分页查询,返回带分页信息的结果集。
|
||
|
||
#### 参数
|
||
- `dict_data` (`dict`)
|
||
- `ns` (`dict`):必须包含分页参数,如 `page`, `pageSize` 或等效字段。
|
||
- `request` (`Request`)
|
||
|
||
#### 行为
|
||
- 使用 `@pool.runSQLPaging` 执行分页 SQL。
|
||
- 自动处理偏移量和限制数量。
|
||
- 返回结构化分页响应。
|
||
|
||
#### 使用的装饰器
|
||
- `@pool.runSQLPaging`:支持分页的 SQL 执行器,返回包含 `data`, `total`, `page`, `pageSize` 的对象。
|
||
|
||
#### 返回值(示例)
|
||
```json
|
||
{
|
||
"data": [...],
|
||
"total": 100,
|
||
"page": 1,
|
||
"pageSize": 10
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 内部机制说明
|
||
|
||
### SQL 模板替换
|
||
|
||
使用 `${param}$` 语法进行变量注入,例如:
|
||
|
||
```sql
|
||
select * from table where id = ${user_id}$
|
||
```
|
||
|
||
运行时由 `DBPools` 的装饰器根据 `ns` 字典替换为实际值。
|
||
|
||
### 自动字段推导与缓存
|
||
|
||
首次访问 `getDataDesc` 时,若 `datadesc` 为空,系统将:
|
||
1. 执行 SQL 获取字段结构;
|
||
2. 过滤掉内部字段 `_row_id`;
|
||
3. 写入本地 `.sqlds` 文件以供后续使用。
|
||
|
||
这提高了性能并减少了对数据库元数据的频繁访问。
|
||
|
||
### 编码与文件写入
|
||
|
||
使用 `codecs.open(..., encoding=config.website.coding)` 确保文件按项目设定编码保存(通常是 UTF-8),并通过 `ensure_ascii=False` 保留中文字符。
|
||
|
||
---
|
||
|
||
## 使用场景
|
||
|
||
适合以下情况:
|
||
- 需要灵活配置 SQL 查询作为 API 输出。
|
||
- 前端需要知道返回字段结构(用于表格渲染)。
|
||
- 支持参数化查询和分页。
|
||
- 开发阶段快速原型设计,无需编写后端代码。
|
||
|
||
---
|
||
|
||
## 示例 `.sqlds` 文件应用
|
||
|
||
假设文件名为 `stock_history.sqlds`,配置如下:
|
||
|
||
```json
|
||
{
|
||
"sqldesc": {
|
||
"sql_string": "SELECT stock_num, trade_date, open, high, low, close FROM stock_daily WHERE stock_num = ${code}$ ORDER BY trade_date DESC",
|
||
"db": "financial_db",
|
||
"sortfield": "trade_date"
|
||
},
|
||
"arguments": [
|
||
{
|
||
"name": "code",
|
||
"type": "str",
|
||
"iotype": "text",
|
||
"default": "600804"
|
||
}
|
||
],
|
||
"datadesc": []
|
||
}
|
||
```
|
||
|
||
调用 `getDataDesc()` 后,`datadesc` 将被自动填充为:
|
||
|
||
```json
|
||
[
|
||
{"name": "stock_num", "type": "string"},
|
||
{"name": "trade_date", "type": "date"},
|
||
{"name": "open", "type": "float"},
|
||
{"name": "high", "type": "float"},
|
||
{"name": "low", "type": "float"},
|
||
{"name": "close", "type": "float"}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
1. **安全性**:确保 `${}` 参数经过验证,防止 SQL 注入(建议配合白名单或类型校验)。
|
||
2. **性能**:大数据集应优先使用 `getPagingData`。
|
||
3. **文件权限**:写回 `.sqlds` 文件时需保证进程有写权限。
|
||
4. **缓存失效**:修改 SQL 后建议手动清除 `datadesc` 以触发重新推导。
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
`SQLDataSourceProcessor` 提供了一种声明式的方式来定义基于 SQL 的数据接口,结合配置文件与异步数据库访问,实现了高效、可维护的数据服务层组件。特别适用于报表、配置化页面、低代码平台等场景。 |