velocity-python 0.0.34__py3-none-any.whl → 0.0.64__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.

Files changed (33) hide show
  1. velocity/__init__.py +1 -1
  2. velocity/db/core/column.py +25 -105
  3. velocity/db/core/database.py +79 -23
  4. velocity/db/core/decorators.py +84 -47
  5. velocity/db/core/engine.py +179 -184
  6. velocity/db/core/result.py +94 -49
  7. velocity/db/core/row.py +81 -46
  8. velocity/db/core/sequence.py +112 -22
  9. velocity/db/core/table.py +660 -243
  10. velocity/db/core/transaction.py +75 -77
  11. velocity/db/servers/mysql.py +5 -237
  12. velocity/db/servers/mysql_reserved.py +237 -0
  13. velocity/db/servers/postgres/__init__.py +19 -0
  14. velocity/db/servers/postgres/operators.py +23 -0
  15. velocity/db/servers/postgres/reserved.py +254 -0
  16. velocity/db/servers/postgres/sql.py +1041 -0
  17. velocity/db/servers/postgres/types.py +109 -0
  18. velocity/db/servers/sqlite.py +1 -210
  19. velocity/db/servers/sqlite_reserved.py +208 -0
  20. velocity/db/servers/sqlserver.py +1 -316
  21. velocity/db/servers/sqlserver_reserved.py +314 -0
  22. velocity/db/servers/tablehelper.py +277 -0
  23. velocity/misc/conv/iconv.py +277 -91
  24. velocity/misc/conv/oconv.py +5 -4
  25. velocity/misc/db.py +2 -2
  26. velocity/misc/format.py +2 -2
  27. {velocity_python-0.0.34.dist-info → velocity_python-0.0.64.dist-info}/METADATA +6 -6
  28. velocity_python-0.0.64.dist-info/RECORD +47 -0
  29. {velocity_python-0.0.34.dist-info → velocity_python-0.0.64.dist-info}/WHEEL +1 -1
  30. velocity/db/servers/postgres.py +0 -1396
  31. velocity_python-0.0.34.dist-info/RECORD +0 -39
  32. {velocity_python-0.0.34.dist-info → velocity_python-0.0.64.dist-info}/LICENSE +0 -0
  33. {velocity_python-0.0.34.dist-info → velocity_python-0.0.64.dist-info}/top_level.txt +0 -0
