From 0e3e982a4b12c4975b094e9485792ac5f56dcc69 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 14 Oct 2025 17:34:55 +0800 Subject: [PATCH] bugfix --- sqlor/dbpools.py | 3 ++ sqlor/duckdbor.py | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 sqlor/duckdbor.py diff --git a/sqlor/dbpools.py b/sqlor/dbpools.py index a29f935..3230d28 100755 --- a/sqlor/dbpools.py +++ b/sqlor/dbpools.py @@ -221,6 +221,9 @@ class DBPools: await sqlor.commit() await self.freeSqlor(sqlor) + def get_exception(self): + return self.e_except + async def _aquireConn(self,dbname): """ p = self._cpools.get(dbname) diff --git a/sqlor/duckdbor.py b/sqlor/duckdbor.py new file mode 100644 index 0000000..41a09f4 --- /dev/null +++ b/sqlor/duckdbor.py @@ -0,0 +1,124 @@ +# -*- coding:utf8 -*- +from appPublic.argsConvert import ArgsConvert, ConditionConvert +from .sor import SQLor +from .const import ROWS + +duckdb_ddl_tmpl = { + # 可以按需扩展 + 'create_table': "CREATE TABLE {table} ({fields})", + 'drop_table': "DROP TABLE IF EXISTS {table}", +} + +class DuckDBor(SQLor): + """ + sqlor 方言适配:DuckDB + """ + ddl_template = duckdb_ddl_tmpl + + db2modelTypeMapping = { + 'boolean': 'short', + 'smallint': 'short', + 'integer': 'long', + 'bigint': 'long', + 'double': 'float', + 'float': 'float', + 'real': 'float', + 'varchar': 'str', + 'char': 'char', + 'string': 'str', + 'blob': 'bin', + 'date': 'date', + 'timestamp': 'datetime', + } + + model2dbTypemapping = { + 'date': 'date', + 'time': 'time', + 'timestamp': 'timestamp', + 'str': 'varchar', + 'char': 'char', + 'short': 'integer', + 'long': 'bigint', + 'float': 'double', + 'text': 'varchar', + 'bin': 'blob', + 'file': 'blob', + } + + @classmethod + def isMe(cls, name): + return name.lower() in ['duckdb'] + + def grammar(self): + # duckdb 使用标准 SQL,通常不需要特殊 select_stmt + return {} + + def placeHolder(self, varname, pos=None): + if varname == '__mainsql__': + return '' + return '?' + + def dataConvert(self, dataList): + if isinstance(dataList, dict): + d = list(dataList.values()) + else: + d = [i['value'] for i in dataList] + return tuple(d) + + def pagingSQLmodel(self): + # DuckDB 兼容标准 SQL LIMIT/OFFSET + return """SELECT * FROM (%s) AS A ORDER BY $[sort]$ LIMIT $[rows]$ OFFSET $[from_line]$""" + + def tablesSQL(self): + # DuckDB 不支持 INFORMATION_SCHEMA.TABLES 完全一致的结构 + sqlcmd = """ + SELECT lower(name) AS name, '' AS title + FROM duckdb_tables() + WHERE database_name = current_database(); + """ + return sqlcmd + + def fieldsSQL(self, tablename=None): + sqlcmd = """ + SELECT + lower(column_name) AS name, + lower(column_type) AS type, + NULL AS length, + NULL AS dec, + 'YES' AS nullable, + '' AS title, + lower(table_name) AS table_name + FROM duckdb_columns() + WHERE database_name = current_database() + """ + if tablename is not None: + sqlcmd += f" AND lower(table_name) = '{tablename.lower()}'" + return sqlcmd + + def fkSQL(self, tablename=None): + # DuckDB 暂不支持外键约束查询 + sqlcmd = "SELECT NULL AS constraint_name WHERE 1=0" + return sqlcmd + + def pkSQL(self, tablename=None): + sqlcmd = f""" + SELECT column_name AS name + FROM duckdb_constraints() + WHERE constraint_type = 'PRIMARY KEY' + AND table_name = '{tablename.lower()}' + """ + return sqlcmd + + def indexesSQL(self, tablename=None): + sqlcmd = """ + SELECT + lower(index_name) AS index_name, + '' AS is_unique, + lower(column_name) AS column_name + FROM duckdb_indexes() + WHERE database_name = current_database() + """ + if tablename is not None: + sqlcmd += f" AND lower(table_name) = '{tablename.lower()}'" + return sqlcmd +