From ab1f42d2ce2b0ca1997520b735d18a879cb7d0f2 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Sat, 3 Oct 2026 10:28:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=9F=A5=E8=AF=A2=E7=9A=84?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E5=8F=A3=E5=BE=84=E5=AF=B9=E9=BD=90=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=B1=82=EF=BC=9A=E5=8F=96=E6=BB=A1=E4=B8=8A=E9=99=90?= =?UTF-8?q?=E4=B9=8B=E5=90=8E=E7=A1=AE=E5=AE=9E=E8=BF=98=E6=9C=89=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E8=A1=8C=E6=89=8D=E7=AE=97=E6=88=AA=E6=96=AD=EF=BC=8C?= =?UTF-8?q?=E6=81=B0=E5=A5=BD=E5=8F=96=E6=BB=A1=E7=9A=84=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E4=B8=8D=E5=86=8D=E8=A2=AB=E4=B8=8B=E6=B8=B8?= =?UTF-8?q?=E6=8B=92=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/engine/merge_query.py | 12 +++++++----- backend/tests/test_merge_engine.py | 7 +++++++ 2 files changed, 14 insertions(+), 5 deletions(-) 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