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.

@@ -1,38 +1,43 @@
1
- import os
1
+ import psycopg2
2
2
  import re
3
+ import os
3
4
  import hashlib
4
5
  import decimal
5
6
  import datetime
6
- from velocity.db.servers.sql import Query
7
- from velocity.db import exceptions
8
-
9
-
10
- def initialize(config = {
11
- 'database': os.environ['DBDatabase'],
12
- 'host': os.environ['DBHost'],
13
- 'port': os.environ['DBPort'],
14
- 'user': os.environ['DBUser'],
15
- 'password': os.environ['DBPassword'],
16
- }, database=None, host=None, port=None, user=None, password=None):
17
- import psycopg2
18
- from velocity.db.core.engine import Engine
19
-
20
- if database:
21
- config['database'] = database
22
- if host:
23
- config['host'] = host
24
- if port:
25
- config['port'] = port
26
- if user:
27
- config['user'] = user
28
- if password:
29
- config['password'] = password
30
- return Engine(psycopg2, config, SQL)
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('WHERE')
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 = 'A'
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: sql.append(connect)
53
+ if connect:
54
+ sql.append(connect)
49
55
  if is_join:
50
- if '.' not in key:
51
- key = alias + '.' + quote(key.lower())
56
+ if "." not in key:
57
+ key = alias + "." + quote(key.lower())
52
58
  if val == None:
53
- if '!' in key:
54
- key = key.replace('!', '')
55
- sql.append('{} is not NULL'.format(key))
59
+ if "!" in key:
60
+ key = key.replace("!", "")
61
+ sql.append("{} is not NULL".format(key))
56
62
  else:
57
- sql.append('{} is NULL'.format(key))
58
- elif isinstance(val, (list, tuple)) and '><' not in key:
59
- if '!' in key:
60
- key = key.replace('!', '')
61
- sql.append('{} not in %s'.format(key))
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('{} in %s'.format(key))
70
+ sql.append("{} in %s".format(key))
65
71
  vals.append(tuple(val))
66
72
  elif isinstance(val, Query):
67
- sql.append('{} in ({})'.format(key, val))
73
+ sql.append("{} in ({})".format(key, val))
68
74
  else:
69
75
  case = None
70
- if '<>' in key:
71
- key = key.replace('<>', '')
72
- op = '<>'
73
- elif '!=' in key:
74
- key = key.replace('!=', '')
75
- op = '<>'
76
- elif '!><' in key:
77
- key = key.replace('!><', '')
78
- op = 'not between'
79
- elif '><' in key:
80
- key = key.replace('><', '')
81
- op = 'between'
82
- elif '!%' in key:
83
- key = key.replace('!%', '')
84
- op = 'not like'
85
- elif '%%' in key:
86
- key = key.replace('%%', '')
87
- op = '%'
88
- elif '%>' in key:
89
- key = key.replace('%>', '')
90
- op = '%>'
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 = 'like'
112
- case = 'lower'
113
- elif '!' in key:
114
- key = key.replace('!', '')
115
- op = '<>'
116
- elif '=' in key:
117
- key = key.replace('=', '')
118
- op = '='
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 '#' in key:
122
- key = key.replace('#', '')
123
- op = '='
124
- case = 'lower'
125
- if isinstance(val, str) and val[:2] == '@@' and val[2:]:
126
- sql.append('{} {} {}'.format(key, op, val[2:]))
127
- elif op in ['between', 'not between']:
128
- sql.append('{} {} %s and %s'.format(key, op))
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('{2}({0}) {1} {2}(%s)'.format(key, op, case))
157
+ sql.append("{2}({0}) {1} {2}(%s)".format(key, op, case))
133
158
  else:
134
- sql.append('{0} {1} %s'.format(key, op))
159
+ sql.append("{0} {1} %s".format(key, op))
135
160
  vals.append(val)
136
- connect = 'AND'
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
- new.append(quote(item))
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('[/]', part):
181
+ elif re.findall("[/]", part):
154
182
  new.append('"' + part + '"')
155
183
  else:
156
184
  new.append(part)
157
- return '.'.join(new)
185
+ return ".".join(new)
158
186
 
159
187
 
160
188
  class SQL(object):
161
189
  server = "PostGreSQL"
162
- type_column_identifier = 'data_type'
163
- is_nullable = 'is_nullable'
190
+ type_column_identifier = "data_type"
191
+ is_nullable = "is_nullable"
164
192
 
