diff --git a/sqlor/__init__.py b/sqlor/__init__.py old mode 100755 new mode 100644 diff --git a/sqlor/aiopostgresqlor.py b/sqlor/aiopostgresqlor.py old mode 100755 new mode 100644 diff --git a/sqlor/aiosqliteor.py b/sqlor/aiosqliteor.py old mode 100755 new mode 100644 index 5de89c8..afbe06c --- a/sqlor/aiosqliteor.py +++ b/sqlor/aiosqliteor.py @@ -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 \ No newline at end of file diff --git a/sqlor/const.py b/sqlor/const.py old mode 100755 new mode 100644 diff --git a/sqlor/dbpools.old.py b/sqlor/dbpools.old.py old mode 100755 new mode 100644 diff --git a/sqlor/dbpools.py b/sqlor/dbpools.py old mode 100755 new mode 100644 diff --git a/sqlor/ddl_template_mysql.py b/sqlor/ddl_template_mysql.py old mode 100755 new mode 100644 diff --git a/sqlor/ddl_template_oracle.py b/sqlor/ddl_template_oracle.py old mode 100755 new mode 100644 diff --git a/sqlor/ddl_template_postgresql.py b/sqlor/ddl_template_postgresql.py old mode 100755 new mode 100644 diff --git a/sqlor/ddl_template_sqlite3.py b/sqlor/ddl_template_sqlite3.py old mode 100755 new mode 100644 diff --git a/sqlor/ddl_template_sqlserver.py b/sqlor/ddl_template_sqlserver.py old mode 100755 new mode 100644 diff --git a/sqlor/filter.py b/sqlor/filter.py old mode 100755 new mode 100644 diff --git a/sqlor/mssqlor.py b/sqlor/mssqlor.py old mode 100755 new mode 100644 index 83d6fcd..0435679 --- a/sqlor/mssqlor.py +++ b/sqlor/mssqlor.py @@ -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 = { @@ -45,7 +56,7 @@ class MsSqlor(SQLor): @classmethod def isMe(self,name): return name=='pymssql' - + def grammar(self): return { 'select':select_stmt, @@ -55,7 +66,7 @@ class MsSqlor(SQLor): if varname=='__mainsql__' : return '' return '%s' - + def dataConvert(self,dataList): if type(dataList) == type({}): d = [ i for i in dataList.values()] @@ -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) \ No newline at end of file diff --git a/sqlor/mysqlor.py b/sqlor/mysqlor.py old mode 100755 new mode 100644 diff --git a/sqlor/oracleor.py b/sqlor/oracleor.py old mode 100755 new mode 100644 index 48b113f..dd4d97a --- a/sqlor/oracleor.py +++ b/sqlor/oracleor.py @@ -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 = { @@ -49,7 +61,7 @@ class Oracleor(SQLor): if varname=='__mainsql__' : return '' return ':%s' % varname - + def dataConvert(self,dataList): if type(dataList) == type({}): return dataList @@ -88,7 +100,7 @@ from USER_TAB_COMMENTS where table_type = 'TABLE'""" if tablename is not None: sqlcmd = sqlcmd + """ where lower(utc.table_name) = '%s'""" % tablename.lower() return sqlcmd - + def fkSQL(self,tablename=None): tablename = tablename.lower() sqlcmd = """select @@ -106,7 +118,7 @@ where if tablename is not None: sqlcmd = sqlcmd + """ and lower(uc.table_name)='%s'""" % tablename.lower() return sqlcmd - + def pkSQL(self,tablename=None): sqlcmd = """ select @@ -130,4 +142,33 @@ where a.index_name = b.index_name""" if tablename is not None: 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) \ No newline at end of file diff --git a/sqlor/postgresqlor.py b/sqlor/postgresqlor.py old mode 100755 new mode 100644 index 7d0205d..858f1e9 --- a/sqlor/postgresqlor.py +++ b/sqlor/postgresqlor.py @@ -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() - - async def close(): + dbdesc = self.dbdesc + self.conn = await aiopg.connect(**dbdesc) + self.dbname = dbdesc.get('dbname', '').lower() + + 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 + diff --git a/sqlor/records.py b/sqlor/records.py old mode 100755 new mode 100644 diff --git a/sqlor/sor.py b/sqlor/sor.py old mode 100755 new mode 100644 diff --git a/sqlor/sqlite3or.py b/sqlor/sqlite3or.py old mode 100755 new mode 100644 diff --git a/sqlor/tidbor.py b/sqlor/tidbor.py index ef9840d..4abaf57 100644 --- a/sqlor/tidbor.py +++ b/sqlor/tidbor.py @@ -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 \ No newline at end of file