velocity-python 0.0.132__tar.gz → 0.0.134__tar.gz
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_python-0.0.132 → velocity_python-0.0.134}/PKG-INFO +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134}/pyproject.toml +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/__init__.py +1 -1
- velocity_python-0.0.134/src/velocity/app/tests/__init__.py +1 -0
- velocity_python-0.0.134/src/velocity/aws/tests/__init__.py +1 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/decorators.py +20 -3
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/engine.py +33 -7
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/exceptions.py +7 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/base/initializer.py +2 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/mysql/__init__.py +13 -4
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/postgres/__init__.py +14 -4
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlite/__init__.py +13 -4
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlserver/__init__.py +13 -4
- velocity_python-0.0.134/src/velocity/db/tests/__init__.py +1 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/__init__.py +1 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/common.py +49 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_column.py +29 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_connections.py +25 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_database.py +21 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_engine.py +205 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_general_usage.py +88 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_imports.py +8 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_result.py +19 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_row.py +137 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_schema_locking.py +335 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_schema_locking_unit.py +115 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_sequence.py +34 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_table.py +101 -0
- velocity_python-0.0.134/src/velocity/db/tests/postgres/test_transaction.py +106 -0
- velocity_python-0.0.134/src/velocity/db/tests/sql/__init__.py +1 -0
- velocity_python-0.0.134/src/velocity/db/tests/sql/common.py +177 -0
- velocity_python-0.0.134/src/velocity/db/tests/sql/test_postgres_select_advanced.py +285 -0
- velocity_python-0.0.134/src/velocity/db/tests/sql/test_postgres_select_variances.py +517 -0
- velocity_python-0.0.134/src/velocity/db/tests/test_schema_locking_initializers.py +226 -0
- velocity_python-0.0.134/src/velocity/db/tests/test_schema_locking_simple.py +97 -0
- velocity_python-0.0.134/src/velocity/misc/__init__.py +0 -0
- velocity_python-0.0.134/src/velocity/misc/tests/__init__.py +1 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_db.py +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_format.py +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_iconv.py +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_merge.py +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_oconv.py +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_timer.py +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity_python.egg-info/PKG-INFO +1 -1
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity_python.egg-info/SOURCES.txt +50 -24
- {velocity_python-0.0.132 → velocity_python-0.0.134}/LICENSE +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/README.md +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/setup.cfg +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/app/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/app/invoices.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/app/orders.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/app/payments.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/app/purchase_orders.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/app}/tests/test_email_processing.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/app}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/app}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/amplify.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/mixins/activity_tracker.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/mixins/error_handler.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/mixins/legacy_mixin.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/mixins/standard_mixin.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/aws}/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/aws}/tests/test_response.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/table.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/core/transaction.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/base/sql.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/mysql/sql.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/postgres/sql.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlite/sql.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlserver/sql.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/tablehelper.py +0 -0
- /velocity_python-0.0.132/src/velocity/misc/__init__.py → /velocity_python-0.0.134/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_db_utils.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_postgres.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_result_caching.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_sql_builder.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/db}/tests/test_tablehelper.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_fix.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134/src/velocity/misc}/tests/test_original_error.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity_python.egg-info/requires.txt +0 -0
- {velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity_python.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# App module tests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# AWS module tests
|
|
@@ -99,7 +99,8 @@ def return_default(
|
|
|
99
99
|
|
|
100
100
|
def create_missing(func):
|
|
101
101
|
"""
|
|
102
|
-
If the function call fails with DbColumnMissingError or DbTableMissingError,
|
|
102
|
+
If the function call fails with DbColumnMissingError or DbTableMissingError,
|
|
103
|
+
tries to create them and re-run (only if schema is not locked).
|
|
103
104
|
"""
|
|
104
105
|
|
|
105
106
|
@wraps(func)
|
|
@@ -109,8 +110,16 @@ def create_missing(func):
|
|
|
109
110
|
result = func(self, *args, **kwds)
|
|
110
111
|
self.tx.release_savepoint(sp, cursor=self.cursor())
|
|
111
112
|
return result
|
|
112
|
-
except exceptions.DbColumnMissingError:
|
|
113
|
+
except exceptions.DbColumnMissingError as e:
|
|
113
114
|
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
115
|
+
|
|
116
|
+
# Check if schema is locked
|
|
117
|
+
if self.tx.engine.schema_locked:
|
|
118
|
+
raise exceptions.DbSchemaLockedError(
|
|
119
|
+
f"Cannot create missing column: schema is locked. Original error: {e}"
|
|
120
|
+
) from e
|
|
121
|
+
|
|
122
|
+
# Existing logic for automatic creation
|
|
114
123
|
data = {}
|
|
115
124
|
if "pk" in kwds:
|
|
116
125
|
data.update(kwds["pk"])
|
|
@@ -121,8 +130,16 @@ def create_missing(func):
|
|
|
121
130
|
data.update(arg)
|
|
122
131
|
self.alter(data)
|
|
123
132
|
return func(self, *args, **kwds)
|
|
124
|
-
except exceptions.DbTableMissingError:
|
|
133
|
+
except exceptions.DbTableMissingError as e:
|
|
125
134
|
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
135
|
+
|
|
136
|
+
# Check if schema is locked
|
|
137
|
+
if self.tx.engine.schema_locked:
|
|
138
|
+
raise exceptions.DbSchemaLockedError(
|
|
139
|
+
f"Cannot create missing table: schema is locked. Original error: {e}"
|
|
140
|
+
) from e
|
|
141
|
+
|
|
142
|
+
# Existing logic for automatic creation
|
|
126
143
|
data = {}
|
|
127
144
|
if "pk" in kwds:
|
|
128
145
|
data.update(kwds["pk"])
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
import re
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import contextmanager
|
|
3
5
|
from functools import wraps
|
|
4
6
|
from velocity.db import exceptions
|
|
5
7
|
from velocity.db.core.transaction import Transaction
|
|
@@ -17,11 +19,12 @@ class Engine:
|
|
|
17
19
|
|
|
18
20
|
MAX_RETRIES = 100
|
|
19
21
|
|
|
20
|
-
def __init__(self, driver, config, sql, connect_timeout=5):
|
|
22
|
+
def __init__(self, driver, config, sql, connect_timeout=5, schema_locked=False):
|
|
21
23
|
self.__config = config
|
|
22
24
|
self.__sql = sql
|
|
23
25
|
self.__driver = driver
|
|
24
26
|
self.__connect_timeout = connect_timeout
|
|
27
|
+
self.__schema_locked = schema_locked
|
|
25
28
|
|
|
26
29
|
def __str__(self):
|
|
27
30
|
return f"[{self.sql.server}] engine({self.config})"
|
|
@@ -205,6 +208,29 @@ class Engine:
|
|
|
205
208
|
def sql(self):
|
|
206
209
|
return self.__sql
|
|
207
210
|
|
|
211
|
+
@property
|
|
212
|
+
def schema_locked(self):
|
|
213
|
+
"""Returns True if schema modifications are locked."""
|
|
214
|
+
return self.__schema_locked
|
|
215
|
+
|
|
216
|
+
def lock_schema(self):
|
|
217
|
+
"""Lock schema to prevent automatic modifications."""
|
|
218
|
+
self.__schema_locked = True
|
|
219
|
+
|
|
220
|
+
def unlock_schema(self):
|
|
221
|
+
"""Unlock schema to allow automatic modifications."""
|
|
222
|
+
self.__schema_locked = False
|
|
223
|
+
|
|
224
|
+
@contextmanager
|
|
225
|
+
def unlocked_schema(self):
|
|
226
|
+
"""Temporarily unlock schema for automatic creation."""
|
|
227
|
+
original_state = self.__schema_locked
|
|
228
|
+
self.__schema_locked = False
|
|
229
|
+
try:
|
|
230
|
+
yield self
|
|
231
|
+
finally:
|
|
232
|
+
self.__schema_locked = original_state
|
|
233
|
+
|
|
208
234
|
@property
|
|
209
235
|
def version(self):
|
|
210
236
|
"""
|
|
@@ -353,12 +379,12 @@ class Engine:
|
|
|
353
379
|
if sql:
|
|
354
380
|
formatted_sql_info = f" sql={self._format_sql_with_params(sql, parameters)}"
|
|
355
381
|
|
|
356
|
-
logger.warning(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
)
|
|
382
|
+
# logger.warning(
|
|
383
|
+
# "Database error caught. Attempting to transform: code=%s message=%s%s",
|
|
384
|
+
# error_code,
|
|
385
|
+
# error_message,
|
|
386
|
+
# formatted_sql_info,
|
|
387
|
+
# )
|
|
362
388
|
|
|
363
389
|
# Direct error code mapping
|
|
364
390
|
if error_code in self.sql.ApplicationErrorCodes:
|
|
@@ -93,6 +93,12 @@ class DbTransactionError(DbException):
|
|
|
93
93
|
pass
|
|
94
94
|
|
|
95
95
|
|
|
96
|
+
class DbSchemaLockedError(DbApplicationError):
|
|
97
|
+
"""Raised when attempting to modify schema while schema is locked."""
|
|
98
|
+
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
96
102
|
class DuplicateRowsFoundError(Exception):
|
|
97
103
|
"""Multiple rows found when expecting single result."""
|
|
98
104
|
|
|
@@ -125,5 +131,6 @@ __all__ = [
|
|
|
125
131
|
"DbDataIntegrityError",
|
|
126
132
|
"DbQueryError",
|
|
127
133
|
"DbTransactionError",
|
|
134
|
+
"DbSchemaLockedError",
|
|
128
135
|
"DuplicateRowsFoundError",
|
|
129
136
|
]
|
{velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/base/initializer.py
RENAMED
|
@@ -16,12 +16,13 @@ class BaseInitializer(ABC):
|
|
|
16
16
|
|
|
17
17
|
@staticmethod
|
|
18
18
|
@abstractmethod
|
|
19
|
-
def initialize(config: Optional[Dict[str, Any]] = None, **kwargs) -> engine.Engine:
|
|
19
|
+
def initialize(config: Optional[Dict[str, Any]] = None, schema_locked: bool = False, **kwargs) -> engine.Engine:
|
|
20
20
|
"""
|
|
21
21
|
Initialize a database engine with the appropriate driver and configuration.
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
24
|
config: Configuration dictionary (can be None)
|
|
25
|
+
schema_locked: Boolean to lock schema modifications (default: False)
|
|
25
26
|
**kwargs: Additional configuration parameters
|
|
26
27
|
|
|
27
28
|
Returns:
|
{velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/mysql/__init__.py
RENAMED
|
@@ -8,12 +8,13 @@ class MySQLInitializer(BaseInitializer):
|
|
|
8
8
|
"""MySQL database initializer."""
|
|
9
9
|
|
|
10
10
|
@staticmethod
|
|
11
|
-
def initialize(config=None, **kwargs):
|
|
11
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
12
12
|
"""
|
|
13
13
|
Initialize MySQL engine with mysql.connector driver.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
16
|
config: Configuration dictionary
|
|
17
|
+
schema_locked: Boolean to lock schema modifications
|
|
17
18
|
**kwargs: Additional configuration parameters
|
|
18
19
|
|
|
19
20
|
Returns:
|
|
@@ -55,10 +56,18 @@ class MySQLInitializer(BaseInitializer):
|
|
|
55
56
|
required_keys = ["database", "host", "user"]
|
|
56
57
|
MySQLInitializer._validate_required_config(final_config, required_keys)
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
# Check for environment variable override for schema locking
|
|
60
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
61
|
+
schema_locked = True
|
|
62
|
+
|
|
63
|
+
return engine.Engine(mysql.connector, final_config, SQL, schema_locked=schema_locked)
|
|
59
64
|
|
|
60
65
|
|
|
61
66
|
# Maintain backward compatibility
|
|
62
|
-
def initialize(config=None, **kwargs):
|
|
67
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
63
68
|
"""Backward compatible initialization function."""
|
|
64
|
-
|
|
69
|
+
# Check for environment variable override for schema locking
|
|
70
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
71
|
+
schema_locked = True
|
|
72
|
+
|
|
73
|
+
return MySQLInitializer.initialize(config, schema_locked, **kwargs)
|
{velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/postgres/__init__.py
RENAMED
|
@@ -9,12 +9,13 @@ class PostgreSQLInitializer(BaseInitializer):
|
|
|
9
9
|
"""PostgreSQL database initializer."""
|
|
10
10
|
|
|
11
11
|
@staticmethod
|
|
12
|
-
def initialize(config=None, **kwargs):
|
|
12
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
13
13
|
"""
|
|
14
14
|
Initialize PostgreSQL engine with psycopg2 driver.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
config: Configuration dictionary
|
|
18
|
+
schema_locked: Boolean to lock schema modifications
|
|
18
19
|
**kwargs: Additional configuration parameters
|
|
19
20
|
|
|
20
21
|
Returns:
|
|
@@ -39,11 +40,15 @@ class PostgreSQLInitializer(BaseInitializer):
|
|
|
39
40
|
required_keys = ["database", "host", "user", "password"]
|
|
40
41
|
PostgreSQLInitializer._validate_required_config(final_config, required_keys)
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
# Check for environment variable override for schema locking
|
|
44
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
45
|
+
schema_locked = True
|
|
46
|
+
|
|
47
|
+
return engine.Engine(psycopg2, final_config, SQL, schema_locked=schema_locked)
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
# Maintain backward compatibility
|
|
46
|
-
def initialize(config=None, **kwargs):
|
|
51
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
47
52
|
"""Backward compatible initialization function - matches original behavior exactly."""
|
|
48
53
|
konfig = {
|
|
49
54
|
"database": os.environ["DBDatabase"],
|
|
@@ -54,4 +59,9 @@ def initialize(config=None, **kwargs):
|
|
|
54
59
|
}
|
|
55
60
|
konfig.update(config or {})
|
|
56
61
|
konfig.update(kwargs)
|
|
57
|
-
|
|
62
|
+
|
|
63
|
+
# Check for environment variable override for schema locking
|
|
64
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
65
|
+
schema_locked = True
|
|
66
|
+
|
|
67
|
+
return engine.Engine(psycopg2, konfig, SQL, schema_locked=schema_locked)
|
{velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlite/__init__.py
RENAMED
|
@@ -9,12 +9,13 @@ class SQLiteInitializer(BaseInitializer):
|
|
|
9
9
|
"""SQLite database initializer."""
|
|
10
10
|
|
|
11
11
|
@staticmethod
|
|
12
|
-
def initialize(config=None, **kwargs):
|
|
12
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
13
13
|
"""
|
|
14
14
|
Initialize SQLite engine with sqlite3 driver.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
config: Configuration dictionary
|
|
18
|
+
schema_locked: Boolean to lock schema modifications
|
|
18
19
|
**kwargs: Additional configuration parameters
|
|
19
20
|
|
|
20
21
|
Returns:
|
|
@@ -43,10 +44,18 @@ class SQLiteInitializer(BaseInitializer):
|
|
|
43
44
|
required_keys = ["database"]
|
|
44
45
|
SQLiteInitializer._validate_required_config(final_config, required_keys)
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
# Check for environment variable override for schema locking
|
|
48
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
49
|
+
schema_locked = True
|
|
50
|
+
|
|
51
|
+
return engine.Engine(sqlite3, final_config, SQL, schema_locked=schema_locked)
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
# Maintain backward compatibility
|
|
50
|
-
def initialize(config=None, **kwargs):
|
|
55
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
51
56
|
"""Backward compatible initialization function."""
|
|
52
|
-
|
|
57
|
+
# Check for environment variable override for schema locking
|
|
58
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
59
|
+
schema_locked = True
|
|
60
|
+
|
|
61
|
+
return SQLiteInitializer.initialize(config, schema_locked, **kwargs)
|
{velocity_python-0.0.132 → velocity_python-0.0.134}/src/velocity/db/servers/sqlserver/__init__.py
RENAMED
|
@@ -8,12 +8,13 @@ class SQLServerInitializer(BaseInitializer):
|
|
|
8
8
|
"""SQL Server database initializer."""
|
|
9
9
|
|
|
10
10
|
@staticmethod
|
|
11
|
-
def initialize(config=None, **kwargs):
|
|
11
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
12
12
|
"""
|
|
13
13
|
Initialize SQL Server engine with pytds driver.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
16
|
config: Configuration dictionary
|
|
17
|
+
schema_locked: Boolean to lock schema modifications
|
|
17
18
|
**kwargs: Additional configuration parameters
|
|
18
19
|
|
|
19
20
|
Returns:
|
|
@@ -55,10 +56,18 @@ class SQLServerInitializer(BaseInitializer):
|
|
|
55
56
|
required_keys = ["database", "server", "user"]
|
|
56
57
|
SQLServerInitializer._validate_required_config(final_config, required_keys)
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
# Check for environment variable override for schema locking
|
|
60
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
61
|
+
schema_locked = True
|
|
62
|
+
|
|
63
|
+
return engine.Engine(pytds, final_config, SQL, schema_locked=schema_locked)
|
|
59
64
|
|
|
60
65
|
|
|
61
66
|
# Maintain backward compatibility
|
|
62
|
-
def initialize(config=None, **kwargs):
|
|
67
|
+
def initialize(config=None, schema_locked=False, **kwargs):
|
|
63
68
|
"""Backward compatible initialization function."""
|
|
64
|
-
|
|
69
|
+
# Check for environment variable override for schema locking
|
|
70
|
+
if os.environ.get("VELOCITY_SCHEMA_LOCKED", "").lower() in ('true', '1', 'yes'):
|
|
71
|
+
schema_locked = True
|
|
72
|
+
|
|
73
|
+
return SQLServerInitializer.initialize(config, schema_locked, **kwargs)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Database module tests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# PostgreSQL tests
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from velocity.db.servers import postgres
|
|
3
|
+
import env
|
|
4
|
+
env.set()
|
|
5
|
+
|
|
6
|
+
test_db = "test_db_postgres"
|
|
7
|
+
engine = postgres.initialize(database=test_db)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CommonPostgresTest(unittest.TestCase):
|
|
11
|
+
"""
|
|
12
|
+
Base test class for PostgreSQL tests following the common pattern.
|
|
13
|
+
All PostgreSQL tests should inherit from this class.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def setUpClass(cls):
|
|
18
|
+
"""Set up the test database and create any common tables."""
|
|
19
|
+
@engine.transaction
|
|
20
|
+
def setup(tx):
|
|
21
|
+
tx.switch_to_database("postgres")
|
|
22
|
+
tx.execute(f"drop database if exists {test_db}", single=True)
|
|
23
|
+
|
|
24
|
+
# Create the test database
|
|
25
|
+
db = tx.database(test_db)
|
|
26
|
+
if not db.exists():
|
|
27
|
+
db.create()
|
|
28
|
+
db.switch()
|
|
29
|
+
|
|
30
|
+
# Call subclass-specific table creation with commit
|
|
31
|
+
if hasattr(cls, 'create_test_tables'):
|
|
32
|
+
cls.create_test_tables(tx)
|
|
33
|
+
|
|
34
|
+
setup()
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def tearDownClass(cls):
|
|
38
|
+
"""Clean up the test database."""
|
|
39
|
+
@engine.transaction
|
|
40
|
+
def cleanup(tx):
|
|
41
|
+
tx.switch_to_database("postgres")
|
|
42
|
+
tx.execute(f"drop database if exists {test_db}", single=True)
|
|
43
|
+
|
|
44
|
+
cleanup()
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def create_test_tables(cls, tx):
|
|
48
|
+
"""Override this method in subclasses to create test-specific tables."""
|
|
49
|
+
pass
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from velocity.db.core.column import Column
|
|
3
|
+
from velocity.db.exceptions import DbColumnMissingError
|
|
4
|
+
from .common import CommonPostgresTest, engine, test_db
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@engine.transaction
|
|
8
|
+
@engine.transaction
|
|
9
|
+
class TestColumn(CommonPostgresTest):
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def create_test_tables(cls, tx):
|
|
13
|
+
"""Create test tables for column tests."""
|
|
14
|
+
tx.table("mock_table").create(
|
|
15
|
+
columns={
|
|
16
|
+
"column1": int,
|
|
17
|
+
"column2": str,
|
|
18
|
+
"column3": str,
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def test_init(self, tx):
|
|
23
|
+
column = tx.table("mock_table").column("column1")
|
|
24
|
+
self.assertIsInstance(column, Column)
|
|
25
|
+
self.assertEqual(column.name, "column1")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
unittest.main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from .common import CommonPostgresTest, engine, test_db
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@engine.transaction
|
|
6
|
+
@engine.transaction
|
|
7
|
+
class TestConnections(CommonPostgresTest):
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def create_test_tables(cls, tx):
|
|
11
|
+
"""Create test tables for connection tests."""
|
|
12
|
+
tx.table("mock_table").create(
|
|
13
|
+
columns={
|
|
14
|
+
"column1": int,
|
|
15
|
+
"column2": str,
|
|
16
|
+
"column3": str,
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
def test_init(self, tx):
|
|
20
|
+
# Test the initialization of the Database object
|
|
21
|
+
assert tx.table("mock_table").exists()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
unittest.main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from velocity.db.core.database import Database
|
|
3
|
+
from .common import CommonPostgresTest, engine, test_db
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@engine.transaction
|
|
7
|
+
@engine.transaction
|
|
8
|
+
class TestDatabase(CommonPostgresTest):
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def create_test_tables(cls, tx):
|
|
12
|
+
"""No special tables needed for database tests."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def test_init(self, tx):
|
|
16
|
+
# Test the initialization of the Database object
|
|
17
|
+
tx.database
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
unittest.main()
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import datetime
|
|
3
|
+
import unittest
|
|
4
|
+
from velocity.db.core.engine import Engine
|
|
5
|
+
from velocity.db.core.transaction import Transaction
|
|
6
|
+
from .common import CommonPostgresTest, engine, test_db
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@engine.transaction
|
|
10
|
+
class TestEngine(CommonPostgresTest):
|
|
11
|
+
@classmethod
|
|
12
|
+
def create_test_tables(cls, tx):
|
|
13
|
+
"""No special tables needed for engine tests."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def test_engine_init(self, tx):
|
|
17
|
+
import psycopg2
|
|
18
|
+
from velocity.db.servers.postgres import SQL
|
|
19
|
+
|
|
20
|
+
# Test the engine instance from common test
|
|
21
|
+
assert engine.sql == SQL
|
|
22
|
+
assert engine.driver == psycopg2
|
|
23
|
+
|
|
24
|
+
def test_engine_attributes(self, tx):
|
|
25
|
+
import psycopg2
|
|
26
|
+
from velocity.db.servers.postgres import SQL
|
|
27
|
+
|
|
28
|
+
# Test private attributes
|
|
29
|
+
assert engine._Engine__sql == SQL
|
|
30
|
+
assert engine._Engine__driver == psycopg2
|
|
31
|
+
|
|
32
|
+
def test_connect(self, tx):
|
|
33
|
+
import psycopg2
|
|
34
|
+
|
|
35
|
+
assert engine.connect() != None
|
|
36
|
+
conn = engine.connect()
|
|
37
|
+
self.assertIsInstance(conn, psycopg2.extensions.connection)
|
|
38
|
+
|
|
39
|
+
def test_other_stuff(self, tx):
|
|
40
|
+
assert engine.version[:10] == "PostgreSQL"
|
|
41
|
+
|
|
42
|
+
timestamp = engine.timestamp
|
|
43
|
+
self.assertIsInstance(timestamp, datetime.datetime)
|
|
44
|
+
assert engine.user == os.environ["DBUser"]
|
|
45
|
+
assert test_db in engine.databases
|
|
46
|
+
assert "public" in engine.schemas
|
|
47
|
+
assert "information_schema" in engine.schemas
|
|
48
|
+
assert "public" == engine.current_schema
|
|
49
|
+
assert engine.current_database == test_db
|
|
50
|
+
assert [] == engine.views
|
|
51
|
+
assert [] == engine.tables
|
|
52
|
+
|
|
53
|
+
def test_process_error(self, tx):
|
|
54
|
+
local_engine = Engine(None, None, None) # Replace None with appropriate arguments
|
|
55
|
+
with self.assertRaises(
|
|
56
|
+
Exception
|
|
57
|
+
): # Replace Exception with the specific exception raised by process_error
|
|
58
|
+
local_engine.process_error(sql_stmt=None, sql_params=None)
|
|
59
|
+
# Add additional assertions as needed
|
|
60
|
+
|
|
61
|
+
def test_transaction_injection_1(self, tx):
|
|
62
|
+
@engine.transaction
|
|
63
|
+
def function():
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
function()
|
|
67
|
+
|
|
68
|
+
@engine.transaction
|
|
69
|
+
def function(_tx):
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
with self.assertRaises(NameError):
|
|
73
|
+
function()
|
|
74
|
+
|
|
75
|
+
@engine.transaction
|
|
76
|
+
def function(tx):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
with self.assertRaises(TypeError):
|
|
80
|
+
function(tx=3)
|
|
81
|
+
with self.assertRaises(TypeError):
|
|
82
|
+
function(tx=None)
|
|
83
|
+
|
|
84
|
+
def test_transaction_injection_function(self, tx):
|
|
85
|
+
with engine.transaction() as original:
|
|
86
|
+
|
|
87
|
+
@engine.transaction
|
|
88
|
+
def function(tx):
|
|
89
|
+
assert tx != None
|
|
90
|
+
assert tx != original
|
|
91
|
+
self.assertIsInstance(tx, Transaction)
|
|
92
|
+
|
|
93
|
+
function()
|
|
94
|
+
|
|
95
|
+
@engine.transaction
|
|
96
|
+
def function(tx):
|
|
97
|
+
assert tx != None
|
|
98
|
+
assert tx == original
|
|
99
|
+
self.assertIsInstance(tx, Transaction)
|
|
100
|
+
|
|
101
|
+
function(original)
|
|
102
|
+
|
|
103
|
+
@engine.transaction
|
|
104
|
+
def function(tx=None):
|
|
105
|
+
assert tx != None
|
|
106
|
+
assert tx != original
|
|
107
|
+
self.assertIsInstance(tx, Transaction)
|
|
108
|
+
|
|
109
|
+
function()
|
|
110
|
+
|
|
111
|
+
@engine.transaction
|
|
112
|
+
def function(tx=None):
|
|
113
|
+
assert tx != None
|
|
114
|
+
assert tx == original
|
|
115
|
+
self.assertIsInstance(tx, Transaction)
|
|
116
|
+
|
|
117
|
+
function(original)
|
|
118
|
+
|
|
119
|
+
@engine.transaction
|
|
120
|
+
def function(tx=None):
|
|
121
|
+
assert tx != None
|
|
122
|
+
assert tx == original
|
|
123
|
+
self.assertIsInstance(tx, Transaction)
|
|
124
|
+
|
|
125
|
+
function(tx=original)
|
|
126
|
+
|
|
127
|
+
@engine.transaction
|
|
128
|
+
def function(tx, a, b):
|
|
129
|
+
assert tx != None
|
|
130
|
+
assert tx != original
|
|
131
|
+
self.assertIsInstance(tx, Transaction)
|
|
132
|
+
|
|
133
|
+
function(1, 2)
|
|
134
|
+
|
|
135
|
+
@engine.transaction
|
|
136
|
+
def function(tx, a, b):
|
|
137
|
+
assert tx != None
|
|
138
|
+
assert tx == original
|
|
139
|
+
self.assertIsInstance(tx, Transaction)
|
|
140
|
+
|
|
141
|
+
function(original, 1, 2)
|
|
142
|
+
|
|
143
|
+
@engine.transaction
|
|
144
|
+
def function(tx, a, b):
|
|
145
|
+
assert tx != None
|
|
146
|
+
assert tx == original
|
|
147
|
+
self.assertIsInstance(tx, Transaction)
|
|
148
|
+
|
|
149
|
+
function(tx=original, a=1, b=2)
|
|
150
|
+
|
|
151
|
+
@engine.transaction
|
|
152
|
+
def function(tx, src, b):
|
|
153
|
+
assert tx != None
|
|
154
|
+
assert tx != src
|
|
155
|
+
self.assertIsInstance(tx, Transaction)
|
|
156
|
+
|
|
157
|
+
function(src=original, b=2)
|
|
158
|
+
|
|
159
|
+
@engine.transaction
|
|
160
|
+
def function(a, tx, b):
|
|
161
|
+
assert tx != None
|
|
162
|
+
assert tx != original
|
|
163
|
+
self.assertIsInstance(tx, Transaction)
|
|
164
|
+
|
|
165
|
+
function(1, 2)
|
|
166
|
+
|
|
167
|
+
@engine.transaction
|
|
168
|
+
def function(a, tx, b):
|
|
169
|
+
assert tx != None
|
|
170
|
+
assert tx == original
|
|
171
|
+
self.assertIsInstance(tx, Transaction)
|
|
172
|
+
|
|
173
|
+
function(1, original, 2)
|
|
174
|
+
|
|
175
|
+
def test_transaction_injection_class(self, tx):
|
|
176
|
+
test_class = self
|
|
177
|
+
with engine.transaction() as original:
|
|
178
|
+
|
|
179
|
+
@engine.transaction
|
|
180
|
+
class TestClass:
|
|
181
|
+
@engine.transaction
|
|
182
|
+
def __init__(self, tx):
|
|
183
|
+
assert tx != None
|
|
184
|
+
assert tx != original
|
|
185
|
+
test_class.assertIsInstance(tx, Transaction)
|
|
186
|
+
|
|
187
|
+
def first_method(self, tx):
|
|
188
|
+
assert tx != None
|
|
189
|
+
assert tx != original
|
|
190
|
+
test_class.assertIsInstance(tx, Transaction)
|
|
191
|
+
|
|
192
|
+
def second_method(self, tx):
|
|
193
|
+
assert tx != None
|
|
194
|
+
assert tx == original
|
|
195
|
+
test_class.assertIsInstance(tx, Transaction)
|
|
196
|
+
|
|
197
|
+
tc = TestClass()
|
|
198
|
+
|
|
199
|
+
tc.first_method()
|
|
200
|
+
tc.second_method(original)
|
|
201
|
+
self.assertRaises(AssertionError, tc.second_method)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
unittest.main()
|