Skip to content

Commit 30ab44c

Browse files
committed
fix(sqlserver): apply set operation, type, and fixture fixes
- Remove parentheses wrapping in format_set_operation_expression - Add BlobType DDL handler in types mixin - Fix create_type_cases_table column types (use proper types) - Fix comments.author FK to NO_ACTION (SQL Server cascade rule) - Remove TinyIntType references from fixtures - Update providers for new testsuite integration
1 parent 5fbaec9 commit 30ab44c

15 files changed

Lines changed: 1722 additions & 395 deletions

File tree

‎src/rhosocial/activerecord/backend/impl/sqlserver/async_backend.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import time
1212
from typing import Any, Dict, List, Optional, Tuple
1313

14-
from rhosocial.activerecord.backend.base import StorageBackend
14+
from rhosocial.activerecord.backend.base import AsyncStorageBackend
1515
from rhosocial.activerecord.backend.base.operations import AsyncSQLOperationsMixin
1616
from rhosocial.activerecord.backend.base.transaction_management import AsyncTransactionManagementMixin
1717
from rhosocial.activerecord.backend.errors import (
@@ -36,11 +36,9 @@
3636

3737

3838
class AsyncSQLServerBackend(
39-
AsyncSQLOperationsMixin,
40-
AsyncTransactionManagementMixin,
4139
IntrospectorBackendMixin,
4240
SQLServerBackendMixin,
43-
StorageBackend
41+
AsyncStorageBackend
4442
):
4543
"""Asynchronous SQL Server backend using aioodbc.
4644

‎src/rhosocial/activerecord/backend/impl/sqlserver/dialect.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1569,7 +1569,8 @@ def format_set_operation_expression(
15691569
base_sql = f"{left_sql} {operation}{all_str} {right_sql}"
15701570

15711571
all_params = list(left_params + right_params)
1572-
sql_parts = [f"({base_sql})"]
1572+
1573+
sql_parts = [base_sql]
15731574

15741575
if alias:
15751576
sql_parts.append(f"AS {self.format_identifier(alias)}")

‎src/rhosocial/activerecord/backend/impl/sqlserver/mixins/types.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from rhosocial.activerecord.backend.dialect.protocols import DDLTypeSupport
1111
from rhosocial.activerecord.backend.expression.types import (
1212
BigIntType,
13+
BlobType,
1314
BooleanType,
1415
CharType,
1516
CustomType,
@@ -100,6 +101,10 @@ def format_data_type_timestamp(self, data_type: TimestampType) -> Tuple[str, tup
100101
def format_data_type_json(self, data_type: JsonType) -> Tuple[str, tuple]:
101102
return "NVARCHAR(MAX)", ()
102103

104+
@DDLTypeMixin.handles(BlobType)
105+
def format_data_type_blob(self, data_type: BlobType) -> Tuple[str, tuple]:
106+
return "VARBINARY(MAX)", ()
107+
103108
# --- Parsing ---
104109

105110
def parse_type(self, raw: str) -> DataType:

‎tests/providers/basic.py‎

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,11 @@ def _select_model_class(base_cls, py312_cls, py311_cls, py310_cls, model_name: s
324324

325325
from .scenarios import get_enabled_scenarios, get_scenario # noqa: E402
326326

327+
from providers.fixtures._common import (
328+
safe_drop_table, safe_drop_table_async,
329+
disable_fk_checks, enable_fk_checks,
330+
disable_fk_checks_async, enable_fk_checks_async,
331+
)
327332

328333
class BasicProviderBase:
329334
def __init__(self):
@@ -374,19 +379,7 @@ def _reset_table_sync(self, model_class: Type[ActiveRecord], table_name: str) ->
374379

375380
options = ExecutionOptions(stmt_type=StatementType.DDL)
376381
backend = model_class.__backend__
377-
try:
378-
backend.execute("SET FOREIGN_KEY_CHECKS = 0")
379-
try:
380-
drop_expr = DropTableExpression(
381-
dialect=backend.dialect,
382-
table=TableExpression(backend.dialect, table_name),
383-
if_exists=True,
384-
)
385-
backend.execute(*drop_expr.to_sql(), options=options)
386-
except Exception:
387-
backend.execute(f"DROP TABLE IF EXISTS `{table_name}`")
388-
finally:
389-
backend.execute("SET FOREIGN_KEY_CHECKS = 1")
382+
safe_drop_table(backend, backend.dialect, table_name)
390383
if fn := TABLE_EXPRESSIONS.get(table_name):
391384
create_expr = fn(backend.dialect, table_name)
392385
create_sql, params = to_sqlserver_ddl_sql(create_expr)
@@ -533,20 +526,15 @@ def get_worker_schema_sql(self, scenario_name: str, table_name: str) -> str:
533526

534527
def cleanup_after_test(self, scenario_name: str):
535528
for backend_instance in self._active_backends:
536-
try:
537-
if self._created_tables:
538-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 0")
539-
for table_name in list(self._created_tables):
540-
try:
541-
backend_instance.execute(f"DROP TABLE IF EXISTS `{table_name}`")
542-
except Exception:
543-
pass
544-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
545-
finally:
529+
for table_name in list(self._created_tables):
546530
try:
547-
backend_instance.disconnect()
531+
safe_drop_table(backend_instance, backend_instance.dialect, table_name)
548532
except Exception:
549533
pass
534+
try:
535+
backend_instance.disconnect()
536+
except Exception:
537+
pass
550538
self._active_backends.clear()
551539
self._created_tables.clear()
552540

@@ -577,19 +565,7 @@ async def _reset_table_async(self, model_class: Type[ActiveRecord], table_name:
577565

578566
options = ExecutionOptions(stmt_type=StatementType.DDL)
579567
backend = model_class.__backend__
580-
try:
581-
await backend.execute("SET FOREIGN_KEY_CHECKS = 0")
582-
try:
583-
drop_expr = DropTableExpression(
584-
dialect=backend.dialect,
585-
table=TableExpression(backend.dialect, table_name),
586-
if_exists=True,
587-
)
588-
await backend.execute(*drop_expr.to_sql(), options=options)
589-
except Exception:
590-
await backend.execute(f"DROP TABLE IF EXISTS `{table_name}`")
591-
finally:
592-
await backend.execute("SET FOREIGN_KEY_CHECKS = 1")
568+
await safe_drop_table_async(backend, backend.dialect, table_name)
593569
if fn := TABLE_EXPRESSIONS.get(table_name):
594570
create_expr = fn(backend.dialect, table_name)
595571
create_sql, params = to_sqlserver_ddl_sql(create_expr)
@@ -692,22 +668,14 @@ async def setup_product_with_column_and_adapter_model(self, scenario_name: str)
692668

693669
async def cleanup_after_test(self, scenario_name: str):
694670
for backend_instance in self._active_async_backends:
695-
try:
671+
for table_name in list(self._created_tables):
696672
try:
697-
if self._created_tables:
698-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 0")
699-
for table_name in list(self._created_tables):
700-
try:
701-
await backend_instance.execute(f"DROP TABLE IF EXISTS `{table_name}`")
702-
except Exception:
703-
pass
704-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
705-
except Exception:
706-
pass
707-
finally:
708-
try:
709-
await backend_instance.disconnect()
673+
await safe_drop_table_async(backend_instance, backend_instance.dialect, table_name)
710674
except Exception:
711675
pass
676+
try:
677+
await backend_instance.disconnect()
678+
except Exception:
679+
pass
712680
self._active_async_backends.clear()
713681
self._created_tables.clear()

‎tests/providers/events.py‎

Lines changed: 19 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ def _select_model_class(base_cls, py312_cls, py311_cls, py310_cls, model_name: s
7878

7979
from .scenarios import get_enabled_scenarios, get_scenario # noqa: E402
8080

81+
from providers.fixtures._common import (
82+
safe_drop_table, safe_drop_table_async,
83+
disable_fk_checks, enable_fk_checks,
84+
disable_fk_checks_async, enable_fk_checks_async,
85+
)
8186

8287
class EventsProviderBase:
8388
def __init__(self):
@@ -115,26 +120,7 @@ def _setup_model(self, model_class: Type[ActiveRecord], scenario_name: str, tabl
115120
if backend_instance not in self._active_backends:
116121
self._active_backends.append(backend_instance)
117122
options = ExecutionOptions(stmt_type=StatementType.DDL)
118-
try:
119-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 0")
120-
try:
121-
drop_expr = DropTableExpression(
122-
dialect=backend_instance.dialect,
123-
table=TableExpression(backend_instance.dialect, table_name),
124-
if_exists=True,
125-
)
126-
backend_instance.execute(*drop_expr.to_sql(), options=options)
127-
except Exception:
128-
backend_instance.execute(f"DROP TABLE IF EXISTS `{table_name}`")
129-
except Exception:
130-
try:
131-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
132-
except Exception:
133-
pass
134-
try:
135-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
136-
except Exception:
137-
pass
123+
safe_drop_table(backend_instance, backend_instance.dialect, table_name)
138124
if fn := TABLE_EXPRESSIONS.get(table_name):
139125
create_expr = fn(backend_instance.dialect, table_name)
140126
create_sql, params = to_sqlserver_ddl_sql(create_expr)
@@ -152,24 +138,15 @@ def setup_event_tracking_model(self, scenario_name: str) -> Type[ActiveRecord]:
152138

153139
def cleanup_after_test(self, scenario_name: str):
154140
for backend_instance in self._active_backends:
155-
try:
156-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 0")
157-
for table_name in ["event_tests", "event_tracking_models"]:
158-
try:
159-
backend_instance.execute(f"DROP TABLE IF EXISTS `{table_name}`")
160-
except Exception:
161-
pass
162-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
163-
except Exception:
164-
try:
165-
backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
166-
except Exception:
167-
pass
168-
finally:
141+
for table_name in ["event_tests", "event_tracking_models"]:
169142
try:
170-
backend_instance.disconnect()
143+
safe_drop_table(backend_instance, backend_instance.dialect, table_name)
171144
except Exception:
172145
pass
146+
try:
147+
backend_instance.disconnect()
148+
except Exception:
149+
pass
173150
self._active_backends.clear()
174151

175152

@@ -194,26 +171,7 @@ async def _setup_async_model(
194171
if backend_instance not in self._active_async_backends:
195172
self._active_async_backends.append(backend_instance)
196173
options = ExecutionOptions(stmt_type=StatementType.DDL)
197-
try:
198-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 0")
199-
try:
200-
drop_expr = DropTableExpression(
201-
dialect=backend_instance.dialect,
202-
table=TableExpression(backend_instance.dialect, table_name),
203-
if_exists=True,
204-
)
205-
await backend_instance.execute(*drop_expr.to_sql(), options=options)
206-
except Exception:
207-
await backend_instance.execute(f"DROP TABLE IF EXISTS `{table_name}`")
208-
except Exception:
209-
try:
210-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
211-
except Exception:
212-
pass
213-
try:
214-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
215-
except Exception:
216-
pass
174+
await safe_drop_table_async(backend_instance, backend_instance.dialect, table_name)
217175
if fn := TABLE_EXPRESSIONS.get(table_name):
218176
create_expr = fn(backend_instance.dialect, table_name)
219177
create_sql, params = to_sqlserver_ddl_sql(create_expr)
@@ -231,23 +189,13 @@ async def setup_event_tracking_model(self, scenario_name: str) -> Type[ActiveRec
231189

232190
async def cleanup_after_test(self, scenario_name: str):
233191
for backend_instance in self._active_async_backends:
234-
try:
235-
try:
236-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 0")
237-
for table_name in ["event_tests", "event_tracking_models"]:
238-
try:
239-
await backend_instance.execute(f"DROP TABLE IF EXISTS `{table_name}`")
240-
except Exception:
241-
pass
242-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
243-
except Exception:
244-
try:
245-
await backend_instance.execute("SET FOREIGN_KEY_CHECKS = 1")
246-
except Exception:
247-
pass
248-
finally:
192+
for table_name in ["event_tests", "event_tracking_models"]:
249193
try:
250-
await backend_instance.disconnect()
194+
await safe_drop_table_async(backend_instance, backend_instance.dialect, table_name)
251195
except Exception:
252196
pass
197+
try:
198+
await backend_instance.disconnect()
199+
except Exception:
200+
pass
253201
self._active_async_backends.clear()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# tests/providers/fixtures/__init__.py
2+
"""SQL Server-specific DDL expression fixtures for the testsuite providers.
3+
4+
Each module in this package exposes a ``TABLE_EXPRESSIONS`` mapping of
5+
table name -> factory callable ``Callable[[DialectLike, str], CreateTableExpression]``.
6+
7+
The factory functions build :class:`CreateTableExpression` instances that emit
8+
SQL Server-compatible DDL (INT IDENTITY / NVARCHAR / DATETIME2 / TINYINT / JSON).
9+
The pre-existing ``.sql`` schema files under
10+
``tests/rhosocial/activerecord_sqlserver_test/feature/<feature>/schema/``
11+
remain as the authoritative reference for what the expressions here must
12+
produce; they are simply no longer read at runtime by the providers.
13+
"""

0 commit comments

Comments
 (0)