velocity-python 0.0.27__py3-none-any.whl → 0.0.29__py3-none-any.whl
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.
Potentially problematic release.
This version of velocity-python might be problematic. Click here for more details.
- velocity/__init__.py +1 -1
- velocity/db/core/decorators.py +60 -41
- velocity/db/core/row.py +33 -21
- velocity/db/core/table.py +318 -169
- velocity/db/core/transaction.py +54 -31
- velocity/db/servers/postgres.py +810 -689
- {velocity_python-0.0.27.dist-info → velocity_python-0.0.29.dist-info}/METADATA +1 -1
- {velocity_python-0.0.27.dist-info → velocity_python-0.0.29.dist-info}/RECORD +11 -12
- velocity/db/servers/sql.py +0 -558
- {velocity_python-0.0.27.dist-info → velocity_python-0.0.29.dist-info}/LICENSE +0 -0
- {velocity_python-0.0.27.dist-info → velocity_python-0.0.29.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.27.dist-info → velocity_python-0.0.29.dist-info}/top_level.txt +0 -0
velocity/db/servers/postgres.py
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import psycopg2
|
|
2
2
|
import re
|
|
3
|
+
import os
|
|
3
4
|
import hashlib
|
|
4
5
|
import decimal
|
|
5
6
|
import datetime
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
7
|
+
from ..core import exceptions
|
|
8
|
+
from ..core import engine
|
|
9
|
+
from ..core.table import Query
|
|
10
|
+
|
|
11
|
+
system_fields = [
|
|
12
|
+
"sys_id",
|
|
13
|
+
"sys_created",
|
|
14
|
+
"sys_modified",
|
|
15
|
+
"sys_modified_by",
|
|
16
|
+
"sys_dirty",
|
|
17
|
+
"sys_table",
|
|
18
|
+
"description",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
default_config = {
|
|
22
|
+
"database": os.environ["DBDatabase"],
|
|
23
|
+
"host": os.environ["DBHost"],
|
|
24
|
+
"port": os.environ["DBPort"],
|
|
25
|
+
"user": os.environ["DBUser"],
|
|
26
|
+
"password": os.environ["DBPassword"],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def initialize(config=None, **kwargs):
|
|
31
|
+
if not config:
|
|
32
|
+
config = default_config.copy()
|
|
33
|
+
config.update(kwargs)
|
|
34
|
+
return engine.Engine(psycopg2, config, SQL)
|
|
35
|
+
|
|
31
36
|
|
|
32
37
|
def make_where(where, sql, vals, is_join=False):
|
|
33
38
|
if not where:
|
|
34
39
|
return
|
|
35
|
-
sql.append(
|
|
40
|
+
sql.append("WHERE")
|
|
36
41
|
if isinstance(where, str):
|
|
37
42
|
sql.append(where)
|
|
38
43
|
return
|
|
@@ -40,144 +45,167 @@ def make_where(where, sql, vals, is_join=False):
|
|
|
40
45
|
where = list(where.items())
|
|
41
46
|
if not isinstance(where, list):
|
|
42
47
|
raise Exception("Parameter `where` is not a valid datatype.")
|
|
43
|
-
alias =
|
|
48
|
+
alias = "A"
|
|
44
49
|
if is_join and isinstance(is_join, str):
|
|
45
50
|
alias = is_join
|
|
46
|
-
connect =
|
|
51
|
+
connect = ""
|
|
47
52
|
for key, val in where:
|
|
48
|
-
if connect:
|
|
53
|
+
if connect:
|
|
54
|
+
sql.append(connect)
|
|
49
55
|
if is_join:
|
|
50
|
-
if
|
|
51
|
-
key = alias +
|
|
56
|
+
if "." not in key:
|
|
57
|
+
key = alias + "." + quote(key.lower())
|
|
52
58
|
if val == None:
|
|
53
|
-
if
|
|
54
|
-
key = key.replace(
|
|
55
|
-
sql.append(
|
|
59
|
+
if "!" in key:
|
|
60
|
+
key = key.replace("!", "")
|
|
61
|
+
sql.append("{} is not NULL".format(key))
|
|
56
62
|
else:
|
|
57
|
-
sql.append(
|
|
58
|
-
elif isinstance(val, (list, tuple)) and
|
|
59
|
-
if
|
|
60
|
-
key = key.replace(
|
|
61
|
-
sql.append(
|
|
63
|
+
sql.append("{} is NULL".format(key))
|
|
64
|
+
elif isinstance(val, (list, tuple)) and "><" not in key:
|
|
65
|
+
if "!" in key:
|
|
66
|
+
key = key.replace("!", "")
|
|
67
|
+
sql.append("{} not in %s".format(key))
|
|
62
68
|
vals.append(tuple(val))
|
|
63
69
|
else:
|
|
64
|
-
sql.append(
|
|
70
|
+
sql.append("{} in %s".format(key))
|
|
65
71
|
vals.append(tuple(val))
|
|
66
72
|
elif isinstance(val, Query):
|
|
67
|
-
sql.append(
|
|
73
|
+
sql.append("{} in ({})".format(key, val))
|
|
68
74
|
else:
|
|
69
75
|
case = None
|
|
70
|
-
if
|
|
71
|
-
key = key.replace(
|
|
72
|
-
op =
|
|
73
|
-
elif
|
|
74
|
-
key = key.replace(
|
|
75
|
-
op =
|
|
76
|
-
elif
|
|
77
|
-
key = key.replace(
|
|
78
|
-
op =
|
|
79
|
-
elif
|
|
80
|
-
key = key.replace(
|
|
81
|
-
op =
|
|
82
|
-
elif
|
|
83
|
-
key = key.replace(
|
|
84
|
-
op =
|
|
85
|
-
elif
|
|
86
|
-
key = key.replace(
|
|
87
|
-
op =
|
|
88
|
-
elif
|
|
89
|
-
key = key.replace(
|
|
90
|
-
op =
|
|
91
|
-
elif
|
|
92
|
-
key = key.replace(
|
|
93
|
-
op =
|
|
94
|
-
elif
|
|
95
|
-
key = key.replace(
|
|
96
|
-
op =
|
|
97
|
-
elif
|
|
98
|
-
key = key.replace(
|
|
99
|
-
op =
|
|
100
|
-
elif
|
|
101
|
-
key = key.replace(
|
|
102
|
-
op =
|
|
103
|
-
elif
|
|
104
|
-
key = key.replace(
|
|
105
|
-
op =
|
|
106
|
-
elif
|
|
107
|
-
key = key.replace(
|
|
108
|
-
op =
|
|
109
|
-
elif
|
|
110
|
-
key = key.replace(
|
|
111
|
-
op =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
76
|
+
if "<>" in key:
|
|
77
|
+
key = key.replace("<>", "")
|
|
78
|
+
op = "<>"
|
|
79
|
+
elif "!=" in key:
|
|
80
|
+
key = key.replace("!=", "")
|
|
81
|
+
op = "<>"
|
|
82
|
+
elif "!><" in key:
|
|
83
|
+
key = key.replace("!><", "")
|
|
84
|
+
op = "not between"
|
|
85
|
+
elif "><" in key:
|
|
86
|
+
key = key.replace("><", "")
|
|
87
|
+
op = "between"
|
|
88
|
+
elif "!%" in key:
|
|
89
|
+
key = key.replace("!%", "")
|
|
90
|
+
op = "not like"
|
|
91
|
+
elif "%%" in key:
|
|
92
|
+
key = key.replace("%%", "")
|
|
93
|
+
op = "%"
|
|
94
|
+
elif "%>" in key:
|
|
95
|
+
key = key.replace("%>", "")
|
|
96
|
+
op = "%>"
|
|
97
|
+
elif "<%" in key:
|
|
98
|
+
key = key.replace("<%", "")
|
|
99
|
+
op = "<%"
|
|
100
|
+
elif "==" in key:
|
|
101
|
+
key = key.replace("==", "")
|
|
102
|
+
op = "="
|
|
103
|
+
elif "<=" in key:
|
|
104
|
+
key = key.replace("<=", "")
|
|
105
|
+
op = "<="
|
|
106
|
+
elif ">=" in key:
|
|
107
|
+
key = key.replace(">=", "")
|
|
108
|
+
op = ">="
|
|
109
|
+
elif "<" in key:
|
|
110
|
+
key = key.replace("<", "")
|
|
111
|
+
op = "<"
|
|
112
|
+
elif ">" in key:
|
|
113
|
+
key = key.replace(">", "")
|
|
114
|
+
op = ">"
|
|
115
|
+
elif "%" in key:
|
|
116
|
+
key = key.replace("%", "")
|
|
117
|
+
op = "ilike"
|
|
118
|
+
elif "!~*" in key:
|
|
119
|
+
# String does not match regular expression, case insensitively
|
|
120
|
+
# 'thomas' !~* 'T.*ma' → False
|
|
121
|
+
key = key.replace("!~*", "")
|
|
122
|
+
op = "!~*"
|
|
123
|
+
elif "~*" in key:
|
|
124
|
+
# String matches regular expression, case insensitively
|
|
125
|
+
# 'thomas' ~* 'T.*ma' → True
|
|
126
|
+
key = key.replace("~*", "")
|
|
127
|
+
op = "~*"
|
|
128
|
+
elif "!~" in key:
|
|
129
|
+
# String does not match regular expression, case sensitively
|
|
130
|
+
# 'thomas' !~ 't.*max' → True
|
|
131
|
+
key = key.replace("!~", "")
|
|
132
|
+
op = "!~"
|
|
133
|
+
elif "~" in key:
|
|
134
|
+
# String matches regular expression, case sensitively
|
|
135
|
+
# 'thomas' ~ 't.*ma' → True
|
|
136
|
+
key = key.replace("~", "")
|
|
137
|
+
op = "~"
|
|
138
|
+
elif "!" in key:
|
|
139
|
+
key = key.replace("!", "")
|
|
140
|
+
op = "<>"
|
|
141
|
+
elif "=" in key:
|
|
142
|
+
key = key.replace("=", "")
|
|
143
|
+
op = "="
|
|
119
144
|
else:
|
|
120
|
-
op =
|
|
121
|
-
if
|
|
122
|
-
key = key.replace(
|
|
123
|
-
op =
|
|
124
|
-
case =
|
|
125
|
-
if isinstance(val, str) and val[:2] ==
|
|
126
|
-
sql.append(
|
|
127
|
-
elif op in [
|
|
128
|
-
sql.append(
|
|
145
|
+
op = "="
|
|
146
|
+
if "#" in key:
|
|
147
|
+
key = key.replace("#", "")
|
|
148
|
+
op = "="
|
|
149
|
+
case = "lower"
|
|
150
|
+
if isinstance(val, str) and val[:2] == "@@" and val[2:]:
|
|
151
|
+
sql.append("{} {} {}".format(key, op, val[2:]))
|
|
152
|
+
elif op in ["between", "not between"]:
|
|
153
|
+
sql.append("{} {} %s and %s".format(key, op))
|
|
129
154
|
vals.extend(val)
|
|
130
155
|
else:
|
|
131
156
|
if case:
|
|
132
|
-
sql.append(
|
|
157
|
+
sql.append("{2}({0}) {1} {2}(%s)".format(key, op, case))
|
|
133
158
|
else:
|
|
134
|
-
sql.append(
|
|
159
|
+
sql.append("{0} {1} %s".format(key, op))
|
|
135
160
|
vals.append(val)
|
|
136
|
-
connect =
|
|
161
|
+
connect = "AND"
|
|
137
162
|
|
|
138
163
|
|
|
139
164
|
def quote(data):
|
|
140
165
|
if isinstance(data, list):
|
|
141
166
|
new = []
|
|
142
167
|
for item in data:
|
|
143
|
-
|
|
168
|
+
if "@@" in item:
|
|
169
|
+
new.append(item[2:])
|
|
170
|
+
else:
|
|
171
|
+
new.append(quote(item))
|
|
144
172
|
return new
|
|
145
173
|
else:
|
|
146
|
-
parts = data.split(
|
|
174
|
+
parts = data.split(".")
|
|
147
175
|
new = []
|
|
148
176
|
for part in parts:
|
|
149
177
|
if '"' in part:
|
|
150
178
|
new.append(part)
|
|
151
179
|
elif part.upper() in reserved_words:
|
|
152
180
|
new.append('"' + part + '"')
|
|
153
|
-
elif re.findall(
|
|
181
|
+
elif re.findall("[/]", part):
|
|
154
182
|
new.append('"' + part + '"')
|
|
155
183
|
else:
|
|
156
184
|
new.append(part)
|
|
157
|
-
return
|
|
185
|
+
return ".".join(new)
|
|
158
186
|
|
|
159
187
|
|
|
160
188
|
class SQL(object):
|
|
161
189
|
server = "PostGreSQL"
|
|
162
|
-
type_column_identifier =
|
|
163
|
-
is_nullable =
|
|
190
|
+
type_column_identifier = "data_type"
|
|
191
|
+
is_nullable = "is_nullable"
|
|
164
192
|
|
|
165
|
-
default_schema =
|
|
193
|
+
default_schema = "public"
|
|
166
194
|
|
|
167
|
-
ApplicationErrorCodes = [
|
|
195
|
+
ApplicationErrorCodes = ["22P02", "42883"]
|
|
168
196
|
|
|
169
197
|
DatabaseMissingErrorCodes = []
|
|
170
|
-
TableMissingErrorCodes = [
|
|
171
|
-
ColumnMissingErrorCodes = [
|
|
172
|
-
ForeignKeyMissingErrorCodes = [
|
|
198
|
+
TableMissingErrorCodes = ["42P01"]
|
|
199
|
+
ColumnMissingErrorCodes = ["42703"]
|
|
200
|
+
ForeignKeyMissingErrorCodes = ["42704"]
|
|
173
201
|
|
|
174
|
-
ConnectionErrorCodes = [
|
|
202
|
+
ConnectionErrorCodes = ["08001", "08S01"]
|
|
175
203
|
DuplicateKeyErrorCodes = [] # Handled in regex check.
|
|
176
204
|
RetryTransactionCodes = []
|
|
177
205
|
TruncationErrorCodes = []
|
|
178
|
-
LockTimeoutErrorCodes = [
|
|
179
|
-
DatabaseObjectExistsErrorCodes = [
|
|
180
|
-
DataIntegrityErrorCodes = [
|
|
206
|
+
LockTimeoutErrorCodes = ["55P03"]
|
|
207
|
+
DatabaseObjectExistsErrorCodes = ["42710", "42P07", "42P04"]
|
|
208
|
+
DataIntegrityErrorCodes = ["23503"]
|
|
181
209
|
|
|
182
210
|
@classmethod
|
|
183
211
|
def version(cls):
|
|
@@ -193,232 +221,284 @@ class SQL(object):
|
|
|
193
221
|
|
|
194
222
|
@classmethod
|
|
195
223
|
def databases(cls):
|
|
196
|
-
return "select datname from pg_database where datistemplate = false", tuple(
|
|
197
|
-
)
|
|
224
|
+
return "select datname from pg_database where datistemplate = false", tuple()
|
|
198
225
|
|
|
199
226
|
@classmethod
|
|
200
227
|
def schemas(cls):
|
|
201
|
-
return
|
|
228
|
+
return "select schema_name from information_schema.schemata", tuple()
|
|
202
229
|
|
|
203
230
|
@classmethod
|
|
204
231
|
def current_schema(cls):
|
|
205
|
-
return
|
|
232
|
+
return "select current_schema", tuple()
|
|
206
233
|
|
|
207
234
|
@classmethod
|
|
208
235
|
def current_database(cls):
|
|
209
|
-
return
|
|
236
|
+
return "select current_database()", tuple()
|
|
210
237
|
|
|
211
238
|
@classmethod
|
|
212
239
|
def tables(cls, system=False):
|
|
213
240
|
if system:
|
|
214
|
-
return
|
|
241
|
+
return (
|
|
242
|
+
"select table_schema,table_name from information_schema.tables where table_type = 'BASE TABLE' order by table_schema,table_name",
|
|
243
|
+
tuple(),
|
|
215
244
|
)
|
|
216
245
|
else:
|
|
217
|
-
return
|
|
246
|
+
return (
|
|
247
|
+
"select table_schema, table_name from information_schema.tables where table_type = 'BASE TABLE' and table_schema NOT IN ('pg_catalog', 'information_schema')",
|
|
248
|
+
tuple(),
|
|
218
249
|
)
|
|
219
250
|
|
|
220
251
|
@classmethod
|
|
221
252
|
def views(cls, system=False):
|
|
222
253
|
if system:
|
|
223
|
-
return
|
|
254
|
+
return (
|
|
255
|
+
"select table_schema, table_name from information_schema.views order by table_schema,table_name",
|
|
256
|
+
tuple(),
|
|
224
257
|
)
|
|
225
258
|
else:
|
|
226
|
-
return
|
|
259
|
+
return (
|
|
260
|
+
"select table_schema, table_name from information_schema.views where table_schema = any (current_schemas(false)) order by table_schema,table_name",
|
|
261
|
+
tuple(),
|
|
227
262
|
)
|
|
228
263
|
|
|
229
264
|
@classmethod
|
|
230
265
|
def __has_pointer(cls, columns):
|
|
231
|
-
if columns:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
266
|
+
if isinstance(columns, str):
|
|
267
|
+
columns = columns.split(",")
|
|
268
|
+
if isinstance(columns, list):
|
|
269
|
+
for column in columns:
|
|
270
|
+
if "@@" in column:
|
|
271
|
+
continue
|
|
272
|
+
if ">" in column:
|
|
273
|
+
return True
|
|
236
274
|
return False
|
|
237
275
|
|
|
238
276
|
@classmethod
|
|
239
|
-
def select(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
277
|
+
def select(
|
|
278
|
+
cls,
|
|
279
|
+
columns=None,
|
|
280
|
+
table=None,
|
|
281
|
+
where=None,
|
|
282
|
+
orderby=None,
|
|
283
|
+
groupby=None,
|
|
284
|
+
having=None,
|
|
285
|
+
start=None,
|
|
286
|
+
qty=None,
|
|
287
|
+
tbl=None,
|
|
288
|
+
lock=None,
|
|
289
|
+
skip_locked=None,
|
|
290
|
+
):
|
|
249
291
|
if not table:
|
|
250
292
|
raise Exception("Table name required")
|
|
251
293
|
is_join = False
|
|
252
294
|
|
|
253
|
-
if isinstance(columns,str)
|
|
254
|
-
and 'distinct' in columns.lower():
|
|
295
|
+
if isinstance(columns, str) and "distinct" in columns.lower():
|
|
255
296
|
sql = [
|
|
256
|
-
|
|
297
|
+
"SELECT",
|
|
257
298
|
columns,
|
|
258
|
-
|
|
299
|
+
"FROM",
|
|
259
300
|
quote(table),
|
|
260
301
|
]
|
|
261
302
|
elif cls.__has_pointer(columns):
|
|
262
303
|
is_join = True
|
|
263
304
|
if isinstance(columns, str):
|
|
264
|
-
columns = columns.split(
|
|
305
|
+
columns = columns.split(",")
|
|
265
306
|
letter = 65
|
|
266
307
|
tables = {table: chr(letter)}
|
|
267
308
|
letter += 1
|
|
268
309
|
__select = []
|
|
269
|
-
__from = [
|
|
310
|
+
__from = ["{} AS {}".format(quote(table), tables.get(table))]
|
|
270
311
|
__left_join = []
|
|
271
312
|
|
|
272
313
|
for column in columns:
|
|
273
|
-
if
|
|
274
|
-
|
|
314
|
+
if "@@" in column:
|
|
315
|
+
__select.append(column[2:])
|
|
316
|
+
elif ">" in column:
|
|
317
|
+
parts = column.split(">")
|
|
275
318
|
foreign = tbl.foreign_key_info(parts[0])
|
|
276
319
|
if not foreign:
|
|
277
320
|
raise exceptions.DbApplicationError("Foreign key not defined")
|
|
278
|
-
ref_table = foreign[
|
|
279
|
-
ref_schema = foreign[
|
|
280
|
-
ref_column = foreign[
|
|
321
|
+
ref_table = foreign["referenced_table_name"]
|
|
322
|
+
ref_schema = foreign["referenced_table_schema"]
|
|
323
|
+
ref_column = foreign["referenced_column_name"]
|
|
281
324
|
lookup = "{}:{}".format(ref_table, parts[0])
|
|
282
325
|
if lookup in tables:
|
|
283
|
-
__select.append(
|
|
284
|
-
|
|
326
|
+
__select.append(
|
|
327
|
+
'{}."{}" as "{}"'.format(
|
|
328
|
+
tables.get(lookup), parts[1], "_".join(parts)
|
|
329
|
+
)
|
|
330
|
+
)
|
|
285
331
|
else:
|
|
286
332
|
tables[lookup] = chr(letter)
|
|
287
333
|
letter += 1
|
|
288
|
-
__select.append(
|
|
289
|
-
|
|
334
|
+
__select.append(
|
|
335
|
+
'{}."{}" as "{}"'.format(
|
|
336
|
+
tables.get(lookup), parts[1], "_".join(parts)
|
|
337
|
+
)
|
|
338
|
+
)
|
|
290
339
|
__left_join.append(
|
|
291
340
|
'LEFT OUTER JOIN "{}"."{}" AS {}'.format(
|
|
292
|
-
ref_schema, ref_table, tables.get(lookup)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
341
|
+
ref_schema, ref_table, tables.get(lookup)
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
__left_join.append(
|
|
345
|
+
'ON {}."{}" = {}."{}"'.format(
|
|
346
|
+
tables.get(table),
|
|
347
|
+
parts[0],
|
|
348
|
+
tables.get(lookup),
|
|
349
|
+
ref_column,
|
|
350
|
+
)
|
|
351
|
+
)
|
|
296
352
|
if orderby and column in orderby:
|
|
297
353
|
orderby = orderby.replace(
|
|
298
|
-
column, "{}.{}".format(tables.get(lookup),
|
|
299
|
-
|
|
354
|
+
column, "{}.{}".format(tables.get(lookup), parts[1])
|
|
355
|
+
)
|
|
300
356
|
|
|
301
357
|
else:
|
|
302
|
-
if
|
|
358
|
+
if "(" in column:
|
|
303
359
|
__select.append(column)
|
|
304
360
|
else:
|
|
305
|
-
__select.append("{}.{}".format(tables.get(table),
|
|
306
|
-
|
|
307
|
-
sql
|
|
308
|
-
sql.append(
|
|
309
|
-
sql.append('FROM')
|
|
361
|
+
__select.append("{}.{}".format(tables.get(table), column))
|
|
362
|
+
sql = ["SELECT"]
|
|
363
|
+
sql.append(",".join(__select))
|
|
364
|
+
sql.append("FROM")
|
|
310
365
|
sql.extend(__from)
|
|
311
366
|
sql.extend(__left_join)
|
|
312
367
|
else:
|
|
313
368
|
if columns:
|
|
314
369
|
if isinstance(columns, str):
|
|
315
|
-
columns = columns.split(
|
|
370
|
+
columns = columns.split(",")
|
|
316
371
|
if isinstance(columns, list):
|
|
317
372
|
columns = quote(columns)
|
|
318
|
-
columns =
|
|
373
|
+
columns = ",".join(columns)
|
|
319
374
|
else:
|
|
320
|
-
columns =
|
|
375
|
+
columns = "*"
|
|
321
376
|
sql = [
|
|
322
|
-
|
|
377
|
+
"SELECT",
|
|
323
378
|
columns,
|
|
324
|
-
|
|
379
|
+
"FROM",
|
|
325
380
|
quote(table),
|
|
326
381
|
]
|
|
327
382
|
vals = []
|
|
328
383
|
make_where(where, sql, vals, is_join)
|
|
329
384
|
if groupby:
|
|
330
|
-
sql.append(
|
|
385
|
+
sql.append("GROUP BY")
|
|
331
386
|
if isinstance(groupby, (list, tuple)):
|
|
332
|
-
groupby =
|
|
387
|
+
groupby = ",".join(groupby)
|
|
333
388
|
sql.append(groupby)
|
|
334
389
|
if having:
|
|
335
|
-
sql.append(
|
|
390
|
+
sql.append("HAVING")
|
|
336
391
|
if isinstance(having, (list, tuple)):
|
|
337
|
-
having =
|
|
392
|
+
having = ",".join(having)
|
|
338
393
|
sql.append(having)
|
|
339
394
|
if orderby:
|
|
340
|
-
sql.append(
|
|
395
|
+
sql.append("ORDER BY")
|
|
341
396
|
if isinstance(orderby, (list, tuple)):
|
|
342
|
-
orderby =
|
|
397
|
+
orderby = ",".join(orderby)
|
|
343
398
|
sql.append(orderby)
|
|
344
399
|
if start and qty:
|
|
345
|
-
sql.append(
|
|
346
|
-
start, qty))
|
|
400
|
+
sql.append("OFFSET {} ROWS FETCH NEXT {} ROWS ONLY".format(start, qty))
|
|
347
401
|
elif start:
|
|
348
|
-
sql.append(
|
|
402
|
+
sql.append("OFFSET {} ROWS".format(start))
|
|
349
403
|
elif qty:
|
|
350
|
-
sql.append(
|
|
351
|
-
|
|
404
|
+
sql.append("FETCH NEXT {} ROWS ONLY".format(qty))
|
|
405
|
+
if lock or skip_locked:
|
|
406
|
+
sql.append("FOR UPDATE")
|
|
407
|
+
if skip_locked:
|
|
408
|
+
sql.append("SKIP LOCKED")
|
|
409
|
+
sql = " ".join(sql)
|
|
352
410
|
return sql, tuple(vals)
|
|
353
411
|
|
|
354
412
|
@classmethod
|
|
355
413
|
def create_database(cls, name):
|
|
356
|
-
return
|
|
414
|
+
return "create database " + name, tuple()
|
|
357
415
|
|
|
358
416
|
@classmethod
|
|
359
417
|
def last_id(cls, table):
|
|
360
|
-
return "SELECT CURRVAL(PG_GET_SERIAL_SEQUENCE(%s, 'sys_id'))", tuple(
|
|
361
|
-
|
|
418
|
+
return "SELECT CURRVAL(PG_GET_SERIAL_SEQUENCE(%s, 'sys_id'))", tuple([table])
|
|
419
|
+
|
|
420
|
+
@classmethod
|
|
421
|
+
def current_id(cls, table):
|
|
422
|
+
return (
|
|
423
|
+
"SELECT pg_sequence_last_value(PG_GET_SERIAL_SEQUENCE(%s, 'sys_id'))",
|
|
424
|
+
tuple([table]),
|
|
425
|
+
)
|
|
362
426
|
|
|
363
427
|
@classmethod
|
|
364
428
|
def set_id(cls, table, start):
|
|
365
429
|
return "SELECT SETVAL(PG_GET_SERIAL_SEQUENCE(%s, 'sys_id'), %s)", tuple(
|
|
366
|
-
[table, start]
|
|
430
|
+
[table, start]
|
|
431
|
+
)
|
|
367
432
|
|
|
368
433
|
@classmethod
|
|
369
434
|
def drop_database(cls, name):
|
|
370
|
-
return
|
|
435
|
+
return "drop database if exists " + name, tuple()
|
|
371
436
|
|
|
372
437
|
@classmethod
|
|
373
438
|
def create_table(cls, name, columns={}, drop=False):
|
|
374
|
-
if
|
|
439
|
+
if "." in name:
|
|
375
440
|
fqtn = name
|
|
376
441
|
else:
|
|
377
|
-
fqtn =
|
|
378
|
-
schema, table = fqtn.split(
|
|
379
|
-
name = fqtn.replace(
|
|
380
|
-
trigger =
|
|
442
|
+
fqtn = "public." + name
|
|
443
|
+
schema, table = fqtn.split(".")
|
|
444
|
+
name = fqtn.replace(".", "_")
|
|
445
|
+
trigger = "".format(name)
|
|
381
446
|
sql = []
|
|
382
447
|
if drop:
|
|
383
448
|
sql.append(cls.drop_table(fqtn))
|
|
384
|
-
sql.append(
|
|
449
|
+
sql.append(
|
|
450
|
+
"""
|
|
385
451
|
CREATE TABLE {0} (
|
|
386
|
-
sys_id
|
|
452
|
+
sys_id BIGSERIAL PRIMARY KEY,
|
|
387
453
|
sys_modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
388
|
-
sys_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
454
|
+
sys_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
455
|
+
sys_modified_by TEXT,
|
|
456
|
+
sys_dirty BOOLEAN NOT NULL DEFAULT FALSE,
|
|
457
|
+
sys_table TEXT,
|
|
458
|
+
description TEXT
|
|
389
459
|
);
|
|
390
460
|
|
|
391
461
|
SELECT SETVAL(PG_GET_SERIAL_SEQUENCE('{0}', 'sys_id'),1000,TRUE);
|
|
392
462
|
|
|
393
|
-
CREATE OR REPLACE FUNCTION {1}.
|
|
463
|
+
CREATE OR REPLACE FUNCTION {1}.on_sys_modified()
|
|
394
464
|
RETURNS TRIGGER AS
|
|
395
465
|
$BODY$
|
|
396
466
|
BEGIN
|
|
397
|
-
-- update sys_modified on each update.
|
|
467
|
+
-- update sys_modified on each insert/update.
|
|
398
468
|
NEW.sys_modified := now();
|
|
399
|
-
|
|
400
|
-
|
|
469
|
+
if (TG_OP = 'INSERT') THEN
|
|
470
|
+
NEW.sys_created :=now();
|
|
471
|
+
ELSEIF (TG_OP = 'UDPATE') THEN
|
|
472
|
+
-- Do not allow sys_created to be modified.
|
|
473
|
+
NEW.sys_created := OLD.sys_created;
|
|
474
|
+
END IF;
|
|
475
|
+
-- Insert table name to row
|
|
476
|
+
NEW.sys_table := TG_TABLE_NAME;
|
|
401
477
|
RETURN NEW;
|
|
402
478
|
END;
|
|
403
479
|
$BODY$
|
|
404
480
|
LANGUAGE plpgsql VOLATILE
|
|
405
481
|
COST 100;
|
|
406
|
-
--ALTER FUNCTION {1}.update_sys_modified()
|
|
407
|
-
--OWNER TO postgres;
|
|
408
482
|
|
|
409
483
|
CREATE TRIGGER on_update_row_{3}
|
|
410
|
-
BEFORE UPDATE ON {0}
|
|
411
|
-
FOR EACH ROW EXECUTE PROCEDURE {1}.
|
|
484
|
+
BEFORE INSERT OR UPDATE ON {0}
|
|
485
|
+
FOR EACH ROW EXECUTE PROCEDURE {1}.on_sys_modified();
|
|
412
486
|
|
|
413
|
-
""".format(
|
|
487
|
+
""".format(
|
|
488
|
+
fqtn, schema, table, fqtn.replace(".", "_")
|
|
489
|
+
)
|
|
490
|
+
)
|
|
414
491
|
|
|
415
492
|
for key, val in columns.items():
|
|
416
|
-
key = re.sub(
|
|
417
|
-
if key in
|
|
493
|
+
key = re.sub("<>!=%", "", key.lower())
|
|
494
|
+
if key in system_fields:
|
|
418
495
|
continue
|
|
419
|
-
sql.append(
|
|
420
|
-
|
|
421
|
-
|
|
496
|
+
sql.append(
|
|
497
|
+
"ALTER TABLE {} ADD COLUMN {} {};".format(
|
|
498
|
+
quote(fqtn), quote(key), cls.get_type(val)
|
|
499
|
+
)
|
|
500
|
+
)
|
|
501
|
+
return "\n\t".join(sql), tuple()
|
|
422
502
|
|
|
423
503
|
@classmethod
|
|
424
504
|
def drop_table(cls, name):
|
|
@@ -427,55 +507,67 @@ class SQL(object):
|
|
|
427
507
|
@classmethod
|
|
428
508
|
def drop_column(cls, table, name, cascade=True):
|
|
429
509
|
if cascade:
|
|
430
|
-
return
|
|
431
|
-
quote(table), quote(name)),
|
|
510
|
+
return (
|
|
511
|
+
"ALTER TABLE %s DROP COLUMN %s CASCADE" % (quote(table), quote(name)),
|
|
512
|
+
tuple(),
|
|
513
|
+
)
|
|
432
514
|
else:
|
|
433
|
-
return
|
|
434
|
-
|
|
515
|
+
return (
|
|
516
|
+
"ALTER TABLE %s DROP COLUMN %s " % (quote(table), quote(name)),
|
|
517
|
+
tuple(),
|
|
518
|
+
)
|
|
435
519
|
|
|
436
520
|
@classmethod
|
|
437
521
|
def columns(cls, name):
|
|
438
|
-
if
|
|
522
|
+
if "." in name:
|
|
439
523
|
return """
|
|
440
524
|
select column_name
|
|
441
525
|
from information_schema.columns
|
|
442
526
|
where UPPER(table_schema) = UPPER(%s)
|
|
443
527
|
and UPPER(table_name) = UPPER(%s)
|
|
444
|
-
""", tuple(
|
|
528
|
+
""", tuple(
|
|
529
|
+
name.split(".")
|
|
530
|
+
)
|
|
445
531
|
else:
|
|
446
532
|
return """
|
|
447
533
|
select column_name
|
|
448
534
|
from information_schema.columns
|
|
449
535
|
where UPPER(table_name) = UPPER(%s)
|
|
450
|
-
""", tuple(
|
|
451
|
-
|
|
452
|
-
|
|
536
|
+
""", tuple(
|
|
537
|
+
[
|
|
538
|
+
name,
|
|
539
|
+
]
|
|
540
|
+
)
|
|
453
541
|
|
|
454
542
|
@classmethod
|
|
455
543
|
def column_info(cls, table, name):
|
|
456
|
-
params = table.split(
|
|
544
|
+
params = table.split(".")
|
|
457
545
|
params.append(name)
|
|
458
|
-
if
|
|
546
|
+
if "." in table:
|
|
459
547
|
return """
|
|
460
548
|
select *
|
|
461
549
|
from information_schema.columns
|
|
462
550
|
where UPPER(table_schema ) = UPPER(%s)
|
|
463
551
|
and UPPER(table_name) = UPPER(%s)
|
|
464
552
|
and UPPER(column_name) = UPPER(%s)
|
|
465
|
-
""", tuple(
|
|
553
|
+
""", tuple(
|
|
554
|
+
params
|
|
555
|
+
)
|
|
466
556
|
else:
|
|
467
557
|
return """
|
|
468
558
|
select *
|
|
469
559
|
from information_schema.columns
|
|
470
560
|
where UPPER(table_name) = UPPER(%s)
|
|
471
561
|
and UPPER(column_name) = UPPER(%s)
|
|
472
|
-
""", tuple(
|
|
562
|
+
""", tuple(
|
|
563
|
+
params
|
|
564
|
+
)
|
|
473
565
|
|
|
474
566
|
@classmethod
|
|
475
567
|
def primary_keys(cls, table):
|
|
476
|
-
params = table.split(
|
|
568
|
+
params = table.split(".")
|
|
477
569
|
params.reverse()
|
|
478
|
-
if
|
|
570
|
+
if "." in table:
|
|
479
571
|
return """
|
|
480
572
|
SELECT
|
|
481
573
|
pg_attribute.attname
|
|
@@ -488,7 +580,9 @@ class SQL(object):
|
|
|
488
580
|
pg_attribute.attrelid = pg_class.oid AND
|
|
489
581
|
pg_attribute.attnum = any(pg_index.indkey)
|
|
490
582
|
AND indisprimary
|
|
491
|
-
""", tuple(
|
|
583
|
+
""", tuple(
|
|
584
|
+
params
|
|
585
|
+
)
|
|
492
586
|
else:
|
|
493
587
|
return """
|
|
494
588
|
SELECT
|
|
@@ -501,12 +595,14 @@ class SQL(object):
|
|
|
501
595
|
pg_attribute.attrelid = pg_class.oid AND
|
|
502
596
|
pg_attribute.attnum = any(pg_index.indkey)
|
|
503
597
|
AND indisprimary
|
|
504
|
-
""", tuple(
|
|
598
|
+
""", tuple(
|
|
599
|
+
params
|
|
600
|
+
)
|
|
505
601
|
|
|
506
602
|
@classmethod
|
|
507
603
|
def foreign_key_info(cls, table=None, column=None, schema=None):
|
|
508
|
-
if
|
|
509
|
-
schema, table = table.split(
|
|
604
|
+
if "." in table:
|
|
605
|
+
schema, table = table.split(".")
|
|
510
606
|
|
|
511
607
|
sql = [
|
|
512
608
|
"""
|
|
@@ -542,23 +638,19 @@ class SQL(object):
|
|
|
542
638
|
vals = []
|
|
543
639
|
where = {}
|
|
544
640
|
if schema:
|
|
545
|
-
where[
|
|
641
|
+
where["LOWER(KCU1.CONSTRAINT_SCHEMA)"] = schema.lower()
|
|
546
642
|
if table:
|
|
547
|
-
where[
|
|
643
|
+
where["LOWER(KCU1.TABLE_NAME)"] = table.lower()
|
|
548
644
|
if column:
|
|
549
|
-
where[
|
|
645
|
+
where["LOWER(KCU1.COLUMN_NAME)"] = column.lower()
|
|
550
646
|
make_where(where, sql, vals)
|
|
551
|
-
return
|
|
647
|
+
return " ".join(sql), tuple(vals)
|
|
552
648
|
|
|
553
649
|
@classmethod
|
|
554
|
-
def create_foreign_key(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
key_to_columns,
|
|
559
|
-
name=None,
|
|
560
|
-
schema=None):
|
|
561
|
-
if '.' not in table and schema:
|
|
650
|
+
def create_foreign_key(
|
|
651
|
+
cls, table, columns, key_to_table, key_to_columns, name=None, schema=None
|
|
652
|
+
):
|
|
653
|
+
if "." not in table and schema:
|
|
562
654
|
table = "{}.{}".format(schema, table)
|
|
563
655
|
if isinstance(key_to_columns, str):
|
|
564
656
|
key_to_columns = [key_to_columns]
|
|
@@ -566,26 +658,28 @@ class SQL(object):
|
|
|
566
658
|
columns = [columns]
|
|
567
659
|
if not name:
|
|
568
660
|
m = hashlib.md5()
|
|
569
|
-
m.update(table.encode(
|
|
570
|
-
m.update(
|
|
571
|
-
m.update(key_to_table.encode(
|
|
572
|
-
m.update(
|
|
573
|
-
name =
|
|
661
|
+
m.update(table.encode("utf-8"))
|
|
662
|
+
m.update(" ".join(columns).encode("utf-8"))
|
|
663
|
+
m.update(key_to_table.encode("utf-8"))
|
|
664
|
+
m.update(" ".join(key_to_columns).encode("utf-8"))
|
|
665
|
+
name = "FK_" + m.hexdigest()
|
|
574
666
|
sql = "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({});".format(
|
|
575
|
-
table, name,
|
|
576
|
-
|
|
667
|
+
table, name, ",".join(columns), key_to_table, ",".join(key_to_columns)
|
|
668
|
+
)
|
|
577
669
|
|
|
578
670
|
return sql, tuple()
|
|
579
671
|
|
|
580
672
|
@classmethod
|
|
581
|
-
def drop_foreign_key(
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
673
|
+
def drop_foreign_key(
|
|
674
|
+
cls,
|
|
675
|
+
table,
|
|
676
|
+
columns,
|
|
677
|
+
key_to_table=None,
|
|
678
|
+
key_to_columns=None,
|
|
679
|
+
name=None,
|
|
680
|
+
schema=None,
|
|
681
|
+
):
|
|
682
|
+
if "." not in table and schema:
|
|
589
683
|
table = "{}.{}".format(schema, table)
|
|
590
684
|
if isinstance(key_to_columns, str):
|
|
591
685
|
key_to_columns = [key_to_columns]
|
|
@@ -593,104 +687,142 @@ class SQL(object):
|
|
|
593
687
|
columns = [columns]
|
|
594
688
|
if not name:
|
|
595
689
|
m = hashlib.md5()
|
|
596
|
-
m.update(table.encode(
|
|
597
|
-
m.update(
|
|
598
|
-
m.update(key_to_table.encode(
|
|
599
|
-
m.update(
|
|
600
|
-
name =
|
|
690
|
+
m.update(table.encode("utf-8"))
|
|
691
|
+
m.update(" ".join(columns).encode("utf-8"))
|
|
692
|
+
m.update(key_to_table.encode("utf-8"))
|
|
693
|
+
m.update(" ".join(key_to_columns).encode("utf-8"))
|
|
694
|
+
name = "FK_" + m.hexdigest()
|
|
601
695
|
sql = "ALTER TABLE {} DROP CONSTRAINT {};".format(table, name)
|
|
602
696
|
return sql, tuple()
|
|
603
697
|
|
|
604
698
|
@classmethod
|
|
605
|
-
def create_index(
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
699
|
+
def create_index(
|
|
700
|
+
cls,
|
|
701
|
+
table=None,
|
|
702
|
+
columns=None,
|
|
703
|
+
unique=False,
|
|
704
|
+
direction=None,
|
|
705
|
+
where=None,
|
|
706
|
+
name=None,
|
|
707
|
+
schema=None,
|
|
708
|
+
trigram=None,
|
|
709
|
+
tbl=None,
|
|
710
|
+
lower=None,
|
|
711
|
+
):
|
|
615
712
|
"""
|
|
616
713
|
The following statements must be executed on the database instance once to enable respective trigram features.
|
|
617
714
|
CREATE EXTENSION pg_trgm; is required to use gin.
|
|
618
715
|
CREATE EXTENSION btree_gist; is required to use gist
|
|
619
716
|
"""
|
|
620
|
-
if
|
|
717
|
+
if "." not in table and schema:
|
|
621
718
|
table = "{}.{}".format(schema, table)
|
|
622
719
|
if isinstance(columns, (list, set)):
|
|
623
|
-
columns =
|
|
720
|
+
columns = ",".join([quote(c.lower()) for c in columns])
|
|
624
721
|
else:
|
|
625
722
|
columns = quote(columns)
|
|
626
|
-
sql = [
|
|
723
|
+
sql = ["CREATE"]
|
|
627
724
|
if unique:
|
|
628
|
-
sql.append(
|
|
629
|
-
sql.append(
|
|
725
|
+
sql.append("UNIQUE")
|
|
726
|
+
sql.append("INDEX")
|
|
630
727
|
tablename = quote(table)
|
|
631
728
|
if not name:
|
|
632
729
|
name = re.sub(
|
|
633
|
-
r
|
|
634
|
-
|
|
730
|
+
r"\([^)]*\)",
|
|
731
|
+
"",
|
|
732
|
+
columns.replace(" ", "").replace(",", "_").replace('"', ""),
|
|
733
|
+
)
|
|
635
734
|
if trigram:
|
|
636
|
-
sql.append(
|
|
637
|
-
|
|
735
|
+
sql.append(
|
|
736
|
+
"IDX__TRGM_{}_{}__{}".format(
|
|
737
|
+
table.replace(".", "_"), trigram.upper(), name
|
|
738
|
+
)
|
|
739
|
+
)
|
|
638
740
|
else:
|
|
639
|
-
sql.append(
|
|
640
|
-
|
|
641
|
-
sql.append('ON')
|
|
741
|
+
sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
|
|
742
|
+
sql.append("ON")
|
|
642
743
|
sql.append(quote(tablename))
|
|
643
744
|
|
|
644
745
|
if trigram:
|
|
645
|
-
sql.append(
|
|
746
|
+
sql.append("USING")
|
|
646
747
|
sql.append(trigram)
|
|
647
|
-
sql.append(
|
|
748
|
+
sql.append("(")
|
|
648
749
|
if tbl:
|
|
649
|
-
join =
|
|
650
|
-
for column_name in columns.split(
|
|
750
|
+
join = ""
|
|
751
|
+
for column_name in columns.split(","):
|
|
752
|
+
column_name = column_name.replace('"', "")
|
|
651
753
|
if join:
|
|
652
754
|
sql.append(join)
|
|
653
755
|
column = tbl.column(column_name)
|
|
654
756
|
if column.py_type == str:
|
|
655
|
-
|
|
757
|
+
if lower:
|
|
758
|
+
sql.append("lower({})".format(quote(column_name)))
|
|
759
|
+
else:
|
|
760
|
+
sql.append(quote(column_name))
|
|
656
761
|
else:
|
|
657
762
|
sql.append(quote(column_name))
|
|
658
|
-
join =
|
|
763
|
+
join = ","
|
|
659
764
|
else:
|
|
660
765
|
sql.append(columns)
|
|
661
766
|
if trigram:
|
|
662
|
-
sql.append(
|
|
663
|
-
sql.append(
|
|
767
|
+
sql.append("{}_trgm_ops".format(trigram.lower()))
|
|
768
|
+
sql.append(")")
|
|
664
769
|
vals = []
|
|
770
|
+
|
|
665
771
|
make_where(where, sql, vals)
|
|
666
|
-
return
|
|
772
|
+
return " ".join(sql), tuple(vals)
|
|
667
773
|
|
|
668
774
|
@classmethod
|
|
669
|
-
def drop_index(cls,
|
|
670
|
-
|
|
671
|
-
columns=None,
|
|
672
|
-
name=None,
|
|
673
|
-
schema=None,
|
|
674
|
-
trigram=None):
|
|
675
|
-
if '.' not in table and schema:
|
|
775
|
+
def drop_index(cls, table=None, columns=None, name=None, schema=None, trigram=None):
|
|
776
|
+
if "." not in table and schema:
|
|
676
777
|
table = "{}.{}".format(schema, table)
|
|
677
778
|
if isinstance(columns, (list, set)):
|
|
678
|
-
columns =
|
|
779
|
+
columns = ",".join([quote(c.lower()) for c in columns])
|
|
679
780
|
else:
|
|
680
781
|
columns = quote(columns)
|
|
681
|
-
sql = [
|
|
682
|
-
sql.append(
|
|
782
|
+
sql = ["DROP"]
|
|
783
|
+
sql.append("INDEX IF EXISTS")
|
|
683
784
|
tablename = quote(table)
|
|
684
785
|
if not name:
|
|
685
786
|
name = re.sub(
|
|
686
|
-
r
|
|
687
|
-
|
|
787
|
+
r"\([^)]*\)",
|
|
788
|
+
"",
|
|
789
|
+
columns.replace(" ", "").replace(",", "_").replace('"', ""),
|
|
790
|
+
)
|
|
688
791
|
if trigram:
|
|
689
|
-
sql.append(
|
|
690
|
-
|
|
792
|
+
sql.append(
|
|
793
|
+
"IDX__TRGM_{}_{}__{}".format(
|
|
794
|
+
table.replace(".", "_"), trigram.upper(), name
|
|
795
|
+
)
|
|
796
|
+
)
|
|
691
797
|
else:
|
|
692
|
-
sql.append(
|
|
693
|
-
return
|
|
798
|
+
sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
|
|
799
|
+
return " ".join(sql), tuple()
|
|
800
|
+
|
|
801
|
+
@classmethod
|
|
802
|
+
def merge(cls, table, data, pk, on_conflict_do_nothing, on_conflict_update):
|
|
803
|
+
d = {}
|
|
804
|
+
d.update(data)
|
|
805
|
+
d.update(pk)
|
|
806
|
+
sql, vals = cls.insert(table, d)
|
|
807
|
+
sql = [sql]
|
|
808
|
+
vals = list(vals)
|
|
809
|
+
if on_conflict_do_nothing != on_conflict_update:
|
|
810
|
+
sql.append("ON CONFLICT")
|
|
811
|
+
sql.append("(")
|
|
812
|
+
sql.append(",".join(pk.keys()))
|
|
813
|
+
sql.append(")")
|
|
814
|
+
sql.append("DO")
|
|
815
|
+
if on_conflict_do_nothing:
|
|
816
|
+
sql.append("NOTHING")
|
|
817
|
+
elif on_conflict_update:
|
|
818
|
+
sql2, vals2 = cls.update(table, data, pk, excluded=True)
|
|
819
|
+
sql.append(sql2)
|
|
820
|
+
vals.extend(vals2)
|
|
821
|
+
else:
|
|
822
|
+
raise Exception(
|
|
823
|
+
"Update on conflict must have one and only one option to complete on conflict."
|
|
824
|
+
)
|
|
825
|
+
return " ".join(sql), tuple(vals)
|
|
694
826
|
|
|
695
827
|
@classmethod
|
|
696
828
|
def insert(cls, table, data):
|
|
@@ -699,121 +831,116 @@ class SQL(object):
|
|
|
699
831
|
args = []
|
|
700
832
|
for key, val in data.items():
|
|
701
833
|
keys.append(quote(key.lower()))
|
|
702
|
-
if isinstance(val,str)
|
|
703
|
-
and len(val) > 2 \
|
|
704
|
-
and val[:2] == '@@' and val[2:]:
|
|
834
|
+
if isinstance(val, str) and len(val) > 2 and val[:2] == "@@" and val[2:]:
|
|
705
835
|
vals.append(val[2:])
|
|
706
836
|
else:
|
|
707
|
-
vals.append(
|
|
837
|
+
vals.append("%s")
|
|
708
838
|
args.append(val)
|
|
709
839
|
|
|
710
|
-
sql = [
|
|
840
|
+
sql = ["INSERT INTO"]
|
|
711
841
|
sql.append(quote(table))
|
|
712
|
-
sql.append(
|
|
713
|
-
sql.append(
|
|
714
|
-
sql.append(
|
|
715
|
-
sql.append(
|
|
716
|
-
sql.append(
|
|
717
|
-
sql.append(
|
|
718
|
-
sql.append(
|
|
719
|
-
sql =
|
|
842
|
+
sql.append("(")
|
|
843
|
+
sql.append(",".join(keys))
|
|
844
|
+
sql.append(")")
|
|
845
|
+
sql.append("VALUES")
|
|
846
|
+
sql.append("(")
|
|
847
|
+
sql.append(",".join(vals))
|
|
848
|
+
sql.append(")")
|
|
849
|
+
sql = " ".join(sql)
|
|
720
850
|
return sql, tuple(args)
|
|
721
851
|
|
|
722
852
|
@classmethod
|
|
723
|
-
def update(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
853
|
+
def update(
|
|
854
|
+
cls,
|
|
855
|
+
table,
|
|
856
|
+
data,
|
|
857
|
+
pk,
|
|
858
|
+
left_join=None,
|
|
859
|
+
inner_join=None,
|
|
860
|
+
outer_join=None,
|
|
861
|
+
excluded=False,
|
|
862
|
+
):
|
|
863
|
+
alias = "A"
|
|
864
|
+
if " " in table:
|
|
865
|
+
alias, table = table.split(" ")
|
|
733
866
|
is_join = bool(left_join or inner_join or outer_join)
|
|
734
|
-
sql = [
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
sql.append(
|
|
867
|
+
sql = ["UPDATE"]
|
|
868
|
+
if not excluded:
|
|
869
|
+
sql.append(quote(table))
|
|
870
|
+
sql.append("SET")
|
|
738
871
|
vals = []
|
|
739
|
-
connect =
|
|
872
|
+
connect = ""
|
|
740
873
|
for key, val in data.items():
|
|
741
874
|
if connect:
|
|
742
875
|
sql.append(connect)
|
|
743
|
-
if isinstance(val, str) and val[:2] ==
|
|
876
|
+
if isinstance(val, str) and val[:2] == "@@" and val[2:]:
|
|
744
877
|
sql.append("{} = {}".format(key, val[2:]))
|
|
745
878
|
else:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
879
|
+
if excluded:
|
|
880
|
+
sql.append("{} = EXCLUDED.{}".format(key, key))
|
|
881
|
+
else:
|
|
882
|
+
sql.append("{} = %s".format(key))
|
|
883
|
+
vals.append(val)
|
|
884
|
+
connect = ","
|
|
749
885
|
if is_join:
|
|
750
|
-
sql.append(
|
|
886
|
+
sql.append("FROM")
|
|
751
887
|
sql.append(table)
|
|
752
|
-
sql.append(
|
|
888
|
+
sql.append("AS")
|
|
753
889
|
sql.append(alias)
|
|
754
890
|
if left_join:
|
|
755
891
|
for k, v in left_join.items():
|
|
756
|
-
sql.append(
|
|
892
|
+
sql.append("LEFT JOIN")
|
|
757
893
|
sql.append(k)
|
|
758
|
-
sql.append(
|
|
894
|
+
sql.append("ON")
|
|
759
895
|
sql.append(v)
|
|
760
896
|
if outer_join:
|
|
761
897
|
for k, v in outer_join.items():
|
|
762
|
-
sql.append(
|
|
898
|
+
sql.append("OUTER JOIN")
|
|
763
899
|
sql.append(k)
|
|
764
|
-
sql.append(
|
|
900
|
+
sql.append("ON")
|
|
765
901
|
sql.append(v)
|
|
766
902
|
if inner_join:
|
|
767
903
|
for k, v in inner_join.items():
|
|
768
|
-
sql.append(
|
|
904
|
+
sql.append("INNER JOIN")
|
|
769
905
|
sql.append(k)
|
|
770
|
-
sql.append(
|
|
906
|
+
sql.append("ON")
|
|
771
907
|
sql.append(v)
|
|
772
|
-
|
|
773
|
-
|
|
908
|
+
if not excluded:
|
|
909
|
+
make_where(pk, sql, vals, is_join)
|
|
910
|
+
return " ".join(sql), tuple(vals)
|
|
774
911
|
|
|
775
912
|
@classmethod
|
|
776
913
|
def get_type(cls, v):
|
|
777
914
|
if isinstance(v, str):
|
|
778
|
-
if v[:2] ==
|
|
915
|
+
if v[:2] == "@@":
|
|
779
916
|
return v[2:] or cls.TYPES.TEXT
|
|
780
|
-
elif isinstance(v, str)
|
|
781
|
-
or v is str:
|
|
917
|
+
elif isinstance(v, str) or v is str:
|
|
782
918
|
return cls.TYPES.TEXT
|
|
783
|
-
elif isinstance(v, bool)
|
|
784
|
-
or v is bool:
|
|
919
|
+
elif isinstance(v, bool) or v is bool:
|
|
785
920
|
return cls.TYPES.BOOLEAN
|
|
786
|
-
elif isinstance(v, int)
|
|
787
|
-
or v is int:
|
|
921
|
+
elif isinstance(v, int) or v is int:
|
|
788
922
|
return cls.TYPES.BIGINT
|
|
789
|
-
elif isinstance(v, int) \
|
|
790
|
-
or v is int:
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
elif isinstance(v, float)
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
or v is
|
|
802
|
-
return cls.TYPES.NUMERIC + '(19, 6)'
|
|
803
|
-
elif isinstance (v, datetime.datetime) \
|
|
804
|
-
or v is datetime.datetime:
|
|
923
|
+
# elif isinstance(v, int) \
|
|
924
|
+
# or v is int:
|
|
925
|
+
# if v is int:
|
|
926
|
+
# return cls.TYPES.INTEGER
|
|
927
|
+
# if v > 2147483647 or v < -2147483648:
|
|
928
|
+
# return cls.TYPES.BIGINT
|
|
929
|
+
# else:
|
|
930
|
+
# return cls.TYPES.INTEGER
|
|
931
|
+
elif isinstance(v, float) or v is float:
|
|
932
|
+
return cls.TYPES.NUMERIC + "(19, 6)"
|
|
933
|
+
elif isinstance(v, decimal.Decimal) or v is decimal.Decimal:
|
|
934
|
+
return cls.TYPES.NUMERIC + "(19, 6)"
|
|
935
|
+
elif isinstance(v, datetime.datetime) or v is datetime.datetime:
|
|
805
936
|
return cls.TYPES.DATETIME
|
|
806
|
-
elif isinstance
|
|
807
|
-
or v is datetime.date:
|
|
937
|
+
elif isinstance(v, datetime.date) or v is datetime.date:
|
|
808
938
|
return cls.TYPES.DATE
|
|
809
|
-
elif isinstance(v, datetime.time)
|
|
810
|
-
or v is datetime.time:
|
|
939
|
+
elif isinstance(v, datetime.time) or v is datetime.time:
|
|
811
940
|
return cls.TYPES.TIME
|
|
812
|
-
elif isinstance(v, datetime.timedelta)
|
|
813
|
-
or v is datetime.timedelta:
|
|
941
|
+
elif isinstance(v, datetime.timedelta) or v is datetime.timedelta:
|
|
814
942
|
return cls.TYPES.INTERVAL
|
|
815
|
-
elif isinstance
|
|
816
|
-
or v is bytes:
|
|
943
|
+
elif isinstance(v, bytes) or v is bytes:
|
|
817
944
|
return cls.TYPES.BINARY
|
|
818
945
|
# Everything else defaults to TEXT, incl. None
|
|
819
946
|
return cls.TYPES.TEXT
|
|
@@ -821,45 +948,35 @@ class SQL(object):
|
|
|
821
948
|
@classmethod
|
|
822
949
|
def get_conv(cls, v):
|
|
823
950
|
if isinstance(v, str):
|
|
824
|
-
if v[:2] ==
|
|
951
|
+
if v[:2] == "@@":
|
|
825
952
|
return v[2:] or cls.TYPES.TEXT
|
|
826
|
-
elif isinstance(v, str)
|
|
827
|
-
or v is str:
|
|
953
|
+
elif isinstance(v, str) or v is str:
|
|
828
954
|
return cls.TYPES.TEXT
|
|
829
|
-
elif isinstance(v, bool)
|
|
830
|
-
or v is bool:
|
|
955
|
+
elif isinstance(v, bool) or v is bool:
|
|
831
956
|
return cls.TYPES.BOOLEAN
|
|
832
|
-
elif isinstance(v, int)
|
|
833
|
-
or v is int:
|
|
957
|
+
elif isinstance(v, int) or v is int:
|
|
834
958
|
return cls.TYPES.BIGINT
|
|
835
|
-
elif isinstance(v, int) \
|
|
836
|
-
or v is int:
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
elif isinstance(v, float)
|
|
844
|
-
or v is float:
|
|
959
|
+
# elif isinstance(v, int) \
|
|
960
|
+
# or v is int:
|
|
961
|
+
# if v is int:
|
|
962
|
+
# return cls.TYPES.INTEGER
|
|
963
|
+
# if v > 2147483647 or v < -2147483648:
|
|
964
|
+
# return cls.TYPES.BIGINT
|
|
965
|
+
# else:
|
|
966
|
+
# return cls.TYPES.INTEGER
|
|
967
|
+
elif isinstance(v, float) or v is float:
|
|
845
968
|
return cls.TYPES.NUMERIC
|
|
846
|
-
elif isinstance(v, decimal.Decimal)
|
|
847
|
-
or v is decimal.Decimal:
|
|
969
|
+
elif isinstance(v, decimal.Decimal) or v is decimal.Decimal:
|
|
848
970
|
return cls.TYPES.NUMERIC
|
|
849
|
-
elif isinstance
|
|
850
|
-
or v is datetime.datetime:
|
|
971
|
+
elif isinstance(v, datetime.datetime) or v is datetime.datetime:
|
|
851
972
|
return cls.TYPES.DATETIME
|
|
852
|
-
elif isinstance
|
|
853
|
-
or v is datetime.date:
|
|
973
|
+
elif isinstance(v, datetime.date) or v is datetime.date:
|
|
854
974
|
return cls.TYPES.DATE
|
|
855
|
-
elif isinstance(v, datetime.time)
|
|
856
|
-
or v is datetime.time:
|
|
975
|
+
elif isinstance(v, datetime.time) or v is datetime.time:
|
|
857
976
|
return cls.TYPES.TIME
|
|
858
|
-
elif isinstance(v, datetime.timedelta)
|
|
859
|
-
or v is datetime.timedelta:
|
|
977
|
+
elif isinstance(v, datetime.timedelta) or v is datetime.timedelta:
|
|
860
978
|
return cls.TYPES.INTERVAL
|
|
861
|
-
elif isinstance
|
|
862
|
-
or v is bytes:
|
|
979
|
+
elif isinstance(v, bytes) or v is bytes:
|
|
863
980
|
return cls.TYPES.BINARY
|
|
864
981
|
# Everything else defaults to TEXT, incl. None
|
|
865
982
|
return cls.TYPES.TEXT
|
|
@@ -883,10 +1000,16 @@ class SQL(object):
|
|
|
883
1000
|
return datetime.date
|
|
884
1001
|
elif v == cls.TYPES.TIME:
|
|
885
1002
|
return datetime.time
|
|
1003
|
+
elif v == cls.TYPES.TIME_TZ:
|
|
1004
|
+
return datetime.time
|
|
886
1005
|
elif v == cls.TYPES.DATETIME:
|
|
887
1006
|
return datetime.datetime
|
|
888
1007
|
elif v == cls.TYPES.INTERVAL:
|
|
889
1008
|
return datetime.timedelta
|
|
1009
|
+
elif v == cls.TYPES.DATETIME_TZ:
|
|
1010
|
+
return datetime.datetime
|
|
1011
|
+
elif v == cls.TYPES.INTERVAL_TZ:
|
|
1012
|
+
return datetime.timedelta
|
|
890
1013
|
else:
|
|
891
1014
|
raise Exception("unmapped type %s" % v)
|
|
892
1015
|
|
|
@@ -903,7 +1026,7 @@ class SQL(object):
|
|
|
903
1026
|
primaryKey = set(cls.GetPrimaryKeyColumnNames())
|
|
904
1027
|
if not primaryKey:
|
|
905
1028
|
if not cls.Exists():
|
|
906
|
-
raise
|
|
1029
|
+
raise DbTableMissingError
|
|
907
1030
|
dataKeys = set(data.keys()).intersection(primaryKey)
|
|
908
1031
|
dataColumns = set(data.keys()).difference(primaryKey)
|
|
909
1032
|
pk = {}
|
|
@@ -915,24 +1038,27 @@ class SQL(object):
|
|
|
915
1038
|
@classmethod
|
|
916
1039
|
def alter_add(cls, table, columns, null_allowed=True):
|
|
917
1040
|
sql = []
|
|
918
|
-
null =
|
|
1041
|
+
null = "NOT NULL" if not null_allowed else ""
|
|
919
1042
|
if isinstance(columns, dict):
|
|
920
1043
|
for key, val in columns.items():
|
|
921
|
-
key = re.sub(
|
|
922
|
-
sql.append(
|
|
923
|
-
|
|
924
|
-
|
|
1044
|
+
key = re.sub("<>!=%", "", key.lower())
|
|
1045
|
+
sql.append(
|
|
1046
|
+
"ALTER TABLE {} ADD {} {} {};".format(
|
|
1047
|
+
quote(table), quote(key), cls.get_type(val), null
|
|
1048
|
+
)
|
|
1049
|
+
)
|
|
1050
|
+
return "\n\t".join(sql), tuple()
|
|
925
1051
|
|
|
926
1052
|
@classmethod
|
|
927
1053
|
def alter_drop(cls, table, columns):
|
|
928
1054
|
sql = ["ALTER TABLE {} DROP COLUMN".format(quote(table))]
|
|
929
1055
|
if isinstance(columns, dict):
|
|
930
1056
|
for key, val in columns.items():
|
|
931
|
-
key = re.sub(
|
|
1057
|
+
key = re.sub("<>!=%", "", key.lower())
|
|
932
1058
|
sql.append("{},".format(key))
|
|
933
|
-
if sql[-1][-1] ==
|
|
1059
|
+
if sql[-1][-1] == ",":
|
|
934
1060
|
sql[-1] = sql[-1][:-1]
|
|
935
|
-
return
|
|
1061
|
+
return "\n\t".join(sql), tuple()
|
|
936
1062
|
|
|
937
1063
|
@classmethod
|
|
938
1064
|
def alter_column_by_type(cls, table, column, value, nullable=True):
|
|
@@ -940,24 +1066,27 @@ class SQL(object):
|
|
|
940
1066
|
sql.append("{} TYPE {}".format(quote(column), cls.get_type(value)))
|
|
941
1067
|
sql.append("USING {}::{}".format(quote(column), cls.get_conv(value)))
|
|
942
1068
|
if not nullable:
|
|
943
|
-
sql.append(
|
|
944
|
-
return
|
|
1069
|
+
sql.append("NOT NULL")
|
|
1070
|
+
return "\n\t".join(sql), tuple()
|
|
945
1071
|
|
|
946
1072
|
@classmethod
|
|
947
1073
|
def alter_column_by_sql(cls, table, column, value):
|
|
948
1074
|
sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
|
|
949
1075
|
sql.append("{} {}".format(quote(column), value))
|
|
950
|
-
return
|
|
1076
|
+
return " ".join(sql), tuple()
|
|
951
1077
|
|
|
952
1078
|
@classmethod
|
|
953
1079
|
def rename_column(cls, table, orig, new):
|
|
954
|
-
return
|
|
955
|
-
|
|
1080
|
+
return (
|
|
1081
|
+
"ALTER TABLE {} RENAME COLUMN {} TO {};".format(
|
|
1082
|
+
quote(table), quote(orig), quote(new)
|
|
1083
|
+
),
|
|
1084
|
+
tuple(),
|
|
1085
|
+
)
|
|
956
1086
|
|
|
957
1087
|
@classmethod
|
|
958
1088
|
def rename_table(cls, table, new):
|
|
959
|
-
return "ALTER TABLE {} RENAME TO {};".format(quote(table),
|
|
960
|
-
quote(new)), tuple()
|
|
1089
|
+
return "ALTER TABLE {} RENAME TO {};".format(quote(table), quote(new)), tuple()
|
|
961
1090
|
|
|
962
1091
|
@classmethod
|
|
963
1092
|
def create_savepoint(cls, sp):
|
|
@@ -972,36 +1101,22 @@ class SQL(object):
|
|
|
972
1101
|
return 'ROLLBACK TO SAVEPOINT "{}"'.format(sp), tuple()
|
|
973
1102
|
|
|
974
1103
|
@classmethod
|
|
975
|
-
def
|
|
976
|
-
|
|
977
|
-
columns
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
""".format(table, ','.join(quote(columns)), key), tuple()
|
|
985
|
-
|
|
986
|
-
@classmethod
|
|
987
|
-
def delete_duplicates(cls, table, columns, key):
|
|
988
|
-
if isinstance(columns, str):
|
|
989
|
-
columns = [columns]
|
|
990
|
-
return """
|
|
991
|
-
DELETE FROM {0}
|
|
992
|
-
WHERE {2} IN (SELECT {2}
|
|
993
|
-
FROM (SELECT {2},
|
|
994
|
-
ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
|
|
995
|
-
FROM {0}) t
|
|
996
|
-
WHERE t.rnum > 1);
|
|
997
|
-
""".format(table, ','.join(quote(columns)), key), tuple()
|
|
1104
|
+
def duplicate_rows(cls, table, columns, where={}):
|
|
1105
|
+
return cls.select(
|
|
1106
|
+
columns,
|
|
1107
|
+
table,
|
|
1108
|
+
where,
|
|
1109
|
+
orderby=columns,
|
|
1110
|
+
groupby=columns,
|
|
1111
|
+
having="count(*) > 2",
|
|
1112
|
+
)
|
|
998
1113
|
|
|
999
1114
|
@classmethod
|
|
1000
1115
|
def delete(cls, table, where):
|
|
1001
|
-
sql = [
|
|
1116
|
+
sql = ["DELETE FROM {}".format(table)]
|
|
1002
1117
|
vals = []
|
|
1003
1118
|
make_where(where, sql, vals)
|
|
1004
|
-
return
|
|
1119
|
+
return " ".join(sql), tuple(vals)
|
|
1005
1120
|
|
|
1006
1121
|
@classmethod
|
|
1007
1122
|
def truncate(cls, table):
|
|
@@ -1009,267 +1124,273 @@ class SQL(object):
|
|
|
1009
1124
|
|
|
1010
1125
|
@classmethod
|
|
1011
1126
|
def create_view(cls, name, query, temp=False, silent=True):
|
|
1012
|
-
sql = [
|
|
1127
|
+
sql = ["CREATE"]
|
|
1013
1128
|
if silent:
|
|
1014
|
-
sql.append(
|
|
1129
|
+
sql.append("OR REPLACE")
|
|
1015
1130
|
if temp:
|
|
1016
|
-
sql.append(
|
|
1017
|
-
sql.append(
|
|
1131
|
+
sql.append("TEMPORARY")
|
|
1132
|
+
sql.append("VIEW")
|
|
1018
1133
|
sql.append(name)
|
|
1019
|
-
sql.append(
|
|
1134
|
+
sql.append("AS")
|
|
1020
1135
|
sql.append(query)
|
|
1021
|
-
return
|
|
1136
|
+
return " ".join(sql), tuple()
|
|
1022
1137
|
|
|
1023
1138
|
@classmethod
|
|
1024
1139
|
def drop_view(cls, name, silent=True):
|
|
1025
|
-
sql = [
|
|
1140
|
+
sql = ["DROP VIEW"]
|
|
1026
1141
|
if silent:
|
|
1027
|
-
sql.append(
|
|
1142
|
+
sql.append("IF EXISTS")
|
|
1028
1143
|
sql.append(name)
|
|
1029
|
-
return
|
|
1144
|
+
return " ".join(sql), tuple()
|
|
1030
1145
|
|
|
1031
1146
|
@classmethod
|
|
1032
|
-
def alter_trigger(cls, table, state=
|
|
1033
|
-
return
|
|
1034
|
-
name), tuple()
|
|
1147
|
+
def alter_trigger(cls, table, state="ENABLE", name="USER"):
|
|
1148
|
+
return "ALTER TABLE {} {} TRIGGER {}".format(table, state, name), tuple()
|
|
1035
1149
|
|
|
1036
1150
|
@classmethod
|
|
1037
1151
|
def set_sequence(cls, table, next_value):
|
|
1038
|
-
return
|
|
1039
|
-
|
|
1152
|
+
return (
|
|
1153
|
+
"SELECT SETVAL(PG_GET_SERIAL_SEQUENCE('{0}', 'sys_id'),{1},FALSE)".format(
|
|
1154
|
+
table, next_value
|
|
1155
|
+
),
|
|
1156
|
+
tuple(),
|
|
1157
|
+
)
|
|
1040
1158
|
|
|
1041
1159
|
@classmethod
|
|
1042
|
-
def missing(cls, table, list, column=
|
|
1160
|
+
def missing(cls, table, list, column="SYS_ID", where=None):
|
|
1043
1161
|
sql = [
|
|
1044
|
-
|
|
1162
|
+
"SELECT * FROM",
|
|
1045
1163
|
f"UNNEST('{{{','.join([str(x) for x in list])}}}'::int[]) id",
|
|
1046
|
-
|
|
1164
|
+
"EXCEPT ALL",
|
|
1047
1165
|
f"SELECT {column} FROM {table}",
|
|
1048
1166
|
]
|
|
1049
1167
|
vals = []
|
|
1050
1168
|
make_where(where, sql, vals)
|
|
1051
|
-
return
|
|
1169
|
+
return " ".join(sql), tuple(vals)
|
|
1052
1170
|
|
|
1053
1171
|
class TYPES(object):
|
|
1054
|
-
TEXT =
|
|
1055
|
-
INTEGER =
|
|
1056
|
-
NUMERIC =
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1172
|
+
TEXT = "TEXT"
|
|
1173
|
+
INTEGER = "INTEGER"
|
|
1174
|
+
NUMERIC = "NUMERIC"
|
|
1175
|
+
DATETIME_TZ = "TIMESTAMP WITH TIME ZONE"
|
|
1176
|
+
TIMESTAMP_TZ = "TIMESTAMP WITH TIME ZONE"
|
|
1177
|
+
DATETIME = "TIMESTAMP WITHOUT TIME ZONE"
|
|
1178
|
+
TIMESTAMP = "TIMESTAMP WITHOUT TIME ZONE"
|
|
1179
|
+
DATE = "DATE"
|
|
1180
|
+
TIME_TZ = "TIME WITH TIME ZONE"
|
|
1181
|
+
TIME = "TIME WITHOUT TIME ZONE"
|
|
1182
|
+
BIGINT = "BIGINT"
|
|
1183
|
+
SMALLINT = "SMALLINT"
|
|
1184
|
+
BOOLEAN = "BOOLEAN"
|
|
1185
|
+
BINARY = "BYTEA"
|
|
1186
|
+
INTERVAL = "INTERVAL"
|
|
1066
1187
|
|
|
1067
1188
|
|
|
1068
1189
|
reserved_words = [
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1190
|
+
"ADMIN",
|
|
1191
|
+
"ALIAS",
|
|
1192
|
+
"ALL",
|
|
1193
|
+
"ALLOCATE",
|
|
1194
|
+
"ANALYSE",
|
|
1195
|
+
"ANALYZE",
|
|
1196
|
+
"AND",
|
|
1197
|
+
"ANY",
|
|
1198
|
+
"ARE",
|
|
1199
|
+
"ARRAY",
|
|
1200
|
+
"AS",
|
|
1201
|
+
"ASC",
|
|
1202
|
+
"AUTHORIZATION",
|
|
1203
|
+
"BETWEEN",
|
|
1204
|
+
"BINARY",
|
|
1205
|
+
"BLOB",
|
|
1206
|
+
"BOTH",
|
|
1207
|
+
"BREADTH",
|
|
1208
|
+
"CALL",
|
|
1209
|
+
"CASCADED",
|
|
1210
|
+
"CASE",
|
|
1211
|
+
"CAST",
|
|
1212
|
+
"CATALOG",
|
|
1213
|
+
"CHECK",
|
|
1214
|
+
"CLOB",
|
|
1215
|
+
"COLLATE",
|
|
1216
|
+
"COLLATION",
|
|
1217
|
+
"COLUMN",
|
|
1218
|
+
"COMPLETION",
|
|
1219
|
+
"CONNECT",
|
|
1220
|
+
"CONNECTION",
|
|
1221
|
+
"CONSTRAINT",
|
|
1222
|
+
"CONSTRUCTOR",
|
|
1223
|
+
"CONTINUE",
|
|
1224
|
+
"CORRESPONDING",
|
|
1225
|
+
"CREATE",
|
|
1226
|
+
"CROSS",
|
|
1227
|
+
"CUBE",
|
|
1228
|
+
"CURRENT",
|
|
1229
|
+
"CURRENT_DATE",
|
|
1230
|
+
"CURRENT_PATH",
|
|
1231
|
+
"CURRENT_ROLE",
|
|
1232
|
+
"CURRENT_TIME",
|
|
1233
|
+
"CURRENT_TIMESTAMP",
|
|
1234
|
+
"CURRENT_USER",
|
|
1235
|
+
"DATA",
|
|
1236
|
+
"DATE",
|
|
1237
|
+
"DEFAULT",
|
|
1238
|
+
"DEFERRABLE",
|
|
1239
|
+
"DEPTH",
|
|
1240
|
+
"DEREF",
|
|
1241
|
+
"DESC",
|
|
1242
|
+
"DESCRIBE",
|
|
1243
|
+
"DESCRIPTOR",
|
|
1244
|
+
"DESTROY",
|
|
1245
|
+
"DESTRUCTOR",
|
|
1246
|
+
"DETERMINISTIC",
|
|
1247
|
+
"DIAGNOSTICS",
|
|
1248
|
+
"DICTIONARY",
|
|
1249
|
+
"DISCONNECT",
|
|
1250
|
+
"DISTINCT",
|
|
1251
|
+
"DO",
|
|
1252
|
+
"DYNAMIC",
|
|
1253
|
+
"ELSE",
|
|
1254
|
+
"END",
|
|
1255
|
+
"END-EXEC",
|
|
1256
|
+
"EQUALS",
|
|
1257
|
+
"EVERY",
|
|
1258
|
+
"EXCEPT",
|
|
1259
|
+
"EXCEPTION",
|
|
1260
|
+
"EXEC",
|
|
1261
|
+
"FALSE",
|
|
1262
|
+
"FIRST",
|
|
1263
|
+
"FOR",
|
|
1264
|
+
"FOREIGN",
|
|
1265
|
+
"FOUND",
|
|
1266
|
+
"FREE",
|
|
1267
|
+
"FREEZE",
|
|
1268
|
+
"FROM",
|
|
1269
|
+
"FULL",
|
|
1270
|
+
"GENERAL",
|
|
1271
|
+
"GO",
|
|
1272
|
+
"GOTO",
|
|
1273
|
+
"GRANT",
|
|
1274
|
+
"GROUP",
|
|
1275
|
+
"GROUPING",
|
|
1276
|
+
"HAVING",
|
|
1277
|
+
"HOST",
|
|
1278
|
+
"IDENTITY",
|
|
1279
|
+
"IGNORE",
|
|
1280
|
+
"ILIKE",
|
|
1281
|
+
"IN",
|
|
1282
|
+
"INDICATOR",
|
|
1283
|
+
"INITIALIZE",
|
|
1284
|
+
"INITIALLY",
|
|
1285
|
+
"INNER",
|
|
1286
|
+
"INTERSECT",
|
|
1287
|
+
"INTO",
|
|
1288
|
+
"IS",
|
|
1289
|
+
"ISNULL",
|
|
1290
|
+
"ITERATE",
|
|
1291
|
+
"JOIN",
|
|
1292
|
+
"LARGE",
|
|
1293
|
+
"LAST",
|
|
1294
|
+
"LATERAL",
|
|
1295
|
+
"LEADING",
|
|
1296
|
+
"LEFT",
|
|
1297
|
+
"LESS",
|
|
1298
|
+
"LIKE",
|
|
1299
|
+
"LIMIT",
|
|
1300
|
+
"LOCALTIME",
|
|
1301
|
+
"LOCALTIMESTAMP",
|
|
1302
|
+
"LOCATOR",
|
|
1303
|
+
"MAP",
|
|
1304
|
+
"MODIFIES",
|
|
1305
|
+
"MODIFY",
|
|
1306
|
+
"MODULE",
|
|
1307
|
+
"NAME",
|
|
1308
|
+
"NATURAL",
|
|
1309
|
+
"NCLOB",
|
|
1310
|
+
"NEW",
|
|
1311
|
+
"NOT",
|
|
1312
|
+
"NOTNULL",
|
|
1313
|
+
"NULL",
|
|
1314
|
+
"OBJECT",
|
|
1315
|
+
"OFF",
|
|
1316
|
+
"OFFSET",
|
|
1317
|
+
"OLD",
|
|
1318
|
+
"ON",
|
|
1319
|
+
"ONLY",
|
|
1320
|
+
"OPEN",
|
|
1321
|
+
"OPERATION",
|
|
1322
|
+
"OR",
|
|
1323
|
+
"ORDER",
|
|
1324
|
+
"ORDINALITY",
|
|
1325
|
+
"OUTER",
|
|
1326
|
+
"OUTPUT",
|
|
1327
|
+
"OVERLAPS",
|
|
1328
|
+
"PAD",
|
|
1329
|
+
"PARAMETER",
|
|
1330
|
+
"PARAMETERS",
|
|
1331
|
+
"PLACING",
|
|
1332
|
+
"POSTFIX",
|
|
1333
|
+
"PREFIX",
|
|
1334
|
+
"PREORDER",
|
|
1335
|
+
"PRESERVE",
|
|
1336
|
+
"PRIMARY",
|
|
1337
|
+
"PUBLIC",
|
|
1338
|
+
"READS",
|
|
1339
|
+
"RECURSIVE",
|
|
1340
|
+
"REF",
|
|
1341
|
+
"REFERENCES",
|
|
1342
|
+
"REFERENCING",
|
|
1343
|
+
"RESULT",
|
|
1344
|
+
"RETURN",
|
|
1345
|
+
"RIGHT",
|
|
1346
|
+
"ROLE",
|
|
1347
|
+
"ROLLUP",
|
|
1348
|
+
"ROUTINE",
|
|
1349
|
+
"ROWS",
|
|
1350
|
+
"SAVEPOINT",
|
|
1351
|
+
"SCOPE",
|
|
1352
|
+
"SEARCH",
|
|
1353
|
+
"SECTION",
|
|
1354
|
+
"SELECT",
|
|
1355
|
+
"SESSION_USER",
|
|
1356
|
+
"SETS",
|
|
1357
|
+
"SIMILAR",
|
|
1358
|
+
"SIZE",
|
|
1359
|
+
"SOME",
|
|
1360
|
+
"SPACE",
|
|
1361
|
+
"SPECIFIC",
|
|
1362
|
+
"SPECIFICTYPE",
|
|
1363
|
+
"SQL",
|
|
1364
|
+
"SQLCODE",
|
|
1365
|
+
"SQLERROR",
|
|
1366
|
+
"SQLEXCEPTION",
|
|
1367
|
+
"SQLSTATE",
|
|
1368
|
+
"SQLWARNING",
|
|
1369
|
+
"STATE",
|
|
1370
|
+
"STATIC",
|
|
1371
|
+
"STRUCTURE",
|
|
1372
|
+
"SYSTEM_USER",
|
|
1373
|
+
"TABLE",
|
|
1374
|
+
"TERMINATE",
|
|
1375
|
+
"THAN",
|
|
1376
|
+
"THEN",
|
|
1377
|
+
"TIMESTAMP",
|
|
1378
|
+
"TIMEZONE_HOUR",
|
|
1379
|
+
"TIMEZONE_MINUTE",
|
|
1380
|
+
"TO",
|
|
1381
|
+
"TRAILING",
|
|
1382
|
+
"TRANSLATION",
|
|
1383
|
+
"TRUE",
|
|
1384
|
+
"UNDER",
|
|
1385
|
+
"UNION",
|
|
1386
|
+
"UNIQUE",
|
|
1387
|
+
"UNNEST",
|
|
1388
|
+
"USER",
|
|
1389
|
+
"USING",
|
|
1390
|
+
"VALUE",
|
|
1391
|
+
"VARIABLE",
|
|
1392
|
+
"VERBOSE",
|
|
1393
|
+
"WHEN",
|
|
1394
|
+
"WHENEVER",
|
|
1395
|
+
"WHERE",
|
|
1275
1396
|
]
|