This commit is contained in:
yumoqing 2026-04-14 17:02:04 +08:00
parent 0859f8162d
commit 472ce1647a
20 changed files with 232 additions and 27 deletions

0
sqlor/__init__.py Executable file → Normal file
View File

0
sqlor/aiopostgresqlor.py Executable file → Normal file
View File

84
sqlor/aiosqliteor.py Executable file → Normal file
View File

@ -1,7 +1,85 @@
import re
from .sqlite3or import SQLite3or
import aiosqlite
from .sor import SQLor
class Aiosqliteor(SQLite3or):
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class Aiosqliteor(SQLor):
@classmethod
def isMe(self,name):
return name=='aiosqlite'
async def connect(self):
"""
dbdesc:
path: 数据库文件路径 :memory:
"""
dbdesc = self.dbdesc
self.dbpath = dbdesc.get('path', ':memory:')
self.conn = await aiosqlite.connect(self.dbpath)
self.conn.row_factory = aiosqlite.Row # 支持 dict 访问
self.dbname = self.dbpath
async def close(self):
await self.conn.close()
async def enter(self):
"""开启事务"""
self.cur = await self.conn.cursor()
async def exit(self):
"""释放 cursor"""
try:
await self.cur.close()
except:
pass
self.cur = None
def grammar(self):
return {
'select':select_stmt,
}
def placeHolder(self,varname,i):
if varname=='__mainsql__' :
return ''
return '?'
def dataConvert(self,dataList):
if type(dataList) == type({}):
return dataList
d = { i['name']:i['value'] for i in dataList }
return d
def pagingSQLmodel(self):
return u"""select * from (%s) page_s limit $[page_size]$ offset $[offset]$"""
def tablesSQL(self):
sqlcmd = """SELECT name,sql as title FROM sqlite_master WHERE type='table'"""
return sqlcmd
def fieldsSQL(self,tablename=None):
sqlcmd="""PRAGMA table_info(%s);""" % tablename
return sqlcmd
def fkSQL(self,tablename=None):
tablename = tablename.lower()
sqlcmd = """PRAGMA foreign_key_list('%s');""" % tablename
return sqlcmd
def pkSQL(self,tablename=None):
tablename = tablename.lower()
sqlcmd="""PRAGMA table_info('%s');""" % tablename
return sqlcmd
def indexesSQL(self,tablename=None):
sqlcmd = """PRAGMA index_list('%s');""" % tablename
return sqlcmd

0
sqlor/const.py Executable file → Normal file
View File

0
sqlor/dbpools.old.py Executable file → Normal file
View File

0
sqlor/dbpools.py Executable file → Normal file
View File

0
sqlor/ddl_template_mysql.py Executable file → Normal file
View File

0
sqlor/ddl_template_oracle.py Executable file → Normal file
View File

0
sqlor/ddl_template_postgresql.py Executable file → Normal file
View File

0
sqlor/ddl_template_sqlite3.py Executable file → Normal file
View File

0
sqlor/ddl_template_sqlserver.py Executable file → Normal file
View File

0
sqlor/filter.py Executable file → Normal file
View File

49
sqlor/mssqlor.py Executable file → Normal file
View File

@ -2,6 +2,17 @@
from .sor import SQLor
from .ddl_template_sqlserver import sqlserver_ddl_tmpl
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class MsSqlor(SQLor):
ddl_template = sqlserver_ddl_tmpl
db2modelTypeMapping = {
@ -86,9 +97,9 @@ where _row_id >= $[from_line]$ and _row_id < $[end_line]$"""
,length = Columnproperty(a.id,a.name,'PRECISION')
,dec = Isnull(Columnproperty(a.id,a.name,'Scale'),null)
,nullable = CASE
WHEN a.isnullable = 1 THEN 'yes'
ELSE 'no'
END
WHEN a.isnullable = 1 THEN 'yes'
ELSE 'no'
END
,title = lower(cast(Isnull(g.[value],a.name) as nvarchar) )
,table_name = lower(d.name)
FROM syscolumns a
@ -98,7 +109,7 @@ where _row_id >= $[from_line]$ and _row_id < $[end_line]$"""
ON (a.id = d.id)
AND (d.xtype = 'U')
AND (d.name <> 'dtproperties')
INNER JOIN sys.all_objects c
INNER JOIN sys.all_objects c
ON d.id=c.object_id
AND schema_name(schema_id)='dbo'
LEFT JOIN sys.extended_properties g
@ -172,3 +183,33 @@ AND IDXC.Column_id=C.Column_id"""
if tablename is not None:
sqlcmd = sqlcmd + """ where lower(O.name)='%s'""" % tablename.lower()
return sqlcmd
async def connect(self):
"""
Note: pymssql is synchronous. For async support, consider using aioodbc.
This implementation uses threading for now.
"""
import asyncio
import pymssql
dbdesc = self.dbdesc
# pymssql uses different parameter names
conn_params = {
'server': dbdesc.get('host', 'localhost'),
'user': dbdesc.get('user'),
'password': dbdesc.get('password'),
'database': dbdesc.get('db'),
'port': dbdesc.get('port', 1433)
}
# Remove None values
conn_params = {k: v for k, v in conn_params.items() if v is not None}
# Use thread pool for synchronous pymssql
loop = asyncio.get_event_loop()
self.conn = await loop.run_in_executor(None, pymssql.connect, **conn_params)
self.dbname = dbdesc.get('db', '')
async def close(self):
import asyncio
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, self.conn.close)

0
sqlor/mysqlor.py Executable file → Normal file
View File

41
sqlor/oracleor.py Executable file → Normal file
View File

@ -1,5 +1,17 @@
from .sor import SQLor
from .ddl_template_oracle import oracle_ddl_tmpl
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class Oracleor(SQLor):
ddl_template = oracle_ddl_tmpl
db2modelTypeMapping = {
@ -131,3 +143,32 @@ where a.index_name = b.index_name"""
sqlcmd += """ and lower(a.table_name) = lower('%s')""" % tablename.lower()
return sqlcmd
async def connect(self):
"""
Note: cx_Oracle is synchronous. Using thread pool for async support.
"""
import asyncio
import cx_Oracle
dbdesc = self.dbdesc
# cx_Oracle connection string format
dsn = cx_Oracle.makedsn(
host=dbdesc.get('host', 'localhost'),
port=dbdesc.get('port', 1521),
service_name=dbdesc.get('service_name', dbdesc.get('sid', 'XE'))
)
loop = asyncio.get_event_loop()
self.conn = await loop.run_in_executor(
None,
cx_Oracle.connect,
dbdesc.get('user'),
dbdesc.get('password'),
dsn
)
self.dbname = dbdesc.get('service_name', dbdesc.get('sid', ''))
async def close(self):
import asyncio
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, self.conn.close)