165
- default_schema = 'public'
193
+ default_schema = "public"
166
194
 
167
- ApplicationErrorCodes = ['22P02', '42883']
195
+ ApplicationErrorCodes = ["22P02", "42883"]
168
196
 
169
197
  DatabaseMissingErrorCodes = []
170
- TableMissingErrorCodes = ['42P01']
171
- ColumnMissingErrorCodes = ['42703']
172
- ForeignKeyMissingErrorCodes = ['42704']
198
+ TableMissingErrorCodes = ["42P01"]
199
+ ColumnMissingErrorCodes = ["42703"]
200
+ ForeignKeyMissingErrorCodes = ["42704"]
173
201
 
174
- ConnectionErrorCodes = ['08001', '08S01']
202
+ ConnectionErrorCodes = ["08001", "08S01"]
175
203
  DuplicateKeyErrorCodes = [] # Handled in regex check.
176
204
  RetryTransactionCodes = []
177
205
  TruncationErrorCodes = []
178
- LockTimeoutErrorCodes = ['55P03']
179
- DatabaseObjectExistsErrorCodes = ['42710', '42P07', '42P04']
180
- DataIntegrityErrorCodes = ['23503']
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 'select schema_name from information_schema.schemata', tuple()
228
+ return "select schema_name from information_schema.schemata", tuple()
202
229
 
203
230
  @classmethod
204
231
  def current_schema(cls):
205
- return 'select current_schema', tuple()
232
+ return "select current_schema", tuple()
206
233
 
207
234
  @classmethod
208
235
  def current_database(cls):
209
- return 'select current_database()', tuple()
236
+ return "select current_database()", tuple()
210
237
 
211
238
  @classmethod
212
239
  def tables(cls, system=False):
213
240
  if system:
214
- return "select table_schema,table_name from information_schema.tables where table_type = 'BASE TABLE' order by table_schema,table_name", tuple(
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 "select table_schema, table_name from information_schema.tables where table_type = 'BASE TABLE' and table_schema NOT IN ('pg_catalog', 'information_schema')", tuple(
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 'select table_schema, table_name from information_schema.views order by table_schema,table_name', tuple(
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 'select table_schema, table_name from information_schema.views where table_schema = any (current_schemas(false)) order by table_schema,table_name', tuple(
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
- if isinstance(columns, list):
233
- columns = ','.join(columns)
234
- if '>' in columns:
235
- return True
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(cls,
240
- columns=None,
241
- table=None,
242
- where=None,
243
- orderby=None,
244
- groupby=None,
245
- having=None,
246
- start=None,
247
- qty=None,
248
- tbl=None):
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
- 'SELECT',
297
+ "SELECT",
257
298
  columns,
258
- 'FROM',
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 = ['{} AS {}'.format(quote(table), tables.get(table))]
310
+ __from = ["{} AS {}".format(quote(table), tables.get(table))]
270
311
  __left_join = []
271
312
 
272
313
  for column in columns:
273
- if '>' in column:
274
- parts = column.split('>')
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['referenced_table_name']
279
- ref_schema = foreign['referenced_table_schema']
280
- ref_column = foreign['referenced_column_name']
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('{}."{}" as "{}"'.format(
284
- tables.get(lookup), parts[1], '_'.join(parts)))
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('{}."{}" as "{}"'.format(
289
- tables.get(lookup), parts[1], '_'.join(parts)))
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
- __left_join.append('ON {}."{}" = {}."{}"'.format(
294
- tables.get(table), parts[0], tables.get(lookup),
295
- ref_column))
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
- parts[1]))
354
+ column, "{}.{}".format(tables.get(lookup), parts[1])
355
+ )
300
356
 
301
357
  else:
302
- if '(' in column:
358
+ if "(" in column:
303
359
  __select.append(column)
304
360
  else:
305
- __select.append("{}.{}".format(tables.get(table),
306
- column))
307
- sql = ['SELECT']
308
- sql.append(','.join(__select))
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 = ','.join(columns)
373
+ columns = ",".join(columns)
319
374
  else:
320
- columns = '*'
375
+ columns = "*"
321
376
  sql = [
322
- 'SELECT',
377
+ "SELECT",
323
378
  columns,
324
- 'FROM',
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('GROUP BY')
385
+ sql.append("GROUP BY")
331
386
  if isinstance(groupby, (list, tuple)):
332
- groupby = ','.join(groupby)
387
+ groupby = ",".join(groupby)
333
388
  sql.append(groupby)
334
389
  if having:
335
- sql.append('HAVING')
390
+ sql.append("HAVING")
336
391
  if isinstance(having, (list, tuple)):
337
- having = ','.join(having)
392
+ having = ",".join(having)
338
393
  sql.append(having)
339
394
  if orderby:
340
- sql.append('ORDER BY')
395
+ sql.append("ORDER BY")
341
396
  if isinstance(orderby, (list, tuple)):
342
- orderby = ','.join(orderby)
397
+ orderby = ",".join(orderby)
343
398
  sql.append(orderby)
344
399
  if start and qty:
345
- sql.append('OFFSET {} ROWS FETCH NEXT {} ROWS ONLY'.format(
346
- start, qty))
400
+ sql.append("OFFSET {} ROWS FETCH NEXT {} ROWS ONLY".format(start, qty))
347
401
  elif start:
348
- sql.append('OFFSET {} ROWS'.format(start))
402
+ sql.append("OFFSET {} ROWS".format(start))
349
403
  elif qty:
350
- sql.append('FETCH NEXT {} ROWS ONLY'.format(qty))
351
- sql = ' '.join(sql)
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 'create database ' + name, tuple()
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
- [table])
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 'drop database if exists ' + name, tuple()
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 '.' in name:
439
+ if "." in name:
375
440
  fqtn = name
376
441
  else:
377
- fqtn = 'public.' + name
378
- schema, table = fqtn.split('.')
379
- name = fqtn.replace('.', '_')
380
- trigger = ''.format(name)
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 SERIAL PRIMARY KEY,
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}.update_sys_modified()
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
- -- Do not allow sys_created to be modified.
400
- NEW.sys_created := OLD.sys_created;
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}.update_sys_modified();
484
+ BEFORE INSERT OR UPDATE ON {0}
485
+ FOR EACH ROW EXECUTE PROCEDURE {1}.on_sys_modified();
412
486
 
