# SQLor 技术文档 `SQLor` 是一个用于简化数据库操作的异步 Python 数据库访问类,支持参数化 SQL 执行、分页查询、CRUD 操作(Create, Read, Update, Delete)、元数据获取、动态 SQL 构建等功能。它设计为可扩展,适用于多种数据库后端。 --- ## 目录 1. [概述](#概述) 2. [依赖与环境要求](#依赖与环境要求) 3. [核心功能](#核心功能) 4. [主要类与方法说明](#主要类与方法说明) - [`db_type_2_py_type`](#db_type_2_py_type) - [`SQLorException`](#sqlorexception) - [`findNamedParameters`](#findnamedparameters) - [`uniParams`](#uniparams) - [`readsql`](#readsql) - [`SQLor` 类](#sqlor-类) - 初始化 - 元数据管理 - 连接与游标控制 - SQL 处理与转换 - 查询执行 - 分页与排序 - 聚合操作(Pivot) - 表结构信息获取 - DDL 与表定义操作 - CRUD 操作(C/U/R/D) 5. [使用示例](#使用示例) 6. [异常处理](#异常处理) 7. [配置说明](#配置说明) --- ## 概述 `SQLor` 提供了一个统一接口来执行 SQL 查询和命令,并通过命名参数 `${var}` 支持模板化 SQL。其主要特点包括: - 异步/同步双模式支持。 - 参数自动替换与安全绑定。 - 自动识别 `SELECT`, `INSERT/UPDATE/DELETE`, `DDL` 类型并返回相应结果。 - 支持分页、排序、条件过滤。 - 内置对表结构(字段、主键、外键、索引)的元数据查询。 - 支持自定义函数注册钩子(before/after 钩子)。 - 可扩展的类型转换机制。 - 简化的 CRUD 接口(I/C/R/U/D)。 --- ## 依赖与环境要求 ### 第三方依赖 ```python from appPublic.myImport import myImport from appPublic.dictObject import DictObject from appPublic.unicoding import uDict from appPublic.myTE import MyTemplateEngine from appPublic.objectAction import ObjectAction from appPublic.argsConvert import ArgsConvert, ConditionConvert from appPublic.registerfunction import RegisterFunction from appPublic.log import info, exception, debug ``` > ⚠️ 注意:这些模块属于 `appPublic` 包,需确保已安装或项目中包含该包。 ### Python 内置模块 - `os` - `sys` - `decimal` - `datetime` - `codecs` - `re` - `json` - `traceback` ### 环境变量 ```python os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8' ``` > 仅在 Oracle 数据库环境下需要设置 NLS 编码以支持中文 UTF-8。 --- ## 核心功能 | 功能 | 描述 | |------|------| | 参数化 SQL | 使用 `${var}` 占位符进行变量注入 | | 异步支持 | 支持 `async/await` 模式运行 SQL | | 分页查询 | 自动生成分页 SQL(需子类实现模型) | | 排序与过滤 | 支持动态排序字段和条件表达式 | | 元数据提取 | 获取表、字段、主键、索引等结构信息 | | CRUD 封装 | 提供 `C()`(创建), `R()`(读取), `U()`(更新), `D()`(删除) 方法 | | 回调机制 | 支持逐行处理查询结果 | | 类型转换 | 自动将数据库类型转为 Python 原生类型 | --- ## 主要类与方法说明 ### `db_type_2_py_type(o)` 将特定数据库类型转换为 Python 原生类型。 #### 参数 - `o`: 数据库字段值 #### 返回值 | 输入类型 | 输出 | |--------|-------| | `decimal.Decimal` | `float` | | `datetime.datetime` | 字符串格式 `'YYYY-MM-DD HH:MM:SS'` | | `datetime.date` | `'YYYY-MM-DD'` | | 其他 | 原值 | #### 示例 ```python db_type_2_py_type(decimal.Decimal('3.14')) → 3.14 ``` --- ### `SQLorException` 自定义异常类,封装 SQL 执行错误。 > ❗ 当前代码存在拼写错误:`__int__` 应为 `__init__`,且 `supper` 应为 `super`。 #### 属性 - `response`: `"error"` - `errtype`: `"SQLor"` - `errmsg`: 异常消息字符串 #### 示例输出 ``` errtype:SQLor,errmsg=database error... ``` > ✅ **建议修复**: ```python class SQLException(Exception): def __init__(self, **kvs): super(SQLException, self).__init__(**kvs) self.dic = { 'response': 'error', 'errtype': 'SQLor', 'errmsg': str(self) } def __str__(self): return 'errtype:%s,errmsg=%s' % (self.dic['errtype'], self.dic['errmsg']) ``` --- ### `setValues(params, ns)` 从命名空间 `ns` 或系统环境变量中查找参数值。 #### 参数 - `params`: 参数名(字符串) - `ns`: 命名空间字典 #### 返回 优先从 `ns` 中取值,若无则尝试 `os.getenv(params)`。 --- ### `findNamedParameters(sql)` 解析 SQL 字符串中的所有 `${...}` 形式的命名参数。 #### 示例 ```python sql = "SELECT * FROM user WHERE id = ${uid}$ AND name = ${uname}$" findNamedParameters(sql) # 返回 ['${uid}$', '${uname}$'] ``` --- ### `uniParams(params1)` 去重保留唯一参数名列表。 #### 示例 ```python uniParams(['${a}$', '${b}$', '${a}$']) → ['${a}$', '${b}$'] ``` --- ### `readsql(fn)` 读取指定文件路径的 SQL 文件内容(UTF-8 编码)。 #### 参数 - `fn`: 文件路径 #### 返回 文件内容字符串。 --- ## `SQLor` 类 ### 初始化 `__init__` #### 参数 | 参数 | 默认值 | 说明 | |------|--------|------| | `dbdesc` | None | 数据库描述字典,至少含 `dbname` | | `sqltp`, `sqlts` | `$[`, `]$` | 模板占位符开始/结束符号 | | `sqlvp`, `sqlvs` | `${`, `}$` | 变量占位符开始/结束符号 | #### 初始化行为 - 设置连接状态(`conn`, `cur`) - 初始化元数据缓存 `metadatas` - 创建 `ConditionConvert` 实例用于条件表达式处理 --- ### 元数据管理 #### `setMeta(tablename, meta)` 保存表的元数据到内存缓存。 #### `getMeta(tablename)` 获取指定表的缓存元数据。 #### `removeMeta(tablename)` 清除指定表的元数据缓存。 --- ### 连接与游标控制 #### `setCursor(async_mode, conn, cur)` 绑定数据库连接和游标对象。 #### `getConn()` 返回当前数据库连接对象。 #### `cursor()` 返回当前游标对象。 --- ### 类型转换支持 #### `setConvertFunction(typ, func)` 注册用户自定义类型转换函数。 #### `convert(typ, value)` 调用注册的转换函数处理值。 --- ### SQL 类型判断 #### `getSqlType(sql)` 判断 SQL 语句类型。 | 类型 | 条件 | |------|------| | `"qry"` | 以 `SELECT` 开头 | | `"dml"` | `INSERT`, `UPDATE`, `DELETE` | | `"ddl"` | 其他(如 `CREATE`, `DROP`) | --- ### SQL 模板处理 #### `maskingSQL(org_sql, NS)` 将命名参数 `${var}$` 替换为 `?` 并生成参数列表。 ##### 步骤 1. 先替换 `$[...]$` 模板片段(如条件块) 2. 提取 `${var}$` 参数名 3. 替换为 `?` 占位符 4. 返回 `(标记后的SQL, 参数值列表)` ##### 特殊变量 - `__mainsql__`: 不参与参数绑定,用于嵌套 SQL 注入 --- ### 执行方法 #### `execute(sql, value, callback, **kwargs)` 执行单条 SQL,支持回调逐行处理结果。 ##### 流程 - 调用 `runVarSQL` 执行 SQL - 若是查询 (`qry`) 且有回调,则逐行调用 `callback(DictObject(row))` - 若是 DML,标记 `dataChanged=True` #### `executemany(sql, values)` 批量执行 SQL(适用于 `INSERT` 等) --- ### 分页与排序 #### `sortSQL(sql, NS)` 添加 `ORDER BY` 子句。 - 支持 `NS['sort']` 为字符串或列表 #### `pagingSQL(sql, paging, NS)` 构建分页 SQL,需配合 `pagingSQLmodel()` 使用。 > 默认为空,需由子类重写提供具体数据库分页语法(如 MySQL 的 `LIMIT OFFSET` 或 Oracle 的 `ROWNUM`) ##### 参数 - `pagename`: 页码参数名(默认 `'page'`) - `rowsname`: 每页行数(默认 `'rows'`) - `sortname`: 排序字段(可选) ##### 示例 NS ```python { 'page': 2, 'rows': 20, 'sort': 'id desc' } ``` #### `recordCnt(sql)` 包装 SQL 查询总记录数: ```sql SELECT COUNT(*) rcnt FROM (your_sql) rowcount_table ``` --- ### 过滤器支持 #### `filterSQL(sql, filters, NS)` 将一组条件过滤器合并进原 SQL。 每个 filter 是一个带 `${}` 的模板字符串,若其中变量未在 `NS` 中存在,则忽略此条件(替换为 `1=1`)。 最终生成: ```sql SELECT * FROM (original_sql) filter_table WHERE f1 AND f2 ... ``` --- ### Pivot 表格转换 #### `pivotSQL(tablename, rowFields, columnFields, valueFields)` 生成透视表 SQL。 ##### 逻辑 1. 查询 `columnFields` 的所有唯一值作为列头 2. 使用 `CASE WHEN` 构造每列聚合 3. 使用 `SUM` 聚合数值字段 ##### 示例输出 ```sql SELECT dept, SUM(salary_0) salary_0, -- 北京 SUM(salary_1) salary_1 -- 上海 FROM ( SELECT dept, CASE WHEN city='北京' THEN salary ELSE 0 END AS salary_0, CASE WHEN city='上海' THEN salary ELSE 0 END AS salary_1 FROM employees ) GROUP BY dept; ``` #### `pivot(desc, ...)` 执行 pivot 查询并返回结果列表。 --- ### 元数据查询方法 #### `tables()` 返回数据库中所有表的列表。 #### `indexes(tablename)` 返回某表的所有索引信息(需子类实现 `indexesSQL()`) #### `fields(tablename)` 返回字段列表,包含名称、类型(映射后),例如: ```python [ {"name": "id", "type": "int"}, {"name": "name", "type": "string"} ] ``` #### `primary(tablename)` 返回主键字段列表。 #### `fkeys(tablename)` 返回外键关系。 #### `getTableDesc(tablename)` 综合获取表的完整描述(summary + fields + indexes),并缓存至 `metadatas`。 --- ### DDL 与表操作 #### `createTable(tabledesc)` 使用模板引擎渲染建表语句并执行。 依赖 `self.ddl_template` 和 `MyTemplateEngine`。 --- ### 事务控制 #### `commit()` 提交事务,重置 `dataChanged=False` #### `rollback()` 回滚事务 --- ### CRUD 接口(I/C/R/U/D) 遵循 RESTful 风格简写: #### `I(tablename)` 等价于 `getTableDesc()` — 获取表结构定义。 #### `C(tablename, ns)` 插入新记录。 - 自动提取 `ns` 中存在于表字段的部分 - 触发 `before` / `after` 钩子函数 - 生成 INSERT 语句 #### `R(tablename, ns, filters=None)` 读取记录。 - 若传入 `page` 参数 → 返回分页 `{total, rows}` - 否则返回匹配行列表 - 支持简单等值条件或复杂 `DBFilter` #### `U(tablename, ns)` 更新记录。 - 使用主键做 WHERE 条件 - 更新非主键字段 - 支持钩子函数 #### `D(tablename, ns)` 删除记录。 - 使用主键匹配删除 - 支持钩子函数 --- ## 使用示例 ### 1. 初始化 SQLor 实例 ```python dbdesc = { 'dbname': 'mydb' } sqlor = SQLor(dbdesc=dbdesc) sqlor.setCursor(False, conn, cursor) # 同步模式 ``` ### 2. 执行参数化查询 ```python sql = "SELECT * FROM users WHERE age > ${min_age}$" result = [] await sqlor.execute(sql, {'min_age': 18}, lambda rec: result.append(rec)) ``` ### 3. 分页查询 ```python desc = { "sql_string": "SELECT id, name FROM users", "sortfield": "name" } ns = {"page": 1, "rows": 10} data = await sqlor.runSQLPaging(desc, ns) # → {"total": 100, "rows": [...]} ``` ### 4. CRUD 操作 ```python # 创建 await sqlor.C("users", {"name": "Alice", "age": 25}) # 读取(分页) res = await sqlor.R("users", {"page": 1, "rows": 10}) # 更新 await sqlor.U("users", {"id": 1, "age": 26}) # 删除 await sqlor.D("users", {"id": 1}) ``` ### 5. 获取表结构 ```python desc = await sqlor.I("users") print(desc['fields']) ``` --- ## 异常处理 所有 SQL 执行异常都会被捕获并记录日志: ```python exception(f"{markedSQL=},{datas=}, {e=}, {fe=}") raise e ``` 推荐外部捕获 `Exception` 或自定义 `SQLException`。 --- ## 配置说明 ### 占位符配置 | 符号 | 默认 | 用途 | |------|------|------| | `sqltp` / `sqlts` | `$[` / `]$` | 控制结构模板(如 IF) | | `sqlvp` / `sqlvs` | `${` / `}$` | 变量参数占位符 | 示例: ```sql SELECT * FROM t WHERE status = ${status}$ $[ IF active ]$ AND active = 1 $[ ENDIF ]$ ``` ### 分页模型扩展 子类应重写 `pagingSQLmodel()` 提供数据库特定的分页语法。 例如 MySQL 子类: ```python def pagingSQLmodel(self): return "SELECT * FROM (%s) t LIMIT ${rows}$ OFFSET ${from_line}$" ``` Oracle 示例: ```python def pagingSQLmodel(self): return """ SELECT * FROM ( SELECT a.*, ROWNUM rn FROM ( %s ORDER BY ${sort}$ ) a WHERE ROWNUM <= ${end_line}$ ) WHERE rn > ${from_line}$ """ ``` --- ## 总结 `SQLor` 是一个轻量级但功能完整的数据库抽象层,适合用于 Web API 后端、ETL 工具或管理后台的数据访问组件。其优势在于: ✅ 清晰的 CRUD 接口 ✅ 支持异步高性能场景 ✅ 安全的参数绑定 ✅ 易于扩展不同数据库方言 > ⚠️ **待改进点**: - 修复 `SQLException.__init__` 错误 - `commit()` 中 `self.datachanged` 应为 `self.dataChanged` - `pagingdata()` 中 `cnt_desc` 未定义 - `setMeta` 中 `getMeta(self.tablename)` 应为 `self.getMeta(...)` --- 📝 **版本**: v1.0 📅 **最后更新**: 2025-04-05 👨‍💻 **作者**: Auto-generated Documentation Tool