private void appendScanRow(StringBuilder buf, Bound bound) { ScanRanges scanRanges = context.getScanRanges(); KeyRange minMaxRange = context.getMinMaxRange(); Iterator<byte[]> minMaxIterator = Iterators.emptyIterator(); if (minMaxRange != null) { RowKeySchema schema = tableRef.getTable().getRowKeySchema(); if (!minMaxRange.isUnbound(bound)) { minMaxIterator = new RowKeyValueIterator(schema, minMaxRange.getRange(bound)); } } int nRanges = scanRanges.getRanges().size(); for (int i = 0, minPos = 0; minPos < nRanges || minMaxIterator.hasNext(); i++) { List<KeyRange> ranges = minPos >= nRanges ? EVERYTHING : scanRanges.getRanges().get(minPos++); KeyRange range = bound == Bound.LOWER ? ranges.get(0) : ranges.get(ranges.size() - 1); byte[] b = range.getRange(bound); Boolean isNull = KeyRange.IS_NULL_RANGE == range ? Boolean.TRUE : KeyRange.IS_NOT_NULL_RANGE == range ? Boolean.FALSE : null; if (minMaxIterator.hasNext()) { byte[] bMinMax = minMaxIterator.next(); int cmp = Bytes.compareTo(bMinMax, b) * (bound == Bound.LOWER ? 1 : -1); if (cmp > 0) { minPos = nRanges; b = bMinMax; isNull = null; } else if (cmp < 0) { minMaxIterator = Iterators.emptyIterator(); } } appendPKColumnValue(buf, b, isNull, i); buf.append(','); } }
private void appendKeyRanges(StringBuilder buf) { ScanRanges scanRanges = context.getScanRanges(); KeyRange minMaxRange = context.getMinMaxRange(); if (minMaxRange == null && (scanRanges == ScanRanges.EVERYTHING || scanRanges == ScanRanges.NOTHING)) { return; } buf.append(" ["); StringBuilder buf1 = new StringBuilder(); appendScanRow(buf1, Bound.LOWER); buf.append(buf1); buf.setCharAt(buf.length() - 1, ']'); StringBuilder buf2 = new StringBuilder(); appendScanRow(buf2, Bound.UPPER); if (!StringUtil.equals(buf1, buf2)) { buf.append(" - ["); buf.append(buf2); } buf.setCharAt(buf.length() - 1, ']'); }
private void appendPKColumnValue(StringBuilder buf, byte[] range, Boolean isNull, int slotIndex) { if (Boolean.TRUE.equals(isNull)) { buf.append("null"); return; } if (Boolean.FALSE.equals(isNull)) { buf.append("not null"); return; } if (range.length == 0) { buf.append('*'); return; } ScanRanges scanRanges = context.getScanRanges(); PDataType type = scanRanges.getSchema().getField(slotIndex).getDataType(); ColumnModifier modifier = tableRef.getTable().getPKColumns().get(slotIndex).getColumnModifier(); if (modifier != null) { buf.append('~'); range = modifier.apply(range, 0, new byte[range.length], 0, range.length); } Format formatter = context.getConnection().getFormatter(type); buf.append(type.toStringLiteral(range, formatter)); }
private boolean explainSkipScan(StringBuilder buf) { ScanRanges scanRanges = context.getScanRanges(); if (scanRanges.isPointLookup()) { int keyCount = scanRanges.getPointLookupCount(); buf.append("POINT LOOKUP ON " + keyCount + " KEY" + (keyCount > 1 ? "S " : " ")); } else if (scanRanges.useSkipScanFilter()) { buf.append("SKIP SCAN "); int count = 1; boolean hasRanges = false; for (List<KeyRange> ranges : scanRanges.getRanges()) { count *= ranges.size(); for (KeyRange range : ranges) { hasRanges |= !range.isSingleKey(); } } buf.append("ON "); buf.append(count); buf.append(hasRanges ? " RANGE" : " KEY"); buf.append(count > 1 ? "S " : " "); } else { buf.append("RANGE SCAN "); } return scanRanges.useSkipScanFilter(); }
protected void explain(String prefix, List<String> planSteps) { StringBuilder buf = new StringBuilder(prefix); ScanRanges scanRanges = context.getScanRanges(); boolean hasSkipScanFilter = false; if (scanRanges.isEverything()) { buf.append("FULL SCAN "); } else { hasSkipScanFilter = explainSkipScan(buf); } buf.append("OVER " + tableRef.getTable().getName().getString()); if (!scanRanges.isPointLookup()) { appendKeyRanges(buf); } planSteps.add(buf.toString()); Scan scan = context.getScan(); Filter filter = scan.getFilter(); PageFilter pageFilter = null; if (filter != null) { int offset = 0; boolean hasFirstKeyOnlyFilter = false; String filterDesc = ""; if (hasSkipScanFilter) { if (filter instanceof FilterList) { List<Filter> filterList = ((FilterList) filter).getFilters(); if (filterList.get(0) instanceof FirstKeyOnlyFilter) { hasFirstKeyOnlyFilter = true; offset = 1; } if (filterList.size() > offset + 1) { filterDesc = filterList.get(offset + 1).toString(); if (filterList.size() > offset + 2) { pageFilter = (PageFilter) filterList.get(offset + 2); } } } } else if (filter instanceof FilterList) { List<Filter> filterList = ((FilterList) filter).getFilters(); if (filterList.get(0) instanceof FirstKeyOnlyFilter) { hasFirstKeyOnlyFilter = true; offset = 1; } if (filterList.size() > offset) { filterDesc = filterList.get(offset).toString(); if (filterList.size() > offset + 1) { pageFilter = (PageFilter) filterList.get(offset + 1); } } } else { if (filter instanceof FirstKeyOnlyFilter) { hasFirstKeyOnlyFilter = true; } else { filterDesc = filter.toString(); } } if (filterDesc.length() > 0) { planSteps.add( " SERVER FILTER BY " + (hasFirstKeyOnlyFilter ? "FIRST KEY ONLY AND " : "") + filterDesc); } else if (hasFirstKeyOnlyFilter) { planSteps.add(" SERVER FILTER BY FIRST KEY ONLY"); } if (pageFilter != null) { planSteps.add(" SERVER " + pageFilter.getPageSize() + " ROW LIMIT"); } } groupBy.explain(planSteps); }