From e6f1175acc3f6d6b013cc42d77d0883a679dc587 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 14 May 2019 10:50:40 +0800 Subject: [PATCH] delete build dist --- build/lib/sqlor/__init__.py | 0 build/lib/sqlor/aiomysqlor.py | 7 - build/lib/sqlor/aiopostgresqlor.py | 8 - build/lib/sqlor/dbpools.py | 277 ------------- build/lib/sqlor/ddl_template_mysql.py | 55 --- build/lib/sqlor/ddl_template_oracle.py | 49 --- build/lib/sqlor/ddl_template_postgresql.py | 45 -- build/lib/sqlor/ddl_template_sqlserver.py | 51 --- build/lib/sqlor/mssqlor.py | 175 -------- build/lib/sqlor/mysqlor.py | 179 -------- build/lib/sqlor/oracleor.py | 131 ------ build/lib/sqlor/postgresqlor.py | 199 --------- build/lib/sqlor/sor.py | 454 --------------------- build/lib/sqlor/sqlite3or.py | 100 ----- build/lib/sqlor/sqlor.py | 393 ------------------ dist/sqlor-0.0.1-py3.7.egg | Bin 56420 -> 0 bytes 16 files changed, 2123 deletions(-) delete mode 100644 build/lib/sqlor/__init__.py delete mode 100644 build/lib/sqlor/aiomysqlor.py delete mode 100644 build/lib/sqlor/aiopostgresqlor.py delete mode 100644 build/lib/sqlor/dbpools.py delete mode 100644 build/lib/sqlor/ddl_template_mysql.py delete mode 100644 build/lib/sqlor/ddl_template_oracle.py delete mode 100644 build/lib/sqlor/ddl_template_postgresql.py delete mode 100644 build/lib/sqlor/ddl_template_sqlserver.py delete mode 100644 build/lib/sqlor/mssqlor.py delete mode 100644 build/lib/sqlor/mysqlor.py delete mode 100644 build/lib/sqlor/oracleor.py delete mode 100644 build/lib/sqlor/postgresqlor.py delete mode 100644 build/lib/sqlor/sor.py delete mode 100644 build/lib/sqlor/sqlite3or.py delete mode 100644 build/lib/sqlor/sqlor.py delete mode 100644 dist/sqlor-0.0.1-py3.7.egg diff --git a/build/lib/sqlor/__init__.py b/build/lib/sqlor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/sqlor/aiomysqlor.py b/build/lib/sqlor/aiomysqlor.py deleted file mode 100644 index 26c6fb7..0000000 --- a/build/lib/sqlor/aiomysqlor.py +++ /dev/null @@ -1,7 +0,0 @@ -from .mysqlor import MySqlor - -class AioMysqlor(MySqlor): - @classmethod - def isMe(self,name): - return name=='aiomysql' - diff --git a/build/lib/sqlor/aiopostgresqlor.py b/build/lib/sqlor/aiopostgresqlor.py deleted file mode 100644 index 7094ebc..0000000 --- a/build/lib/sqlor/aiopostgresqlor.py +++ /dev/null @@ -1,8 +0,0 @@ - -from .postgresqlor import PostgreSQLor -class AioPostgresqlor(PostgreSQLor): - @classmethod - def isMe(self,name): - return name=='aiopg' - - diff --git a/build/lib/sqlor/dbpools.py b/build/lib/sqlor/dbpools.py deleted file mode 100644 index 6ee08c0..0000000 --- a/build/lib/sqlor/dbpools.py +++ /dev/null @@ -1,277 +0,0 @@ - -import asyncio -from queue import Queue -from functools import wraps -import codecs - -from appPublic.myImport import myImport -from appPublic.dictObject import DictObject -from appPublic.Singleton import SingletonDecorator -from appPublic.myjson import loadf -from appPublic.jsonConfig import getConfig - -from .sor import SQLor -from .mssqlor import MsSqlor -from .oracleor import Oracleor -from .sqlite3or import SQLite3or -from .mysqlor import MySqlor -from .aiomysqlor import AioMysqlor -from .aiopostgresqlor import AioPostgresqlor - - -def sqlorFactory(dbdesc): - driver = dbdesc.get('driver',dbdesc) - def findSubclass(name,klass): - for k in klass.__subclasses__(): - if k.isMe(name): - return k - k1 = findSubclass(name,k) - if k1 is not None: - return k1 - return None - k = findSubclass(driver,SQLor) - if k is None: - return SQLor(dbdesc=dbdesc) - return k(dbdesc=dbdesc) - -def sqlorFromFile(dbdef_file,coding='utf8'): - dbdef = loadf(dbdef_file) - return sqlorFactory(dbdef) - -class LifeConnect: - __conndict = {} - def __init__(self,connfunc,kw,use_max=1000,async_mode=False): - self.connfunc = connfunc - self.async_mode = async_mode - self.use_max = use_max - self.kw = kw - self.conn = None - - def print(self): - print(self.use_max) - print(self.conn) - - async def _mkconn(self): - if self.async_mode: - self.conn = await self.connfunc(**self.kw) - else: - self.conn = self.connfunc(**self.kw) - self.use_cnt = 0 - self.__conndict[self.conn] = self - - async def use(self): - if self.conn is None: - await self._mkconn() - conn = self.conn - if await self.testok(): - return conn - del self.__conndict[conn] - await self._mkconn() - - @classmethod - async def free(self,conn): - lc = self.__conndict[conn] - lc.use_cnt = lc.use_cnt + 1 - if lc.use_cnt >= lc.use_max: - await lc.conn.close() - await lc._mkcomm() - return lc - - async def testok(self): - if self.async_mode: - async with self.conn.cursor() as cur: - try: - await cur.execute('select 1 as cnt') - return True - except: - return False - else: - cur = self.conn.cursor() - try: - cur.execute('select 1 as cnt') - r = cur.fetchall() - return True - except: - return False - finally: - cur.close() - -class ConnectionPool(object): - def __init__(self,dbdesc,loop): - self.dbdesc = dbdesc - self.async_mode = dbdesc.get('async_mode',False) - self.loop = loop - self.driver = myImport(self.dbdesc['driver']) - self.maxconn = dbdesc.get('maxconn',5) - self.maxuse = dbdesc.get('maxuse',1000) - self._pool = Queue(self.maxconn) - self._fillPool() - self.using = [] - self.use_cnt = 0 - self.max_use = 1000 - - def _fillPool(self): - for i in range(self.maxconn): - lc = self.connect() - i = i + 1 - - def connect(self): - lc = LifeConnect(self.driver.connect,self.dbdesc['kwargs'], - use_max=self.maxuse,async_mode=self.async_mode) - self._pool.put(lc) - return lc - - def isEmpty(self): - return self._pool.empty() - - def isFull(self): - return self._pool.full() - - async def aquire(self): - lc = self._pool.get() - self.using.append(lc) - conn = await lc.use() - return conn - - async def release(self,conn): - lc = await LifeConnect.free(conn) - self.using = [c for c in self.using if c != lc ] - self._pool.put(lc) - -@SingletonDecorator -class DBPools: - def __init__(self,databases={},max_connect=10,loop=None): - if loop is None: - loop = asyncio.get_event_loop() - self.loop = loop - self._cpools = {} - self.databases = databases - - def addDatabase(self,name,desc): - self.databases[name] = desc - - async def getSqlor(self,name): - desc = self.databases.get(name) - sor = sqlorFactory(desc) - sor.name = name - a,conn,cur = await self._aquireConn(name) - sor.setCursor(a,conn,cur) - return sor - - async def freeSqlor(self,sor): - await self._releaseConn(sor.name,sor.conn,sor.cur) - - async def _aquireConn(self,dbname): - p = self._cpools.get(dbname) - if p == None: - p = ConnectionPool(self.databases.get(dbname),self.loop) - self._cpools[dbname] = p - conn = await p.aquire() - if self.isAsyncDriver(dbname): - cur = await conn.cursor() - else: - cur = conn.cursor() - return self.isAsyncDriver(dbname),conn,cur - - def isAsyncDriver(self,dbname): - ret = self.databases[dbname].get('async_mode',False) - return ret - - async def _releaseConn(self,dbname,conn,cur): - if self.isAsyncDriver(dbname): - await cur.close() - else: - try: - cur.fetchall() - except: - pass - cur.close() - p = self._cpools.get(dbname) - if p == None: - raise Exception('database (%s) not connected'%dbname) - await p.release(conn) - - def inSqlor(self,func): - @wraps(func) - async def wrap_func(sor,dbname,*args,**kw): - sor = await self.getSqlor(dbname) - try: - ret = await func(sor,dbname,*args,**kw) - return ret - except Exception as e: - print('error',sor) - raise e - finally: - await self.freeSqlor(sor) - - return wrap_func - - def runSQL(self,func): - @wraps(func) - async def wrap_func(dbname,NS,callback,**kw): - sor = await self.getSqlor(dbname) - desc = func(dbname,NS,callback,**kw) - try: - ret = await sor.runSQL(desc,NS,callback,**kw) - except Exception as e: - print('error:',e) - raise e - finally: - await self.freeSqlor(sor) - return wrap_func - - def runSQLPaging(self,func): - @wraps(func) - async def wrap_func(dbname,NS,**kw): - sor = await self.getSqlor(dbname) - desc = func(dbname,NS,**kw) - total = await sor.record_count(desc,NS) - recs = await sor.pagingdata(desc,NS) - data = { - "total":total, - "rows":recs - } - print(len(recs),'records return') - await self.freeSqlor(sor) - return data - return wrap_func - - async def runSQLResultFields(self, dbname,NS,**kwargs): - sor = self.getSqlor(dbname) - desc = func(dbname,NS,**kw) - conn = await self._aquireConn(dbname) - async with conn.cursor() as cur: - sor.setCursor(conn,cur) - ret=await sor.sqlIterator(desc,NS) - ret = [ {'name':i[0],'type':i[1]} for i in cur.description ] - return ret - await self._releaseConn(dbname,conn) - - async def getTables(self,dbname): - @self.inSqlor - async def _getTables(sor,dbname): - ret = await sor.tables() - return ret - return await _getTables(None,dbname) - - async def getTableFields(self,dbname,tblname): - @self.inSqlor - async def _getTableFields(sor,dbname,tblname): - ret = await sor.fields(tblname) - return ret - return await _getTableFields(None,dbname,tblname) - - async def getTablePrimaryKey(self,dbname,tblname): - @self.inSqlor - async def _getTablePrimaryKey(sor,dbname,tblname): - ret = await sor.primary(tblname) - return ret - return await _getTablePrimaryKey(None,dbname,tblname) - - async def getTableForignKeys(self,dbname,tblname): - @self.inSqlor - async def _getTableForignKeys(sor,dbname,tblname): - ret = await sor.fkeys(tblname) - return ret - return await _getTableForignKeys(None,dbname,tblname) - diff --git a/build/lib/sqlor/ddl_template_mysql.py b/build/lib/sqlor/ddl_template_mysql.py deleted file mode 100644 index 5c9b039..0000000 --- a/build/lib/sqlor/ddl_template_mysql.py +++ /dev/null @@ -1,55 +0,0 @@ -mysql_ddl_tmpl = """{% macro typeStr(type,len,dec) %} -{%- if type=='str' -%} -VARCHAR({{len}}) -{%- elif type=='char' -%} -CHAR({{len}}) -{%- elif type=='long' or type=='int' or type=='short' -%} -int -{%- elif type=='long' -%} -bigint -{%- elif type=='float' or type=='double' or type=='ddouble' -%} -double({{len}},{{dec}}) -{%- elif type=='date' -%} -date -{%- elif type=='time' -%} -time -{%- elif type=='datetime' -%} -datetime -{%- elif type=='timestamp' -%} -TIMESTAMP -{%- elif type=='text' -%} -longtext -{%- elif type=='bin' -%} -longblob -{%- else -%} -{{type}} -{%- endif %} -{%- endmacro %} -{% macro nullStr(nullable) %} -{%- if nullable=='no' -%} -NOT NULL -{%- endif -%} -{% endmacro %} -{% macro primary() %} -{% if len(','.join(summary[0].primary))>0 %} -,primary key({{','.join(summary[0].primary)}}) -{% endif %} -{% endmacro %} -drop table {{summary[0].name}}; -CREATE TABLE {{summary[0].name}} -( -{% for field in fields %} - {{field.name}} {{typeStr(field.type,field.length,field.dec)}} {{nullStr(field.nullable)}} {%if field.title -%} comment '{{field.title}}'{%- endif %}{%- if not loop.last -%},{%- endif -%} -{% endfor %} -{{primary()}} -) -engine=innodb -default charset=utf8 -{% if summary[0].title %}comment '{{summary[0].title}}'{% endif %} -; -{% for v in validation %} -{% if v.oper=='idx' %} -CREATE {% if v.value.idxtype=='unique' %}UNIQUE{% endif %} INDEX {{summary[0].name}}_{{v.name}} ON {{summary[0].name}}({{",".join(v.value.fields)}}); -{% endif %} -{%- endfor -%} -""" \ No newline at end of file diff --git a/build/lib/sqlor/ddl_template_oracle.py b/build/lib/sqlor/ddl_template_oracle.py deleted file mode 100644 index ff39372..0000000 --- a/build/lib/sqlor/ddl_template_oracle.py +++ /dev/null @@ -1,49 +0,0 @@ -oracle_ddl_tmpl = """{% macro typeStr(type,len,dec) %} -{%- if type=='str' -%} -VARCHAR2({{len}}) -{%- elif type=='char' -%} -CHAR({{len}}) -{%- elif type=='long' or type=='int' or type=='short' -%} -NUMBER -{%- elif type=='float' or type=='double' or type=='ddouble' -%} -NUMBER({{len}},{{dec}}) -{%- elif type=='date' or type=='time' -%} -DATE -{%- elif type=='timestamp' -%} -TIMESTAMP -{%- elif type=='text' -%} -CLOB -{%- elif type=='bin' -%} -BLOB -{%- else -%} -{{type}} -{%- endif %} -{%- endmacro %} -{% macro nullStr(nullable) %} -{%- if nullable=='no' -%} -NOT NULL -{%- endif -%} -{% endmacro %} -{% macro primary() %} -{% if len(','.join(summary[0].primary))>0 %} -,primary key({{','.join(summary[0].primary)}}) -{% endif %} -{% endmacro %} -drop table {{summary[0].name}}; -CREATE TABLE {{summary[0].name}} -( -{% for field in fields %} - {{field.name}} {{typeStr(field.type,field.length,field.dec)}} {{nullStr(field.nullable)}}{%- if not loop.last -%},{%- endif -%} -{% endfor %} -{{primary()}} -); -{% for v in validation %} -{% if v.oper=='idx' %} -CREATE {% if v.value.idxtype=='unique' %}UNIQUE{% endif %} INDEX {{summary[0].name}}_{{v.name}} ON {{summary[0].name}}({{",".join(v.value.fields)}}); -{% endif %} -{%- endfor -%} -COMMENT ON TABLE {{summary[0].name}} IS '{{summary[0].title}}'; -{% for field in fields %} -COMMENT ON COLUMN {{summary[0].name}}.{{field.name}} is '{{field.title}}'; -{% endfor %} -""" \ No newline at end of file diff --git a/build/lib/sqlor/ddl_template_postgresql.py b/build/lib/sqlor/ddl_template_postgresql.py deleted file mode 100644 index 6b17592..0000000 --- a/build/lib/sqlor/ddl_template_postgresql.py +++ /dev/null @@ -1,45 +0,0 @@ -postgresql_ddl_tmpl = """{% macro typeStr(type,len,dec) %} -{%- if type=='str' -%} -VARCHAR({{len}}) -{%- elif type=='char' -%} -CHAR({{len}}) -{%- elif type=='long' or type=='int' or type=='short' -%} -INTEGER -{%- elif type=='float' or type=='double' or type=='ddouble' -%} -NUMERIC({{len}},{{dec}}) -{%- elif type=='date' -%} -DATE -{%- elif type=='time' -%} -TIME -{%- elif type=='timestamp' -%} -TIMESTAMP -{%- else -%} -{{type}} -{%- endif %} -{%- endmacro %} -{% macro nullStr(nullable) %} -{%- if nullable=='no' -%} -NOT NULL -{%- endif -%} -{% endmacro %} -{% macro primary() %} -primary key({{','.join(summary[0].primary)}}) -{% endmacro %} -DROP TABLE IF EXISTS {{summary[0].name}}; -CREATE TABLE {{summary[0].name}} -( -{% for field in fields %} - {{field.name}} {{typeStr(field.type,field.length,field.dec)}} {{nullStr(field.nullable)}}, -{% endfor %} -{{primary()}} -); -{% for v in validation %} -{% if v.oper=='idx' %} -CREATE {% if v.value.idxtype=='unique' %}UNIQUE{% endif %} INDEX {{summary[0].name}}_{{v.name}} ON {{summary[0].name}}({{",".join(v.value.fields)}}); -{% endif %} -{%- endfor -%} -COMMENT ON TABLE {{summary[0].name}} IS '{{summary[0].title.decode('utf8')}}'; -{% for field in fields %} -COMMENT ON COLUMN {{summary[0].name}}.{{field.name}} is '{{field.title.decode('utf8')}}'; -{% endfor %} -""" \ No newline at end of file diff --git a/build/lib/sqlor/ddl_template_sqlserver.py b/build/lib/sqlor/ddl_template_sqlserver.py deleted file mode 100644 index 49e73c7..0000000 --- a/build/lib/sqlor/ddl_template_sqlserver.py +++ /dev/null @@ -1,51 +0,0 @@ -sqlserver_ddl_tmpl = """{% macro typeStr(type,len,dec) %} -{%- if type=='str' -%} -NVARCHAR({{len}}) -{%- elif type=='char' -%} -CHAR({{len}}) -{%- elif type=='long' or type=='int' or type=='short' -%} -NUMERIC -{%- elif type=='float' or type=='double' or type=='ddouble' -%} -numeric({{len}},{{dec}}) -{%- elif type=='date' or type=='time' -%} -DATE -{%- elif type=='timestamp' -%} -TIMESTAMP -{%- elif type=='text' -%} -NVARCHAR(MAX) -{%- elif type=='bin' -%} -IMAGE -{%- else -%} -{{type}} -{%- endif %} -{%- endmacro %} -{% macro nullStr(nullable) %} -{%- if nullable=='no' -%} -NOT NULL -{%- endif -%} -{% endmacro %} - -{% macro primary() %} -{% if len(','.join(summary[0].primary))>0 %} -,primary key({{','.join(summary[0].primary)}}) -{% endif %} -{% endmacro %} - -drop table dbo.{{summary[0].name}}; -CREATE TABLE dbo.{{summary[0].name}} -( -{% for field in fields %} - {{field.name}} {{typeStr(field.type,field.length,field.dec)}} {{nullStr(field.nullable)}}{%- if not loop.last -%},{%- endif -%} -{% endfor %} -{{primary()}} -) -{% for v in validation %} -{% if v.oper=='idx' %} -CREATE {% if v.value.idxtype=='unique' %}UNIQUE{% endif %} INDEX {{summary[0].name}}_{{v.name}} ON {{summary[0].name}}({{",".join(v.value.fields)}}); -{% endif %} -{%- endfor -%} -EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{{summary[0].title}}' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{{summary[0].name}}' -{% for field in fields %} -EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'{{field.title}}' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'{{summary[0].name}}', @level2type=N'COLUMN',@level2name=N'{{field.name}}' -{% endfor %} -""" \ No newline at end of file diff --git a/build/lib/sqlor/mssqlor.py b/build/lib/sqlor/mssqlor.py deleted file mode 100644 index b527a1a..0000000 --- a/build/lib/sqlor/mssqlor.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- coding:utf8 -*- -from .sor import SQLor -from .ddl_template_sqlserver import sqlserver_ddl_tmpl - -class MsSqlor(SQLor): - ddl_template = sqlserver_ddl_tmpl - db2modelTypeMapping = { - 'bit':'short', - 'tinyint':'short', - 'date':'date', - 'bigint':'long', - 'smallint':'short', - 'int':'long', - 'decimal':'float', - 'numeric':'float', - 'smallmoney':'float', - 'money':'float', - 'real':'float', - 'float':'float', - 'datetime':'date', - 'timestamp':'timestamp', - 'uniqueidentifier':'timestamp', - 'char':'char', - 'varchar':'str', - 'text':'text', - 'nchar':'str', - 'nvarchar':'str', - 'ntext':'text', - 'binary':'str', - 'varbinary':'str', - 'image':'file', - } - model2dbTypemapping = { - 'date':'datetime', - 'time':'date', - 'timestamp':'timestamp', - 'str':'nvarchar', - 'char':'char', - 'short':'int', - 'long':'numeric', - 'float':'numeric', - 'text':'ntext', - 'file':'image', - } - @classmethod - def isMe(self,name): - return name=='pymssql' - - def grammar(self): - return { - 'select':select_stmt, - } - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return '%s' - - def dataConvert(self,dataList): - if type(dataList) == type({}): - d = [ i for i in dataList.values()] - else: - d = [ i['value'] for i in dataList] - return tuple(d) - - def pagingSQLmodel(self): - return u"""select * -from ( - select row_number() over(order by $[sort]$ $[order]$) as _row_id,page_s.* - from (%s) page_s - ) A -where _row_id >= $[from_line]$ and _row_id < $[end_line]$""" - - def tablesSQL(self): - sqlcmd = u"""select - lower(d.name) as name, - lower(cast(Isnull(f.VALUE,d.name) as nvarchar )) title - from sysobjects d - left join sys.extended_properties f on d.id = f.major_id and f.minor_id = 0 - where d.xtype = 'U'""" - return sqlcmd - - def fieldsSQL(self,tablename=None): - sqlcmd=u"""SELECT name = lower(a.name) - ,type = b.name - ,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 - ,title = lower(cast(Isnull(g.[value],a.name) as nvarchar) ) - ,table_name = lower(d.name) - FROM syscolumns a - LEFT JOIN systypes b - ON a.xusertype = b.xusertype - INNER JOIN sysobjects d - ON (a.id = d.id) - AND (d.xtype = 'U') - AND (d.name <> 'dtproperties') - INNER JOIN sys.all_objects c - ON d.id=c.object_id - AND schema_name(schema_id)='dbo' - LEFT JOIN sys.extended_properties g - ON (a.id = g.major_id) - AND (a.colid = g.minor_id) - LEFT JOIN sys.extended_properties f - ON (d.id = f.major_id) - AND (f.minor_id = 0)""" - if tablename is not None: - sqlcmd = sqlcmd + """ where lower(d.name)='%s' - ORDER BY a.id,a.colorder""" % tablename.lower() - else: - sqlcmd = sqlcmd + """ ORDER BY a.id,a.colorder""" - return sqlcmd - - def fkSQL(self,tablename=None): - sqlcmd = u"""select - MainCol.name AS field -- [主表列名] - ,oSub.name AS fk_table -- [子表名称], - ,SubCol.name AS fk_field -- [子表列名], -from - sys.foreign_keys fk - JOIN sys.all_objects oSub - ON (fk.parent_object_id = oSub.object_id) - JOIN sys.all_objects oMain - ON (fk.referenced_object_id = oMain.object_id) - JOIN sys.foreign_key_columns fkCols - ON (fk.object_id = fkCols.constraint_object_id) - JOIN sys.columns SubCol - ON (oSub.object_id = SubCol.object_id - AND fkCols.parent_column_id = SubCol.column_id) - JOIN sys.columns MainCol - ON (oMain.object_id = MainCol.object_id - AND fkCols.referenced_column_id = MainCol.column_id)""" - if tablename is not None: - sqlcmd = sqlcmd + """ where lower(oMain.name) = '%s'""" % tablename.lower() - - return sqlcmd - - def pkSQL(self,tablename=None): - sqlcmd = u"""select - lower(a.table_name) as table_name, - lower(b.column_name) as field_name - from information_schema.table_constraints a - inner join information_schema.constraint_column_usage b - on a.constraint_name = b.constraint_name - where a.constraint_type = 'PRIMARY KEY'""" - if tablename is not None: - sqlcmd = sqlcmd + """ and lower(a.table_name) = '%s'""" % tablename.lower() - return sqlcmd - - def indexesSQL(self,tablename=None): - sqlcmd = """SELECT -index_name=lower(IDX.Name), -index_type=case when KC.type_desc is null then 'primary' WHEN IDX.is_unique=1 THEN 'unique' ELSE 'ununique' END, -table_name=lower(O.Name), -column_name=lower(C.Name) -FROM sys.indexes IDX -INNER JOIN sys.index_columns IDXC -ON IDX.[object_id]=IDXC.[object_id] -AND IDX.index_id=IDXC.index_id -LEFT JOIN sys.key_constraints KC -ON IDX.[object_id]=KC.[parent_object_id] -AND IDX.index_id=KC.unique_index_id -INNER JOIN sys.objects O -ON O.[object_id]=IDX.[object_id] -INNER JOIN sys.columns C -ON O.[object_id]=C.[object_id] -AND O.type='U' -AND O.is_ms_shipped=0 -AND IDXC.Column_id=C.Column_id""" - if tablename is not None: - sqlcmd = sqlcmd + """ where lower(O.name)='%s'""" % tablename.lower() - return sqlcmd diff --git a/build/lib/sqlor/mysqlor.py b/build/lib/sqlor/mysqlor.py deleted file mode 100644 index e5df373..0000000 --- a/build/lib/sqlor/mysqlor.py +++ /dev/null @@ -1,179 +0,0 @@ -# -*- coding:utf8 -*- -from mysql import connector -from appPublic.argsConvert import ArgsConvert,ConditionConvert - -from .sor import SQLor -from .ddl_template_mysql import mysql_ddl_tmpl -class MySqlor(SQLor): - ddl_template = mysql_ddl_tmpl - db2modelTypeMapping = { - 'tinyint':'short', - 'smallint':'short', - 'mediumint':'long', - 'int':'long', - 'bigint':'long', - 'decimal':'float', - 'double':'float', - 'float':'float', - 'char':'char', - 'varchar':'str', - 'tinyblob':'text', - 'tinytext':'text', - 'mediumblob':'text', - 'mediumtext':'text', - 'blob':'text', - 'text':'text', - 'mediumblob':'text', - 'mediumtext':'text', - 'longblob':'bin', - 'longtext':'text', - 'barbinary':'text', - 'binary':'text', - 'date':'date', - 'time':'time', - 'datetime':'datetime', - 'timestamp':'datestamp', - 'year':'short', - } - model2dbTypemapping = { - 'date':'date', - 'time':'date', - 'timestamp':'timestamp', - 'str':'varchar', - 'char':'char', - 'short':'int', - 'long':'bigint', - 'float':'double', - 'text':'longtext', - 'bin':'longblob', - 'file':'longblob', - } - @classmethod - def isMe(self,name): - return name=='mysql.connector' - - def grammar(self): - return { - 'select':select_stmt, - } - - def _opendb(self): - self.conn = connector.connect(**self.dbdesc['kwargs']) - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return '%s' - - def dataConvert(self,dataList): - if type(dataList) == type({}): - d = [ i for i in dataList.values()] - else: - d = [ i['value'] for i in dataList] - return tuple(d) - - def pagingSQL(self,sql,paging,NS): - """ - default it not support paging - """ - page = int(NS.get(paging['pagename'],1)) - rows = int(NS.get(paging['rowsname'],10)) - sort = NS.get(paging.get('sortname','sort'),None) - order = NS.get(paging.get('ordername','asc'),'asc') - if not sort: - return sql - if page < 1: - page = 1 - from_line = (page - 1) * rows - end_line = page * rows + 1 - psql = self.pagingSQLmodel() - ns={ - 'from_line':from_line, - 'end_line':end_line, - 'rows':rows, - 'sort':sort, - 'order':order, - } - ac = ArgsConvert('$[',']$') - psql = ac.convert(psql,ns) - retSQL=psql % sql - return retSQL - - def pagingSQLmodel(self): - return u"""select * from (%s) A order by $[sort]$ $[order]$ -limit $[from_line]$,$[rows]$""" - - def tablesSQL(self): - sqlcmd = """SELECT lower(TABLE_NAME) as name, lower(TABLE_COMMENT) as title FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '%s'""" % self.dbdesc.get('dbname','unknown') - return sqlcmd - - def fieldsSQL(self,tablename=None): - sqlcmd=""" - select - lower(column_name) as name, - data_type as type, - case when character_maximum_length is null then NUMERIC_PRECISION - else character_maximum_length end - as length, - NUMERIC_SCALE as dec1, - lower(is_nullable) as nullable, - column_comment as title, - lower(table_name) as table_name - from information_schema.columns where lower(TABLE_SCHEMA) = '%s' """ % self.dbdesc.get('dbname','unknown').lower() - if tablename is not None: - sqlcmd = sqlcmd + """and lower(table_name)='%s';""" % tablename.lower() - return sqlcmd - - def fkSQL(self,tablename=None): - sqlcmd = """SELECT C.TABLE_SCHEMA 拥有者, - C.REFERENCED_TABLE_NAME 父表名称 , - C.REFERENCED_COLUMN_NAME 父表字段 , - C.TABLE_NAME 子表名称, - C.COLUMN_NAME 子表字段, - C.CONSTRAINT_NAME 约束名, - T.TABLE_COMMENT 表注释, - R.UPDATE_RULE 约束更新规则, - R.DELETE_RULE 约束删除规则 - FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE C - JOIN INFORMATION_SCHEMA. TABLES T - ON T.TABLE_NAME = C.TABLE_NAME - JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R - ON R.TABLE_NAME = C.TABLE_NAME - AND R.CONSTRAINT_NAME = C.CONSTRAINT_NAME - AND R.REFERENCED_TABLE_NAME = C.REFERENCED_TABLE_NAME - WHERE C.REFERENCED_TABLE_NAME IS NOT NULL ; - and C.TABLE_SCHEMA = '%s' -""" % self.dbdesc.get('dbname','unknown').lower() - if tablename is not None: - sqlcmd = sqlcmd + " and C.REFERENCED_TABLE_NAME = '%s'" % tablename.lower() - return sqlcmd - - def pkSQL(self,tablename=None): - sqlcmd = """SELECT - lower(c.table_name) as table_name, - lower(c.COLUMN_NAME) as field_name -FROM - INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS t, - INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS c -WHERE - t.CONSTRAINT_TYPE = 'PRIMARY KEY' - AND t.TABLE_SCHEMA = '%s' - AND t.TABLE_NAME = c.TABLE_NAME -""" % self.dbdesc.get('dbname','unknown').lower() - if tablename is not None: - sqlcmd = sqlcmd + " AND c.TABLE_NAME = '%s'" % tablename.lower() - return sqlcmd - - def indexesSQL(self,tablename=None): - sqlcmd = """SELECT DISTINCT - lower(index_name) as index_name, - lower(index_type) as index_type, - lower(table_name) as table_name, - lower(column_name) as column_name -FROM - information_schema.statistics -WHERE - table_schema = '%s'""" % self.dbdesc.get('dbname','unknown') - if tablename is not None: - sqlcmd = sqlcmd + """ AND table_name = '%s'""" % tablename.lower() - return sqlcmd diff --git a/build/lib/sqlor/oracleor.py b/build/lib/sqlor/oracleor.py deleted file mode 100644 index 75ca325..0000000 --- a/build/lib/sqlor/oracleor.py +++ /dev/null @@ -1,131 +0,0 @@ -from .sor import SQLor -from .ddl_template_oracle import oracle_ddl_tmpl -class Oracleor(SQLor): - ddl_template = oracle_ddl_tmpl - db2modelTypeMapping = { - 'char':'char', - 'nchar':'str', - 'varchar':'str', - 'varchar2':'str', - 'nvarchar2':'str', - 'number':'long', - 'integer':'long', - 'binary_float':'float', - 'binary_double':'float', - 'float':'float', - 'timestamp':'timestamp', - 'timestamp with time zone':'timestamp', - 'timestamp with local time zone':'timestamp', - 'interval year to moth':'date', - 'interval day to second':'timestamp', - 'clob':'text', - 'nclob':'text', - 'blob':'file', - 'bfile':'file', - 'date':'date', - } - model2dbTypemapping = { - 'date':'date', - 'time':'date', - 'timestamp':'date', - 'str':'varchar2', - 'char':'char', - 'short':'number', - 'long':'number', - 'float':'number', - 'text':'nclob', - 'file':'blob', - } - @classmethod - def isMe(self,name): - return name=='cx_Oracle' - - def grammar(self): - return { - 'select':select_stmt, - } - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return ':%s' % varname - - def dataConvert(self,dataList): - if type(dataList) == type({}): - return dataList - d = {} - [ d.update({i['name']:i['value']}) for i in dataList ] - return d - - def pagingSQLmodel(self): - return u"""select * -from ( - select page_s.*,rownum row_id - from (%s) page_s - order by $[sort]$ $[order]$ - ) -where row_id >=$[from_line]$ and row_id < $[end_line]$""" - - def tablesSQL(self): - sqlcmd = """select -lower(table_name) as name, -lower(decode(comments,null,table_name,comments)) as title -from USER_TAB_COMMENTS where table_type = 'TABLE'""" - return sqlcmd - - def fieldsSQL(self,tablename=None): - sqlcmd="""select lower(utc.COLUMN_NAME) name - ,utc.DATA_TYPE type - ,utc.DATA_LENGTH length - ,utc.data_scale dec - ,case when utc.nullable = 'Y' then 'yes' else 'no' end nullable - ,lower(nvl(ucc.comments,utc.COLUMN_NAME)) title - ,lower(utc.table_name) as table_name - from user_tab_cols utc left join USER_COL_COMMENTS ucc on utc.table_name = ucc.table_name and utc.COLUMN_NAME = ucc.COLUMN_NAME""" - 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 - distinct(ucc.column_name) as field,rela.table_name as fk_table,rela.column_name as fk_field -from - user_constraints uc,user_cons_columns ucc, - ( - select t2.table_name,t2.column_name,t1.r_constraint_name - from user_constraints t1,user_cons_columns t2 - where t1.r_constraint_name=t2.constraint_name -) rela -where - uc.constraint_name=ucc.constraint_name - and uc.r_constraint_name=rela.r_constraint_name""" - if tablename is not None: - sqlcmd = sqlcmd + """ and lower(uc.table_name)='%s'""" % tablename.lower() - return sqlcmd - - def pkSQL(self,tablename=None): - sqlcmd = """ -select - lower(col.table_name) table_name, - lower(col.column_name) as field_name -from - user_constraints con,user_cons_columns col -where - con.constraint_name=col.constraint_name and con.constraint_type='P'""" - if tablename is not None: - sqlcmd = sqlcmd + """ and lower(col.table_name)='%s'""" % tablename.lower() - return sqlcmd - - def indexesSQL(self,tablename=None): - sqlcmd = """select - lower(a.index_name) index_name, - lower(a.UNIQUENESS) index_type, - lower(a.table_name) table_name, - lower(b.column_name) column_name -from user_indexes a, user_ind_columns b -where a.index_name = b.index_name""" - if tablename is not None: - sqlcmd += """ and lower(a.table_name) = lower('%s')""" % tablename.lower() - return sqlcmd - diff --git a/build/lib/sqlor/postgresqlor.py b/build/lib/sqlor/postgresqlor.py deleted file mode 100644 index 0fd3033..0000000 --- a/build/lib/sqlor/postgresqlor.py +++ /dev/null @@ -1,199 +0,0 @@ -from .sor import SQLor -from .ddl_template_postgresql import postgresql_ddl_tmpl - -class PostgreSQLor(SQLor): - ddl_template = postgresql_ddl_tmpl - db2modelTypeMapping = { - 'smallint':'short', - 'integer':'long', - 'bigint':'llong', - 'decimal':'float', - 'numeric':'float', - 'real':'float', - 'double':'float', - 'serial':'long', - 'bigserial':'llong', - 'char':'char', - 'character':'char', - 'varchar':'str', - 'character varying':'str', - 'text':'text', - 'timestamp':'timestamp', - 'date':'date', - 'time':'time', - 'boolean':'char', - 'bytea':'file' - } - model2dbTypemapping = { - 'date':'date', - 'time':'date', - 'timestamp':'timestamp', - 'str':'varchar', - 'char':'char', - 'short':'smallint', - 'long':'integer', - 'float':'numeric', - 'text':'text', - 'file':'bytea', - } - @classmethod - def isMe(self,name): - return name=='psycopg2' - - def grammar(self): - return { - 'select':select_stmt, - } - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return ':%s' % varname - - def dataConvert(self,dataList): - if type(dataList) == type({}): - return dataList - d = {} - [ d.update({i['name']:i['value']}) for i in dataList ] - return d - - def pagingSQLmodel(self): - return u"""select * -from ( - select page_s.*,rownum row_id - from (%s) page_s - order by $[sort]$ $[order]$ - ) -where row_id >=$[from_line]$ and row_id < $[end_line]$""" - - def tablesSQL(self): - """ - 列出表名 - SELECT tablename FROM pg_tables; - WHERE tablename NOT LIKE 'pg%' - AND tablename NOT LIKE 'sql_%' - ORDER BY tablename; - """ - sqlcmd = """select -lower(table_name) as name, -lower(decode(comments,null,table_name,comments)) as title -from USER_TAB_COMMENTS where table_type = 'TABLE'""" - return sqlcmd - - def fieldsSQL(self,tablename=None): - """SELECT col_description(a.attrelid,a.attnum) as comment,pg_type.typname as typename,a.attname as name, a.attnotnull as notnull -FROM pg_class as c,pg_attribute as a inner join pg_type on pg_type.oid = a.atttypid -where c.relname = 'tablename' and a.attrelid = c.oid and a.attnum>0 - """ - sqlcmd="""select lower(utc.COLUMN_NAME) name - ,utc.DATA_TYPE type - ,utc.DATA_LENGTH length - ,utc.data_scale dec - ,case when utc.nullable = 'Y' then 'yes' else 'no' end nullable - ,lower(nvl(ucc.comments,utc.COLUMN_NAME)) title - ,lower(utc.table_name) as table_name - from user_tab_cols utc left join USER_COL_COMMENTS ucc on utc.table_name = ucc.table_name and utc.COLUMN_NAME = ucc.COLUMN_NAME""" - 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 - distinct(ucc.column_name) as field,rela.table_name as fk_table,rela.column_name as fk_field -from - user_constraints uc,user_cons_columns ucc, - ( - select t2.table_name,t2.column_name,t1.r_constraint_name - from user_constraints t1,user_cons_columns t2 - where t1.r_constraint_name=t2.constraint_name -) rela -where - uc.constraint_name=ucc.constraint_name - and uc.r_constraint_name=rela.r_constraint_name""" - if tablename is not None: - sqlcmd = sqlcmd + """ and lower(uc.table_name)='%s'""" % tablename.lower() - return sqlcmd - - def pkSQL(self,tablename=None): - """ - select pg_attribute.attname as colname,pg_type.typname as typename,pg_constraint.conname as pk_name from -pg_constraint inner join pg_class -on pg_constraint.conrelid = pg_class.oid -inner join pg_attribute on pg_attribute.attrelid = pg_class.oid -and pg_attribute.attnum = pg_constraint.conkey[1] -inner join pg_type on pg_type.oid = pg_attribute.atttypid -where pg_class.relname = 'tablename' -and pg_constraint.contype='p' - """ - sqlcmd = """ -select - lower(col.table_name) table_name, - lower(col.column_name) as field_name -from - user_constraints con,user_cons_columns col -where - con.constraint_name=col.constraint_name and con.constraint_type='P'""" - if tablename is not None: - sqlcmd = sqlcmd + """ and lower(col.table_name)='%s'""" % tablename.lower() - return sqlcmd - - def indexesSQL(self,tablename=None): - """ -SELECT - -A.SCHEMANAME, - -A.TABLENAME, - -A.INDEXNAME, - -A.TABLESPACE, - -A.INDEXDEF, - -B.AMNAME, - -C.INDEXRELID, - -C.INDNATTS, - -C.INDISUNIQUE, - -C.INDISPRIMARY, - -C.INDISCLUSTERED, - -D.DESCRIPTION - -FROM - -PG_AM B - -LEFT JOIN PG_CLASS F ON B.OID = F.RELAM - -LEFT JOIN PG_STAT_ALL_INDEXES E ON F.OID = E.INDEXRELID - -LEFT JOIN PG_INDEX C ON E.INDEXRELID = C.INDEXRELID - -LEFT OUTER JOIN PG_DESCRIPTION D ON C.INDEXRELID = D.OBJOID, - -PG_INDEXES A - -WHERE - -A.SCHEMANAME = E.SCHEMANAME AND A.TABLENAME = E.RELNAME AND A.INDEXNAME = E.INDEXRELNAME - -AND E.SCHEMANAME = 'public' AND E.RELNAME = 'table_name' -""" - sqlcmd = """select - lower(a.index_name) index_name, - lower(a.UNIQUENESS) index_type, - lower(a.table_name) table_name, - lower(b.column_name) column_name -from user_indexes a, user_ind_columns b -where a.index_name = b.index_name""" - if tablename is not None: - sqlcmd += """ and lower(a.table_name) = lower('%s')""" % tablename.lower() - return sqlcmd - diff --git a/build/lib/sqlor/sor.py b/build/lib/sqlor/sor.py deleted file mode 100644 index b3a18b4..0000000 --- a/build/lib/sqlor/sor.py +++ /dev/null @@ -1,454 +0,0 @@ -import os -os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8' -import sys -import codecs -import re -import json -from appPublic.myImport import myImport -from appPublic.dictObject import DictObject,dictObjectFactory -from appPublic.unicoding import uDict -from patterncoding.myTemplateEngine import MyTemplateEngine - - -from appPublic.argsConvert import ArgsConvert,ConditionConvert - -class SQLorException(Exception,object): - def __int__(self,**kvs): - supper(SQLException,self).__init__(self,**kvs) - self.dic = { - 'response':'error', - 'errtype':'SQLor', - 'errmsg':supper(SQLException,self).message, - } - - def __str__(self): - return 'errtype:%s,errmsg=%s' % (self.dic['errtype'],self.dic['errmsg']) - -def setValues(params,ns): - r = ns.get(params,os.getenv(params)) - return r - -def findNamedParameters(sql): - """ - return a list of named parameters - """ - re1 = '\$\{[_a-zA-Z_][a-zA-Z_0-9]*\}' - params1 = re.findall(re1,sql) - return params1 - - -def uniParams(params1): - ret = [] - for i in params1: - if i not in ret: - ret.append(i) - return ret - -def readsql(fn): - f = codecs.open(fn,'r','utf-8') - b = f.read() - f.close() - return b - -class SQLor(object): - def __init__(self,dbdesc=None,sqltp = '$[',sqlts = ']$',sqlvp = '${',sqlvs = '}$'): - self.conn = None - self.cur = None - self.async_mode = False - self.sqltp = sqltp - self.sqlts = sqlts - self.sqlvp = sqlvp - self.sqlvs = sqlvs - self.dbdesc = dbdesc - self.writer = None - self.convfuncs = {} - self.cc = ConditionConvert() - - def setCursor(self,async_mode,conn,cur): - self.async_mode = async_mode - self.conn = conn - self.cur = cur - - def getConn(self): - return self.conn - - def setConvertFunction(self,typ,func): - self.convfuncs.update({typ:func}) - - def convert(self,typ,value): - if self.convfuncs.get(typ,None) is not None: - return self.convfuncs[typ](value) - return value - @classmethod - def isMe(self,name): - return name=='sqlor' - - def pagingSQLmodel(self): - return u"" - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return '?' - - def dataConvert(self,dataList): - return [ i.get('value',None) for i in dataList] - - def dataList(self,k,v): - a = [] - a.append({'name':k,'value':v}) - return a - - def cursor(self): - return self.cur - - def recordCnt(self,sql): - ret = u"""select count(*) rcnt from (%s) rowcount_table""" % sql - return ret - - def pagingSQL(self,sql,paging,NS): - """ - default it not support paging - """ - page = int(NS.get(paging['pagename'],1)) - rows = int(NS.get(paging['rowsname'],10)) - sort = NS.get(paging.get('sortname','sort'),None) - order = NS.get(paging.get('ordername','asc'),'asc') - if not sort: - return sql - if page < 1: - page = 1 - from_line = (page - 1) * rows + 1 - end_line = page * rows + 1 - psql = self.pagingSQLmodel() - ns={ - 'from_line':from_line, - 'end_line':end_line, - 'rows':rows, - 'sort':sort, - 'order':order, - } - ac = ArgsConvert('$[',']$') - psql = ac.convert(psql,ns) - retSQL=psql % sql - return retSQL - - def filterSQL(self,sql,filters,NS): - ac = ArgsConvert('$[',']$') - fbs = [] - for f in filters: - vars = ac.findAllVariables(f) - if len(vars) > 0: - ignoreIt = False - for v in vars: - if not NS.get(v,False): - ignoreIt = True - if not ignoreIt: - f = ac.convert(f,NS) - else: - f = '1=1' - fbs.append(f) - fb = ' '.join(fbs) - retsql = u"""select * from (%s) filter_table where %s""" % (sql,fb) - return retsql - - async def runVarSQL(self,cursor,sql,NS): - """ - using a opened cursor to run a SQL statment with variable, the variable is setup in NS namespace - return a cursor with data - """ - markedSQL,datas = self.maskingSQL(sql,NS) - datas = self.dataConvert(datas) - try: - markedSQL = markedSQL.encode('utf8') - if self.async_mode: - await cursor.execute(markedSQL,datas) - else: - cursor.execute(markedSQL,datas) - - except Exception as e: - print( "markedSQL=",markedSQL,datas,e) - raise e - return - - def maskingSQL(self,org_sql,NS): - """ - replace all ${X}$ format variable exception named by '__mainsql__' in sql with '%s', - and return the marked sql sentent and variable list - sql is a sql statment with variable formated in '${X}$ - the '__mainsql__' variable use to identify the main sql will outout data. - NS is the name space the variable looking for, it is a variable dictionary - return (MarkedSQL,list_of_variable) - """ - sqltextAC = ArgsConvert(self.sqltp,self.sqlts) - sqlargsAC = ArgsConvert(self.sqlvp,self.sqlvs) - sql1 = sqltextAC.convert(org_sql,NS) - cc = ConditionConvert() - sql1 = cc.convert(sql1,NS) - vars = sqlargsAC.findAllVariables(sql1) - phnamespace = {} - [phnamespace.update({v:self.placeHolder(v)}) for v in vars] - m_sql = sqlargsAC.convert(sql1,phnamespace) - newdata = [] - for v in vars: - if v != '__mainsql__': - value = sqlargsAC.getVarValue(v,NS,None) - newdata += self.dataList(v,value) - - return (m_sql,newdata) - - async def execute(self,sql,value,callback,**kwargs): - cur = self.cursor() - await self.runVarSQL(cur,sql,value) - if callback is not None: - fields = [ i[0].lower() for i in cur.description ] - if self.async_mode: - rec = await cur.fetchone() - else: - rec = cur.fetchone() - - while rec is not None: - dic = {} - for i in range(len(fields)): - dic.update({fields[i]:rec[i]}) - #dic = uDict(dic,coding='utf8') - callback(dic,**kwargs) - if self.async_mode: - rec = await cur.fetchone() - else: - rec = cur.fetchone() - - - async def executemany(self,sql,values): - cur = self.cursor() - markedSQL,datas = self.maskingSQL(sql,{}) - datas = [ self.dataConvert(d) for d in values ] - if async_mode: - await cur.executemany(markedSQL,datas) - else: - cur.executemany(markedSQL,datas) - - def pivotSQL(self,tablename,rowFields,columnFields,valueFields): - def maxValue(columnFields,valueFields,cfvalues): - sql = '' - for f in valueFields: - i = 0 - for field in columnFields: - for v in cfvalues[field]: - sql += """ - ,sum(%s_%d) %s_%d""" % (f,i,f,i) - i+=1 - return sql - def casewhen(columnFields,valueFields,cfvalues): - sql = '' - for f in valueFields: - i = 0 - for field in columnFields: - for v in cfvalues[field]: - if v is None: - sql += """,case when %s is null then %s - else 0 end as %s_%d -- %s - """ % (field,f,f,i,v) - else: - sql += """,case when trim(%s) = trim('%s') then %s - else 0 end as %s_%d -- %s - """ % (field,v,f,f,i,v) - - i += 1 - return sql - - cfvalues={} - for field in columnFields: - sqlstring = 'select distinct %s from %s' % (field,tablename) - v = [] - self.execute(sqlstring,{},lambda x: v.append(x)) - cfvalues[field] = [ i[field] for i in v ] - - sql =""" - select """ + ','.join(rowFields) - sql += maxValue(columnFields,valueFields,cfvalues) - sql += """ from - (select """ + ','.join(rowFields) - sql += casewhen(columnFields,valueFields,cfvalues) - sql += """ - from %s) - group by %s""" % (tablename,','.join(rowFields)) - return sql - - async def pivot(self,desc,tablename,rowFields,columnFields,valueFields): - sql = self.pivotSQL(tablename,rowFields,columnFields,valueFields) - desc['sql_string'] = sql - ret = [] - return await self.execute(sql,{},lambda x:ret.append(x)) - - def isSelectSql(self,sql): - i = 0 - while sql[i] in "\r\n \t": - i = i + 1 - return sql.lower().startswith('select ') - - def getSQLfromDesc(self,desc): - sql = '' - if 'sql_file' in desc.keys(): - sql = readsql(desc['sql_file']) - else: - sql = desc['sql_string'] - return sql - - async def record_count(self,desc,NS): - cnt_desc = {} - cnt_desc.update(desc) - sql = self.getSQLfromDesc(desc) - if desc.get('sql_file',False): - del cnt_desc['sql_file'] - cnt_desc['sql_string'] = self.recordCnt(sql) - class Cnt: - def __init__(self): - self.recs = [] - def handler(self,rec): - self.recs.append(rec) - - c = Cnt() - await self.sqlIterator(cnt_desc,NS,c.handler) - print(c.recs[0]) - t = c.recs[0]['rcnt'] - return t - - async def pagingdata(self,desc,NS): - paging_desc = {} - paging_desc.update(desc) - paging_desc.update( - { - "paging":{ - "rowsname":"rows", - "pagename":"page", - "sortname":"sort", - "ordername":"order" - } - }) - if desc.get('sortfield',False): - NS['sort'] = desc.get('sortfield') - sql = self.getSQLfromDesc(desc) - if desc.get('sql_file',False): - del cnt_desc['sql_file'] - paging_desc['sql_string'] = self.pagingSQL(sql, - paging_desc.get('paging'),NS) - - class Cnt: - def __init__(self): - self.recs = [] - def handler(self,rec): - self.recs.append(rec) - - c = Cnt() - await self.sqlIterator(paging_desc,NS,c.handler) - return c.recs - - async def runSQL(self,desc,NS,callback,**kw): - class RecordHandler: - def __init__(self,ns,name): - self.ns = ns - self.name = name - self.ns[name] = [] - - def handler(self,rec): - obj = DictObject(rec) - self.ns[self.name].append(obj) - - cur = self.cursor() - sql = self.getSQLfromDesc(desc) - if self.isSelectSql(sql): - if callback is None: - klass = desc.get('dataname','dummy') - if klass is not None: - rh = RecordHandler(NS,klass) - callback = rh.handler - else: - callback = None - await self.execute(sql,NS,callback) - - async def sqlExecute(self,desc,NS): - await self.execute(sql,NS,None) - - async def tables(self): - sqlstring = self.tablesSQL() - ret = [] - await self.execute(sqlstring,{},lambda x:ret.append(x)) - return ret - - def indexesSQL(self,tablename): - """ - record of { - index_name, - index_type, - table_name, - column_name - } - """ - return None - - async def indexes(self,tablename=None): - sqlstring = self.indexesSQL(tablename.lower()) - if sqlstring is None: - return [] - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - return recs - - async def fields(self,tablename=None): - sqlstring = self.fieldsSQL(tablename) - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - ret = [] - for r in recs: - r.update({'type':self.db2modelTypeMapping.get(r['type'].lower(),'unknown')}) - r.update({'name':r['name'].lower()}) - ret.append(r) - return ret - - async def primary(self,tablename): - sqlstring = self.pkSQL(tablename) - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - return recs - - async def fkeys(self,tablename): - sqlstring = self.fkSQL(tablename) - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - return recs - - async def createTable(self,tabledesc): - te = MyTemplateEngine([],'utf8','utf8') - desc = { - "sql_string":te.renders(self.ddl_template,tabledesc) - } - return await self.sqlExecute(desc,{}) - - async def getTableDesc(self,tablename): - desc = {} - summary = [ i for i in await self.tables() if tablename.lower() == i.name ] - primary = [i.field_name for i in await self.primary(tablename) ] - summary['primary'] = primary - desc['summary'] = summary - desc['fields'] = await self.fields(tablename=tablename) - desc['validation'] = [] - idx = {} - async for idxrec in self.indexes(tablename=tablename): - if idxrec.index_name != idx.get('name',None): - if idx != {}: - desc['validation'].append(idx) - idx = { - 'fields':[] - } - else: - idx['fields'] = [] - idx['name'] = idxrec.index_name - idx['oper'] = 'idx' - idx['fields'].append(idxrec.field_name) - if idx != {}: - desc['validation'].append(idx) - return desc - - - diff --git a/build/lib/sqlor/sqlite3or.py b/build/lib/sqlor/sqlite3or.py deleted file mode 100644 index adb065d..0000000 --- a/build/lib/sqlor/sqlite3or.py +++ /dev/null @@ -1,100 +0,0 @@ -import re -from .sor import SQLor - -class SQLite3or(SQLor): - db2modelTypeMapping = { - 'char':'char', - 'nchar':'str', - 'text':'text', - 'ntext':'text', - 'varchar':'str', - 'nvarchar':'str', - 'blob':'file', - 'integer':'long', - 'double':'float', - 'date':'date', - 'time':'time', - 'timestamp':'timestamp', - 'number':'long', - } - model2dbTypemapping = { - 'date':'date', - 'time':'time', - 'timestamp':'timestamp', - 'str':'nvarchar', - 'char':'char', - 'short':'int', - 'long':'integer', - 'float':'double', - 'text':'ntext', - 'file':'blob', - } - @classmethod - def isMe(self,name): - return name=='sqlite3' - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return '?' - - def dataConvert(self,dataList): - if type(dataList) == type({}): - d = [ i for i in dataList.values()] - else: - d = [ i['value'] for i in dataList] - return tuple(d) - - def pagingSQLmodel(self): - sql = u"""select * from (%s) order by $[sort]$ $[order]$ limit $[from_line]$,$[end_line]$""" - return sql - - def tablesSQL(self): - sqlcmd = u"""select name, tbl_name as title from sqlite_master where upper(type) = 'TABLE'""" - return sqlcmd - - def fieldsSQL(self,tablename): - sqlcmd="""PRAGMA table_info('%s')""" % tablename.lower() - return sqlcmd - - def fields(self,tablename): - m = u'(\w+)\(((\d+)(,(\d+)){0,1})\){0,1}' - k = re.compile(m) - def typesplit(typ): - d = k.search(typ) - if d is None: - return typ,0,0 - - return d.group(1),int(d.group(3) if d.group(3) is not None else 0 ),int(d.group(5) if d.group(5) is not None else 0) - - sqlstring = self.fieldsSQL(tablename) - recs = [] - self.execute(sqlstring,callback=lambda x:recs.append(x)) - for r in recs: - t,l,d = typesplit(r['type']) - r['type'] = t - r['length'] = int(l) - r['dec'] = int(d) - r['title'] = r['name'] - ret = [] - for r in recs: - r.update({'type':self.db2modelTypeMapping[r['type'].lower()]}) - r.update({'name':r['name'].lower()}) - ret.append(r) - return ret - - def fkSQL(self,tablename): - sqlcmd = "" - return sqlcmd - - def fkeys(self,tablename): - return [] - - def primary(self,tablename): - recs = self.fields(tablename) - ret = [ {'field':r['name']} for r in recs if r['pk'] == 1 ] - return ret - - def pkSQL(self,tablename): - sqlcmd = "" - return sqlcmd diff --git a/build/lib/sqlor/sqlor.py b/build/lib/sqlor/sqlor.py deleted file mode 100644 index cbb03fc..0000000 --- a/build/lib/sqlor/sqlor.py +++ /dev/null @@ -1,393 +0,0 @@ -import os -os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8' -import sys -import codecs -import re -import json -from appPublic.myImport import myImport -from appPublic.dictObject import DictObject,dictObjectFactory -from appPublic.unicoding import uDict -from patterncoding.myTemplateEngine import MyTemplateEngine - - -from appPublic.argsConvert import ArgsConvert,ConditionConvert - -class SQLorException(Exception,object): - def __int__(self,**kvs): - supper(SQLException,self).__init__(self,**kvs) - self.dic = { - 'response':'error', - 'errtype':'SQLor', - 'errmsg':supper(SQLException,self).message, - } - - def __str__(self): - return 'errtype:%s,errmsg=%s' % (self.dic['errtype'],self.dic['errmsg']) - -def setValues(params,ns): - r = ns.get(params,os.getenv(params)) - return r - -def findNamedParameters(sql): - """ - return a list of named parameters - """ - re1 = '\$\{[_a-zA-Z_][a-zA-Z_0-9]*\}' - params1 = re.findall(re1,sql) - return params1 - - -def uniParams(params1): - ret = [] - for i in params1: - if i not in ret: - ret.append(i) - return ret - -def readsql(fn): - f = codecs.open(fn,'r','utf-8') - b = f.read() - f.close() - return b - -class SQLor(object): - def __init__(self,dbdesc=None,sqltp = '$[',sqlts = ']$',sqlvp = '${',sqlvs = '}$'): - self.conn = None - self.cur = None - self.sqltp = sqltp - self.sqlts = sqlts - self.sqlvp = sqlvp - self.sqlvs = sqlvs - self.dbdesc = dbdesc - self.writer = None - self.convfuncs = {} - self.cc = ConditionConvert() - - def getConn(self): - return self.conn - - def setConvertFunction(self,typ,func): - self.convfuncs.update({typ:func}) - - def convert(self,typ,value): - if self.convfuncs.get(typ,None) is not None: - return self.convfuncs[typ](value) - return value - @classmethod - def isMe(self,name): - return name=='sqlor' - - def pagingSQLmodel(self): - return u"" - - def placeHolder(self,varname): - if varname=='__mainsql__' : - return '' - return '?' - - def dataConvert(self,dataList): - return [ i.get('value',None) for i in dataList] - - def dataList(self,k,v): - a = [] - a.append({'name':k,'value':v}) - return a - - def cursor(self): - return self.cur - - def recordCnt(self,sql): - ret = u"""select count(*) rcnt from (%s) rowcount_table""" % sql - return ret - - def pagingSQL(self,sql,paging,NS): - """ - default it not support paging - """ - page = int(NS.get(paging['pagename'],1)) - rows = int(NS.get(paging['rowsname'],10)) - sort = NS.get(paging.get('sortname','sort'),None) - order = NS.get(paging.get('ordername','asc'),'asc') - if not sort: - return sql - if page < 1: - page = 1 - from_line = (page - 1) * rows + 1 - end_line = page * rows + 1 - psql = self.pagingSQLmodel() - ns={ - 'from_line':from_line, - 'end_line':end_line, - 'rows':rows, - 'sort':sort, - 'order':order, - } - ac = ArgsConvert('$[',']$') - psql = ac.convert(psql,ns) - retSQL=psql % sql - return retSQL - - def filterSQL(self,sql,filters,NS): - ac = ArgsConvert('$[',']$') - fbs = [] - for f in filters: - vars = ac.findAllVariables(f) - if len(vars) > 0: - ignoreIt = False - for v in vars: - if not NS.get(v,False): - ignoreIt = True - if not ignoreIt: - f = ac.convert(f,NS) - else: - f = '1=1' - fbs.append(f) - fb = ' '.join(fbs) - retsql = u"""select * from (%s) filter_table where %s""" % (sql,fb) - return retsql - - async def runVarSQL(self,cursor,sql,NS): - """ - using a opened cursor to run a SQL statment with variable, the variable is setup in NS namespace - return a cursor with data - """ - markedSQL,datas = self.maskingSQL(sql,NS) - datas = self.dataConvert(datas) - try: - markedSQL = markedSQL.encode('utf8') - await cursor.execute(markedSQL,datas) - except Exception as e: - print( "markedSQL=",markedSQL,datas,e) - raise e - return - - def maskingSQL(self,org_sql,NS): - """ - replace all ${X}$ format variable exception named by '__mainsql__' in sql with '%s', - and return the marked sql sentent and variable list - sql is a sql statment with variable formated in '${X}$ - the '__mainsql__' variable use to identify the main sql will outout data. - NS is the name space the variable looking for, it is a variable dictionary - return (MarkedSQL,list_of_variable) - """ - sqltextAC = ArgsConvert(self.sqltp,self.sqlts) - sqlargsAC = ArgsConvert(self.sqlvp,self.sqlvs) - sql1 = sqltextAC.convert(org_sql,NS) - cc = ConditionConvert() - sql1 = cc.convert(sql1,NS) - vars = sqlargsAC.findAllVariables(sql1) - phnamespace = {} - [phnamespace.update({v:self.placeHolder(v)}) for v in vars] - m_sql = sqlargsAC.convert(sql1,phnamespace) - newdata = [] - for v in vars: - if v != '__mainsql__': - value = sqlargsAC.getVarValue(v,NS,None) - newdata += self.dataList(v,value) - - return (m_sql,newdata) - - async def execute(self,sql,value,callback,**kwargs): - cur = self.cursor() - await self.runVarSQL(cur,sql,value) - if callback is not None: - fields = [ i[0].lower() for i in cur.description ] - rec = await cur.fetchone() - while rec is not None: - dic = {} - for i in range(len(fields)): - dic.update({fields[i]:rec[i]}) - dic = uDict(dic,coding='utf8') - callback(dic,**kwargs) - rec = await cur.fetchone() - - self.close_cursor() - - async def isOK(self): - try: - await self.execute('select 1 as cnt',{}); - return True - except: - return False - - async def executemany(self,sql,values): - cur = self.cursor() - markedSQL,datas = self.maskingSQL(sql,{}) - datas = [ self.dataConvert(d) for d in values ] - await cur.executemany(markedSQL,datas) - self.close_cursor() - - def pivotSQL(self,tablename,rowFields,columnFields,valueFields): - def maxValue(columnFields,valueFields,cfvalues): - sql = '' - for f in valueFields: - i = 0 - for field in columnFields: - for v in cfvalues[field]: - sql += """ - ,sum(%s_%d) %s_%d""" % (f,i,f,i) - i+=1 - return sql - def casewhen(columnFields,valueFields,cfvalues): - sql = '' - for f in valueFields: - i = 0 - for field in columnFields: - for v in cfvalues[field]: - if v is None: - sql += """,case when %s is null then %s - else 0 end as %s_%d -- %s - """ % (field,f,f,i,v) - else: - sql += """,case when trim(%s) = trim('%s') then %s - else 0 end as %s_%d -- %s - """ % (field,v,f,f,i,v) - - i += 1 - return sql - - cfvalues={} - for field in columnFields: - sqlstring = 'select distinct %s from %s' % (field,tablename) - v = [] - self.execute(sqlstring,{},lambda x: v.append(x)) - cfvalues[field] = [ i[field] for i in v ] - - sql =""" - select """ + ','.join(rowFields) - sql += maxValue(columnFields,valueFields,cfvalues) - sql += """ from - (select """ + ','.join(rowFields) - sql += casewhen(columnFields,valueFields,cfvalues) - sql += """ - from %s) - group by %s""" % (tablename,','.join(rowFields)) - return sql - - async def pivot(self,desc,tablename,rowFields,columnFields,valueFields): - sql = self.pivotSQL(tablename,rowFields,columnFields,valueFields) - desc['sql_string'] = sql - ret = [] - return await self.execute(sql,{},lambda x:ret.append(x)) - - def isSeelectSQL(self,sql): - i = 0 - while sql[i] in "\r\n \t": - i = i + 1 - return sql.lower().startswith('select ') - - async def sqlIterator(self,desc,NS,callback,**kw): - class RecordHandler: - def __init__(self,ns,klass): - self.ns = ns - self.klass = klass - self.recs = [] - - def handler(self,rec): - obj = self.klass(rec) - self.recs.append(obj) - - def finish(self): - self.ns[name] = self.recs - - cur = self.cursor() - sql = '' - if 'sql_file' in desc.keys(): - sql = readsql(desc['sql_file']) - else: - sql = desc['sql_string'] - if isSelectSql(sql): - if callback is None: - klass = desc.get('classname','DictObject') - if klass is not None: - rh = RecordHandler(NS,klass) - callback = rh.handler - else: - callback = None - await self.execute(sql,NS,callback) - - async def sqlExecute(self,desc,NS): - await self.execute(sql,NS,None) - - async def tables(self): - sqlstring = self.tablesSQL() - ret = [] - await self.execute(sqlstring,{},lambda x:ret.append(x)) - return ret - - def indexesSQL(self,tablename): - """ - record of { - index_name, - index_type, - table_name, - column_name - } - """ - return None - - async def indexes(self,tablename=None): - sqlstring = self.indexesSQL(tablename.lower()) - if sqlstring is None: - return [] - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - return recs - - async def fields(self,tablename=None): - sqlstring = self.fieldsSQL(tablename) - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - ret = [] - for r in recs: - r.update({'type':self.db2modelTypeMapping.get(r['type'].lower(),'unknown')}) - r.update({'name':r['name'].lower()}) - ret.append(r) - return ret - - async def primary(self,tablename): - sqlstring = self.pkSQL(tablename) - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - return recs - - async def fkeys(self,tablename): - sqlstring = self.fkSQL(tablename) - recs = [] - await self.execute(sqlstring,{},lambda x:recs.append(x)) - return recs - - async def createTable(self,tabledesc): - te = MyTemplateEngine([],'utf8','utf8') - desc = { - "sql_string":te.renders(self.ddl_template,tabledesc) - } - return await self.sqlExecute(desc,{}) - - async def getTableDesc(self,tablename): - desc = {} - summary = [ i for i in await self.tables() if tablename.lower() == i.name ] - primary = [i.field_name for i in await self.primary(tablename) ] - summary['primary'] = primary - desc['summary'] = summary - desc['fields'] = await self.fields(tablename=tablename) - desc['validation'] = [] - idx = {} - async for idxrec in self.indexes(tablename=tablename): - if idxrec.index_name != idx.get('name',None): - if idx != {}: - desc['validation'].append(idx) - idx = { - 'fields':[] - } - else: - idx['fields'] = [] - idx['name'] = idxrec.index_name - idx['oper'] = 'idx' - idx['fields'].append(idxrec.field_name) - if idx != {}: - desc['validation'].append(idx) - return desc - - - diff --git a/dist/sqlor-0.0.1-py3.7.egg b/dist/sqlor-0.0.1-py3.7.egg deleted file mode 100644 index d0f4e5c4d96906bb7fcf8c235d44b7eda9be6541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56420 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 z 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 V3jD<)BZGpUKu{r~xqai){{ZMD%}M|O