nizhikov commented on code in PR #11438: URL: https://github.com/apache/ignite/pull/11438#discussion_r1759729196
########## modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java: ########## @@ -153,60 +164,133 @@ public Index queryIndex() { /** {@inheritDoc} */ @Override public long count(ExecutionContext<?> ectx, ColocationGroup grp, boolean notNull) { + if (idx == null || !grp.nodeIds().contains(ectx.localNodeId())) + return 0L; + + int[] locParts = grp.partitions(ectx.localNodeId()); + + IndexingQueryFilter filter = new IndexingQueryFilterImpl(tbl.descriptor().cacheContext().kernalContext(), + ectx.topologyVersion(), locParts); + + InlineIndex iidx = idx.unwrap(InlineIndex.class); + + TreeRowClosure<IndexRow, IndexRow> rowFilter = countRowFilter(notNull, iidx); + long cnt = 0; - if (idx != null && grp.nodeIds().contains(ectx.localNodeId())) { - IndexingQueryFilter filter = new IndexingQueryFilterImpl(tbl.descriptor().cacheContext().kernalContext(), - ectx.topologyVersion(), grp.partitions(ectx.localNodeId())); + if (!F.isEmpty(ectx.getTxWriteEntries())) { + IgniteBiTuple<Set<KeyCacheObject>, List<CacheDataRow>> txChanges = transactionData( + ectx.getTxWriteEntries(), + iidx.indexDefinition().cacheInfo().cacheId(), + locParts, + Function.identity() + ); - InlineIndex iidx = idx.unwrap(InlineIndex.class); + if (!txChanges.get1().isEmpty()) { + // This call will change `txChanges.get1()` content. + // Removing found key from set more efficient so we break some rules here. + rowFilter = transactionAwareCountRowFilter(rowFilter, txChanges.get1()); - BPlusTree.TreeRowClosure<IndexRow, IndexRow> rowFilter = null; + cnt = countTransactionRows(iidx, txChanges.get2()); + } + } - boolean checkExpired = !tbl.descriptor().cacheContext().config().isEagerTtl(); + try { + for (int i = 0; i < iidx.segmentsCount(); ++i) + cnt += iidx.count(i, new IndexQueryContext(filter, rowFilter)); - if (notNull) { - boolean nullsFirst = collation.getFieldCollations().get(0).nullDirection == - RelFieldCollation.NullDirection.FIRST; + return cnt; + } + catch (IgniteCheckedException e) { + throw new IgniteException("Unable to count index records.", e); + } + } - BPlusTree.TreeRowClosure<IndexRow, IndexRow> notNullRowFilter = - IndexScan.createNotNullRowFilter(iidx, checkExpired); + /** */ + private @Nullable TreeRowClosure<IndexRow, IndexRow> countRowFilter(boolean notNull, InlineIndex iidx) { + boolean checkExpired = !tbl.descriptor().cacheContext().config().isEagerTtl(); + + if (notNull) { + boolean nullsFirst = collation.getFieldCollations().get(0).nullDirection == RelFieldCollation.NullDirection.FIRST; + + TreeRowClosure<IndexRow, IndexRow> notNullRowFilter = IndexScan.createNotNullRowFilter(iidx, checkExpired); + + return new TreeRowClosure<IndexRow, IndexRow>() { + private boolean skipCheck; + + @Override public boolean apply( + BPlusTree<IndexRow, IndexRow> tree, + BPlusIO<IndexRow> io, + long pageAddr, + int idx + ) throws IgniteCheckedException { + // If we have NULLS-FIRST collation, all values after first not-null value will be not-null, + // don't need to check it with notNullRowFilter. + // In case of NULL-LAST collation, all values after first null value will be null, + // don't need to check it too. + if (skipCheck && !checkExpired) + return nullsFirst; + + boolean res = notNullRowFilter.apply(tree, io, pageAddr, idx); + + if (res == nullsFirst) + skipCheck = true; + + return res; + } + + @Override public IndexRow lastRow() { + return (skipCheck && !checkExpired) + ? null + : notNullRowFilter.lastRow(); + } + }; + } + else if (checkExpired) + return IndexScan.createNotExpiredRowFilter(); - AtomicBoolean skipCheck = new AtomicBoolean(); + return null; + } - rowFilter = new BPlusTree.TreeRowClosure<IndexRow, IndexRow>() { - @Override public boolean apply( - BPlusTree<IndexRow, IndexRow> tree, - BPlusIO<IndexRow> io, - long pageAddr, - int idx - ) throws IgniteCheckedException { - // If we have NULLS-FIRST collation, all values after first not-null value will be not-null, - // don't need to check it with notNullRowFilter. - // In case of NULL-LAST collation, all values after first null value will be null, - // don't need to check it too. - if (skipCheck.get() && !checkExpired) - return nullsFirst; + /** */ + private static @NotNull TreeRowClosure<IndexRow, IndexRow> transactionAwareCountRowFilter( + TreeRowClosure<IndexRow, IndexRow> rowFilter, + Set<KeyCacheObject> skipKeys + ) { + return new TreeRowClosure<IndexRow, IndexRow>() { + @Override public boolean apply( + BPlusTree<IndexRow, IndexRow> tree, + BPlusIO<IndexRow> io, + long pageAddr, + int idx + ) throws IgniteCheckedException { + if (rowFilter != null && !rowFilter.apply(tree, io, pageAddr, idx)) + return false; - boolean res = notNullRowFilter.apply(tree, io, pageAddr, idx); + if (skipKeys.isEmpty()) + return true; - if (res == nullsFirst) - skipCheck.set(true); + IndexRow row = rowFilter == null ? null : rowFilter.lastRow(); - return res; - } - }; - } - else if (checkExpired) - rowFilter = IndexScan.createNotExpiredRowFilter(); + if (row == null) + row = tree.getRow(io, pageAddr, idx); - try { - for (int i = 0; i < iidx.segmentsCount(); ++i) - cnt += iidx.count(i, new IndexQueryContext(filter, rowFilter)); - } - catch (IgniteCheckedException e) { - throw new IgniteException("Unable to count index records.", e); + return !skipKeys.remove(row.cacheDataRow().key()); } + }; + } + + /** */ + private static long countTransactionRows(InlineIndex iidx, List<CacheDataRow> changedRows) { + InlineIndexRowHandler rowHnd = iidx.segment(0).rowHandler(); + + long cnt = 0; + + for (CacheDataRow txRow : changedRows) { + if (rowHnd.indexKey(0, txRow) == NullIndexKey.INSTANCE) Review Comment: Fixed. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@ignite.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org