Skip to content

Commit 4ae86e1

Browse files
fix: address reviewer feedback for Db2 CI isolation and error handling
- Skip test_sqlglot_extended_correctly when db2-sqlglot-dialect is not installed - Use static exp.DataType.Type enums in SCHEMA_DIFFER_KWARGS in db2.py to allow safe doc-tests collection without plugin - Revert dead db2 migration code in migration.py to match upstream - Raise explicit ConfigError in Db2ConnectionConfig._engine_adapter when plugin is missing - Add plugin disclaimer and SCD Type 2 limitation note in db2.md docs - Add pyproject.toml to db2.yaml path filters - Clean up commented-out test configurations in config.yaml and test_integration.py Signed-off-by: IBM Db2 Eco System <Hdm-dev-persona-db2-eco-system@ibm.com>
1 parent f40c7ee commit 4ae86e1

8 files changed

Lines changed: 27 additions & 38 deletions

File tree

‎.github/workflows/db2.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
- 'tests/core/engine_adapter/integration/test_integration_db2.py'
1111
- 'tests/core/engine_adapter/integration/docker/compose.db2.yaml'
1212
- '.github/workflows/db2.yaml'
13+
- 'pyproject.toml'
1314
pull_request:
1415
branches:
1516
- main
@@ -19,6 +20,7 @@ on:
1920
- 'tests/core/engine_adapter/integration/test_integration_db2.py'
2021
- 'tests/core/engine_adapter/integration/docker/compose.db2.yaml'
2122
- '.github/workflows/db2.yaml'
23+
- 'pyproject.toml'
2224

2325
concurrency:
2426
group: db2-${{ github.event.pull_request.number || github.sha }}