23
sqlor/postgresqlor.py Executable file → Normal file
View File

@ -170,7 +170,7 @@ order by
i.relname""" % tablename.lower()
return sqlcmd
async def connect():
async def connect(self):
"""
kwargs:
dbname:
@ -179,13 +179,22 @@ order by
host:
port:
"""
kwargs = self.dbdesc
dns = ' '.join([f'{k}={v}' for k, v in kwargs.items()])
self.conn = await self.connect(dns)
self.cur = await self.conn.cursor()
self.dbname = kwargs.dbname.lower()
dbdesc = self.dbdesc
self.conn = await aiopg.connect(**dbdesc)
self.dbname = dbdesc.get('dbname', '').lower()
async def close():
async def close(self):
await self.cur.close()
await self.conn.close()
async def enter(self):
self.cur = await self.conn.cursor()
async def exit(self):
try:
await self.cur.fetchall()
await self.cur.close()
except:
pass
self.cur = None

0
sqlor/records.py Executable file → Normal file
View File

0
sqlor/sor.py Executable file → Normal file
View File

0
sqlor/sqlite3or.py Executable file → Normal file
View File

View File

@ -4,6 +4,17 @@ from .sor import SQLor
from .const import ROWS
from .ddl_template_mysql import mysql_ddl_tmpl # ✅ 直接复用 MySQL 模板
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class TiDBor(SQLor):
ddl_template = mysql_ddl_tmpl
@ -50,7 +61,7 @@ class TiDBor(SQLor):
def grammar(self):
return {
'select': 'select * from {table} where {condition}',
'select': select_stmt,
}
def placeHolder(self, varname, pos=None):
@ -71,14 +82,14 @@ class TiDBor(SQLor):
def tablesSQL(self):
# ✅ TiDB 支持 INFORMATION_SCHEMA.TABLES
dbname = self.dbdesc.get('dbname', 'unknown')
dbname = self.dbdesc.get('db', 'unknown') # MySQL uses 'db', not 'dbname'
sqlcmd = f"""SELECT lower(TABLE_NAME) AS name, TABLE_COMMENT AS title
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{dbname}'"""
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{dbname}'"""
return sqlcmd
def fieldsSQL(self, tablename=None):
dbname = self.dbdesc.get('dbname', 'unknown').lower()
dbname = self.dbdesc.get('db', 'unknown').lower() # MySQL uses 'db'
sqlcmd = f"""
SELECT
lower(column_name) AS name,
@ -98,7 +109,7 @@ WHERE lower(TABLE_SCHEMA) = '{dbname}'
def fkSQL(self, tablename=None):
# ✅ TiDB 兼容 MySQL 的 FK 元信息
dbname = self.dbdesc.get('dbname', 'unknown').lower()
dbname = self.dbdesc.get('db', 'unknown').lower()
sqlcmd = f"""
SELECT
C.TABLE_SCHEMA AS owner,
@ -121,7 +132,7 @@ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE table_name='{tablename.lower()}' AND constraint_name='PRIMARY'"""
def indexesSQL(self, tablename=None):
dbname = self.dbdesc.get('dbname', 'unknown')
dbname = self.dbdesc.get('db', 'unknown')
sqlcmd = f"""SELECT DISTINCT
lower(index_name) AS index_name,
CASE NON_UNIQUE WHEN 0 THEN 'unique' ELSE '' END AS is_unique,
@ -132,3 +143,28 @@ WHERE table_schema = '{dbname}'"""
sqlcmd += f" AND table_name = '{tablename.lower()}'"
return sqlcmd
async def connect(self):
"""
TiDB 兼容 MySQL 协议使用 aiomysql
"""
import aiomysql
dbdesc = self.dbdesc
# TiDB 使用与 MySQL 相同的连接参数
self.conn = await aiomysql.connect(**dbdesc)
self.dbname = dbdesc.get('db', '')
async def close(self):
await self.cur.close()
await self.conn.close()
async def enter(self):
self.cur = await self.conn.cursor()
async def exit(self):
try:
await self.cur.fetchall()
await self.cur.close()
except:
pass
self.cur = None