413
- """.format(fqtn, schema, table, fqtn.replace('.', '_')))
487
+ """.format(
488
+ fqtn, schema, table, fqtn.replace(".", "_")
489
+ )
490
+ )
414
491
 
415
492
  for key, val in columns.items():
416
- key = re.sub('<>!=%', '', key.lower())
417
- if key in ['sys_id', 'sys_created', 'sys_modified']:
493
+ key = re.sub("<>!=%", "", key.lower())
494
+ if key in system_fields:
418
495
  continue
419
- sql.append("ALTER TABLE {} ADD COLUMN {} {};".format(
420
- quote(fqtn), quote(key), cls.get_type(val)))
421
- return '\n\t'.join(sql), tuple()
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 "ALTER TABLE %s DROP COLUMN %s CASCADE" % (
431
- quote(table), quote(name)), tuple()
510
+ return (
511
+ "ALTER TABLE %s DROP COLUMN %s CASCADE" % (quote(table), quote(name)),
512
+ tuple(),
513
+ )
432
514
  else:
433
- return "ALTER TABLE %s DROP COLUMN %s " % (quote(table),
434
- quote(name)), tuple()
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 '.' in name:
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(name.split('.'))
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
- name,
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 '.' in table:
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(params)
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(params)
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 '.' in table:
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(params)
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(params)
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 '.' in table:
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['LOWER(KCU1.CONSTRAINT_SCHEMA)'] = schema.lower()
641
+ where["LOWER(KCU1.CONSTRAINT_SCHEMA)"] = schema.lower()
546
642
  if table:
547
- where['LOWER(KCU1.TABLE_NAME)'] = table.lower()
643
+ where["LOWER(KCU1.TABLE_NAME)"] = table.lower()
548
644
  if column:
549
- where['LOWER(KCU1.COLUMN_NAME)'] = column.lower()
645
+ where["LOWER(KCU1.COLUMN_NAME)"] = column.lower()
550
646
  make_where(where, sql, vals)
551
- return ' '.join(sql), tuple(vals)
647
+ return " ".join(sql), tuple(vals)
552
648
 
553
649
  @classmethod
