Skip to content

Commit 7235fde

Browse files
author
ci bot
committed
Merge branch 'fix/handle-null-max-chars' into 'enterprise'
fix: harden profiling/test-execution against NULL connection.max_query_chars (TG-1114) See merge request dkinternal/testgen/dataops-testgen!536
2 parents 4ba412a + f6abfa3 commit 7235fde

6 files changed

Lines changed: 42 additions & 6 deletions

File tree

testgen/commands/queries/execute_tests_query.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
is_excluded_day,
2222
resolve_holiday_dates,
2323
)
24-
from testgen.common.models.connection import Connection
24+
from testgen.common.models.connection import DEFAULT_MAX_QUERY_CHARS, Connection
2525
from testgen.common.models.scheduler import JobSchedule
2626
from testgen.common.models.table_group import TableGroup
2727
from testgen.common.models.test_definition import TestRunType, TestScope
@@ -534,7 +534,7 @@ def aggregate_cat_tests(
534534
null_value=self.null_value,
535535
)
536536

537-
max_query_chars = self.connection.max_query_chars - 400
537+
max_query_chars = (self.connection.max_query_chars or DEFAULT_MAX_QUERY_CHARS) - 400
538538
groups = group_cat_tests(test_defs, max_query_chars, concat_operator, single)
539539

540540
aggregate_queries: list[tuple[str, None]] = []

testgen/commands/queries/refresh_data_chars_query.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from testgen.common import read_template_sql_file
66
from testgen.common.database.column_chars import ColumnChars
77
from testgen.common.database.database_service import get_flavor_service, replace_params
8-
from testgen.common.models.connection import Connection
8+
from testgen.common.models.connection import DEFAULT_MAX_QUERY_CHARS, Connection
99
from testgen.common.models.table_group import TableGroup
1010
from testgen.utils import chunk_queries, to_sql_timestamp
1111

@@ -133,7 +133,8 @@ def get_row_counts(self, table_names: Iterable[str]) -> list[tuple[str, None]]:
133133
f"SELECT '{table}' AS table_name, COUNT(*) AS row_count FROM {self.flavor_service.get_table_ref(schema, table)}"
134134
for table in table_names
135135
]
136-
chunked_queries = chunk_queries(count_queries, " UNION ALL ", self.connection.max_query_chars)
136+
max_query_chars = self.connection.max_query_chars or DEFAULT_MAX_QUERY_CHARS
137+
chunked_queries = chunk_queries(count_queries, " UNION ALL ", max_query_chars)
137138
return [ (query, None) for query in chunked_queries ]
138139

139140
def verify_access(self, table_name: str) -> tuple[str, None]:

testgen/common/database/connection_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from sqlalchemy.exc import DatabaseError, DBAPIError
2727

2828
from testgen.common.database.database_service import empty_cache, get_flavor_service
29-
from testgen.common.models.connection import Connection
29+
from testgen.common.models.connection import DEFAULT_MAX_QUERY_CHARS, Connection
3030
from testgen.ui.services.database_service import fetch_from_target_db
3131

3232
try:
@@ -142,4 +142,4 @@ def apply_connection_defaults(connection: Connection) -> None:
142142
"""Fill flavor-dependent defaults for fields the caller didn't supply."""
143143
if connection.max_query_chars is None:
144144
# Salesforce Data 360's Hyper engine has a lower expression-depth limit
145-
connection.max_query_chars = 15000 if connection.sql_flavor_code == "salesforce_data360" else 20000
145+
connection.max_query_chars = 15000 if connection.sql_flavor_code == "salesforce_data360" else DEFAULT_MAX_QUERY_CHARS

testgen/common/models/connection.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828

2929
SQLFlavorCode = Literal["redshift", "redshift_spectrum", "snowflake", "mssql", "azure_mssql", "synapse_mssql", "postgresql", "databricks", "bigquery", "oracle", "sap_hana", "salesforce_data360"]
3030

31+
# Fallback when a connection row has a NULL max_query_chars (no DB default; older
32+
# rows / non-UI insert paths may not seed it). Matches the UI's non-Salesforce
33+
# default and the 0160 migration.
34+
DEFAULT_MAX_QUERY_CHARS = 20000
35+
3136

3237
@dataclass
3338
class ConnectionMinimal(EntityMinimal):

tests/unit/commands/queries/test_execute_tests_query.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
group_cat_tests,
1212
parse_cat_results,
1313
)
14+
from testgen.common.database.database_service import get_flavor_service
15+
from testgen.common.models.connection import Connection
1416

1517
pytestmark = pytest.mark.unit
1618

@@ -478,3 +480,18 @@ def test_resolve_cat_no_freshness_result_uses_band_check(_mock_changed):
478480
assert operator == "NOT BETWEEN"
479481

480482

483+
def test_aggregate_cat_tests_handles_null_max_query_chars():
484+
"""A connection with NULL max_query_chars must not crash CAT batching — the
485+
`- 400` headroom subtraction falls back to DEFAULT_MAX_QUERY_CHARS."""
486+
instance = _make_execution_sql()
487+
instance.connection = Connection(sql_flavor="postgresql", max_query_chars=None)
488+
instance.flavor = "postgresql"
489+
instance.flavor_service = get_flavor_service("postgresql")
490+
491+
td = _make_td(measure_expression="m_expr", condition_expression="c_expr")
492+
queries, grouped_defs = instance.aggregate_cat_tests([td], single=True)
493+
494+
assert len(queries) == 1
495+
assert grouped_defs == [[td]]
496+
497+

tests/unit/commands/queries/test_refresh_data_chars_query.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,16 @@ def test_filter_schema_columns_no_filters_returns_all():
217217
filtered = sql_generator.filter_schema_columns(columns)
218218

219219
assert {c.table_name for c in filtered} == {"users", "orders"}
220+
221+
222+
def test_get_row_counts_handles_null_max_query_chars():
223+
"""A connection with NULL max_query_chars must not crash chunking — it falls
224+
back to DEFAULT_MAX_QUERY_CHARS so the UNION ALL count queries still build."""
225+
connection = Connection(sql_flavor="postgresql", max_query_chars=None)
226+
table_group = TableGroup(table_group_schema="test_schema")
227+
sql_generator = RefreshDataCharsSQL(connection, table_group)
228+
229+
result = sql_generator.get_row_counts(["orders", "customers"])
230+
231+
assert result
232+
assert all(isinstance(query, str) and query for query, _ in result)

0 commit comments

Comments
 (0)