From b7ecc39f2a6d84947f5bc92f083991199e12eb4e Mon Sep 17 00:00:00 2001 From: root Date: Wed, 15 Oct 2025 14:58:37 +0000 Subject: [PATCH 1/2] fix 'filter with empty name' error --- src/searchdsql.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/searchdsql.cpp b/src/searchdsql.cpp index 2b228f2fb0..48546e309c 100644 --- a/src/searchdsql.cpp +++ b/src/searchdsql.cpp @@ -1806,8 +1806,18 @@ bool SqlParser_c::AddNullFilter ( const SqlNode_t & tCol, bool bEqualsNull ) void SqlParser_c::AddHaving () { - assert ( m_pQuery->m_dFilters.GetLength() ); - m_pQuery->m_tHaving = m_pQuery->m_dFilters.Pop(); + // Move the last parsed filter into HAVING + assert ( m_pQuery->m_dFilters.GetLength() ); + m_pQuery->m_tHaving = m_pQuery->m_dFilters.Pop(); + + // FIX(#887): The filter tree (m_dFilterTree) still contains a leaf for the + // just-moved HAVING predicate. If we keep it, CreateFilterTree() for WHERE + // will attempt to build a node for a filter that was popped <86><92> it will look + // like a filter with an empty name and throw "filter with empty name". + // + // Remove the trailing tree op that corresponds to the just-added HAVING item. + if ( m_dFilterTree.GetLength() > 0 ) + m_dFilterTree.Pop(); } From 94ad3aec7bf491601b5d872fd2581ec65b45be68 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 23 Oct 2025 19:34:48 +0000 Subject: [PATCH 2/2] Improved the login in AddHaving to make sure the last tree Having content is the expected --- src/searchdsql.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/searchdsql.cpp b/src/searchdsql.cpp index 48546e309c..2f01c7992b 100644 --- a/src/searchdsql.cpp +++ b/src/searchdsql.cpp @@ -1806,18 +1806,25 @@ bool SqlParser_c::AddNullFilter ( const SqlNode_t & tCol, bool bEqualsNull ) void SqlParser_c::AddHaving () { - // Move the last parsed filter into HAVING - assert ( m_pQuery->m_dFilters.GetLength() ); - m_pQuery->m_tHaving = m_pQuery->m_dFilters.Pop(); - - // FIX(#887): The filter tree (m_dFilterTree) still contains a leaf for the - // just-moved HAVING predicate. If we keep it, CreateFilterTree() for WHERE - // will attempt to build a node for a filter that was popped <86><92> it will look - // like a filter with an empty name and throw "filter with empty name". - // - // Remove the trailing tree op that corresponds to the just-added HAVING item. - if ( m_dFilterTree.GetLength() > 0 ) - m_dFilterTree.Pop(); + assert ( m_pQuery->m_dFilters.GetLength() ); + + // The filter we are about to move to HAVING has index (length-1). + // After Pop(), GetLength() equals that index. + const int iExpectHavingIdx = m_pQuery->m_dFilters.GetLength() - 1; + m_pQuery->m_tHaving = m_pQuery->m_dFilters.Pop(); + + // FIX(#887): The filter tree still contains a leaf for the just-moved HAVING filter. + // That leaf must be the *last* tree item and must reference the same filter index. + if ( m_dFilterTree.GetLength() > 0 ) + { + const int iLastFilterItem = m_dFilterTree.Last().m_iFilterItem; + // Assert the tree's last leaf matches the moved filter. + assert ( iLastFilterItem == iExpectHavingIdx && "HAVING leaf must be the last filter-tree item" ); + + // Remove that dangling leaf so WHERE's CreateFilterTree() won't see an empty-name filter. + if ( iLastFilterItem == iExpectHavingIdx ) + m_dFilterTree.Pop(); + } }