diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 5e573d4411..d8a6c3cbf3 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -204,7 +204,7 @@ LookupValueList::LookupValueList(MemoryPool& pool, ValueListNode* values, ULONG m_values.add(item); } -TriState LookupValueList::find(thread_db* tdbb, Request* request, const ValueExprNode* value, const dsc* desc) const +const SortedValueList* LookupValueList::init(thread_db* tdbb, Request* request) const { const auto impure = request->getImpure(m_impureOffset); auto sortedList = impure->vlu_misc.vlu_sortedList; @@ -231,6 +231,14 @@ TriState LookupValueList::find(thread_db* tdbb, Request* request, const ValueExp impure->vlu_flags |= VLU_computed; } + return sortedList; +} + +TriState LookupValueList::find(thread_db* tdbb, Request* request, const ValueExprNode* value, const dsc* desc) const +{ + const auto sortedList = init(tdbb, request); + fb_assert(sortedList); + if (sortedList->isEmpty()) return TriState(); diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index e18cdaecb2..00e7d95747 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -685,8 +685,6 @@ IndexScanListIterator::IndexScanListIterator(thread_db* tdbb, const IndexRetriev m_lowerValues(*tdbb->getDefaultPool()), m_upperValues(*tdbb->getDefaultPool()), m_iterator(m_listValues.begin()) { - fb_assert(retrieval->irb_list && retrieval->irb_list->getCount()); - // Find and store the position of the variable key segment const auto count = MIN(retrieval->irb_lower_count, retrieval->irb_upper_count); @@ -703,24 +701,38 @@ IndexScanListIterator::IndexScanListIterator(thread_db* tdbb, const IndexRetriev fb_assert(m_segno < count); - // Copy the list values. Reverse them if index is descending. + // Copy the sorted values, skipping duplicates - m_listValues.assign(retrieval->irb_list->begin(), retrieval->irb_list->getCount()); + const auto sortedList = retrieval->irb_list->init(tdbb, tdbb->getRequest()); + fb_assert(sortedList); - if (retrieval->irb_generic & irb_descending) - std::reverse(m_listValues.begin(), m_listValues.end()); + const SortValueItem* prior = nullptr; + for (const auto& item : *sortedList) + { + if (!prior || *prior != item) + m_listValues.add(item.value); + prior = &item; + } - // Prepare the lower/upper key expressions for evaluation + if (m_listValues.hasData()) + { + // Reverse the list if index is descending - auto values = m_retrieval->irb_value; - m_lowerValues.assign(values, m_retrieval->irb_lower_count); - fb_assert(!m_lowerValues[m_segno]); - m_lowerValues[m_segno] = *m_iterator; + if (retrieval->irb_generic & irb_descending) + std::reverse(m_listValues.begin(), m_listValues.end()); - values += m_retrieval->irb_desc.idx_count; - m_upperValues.assign(values, m_retrieval->irb_upper_count); - fb_assert(!m_upperValues[m_segno]); - m_upperValues[m_segno] = *m_iterator; + // Prepare the lower/upper key expressions for evaluation + + auto values = m_retrieval->irb_value; + m_lowerValues.assign(values, m_retrieval->irb_lower_count); + fb_assert(!m_lowerValues[m_segno]); + m_lowerValues[m_segno] = *m_iterator; + + values += m_retrieval->irb_desc.idx_count; + m_upperValues.assign(values, m_retrieval->irb_upper_count); + fb_assert(!m_upperValues[m_segno]); + m_upperValues[m_segno] = *m_iterator; + } } void IndexScanListIterator::makeKeys(temporary_key* lower, temporary_key* upper) @@ -1118,7 +1130,9 @@ void BTR_evaluate(thread_db* tdbb, const IndexRetrieval* retrieval, RecordBitmap temporary_key* lower = &lowerKey; temporary_key* upper = &upperKey; - BTR_make_bounds(tdbb, retrieval, iterator, lower, upper); + + if (!BTR_make_bounds(tdbb, retrieval, iterator, lower, upper)) + return; index_desc idx; btree_page* page = nullptr; @@ -1719,7 +1733,7 @@ bool BTR_lookup(thread_db* tdbb, jrd_rel* relation, USHORT id, index_desc* buffe } -void BTR_make_bounds(thread_db* tdbb, const IndexRetrieval* retrieval, +bool BTR_make_bounds(thread_db* tdbb, const IndexRetrieval* retrieval, IndexScanListIterator* iterator, temporary_key* lower, temporary_key* upper) { @@ -1743,6 +1757,9 @@ void BTR_make_bounds(thread_db* tdbb, const IndexRetrieval* retrieval, } else { + if (iterator && iterator->isEmpty()) + return false; + idx_e errorCode = idx_e_ok; const auto idx = &retrieval->irb_desc; @@ -1778,6 +1795,8 @@ void BTR_make_bounds(thread_db* tdbb, const IndexRetrieval* retrieval, context.raise(tdbb, errorCode); } } + + return true; } diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 389a1d0adf..03a4820704 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -477,6 +477,11 @@ class IndexScanListIterator public: IndexScanListIterator(thread_db* tdbb, const IndexRetrieval* retrieval); + bool isEmpty() const + { + return m_listValues.isEmpty(); + } + bool getNext(temporary_key* lower, temporary_key* upper) { if (++m_iterator < m_listValues.end()) @@ -489,12 +494,12 @@ public: return false; } - ValueExprNode* const* getLowerValues() const + const ValueExprNode* const* getLowerValues() const { return m_lowerValues.begin(); } - ValueExprNode* const* getUpperValues() const + const ValueExprNode* const* getUpperValues() const { return m_upperValues.begin(); } @@ -504,10 +509,10 @@ private: thread_db* const m_tdbb; const IndexRetrieval* const m_retrieval; - Firebird::HalfStaticArray m_listValues; - Firebird::HalfStaticArray m_lowerValues; - Firebird::HalfStaticArray m_upperValues; - ValueExprNode* const* m_iterator; + Firebird::HalfStaticArray m_listValues; + Firebird::HalfStaticArray m_lowerValues; + Firebird::HalfStaticArray m_upperValues; + const ValueExprNode* const* m_iterator; USHORT m_segno = MAX_USHORT; }; diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index ada71fd9f4..504dab038b 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -44,7 +44,7 @@ void BTR_insert(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); USHORT BTR_key_length(Jrd::thread_db*, Jrd::jrd_rel*, Jrd::index_desc*); Ods::btree_page* BTR_left_handoff(Jrd::thread_db*, Jrd::win*, Ods::btree_page*, SSHORT); bool BTR_lookup(Jrd::thread_db*, Jrd::jrd_rel*, USHORT, Jrd::index_desc*, Jrd::RelationPages*); -void BTR_make_bounds(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::IndexScanListIterator*, +bool BTR_make_bounds(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::IndexScanListIterator*, Jrd::temporary_key*, Jrd::temporary_key*); Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const Jrd::index_desc*, Jrd::temporary_key*, USHORT); diff --git a/src/jrd/recsrc/IndexTableScan.cpp b/src/jrd/recsrc/IndexTableScan.cpp index 941815de53..53020e4814 100644 --- a/src/jrd/recsrc/IndexTableScan.cpp +++ b/src/jrd/recsrc/IndexTableScan.cpp @@ -176,8 +176,12 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const FB_NEW_POOL(*tdbb->getDefaultPool()) IndexScanListIterator(tdbb, m_index->retrieval) : nullptr; - BTR_make_bounds(tdbb, m_index->retrieval, impure->irsb_iterator, - impure->irsb_nav_lower, impure->irsb_nav_upper); + if (!BTR_make_bounds(tdbb, m_index->retrieval, impure->irsb_iterator, + impure->irsb_nav_lower, impure->irsb_nav_upper)) + { + rpb->rpb_number.setValid(false); + return false; + } } // If this is the first time, start at the beginning diff --git a/src/jrd/val.h b/src/jrd/val.h index 1c547622a0..fe727a4d6a 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -71,6 +71,16 @@ struct SortValueItem static int compare(const dsc* desc1, const dsc* desc2); + bool operator==(const SortValueItem& other) const + { + return (compare(desc, other.desc) == 0); + } + + bool operator!=(const SortValueItem& other) const + { + return (compare(desc, other.desc) != 0); + } + bool operator>(const SortValueItem& other) const { return (compare(desc, other.desc) > 0); @@ -91,6 +101,8 @@ public: ValueExprNode** begin() { return m_values.begin(); } ValueExprNode** end() { return m_values.end(); } + const SortedValueList* init(thread_db* tdbb, Request* request) const; + TriState find(thread_db* tdbb, Request* request, const ValueExprNode* value, const dsc* desc) const;