diff --git a/backend/app/engine/merge_query.py b/backend/app/engine/merge_query.py index 4d473886..52a54d59 100644 --- a/backend/app/engine/merge_query.py +++ b/backend/app/engine/merge_query.py @@ -343,21 +343,23 @@ def _authorize(action: int, *_args: Any) -> int: def _read(conn: sqlite3.Connection, stmt: str, limits: QueryLimits) -> tuple[list[str], list[list[Any]], bool]: - """逐行读、边读边判上限:和查询层(data.engine.run_query)同一个口径,到了行数或字节上限就停,记 truncated。""" + """逐行读、边读边判上限:和查询层(data.engine.run_query)同一个口径——到了行数或字节上限之后 + 确实还有下一行才记 truncated,恰好取满不算(截断的合并结果下游会拒收,误报就是拒了完整的结果)。""" cursor = conn.execute(stmt) columns = [str(d[0]) for d in cursor.description or []] if not columns: raise MergeError("合并 SQL 没有返回任何列") rows: list[list[Any]] = [] size = 0 - truncated = False + truncated = full = False for row in cursor: + if full: + truncated = True + break values = [_jsonable(v) for v in row] rows.append(values) size += len(json.dumps(values, ensure_ascii=False, default=str)) - if len(rows) >= limits.max_rows or size >= limits.max_bytes: - truncated = True - break + full = len(rows) >= limits.max_rows or size >= limits.max_bytes return columns, rows, truncated diff --git a/backend/tests/test_merge_engine.py b/backend/tests/test_merge_engine.py index 55bda2be..9acf20c9 100644 --- a/backend/tests/test_merge_engine.py +++ b/backend/tests/test_merge_engine.py @@ -213,6 +213,13 @@ def test_result_over_the_row_limit_is_truncated_and_warned(): assert "result_truncated" in [w["code"] for w in out.warnings] +def test_exactly_full_result_is_not_truncated(): + """恰好取满上限、后面没有了:和查询层同一个口径,不算截断——截断的合并结果下游会拒收,误报就是把完整的结果拒了。""" + out = execute([sales(), visits()], "SELECT * FROM s CROSS JOIN v", limits=QueryLimits(max_rows=16)) + assert out.truncated is False and len(out.rows) == 16 + assert "result_truncated" not in [w["code"] for w in out.warnings] + + def test_byte_limit_matches_the_query_layer(): out = execute([sales(), visits()], "SELECT * FROM s CROSS JOIN v", limits=QueryLimits(max_bytes=100)) assert out.truncated is True and 0 < len(out.rows) < 16