bugfix
This commit is contained in:
parent
bd15c2fb8d
commit
d3c9c060ea
184
sqlor/pgsqlor.py
Normal file
184
sqlor/pgsqlor.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
# -*- coding:utf8 -*-
|
||||||
|
import asyncpg
|
||||||
|
from appPublic.argsConvert import ArgsConvert, ConditionConvert
|
||||||
|
from .sor import SQLor
|
||||||
|
from .ddl_template_pgsql import pgsql_ddl_tmpl # 需要提供 PostgreSQL DDL 模板
|
||||||
|
|
||||||
|
class PgSqlor(SQLor):
|
||||||
|
ddl_template = pgsql_ddl_tmpl
|
||||||
|
|
||||||
|
db2modelTypeMapping = {
|
||||||
|
'smallint': 'short',
|
||||||
|
'integer': 'long',
|
||||||
|
'bigint': 'long',
|
||||||
|
'decimal': 'float',
|
||||||
|
'numeric': 'float',
|
||||||
|
'real': 'float',
|
||||||
|
'double precision': 'float',
|
||||||
|
'serial': 'long',
|
||||||
|
'bigserial': 'long',
|
||||||
|
'varchar': 'str',
|
||||||
|
'char': 'char',
|
||||||
|
'text': 'text',
|
||||||
|
'bytea': 'bin',
|
||||||
|
'date': 'date',
|
||||||
|
'time': 'time',
|
||||||
|
'timestamp': 'datetime',
|
||||||
|
'timestamptz': 'datestamp',
|
||||||
|
'boolean': 'short',
|
||||||
|
'json': 'text',
|
||||||
|
'jsonb': 'text',
|
||||||
|
}
|
||||||
|
|
||||||
|
model2dbTypemapping = {
|
||||||
|
'date': 'date',
|
||||||
|
'time': 'time',
|
||||||
|
'timestamp': 'timestamp',
|
||||||
|
'str': 'varchar',
|
||||||
|
'char': 'char',
|
||||||
|
'short': 'smallint',
|
||||||
|
'long': 'bigint',
|
||||||
|
'float': 'double precision',
|
||||||
|
'text': 'text',
|
||||||
|
'bin': 'bytea',
|
||||||
|
'file': 'bytea',
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def isMe(cls, name):
|
||||||
|
return name.lower() in ['pgsql', 'postgres', 'postgresql']
|
||||||
|
|
||||||
|
def grammar(self):
|
||||||
|
return {
|
||||||
|
# PostgreSQL 支持 window/CTE 等,可在这里扩展
|
||||||
|
'select': 'select',
|
||||||
|
}
|
||||||
|
|
||||||
|
def placeHolder(self, varname, pos=None):
|
||||||
|
"""PostgreSQL 使用 $1, $2 作为占位符"""
|
||||||
|
if varname == '__mainsql__':
|
||||||
|
return ''
|
||||||
|
if pos is not None:
|
||||||
|
return f"${pos + 1}"
|
||||||
|
return '$1'
|
||||||
|
|
||||||
|
def dataConvert(self, dataList):
|
||||||
|
if isinstance(dataList, dict):
|
||||||
|
return tuple(dataList.values())
|
||||||
|
else:
|
||||||
|
return tuple(i['value'] for i in dataList)
|
||||||
|
|
||||||
|
def pagingSQLmodel(self):
|
||||||
|
"""分页模板"""
|
||||||
|
return """select * from (%s) as A order by $[sort]$ offset $[from_line]$ limit $[rows]$"""
|
||||||
|
|
||||||
|
def tablesSQL(self):
|
||||||
|
sqlcmd = f"""
|
||||||
|
SELECT
|
||||||
|
lower(tablename) as name,
|
||||||
|
obj_description(format('%s.%s', schemaname, tablename)::regclass) as title
|
||||||
|
FROM pg_tables
|
||||||
|
WHERE schemaname NOT IN ('pg_catalog', 'information_schema');
|
||||||
|
"""
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def fieldsSQL(self, tablename=None):
|
||||||
|
sqlcmd = f"""
|
||||||
|
SELECT
|
||||||
|
lower(a.attname) as name,
|
||||||
|
format_type(a.atttypid, a.atttypmod) as type,
|
||||||
|
CASE
|
||||||
|
WHEN a.atttypmod > 0 THEN a.atttypmod - 4
|
||||||
|
ELSE NULL
|
||||||
|
END as length,
|
||||||
|
NULL as "dec",
|
||||||
|
CASE WHEN a.attnotnull THEN 'no' ELSE 'yes' END as nullable,
|
||||||
|
col_description(a.attrelid, a.attnum) as title,
|
||||||
|
lower(c.relname) as table_name
|
||||||
|
FROM pg_attribute a
|
||||||
|
JOIN pg_class c ON a.attrelid = c.oid
|
||||||
|
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||||
|
WHERE a.attnum > 0 AND NOT a.attisdropped
|
||||||
|
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
|
||||||
|
"""
|
||||||
|
if tablename:
|
||||||
|
sqlcmd += f" AND lower(c.relname) = '{tablename.lower()}'"
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def fkSQL(self, tablename=None):
|
||||||
|
sqlcmd = f"""
|
||||||
|
SELECT
|
||||||
|
con.conname AS constraint_name,
|
||||||
|
nsp.nspname AS schema_name,
|
||||||
|
rel.relname AS child_table,
|
||||||
|
att.attname AS child_column,
|
||||||
|
frel.relname AS parent_table,
|
||||||
|
fatt.attname AS parent_column,
|
||||||
|
con.confupdtype AS update_rule,
|
||||||
|
con.confdeltype AS delete_rule
|
||||||
|
FROM pg_constraint con
|
||||||
|
JOIN pg_class rel ON rel.oid = con.conrelid
|
||||||
|
JOIN pg_class frel ON frel.oid = con.confrelid
|
||||||
|
JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = ANY(con.conkey)
|
||||||
|
JOIN pg_attribute fatt ON fatt.attrelid = con.confrelid AND fatt.attnum = ANY(con.confkey)
|
||||||
|
JOIN pg_namespace nsp ON nsp.oid = con.connamespace
|
||||||
|
WHERE con.contype = 'f'
|
||||||
|
"""
|
||||||
|
if tablename:
|
||||||
|
sqlcmd += f" AND lower(rel.relname) = '{tablename.lower()}'"
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def pkSQL(self, tablename=None):
|
||||||
|
sqlcmd = f"""
|
||||||
|
SELECT a.attname as name
|
||||||
|
FROM pg_index i
|
||||||
|
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
||||||
|
WHERE i.indrelid = '{tablename.lower()}'::regclass
|
||||||
|
AND i.indisprimary;
|
||||||
|
"""
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def indexesSQL(self, tablename=None):
|
||||||
|
sqlcmd = f"""
|
||||||
|
SELECT
|
||||||
|
lower(i.relname) as index_name,
|
||||||
|
CASE WHEN ix.indisunique THEN 'unique' ELSE '' END as is_unique,
|
||||||
|
lower(a.attname) as column_name
|
||||||
|
FROM pg_class t,
|
||||||
|
pg_class i,
|
||||||
|
pg_index ix,
|
||||||
|
pg_attribute a
|
||||||
|
WHERE
|
||||||
|
t.oid = ix.indrelid
|
||||||
|
AND i.oid = ix.indexrelid
|
||||||
|
AND a.attrelid = t.oid
|
||||||
|
AND a.attnum = ANY(ix.indkey)
|
||||||
|
AND t.relkind = 'r'
|
||||||
|
"""
|
||||||
|
if tablename:
|
||||||
|
sqlcmd += f" AND lower(t.relname) = '{tablename.lower()}'"
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
"""建立数据库连接"""
|
||||||
|
dbdesc = self.dbdesc
|
||||||
|
self.conn = await asyncpg.connect(
|
||||||
|
user=dbdesc.get('user'),
|
||||||
|
password=dbdesc.get('password'),
|
||||||
|
database=dbdesc.get('db'),
|
||||||
|
host=dbdesc.get('host', 'localhost'),
|
||||||
|
port=dbdesc.get('port', 5432),
|
||||||
|
)
|
||||||
|
self.dbname = dbdesc.get('db')
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
await self.conn.close()
|
||||||
|
|
||||||
|
async def enter(self):
|
||||||
|
"""开启事务或获取游标(asyncpg 无游标,直接用连接执行)"""
|
||||||
|
self.cur = self.conn # 保留接口一致性
|
||||||
|
|
||||||
|
async def exit(self):
|
||||||
|
"""释放资源"""
|
||||||
|
self.cur = None
|
||||||
|
|
||||||
145
sqlor/sqliteor.py
Normal file
145
sqlor/sqliteor.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# -*- coding:utf8 -*-
|
||||||
|
import aiosqlite
|
||||||
|
from appPublic.argsConvert import ArgsConvert, ConditionConvert
|
||||||
|
from .sor import SQLor
|
||||||
|
from .ddl_template_sqlite import sqlite_ddl_tmpl
|
||||||
|
|
||||||
|
|
||||||
|
class SQLiteor(SQLor):
|
||||||
|
ddl_template = sqlite_ddl_tmpl
|
||||||
|
|
||||||
|
db2modelTypeMapping = {
|
||||||
|
'integer': 'long',
|
||||||
|
'int': 'long',
|
||||||
|
'tinyint': 'short',
|
||||||
|
'smallint': 'short',
|
||||||
|
'mediumint': 'long',
|
||||||
|
'bigint': 'long',
|
||||||
|
'decimal': 'float',
|
||||||
|
'numeric': 'float',
|
||||||
|
'real': 'float',
|
||||||
|
'double': 'float',
|
||||||
|
'float': 'float',
|
||||||
|
'char': 'char',
|
||||||
|
'varchar': 'str',
|
||||||
|
'text': 'text',
|
||||||
|
'clob': 'text',
|
||||||
|
'blob': 'bin',
|
||||||
|
'date': 'date',
|
||||||
|
'datetime': 'datetime',
|
||||||
|
'boolean': 'short',
|
||||||
|
}
|
||||||
|
|
||||||
|
model2dbTypemapping = {
|
||||||
|
'date': 'date',
|
||||||
|
'time': 'time',
|
||||||
|
'timestamp': 'datetime',
|
||||||
|
'str': 'text',
|
||||||
|
'char': 'char',
|
||||||
|
'short': 'integer',
|
||||||
|
'long': 'integer',
|
||||||
|
'float': 'real',
|
||||||
|
'text': 'text',
|
||||||
|
'bin': 'blob',
|
||||||
|
'file': 'blob',
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def isMe(cls, name):
|
||||||
|
return name.lower() in ['sqlite', 'sqlite3']
|
||||||
|
|
||||||
|
def grammar(self):
|
||||||
|
return {
|
||||||
|
'select': 'select',
|
||||||
|
}
|
||||||
|
|
||||||
|
def placeHolder(self, varname, pos=None):
|
||||||
|
"""SQLite 使用 ? 占位符"""
|
||||||
|
if varname == '__mainsql__':
|
||||||
|
return ''
|
||||||
|
return '?'
|
||||||
|
|
||||||
|
def dataConvert(self, dataList):
|
||||||
|
if isinstance(dataList, dict):
|
||||||
|
return tuple(dataList.values())
|
||||||
|
else:
|
||||||
|
return tuple(i['value'] for i in dataList)
|
||||||
|
|
||||||
|
def pagingSQLmodel(self):
|
||||||
|
"""分页 SQL 模板"""
|
||||||
|
return """select * from (%s) as A order by $[sort]$ limit $[rows]$ offset $[from_line]$"""
|
||||||
|
|
||||||
|
def tablesSQL(self):
|
||||||
|
"""获取表名和备注"""
|
||||||
|
sqlcmd = """
|
||||||
|
SELECT
|
||||||
|
lower(name) AS name,
|
||||||
|
'' AS title
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type='table'
|
||||||
|
AND name NOT LIKE 'sqlite_%';
|
||||||
|
"""
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def fieldsSQL(self, tablename=None):
|
||||||
|
"""获取字段信息"""
|
||||||
|
sqlcmd = f"""
|
||||||
|
PRAGMA table_info({tablename});
|
||||||
|
"""
|
||||||
|
# SQLite 无信息架构表,直接返回 PRAGMA 命令
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def fkSQL(self, tablename=None):
|
||||||
|
"""获取外键信息"""
|
||||||
|
if tablename:
|
||||||
|
sqlcmd = f"PRAGMA foreign_key_list({tablename});"
|
||||||
|
else:
|
||||||
|
sqlcmd = "-- SQLite 需逐表获取外键信息"
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def pkSQL(self, tablename=None):
|
||||||
|
"""获取主键信息"""
|
||||||
|
sqlcmd = f"""
|
||||||
|
SELECT name
|
||||||
|
FROM pragma_table_info('{tablename}')
|
||||||
|
WHERE pk != 0;
|
||||||
|
"""
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
def indexesSQL(self, tablename=None):
|
||||||
|
"""获取索引信息"""
|
||||||
|
if not tablename:
|
||||||
|
return "SELECT name as index_name, '' as is_unique, '' as column_name FROM sqlite_master WHERE type='index';"
|
||||||
|
|
||||||
|
sqlcmd = f"""
|
||||||
|
PRAGMA index_list('{tablename}');
|
||||||
|
"""
|
||||||
|
return sqlcmd
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
"""
|
||||||
|
连接 SQLite 数据库
|
||||||
|
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
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user