‎docs/integrations/engines/db2.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This page provides information about how to use SQLMesh with [IBM Db2](https://w
55
!!! info
66
The Db2 engine adapter is a community contribution. Due to this, only limited community support is available.
77

8+
This adapter depends on the external `db2-sqlglot-dialect` package, which pins its own SQLGlot version range. It isn't covered by SQLMesh's core CI and may temporarily break or fail to install after SQLMesh upgrades SQLGlot, until the plugin catches up.
9+
810
## Local/Built-in Scheduler
911

1012
**Engine Adapter Type**: `db2`
@@ -63,6 +65,7 @@ physical_table_naming_convention: hash_md5
6365

6466
## Limitations
6567

68+
- **SCD Type 2 unsupported**: SCD Type 2 models are currently not supported on Db2.
6669
- **Single catalog only**: Db2 operates in single-catalog mode; cross-catalog queries are not supported.
6770
- **No inline column comments**: Column-level comments cannot be set inline during table creation.
6871
- **No atomic table replacement**: Db2 does not support `CREATE OR REPLACE TABLE`, so full model refreshes are not atomic. There is a brief window during which the table may be empty or partially populated.

‎sqlmesh/core/config/connection.py‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2643,11 +2643,13 @@ def _connection_kwargs_keys(self) -> t.Set[str]:
26432643

26442644
@property
26452645
def _engine_adapter(self) -> t.Type[EngineAdapter]:
2646-
# DB2 adapter requires Python 3.10+ for db2-sqlglot-dialect
2647-
# Use getattr to avoid mypy errors on Python 3.9
2648-
return t.cast(
2649-
t.Type[EngineAdapter], getattr(engine_adapter, "Db2EngineAdapter", EngineAdapter)
2650-
)
2646+
adapter = getattr(engine_adapter, "Db2EngineAdapter", None)
2647+
if adapter is None:
2648+
raise ConfigError(
2649+
"The Db2 engine adapter requires Python 3.10+ and the 'db2' extra. "
2650+
"Please install it with: pip install 'sqlmesh[db2]'"
2651+
)
2652+
return t.cast(t.Type[EngineAdapter], adapter)
26512653

26522654
def get_catalog(self) -> t.Optional[str]:
26532655
"""Db2 stores catalog names in uppercase; normalise here so the default_catalog

‎sqlmesh/core/engine_adapter/db2.py‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,21 @@ class Db2EngineAdapter(
6565
SCHEMA_DIFFER_KWARGS = {
6666
"parameterized_type_defaults": {
6767
# DECIMAL without precision defaults to (5, 0)
68-
exp.DataType.build("DECIMAL", dialect=DIALECT).this: [(5, 0), (0,)],
68+
exp.DataType.Type.DECIMAL: [(5, 0), (0,)],
6969
# CHAR without length defaults to 1
70-
exp.DataType.build("CHAR", dialect=DIALECT).this: [(1,)],
70+
exp.DataType.Type.CHAR: [(1,)],
7171
# VARCHAR without length defaults to 1
72-
exp.DataType.build("VARCHAR", dialect=DIALECT).this: [(1,)],
72+
exp.DataType.Type.VARCHAR: [(1,)],
7373
# TIMESTAMP defaults to 6 digits of fractional seconds
74-
exp.DataType.build("TIMESTAMP", dialect=DIALECT).this: [(6,)],
74+
exp.DataType.Type.TIMESTAMP: [(6,)],
7575
# TIME defaults to 0 digits of fractional seconds
76-
exp.DataType.build("TIME", dialect=DIALECT).this: [(0,)],
76+
exp.DataType.Type.TIME: [(0,)],
7777
},
7878
"types_with_unlimited_length": {
7979
# CLOB can be used for unlimited text
80-
exp.DataType.build("CLOB", dialect=DIALECT).this: {
81-
exp.DataType.build("VARCHAR", dialect=DIALECT).this,
82-
exp.DataType.build("CHAR", dialect=DIALECT).this,
80+
exp.DataType.Type.TEXT: {
81+
exp.DataType.Type.VARCHAR,
82+
exp.DataType.Type.CHAR,
8383
},
8484
},
8585
"drop_cascade": False,

‎sqlmesh/utils/migration.py‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
MAX_TEXT_INDEX_LENGTH = {
55
"mysql": "250", # 250 characters per column, <= 767 byte index size limit
66
"tsql": "450", # 450 bytes per column, <= 900 byte index size limit
7-
"db2": "255", # Db2 has strict primary key size limits, keep it conservative
87
}
98

109

@@ -24,8 +23,4 @@ def index_text_type(dialect: DialectType) -> str:
2423

2524

2625
def blob_text_type(dialect: DialectType) -> str:
27-
if dialect == "mysql":
28-
return "LONGTEXT"
29-
if dialect == "db2":
30-
return "VARCHAR(32000)"
31-
return "TEXT"
26+
return "LONGTEXT" if dialect == "mysql" else "TEXT"

‎tests/core/engine_adapter/integration/config.yaml‎

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -200,20 +200,6 @@ gateways:
200200
state_connection:
201201
type: duckdb
202202

203-
# inttest_db2:
204-
# connection:
205-
# type: db2
206-
# host: {{ env_var('DB2_HOST') }}
207-
# port: {{ env_var('DB2_PORT', '50000') }}
208-
# database: {{ env_var('DB2_DATABASE') }}
209-
# username: {{ env_var('DB2_USERNAME') }}
210-
# password: {{ env_var('DB2_PASSWORD') }}
211-
# # db2_schema sets CURRENTSCHEMA on the connection — controls the default schema
212-
# # for unqualified references. The test framework always uses fully-qualified names
213-
# # so any valid schema the user has access to works here (e.g. the username itself,
214-
# # which is the Db2 default when no schema is specified).
215-
# db2_schema: {{ env_var('DB2_SCHEMA', env_var('DB2_USERNAME')) }}
216-
# check_import: false
217203
inttest_db2:
218204
connection:
219205
type: db2

‎tests/core/engine_adapter/integration/test_integration.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ def dev_table_name_for(self, snapshot: Snapshot) -> str:
9393

9494
def test_connection(ctx: TestContext):
9595
cursor_from_connection = ctx.engine_adapter.connection.cursor()
96-
# cursor_from_connection.execute("SELECT 1") # fails on Db2 — bare SELECT 1 raises SQL0104N
97-
# Fix: use dialect-aware SQL so Db2 generates SELECT 1 FROM SYSIBM.SYSDUMMY1
9896
cursor_from_connection.execute(exp.select("1").sql(dialect=ctx.dialect))
9997
assert cursor_from_connection.fetchone()[0] == 1
10098

‎tests/core/test_dialect.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib.util
12
import sys
23
import pytest
34
from sqlglot import Dialect, ParseError, exp, parse_one
@@ -1051,9 +1052,11 @@ def test_parse_snowflake_create_schema_ddl():
10511052

10521053
@pytest.mark.parametrize("dialect", sorted(set(DIALECT_TO_TYPE.values())))
10531054
def test_sqlglot_extended_correctly(dialect: str) -> None:
1054-
# Skip Db2 on Python 3.9 since db2-sqlglot-dialect requires Python 3.10+
1055-
if dialect == "db2" and sys.version_info < (3, 10):
1056-
pytest.skip("DB2 dialect requires Python 3.10+ for db2-sqlglot-dialect")
1055+
# Skip Db2 if db2-sqlglot-dialect is not installed
1056+
if dialect == "db2" and (
1057+
sys.version_info < (3, 10) or importlib.util.find_spec("db2_sqlglot") is None
1058+
):
1059+
pytest.skip("db2-sqlglot-dialect is not installed")
10571060

10581061
# MODEL is a SQLMesh extension and not part of SQLGlot
10591062
# If we can roundtrip an expression containing MODEL across every dialect, then the SQLMesh extensions have been registered correctly

0 commit comments

Comments
 (0)