554
- def create_foreign_key(cls,
555
- table,
556
- columns,
557
- key_to_table,
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('utf-8'))
570
- m.update(' '.join(columns).encode('utf-8'))
571
- m.update(key_to_table.encode('utf-8'))
572
- m.update(' '.join(key_to_columns).encode('utf-8'))
573
- name = 'FK_' + m.hexdigest()
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, ','.join(columns), key_to_table,
576
- ','.join(key_to_columns))
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(cls,
582
- table,
583
- columns,
584
- key_to_table=None,
585
- key_to_columns=None,
586
- name=None,
587
- schema=None):
588
- if '.' not in table and schema:
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('utf-8'))
597
- m.update(' '.join(columns).encode('utf-8'))
598
- m.update(key_to_table.encode('utf-8'))
599
- m.update(' '.join(key_to_columns).encode('utf-8'))
600
- name = 'FK_' + m.hexdigest()
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(cls,
606
- table=None,
607
- columns=None,
608
- unique=False,
609
- direction=None,
610
- where=None,
611
- name=None,
612
- schema=None,
613
- trigram=None,
614
- tbl=None):
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 '.' not in table and schema:
717
+ if "." not in table and schema:
621
718
  table = "{}.{}".format(schema, table)
622
719
  if isinstance(columns, (list, set)):
623
- columns = ','.join([quote(c.lower()) for c in columns])
720
+ columns = ",".join([quote(c.lower()) for c in columns])
624
721
  else:
625
722
  columns = quote(columns)
626
- sql = ['CREATE']
723
+ sql = ["CREATE"]
627
724
  if unique:
628
- sql.append('UNIQUE')
629
- sql.append('INDEX')
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
- columns.replace(' ', '').replace(',', '_').replace('"', ''))
730
+ r"\([^)]*\)",
731
+ "",
732
+ columns.replace(" ", "").replace(",", "_").replace('"', ""),
733
+ )
635
734
  if trigram:
636
- sql.append('IDX__TRGM_{}_{}__{}'.format(table.replace('.', '_'),
637
- trigram.upper(), name))
735
+ sql.append(
736
+ "IDX__TRGM_{}_{}__{}".format(
737
+ table.replace(".", "_"), trigram.upper(), name
738
+ )
739
+ )
638
740
  else:
639
- sql.append('IDX__{}__{}'.format(table.replace('.', '_'), name))
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('USING')
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
- sql.append("lower({})".format(quote(column_name)))
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('{}_trgm_ops'.format(trigram.lower()))
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 ' '.join(sql), tuple(vals)
772
+ return " ".join(sql), tuple(vals)
667
773
 
668
774
  @classmethod
669
- def drop_index(cls,
670
- table=None,
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 = ','.join([quote(c.lower()) for c in columns])
779
+ columns = ",".join([quote(c.lower()) for c in columns])
679
780
  else:
680
781
  columns = quote(columns)
681
- sql = ['DROP']
682
- sql.append('INDEX IF EXISTS')
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
- columns.replace(' ', '').replace(',', '_').replace('"', ''))
787
+ r"\([^)]*\)",
788
+ "",
789
+ columns.replace(" ", "").replace(",", "_").replace('"', ""),
790
+ )
688
791
  if trigram:
689
- sql.append('IDX__TRGM_{}_{}__{}'.format(table.replace('.', '_'),
690
- trigram.upper(), name))
792
+ sql.append(
793
+ "IDX__TRGM_{}_{}__{}".format(
794
+ table.replace(".", "_"), trigram.upper(), name
795
+ )
796
+ )
691
797
  else:
692
- sql.append('IDX__{}__{}'.format(table.replace('.', '_'), name))
693
- return ' '.join(sql), tuple()
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('%s')
837
+ vals.append("%s")
708
838
  args.append(val)
709
839
 
710
- sql = ['INSERT INTO']
840
+ sql = ["INSERT INTO"]
711
841
  sql.append(quote(table))
712
- sql.append('(')
713
- sql.append(','.join(keys))
714
- sql.append(')')
715
- sql.append('VALUES')
716
- sql.append('(')
717
- sql.append(','.join(vals))
718
- sql.append(')')
719
- sql = ' '.join(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(cls,
724
- table,
725
- data,
726
- pk,
727
- left_join=None,
728
- inner_join=None,
729
- outer_join=None):
730
- alias = 'A'
731
- if ' ' in table:
732
- alias, table = table.split(' ')
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 = ['UPDATE']
735
-
736
- sql.append(quote(table))
737
- sql.append('SET')
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] == '@@' 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
- sql.append('{} = %s'.format(key))
747
- vals.append(val)
748
- connect = ','
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('FROM')
886
+ sql.append("FROM")
751
887
  sql.append(table)
