rtlnewkey.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifndef RTLNEWKEY_INCL
  14. #define RTLNEWKEY_INCL
  15. #include "eclrtl.hpp"
  16. #include "rtlkey.hpp"
  17. #include "rtlrecord.hpp"
  18. BITMASK_ENUM(TransitionMask);
  19. /*
  20. * The RowFilter class represents a multiple-field filter of a row.
  21. */
  22. class ECLRTL_API RowFilter
  23. {
  24. public:
  25. void addFilter(const IFieldFilter & filter);
  26. bool matches(const RtlRow & row) const;
  27. void extractKeyFilter(const RtlRecord & record, IConstArrayOf<IFieldFilter> & keyFilters) const;
  28. void extractMemKeyFilter(const RtlRecord & record, const UnsignedArray &sortOrder, IConstArrayOf<IFieldFilter> & keyFilters) const;
  29. unsigned numFilterFields() const { return filters.ordinality(); }
  30. const IFieldFilter & queryFilter(unsigned i) const { return filters.item(i); }
  31. const IFieldFilter *findFilter(unsigned fieldIdx) const;
  32. const IFieldFilter *extractFilter(unsigned fieldIdx);
  33. unsigned getNumFieldsRequired() const { return numFieldsRequired; }
  34. void remapField(unsigned filterIdx, unsigned newFieldNum);
  35. void recalcFieldsRequired();
  36. void remove(unsigned idx);
  37. void clear();
  38. void appendFilters(IConstArrayOf<IFieldFilter> &_filters);
  39. protected:
  40. IConstArrayOf<IFieldFilter> filters;
  41. unsigned numFieldsRequired = 0;
  42. };
  43. //This class represents the current set of values which have been matched in the filter sets.
  44. //A field can either have a valid current value, or it has an index of the next filter range which must match
  45. //for that field.
  46. //Note: If any field is matching a range, then all subsequent fields must be matching the lowest possible filter range
  47. // Therefore it is only necessary to keep track of the number of exactly matched fields, and which range the next
  48. // field (if any) is matched against.
  49. class ECLRTL_API RowCursor
  50. {
  51. public:
  52. RowCursor(const RtlRecord & record, RowFilter & filter) : currentRow(record, nullptr)
  53. {
  54. filter.extractKeyFilter(record, filters);
  55. ForEachItemIn(i, filters)
  56. matchedRanges.append(0);
  57. numFieldsRequired = filter.getNumFieldsRequired();
  58. }
  59. RowCursor(const RtlRecord & record, const UnsignedArray &sortOrder, RowFilter & filter) : currentRow(record, nullptr)
  60. {
  61. filter.extractMemKeyFilter(record, sortOrder, filters);
  62. ForEachItemIn(i, filters)
  63. matchedRanges.append(0);
  64. numFieldsRequired = filter.getNumFieldsRequired();
  65. }
  66. void selectFirst()
  67. {
  68. eos = false;
  69. numMatched = 0;
  70. nextUnmatchedRange = 0;
  71. }
  72. //Compare the incoming row against the current search row
  73. int compareNext(const RtlRow & candidate) const
  74. {
  75. if (nextSeekIsGT())
  76. {
  77. assertex(nextUnmatchedRange == -1U);
  78. assertex(numMatched != filters.ordinality());
  79. unsigned i;
  80. int c = 0;
  81. //Note field numMatched is not matched, but we are searching for the next highest value => loop until <= numMatched
  82. for (i = 0; i <= numMatched; i++)
  83. {
  84. //Use sequential searches for the values that have previously been matched
  85. c = queryFilter(i).compareRow(candidate, currentRow);
  86. if (c != 0)
  87. return c;
  88. }
  89. //If the next value of the trailing field isn't known then no limit can be placed on the
  90. //subsequent fields.
  91. //If it is possible to increment a key value, then it should be going through the compareGE() code instead
  92. return -1;
  93. }
  94. else
  95. {
  96. unsigned i;
  97. for (i = 0; i < numMatched; i++)
  98. {
  99. //Use sequential searches for the values that have previously been matched
  100. int c = queryFilter(i).compareRow(candidate, currentRow);
  101. if (c != 0)
  102. return c;
  103. }
  104. unsigned nextRange = nextUnmatchedRange;
  105. for (; i < numFilterFields(); i++)
  106. {
  107. //Compare the row against each of the potential ranges.
  108. int c = queryFilter(i).compareLowest(candidate, nextRange);
  109. if (c != 0)
  110. return c;
  111. nextRange = 0;
  112. }
  113. return 0;
  114. }
  115. }
  116. //Compare with the row that is larger than the first numMatched fields.
  117. bool matches() const
  118. {
  119. ForEachItemIn(i, filters)
  120. {
  121. if (!filters.item(i).matches(currentRow))
  122. return false;
  123. }
  124. return true;
  125. }
  126. unsigned numFilterFields() const { return filters.ordinality(); }
  127. const RtlRow & queryRow() const { return currentRow; }
  128. bool setRowForward(const byte * row);
  129. bool nextSeekIsGT() const { return (nextUnmatchedRange == -1U); }
  130. bool noMoreMatches() const { return eos; }
  131. protected:
  132. unsigned matchPos(unsigned i) const { return matchedRanges.item(i); }
  133. bool isValid(unsigned i) const { return matchPos(i) < queryFilter(i).numRanges(); }
  134. const IFieldFilter & queryFilter(unsigned i) const { return filters.item(i); }
  135. bool findNextRange(unsigned field);
  136. protected:
  137. RtlDynRow currentRow;
  138. unsigned numMatched = 0;
  139. unsigned nextUnmatchedRange = 0;
  140. UnsignedArray matchedRanges;
  141. unsigned numFieldsRequired = 0;
  142. bool eos = false;
  143. IConstArrayOf<IFieldFilter> filters;
  144. };
  145. interface ISourceRowCursor
  146. {
  147. public:
  148. //Find the first row that is forward from the search cursor
  149. virtual const byte * findNext(const RowCursor & current) = 0;
  150. //select the next row
  151. virtual const byte * next() = 0;
  152. //prepare ready for a new set of seeks
  153. virtual void reset() = 0;
  154. };
  155. class ECLRTL_API KeySearcher : public CInterface
  156. {
  157. public:
  158. KeySearcher(const RtlRecord & _info, RowFilter & _filter, ISourceRowCursor * _rows) : cursor(_info, _filter), rows(_rows)
  159. {
  160. }
  161. KeySearcher(const RtlRecord & _info, const UnsignedArray &_sortOrder, RowFilter & _filter, ISourceRowCursor * _rows) : cursor(_info, _sortOrder, _filter), rows(_rows)
  162. {
  163. }
  164. void reset()
  165. {
  166. rows->reset();
  167. firstPending = true;
  168. }
  169. bool next()
  170. {
  171. if (firstPending)
  172. {
  173. cursor.selectFirst();
  174. firstPending = false;
  175. }
  176. else
  177. {
  178. const byte * next = rows->next(); // MORE: Return a RtlRow?
  179. if (!next)
  180. return false;
  181. if (cursor.setRowForward(next))
  182. return true;
  183. }
  184. return resolveValidRow();
  185. }
  186. bool resolveValidRow()
  187. {
  188. for (;;)
  189. {
  190. if (cursor.noMoreMatches())
  191. return false;
  192. const byte * match;
  193. match = rows->findNext(cursor); // more - return the row pointer to avoid recalculation
  194. if (!match)
  195. return false;
  196. if (cursor.setRowForward(match))
  197. return true;
  198. }
  199. }
  200. const RtlRow & queryRow() const { return cursor.queryRow(); }
  201. protected:
  202. ISourceRowCursor * rows = nullptr;
  203. RowCursor cursor;
  204. bool firstPending = true;
  205. };
  206. #endif