This commit is contained in:
shufeng2012 committed 2026-07-24 06:35:46 +08:00
1 parent 0603c473fa
commit 03d7b07bf6
5 files changed
+334 -231

No files matched your search

+83 -187
View File
@@ -1,66 +1,96 @@
# 跨数据库通用查询服务(基于 SQLAlchemy Core)
# Flask SQLite 执行服务
一个轻量级的 Flask HTTP 服务,用于安全地执行带参数的 SQLite SQL 语句,支持 `?` 和命名参数占位符,杜绝 SQL 注入。
一个安全的 HTTP 服务,通过结构化 JSON 描述查询意图,后端自动转换为对应数据库的 SQL,无需手写 SQL,即可无缝切换 SQLite、PostgreSQL、MySQL 等数据库。
## 功能特点
- 通过 JSON 接口发送 SQL 和参数
- 强制使用参数绑定(`execute(sql, params)`),无 SQL 注入风险
- 统一返回格式,所有语句执行结果均包含 `data` 数组
- 自动事务管理,异常时回滚
- 单线程单进程模式,绑定 127.0.0.1,仅本地访问
- **跨数据库**:基于 SQLAlchemy Core 表达式语言,一套接口适配多种数据库
- **零 SQL 注入**:所有条件、值均通过 SQLAlchemy 参数绑定处理
- **统一 JSON 接口**:`action` + `table` + `conditions` 模式
- **支持增删改查**:select, insert, update, delete 全覆盖
- **单线程本地监听**:waitress + 127.0.0.1:8005,简单可靠
## 快速开始
### 环境要求
- Python 3.7+
- pip
### 安装依赖
```bash
pip install flask waitress
pip install flask sqlalchemy waitress
# 根据需要安装数据库驱动,例如:
# pip install psycopg2-binary # PostgreSQL
# pip install pymysql # MySQL
```
### 启动服务
### 启动服务(默认 SQLite)
```bash
python app.py
```
服务运行在 `http://127.0.0.1:8005`。
服务运行在 `http://127.0.0.1:5000`,仅允许本地访问。数据库文件 `database.db` 会在首次连接时自动创建(可提前建表或通过接口执行 `CREATE TABLE`)。
使用其他数据库:
```bash
export DATABASE_URL="postgresql://user:pass@localhost/dbname"
python app.py
```
**注意**:新接口依赖表已存在(通过反射获取结构)。首次使用时请先手动建表:
```bash
sqlite3 database.db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)"
```
## API 文档
### 执行 SQL 语句
### 请求格式
**端点**:`POST /execute`
**请求格式**:`Content-Type: application/json`
**端点**:`POST /execute`
**Content-Type**:`application/json`
```json
{
"sql": "SQL 语句,使用 ? 或 :key 作为占位符",
"params": [] 或 {}
"action": "操作类型", // select | insert | update | delete
"table": "表名",
"columns": ["列1", "列2"], // select/insert 时可选
"values": { // insert/update 时必填
"列名": "值"
},
"where": { // 条件,支持组合操作符
"age": {"ge": 18, "lt": 60},
"name": "Alice"
},
"order_by": [{"column": "age", "desc": true}],
"limit": 10,
"offset": 0
}
```
- `sql`:待执行的 SQL 语句(仅允许单条语句)
- `params`:参数列表 `[值1, 值2, ...]` 或参数字典 `{"key": 值}`
### where 条件支持的运算符
**成功响应**:
| 运算符 | 说明 | 示例 |
|--------|------|------|
| 直接值 | 等于 | `"name": "Alice"` |
| eq | 等于 | `"age": {"eq": 30}` |
| ne | 不等于 | `"status": {"ne": "deleted"}` |
| gt | 大于 | `"score": {"gt": 80}` |
| ge | 大于等于 | `"score": {"ge": 60}` |
| lt | 小于 | `"price": {"lt": 100}` |
| le | 小于等于 | `"price": {"le": 200}` |
| like | 模糊匹配 | `"name": {"like": "%Smith%"}` |
| in | 包含于列表 | `"id": {"in": [1,2,3]}` |
多个条件自动使用 **AND** 连接。
### 响应格式
成功:
```json
{
"success": true,
"data": [] // 查询结果数组,每条记录为字典;非查询语句返回空数组
"data": [ // 查询结果数组(insert/update/delete 时包含影响行数或 lastrowid)
{"id": 1, "name": "Alice"}
]
}
```
**错误响应**:
失败:
```json
{
"success": false,
@@ -68,184 +98,50 @@ python app.py
}
```
## 使用示例
以下示例均通过 `curl` 和 Python `requests` 库演示。
### 1. 创建数据表
## 使用示例(curl)
**插入**:
```bash
curl -X POST http://127.0.0.1:5000/execute \
curl -X POST http://127.0.0.1:8005/execute \
-H "Content-Type: application/json" \
-d '{"sql": "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER)"}'
-d '{"action":"insert","table":"users","values":{"name":"Bob","age":25}}'
```
Python 版本:
```python
import requests
url = "http://127.0.0.1:5000/execute"
payload = {
"sql": "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER)"
}
resp = requests.post(url, json=payload)
print(resp.json()) # {"success": true, "data": []}
```
### 2. 插入数据(使用 `?` 占位符)
**查询**:
```bash
curl -X POST http://127.0.0.1:5000/execute \
curl -X POST http://127.0.0.1:8005/execute \
-H "Content-Type: application/json" \
-d '{"sql": "INSERT INTO users (name, age) VALUES (?, ?)", "params": ["Alice", 30]}'
-d '{"action":"select","table":"users","where":{"age":{"gt":20}},"order_by":[{"column":"id","desc":true}],"limit":5}'
```
Python 版本:
```python
payload = {
"sql": "INSERT INTO users (name, age) VALUES (?, ?)",
"params": ["Bob", 25]
}
resp = requests.post(url, json=payload)
print(resp.json()) # {"success": true, "data": []}
```
### 3. 插入数据(使用命名参数 `:key`)
**更新**:
```bash
curl -X POST http://127.0.0.1:5000/execute \
curl -X POST http://127.0.0.1:8005/execute \
-H "Content-Type: application/json" \
-d '{"sql": "INSERT INTO users (name, age) VALUES (:name, :age)", "params": {"name": "Charlie", "age": 28}}'
-d '{"action":"update","table":"users","values":{"age":26},"where":{"name":"Bob"}}'
```
### 4. 查询全部数据
**删除**:
```bash
curl -X POST http://127.0.0.1:5000/execute \
curl -X POST http://127.0.0.1:8005/execute \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT * FROM users"}'
```
响应示例:
```json
{
"success": true,
"data": [
{"id": 1, "name": "Alice", "age": 30},
{"id": 2, "name": "Bob", "age": 25},
{"id": 3, "name": "Charlie", "age": 28}
]
}
```
Python 版本:
```python
payload = {"sql": "SELECT * FROM users"}
resp = requests.post(url, json=payload)
users = resp.json()["data"]
for user in users:
print(user["name"], user["age"])
```
### 5. 按条件查询
```bash
curl -X POST http://127.0.0.1:5000/execute \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT * FROM users WHERE age > ?", "params": [26]}'
```
Python 版本:
```python
payload = {
"sql": "SELECT * FROM users WHERE age > :min_age",
"params": {"min_age": 26}
}
resp = requests.post(url, json=payload)
print(resp.json()["data"])
```
### 6. 更新数据
```bash
curl -X POST http://127.0.0.1:5000/execute \
-H "Content-Type: application/json" \
-d '{"sql": "UPDATE users SET age = ? WHERE name = ?", "params": [31, "Alice"]}'
```
Python 版本:
```python
payload = {
"sql": "UPDATE users SET age = :new_age WHERE id = :user_id",
"params": {"new_age": 26, "user_id": 2}
}
resp = requests.post(url, json=payload)
print(resp.json()) # {"success": true, "data": []}
```
### 7. 删除数据
```bash
curl -X POST http://127.0.0.1:5000/execute \
-H "Content-Type: application/json" \
-d '{"sql": "DELETE FROM users WHERE id = ?", "params": [3]}'
```
Python 版本:
```python
payload = {
"sql": "DELETE FROM users WHERE name = :name",
"params": {"name": "Charlie"}
}
resp = requests.post(url, json=payload)
print(resp.json())
```
### 8. 错误处理示例
当 SQL 或参数错误时,返回明确的错误信息:
```bash
curl -X POST http://127.0.0.1:5000/execute \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT * FROM nonexistent_table"}'
```
响应:
```json
{
"success": false,
"error": "no such table: nonexistent_table"
}
-d '{"action":"delete","table":"users","where":{"id":{"in":[1,2]}}}'
```
## 安全机制
1. **参数绑定**:使用 `sqlite3` 官方推荐的 `execute(sql, params)` 方式传参,参数值与 SQL 结构完全分离。
2. **单语句执行**:`sqlite3.execute()` 仅执行第一条语句,无法通过分号注入多条命令。
3. **错误回滚**:若执行过程中出现异常,已修改的数据会回滚,确保数据一致性。
4. **本地绑定**:服务监听 `127.0.0.1`,仅本机可访问,避免网络暴露风险。
- 强制使用 SQLAlchemy 参数绑定,杜绝 SQL 注入。
- 仅反射已有表,无法创建或删除表(保证结构安全)。
- 所有异常自动回滚事务。
- 默认监听 `127.0.0.1`,仅本机可访问。
## 项目结构
## 切换数据库
```
.
├── app.py # 主程序(使用 waitress 单线程模式)
└── README.md # 本文档
```
只需修改环境变量 `DATABASE_URL`,并安装对应驱动:
数据库文件 `database.db` 会在运行时自动生成。
- SQLite:`sqlite:///database.db`
- PostgreSQL:`postgresql://user:pass@localhost/dbname` (需 `pip install psycopg2`)
- MySQL:`mysql+pymysql://user:pass@localhost/dbname` (需 `pip install pymysql`)
## 注意事项
SQLAlchemy 会自动生成适配各数据库的 SQL 方言,无需修改接口调用。
- 服务采用 waitress 单线程模式(`threads=1`),确保请求顺序执行,适合低并发场景。
- 如需处理更高并发,可适当增加 `threads` 参数(例如 `threads=4`),但仍需注意 SQLite 的并发限制。
- 该服务无认证机制,推荐仅在本机或内网可信环境使用。如需对外服务,请添加鉴权并考虑使用 nginx 等反向代理。