752
- sql.append('AS')
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('LEFT JOIN')
892
+ sql.append("LEFT JOIN")
757
893
  sql.append(k)
758
- sql.append('ON')
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('OUTER JOIN')
898
+ sql.append("OUTER JOIN")
763
899
  sql.append(k)
764
- sql.append('ON')
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('INNER JOIN')
904
+ sql.append("INNER JOIN")
769
905
  sql.append(k)
770
- sql.append('ON')
906
+ sql.append("ON")
771
907
  sql.append(v)
772
- make_where(pk, sql, vals, is_join)
773
- return ' '.join(sql), tuple(vals)
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
- if v is int:
792
- return cls.TYPES.INTEGER
793
- if v > 2147483647 or v < -2147483648:
794
- return cls.TYPES.BIGINT
795
- else:
796
- return cls.TYPES.INTEGER
797
- elif isinstance(v, float) \
798
- or v is float:
799
- return cls.TYPES.NUMERIC + '(19, 6)'
800
- elif isinstance(v, decimal.Decimal) \
801
- or v is decimal.Decimal:
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 (v, datetime.date) \
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 (v, bytes) \
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
- if v is int:
838
- return cls.TYPES.INTEGER
839
- if v > 2147483647 or v < -2147483648:
840
- return cls.TYPES.BIGINT
841
- else:
842
- return cls.TYPES.INTEGER
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 (v, datetime.datetime) \
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 (v, datetime.date) \
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 (v, bytes) \
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 exceptions.DbTableMissingError
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 = 'NOT NULL' if not null_allowed else ''
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('<>!=%', '', key.lower())
922
- sql.append("ALTER TABLE {} ADD {} {} {};".format(
923
- quote(table), quote(key), cls.get_type(val), null))
924
- return '\n\t'.join(sql), tuple()
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('<>!=%', '', key.lower())
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 '\n\t'.join(sql), tuple()
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('NOT NULL')
944
- return '\n\t'.join(sql), tuple()
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 ' '.join(sql), tuple()
1076
+ return " ".join(sql), tuple()
951
1077
 
952
1078
  @classmethod
953
1079
  def rename_column(cls, table, orig, new):
