velocity-python 0.0.116__py3-none-any.whl → 0.0.118__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of velocity-python might be problematic. Click here for more details.
- velocity/__init__.py +3 -1
- velocity/aws/__init__.py +3 -0
- velocity/aws/amplify.py +10 -6
- velocity/aws/handlers/__init__.py +2 -0
- velocity/aws/handlers/base_handler.py +245 -0
- velocity/aws/handlers/context.py +58 -41
- velocity/aws/handlers/exceptions.py +16 -0
- velocity/aws/handlers/lambda_handler.py +15 -84
- velocity/aws/handlers/response.py +1 -1
- velocity/aws/handlers/sqs_handler.py +23 -144
- velocity/db/__init__.py +16 -1
- velocity/db/core/decorators.py +0 -1
- velocity/db/core/engine.py +33 -31
- velocity/db/core/exceptions.py +3 -0
- velocity/db/core/result.py +30 -24
- velocity/db/core/row.py +3 -1
- velocity/db/core/table.py +6 -5
- velocity/db/exceptions.py +35 -18
- velocity/db/servers/mysql.py +2 -3
- velocity/db/servers/postgres/__init__.py +10 -12
- velocity/db/servers/postgres/sql.py +36 -17
- velocity/db/servers/sqlite.py +2 -2
- velocity/db/servers/sqlserver.py +3 -3
- velocity/db/servers/tablehelper.py +117 -91
- velocity/db/utils.py +62 -47
- velocity/misc/conv/__init__.py +2 -0
- velocity/misc/conv/iconv.py +5 -4
- velocity/misc/export.py +1 -4
- velocity/misc/merge.py +1 -1
- velocity/misc/tools.py +0 -1
- {velocity_python-0.0.116.dist-info → velocity_python-0.0.118.dist-info}/METADATA +1 -1
- velocity_python-0.0.118.dist-info/RECORD +58 -0
- velocity_python-0.0.116.dist-info/RECORD +0 -56
- {velocity_python-0.0.116.dist-info → velocity_python-0.0.118.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.116.dist-info → velocity_python-0.0.118.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.0.116.dist-info → velocity_python-0.0.118.dist-info}/top_level.txt +0 -0
velocity/db/exceptions.py
CHANGED
|
@@ -5,108 +5,125 @@ Database exceptions for the velocity library.
|
|
|
5
5
|
|
|
6
6
|
class DbException(Exception):
|
|
7
7
|
"""Base class for all database exceptions."""
|
|
8
|
+
|
|
8
9
|
pass
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class DbApplicationError(DbException):
|
|
12
13
|
"""Application-level database error."""
|
|
14
|
+
|
|
13
15
|
pass
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class DbForeignKeyMissingError(DbException):
|
|
17
19
|
"""Foreign key constraint violation."""
|
|
20
|
+
|
|
18
21
|
pass
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
class DbDatabaseMissingError(DbException):
|
|
22
25
|
"""Database does not exist."""
|
|
26
|
+
|
|
23
27
|
pass
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
class DbTableMissingError(DbException):
|
|
27
31
|
"""Table does not exist."""
|
|
32
|
+
|
|
28
33
|
pass
|
|
29
34
|
|
|
30
35
|
|
|
31
36
|
class DbColumnMissingError(DbException):
|
|
32
37
|
"""Column does not exist."""
|
|
38
|
+
|
|
33
39
|
pass
|
|
34
40
|
|
|
35
41
|
|
|
36
42
|
class DbTruncationError(DbException):
|
|
37
43
|
"""Data truncation error."""
|
|
44
|
+
|
|
38
45
|
pass
|
|
39
46
|
|
|
40
47
|
|
|
41
48
|
class DbConnectionError(DbException):
|
|
42
49
|
"""Database connection error."""
|
|
50
|
+
|
|
43
51
|
pass
|
|
44
52
|
|
|
45
53
|
|
|
46
54
|
class DbDuplicateKeyError(DbException):
|
|
47
55
|
"""Duplicate key constraint violation."""
|
|
56
|
+
|
|
48
57
|
pass
|
|
49
58
|
|
|
50
59
|
|
|
51
60
|
class DbObjectExistsError(DbException):
|
|
52
61
|
"""Database object already exists."""
|
|
62
|
+
|
|
53
63
|
pass
|
|
54
64
|
|
|
55
65
|
|
|
56
66
|
class DbLockTimeoutError(DbException):
|
|
57
67
|
"""Lock timeout error."""
|
|
68
|
+
|
|
58
69
|
pass
|
|
59
70
|
|
|
60
71
|
|
|
61
72
|
class DbRetryTransaction(DbException):
|
|
62
73
|
"""Transaction should be retried."""
|
|
74
|
+
|
|
63
75
|
pass
|
|
64
76
|
|
|
65
77
|
|
|
66
78
|
class DbDataIntegrityError(DbException):
|
|
67
79
|
"""Data integrity constraint violation."""
|
|
80
|
+
|
|
68
81
|
pass
|
|
69
82
|
|
|
70
83
|
|
|
71
84
|
class DbQueryError(DbException):
|
|
72
85
|
"""Database query error."""
|
|
86
|
+
|
|
73
87
|
pass
|
|
74
88
|
|
|
75
89
|
|
|
76
90
|
class DbTransactionError(DbException):
|
|
77
91
|
"""Database transaction error."""
|
|
92
|
+
|
|
78
93
|
pass
|
|
79
94
|
|
|
80
95
|
|
|
81
96
|
class DuplicateRowsFoundError(Exception):
|
|
82
97
|
"""Multiple rows found when expecting single result."""
|
|
98
|
+
|
|
83
99
|
pass
|
|
84
100
|
|
|
85
101
|
|
|
86
102
|
# Add aliases for backward compatibility with engine.py
|
|
87
103
|
class DatabaseError(DbException):
|
|
88
104
|
"""Generic database error - alias for DbException."""
|
|
105
|
+
|
|
89
106
|
pass
|
|
90
107
|
|
|
108
|
+
|
|
91
109
|
__all__ = [
|
|
92
110
|
# Base exceptions
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
111
|
+
"DbException",
|
|
112
|
+
"DatabaseError",
|
|
96
113
|
# Specific exceptions
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
"DbApplicationError",
|
|
115
|
+
"DbForeignKeyMissingError",
|
|
116
|
+
"DbDatabaseMissingError",
|
|
117
|
+
"DbTableMissingError",
|
|
118
|
+
"DbColumnMissingError",
|
|
119
|
+
"DbTruncationError",
|
|
120
|
+
"DbConnectionError",
|
|
121
|
+
"DbDuplicateKeyError",
|
|
122
|
+
"DbObjectExistsError",
|
|
123
|
+
"DbLockTimeoutError",
|
|
124
|
+
"DbRetryTransaction",
|
|
125
|
+
"DbDataIntegrityError",
|
|
126
|
+
"DbQueryError",
|
|
127
|
+
"DbTransactionError",
|
|
128
|
+
"DuplicateRowsFoundError",
|
|
112
129
|
]
|
velocity/db/servers/mysql.py
CHANGED
|
@@ -30,7 +30,7 @@ def make_where(where, sql, vals, is_join=False):
|
|
|
30
30
|
if is_join:
|
|
31
31
|
if "." not in key:
|
|
32
32
|
key = "A." + key
|
|
33
|
-
if val
|
|
33
|
+
if val is None:
|
|
34
34
|
if "!" in key:
|
|
35
35
|
key = key.replace("!", "")
|
|
36
36
|
sql.append("{} is not NULL".format(quote(key.lower())))
|
|
@@ -198,7 +198,7 @@ class SQL:
|
|
|
198
198
|
cls, table, columns, key_to_table, key_to_columns, name=None, schema=None
|
|
199
199
|
):
|
|
200
200
|
if "." not in table and schema:
|
|
201
|
-
if schema
|
|
201
|
+
if schema is None:
|
|
202
202
|
schema = cls.default_schema
|
|
203
203
|
table = "{}.{}".format(schema, table)
|
|
204
204
|
if isinstance(key_to_columns, str):
|
|
@@ -254,7 +254,6 @@ class SQL:
|
|
|
254
254
|
|
|
255
255
|
@classmethod
|
|
256
256
|
def create_table(cls, name, columns={}, drop=False):
|
|
257
|
-
trigger = "".format(name)
|
|
258
257
|
sql = []
|
|
259
258
|
if drop:
|
|
260
259
|
sql.append(cls.drop_table(name))
|
|
@@ -4,16 +4,14 @@ from .sql import SQL
|
|
|
4
4
|
from velocity.db.core import engine
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
def initialize(config=None, **kwargs):
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return engine.Engine(psycopg2, config, SQL)
|
|
8
|
+
konfig = {
|
|
9
|
+
"database": os.environ["DBDatabase"],
|
|
10
|
+
"host": os.environ["DBHost"],
|
|
11
|
+
"port": os.environ["DBPort"],
|
|
12
|
+
"user": os.environ["DBUser"],
|
|
13
|
+
"password": os.environ["DBPassword"],
|
|
14
|
+
}
|
|
15
|
+
konfig.update(config or {})
|
|
16
|
+
konfig.update(kwargs)
|
|
17
|
+
return engine.Engine(psycopg2, konfig, SQL)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import hashlib
|
|
3
3
|
import sqlparse
|
|
4
|
-
from psycopg2 import sql
|
|
4
|
+
from psycopg2 import sql as psycopg2_sql
|
|
5
5
|
|
|
6
6
|
from velocity.db import exceptions
|
|
7
7
|
|
|
@@ -37,7 +37,7 @@ def _handle_predicate_errors(predicates, operation="WHERE"):
|
|
|
37
37
|
"""Process a list of predicates with error handling."""
|
|
38
38
|
sql_parts = []
|
|
39
39
|
vals = []
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
for pred, val in predicates:
|
|
42
42
|
sql_parts.append(pred)
|
|
43
43
|
if val is None:
|
|
@@ -46,7 +46,7 @@ def _handle_predicate_errors(predicates, operation="WHERE"):
|
|
|
46
46
|
vals.extend(val)
|
|
47
47
|
else:
|
|
48
48
|
vals.append(val)
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
return sql_parts, vals
|
|
51
51
|
|
|
52
52
|
|
|
@@ -75,8 +75,19 @@ class SQL:
|
|
|
75
75
|
ColumnMissingErrorCodes = ["42703"]
|
|
76
76
|
ForeignKeyMissingErrorCodes = ["42704"]
|
|
77
77
|
|
|
78
|
-
ConnectionErrorCodes = [
|
|
79
|
-
|
|
78
|
+
ConnectionErrorCodes = [
|
|
79
|
+
"08001",
|
|
80
|
+
"08S01",
|
|
81
|
+
"57P03",
|
|
82
|
+
"08006",
|
|
83
|
+
"53300",
|
|
84
|
+
"08003",
|
|
85
|
+
"08004",
|
|
86
|
+
"08P01",
|
|
87
|
+
]
|
|
88
|
+
DuplicateKeyErrorCodes = [
|
|
89
|
+
"23505"
|
|
90
|
+
] # unique_violation - no longer relying only on regex
|
|
80
91
|
RetryTransactionCodes = ["40001", "40P01", "40002"]
|
|
81
92
|
TruncationErrorCodes = ["22001"]
|
|
82
93
|
LockTimeoutErrorCodes = ["55P03"]
|
|
@@ -111,7 +122,7 @@ class SQL:
|
|
|
111
122
|
"""
|
|
112
123
|
if not table:
|
|
113
124
|
raise ValueError("Table name is required.")
|
|
114
|
-
|
|
125
|
+
|
|
115
126
|
# Validate pagination parameters
|
|
116
127
|
if start is not None and not isinstance(start, int):
|
|
117
128
|
raise ValueError("Start (OFFSET) must be an integer.")
|
|
@@ -148,7 +159,7 @@ class SQL:
|
|
|
148
159
|
columns = [c.strip() for c in columns if c.strip()] # Remove empty columns
|
|
149
160
|
if not columns:
|
|
150
161
|
raise ValueError("No valid columns specified")
|
|
151
|
-
|
|
162
|
+
|
|
152
163
|
distinct = False
|
|
153
164
|
|
|
154
165
|
# Check for DISTINCT keyword in any column
|
|
@@ -188,7 +199,7 @@ class SQL:
|
|
|
188
199
|
new_orderby = []
|
|
189
200
|
if isinstance(orderby, str):
|
|
190
201
|
orderby = th.split_columns(orderby)
|
|
191
|
-
|
|
202
|
+
|
|
192
203
|
# Handle orderby references
|
|
193
204
|
if isinstance(orderby, Sequence):
|
|
194
205
|
for column in orderby:
|
|
@@ -200,7 +211,9 @@ class SQL:
|
|
|
200
211
|
# Validate direction
|
|
201
212
|
direction = direction.upper()
|
|
202
213
|
if direction not in ("ASC", "DESC"):
|
|
203
|
-
raise ValueError(
|
|
214
|
+
raise ValueError(
|
|
215
|
+
f"Invalid ORDER BY direction: {direction}"
|
|
216
|
+
)
|
|
204
217
|
col_name = th.resolve_references(
|
|
205
218
|
col_name.strip(), options={"alias_only": True}
|
|
206
219
|
)
|
|
@@ -213,7 +226,9 @@ class SQL:
|
|
|
213
226
|
)
|
|
214
227
|
new_orderby.append(resolved_col)
|
|
215
228
|
except Exception as e:
|
|
216
|
-
raise ValueError(
|
|
229
|
+
raise ValueError(
|
|
230
|
+
f"Error processing ORDER BY column '{column}': {e}"
|
|
231
|
+
)
|
|
217
232
|
|
|
218
233
|
elif isinstance(orderby, Mapping):
|
|
219
234
|
for key, val in orderby.items():
|
|
@@ -222,11 +237,13 @@ class SQL:
|
|
|
222
237
|
direction = str(val).upper()
|
|
223
238
|
if direction not in ("ASC", "DESC"):
|
|
224
239
|
raise ValueError(f"Invalid ORDER BY direction: {direction}")
|
|
225
|
-
parsed_key = th.resolve_references(
|
|
240
|
+
parsed_key = th.resolve_references(
|
|
241
|
+
key, options={"alias_only": True}
|
|
242
|
+
)
|
|
226
243
|
new_orderby.append(f"{parsed_key} {direction}")
|
|
227
244
|
except Exception as e:
|
|
228
245
|
raise ValueError(f"Error processing ORDER BY key '{key}': {e}")
|
|
229
|
-
|
|
246
|
+
|
|
230
247
|
orderby = new_orderby
|
|
231
248
|
|
|
232
249
|
# Handle groupby
|
|
@@ -256,7 +273,9 @@ class SQL:
|
|
|
256
273
|
|
|
257
274
|
# FROM clause
|
|
258
275
|
if th.foreign_keys:
|
|
259
|
-
sql_parts["FROM"].append(
|
|
276
|
+
sql_parts["FROM"].append(
|
|
277
|
+
f"{TableHelper.quote(table)} AS {TableHelper.quote(alias)}"
|
|
278
|
+
)
|
|
260
279
|
# Handle joins
|
|
261
280
|
done = []
|
|
262
281
|
for key, ref_info in th.foreign_keys.items():
|
|
@@ -463,7 +482,7 @@ class SQL:
|
|
|
463
482
|
# Create a temporary TableHelper instance for quoting
|
|
464
483
|
# Note: We pass None for tx since we only need quoting functionality
|
|
465
484
|
temp_helper = TableHelper(None, table)
|
|
466
|
-
|
|
485
|
+
|
|
467
486
|
keys = []
|
|
468
487
|
vals_placeholders = []
|
|
469
488
|
args = []
|
|
@@ -966,7 +985,7 @@ class SQL:
|
|
|
966
985
|
columns = TableHelper.quote(columns)
|
|
967
986
|
sql = ["DROP"]
|
|
968
987
|
sql.append("INDEX IF EXISTS")
|
|
969
|
-
|
|
988
|
+
_tablename = TableHelper.quote(table)
|
|
970
989
|
if not name:
|
|
971
990
|
name = re.sub(
|
|
972
991
|
r"\([^)]*\)",
|
|
@@ -1136,9 +1155,9 @@ class SQL:
|
|
|
1136
1155
|
@classmethod
|
|
1137
1156
|
def missing(cls, tx, table, list, column="SYS_ID", where=None):
|
|
1138
1157
|
sql = [
|
|
1139
|
-
|
|
1158
|
+
"SELECT * FROM",
|
|
1140
1159
|
f"UNNEST('{{{','.join([str(x) for x in list])}}}'::int[]) id",
|
|
1141
|
-
|
|
1160
|
+
"EXCEPT ALL",
|
|
1142
1161
|
f"SELECT {column} FROM {table}",
|
|
1143
1162
|
]
|
|
1144
1163
|
vals = []
|
velocity/db/servers/sqlite.py
CHANGED
|
@@ -216,7 +216,7 @@ class SQL(object):
|
|
|
216
216
|
if is_join:
|
|
217
217
|
if "." not in key:
|
|
218
218
|
key = "A." + key
|
|
219
|
-
if val
|
|
219
|
+
if val is None:
|
|
220
220
|
if "!" in key:
|
|
221
221
|
key = key.replace("!", "")
|
|
222
222
|
sql.append("{} is not NULL".format(quote(key.lower())))
|
|
@@ -919,7 +919,7 @@ class SQL(object):
|
|
|
919
919
|
for key in sorted(where.keys()):
|
|
920
920
|
if join:
|
|
921
921
|
sql.append(join)
|
|
922
|
-
if where[key]
|
|
922
|
+
if where[key] is None:
|
|
923
923
|
sql.append("{} is NULL".format(quote(key.lower())))
|
|
924
924
|
else:
|
|
925
925
|
sql.append("{} = ?".format(quote(key.lower())))
|
velocity/db/servers/sqlserver.py
CHANGED
|
@@ -30,7 +30,7 @@ def make_where(where, sql, vals, is_join=False):
|
|
|
30
30
|
if is_join:
|
|
31
31
|
if "." not in key:
|
|
32
32
|
key = "A." + key
|
|
33
|
-
if val
|
|
33
|
+
if val is None:
|
|
34
34
|
if "!" in key:
|
|
35
35
|
key = key.replace("!", "")
|
|
36
36
|
sql.append("{} is not NULL".format(quote(key.lower())))
|
|
@@ -394,7 +394,7 @@ class SQL:
|
|
|
394
394
|
cls, table, columns, key_to_table, key_to_columns, name=None, schema=None
|
|
395
395
|
):
|
|
396
396
|
if "." not in table and schema:
|
|
397
|
-
if schema
|
|
397
|
+
if schema is None:
|
|
398
398
|
schema = cls.default_schema
|
|
399
399
|
table = "{}.{}".format(schema, table)
|
|
400
400
|
if isinstance(key_to_columns, str):
|
|
@@ -873,7 +873,7 @@ class SQL:
|
|
|
873
873
|
for key in sorted(where.keys()):
|
|
874
874
|
if join:
|
|
875
875
|
sql.append(join)
|
|
876
|
-
if where[key]
|
|
876
|
+
if where[key] is None:
|
|
877
877
|
sql.append("{} is NULL".format(quote(key.lower())))
|
|
878
878
|
else:
|
|
879
879
|
sql.append("{} = %s".format(quote(key.lower())))
|