velocity-python 0.0.109__py3-none-any.whl → 0.0.161__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.
- velocity/__init__.py +3 -1
- velocity/app/orders.py +3 -4
- velocity/app/tests/__init__.py +1 -0
- velocity/app/tests/test_email_processing.py +112 -0
- velocity/app/tests/test_payment_profile_sorting.py +191 -0
- velocity/app/tests/test_spreadsheet_functions.py +124 -0
- 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 +248 -0
- velocity/aws/handlers/context.py +251 -2
- velocity/aws/handlers/exceptions.py +16 -0
- velocity/aws/handlers/lambda_handler.py +24 -85
- velocity/aws/handlers/mixins/__init__.py +16 -0
- velocity/aws/handlers/mixins/activity_tracker.py +181 -0
- velocity/aws/handlers/mixins/aws_session_mixin.py +192 -0
- velocity/aws/handlers/mixins/error_handler.py +192 -0
- velocity/aws/handlers/mixins/legacy_mixin.py +53 -0
- velocity/aws/handlers/mixins/standard_mixin.py +73 -0
- velocity/aws/handlers/response.py +1 -1
- velocity/aws/handlers/sqs_handler.py +28 -143
- velocity/aws/tests/__init__.py +1 -0
- velocity/aws/tests/test_lambda_handler_json_serialization.py +120 -0
- velocity/aws/tests/test_response.py +163 -0
- velocity/db/__init__.py +16 -4
- velocity/db/core/decorators.py +48 -13
- velocity/db/core/engine.py +187 -840
- velocity/db/core/result.py +33 -25
- velocity/db/core/row.py +15 -3
- velocity/db/core/table.py +493 -50
- velocity/db/core/transaction.py +28 -15
- velocity/db/exceptions.py +42 -18
- velocity/db/servers/base/__init__.py +9 -0
- velocity/db/servers/base/initializer.py +70 -0
- velocity/db/servers/base/operators.py +98 -0
- velocity/db/servers/base/sql.py +503 -0
- velocity/db/servers/base/types.py +135 -0
- velocity/db/servers/mysql/__init__.py +73 -0
- velocity/db/servers/mysql/operators.py +54 -0
- velocity/db/servers/{mysql_reserved.py → mysql/reserved.py} +2 -14
- velocity/db/servers/mysql/sql.py +718 -0
- velocity/db/servers/mysql/types.py +107 -0
- velocity/db/servers/postgres/__init__.py +59 -11
- velocity/db/servers/postgres/operators.py +34 -0
- velocity/db/servers/postgres/sql.py +474 -120
- velocity/db/servers/postgres/types.py +88 -2
- velocity/db/servers/sqlite/__init__.py +61 -0
- velocity/db/servers/sqlite/operators.py +52 -0
- velocity/db/servers/sqlite/reserved.py +20 -0
- velocity/db/servers/sqlite/sql.py +677 -0
- velocity/db/servers/sqlite/types.py +92 -0
- velocity/db/servers/sqlserver/__init__.py +73 -0
- velocity/db/servers/sqlserver/operators.py +47 -0
- velocity/db/servers/sqlserver/reserved.py +32 -0
- velocity/db/servers/sqlserver/sql.py +805 -0
- velocity/db/servers/sqlserver/types.py +114 -0
- velocity/db/servers/tablehelper.py +117 -91
- velocity/db/tests/__init__.py +1 -0
- velocity/db/tests/common_db_test.py +0 -0
- velocity/db/tests/postgres/__init__.py +1 -0
- velocity/db/tests/postgres/common.py +49 -0
- velocity/db/tests/postgres/test_column.py +29 -0
- velocity/db/tests/postgres/test_connections.py +25 -0
- velocity/db/tests/postgres/test_database.py +21 -0
- velocity/db/tests/postgres/test_engine.py +205 -0
- velocity/db/tests/postgres/test_general_usage.py +88 -0
- velocity/db/tests/postgres/test_imports.py +8 -0
- velocity/db/tests/postgres/test_result.py +19 -0
- velocity/db/tests/postgres/test_row.py +137 -0
- velocity/db/tests/postgres/test_row_comprehensive.py +720 -0
- velocity/db/tests/postgres/test_schema_locking.py +335 -0
- velocity/db/tests/postgres/test_schema_locking_unit.py +115 -0
- velocity/db/tests/postgres/test_sequence.py +34 -0
- velocity/db/tests/postgres/test_sql_comprehensive.py +462 -0
- velocity/db/tests/postgres/test_table.py +101 -0
- velocity/db/tests/postgres/test_table_comprehensive.py +646 -0
- velocity/db/tests/postgres/test_transaction.py +106 -0
- velocity/db/tests/sql/__init__.py +1 -0
- velocity/db/tests/sql/common.py +177 -0
- velocity/db/tests/sql/test_postgres_select_advanced.py +285 -0
- velocity/db/tests/sql/test_postgres_select_variances.py +517 -0
- velocity/db/tests/test_cursor_rowcount_fix.py +150 -0
- velocity/db/tests/test_db_utils.py +270 -0
- velocity/db/tests/test_postgres.py +448 -0
- velocity/db/tests/test_postgres_unchanged.py +81 -0
- velocity/db/tests/test_process_error_robustness.py +292 -0
- velocity/db/tests/test_result_caching.py +279 -0
- velocity/db/tests/test_result_sql_aware.py +117 -0
- velocity/db/tests/test_row_get_missing_column.py +72 -0
- velocity/db/tests/test_schema_locking_initializers.py +226 -0
- velocity/db/tests/test_schema_locking_simple.py +97 -0
- velocity/db/tests/test_sql_builder.py +165 -0
- velocity/db/tests/test_tablehelper.py +486 -0
- velocity/db/utils.py +129 -51
- 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/tests/__init__.py +1 -0
- velocity/misc/tests/test_db.py +90 -0
- velocity/misc/tests/test_fix.py +78 -0
- velocity/misc/tests/test_format.py +64 -0
- velocity/misc/tests/test_iconv.py +203 -0
- velocity/misc/tests/test_merge.py +82 -0
- velocity/misc/tests/test_oconv.py +144 -0
- velocity/misc/tests/test_original_error.py +52 -0
- velocity/misc/tests/test_timer.py +74 -0
- velocity/misc/tools.py +0 -1
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/METADATA +2 -2
- velocity_python-0.0.161.dist-info/RECORD +129 -0
- velocity/db/core/exceptions.py +0 -70
- velocity/db/servers/mysql.py +0 -641
- velocity/db/servers/sqlite.py +0 -968
- velocity/db/servers/sqlite_reserved.py +0 -208
- velocity/db/servers/sqlserver.py +0 -921
- velocity/db/servers/sqlserver_reserved.py +0 -314
- velocity_python-0.0.109.dist-info/RECORD +0 -56
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/WHEEL +0 -0
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.0.109.dist-info → velocity_python-0.0.161.dist-info}/top_level.txt +0 -0
velocity/db/core/decorators.py
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import random
|
|
3
|
-
import traceback
|
|
4
3
|
from functools import wraps
|
|
5
4
|
from velocity.db import exceptions
|
|
6
5
|
|
|
7
6
|
|
|
7
|
+
_PRIMARY_KEY_PATTERNS = (
|
|
8
|
+
"primary key",
|
|
9
|
+
"key 'primary'",
|
|
10
|
+
'key "primary"',
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _is_primary_key_duplicate(error):
|
|
15
|
+
"""Return True when the duplicate-key error is targeting the primary key."""
|
|
16
|
+
|
|
17
|
+
message = str(error or "")
|
|
18
|
+
lowered = message.lower()
|
|
19
|
+
|
|
20
|
+
if "sys_id" in lowered:
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
return any(pattern in lowered for pattern in _PRIMARY_KEY_PATTERNS)
|
|
24
|
+
|
|
25
|
+
|
|
8
26
|
def retry_on_dup_key(func):
|
|
9
|
-
"""
|
|
10
|
-
Retries a function call if it raises DbDuplicateKeyError, up to max_retries.
|
|
11
|
-
"""
|
|
27
|
+
"""Retry when insert/update fails because the primary key already exists."""
|
|
12
28
|
|
|
13
29
|
@wraps(func)
|
|
14
30
|
def retry_decorator(self, *args, **kwds):
|
|
@@ -20,10 +36,12 @@ def retry_on_dup_key(func):
|
|
|
20
36
|
result = func(self, *args, **kwds)
|
|
21
37
|
self.tx.release_savepoint(sp, cursor=self.cursor())
|
|
22
38
|
return result
|
|
23
|
-
except exceptions.DbDuplicateKeyError:
|
|
39
|
+
except exceptions.DbDuplicateKeyError as error:
|
|
24
40
|
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
25
41
|
if "sys_id" in kwds.get("data", {}):
|
|
26
42
|
raise
|
|
43
|
+
if not _is_primary_key_duplicate(error):
|
|
44
|
+
raise
|
|
27
45
|
retries += 1
|
|
28
46
|
if retries >= max_retries:
|
|
29
47
|
raise
|
|
@@ -35,9 +53,7 @@ def retry_on_dup_key(func):
|
|
|
35
53
|
|
|
36
54
|
|
|
37
55
|
def reset_id_on_dup_key(func):
|
|
38
|
-
"""
|
|
39
|
-
Wraps an INSERT/UPSERT to reset the sys_id sequence on duplicate key collisions.
|
|
40
|
-
"""
|
|
56
|
+
"""Retry sys_id sequence bump only when the primary key collides."""
|
|
41
57
|
|
|
42
58
|
@wraps(func)
|
|
43
59
|
def reset_decorator(self, *args, retries=0, **kwds):
|
|
@@ -46,10 +62,12 @@ def reset_id_on_dup_key(func):
|
|
|
46
62
|
result = func(self, *args, **kwds)
|
|
47
63
|
self.tx.release_savepoint(sp, cursor=self.cursor())
|
|
48
64
|
return result
|
|
49
|
-
except exceptions.DbDuplicateKeyError:
|
|
65
|
+
except exceptions.DbDuplicateKeyError as error:
|
|
50
66
|
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
51
67
|
if "sys_id" in kwds.get("data", {}):
|
|
52
68
|
raise
|
|
69
|
+
if not _is_primary_key_duplicate(error):
|
|
70
|
+
raise
|
|
53
71
|
if retries < 3:
|
|
54
72
|
backoff_time = (2**retries) * 0.01 + random.uniform(0, 0.02)
|
|
55
73
|
time.sleep(backoff_time)
|
|
@@ -100,7 +118,8 @@ def return_default(
|
|
|
100
118
|
|
|
101
119
|
def create_missing(func):
|
|
102
120
|
"""
|
|
103
|
-
If the function call fails with DbColumnMissingError or DbTableMissingError,
|
|
121
|
+
If the function call fails with DbColumnMissingError or DbTableMissingError,
|
|
122
|
+
tries to create them and re-run (only if schema is not locked).
|
|
104
123
|
"""
|
|
105
124
|
|
|
106
125
|
@wraps(func)
|
|
@@ -110,8 +129,16 @@ def create_missing(func):
|
|
|
110
129
|
result = func(self, *args, **kwds)
|
|
111
130
|
self.tx.release_savepoint(sp, cursor=self.cursor())
|
|
112
131
|
return result
|
|
113
|
-
except exceptions.DbColumnMissingError:
|
|
132
|
+
except exceptions.DbColumnMissingError as e:
|
|
114
133
|
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
134
|
+
|
|
135
|
+
# Check if schema is locked
|
|
136
|
+
if self.tx.engine.schema_locked:
|
|
137
|
+
raise exceptions.DbSchemaLockedError(
|
|
138
|
+
f"Cannot create missing column: schema is locked. Original error: {e}"
|
|
139
|
+
) from e
|
|
140
|
+
|
|
141
|
+
# Existing logic for automatic creation
|
|
115
142
|
data = {}
|
|
116
143
|
if "pk" in kwds:
|
|
117
144
|
data.update(kwds["pk"])
|
|
@@ -120,10 +147,18 @@ def create_missing(func):
|
|
|
120
147
|
for i, arg in enumerate(args):
|
|
121
148
|
if isinstance(arg, dict):
|
|
122
149
|
data.update(arg)
|
|
123
|
-
self.alter(data)
|
|
150
|
+
self.alter(data, mode="add")
|
|
124
151
|
return func(self, *args, **kwds)
|
|
125
|
-
except exceptions.DbTableMissingError:
|
|
152
|
+
except exceptions.DbTableMissingError as e:
|
|
126
153
|
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
154
|
+
|
|
155
|
+
# Check if schema is locked
|
|
156
|
+
if self.tx.engine.schema_locked:
|
|
157
|
+
raise exceptions.DbSchemaLockedError(
|
|
158
|
+
f"Cannot create missing table: schema is locked. Original error: {e}"
|
|
159
|
+
) from e
|
|
160
|
+
|
|
161
|
+
# Existing logic for automatic creation
|
|
127
162
|
data = {}
|
|
128
163
|
if "pk" in kwds:
|
|
129
164
|
data.update(kwds["pk"])
|