954
- return "ALTER TABLE {} RENAME COLUMN {} TO {};".format(
955
- quote(table), quote(orig), quote(new)), tuple()
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 find_duplicates(cls, table, columns, key):
976
- if isinstance(columns, str):
977
- columns = [columns]
978
- return """
979
- SELECT {2}
980
- FROM (SELECT {2},
981
- ROW_NUMBER() OVER (partition BY {1} ORDER BY {2}) AS rnum
982
- FROM {0}) t
983
- WHERE t.rnum > 1;
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 = ['DELETE FROM {}'.format(table)]
1116
+ sql = ["DELETE FROM {}".format(table)]
1002
1117
  vals = []
1003
1118
  make_where(where, sql, vals)
1004
- return ' '.join(sql), tuple(vals)
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 = ['CREATE']
1127
+ sql = ["CREATE"]
1013
1128
  if silent:
1014
- sql.append('OR REPLACE')
1129
+ sql.append("OR REPLACE")
1015
1130
  if temp:
1016
- sql.append('TEMPORARY')
1017
- sql.append('VIEW')
1131
+ sql.append("TEMPORARY")
1132
+ sql.append("VIEW")
1018
1133
  sql.append(name)
1019
- sql.append('AS')
1134
+ sql.append("AS")
1020
1135
  sql.append(query)
1021
- return ' '.join(sql), tuple()
1136
+ return " ".join(sql), tuple()
1022
1137
 
1023
1138
  @classmethod
1024
1139
  def drop_view(cls, name, silent=True):
1025
- sql = ['DROP VIEW']
1140
+ sql = ["DROP VIEW"]
1026
1141
  if silent:
1027
- sql.append('IF EXISTS')
1142
+ sql.append("IF EXISTS")
1028
1143
  sql.append(name)
1029
- return ' '.join(sql), tuple()
1144
+ return " ".join(sql), tuple()
1030
1145
 
1031
1146
  @classmethod
1032
- def alter_trigger(cls, table, state='ENABLE', name='USER'):
1033
- return 'ALTER TABLE {} {} TRIGGER {}'.format(table, state,
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 "SELECT SETVAL(PG_GET_SERIAL_SEQUENCE('{0}', 'sys_id'),{1},FALSE)".format(
1039
- table, next_value), tuple()
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='SYS_ID', where=None):
1160
+ def missing(cls, table, list, column="SYS_ID", where=None):
1043
1161
  sql = [
1044
- 'SELECT * FROM',
1162
+ "SELECT * FROM",
1045
1163
  f"UNNEST('{{{','.join([str(x) for x in list])}}}'::int[]) id",
1046
- 'EXCEPT ALL',
1164
+ "EXCEPT ALL",
1047
1165
  f"SELECT {column} FROM {table}",
1048
1166
  ]
1049
1167
  vals = []
1050
1168
  make_where(where, sql, vals)
1051
- return ' '.join(sql), tuple(vals)
1169
+ return " ".join(sql), tuple(vals)
1052
1170
 
1053
1171
  class TYPES(object):
1054
- TEXT = 'TEXT'
1055
- INTEGER = 'INTEGER'
1056
- NUMERIC = 'NUMERIC'
1057
- DATETIME = 'TIMESTAMP WITHOUT TIME ZONE'
1058
- TIMESTAMP = 'TIMESTAMP WITHOUT TIME ZONE'
1059
- DATE = 'DATE'
1060
- TIME = 'TIME WITHOUT TIME ZONE'
1061
- BIGINT = 'BIGINT'
1062
- SMALLINT = 'SMALLINT'
1063
- BOOLEAN = 'BOOLEAN'
1064
- BINARY = 'BYTEA'
1065
- INTERVAL = 'INTERVAL'
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
- 'ADMIN',
1070
- 'ALIAS',
1071
- 'ALL',
1072
- 'ALLOCATE',
1073
- 'ANALYSE',
1074
- 'ANALYZE',
1075
- 'AND',
1076
- 'ANY',
1077
- 'ARE',
1078
- 'ARRAY',
1079
- 'AS',
1080
- 'ASC',
1081
- 'AUTHORIZATION',
1082
- 'BETWEEN',
1083
- 'BINARY',
1084
- 'BLOB',
1085
- 'BOTH',
1086
- 'BREADTH',
1087
- 'CALL',
1088
- 'CASCADED',
1089
- 'CASE',
1090
- 'CAST',
1091
- 'CATALOG',
1092
- 'CHECK',
1093
- 'CLOB',
1094
- 'COLLATE',
1095
- 'COLLATION',
1096
- 'COLUMN',
1097
- 'COMPLETION',
1098
- 'CONNECT',
1099
- 'CONNECTION',
1100
- 'CONSTRAINT',
1101
- 'CONSTRUCTOR',
1102
- 'CONTINUE',
1103
- 'CORRESPONDING',
1104
- 'CREATE',
1105
- 'CROSS',
1106
- 'CUBE',
1107
- 'CURRENT',
1108
- 'CURRENT_DATE',
1109
- 'CURRENT_PATH',
1110
- 'CURRENT_ROLE',
1111
- 'CURRENT_TIME',
1112
- 'CURRENT_TIMESTAMP',
1113
- 'CURRENT_USER',
1114
- 'DATA',
1115
- 'DATE',
1116
- 'DEFAULT',
1117
- 'DEFERRABLE',
1118
- 'DEPTH',
1119
- 'DEREF',
1120
- 'DESC',
1121
- 'DESCRIBE',
1122
- 'DESCRIPTOR',
1123
- 'DESTROY',
1124
- 'DESTRUCTOR',
1125
- 'DETERMINISTIC',
1126
- 'DIAGNOSTICS',
1127
- 'DICTIONARY',
1128
- 'DISCONNECT',
1129
- 'DISTINCT',
1130
- 'DO',
1131
- 'DYNAMIC',
1132
- 'ELSE',
1133
- 'END',
1134
- 'END-EXEC',
1135
- 'EQUALS',
1136
- 'EVERY',
1137
- 'EXCEPT',
1138
- 'EXCEPTION',
1139
- 'EXEC',
1140
- 'FALSE',
1141
- 'FIRST',
1142
- 'FOR',
1143
- 'FOREIGN',
1144
- 'FOUND',
1145
- 'FREE',
1146
- 'FREEZE',
1147
- 'FROM',
1148
- 'FULL',
1149
- 'GENERAL',
1150
- 'GO',
1151
- 'GOTO',
1152
- 'GRANT',
1153
- 'GROUP',
1154
- 'GROUPING',
1155
- 'HAVING',
1156
- 'HOST',
1157
- 'IDENTITY',
1158
- 'IGNORE',
1159
- 'ILIKE',
1160
- 'IN',
1161
- 'INDICATOR',
1162
- 'INITIALIZE',
1163
- 'INITIALLY',
1164
- 'INNER',
1165
- 'INTERSECT',
1166
- 'INTO',
1167
- 'IS',
1168
- 'ISNULL',
1169
- 'ITERATE',
1170
- 'JOIN',
1171
- 'LARGE',
1172
- 'LAST',
1173
- 'LATERAL',
1174
- 'LEADING',
1175
- 'LEFT',
1176
- 'LESS',
1177
- 'LIKE',
1178
- 'LIMIT',
1179
- 'LOCALTIME',
1180
- 'LOCALTIMESTAMP',
1181
- 'LOCATOR',
1182
- 'MAP',
1183
- 'MODIFIES',
1184
- 'MODIFY',
1185
- 'MODULE',
1186
- 'NAME',
1187
- 'NATURAL',
1188
- 'NCLOB',
1189
- 'NEW',
1190
- 'NOT',
1191
- 'NOTNULL',
1192
- 'NULL',
1193
- 'OBJECT',
1194
- 'OFF',
1195
- 'OFFSET',
1196
- 'OLD',
1197
- 'ON',
1198
- 'ONLY',
1199
- 'OPEN',
1200
- 'OPERATION',
1201
- 'OR',
1202
- 'ORDER',
1203
- 'ORDINALITY',
1204
- 'OUTER',
1205
- 'OUTPUT',
1206
- 'OVERLAPS',
1207
- 'PAD',
1208
- 'PARAMETER',
1209
- 'PARAMETERS',
1210
- 'PLACING',
1211
- 'POSTFIX',
1212
- 'PREFIX',
1213
- 'PREORDER',
1214
- 'PRESERVE',
1215
- 'PRIMARY',
1216
- 'PUBLIC',
1217
- 'READS',
1218
- 'RECURSIVE',
1219
- 'REF',
1220
- 'REFERENCES',
1221
- 'REFERENCING',
1222
- 'RESULT',
1223
- 'RETURN',
1224
- 'RIGHT',
1225
- 'ROLE',
1226
- 'ROLLUP',
1227
- 'ROUTINE',
1228
- 'ROWS',
1229
- 'SAVEPOINT',
1230
- 'SCOPE',
1231
- 'SEARCH',
1232
- 'SECTION',
1233
- 'SELECT',
1234
- 'SESSION_USER',
1235
- 'SETS',
1236
- 'SIMILAR',
1237
- 'SIZE',
1238
- 'SOME',
1239
- 'SPACE',
1240
- 'SPECIFIC',
1241
- 'SPECIFICTYPE',
1242
- 'SQL',
1243
- 'SQLCODE',
1244
- 'SQLERROR',
1245
- 'SQLEXCEPTION',
1246
- 'SQLSTATE',
1247
- 'SQLWARNING',
1248
- 'STATE',
1249
- 'STATIC',
1250
- 'STRUCTURE',
1251
- 'SYSTEM_USER',
1252
- 'TABLE',
1253
- 'TERMINATE',
1254
- 'THAN',
1255
- 'THEN',
1256
- 'TIMESTAMP',
1257
- 'TIMEZONE_HOUR',
1258
- 'TIMEZONE_MINUTE',
1259
- 'TO',
1260
- 'TRAILING',
1261
- 'TRANSLATION',
1262
- 'TRUE',
1263
- 'UNDER',
1264
- 'UNION',
1265
- 'UNIQUE',
1266
- 'UNNEST',
1267
- 'USER',
1268
- 'USING',
1269
- 'VALUE',
1270
- 'VARIABLE',
1271
- 'VERBOSE',
1272
- 'WHEN',
1273
- 'WHENEVER',
1274
- 'WHERE',
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
  ]