cloudoll 2.0.24__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cloudoll-2.0.24 → cloudoll-2.2.0}/PKG-INFO +26 -10
- {cloudoll-2.0.24 → cloudoll-2.2.0}/README.md +25 -9
- cloudoll-2.2.0/cloudoll/__init__.py +1 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/cli.py +2 -3
- cloudoll-2.2.0/cloudoll/orm/__init__.py +193 -0
- cloudoll-2.2.0/cloudoll/orm/base.py +68 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/orm/field.py +142 -58
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/orm/model.py +146 -123
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/orm/mysql.py +15 -7
- cloudoll-2.2.0/cloudoll/orm/parse.py +139 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/orm/postgres.py +15 -9
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/utils/m2d.py +123 -51
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/web/__init__.py +92 -49
- cloudoll-2.2.0/cloudoll/web/exception.py +63 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/web/settings.py +2 -1
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll.egg-info/PKG-INFO +26 -10
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll.egg-info/SOURCES.txt +2 -0
- cloudoll-2.0.24/cloudoll/__init__.py +0 -1
- cloudoll-2.0.24/cloudoll/orm/base.py +0 -87
- cloudoll-2.0.24/cloudoll/utils/__init__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/LICENSE +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/__main__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/error/__init__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/error/errors.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/logging/__init__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/mail/__init__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/mail/smtp.py +0 -0
- {cloudoll-2.0.24/cloudoll/orm → cloudoll-2.2.0/cloudoll/robot}/__init__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/robot/dingtalk.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/robot/feishu.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/robot/slack.py +0 -0
- {cloudoll-2.0.24/cloudoll/robot → cloudoll-2.2.0/cloudoll/utils}/__init__.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/utils/common.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/utils/watch.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/web/html.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/web/http.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll/web/jwt.py +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll.egg-info/dependency_links.txt +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll.egg-info/entry_points.txt +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll.egg-info/requires.txt +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/cloudoll.egg-info/top_level.txt +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/setup.cfg +0 -0
- {cloudoll-2.0.24 → cloudoll-2.2.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: 辅助快速创建可分布的微服务。
|
|
5
5
|
Home-page: https://gitee.com/chuchur/cloudoll-py
|
|
6
6
|
Author: chuchur
|
|
@@ -350,20 +350,36 @@ server:
|
|
|
350
350
|
show_index: true
|
|
351
351
|
append_version: true
|
|
352
352
|
follow_symlinks: true
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
353
|
+
database:
|
|
354
|
+
mysql:
|
|
355
|
+
host: 127.0.0.1
|
|
356
|
+
port: 3306
|
|
357
|
+
user: root
|
|
358
|
+
password: abcd
|
|
359
|
+
db: blog
|
|
360
|
+
charset: utf8mb4
|
|
360
361
|
```
|
|
361
362
|
|
|
362
363
|
默认开发会使用默认的`local`作为配置。 启动时 通过 `env` 加载对应的配置。 如 `python3 app.py --env=prod` 会加载 `conf.prod.yaml`
|
|
363
364
|
|
|
365
|
+
# cli
|
|
364
366
|
|
|
365
|
-
|
|
367
|
+
## 生成模型
|
|
366
368
|
|
|
369
|
+
从数据库导出 `users` 表模型
|
|
367
370
|
```sh
|
|
368
|
-
|
|
371
|
+
cloudoll gen -t users
|
|
372
|
+
```
|
|
373
|
+
更多参数 :
|
|
374
|
+
- -p (--path) 导出的模型路径
|
|
375
|
+
- -c (--create) 值 model(默认) 生成模型, 值table 建表
|
|
376
|
+
- -t (--table) 要生的模型或要建的表名,以`,`分开, `ALL` 所有表
|
|
377
|
+
- -env (--environment) 读取配置名 ,值 local(默认) / test / prod
|
|
378
|
+
- -db (--database) 数据库实例名,取决于配置文件,如果有多个数据库
|
|
379
|
+
- -h (--help) 帮助
|
|
380
|
+
|
|
381
|
+
## 开发调试
|
|
382
|
+
|
|
383
|
+
```sh
|
|
384
|
+
cloudoll start
|
|
369
385
|
```
|
|
@@ -331,20 +331,36 @@ server:
|
|
|
331
331
|
show_index: true
|
|
332
332
|
append_version: true
|
|
333
333
|
follow_symlinks: true
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
334
|
+
database:
|
|
335
|
+
mysql:
|
|
336
|
+
host: 127.0.0.1
|
|
337
|
+
port: 3306
|
|
338
|
+
user: root
|
|
339
|
+
password: abcd
|
|
340
|
+
db: blog
|
|
341
|
+
charset: utf8mb4
|
|
341
342
|
```
|
|
342
343
|
|
|
343
344
|
默认开发会使用默认的`local`作为配置。 启动时 通过 `env` 加载对应的配置。 如 `python3 app.py --env=prod` 会加载 `conf.prod.yaml`
|
|
344
345
|
|
|
346
|
+
# cli
|
|
345
347
|
|
|
346
|
-
|
|
348
|
+
## 生成模型
|
|
347
349
|
|
|
350
|
+
从数据库导出 `users` 表模型
|
|
348
351
|
```sh
|
|
349
|
-
|
|
352
|
+
cloudoll gen -t users
|
|
353
|
+
```
|
|
354
|
+
更多参数 :
|
|
355
|
+
- -p (--path) 导出的模型路径
|
|
356
|
+
- -c (--create) 值 model(默认) 生成模型, 值table 建表
|
|
357
|
+
- -t (--table) 要生的模型或要建的表名,以`,`分开, `ALL` 所有表
|
|
358
|
+
- -env (--environment) 读取配置名 ,值 local(默认) / test / prod
|
|
359
|
+
- -db (--database) 数据库实例名,取决于配置文件,如果有多个数据库
|
|
360
|
+
- -h (--help) 帮助
|
|
361
|
+
|
|
362
|
+
## 开发调试
|
|
363
|
+
|
|
364
|
+
```sh
|
|
365
|
+
cloudoll start
|
|
350
366
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.0"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from .web.settings import get_config
|
|
3
|
-
from .orm
|
|
4
|
-
from .orm.postgres import Postgres
|
|
3
|
+
from .orm import create_engine
|
|
5
4
|
from .utils.m2d import create_models, create_tables
|
|
6
5
|
import asyncio
|
|
7
6
|
import os
|
|
@@ -50,7 +49,7 @@ def gen(path, create, table, environment, database) -> None:
|
|
|
50
49
|
if db_config is None:
|
|
51
50
|
raise ValueError(
|
|
52
51
|
f"Can't find the database config key ->{database} in conf.{environment}.yaml")
|
|
53
|
-
sa = await
|
|
52
|
+
sa = await create_engine(**db_config)
|
|
54
53
|
if sa.pool is None:
|
|
55
54
|
return
|
|
56
55
|
model_path = os.path.join(os.path.abspath("."), path)
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from redis import asyncio as aioredis
|
|
2
|
+
from .parse import parse_coon
|
|
3
|
+
import aiopg, aiomysql
|
|
4
|
+
from .base import MeteBase, QueryTypes
|
|
5
|
+
from typing import Any
|
|
6
|
+
from ..logging import error
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def create_engine(**kw):
|
|
11
|
+
url = kw.get("url")
|
|
12
|
+
driver = None
|
|
13
|
+
configs = {}
|
|
14
|
+
query = {}
|
|
15
|
+
|
|
16
|
+
if url is not None:
|
|
17
|
+
configs, query = parse_coon(url)
|
|
18
|
+
driver = configs["type"]
|
|
19
|
+
else:
|
|
20
|
+
driver = kw.get("type")
|
|
21
|
+
configs = kw
|
|
22
|
+
|
|
23
|
+
if driver == "mysql":
|
|
24
|
+
return await Mysql().create_engine(**configs, **query)
|
|
25
|
+
elif driver == "postgres":
|
|
26
|
+
return await Postgres().create_engine(**configs, **query)
|
|
27
|
+
elif driver == "redis" or driver == "rediss":
|
|
28
|
+
"""
|
|
29
|
+
redis://[[username]:[password]]@localhost:6379/0
|
|
30
|
+
rediss://[[username]:[password]]@localhost:6379/0
|
|
31
|
+
"""
|
|
32
|
+
if url is None:
|
|
33
|
+
url = f"{driver}://{configs['username']}:{configs['password']}@{configs['host']}:{configs['port']}/{configs['db']}"
|
|
34
|
+
return await aioredis.from_url(url, **query)
|
|
35
|
+
else:
|
|
36
|
+
error("Not suport this database type.")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Postgres(MeteBase):
|
|
40
|
+
def __init__(self, pool=None):
|
|
41
|
+
self.pool = pool
|
|
42
|
+
self.driver = "postgres"
|
|
43
|
+
|
|
44
|
+
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
45
|
+
self.__init__(*args, **kwds)
|
|
46
|
+
|
|
47
|
+
async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
48
|
+
sql = sql.replace("?", "%s").replace("`", "")
|
|
49
|
+
print("sql", sql, params)
|
|
50
|
+
if not self.pool:
|
|
51
|
+
raise ValueError("must be create_engine first.")
|
|
52
|
+
async with self.pool.acquire() as conn:
|
|
53
|
+
async with conn.cursor() as cursor:
|
|
54
|
+
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
55
|
+
await cursor.execute(sql, params)
|
|
56
|
+
|
|
57
|
+
# if query_type.value > 4:
|
|
58
|
+
# if self.driver == "mysql":
|
|
59
|
+
# await conn.commit()
|
|
60
|
+
# elif self.driver == "postgres":
|
|
61
|
+
columns = [desc[0] for desc in cursor.description]
|
|
62
|
+
result = None
|
|
63
|
+
|
|
64
|
+
if query_type == QueryTypes.ALL:
|
|
65
|
+
rows = await cursor.fetchall()
|
|
66
|
+
result = [dict(zip(columns, row)) for row in rows]
|
|
67
|
+
return result
|
|
68
|
+
elif query_type == QueryTypes.ONE:
|
|
69
|
+
rows = await cursor.fetchone()
|
|
70
|
+
result = dict(zip(columns, rows))
|
|
71
|
+
return result
|
|
72
|
+
elif query_type == QueryTypes.MANY:
|
|
73
|
+
rows = await cursor.fetchmany(size)
|
|
74
|
+
result = [dict(zip(columns, row)) for row in rows]
|
|
75
|
+
return result
|
|
76
|
+
elif query_type == QueryTypes.COUNT:
|
|
77
|
+
rs = await cursor.fetchone()
|
|
78
|
+
return rs[0]
|
|
79
|
+
elif query_type == QueryTypes.CREATE:
|
|
80
|
+
result = cursor.rowcount > 0
|
|
81
|
+
id = cursor.lastrowid
|
|
82
|
+
return result, id
|
|
83
|
+
elif query_type == QueryTypes.UPDATE:
|
|
84
|
+
return cursor.rowcount > 0
|
|
85
|
+
elif query_type == QueryTypes.DELETE:
|
|
86
|
+
return cursor.rowcount > 0
|
|
87
|
+
|
|
88
|
+
async def create_engine(self, **kw):
|
|
89
|
+
try:
|
|
90
|
+
host = (kw.get("host", "localhost"),)
|
|
91
|
+
port = (kw.get("port", 5432),)
|
|
92
|
+
user = (kw.get("username"),)
|
|
93
|
+
password = (str(kw.get("password", "")),)
|
|
94
|
+
db = (kw.get("db"),)
|
|
95
|
+
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}"
|
|
96
|
+
self.pool = await aiopg.create_pool(
|
|
97
|
+
dsn=dsn,
|
|
98
|
+
timeout=kw.get("timeout"),
|
|
99
|
+
echo=kw.get("echo", False),
|
|
100
|
+
maxsize=kw.get("maxsize", 10),
|
|
101
|
+
minsize=kw.get("pool_size", 5),
|
|
102
|
+
)
|
|
103
|
+
print(f"Database connection successfuly for postgres/{kw.get('db')}")
|
|
104
|
+
except Exception as e:
|
|
105
|
+
error(e)
|
|
106
|
+
# print(traceback.format_exc())
|
|
107
|
+
error(f"Database connection failed,the instance : postgres/{kw.get('db')}")
|
|
108
|
+
|
|
109
|
+
return self
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class AttrDict(dict):
|
|
113
|
+
"""Dict that can get attribute by dot, and doesn't raise KeyError"""
|
|
114
|
+
|
|
115
|
+
def __getattr__(self, name):
|
|
116
|
+
try:
|
|
117
|
+
return self[name]
|
|
118
|
+
except KeyError:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class AttrDictCursor(aiomysql.DictCursor):
|
|
123
|
+
dict_type = AttrDict
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class Mysql(MeteBase):
|
|
127
|
+
def __init__(self, pool=None):
|
|
128
|
+
self.pool = pool
|
|
129
|
+
self.driver = "mysql"
|
|
130
|
+
|
|
131
|
+
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
132
|
+
self.__init__(*args, **kwds)
|
|
133
|
+
return self
|
|
134
|
+
|
|
135
|
+
async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
136
|
+
sql = sql.replace("?", "%s")
|
|
137
|
+
print("sql", sql, params)
|
|
138
|
+
if not self.pool:
|
|
139
|
+
raise ValueError("must be create_engine first.")
|
|
140
|
+
async with self.pool.acquire() as conn:
|
|
141
|
+
async with conn.cursor() as cursor:
|
|
142
|
+
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
143
|
+
await cursor.execute(sql, params)
|
|
144
|
+
|
|
145
|
+
# if query_type.value > 4:
|
|
146
|
+
# if self.driver == "mysql":
|
|
147
|
+
await conn.commit()
|
|
148
|
+
# elif self.driver == "postgres":
|
|
149
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
150
|
+
|
|
151
|
+
if query_type == QueryTypes.ALL:
|
|
152
|
+
return await cursor.fetchall()
|
|
153
|
+
elif query_type == QueryTypes.ONE:
|
|
154
|
+
return await cursor.fetchone()
|
|
155
|
+
elif query_type == QueryTypes.MANY:
|
|
156
|
+
return await cursor.fetchmany(size)
|
|
157
|
+
elif query_type == QueryTypes.COUNT:
|
|
158
|
+
rs = await cursor.fetchone()
|
|
159
|
+
value = 0
|
|
160
|
+
for r in rs:
|
|
161
|
+
value = rs[r]
|
|
162
|
+
return value
|
|
163
|
+
elif query_type == QueryTypes.CREATE:
|
|
164
|
+
result = cursor.rowcount > 0
|
|
165
|
+
id = cursor.lastrowid
|
|
166
|
+
return result, id
|
|
167
|
+
elif query_type == QueryTypes.UPDATE:
|
|
168
|
+
return cursor.rowcount > 0
|
|
169
|
+
elif query_type == QueryTypes.DELETE:
|
|
170
|
+
return cursor.rowcount > 0
|
|
171
|
+
|
|
172
|
+
async def create_engine(self, loop=None, **kw):
|
|
173
|
+
try:
|
|
174
|
+
self.pool = await aiomysql.create_pool(
|
|
175
|
+
host=kw.get("host", "localhost"),
|
|
176
|
+
port=kw.get("port", 3306),
|
|
177
|
+
user=kw.get("username"),
|
|
178
|
+
password=str(kw.get("password", "")),
|
|
179
|
+
db=kw.get("db"),
|
|
180
|
+
echo=kw.get("echo", False),
|
|
181
|
+
charset=kw.get("charset", "utf8"),
|
|
182
|
+
autocommit=False, # kw.get("autocommit", False),
|
|
183
|
+
maxsize=kw.get("maxsize", 10),
|
|
184
|
+
minsize=kw.get("pool_size", 5),
|
|
185
|
+
cursorclass=AttrDictCursor,
|
|
186
|
+
loop=loop,
|
|
187
|
+
)
|
|
188
|
+
print(f"Database connection successfuly for mysql/{kw.get('db')}.")
|
|
189
|
+
except Exception as e:
|
|
190
|
+
print(e)
|
|
191
|
+
# print(traceback.format_exc())
|
|
192
|
+
error(f"Database connection failed,the instance : mysql/{kw.get('db')}")
|
|
193
|
+
return self
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class QueryTypes(Enum):
|
|
5
|
+
ALL = 1
|
|
6
|
+
ONE = 2
|
|
7
|
+
MANY = 3
|
|
8
|
+
COUNT = 4
|
|
9
|
+
CREATE = 5
|
|
10
|
+
UPDATE = 6
|
|
11
|
+
DELETE = 7
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MeteBase:
|
|
15
|
+
|
|
16
|
+
async def __aexit__(self):
|
|
17
|
+
await self.close()
|
|
18
|
+
|
|
19
|
+
async def close(self):
|
|
20
|
+
if self.pool:
|
|
21
|
+
self.pool.close()
|
|
22
|
+
await self.pool.wait_closed()
|
|
23
|
+
|
|
24
|
+
# async def begin_transaction(self):
|
|
25
|
+
# conn = await self._set_conn()
|
|
26
|
+
# await conn.begin()
|
|
27
|
+
# return conn
|
|
28
|
+
|
|
29
|
+
# async def begin_transaction_scope(self, fun):
|
|
30
|
+
# conn = await self._set_conn()
|
|
31
|
+
# await conn.begin()
|
|
32
|
+
# cursor = None
|
|
33
|
+
# if isfunction(fun):
|
|
34
|
+
# try:
|
|
35
|
+
# cursor = await conn.cursor()
|
|
36
|
+
# await fun(cursor)
|
|
37
|
+
# await conn.commit()
|
|
38
|
+
# except Exception as e:
|
|
39
|
+
# error(e)
|
|
40
|
+
# await conn.rollback()
|
|
41
|
+
# finally:
|
|
42
|
+
# if cursor:
|
|
43
|
+
# await cursor.close()
|
|
44
|
+
# self.pool.release(conn)
|
|
45
|
+
async def query(
|
|
46
|
+
self, sql, params=None, query_type: QueryTypes = 2, size: int = 10
|
|
47
|
+
): ...
|
|
48
|
+
|
|
49
|
+
async def all(self, sql, params):
|
|
50
|
+
return await self.query(sql, params, QueryTypes.ALL)
|
|
51
|
+
|
|
52
|
+
async def one(self, sql, params):
|
|
53
|
+
return await self.query(sql, params, QueryTypes.ONE)
|
|
54
|
+
|
|
55
|
+
async def many(self, sql, params, size: int):
|
|
56
|
+
return await self.query(sql, params, QueryTypes.MANY, size)
|
|
57
|
+
|
|
58
|
+
async def count(self, sql, params):
|
|
59
|
+
return await self.query(sql, params, QueryTypes.COUNT)
|
|
60
|
+
|
|
61
|
+
async def update(self, sql, params):
|
|
62
|
+
return await self.query(sql, params, QueryTypes.UPDATE)
|
|
63
|
+
|
|
64
|
+
async def delete(self, sql, params):
|
|
65
|
+
return await self.query(sql, params, QueryTypes.DELETE)
|
|
66
|
+
|
|
67
|
+
async def create(self, sql, params):
|
|
68
|
+
return await self.query(sql, params, QueryTypes.CREATE)
|
|
@@ -7,45 +7,50 @@ class objdict(dict):
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
OP = objdict(
|
|
10
|
-
AND=
|
|
11
|
-
OR=
|
|
12
|
-
ADD=
|
|
13
|
-
SUB=
|
|
14
|
-
MUL=
|
|
15
|
-
DIV=
|
|
16
|
-
BIN_AND=
|
|
17
|
-
BIN_OR=
|
|
18
|
-
XOR=
|
|
19
|
-
MOD=
|
|
20
|
-
EQ=
|
|
21
|
-
LT=
|
|
22
|
-
LTE=
|
|
23
|
-
GT=
|
|
24
|
-
GTE=
|
|
25
|
-
NE=
|
|
26
|
-
IN=
|
|
27
|
-
NOT_IN=
|
|
28
|
-
IS=
|
|
29
|
-
DESC=
|
|
30
|
-
ASC=
|
|
31
|
-
AVG=
|
|
32
|
-
AS=
|
|
33
|
-
SUM=
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
10
|
+
AND="AND",
|
|
11
|
+
OR="OR",
|
|
12
|
+
ADD="+",
|
|
13
|
+
SUB="-",
|
|
14
|
+
MUL="*",
|
|
15
|
+
DIV="/",
|
|
16
|
+
BIN_AND="&",
|
|
17
|
+
BIN_OR="|",
|
|
18
|
+
XOR="#",
|
|
19
|
+
MOD="%",
|
|
20
|
+
EQ="=",
|
|
21
|
+
LT="<",
|
|
22
|
+
LTE="<=",
|
|
23
|
+
GT=">",
|
|
24
|
+
GTE=">=",
|
|
25
|
+
NE="!=",
|
|
26
|
+
IN="IN",
|
|
27
|
+
NOT_IN="NOT IN",
|
|
28
|
+
IS="IS",
|
|
29
|
+
DESC="DESC",
|
|
30
|
+
ASC="ASC",
|
|
31
|
+
AVG="AVG",
|
|
32
|
+
AS="AS",
|
|
33
|
+
SUM="SUM",
|
|
34
|
+
DATE_FORMAT="DATE_FORMAT",
|
|
35
|
+
JSON_CONTAINS_OBJECT="JSON_CONTAINS_OBJECT",
|
|
36
|
+
JSON_CONTAINS_ARRAY="JSON_CONTAINS_ARRAY",
|
|
37
|
+
COUNT="COUNT",
|
|
38
|
+
COUNT_WHEN="COUNT_WHEN",
|
|
39
|
+
IS_NOT="IS NOT",
|
|
40
|
+
IS_NOT_NULL="IS NOT NULL",
|
|
41
|
+
IS_NULL="IS NULL",
|
|
42
|
+
LIKE="LIKE",
|
|
43
|
+
NOT_LIKE="NOT LIKE",
|
|
44
|
+
ILIKE="ILIKE",
|
|
45
|
+
BETWEEN="BETWEEN",
|
|
46
|
+
CONTAINS="CONTAINS",
|
|
47
|
+
NOT_BETWEEN="NOT BETWEEN",
|
|
48
|
+
REGEXP="REGEXP",
|
|
49
|
+
IREGEXP="IREGEXP",
|
|
50
|
+
DISTINCT="DISTINCT",
|
|
51
|
+
CONCAT="||",
|
|
52
|
+
BITWISE_NEGATION="~",
|
|
53
|
+
)
|
|
49
54
|
|
|
50
55
|
|
|
51
56
|
class FieldBase:
|
|
@@ -54,6 +59,7 @@ class FieldBase:
|
|
|
54
59
|
if inverse:
|
|
55
60
|
return Expression(rhs, op, self)
|
|
56
61
|
return Expression(self, op, rhs)
|
|
62
|
+
|
|
57
63
|
return inner
|
|
58
64
|
|
|
59
65
|
def __eq__(self, rhs):
|
|
@@ -73,7 +79,7 @@ class FieldBase:
|
|
|
73
79
|
__add__ = _op(OP.ADD) # +
|
|
74
80
|
__radd__ = _op(OP.ADD, True) # +=
|
|
75
81
|
__sub__ = _op(OP.SUB) # -
|
|
76
|
-
|
|
82
|
+
__rsub__ = _op(OP.SUB, True) # -=
|
|
77
83
|
__mul__ = _op(OP.MUL) # *
|
|
78
84
|
__rmul__ = _op(OP.MUL, True) # *=
|
|
79
85
|
__div__ = _op(OP.DIV) # /
|
|
@@ -97,12 +103,24 @@ class FieldBase:
|
|
|
97
103
|
def count(self):
|
|
98
104
|
return Function(self, OP.COUNT)
|
|
99
105
|
|
|
106
|
+
def count_when(self, value):
|
|
107
|
+
return Function(self, OP.COUNT_WHEN, value)
|
|
108
|
+
|
|
100
109
|
def sum(self):
|
|
101
110
|
return Function(self, OP.SUM)
|
|
102
111
|
|
|
103
112
|
def avg(self):
|
|
104
113
|
return Function(self, OP.AVG)
|
|
105
114
|
|
|
115
|
+
def date_format(self, args):
|
|
116
|
+
return Function(self, OP.DATE_FORMAT, args)
|
|
117
|
+
|
|
118
|
+
def json_contains_object(self, key, value):
|
|
119
|
+
return Function(self, OP.JSON_CONTAINS_OBJECT, (key, value))
|
|
120
|
+
|
|
121
|
+
def json_contains_array(self, args):
|
|
122
|
+
return Function(self, OP.JSON_CONTAINS_ARRAY, args)
|
|
123
|
+
|
|
106
124
|
def contains(self, args):
|
|
107
125
|
return Function(self, OP.CONTAINS, args)
|
|
108
126
|
# return AO(f"contains({self.full_name},?)", args)
|
|
@@ -137,16 +155,16 @@ class ExpList(FieldBase):
|
|
|
137
155
|
q = []
|
|
138
156
|
if isinstance(lpt, Exception):
|
|
139
157
|
_q, _p = lpt.sql()
|
|
140
|
-
q = q+_q
|
|
141
|
-
p = p+_p
|
|
158
|
+
q = q + _q
|
|
159
|
+
p = p + _p
|
|
142
160
|
else:
|
|
143
161
|
q.append("?")
|
|
144
162
|
p.append(lpt)
|
|
145
163
|
q.append(f" {self.op} ")
|
|
146
164
|
if isinstance(rpt, Exception):
|
|
147
165
|
_q, _p = lpt.sql()
|
|
148
|
-
q = q+_q
|
|
149
|
-
p = p+_p
|
|
166
|
+
q = q + _q
|
|
167
|
+
p = p + _p
|
|
150
168
|
else:
|
|
151
169
|
q.append("?")
|
|
152
170
|
p.append(lpt)
|
|
@@ -162,8 +180,27 @@ class Function(FieldBase):
|
|
|
162
180
|
self.rpt = rpt
|
|
163
181
|
|
|
164
182
|
def sql(self):
|
|
183
|
+
op = self.op
|
|
184
|
+
col_name = self.col.full_name
|
|
185
|
+
is_field = isinstance(self.rpt, Field)
|
|
165
186
|
# todo:
|
|
166
|
-
|
|
187
|
+
if op == OP.DATE_FORMAT:
|
|
188
|
+
return f"{op}({col_name},?)", [self.rpt]
|
|
189
|
+
elif op == OP.JSON_CONTAINS_OBJECT:
|
|
190
|
+
_k, _v = self.rpt
|
|
191
|
+
key = _k.full_name if isinstance(_k, Field) else f"'{_k}'"
|
|
192
|
+
return f"json_contains({col_name},json_object({key},?))", [_v]
|
|
193
|
+
elif op == OP.JSON_CONTAINS_ARRAY:
|
|
194
|
+
if is_field:
|
|
195
|
+
return (
|
|
196
|
+
f"json_contains({col_name},json_array({self.rpt.full_name}))",
|
|
197
|
+
None,
|
|
198
|
+
)
|
|
199
|
+
return f"json_contains({col_name},json_array(?))", [self.rpt]
|
|
200
|
+
elif op == OP.COUNT:
|
|
201
|
+
return f"COUNT({col_name})", None
|
|
202
|
+
elif op == OP.COUNT_WHEN:
|
|
203
|
+
return f"COUNT(CASE WHEN {col_name} = ? THEN 1 END)", [self.rpt]
|
|
167
204
|
|
|
168
205
|
|
|
169
206
|
class Expression(FieldBase):
|
|
@@ -171,32 +208,75 @@ class Expression(FieldBase):
|
|
|
171
208
|
self.lhs = lhs
|
|
172
209
|
self.op = op
|
|
173
210
|
self.rhs = rhs
|
|
211
|
+
# self.__value = ""
|
|
212
|
+
# if op in "+-*/|&~" and lhs._value:
|
|
213
|
+
# cal = f"{lhs._value}{op}"
|
|
214
|
+
# if isinstance(rhs, FieldBase):
|
|
215
|
+
# self.__value = str(eval(f"{cal}{rhs._value}"))
|
|
216
|
+
# else:
|
|
217
|
+
# self.__value = str(eval(f"{cal}{rhs}"))
|
|
174
218
|
|
|
175
219
|
def sql(self):
|
|
176
220
|
l = self.lhs
|
|
177
221
|
r = self.rhs
|
|
222
|
+
is_fun = isinstance(l, Function)
|
|
178
223
|
p = []
|
|
179
|
-
q = [
|
|
224
|
+
q = []
|
|
225
|
+
if not is_fun:
|
|
226
|
+
q.append("(")
|
|
180
227
|
if isinstance(l, Field):
|
|
181
228
|
q.append(l.full_name)
|
|
182
229
|
elif isinstance(l, Expression):
|
|
183
230
|
_q, _p = l.sql()
|
|
184
231
|
q.append(_q)
|
|
185
|
-
p = p+_p
|
|
232
|
+
p = p + _p
|
|
233
|
+
elif is_fun:
|
|
234
|
+
_q, _p = l.sql()
|
|
235
|
+
q.append(_q)
|
|
236
|
+
if _p:
|
|
237
|
+
p = p + _p
|
|
238
|
+
# return l.sql()
|
|
239
|
+
|
|
186
240
|
q.append(f" {self.op} ")
|
|
187
241
|
|
|
188
242
|
if isinstance(r, Field):
|
|
189
|
-
|
|
243
|
+
if r._value is not None:
|
|
244
|
+
# _value = r._value if r._value is not None else r.full_name
|
|
245
|
+
q.append("?")
|
|
246
|
+
p.append(r._value)
|
|
247
|
+
else:
|
|
248
|
+
q.append(r.full_name)
|
|
249
|
+
# q.append(str(r._value) if r._value is not None else r.full_name)
|
|
190
250
|
elif isinstance(r, FieldBase):
|
|
191
251
|
_q, _p = r.sql()
|
|
192
252
|
q.append(_q)
|
|
193
|
-
p = p+_p
|
|
194
|
-
|
|
195
|
-
|
|
253
|
+
p = p + _p
|
|
254
|
+
elif self.op == OP.AS:
|
|
255
|
+
# is_fun or (isinstance(r, str) and (self.op == OP.AND or self.op == OP.OR)):
|
|
256
|
+
q.append(r)
|
|
257
|
+
elif r is not None:
|
|
258
|
+
q.append("?")
|
|
196
259
|
p.append(r)
|
|
197
|
-
|
|
260
|
+
if not is_fun:
|
|
261
|
+
q.append(")")
|
|
262
|
+
# q.append(")")
|
|
263
|
+
q = [str(num) for num in q]
|
|
264
|
+
# print("".join(q))
|
|
198
265
|
return "".join(q), p
|
|
199
|
-
|
|
266
|
+
|
|
267
|
+
# def __str__(self):
|
|
268
|
+
# return self.__value
|
|
269
|
+
# cal = f"{self.lhs._value}{self.op}"
|
|
270
|
+
# if isinstance(self.rhs, FieldBase):
|
|
271
|
+
# return str(eval(f"{cal}{self.rhs._value}"))
|
|
272
|
+
# return str(eval(f"{cal}{self.rhs}"))
|
|
273
|
+
|
|
274
|
+
def __repr__(self):
|
|
275
|
+
# return self.__value
|
|
276
|
+
cal = f"{self.lhs.value}{self.op}"
|
|
277
|
+
if isinstance(self.rhs, FieldBase):
|
|
278
|
+
return str(eval(f"{cal}{self.rhs.value}"))
|
|
279
|
+
return str(eval(f"{cal}{self.rhs}"))
|
|
200
280
|
|
|
201
281
|
|
|
202
282
|
class Field(FieldBase):
|
|
@@ -237,11 +317,15 @@ class Field(FieldBase):
|
|
|
237
317
|
# "<%s, %s:%s>" % (self.__class__.__name__, self.column_type, self.name)
|
|
238
318
|
# return self.name
|
|
239
319
|
|
|
320
|
+
def __repr__(self):
|
|
321
|
+
return str(self._value)
|
|
322
|
+
|
|
240
323
|
# def __getattr__(self, item):
|
|
241
324
|
# return self[item]
|
|
242
|
-
|
|
243
|
-
def
|
|
244
|
-
self._value = value
|
|
245
|
-
|
|
246
|
-
def get_value(self):
|
|
325
|
+
@property
|
|
326
|
+
def value(self):
|
|
247
327
|
return self._value
|
|
328
|
+
|
|
329
|
+
@value.setter
|
|
330
|
+
def value(self, value):
|
|
331
|
+
self._value = value
|