Skip to content

Commit ae76529

Browse files
committed
fix(db2): implement grants via GrantsFromInfoSchemaMixin + role-based test infrastructure
db2.py: - Add GrantsFromInfoSchemaMixin to Db2EngineAdapter — provides _get_current_grants_config, _apply_grants_config_expr, _revoke_grants_config_expr via INFORMATION_SCHEMA.table_privileges - Set CURRENT_USER_OR_ROLE_EXPRESSION to CURRENT USER (Db2 special register) - Add _grant_object_kind() returning 'TABLE' (Db2 GRANT requires the TABLE keyword) Without the mixin, SUPPORTS_GRANTS=True with no implementations caused NotImplementedError on every grant test method call. __init__.py: - Add db2 case to _get_create_user_or_role(): CREATE ROLE (Db2 LUW uses OS-level users for auth; roles work for GRANT/REVOKE testing without OS user setup) - Add db2 to _cleanup_user_or_role(): DROP ROLE IF EXISTS (same as Snowflake)
1 parent e306cdb commit ae76529

2 files changed

Lines changed: 17 additions & 2 deletions

File tree

sqlmesh/core/engine_adapter/db2.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
1010

1111
from sqlmesh.core.engine_adapter.base import EngineAdapter, _get_data_object_cache_key
12-
from sqlmesh.core.engine_adapter.mixins import PandasNativeFetchDFSupportMixin
12+
from sqlmesh.core.engine_adapter.mixins import (
13+
GrantsFromInfoSchemaMixin,
14+
PandasNativeFetchDFSupportMixin,
15+
)
1316
from sqlmesh.core.engine_adapter.shared import (
1417
CatalogSupport,
1518
CommentCreationTable,
@@ -43,13 +46,16 @@ def is_db2_error(exception: Exception, error_code: str) -> bool:
4346

4447
@set_catalog()
4548
class Db2EngineAdapter(
49+
GrantsFromInfoSchemaMixin,
4650
PandasNativeFetchDFSupportMixin,
4751
EngineAdapter,
4852
):
4953
DIALECT = "db2"
5054
SUPPORTS_INDEXES = True
5155
SUPPORTS_REPLACE_TABLE = False
5256
SUPPORTS_GRANTS = True
57+
# Db2 uses CURRENT USER special register to identify the grantor.
58+
CURRENT_USER_OR_ROLE_EXPRESSION: exp.Expr = exp.column("CURRENT USER")
5359
COMMENT_CREATION_TABLE = CommentCreationTable.COMMENT_COMMAND_ONLY
5460
COMMENT_CREATION_VIEW = CommentCreationView.COMMENT_COMMAND_ONLY
5561
SUPPORTS_QUERY_EXECUTION_TRACKING = True
@@ -324,6 +330,11 @@ def _db2_type_to_sqlglot(self, db2_type: str, length: int, scale: int) -> exp.Da
324330
sqlglot_type = type_mapping.get(db2_type, f"VARCHAR({length})")
325331
return exp.DataType.build(sqlglot_type, dialect="db2")
326332

333+
@staticmethod
334+
def _grant_object_kind(table_type: DataObjectType) -> t.Optional[str]:
335+
"""Db2 GRANT/REVOKE requires TABLE keyword for tables and views."""
336+
return "TABLE"
337+
327338
@property
328339
def catalog_support(self) -> CatalogSupport:
329340
return CatalogSupport.SINGLE_CATALOG_ONLY

tests/core/engine_adapter/integration/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,10 @@ def _get_create_user_or_role(
818818
project_id = self.engine_adapter.get_current_catalog()
819819
service_account = f"sqlmesh-test-{role_name}@{project_id}.iam.gserviceaccount.com"
820820
return f"serviceAccount:{service_account}", None
821+
if self.dialect == "db2":
822+
# Db2 LUW uses OS-level users for authentication, but database roles
823+
# work for GRANT/REVOKE testing without requiring OS user setup.
824+
return username, f"CREATE ROLE {username}"
821825
raise ValueError(f"User creation not supported for dialect: {self.dialect}")
822826

823827
def _create_user_or_role(self, username: str, password: t.Optional[str] = None) -> str:
@@ -883,7 +887,7 @@ def _cleanup_user_or_role(self, user_name: str) -> None:
883887
""")
884888
self.engine_adapter.execute(f'DROP OWNED BY "{user_name}"')
885889
self.engine_adapter.execute(f'DROP USER IF EXISTS "{user_name}"')
886-
elif self.dialect == "snowflake":
890+
elif self.dialect in ["snowflake", "db2"]:
887891
self.engine_adapter.execute(f"DROP ROLE IF EXISTS {user_name}")
888892
elif self.dialect in ["databricks", "bigquery"]:
889893
# For Databricks and BigQuery, we use pre-created accounts that should not be deleted

0 commit comments

Comments
 (0)