diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index b36125c762..676625df6f 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1621,11 +1621,12 @@ class SQLCompiler: # tuples with integers and strings. Flatten them out into strings. format_ = self.query.explain_info.format output_formatter = json.dumps if format_ and format_.lower() == "json" else str - for row in result[0]: - if not isinstance(row, str): - yield " ".join(output_formatter(c) for c in row) - else: - yield row + for row in result: + for value in row: + if not isinstance(value, str): + yield " ".join([output_formatter(c) for c in value]) + else: + yield value class SQLInsertCompiler(SQLCompiler): diff --git a/tests/queries/test_explain.py b/tests/queries/test_explain.py index 980e8e9621..44689aedf8 100644 --- a/tests/queries/test_explain.py +++ b/tests/queries/test_explain.py @@ -96,6 +96,15 @@ class ExplainTests(TestCase): option = "{} {}".format(name.upper(), "true" if value else "false") self.assertIn(option, captured_queries[0]["sql"]) + def test_multi_page_text_explain(self): + if "TEXT" not in connection.features.supported_explain_formats: + self.skipTest("This backend does not support TEXT format.") + + base_qs = Tag.objects.order_by() + qs = base_qs.filter(name="test").union(*[base_qs for _ in range(100)]) + result = qs.explain(format="text") + self.assertGreaterEqual(result.count("\n"), 100) + def test_option_sql_injection(self): qs = Tag.objects.filter(name="test") options = {"SUMMARY true) SELECT 1; --": True}