datus-sqlalchemy 0.1.7__tar.gz → 0.1.8__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.
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/PKG-INFO +2 -2
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/datus_sqlalchemy/connector.py +28 -1
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/pyproject.toml +2 -2
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/tests/unit/test_connector_unit.py +4 -1
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/.gitignore +0 -0
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/README.md +0 -0
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/datus_sqlalchemy/__init__.py +0 -0
- {datus_sqlalchemy-0.1.7 → datus_sqlalchemy-0.1.8}/tests/unit/test_migration_mixin.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datus-sqlalchemy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: SQLAlchemy base connector for Datus database adapters
|
|
5
5
|
Project-URL: Homepage, https://github.com/Datus-ai/datus-db-adapters
|
|
6
6
|
Project-URL: Repository, https://github.com/Datus-ai/datus-db-adapters
|
|
@@ -14,7 +14,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
|
-
Requires-Dist: datus-db-core>=0.1.
|
|
17
|
+
Requires-Dist: datus-db-core>=0.1.5
|
|
18
18
|
Requires-Dist: pandas>=2.1.4
|
|
19
19
|
Requires-Dist: pyarrow<19.0.0,>=14.0.0
|
|
20
20
|
Requires-Dist: sqlalchemy>=2.0.23
|
|
@@ -40,6 +40,32 @@ from datus_db_core import (
|
|
|
40
40
|
|
|
41
41
|
logger = get_logger(__name__)
|
|
42
42
|
|
|
43
|
+
_SQL_PREVIEW_CHARS = 50
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _log_sql_exception(event: str, sql: str, exc: Exception) -> None:
|
|
47
|
+
"""Log a SQL failure with a bounded preview instead of the full statement."""
|
|
48
|
+
statement = sql or ""
|
|
49
|
+
normalized_sql = " ".join(statement.split())
|
|
50
|
+
sql_preview = normalized_sql[:_SQL_PREVIEW_CHARS]
|
|
51
|
+
if len(normalized_sql) > _SQL_PREVIEW_CHARS:
|
|
52
|
+
sql_preview += "..."
|
|
53
|
+
driver_error = str(getattr(exc, "orig", None) or exc) or type(exc).__name__
|
|
54
|
+
if statement:
|
|
55
|
+
driver_error = driver_error.replace(statement, "<sql>")
|
|
56
|
+
if normalized_sql and normalized_sql != statement:
|
|
57
|
+
driver_error = driver_error.replace(normalized_sql, "<sql>")
|
|
58
|
+
safe_exception = RuntimeError(driver_error)
|
|
59
|
+
logger.error(
|
|
60
|
+
"%s; sql_preview=%r; sql_chars=%d; error_type=%s; error=%s",
|
|
61
|
+
event,
|
|
62
|
+
sql_preview,
|
|
63
|
+
len(statement),
|
|
64
|
+
type(exc).__name__,
|
|
65
|
+
driver_error,
|
|
66
|
+
exc_info=(type(safe_exception), safe_exception, exc.__traceback__),
|
|
67
|
+
)
|
|
68
|
+
|
|
43
69
|
|
|
44
70
|
class SQLAlchemyConnector(BaseSqlConnector, MigrationTargetMixin):
|
|
45
71
|
"""
|
|
@@ -170,6 +196,7 @@ class SQLAlchemyConnector(BaseSqlConnector, MigrationTargetMixin):
|
|
|
170
196
|
"""Map SQLAlchemy exceptions to Datus exceptions."""
|
|
171
197
|
if isinstance(e, DatusDbException):
|
|
172
198
|
return e
|
|
199
|
+
_log_sql_exception(f"{operation} failed", sql, e)
|
|
173
200
|
|
|
174
201
|
# Extract error message
|
|
175
202
|
if hasattr(e, "detail") and e.detail:
|
|
@@ -767,7 +794,7 @@ class SQLAlchemyConnector(BaseSqlConnector, MigrationTargetMixin):
|
|
|
767
794
|
yield ()
|
|
768
795
|
yield from []
|
|
769
796
|
except Exception as e:
|
|
770
|
-
raise self._handle_exception(e) from e
|
|
797
|
+
raise self._handle_exception(e, sql, "streaming query") from e
|
|
771
798
|
|
|
772
799
|
# ==================== MigrationTargetMixin (generic fallback) ====================
|
|
773
800
|
#
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datus-sqlalchemy"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.8"
|
|
4
4
|
description = "SQLAlchemy base connector for Datus database adapters"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
dependencies = [
|
|
21
|
-
"datus-db-core>=0.1.
|
|
21
|
+
"datus-db-core>=0.1.5",
|
|
22
22
|
"sqlalchemy>=2.0.23",
|
|
23
23
|
"pyarrow>=14.0.0,<19.0.0",
|
|
24
24
|
"pandas>=2.1.4",
|
|
@@ -255,13 +255,16 @@ def test_execute_ddl_with_context_params():
|
|
|
255
255
|
),
|
|
256
256
|
],
|
|
257
257
|
)
|
|
258
|
-
def
|
|
258
|
+
def test_handle_exception_classifies_and_logs_common_failures(exception, expected_code, caplog):
|
|
259
259
|
"""SQLAlchemy failures should map to stable Datus error categories."""
|
|
260
260
|
connector = DummySQLAlchemyConnector("sqlite://", dialect="sqlite")
|
|
261
261
|
|
|
262
262
|
classified = connector._handle_exception(exception, "SELECT 1", "query")
|
|
263
263
|
|
|
264
264
|
assert classified.code == expected_code
|
|
265
|
+
assert "query failed; sql_preview='SELECT 1'; sql_chars=8" in caplog.text
|
|
266
|
+
assert caplog.text.count("SELECT 1") == 1
|
|
267
|
+
assert caplog.records[-1].exc_info[2] is exception.__traceback__
|
|
265
268
|
|
|
266
269
|
|
|
267
270
|
def test_ensure_engine_classifies_connection_creation_failure():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|