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 import aiosqlite
from .sqlite3or import SQLite3or from .sor import SQLor
class Aiosqliteor(SQLite3or): select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class Aiosqliteor(SQLor):
@classmethod @classmethod
def isMe(self,name): def isMe(self,name):
return name=='aiosqlite' 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

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

@ -2,6 +2,17 @@
from .sor import SQLor from .sor import SQLor
from .ddl_template_sqlserver import sqlserver_ddl_tmpl from .ddl_template_sqlserver import sqlserver_ddl_tmpl
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class MsSqlor(SQLor): class MsSqlor(SQLor):
ddl_template = sqlserver_ddl_tmpl ddl_template = sqlserver_ddl_tmpl
db2modelTypeMapping = { db2modelTypeMapping = {
@ -172,3 +183,33 @@ AND IDXC.Column_id=C.Column_id"""
if tablename is not None: if tablename is not None:
sqlcmd = sqlcmd + """ where lower(O.name)='%s'""" % tablename.lower() sqlcmd = sqlcmd + """ where lower(O.name)='%s'""" % tablename.lower()
return sqlcmd 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 .sor import SQLor
from .ddl_template_oracle import oracle_ddl_tmpl from .ddl_template_oracle import oracle_ddl_tmpl
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class Oracleor(SQLor): class Oracleor(SQLor):
ddl_template = oracle_ddl_tmpl ddl_template = oracle_ddl_tmpl
db2modelTypeMapping = { db2modelTypeMapping = {
@ -131,3 +143,32 @@ where a.index_name = b.index_name"""
sqlcmd += """ and lower(a.table_name) = lower('%s')""" % tablename.lower() sqlcmd += """ and lower(a.table_name) = lower('%s')""" % tablename.lower()
return sqlcmd 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() i.relname""" % tablename.lower()
return sqlcmd return sqlcmd
async def connect(): async def connect(self):
""" """
kwargs: kwargs:
dbname: dbname:
@ -179,13 +179,22 @@ order by
host: host:
port: port:
""" """
kwargs = self.dbdesc dbdesc = self.dbdesc
dns = ' '.join([f'{k}={v}' for k, v in kwargs.items()]) self.conn = await aiopg.connect(**dbdesc)
self.conn = await self.connect(dns) self.dbname = dbdesc.get('dbname', '').lower()
self.cur = await self.conn.cursor()
self.dbname = kwargs.dbname.lower()
async def close(): async def close(self):
await self.cur.close() await self.cur.close()
await self.conn.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 .const import ROWS
from .ddl_template_mysql import mysql_ddl_tmpl # ✅ 直接复用 MySQL 模板 from .ddl_template_mysql import mysql_ddl_tmpl # ✅ 直接复用 MySQL 模板
select_stmt = """
select
$[fields]$
from
$[tables]$
$[whereclause]$
$[groupbyclause]$
$[havingclause]$
$[orderbyclause]$
"""
class TiDBor(SQLor): class TiDBor(SQLor):
ddl_template = mysql_ddl_tmpl ddl_template = mysql_ddl_tmpl
@ -50,7 +61,7 @@ class TiDBor(SQLor):
def grammar(self): def grammar(self):
return { return {
'select': 'select * from {table} where {condition}', 'select': select_stmt,
} }
def placeHolder(self, varname, pos=None): def placeHolder(self, varname, pos=None):
@ -71,14 +82,14 @@ class TiDBor(SQLor):
def tablesSQL(self): def tablesSQL(self):
# ✅ TiDB 支持 INFORMATION_SCHEMA.TABLES # ✅ 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 sqlcmd = f"""SELECT lower(TABLE_NAME) AS name, TABLE_COMMENT AS title
FROM INFORMATION_SCHEMA.TABLES FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = '{dbname}'""" WHERE TABLE_SCHEMA = '{dbname}'"""
return sqlcmd return sqlcmd
def fieldsSQL(self, tablename=None): def fieldsSQL(self, tablename=None):
dbname = self.dbdesc.get('dbname', 'unknown').lower() dbname = self.dbdesc.get('db', 'unknown').lower() # MySQL uses 'db'
sqlcmd = f""" sqlcmd = f"""
SELECT SELECT
lower(column_name) AS name, lower(column_name) AS name,
@ -98,7 +109,7 @@ WHERE lower(TABLE_SCHEMA) = '{dbname}'
def fkSQL(self, tablename=None): def fkSQL(self, tablename=None):
# ✅ TiDB 兼容 MySQL 的 FK 元信息 # ✅ TiDB 兼容 MySQL 的 FK 元信息
dbname = self.dbdesc.get('dbname', 'unknown').lower() dbname = self.dbdesc.get('db', 'unknown').lower()
sqlcmd = f""" sqlcmd = f"""
SELECT SELECT
C.TABLE_SCHEMA AS owner, C.TABLE_SCHEMA AS owner,
@ -121,7 +132,7 @@ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE table_name='{tablename.lower()}' AND constraint_name='PRIMARY'""" WHERE table_name='{tablename.lower()}' AND constraint_name='PRIMARY'"""
def indexesSQL(self, tablename=None): def indexesSQL(self, tablename=None):
dbname = self.dbdesc.get('dbname', 'unknown') dbname = self.dbdesc.get('db', 'unknown')
sqlcmd = f"""SELECT DISTINCT sqlcmd = f"""SELECT DISTINCT
lower(index_name) AS index_name, lower(index_name) AS index_name,
CASE NON_UNIQUE WHEN 0 THEN 'unique' ELSE '' END AS is_unique, 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()}'" sqlcmd += f" AND table_name = '{tablename.lower()}'"
return sqlcmd 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