@@ -1,1396 +0,0 @@
1
- import psycopg2
2
- import re
3
- import os
4
- import hashlib
5
- import decimal
6
- import datetime
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
-
36
-
37
- def make_where(where, sql, vals, is_join=False):
38
- if not where:
39
- return
40
- sql.append("WHERE")
41
- if isinstance(where, str):
42
- sql.append(where)
43
- return
44
- if isinstance(where, dict):
45
- where = list(where.items())
46
- if not isinstance(where, list):
47
- raise Exception("Parameter `where` is not a valid datatype.")
48
- alias = "A"
49
- if is_join and isinstance(is_join, str):
50
- alias = is_join
51
- connect = ""
52
- for key, val in where:
53
- if connect:
54
- sql.append(connect)
55
- if is_join:
56
- if "." not in key:
57
- key = alias + "." + quote(key.lower())
58
- if val == None:
59
- if "!" in key:
60
- key = key.replace("!", "")
61
- sql.append("{} is not NULL".format(key))
62
- else:
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))
68
- vals.append(tuple(val))
69
- else:
70
- sql.append("{} in %s".format(key))
71
- vals.append(tuple(val))
72
- elif isinstance(val, Query):
73
- sql.append("{} in ({})".format(key, val))
74
- else:
75
- case = None
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 = "="
144
- else:
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))
154
- vals.extend(val)
155
- else:
156
- if case:
157
- sql.append("{2}({0}) {1} {2}(%s)".format(key, op, case))
158
- else:
159
- sql.append("{0} {1} %s".format(key, op))
160
- vals.append(val)
161
- connect = "AND"
162
-
163
-
164
- def quote(data):
165
- if isinstance(data, list):
166
- new = []
167
- for item in data:
168
- if "@@" in item:
169
- new.append(item[2:])
170
- else:
171
- new.append(quote(item))
172
- return new
173
- else:
174
- parts = data.split(".")
175
- new = []
176
- for part in parts:
177
- if '"' in part:
178
- new.append(part)
179
- elif part.upper() in reserved_words:
180
- new.append('"' + part + '"')
181
- elif re.findall("[/]", part):
182
- new.append('"' + part + '"')
183
- else:
184
- new.append(part)
185
- return ".".join(new)
186
-
187
-
188
- class SQL(object):
189
- server = "PostGreSQL"
190
- type_column_identifier = "data_type"
191
- is_nullable = "is_nullable"
192
-
193
- default_schema = "public"
194
-
195
- ApplicationErrorCodes = ["22P02", "42883"]
196
-
197
- DatabaseMissingErrorCodes = []
198
- TableMissingErrorCodes = ["42P01"]
199
- ColumnMissingErrorCodes = ["42703"]
200
- ForeignKeyMissingErrorCodes = ["42704"]
201
-
202
- ConnectionErrorCodes = ["08001", "08S01"]
203
- DuplicateKeyErrorCodes = [] # Handled in regex check.
204
- RetryTransactionCodes = []
205
- TruncationErrorCodes = []
206
- LockTimeoutErrorCodes = ["55P03"]
207
- DatabaseObjectExistsErrorCodes = ["42710", "42P07", "42P04"]
208
- DataIntegrityErrorCodes = ["23503"]
209
-
210
- @classmethod
211
- def version(cls):
212
- return "select version()", tuple()
213
-
214
- @classmethod
215
- def timestamp(cls):
216
- return "select current_timestamp", tuple()
217
-
218
- @classmethod
219
- def user(cls):
220
- return "select current_user", tuple()
221
-
222
- @classmethod
223
- def databases(cls):
224
- return "select datname from pg_database where datistemplate = false", tuple()
225
-
226
- @classmethod
227
- def schemas(cls):
228
- return "select schema_name from information_schema.schemata", tuple()
229
-
230
- @classmethod
231
- def current_schema(cls):
232
- return "select current_schema", tuple()
233
-
234
- @classmethod
235
- def current_database(cls):
236
- return "select current_database()", tuple()
237
-
238
- @classmethod
239
- def tables(cls, system=False):
240
- if system:
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(),
244
- )
245
- else:
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(),
249
- )
250
-
251
- @classmethod
252
- def views(cls, system=False):
253
- if system:
254
- return (
255
- "select table_schema, table_name from information_schema.views order by table_schema,table_name",
256
- tuple(),
257
- )
258
- else:
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(),
262
- )
263
-
264
- @classmethod
265
- def __has_pointer(cls, columns):
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
274
- return False
275
-
276
- @classmethod
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
- ):
291
- if not table:
292
- raise Exception("Table name required")
293
- is_join = False
294
-
295
- if isinstance(columns, str) and "distinct" in columns.lower():
296
- sql = [
297
- "SELECT",
298
- columns,
299
- "FROM",
300
- quote(table),
301
- ]
302
- elif cls.__has_pointer(columns):
303
- is_join = True
304
- if isinstance(columns, str):
305
- columns = columns.split(",")
306
- letter = 65
307
- tables = {table: chr(letter)}
308
- letter += 1
309
- __select = []
310
- __from = ["{} AS {}".format(quote(table), tables.get(table))]
311
- __left_join = []
312
-
313
- for column in columns:
314
- if "@@" in column:
315
- __select.append(column[2:])
316
- elif ">" in column:
317
- parts = column.split(">")
318
- foreign = tbl.foreign_key_info(parts[0])
319
- if not foreign:
320
- raise exceptions.DbApplicationError("Foreign key not defined")
321
- ref_table = foreign["referenced_table_name"]
322
- ref_schema = foreign["referenced_table_schema"]
323
- ref_column = foreign["referenced_column_name"]
324
- lookup = "{}:{}".format(ref_table, parts[0])
325
- if lookup in tables:
326
- __select.append(
327
- '{}."{}" as "{}"'.format(
328
- tables.get(lookup), parts[1], "_".join(parts)
329
- )
330
- )
331
- else:
332
- tables[lookup] = chr(letter)
333
- letter += 1
334
- __select.append(
335
- '{}."{}" as "{}"'.format(
336
- tables.get(lookup), parts[1], "_".join(parts)
337
- )
338
- )
339
- __left_join.append(
340
- 'LEFT OUTER JOIN "{}"."{}" AS {}'.format(
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
- )
352
- if orderby and column in orderby:
353
- orderby = orderby.replace(
354
- column, "{}.{}".format(tables.get(lookup), parts[1])
355
- )
356
-
357
- else:
358
- if "(" in column:
359
- __select.append(column)
360
- else:
361
- __select.append("{}.{}".format(tables.get(table), column))
362
- sql = ["SELECT"]
363
- sql.append(",".join(__select))
364
- sql.append("FROM")
365
- sql.extend(__from)
366
- sql.extend(__left_join)
367
- else:
368
- if columns:
369
- if isinstance(columns, str):
370
- columns = columns.split(",")
371
- if isinstance(columns, list):
372
- columns = quote(columns)
373
- columns = ",".join(columns)
374
- else:
375
- columns = "*"
376
- sql = [
377
- "SELECT",
378
- columns,
379
- "FROM",
380
- quote(table),
381
- ]
382
- vals = []
383
- make_where(where, sql, vals, is_join)
384
- if groupby:
385
- sql.append("GROUP BY")
386
- if isinstance(groupby, (list, tuple)):
387
- groupby = ",".join(groupby)
388
- sql.append(groupby)
389
- if having:
390
- sql.append("HAVING")
391
- if isinstance(having, (list, tuple)):
392
- having = ",".join(having)
393
- sql.append(having)
394
- if orderby:
395
- sql.append("ORDER BY")
396
- if isinstance(orderby, (list, tuple)):
397
- orderby = ",".join(orderby)
398
- sql.append(orderby)
399
- if start and qty:
400
- sql.append("OFFSET {} ROWS FETCH NEXT {} ROWS ONLY".format(start, qty))
401
- elif start:
402
- sql.append("OFFSET {} ROWS".format(start))
403
- elif qty:
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)
410
- return sql, tuple(vals)
411
-
412
- @classmethod
413
- def create_database(cls, name):
414
- return "create database " + name, tuple()
415
-
416
- @classmethod
417
- def last_id(cls, 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
- )
426
-
427
- @classmethod
428
- def set_id(cls, table, start):
429
- return "SELECT SETVAL(PG_GET_SERIAL_SEQUENCE(%s, 'sys_id'), %s)", tuple(
430
- [table, start]
431
- )
432
-
433
- @classmethod
434
- def drop_database(cls, name):
435
- return "drop database if exists " + name, tuple()
436
-
437
- @classmethod
438
- def create_table(cls, name, columns={}, drop=False):
439
- if "." in name:
440
- fqtn = name
441
- else:
442
- fqtn = "public." + name
443
- schema, table = fqtn.split(".")
444
- name = fqtn.replace(".", "_")
445
- trigger = "".format(name)
446
- sql = []
447
- if drop:
448
- sql.append(cls.drop_table(fqtn)[0])
449
- sql.append(
450
- """
451
- CREATE TABLE {0} (
452
- sys_id BIGSERIAL PRIMARY KEY,
453
- sys_modified 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
459
- );
460
-
461
- SELECT SETVAL(PG_GET_SERIAL_SEQUENCE('{0}', 'sys_id'),1000,TRUE);
462
-
463
- CREATE OR REPLACE FUNCTION {1}.on_sys_modified()
464
- RETURNS TRIGGER AS
465
- $BODY$
466
- BEGIN
467
- -- update sys_modified on each insert/update.
468
- NEW.sys_modified := now();
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;
477
- RETURN NEW;
478
- END;
479
- $BODY$
480
- LANGUAGE plpgsql VOLATILE
481
- COST 100;
482
-
483
- CREATE TRIGGER on_update_row_{3}
484
- BEFORE INSERT OR UPDATE ON {0}
485
- FOR EACH ROW EXECUTE PROCEDURE {1}.on_sys_modified();
486
-
487
- """.format(
488
- fqtn, schema, table, fqtn.replace(".", "_")
489
- )
490
- )
491
-
492
- for key, val in columns.items():
493
- key = re.sub("<>!=%", "", key.lower())
494
- if key in system_fields:
495
- continue
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()
502
-
503
- @classmethod
504
- def drop_table(cls, name):
505
- return "drop table if exists %s cascade;" % quote(name), tuple()
506
-
507
- @classmethod
508
- def drop_column(cls, table, name, cascade=True):
509
- if cascade:
510
- return (
511
- "ALTER TABLE %s DROP COLUMN %s CASCADE" % (quote(table), quote(name)),
512
- tuple(),
513
- )
514
- else:
515
- return (
516
- "ALTER TABLE %s DROP COLUMN %s " % (quote(table), quote(name)),
517
- tuple(),
518
- )
519
-
520
- @classmethod
521
- def columns(cls, name):
522
- if "." in name:
523
- return """
524
- select column_name
525
- from information_schema.columns
526
- where UPPER(table_schema) = UPPER(%s)
527
- and UPPER(table_name) = UPPER(%s)
528
- """, tuple(
529
- name.split(".")
530
- )
531
- else:
532
- return """
533
- select column_name
534
- from information_schema.columns
535
- where UPPER(table_name) = UPPER(%s)
536
- """, tuple(
537
- [
538
- name,
539
- ]
540
- )
541
-
542
- @classmethod
543
- def column_info(cls, table, name):
544
- params = table.split(".")
545
- params.append(name)
546
- if "." in table:
547
- return """
548
- select *
549
- from information_schema.columns
550
- where UPPER(table_schema ) = UPPER(%s)
551
- and UPPER(table_name) = UPPER(%s)
552
- and UPPER(column_name) = UPPER(%s)
553
- """, tuple(
554
- params
555
- )
556
- else:
557
- return """
558
- select *
559
- from information_schema.columns
560
- where UPPER(table_name) = UPPER(%s)
561
- and UPPER(column_name) = UPPER(%s)
562
- """, tuple(
563
- params
564
- )
565
-
566
- @classmethod
567
- def primary_keys(cls, table):
568
- params = table.split(".")
569
- params.reverse()
570
- if "." in table:
571
- return """
572
- SELECT
573
- pg_attribute.attname
574
- FROM pg_index, pg_class, pg_attribute, pg_namespace
575
- WHERE
576
- pg_class.oid = %s::regclass AND
577
- indrelid = pg_class.oid AND
578
- nspname = %s AND
579
- pg_class.relnamespace = pg_namespace.oid AND
580
- pg_attribute.attrelid = pg_class.oid AND
581
- pg_attribute.attnum = any(pg_index.indkey)
582
- AND indisprimary
583
- """, tuple(
584
- params
585
- )
586
- else:
587
- return """
588
- SELECT
589
- pg_attribute.attname
590
- FROM pg_index, pg_class, pg_attribute, pg_namespace
591
- WHERE
592
- pg_class.oid = %s::regclass AND
593
- indrelid = pg_class.oid AND
594
- pg_class.relnamespace = pg_namespace.oid AND
595
- pg_attribute.attrelid = pg_class.oid AND
596
- pg_attribute.attnum = any(pg_index.indkey)
597
- AND indisprimary
598
- """, tuple(
599
- params
600
- )
601
-
602
- @classmethod
603
- def foreign_key_info(cls, table=None, column=None, schema=None):
604
- if "." in table:
605
- schema, table = table.split(".")
606
-
607
- sql = [
608
- """
609
- SELECT
610
- KCU1.CONSTRAINT_NAME AS "FK_CONSTRAINT_NAME"
611
- , KCU1.CONSTRAINT_SCHEMA AS "FK_CONSTRAINT_SCHEMA"
612
- , KCU1.CONSTRAINT_CATALOG AS "FK_CONSTRAINT_CATALOG"
613
- , KCU1.TABLE_NAME AS "FK_TABLE_NAME"
614
- , KCU1.COLUMN_NAME AS "FK_COLUMN_NAME"
615
- , KCU1.ORDINAL_POSITION AS "FK_ORDINAL_POSITION"
616
- , KCU2.CONSTRAINT_NAME AS "UQ_CONSTRAINT_NAME"
617
- , KCU2.CONSTRAINT_SCHEMA AS "UQ_CONSTRAINT_SCHEMA"
618
- , KCU2.CONSTRAINT_CATALOG AS "UQ_CONSTRAINT_CATALOG"
619
- , KCU2.TABLE_NAME AS "UQ_TABLE_NAME"
620
- , KCU2.COLUMN_NAME AS "UQ_COLUMN_NAME"
621
- , KCU2.ORDINAL_POSITION AS "UQ_ORDINAL_POSITION"
622
- , KCU1.CONSTRAINT_NAME AS "CONSTRAINT_NAME"
623
- , KCU2.CONSTRAINT_SCHEMA AS "REFERENCED_TABLE_SCHEMA"
624
- , KCU2.TABLE_NAME AS "REFERENCED_TABLE_NAME"
625
- , KCU2.COLUMN_NAME AS "REFERENCED_COLUMN_NAME"
626
- FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
627
- JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
628
- ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
629
- AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
630
- AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
631
- JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
632
- ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG
633
- AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
634
- AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME
635
- AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
636
- """
637
- ]
638
- vals = []
639
- where = {}
640
- if schema:
641
- where["LOWER(KCU1.CONSTRAINT_SCHEMA)"] = schema.lower()
642
- if table:
643
- where["LOWER(KCU1.TABLE_NAME)"] = table.lower()
644
- if column:
645
- where["LOWER(KCU1.COLUMN_NAME)"] = column.lower()
646
- make_where(where, sql, vals)
647
- return " ".join(sql), tuple(vals)
648
-
649
- @classmethod
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:
654
- table = "{}.{}".format(schema, table)
655
- if isinstance(key_to_columns, str):
656
- key_to_columns = [key_to_columns]
657
- if isinstance(columns, str):
658
- columns = [columns]
659
- if not name:
660
- m = hashlib.md5()
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()
666
- sql = "ALTER TABLE {} ADD CONSTRAINT {} FOREIGN KEY ({}) REFERENCES {} ({});".format(
667
- table, name, ",".join(columns), key_to_table, ",".join(key_to_columns)
668
- )
669
-
670
- return sql, tuple()
671
-
672
- @classmethod
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:
683
- table = "{}.{}".format(schema, table)
684
- if isinstance(key_to_columns, str):
685
- key_to_columns = [key_to_columns]
686
- if isinstance(columns, str):
687
- columns = [columns]
688
- if not name:
689
- m = hashlib.md5()
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()
695
- sql = "ALTER TABLE {} DROP CONSTRAINT {};".format(table, name)
696
- return sql, tuple()
697
-
698
- @classmethod
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
- ):
712
- """
713
- The following statements must be executed on the database instance once to enable respective trigram features.
714
- CREATE EXTENSION pg_trgm; is required to use gin.
715
- CREATE EXTENSION btree_gist; is required to use gist
716
- """
717
- if "." not in table and schema:
718
- table = "{}.{}".format(schema, table)
719
- if isinstance(columns, (list, set)):
720
- columns = ",".join([quote(c.lower()) for c in columns])
721
- else:
722
- columns = quote(columns)
723
- sql = ["CREATE"]
724
- if unique:
725
- sql.append("UNIQUE")
726
- sql.append("INDEX")
727
- tablename = quote(table)
728
- if not name:
729
- name = re.sub(
730
- r"\([^)]*\)",
731
- "",
732
- columns.replace(" ", "").replace(",", "_").replace('"', ""),
733
- )
734
- if trigram:
735
- sql.append(
736
- "IDX__TRGM_{}_{}__{}".format(
737
- table.replace(".", "_"), trigram.upper(), name
738
- )
739
- )
740
- else:
741
- sql.append("IDX__{}__{}".format(table.replace(".", "_"), name))
742
- sql.append("ON")
743
- sql.append(quote(tablename))
744
-
745
- if trigram:
746
- sql.append("USING")
747
- sql.append(trigram)
748
- sql.append("(")
749
- if tbl:
750
- join = ""
751
- for column_name in columns.split(","):
752
- column_name = column_name.replace('"', "")
753
- if join:
754
- sql.append(join)
755
- column = tbl.column(column_name)
756
- if column.py_type == str:
757
- if lower:
758
- sql.append("lower({})".format(quote(column_name)))
759
- else:
760
- sql.append(quote(column_name))
761
- else:
762
- sql.append(quote(column_name))
763
- join = ","
764
- else:
765
- sql.append(columns)
766
- if trigram:
767
- sql.append("{}_trgm_ops".format(trigram.lower()))
768
- sql.append(")")
769
- vals = []
770
-
771
- make_where(where, sql, vals)
772
- return " ".join(sql), tuple(vals)
773
-
774
- @classmethod
775
- def drop_index(cls, table=None, columns=None, name=None, schema=None, trigram=None):
776
- if "." not in table and schema:
777
- table = "{}.{}".format(schema, table)
778
- if isinstance(columns, (list, set)):
779
- columns = ",".join([quote(c.lower()) for c in columns])
780
- else:
781
- columns = quote(columns)
782
- sql = ["DROP"]
783
- sql.append("INDEX IF EXISTS")
784
- tablename = quote(table)
785
- if not name:
786
- name = re.sub(
787
- r"\([^)]*\)",
788
- "",
789
- columns.replace(" ", "").replace(",", "_").replace('"', ""),
790
- )
791
- if trigram:
792
- sql.append(
793
- "IDX__TRGM_{}_{}__{}".format(
794
- table.replace(".", "_"), trigram.upper(), name
795
- )
796
- )
797
- else:
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)
826
-
827
- @classmethod
828
- def insert(cls, table, data):
829
- keys = []
830
- vals = []
831
- args = []
832
- for key, val in data.items():
833
- keys.append(quote(key.lower()))
834
- if isinstance(val, str) and len(val) > 2 and val[:2] == "@@" and val[2:]:
835
- vals.append(val[2:])
836
- else:
837
- vals.append("%s")
838
- args.append(val)
839
-
840
- sql = ["INSERT INTO"]
841
- sql.append(quote(table))
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)
850
- return sql, tuple(args)
851
-
852
- @classmethod
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(" ")
866
- is_join = bool(left_join or inner_join or outer_join)
867
- sql = ["UPDATE"]
868
- if not excluded:
869
- sql.append(quote(table))
870
- sql.append("SET")
871
- vals = []
872
- connect = ""
873
- for key, val in data.items():
874
- if connect:
875
- sql.append(connect)
876
- if isinstance(val, str) and val[:2] == "@@" and val[2:]:
877
- sql.append("{} = {}".format(key, val[2:]))
878
- else:
879
- if excluded:
880
- sql.append("{} = EXCLUDED.{}".format(key, key))
881
- else:
882
- sql.append("{} = %s".format(key))
883
- vals.append(val)
884
- connect = ","
885
- if is_join:
886
- sql.append("FROM")
887
- sql.append(table)
888
- sql.append("AS")
889
- sql.append(alias)
890
- if left_join:
891
- for k, v in left_join.items():
892
- sql.append("LEFT JOIN")
893
- sql.append(k)
894
- sql.append("ON")
895
- sql.append(v)
896
- if outer_join:
897
- for k, v in outer_join.items():
898
- sql.append("OUTER JOIN")
899
- sql.append(k)
900
- sql.append("ON")
901
- sql.append(v)
902
- if inner_join:
903
- for k, v in inner_join.items():
904
- sql.append("INNER JOIN")
905
- sql.append(k)
906
- sql.append("ON")
907
- sql.append(v)
908
- if not excluded:
909
- make_where(pk, sql, vals, is_join)
910
- return " ".join(sql), tuple(vals)
911
-
912
- @classmethod
913
- def get_type(cls, v):
914
- if isinstance(v, str):
915
- if v[:2] == "@@":
916
- return v[2:] or cls.TYPES.TEXT
917
- elif isinstance(v, str) or v is str:
918
- return cls.TYPES.TEXT
919
- elif isinstance(v, bool) or v is bool:
920
- return cls.TYPES.BOOLEAN
921
- elif isinstance(v, int) or v is int:
922
- return cls.TYPES.BIGINT
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:
936
- return cls.TYPES.DATETIME
937
- elif isinstance(v, datetime.date) or v is datetime.date:
938
- return cls.TYPES.DATE
939
- elif isinstance(v, datetime.time) or v is datetime.time:
940
- return cls.TYPES.TIME
941
- elif isinstance(v, datetime.timedelta) or v is datetime.timedelta:
942
- return cls.TYPES.INTERVAL
943
- elif isinstance(v, bytes) or v is bytes:
944
- return cls.TYPES.BINARY
945
- # Everything else defaults to TEXT, incl. None
946
- return cls.TYPES.TEXT
947
-
948
- @classmethod
949
- def get_conv(cls, v):
950
- if isinstance(v, str):
951
- if v[:2] == "@@":
952
- return v[2:] or cls.TYPES.TEXT
953
- elif isinstance(v, str) or v is str:
954
- return cls.TYPES.TEXT
955
- elif isinstance(v, bool) or v is bool:
956
- return cls.TYPES.BOOLEAN
957
- elif isinstance(v, int) or v is int:
958
- return cls.TYPES.BIGINT
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:
968
- return cls.TYPES.NUMERIC
969
- elif isinstance(v, decimal.Decimal) or v is decimal.Decimal:
970
- return cls.TYPES.NUMERIC
971
- elif isinstance(v, datetime.datetime) or v is datetime.datetime:
972
- return cls.TYPES.DATETIME
973
- elif isinstance(v, datetime.date) or v is datetime.date:
974
- return cls.TYPES.DATE
975
- elif isinstance(v, datetime.time) or v is datetime.time:
976
- return cls.TYPES.TIME
977
- elif isinstance(v, datetime.timedelta) or v is datetime.timedelta:
978
- return cls.TYPES.INTERVAL
979
- elif isinstance(v, bytes) or v is bytes:
980
- return cls.TYPES.BINARY
981
- # Everything else defaults to TEXT, incl. None
982
- return cls.TYPES.TEXT
983
-
984
- @classmethod
985
- def py_type(cls, v):
986
- v = str(v).upper()
987
- if v == cls.TYPES.INTEGER:
988
- return int
989
- elif v == cls.TYPES.SMALLINT:
990
- return int
991
- elif v == cls.TYPES.BIGINT:
992
- return int
993
- elif v == cls.TYPES.NUMERIC:
994
- return decimal.Decimal
995
- elif v == cls.TYPES.TEXT:
996
- return str
997
- elif v == cls.TYPES.BOOLEAN:
998
- return bool
999
- elif v == cls.TYPES.DATE:
1000
- return datetime.date
1001
- elif v == cls.TYPES.TIME:
1002
- return datetime.time
1003
- elif v == cls.TYPES.TIME_TZ:
1004
- return datetime.time
1005
- elif v == cls.TYPES.DATETIME:
1006
- return datetime.datetime
1007
- elif v == cls.TYPES.INTERVAL:
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
1013
- else:
1014
- raise Exception("unmapped type %s" % v)
1015
-
1016
- @classmethod
1017
- def massage_data(cls, data):
1018
- """
1019
-
1020
- :param :
1021
- :param :
1022
- :param :
1023
- :returns:
1024
- """
1025
- data = {key.lower(): val for key, val in data.items()}
1026
- primaryKey = set(cls.GetPrimaryKeyColumnNames())
1027
- if not primaryKey:
1028
- if not cls.Exists():
1029
- raise DbTableMissingError
1030
- dataKeys = set(data.keys()).intersection(primaryKey)
1031
- dataColumns = set(data.keys()).difference(primaryKey)
1032
- pk = {}
1033
- pk.update([(k, data[k]) for k in dataKeys])
1034
- d = {}
1035
- d.update([(k, data[k]) for k in dataColumns])
1036
- return d, pk
1037
-
1038
- @classmethod
1039
- def alter_add(cls, table, columns, null_allowed=True):
1040
- sql = []
1041
- null = "NOT NULL" if not null_allowed else ""
1042
- if isinstance(columns, dict):
1043
- for key, val in columns.items():
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()
1051
-
1052
- @classmethod
1053
- def alter_drop(cls, table, columns):
1054
- sql = ["ALTER TABLE {} DROP COLUMN".format(quote(table))]
1055
- if isinstance(columns, dict):
1056
- for key, val in columns.items():
1057
- key = re.sub("<>!=%", "", key.lower())
1058
- sql.append("{},".format(key))
1059
- if sql[-1][-1] == ",":
1060
- sql[-1] = sql[-1][:-1]
1061
- return "\n\t".join(sql), tuple()
1062
-
1063
- @classmethod
1064
- def alter_column_by_type(cls, table, column, value, nullable=True):
1065
- sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
1066
- sql.append("{} TYPE {}".format(quote(column), cls.get_type(value)))
1067
- sql.append("USING {}::{}".format(quote(column), cls.get_conv(value)))
1068
- if not nullable:
1069
- sql.append("NOT NULL")
1070
- return "\n\t".join(sql), tuple()
1071
-
1072
- @classmethod
1073
- def alter_column_by_sql(cls, table, column, value):
1074
- sql = ["ALTER TABLE {} ALTER COLUMN".format(quote(table))]
1075
- sql.append("{} {}".format(quote(column), value))
1076
- return " ".join(sql), tuple()
1077
-
1078
- @classmethod
1079
- def rename_column(cls, table, orig, new):
1080
- return (
1081
- "ALTER TABLE {} RENAME COLUMN {} TO {};".format(
1082
- quote(table), quote(orig), quote(new)
1083
- ),
1084
- tuple(),
1085
- )
1086
-
1087
- @classmethod
1088
- def rename_table(cls, table, new):
1089
- return "ALTER TABLE {} RENAME TO {};".format(quote(table), quote(new)), tuple()
1090
-
1091
- @classmethod
1092
- def create_savepoint(cls, sp):
1093
- return 'SAVEPOINT "{}"'.format(sp), tuple()
1094
-
1095
- @classmethod
1096
- def release_savepoint(cls, sp):
1097
- return 'RELEASE SAVEPOINT "{}"'.format(sp), tuple()
1098
-
1099
- @classmethod
1100
- def rollback_savepoint(cls, sp):
1101
- return 'ROLLBACK TO SAVEPOINT "{}"'.format(sp), tuple()
1102
-
1103
- @classmethod
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
- )
1113
-
1114
- @classmethod
1115
- def delete(cls, table, where):
1116
- sql = ["DELETE FROM {}".format(table)]
1117
- vals = []
1118
- make_where(where, sql, vals)
1119
- return " ".join(sql), tuple(vals)
1120
-
1121
- @classmethod
1122
- def truncate(cls, table):
1123
- return "truncate table {}".format(quote(table)), tuple()
1124
-
1125
- @classmethod
1126
- def create_view(cls, name, query, temp=False, silent=True):
1127
- sql = ["CREATE"]
1128
- if silent:
1129
- sql.append("OR REPLACE")
1130
- if temp:
1131
- sql.append("TEMPORARY")
1132
- sql.append("VIEW")
1133
- sql.append(name)
1134
- sql.append("AS")
1135
- sql.append(query)
1136
- return " ".join(sql), tuple()
1137
-
1138
- @classmethod
1139
- def drop_view(cls, name, silent=True):
1140
- sql = ["DROP VIEW"]
1141
- if silent:
1142
- sql.append("IF EXISTS")
1143
- sql.append(name)
1144
- return " ".join(sql), tuple()
1145
-
1146
- @classmethod
1147
- def alter_trigger(cls, table, state="ENABLE", name="USER"):
1148
- return "ALTER TABLE {} {} TRIGGER {}".format(table, state, name), tuple()
1149
-
1150
- @classmethod
1151
- def set_sequence(cls, table, next_value):
1152
- return (
1153
- "SELECT SETVAL(PG_GET_SERIAL_SEQUENCE('{0}', 'sys_id'),{1},FALSE)".format(
1154
- table, next_value
1155
- ),
1156
- tuple(),
1157
- )
1158
-
1159
- @classmethod
1160
- def missing(cls, table, list, column="SYS_ID", where=None):
1161
- sql = [
1162
- "SELECT * FROM",
1163
- f"UNNEST('{{{','.join([str(x) for x in list])}}}'::int[]) id",
1164
- "EXCEPT ALL",
1165
- f"SELECT {column} FROM {table}",
1166
- ]
1167
- vals = []
1168
- make_where(where, sql, vals)
1169
- return " ".join(sql), tuple(vals)
1170
-
1171
- class TYPES(object):
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"
1187
-
1188
-
1189
- reserved_words = [
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",
1396
- ]