|
17 | 17 | from .conversions import ( |
18 | 18 | BulkInsertJob, |
19 | 19 | DdlErrorHandler, |
| 20 | + DdlSplitter, |
20 | 21 | horolog_to_pg, |
21 | 22 | pg_to_horolog, |
22 | 23 | ) |
@@ -63,8 +64,9 @@ def __init__(self, iris_config: dict[str, Any], server=None): |
63 | 64 | # Column alias extraction for PostgreSQL compatibility |
64 | 65 | self.alias_extractor = AliasExtractor() |
65 | 66 |
|
66 | | - # DDL idempotency handler |
| 67 | + # DDL idempotency and splitting handlers |
67 | 68 | self.ddl_handler = DdlErrorHandler() |
| 69 | + self.ddl_splitter = DdlSplitter() |
68 | 70 |
|
69 | 71 | # Reusable translators to reduce overhead in hot paths |
70 | 72 | self.sql_translator = SQLTranslator() |
@@ -344,11 +346,24 @@ def _split_sql_statements(self, sql: str) -> list[str]: |
344 | 346 | if stmt: |
345 | 347 | statements.append(stmt) |
346 | 348 |
|
| 349 | + # Stage 2: Split multi-action ALTER TABLE statements |
| 350 | + # IRIS typically requires separate ALTER TABLE statements for each action |
| 351 | + final_statements = [] |
| 352 | + for stmt in statements: |
| 353 | + if stmt.upper().startswith("ALTER TABLE"): |
| 354 | + split_ddl = self.ddl_splitter.split_alter_table(stmt) |
| 355 | + final_statements.extend(split_ddl) |
| 356 | + else: |
| 357 | + final_statements.append(stmt) |
| 358 | + |
347 | 359 | logger.debug( |
348 | | - "Split SQL into statements", total_statements=len(statements), original_length=len(sql) |
| 360 | + "Split SQL into statements", |
| 361 | + total_statements=len(final_statements), |
| 362 | + original_statements=len(statements), |
| 363 | + original_length=len(sql), |
349 | 364 | ) |
350 | 365 |
|
351 | | - return statements |
| 366 | + return final_statements |
352 | 367 |
|
353 | 368 | async def test_connection(self): |
354 | 369 | """Test IRIS connectivity before starting server""" |
@@ -3965,9 +3980,24 @@ def _sync_external_execute(): |
3965 | 3980 | # PROFILING: IRIS execution timing |
3966 | 3981 | t_iris_start = time.perf_counter() |
3967 | 3982 |
|
3968 | | - # Execute query using safe execution wrapper |
3969 | | - # For external mode, safe_execute returns a cursor or split result |
3970 | | - cursor = self._safe_execute(optimized_sql, optimized_params, is_embedded=False) |
| 3983 | + # CRITICAL FIX: Split SQL by semicolons and handle multi-action ALTER TABLE |
| 3984 | + statements = self._split_sql_statements(optimized_sql) |
| 3985 | + |
| 3986 | + if len(statements) > 1: |
| 3987 | + logger.info( |
| 3988 | + "Executing multiple statements (external mode)", |
| 3989 | + statement_count=len(statements), |
| 3990 | + session_id=session_id, |
| 3991 | + ) |
| 3992 | + # Execute all statements except the last |
| 3993 | + for stmt in statements[:-1]: |
| 3994 | + self._safe_execute(stmt, optimized_params, is_embedded=False) |
| 3995 | + |
| 3996 | + # Execute last statement and capture cursor |
| 3997 | + cursor = self._safe_execute(statements[-1], optimized_params, is_embedded=False) |
| 3998 | + else: |
| 3999 | + # Single statement - execute normally |
| 4000 | + cursor = self._safe_execute(optimized_sql, optimized_params, is_embedded=False) |
3971 | 4001 |
|
3972 | 4002 | t_iris_elapsed = (time.perf_counter() - t_iris_start) * 1000 |
3973 | 4003 | execution_time = (time.perf_counter() - start_time) * 1000 |
|
0 commit comments