From fd34087b8cb42935f5cf1974a9f5f1c5acb84c18 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 Date: Mon, 27 May 2024 16:20:13 +0800 Subject: [PATCH] fix select from union all caused crash --- include/libs/nodes/plannodes.h | 1 + source/libs/planner/src/planLogicCreater.c | 1 + source/libs/planner/src/planOptimizer.c | 30 +++++++++++++++++----- tests/system-test/2-query/union1.py | 13 ++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/include/libs/nodes/plannodes.h b/include/libs/nodes/plannodes.h index 18bc24d612..a691433ee6 100644 --- a/include/libs/nodes/plannodes.h +++ b/include/libs/nodes/plannodes.h @@ -183,6 +183,7 @@ typedef struct SProjectLogicNode { char stmtName[TSDB_TABLE_NAME_LEN]; bool ignoreGroupId; bool inputIgnoreGroup; + bool isSetOpProj; } SProjectLogicNode; typedef struct SIndefRowsFuncLogicNode { diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index 23b8baf031..acc29997a2 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -1587,6 +1587,7 @@ static int32_t createSetOpProjectLogicNode(SLogicPlanContext* pCxt, SSetOperator TSWAP(pProject->node.pLimit, pSetOperator->pLimit); } pProject->ignoreGroupId = true; + pProject->isSetOpProj = true; int32_t code = TSDB_CODE_SUCCESS; diff --git a/source/libs/planner/src/planOptimizer.c b/source/libs/planner/src/planOptimizer.c index a2a0343316..7d10c02529 100644 --- a/source/libs/planner/src/planOptimizer.c +++ b/source/libs/planner/src/planOptimizer.c @@ -3284,18 +3284,34 @@ static int32_t eliminateProjOptimizeImpl(SOptimizeContext* pCxt, SLogicSubplan* SNodeList* pNewChildTargets = nodesMakeList(); if (NULL == pProjectNode->node.pParent) { - SNode* pProjection = NULL; - FOREACH(pProjection, pProjectNode->pProjections) { - SNode* pChildTarget = NULL; - FOREACH(pChildTarget, pChild->pTargets) { - if (0 == strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) { - nodesListAppend(pNewChildTargets, nodesCloneNode(pChildTarget)); + SNode *pProjection = NULL, *pChildTarget = NULL; + bool needOrderMatch = QUERY_NODE_LOGIC_PLAN_PROJECT == nodeType(pChild) && ((SProjectLogicNode*)pChild)->isSetOpProj; + bool orderMatch = true; + if (needOrderMatch) { + // For sql: select ... from (select ... union all select ...); + // When eliminating the outer proj (the outer select), we have to make sure that the outer proj projections and + // union all project targets have same columns in the same order. See detail in TD-30188 + FORBOTH(pProjection, pProjectNode->pProjections, pChildTarget, pChild->pTargets) { + if (!pProjection) break; + if (0 != strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) { + orderMatch = false; break; } + nodesListAppend(pNewChildTargets, nodesCloneNode(pChildTarget)); + } + } else { + FOREACH(pProjection, pProjectNode->pProjections) { + FOREACH(pChildTarget, pChild->pTargets) { + if (0 == strcmp(((SColumnNode*)pProjection)->colName, ((SColumnNode*)pChildTarget)->colName)) { + nodesListAppend(pNewChildTargets, nodesCloneNode(pChildTarget)); + break; + } + } } } - if (eliminateProjOptCanChildConditionUseChildTargets(pChild, pNewChildTargets)) { + if (eliminateProjOptCanChildConditionUseChildTargets(pChild, pNewChildTargets) && + (!needOrderMatch || (needOrderMatch && orderMatch))) { nodesDestroyList(pChild->pTargets); pChild->pTargets = pNewChildTargets; } else { diff --git a/tests/system-test/2-query/union1.py b/tests/system-test/2-query/union1.py index 8db5ce01f3..853dfe9582 100644 --- a/tests/system-test/2-query/union1.py +++ b/tests/system-test/2-query/union1.py @@ -240,9 +240,22 @@ class TDTestCase: tdSql.error( " '' union all select c1 from ct1 " ) # tdSql.error( "select c1 from ct1 union select c1 from ct2 union select c1 from ct4 ") + def test_select_from_union_all(self): + tdSql.query('select c8, ts from ((select ts, c8,c1 from stb1 order by c1) union all select ts, c8, c1 from stb1 limit 15)') + tdSql.checkRows(15) + tdSql.query('select c8, ts from ((select ts, c8,c1 from stb1 order by c1) union all (select ts, c8, c1 from stb1 order by c8 limit 10) limit 15)') + tdSql.checkRows(15) + tdSql.query('select ts, c1 from ((select ts, c8,c1 from stb1 order by c1) union all (select ts, c8, c1 from stb1 order by c8 limit 10) limit 15)') + tdSql.checkRows(15) + tdSql.query('select ts, c1, c8 from ((select ts, c8,c1 from stb1 order by c1) union all (select ts, c8, c1 from stb1 order by c8 limit 10) limit 15)') + tdSql.checkRows(15) + tdSql.query('select ts, c8, c1, 123 from ((select ts, c8,c1 from stb1 order by c1) union all (select ts, c8, c1 from stb1 order by c8 limit 10) limit 15)') + tdSql.checkRows(15) + def all_test(self): self.__test_error() self.union_check() + self.test_select_from_union_all() def __create_tb(self):