/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.explain.format; // NOTE: Should only depend on explain objects and standard Java. // Should theoretically be able to run outside of server. import com.foundationdb.server.explain.*; import java.util.ArrayList; import java.util.List; public class DefaultFormatter { public static enum LevelOfDetail { BRIEF, NORMAL, VERBOSE_WITHOUT_COST, VERBOSE }; private String defaultSchemaName; private LevelOfDetail levelOfDetail; private int numSubqueries = 0; private List<CompoundExplainer> subqueries = new ArrayList<>(); private StringBuilder sb = new StringBuilder(); private List<String> rows = new ArrayList<>(); public DefaultFormatter(String defaultSchemaName) { this(defaultSchemaName, LevelOfDetail.NORMAL); } public DefaultFormatter(String defaultSchemaName, LevelOfDetail levelOfDetail) { this.defaultSchemaName = defaultSchemaName; this.levelOfDetail = levelOfDetail; } public List<String> format(Explainer explainer) { append(explainer); for (int i = 0; i < numSubqueries; i++) { newRow(); appendSubqueryBody(subqueries.get(i), i+1); } newRow(); return rows; } protected void append(Explainer explainer) { append(explainer, false, null); } protected void append(Explainer explainer, boolean needsParens, String parentName) { switch (explainer.getType().generalType()) { case SCALAR_VALUE: appendPrimitive((PrimitiveExplainer)explainer); break; case OPERATOR: appendOperator((CompoundExplainer)explainer, 0); break; case PROCEDURE: appendProcedure((CompoundExplainer)explainer, 0); break; case ROWTYPE: appendRowType((CompoundExplainer)explainer); break; case ROW: appendRow((CompoundExplainer)explainer); break; default: appendExpression((CompoundExplainer)explainer, needsParens, parentName); } } protected void appendPrimitive(PrimitiveExplainer explainer) { sb.append(explainer.get()); } protected void appendExpression(CompoundExplainer explainer, boolean needsParens, String parentName) { switch (explainer.getType()) { case FIELD: appendField(explainer); break; case FUNCTION: case BINARY_OPERATOR: appendFunction(explainer, needsParens, parentName); break; case SUBQUERY: appendSubquery(explainer); break; case LITERAL: appendLiteral(explainer); break; case VARIABLE: appendVariable(explainer); break; } } protected void appendFunction(CompoundExplainer explainer, boolean needsParens, String parentName) { Attributes atts = explainer.get(); String name = (String)atts.getValue(Label.NAME); if (atts.containsKey(Label.INFIX_REPRESENTATION)) { Explainer leftExplainer = atts.valuePairs().get(0).getValue(); Explainer rightExplainer = atts.valuePairs().get(1).getValue(); if (name.equals(parentName) && atts.containsKey(Label.ASSOCIATIVE)) { if (Boolean.TRUE.equals(atts.getValue(Label.ASSOCIATIVE))) needsParens = false; } if (needsParens) sb.append('('); append(leftExplainer, true, name); sb.append(' ').append(atts.getValue(Label.INFIX_REPRESENTATION)).append(' '); append(rightExplainer, true, name); if (needsParens) sb.append(')'); } else if (name.startsWith("CAST")) { boolean display = ((levelOfDetail == LevelOfDetail.VERBOSE_WITHOUT_COST) || (levelOfDetail == LevelOfDetail.VERBOSE)); if (display) sb.append(name.substring(0, 4)).append('('); append(atts.getAttribute(Label.OPERAND)); if (display) sb.append(" AS ").append(atts.getValue(Label.OUTPUT_TYPE)).append(')'); } else { sb.append(name).append('('); if (atts.containsKey(Label.OPERAND)) { for (Explainer entry : atts.get(Label.OPERAND)) { append(entry); sb.append(", "); } sb.setLength(sb.length()-2); } sb.append(')'); } } protected void appendField(CompoundExplainer explainer) { Attributes atts = explainer.get(); boolean started = false; if (atts.containsKey(Label.TABLE_CORRELATION)) { sb.append(atts.getValue(Label.TABLE_CORRELATION)); started = true; } else if (atts.containsKey(Label.TABLE_NAME)) { appendTableName(atts); started = true; } if (atts.containsKey(Label.COLUMN_NAME)) { if (started) sb.append('.'); sb.append(atts.getValue(Label.COLUMN_NAME)); } else if (started) { sb.append('[').append(atts.getValue(Label.POSITION)).append(']'); } else { sb.append(atts.getValue(Label.NAME)).append('('); if (atts.containsKey(Label.BINDING_POSITION)) { sb.append(atts.getValue(Label.BINDING_POSITION)).append(", "); } sb.append(atts.getValue(Label.POSITION)).append(')'); } } protected void appendSubquery(CompoundExplainer explainer) { Attributes atts = explainer.get(); sb.append(atts.getValue(Label.NAME)).append('('); sb.append("SUBQUERY ").append(++numSubqueries).append(')'); subqueries.add(explainer); } protected void appendSubqueryBody(CompoundExplainer explainer, int n) { Attributes atts = explainer.get(); String name = (String)atts.getValue(Label.NAME); sb.append("SUBQUERY ").append(n).append(": ").append(name).append('('); if (atts.containsKey(Label.EXPRESSIONS)) { for (Explainer ex : atts.get(Label.EXPRESSIONS)) { append(ex); sb.append(", "); } sb.setLength(sb.length() - 2); } sb.append(')'); newRow(); sb.append(" "); appendOperator((CompoundExplainer)atts.getAttribute(Label.OPERAND), 1); } protected void appendLiteral(CompoundExplainer explainer) { Attributes atts = explainer.get(); sb.append(atts.getValue(Label.OPERAND)); } protected void appendVariable(CompoundExplainer explainer) { Attributes atts = explainer.get(); int pos = ((Number)atts.getValue(Label.BINDING_POSITION)).intValue(); sb.append("$").append(pos+1); } protected void appendOperator(CompoundExplainer explainer, int depth) { Attributes atts = explainer.get(); String name = (String)atts.getValue(Label.NAME); sb.append((levelOfDetail != LevelOfDetail.BRIEF) ? name : name.substring(0, name.indexOf('_'))).append('('); switch (explainer.getType()) { case SELECT_HKEY: appendSelectOperator(name, atts); break; case PROJECT: appendProjectOperator(name, atts); break; case SCAN_OPERATOR: appendScanOperator(name, atts); break; case LOOKUP_OPERATOR: appendLookupOperator(name, atts); break; case COUNT_OPERATOR: appendCountOperator(name, atts); break; case FILTER: appendFilterOperator(name, atts); break; case FLATTEN_OPERATOR: appendFlattenOperator(name, atts); break; case PRODUCT_OPERATOR: appendProductOperator(name, atts); break; case ORDERED: appendOrderedOperator(name, atts); break; case IF_EMPTY: appendIfEmptyOperator(name, atts); break; case LIMIT_OPERATOR: appendLimitOperator(name, atts); break; case NESTED_LOOPS: appendNestedLoopsOperator(name, atts); break; case SORT: appendSortOperator(name, atts); break; case DUI: appendDUIOperator(name, atts); break; case AGGREGATE: appendAggregateOperator(name, atts); break; case BLOOM_FILTER: appendBloomFilterOperator(name, atts); break; case HASH_JOIN: appendHashTableOperator(name, atts); break; case DISTINCT: appendDistinctOperator(name, atts); break; case UNION: // ALL appendUnionOperator(name, atts); break; case BUFFER_OPERATOR: appendBufferOperator(name, atts); break; case HKEY_OPERATOR: appendHKeyOperator(name, atts); break; default: throw new UnsupportedOperationException("Formatter does not recognize " + explainer.getType()); } sb.append(')'); if ((levelOfDetail == LevelOfDetail.VERBOSE) && atts.containsKey(Label.COST)) { sb.append(" ("); sb.append((String)atts.getValue(Label.COST)); sb.append(")"); } if (atts.containsKey(Label.INPUT_OPERATOR)) { for (Explainer input : atts.get(Label.INPUT_OPERATOR)) { newRow(); for (int i = 0; i <= depth; i++) { sb.append(" "); } appendOperator((CompoundExplainer)input, depth + 1); } } } protected void appendSelectOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { append(atts.getAttribute(Label.PREDICATE)); } } protected void appendProjectOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { for (Explainer projection : atts.get(Label.PROJECTION)) { append(projection); sb.append(", "); } sb.setLength(sb.length() - 2); } } protected void appendScanOperator(String name, Attributes atts) { if (name.equals("IndexScan_Default")) { appendIndexScanOperator(atts); if ((levelOfDetail == LevelOfDetail.VERBOSE_WITHOUT_COST) || (levelOfDetail == LevelOfDetail.VERBOSE)) { if((Long)atts.getValue(Label.PIPELINE) !=1){ sb.append(", Pipelining "); sb.append((long)(atts.getValue(Label.PIPELINE))); } } } else if (name.equals("ValuesScan_Default")) { if (levelOfDetail != LevelOfDetail.BRIEF) { if (atts.containsKey(Label.EXPRESSIONS)) { for (Explainer row : atts.get(Label.EXPRESSIONS)) { append(row); sb.append(", "); } sb.setLength(sb.length() - 2); } } } else if (name.equals("GroupScan_Default")) { if (levelOfDetail != LevelOfDetail.BRIEF) { String opt = (String)atts.getValue(Label.SCAN_OPTION); if (!opt.equals("full scan")) sb.append(opt).append(" on "); } appendTableName(atts); } else if (name.equals("IndexScan_FullText")) { appendFullTextScanOperator(atts); } } protected void appendIndexScanOperator(Attributes atts) { append(atts.getAttribute(Label.INDEX)); if (levelOfDetail != LevelOfDetail.BRIEF) { boolean isSpatial = false; boolean isGroup = false; if (atts.containsKey(Label.INDEX_KIND)) { String indexKind = (String) atts.getValue(Label.INDEX_KIND); isSpatial = indexKind.contains("SPATIAL"); isGroup = indexKind.contains("GROUP"); } int ncols = atts.get(Label.COLUMN_NAME).size(); int nequals = 0; if (atts.containsKey(Label.EQUAL_COMPARAND)) nequals = atts.get(Label.EQUAL_COMPARAND).size(); if (atts.containsKey(Label.USED_COLUMNS)) { // Don't display non-key columns if not used. ncols = ((Number)atts.getValue(Label.USED_COLUMNS)).intValue(); int nconds = nequals; if (atts.containsKey(Label.LOW_COMPARAND) || atts.containsKey(Label.HIGH_COMPARAND)) nconds++; if (ncols < nconds) ncols = nconds; } int norders = 0; if (atts.containsKey(Label.ORDERING)) { norders = atts.get(Label.ORDERING).size(); if (atts.containsKey(Label.ORDER_EFFECTIVENESS) && "NONE".equals(atts.getValue(Label.ORDER_EFFECTIVENESS))) { // No need to display ordering if not used. norders = 0; } while (norders > nequals+1) { if (!atts.get(Label.ORDERING).get(norders-1).equals(atts.get(Label.ORDERING).get(norders-2))) break; norders--; } } String indexSchema = (String)((CompoundExplainer)atts.getAttribute(Label.INDEX)).get().getValue(Label.TABLE_SCHEMA); String indexTable = (String)((CompoundExplainer)atts.getAttribute(Label.INDEX)).get().getValue(Label.TABLE_NAME); for (int i = 0; i < ncols; i++) { if (isSpatial && (i == nequals)) { sb.append(", ("); int ndims = ((Number)atts.getValue(Label.INDEX_SPATIAL_DIMENSIONS)).intValue(); for (int j = 0; j < ndims; j++) { append(atts.get(Label.COLUMN_NAME).get(i+j)); sb.append(", "); } sb.setLength(sb.length() - 2); sb.append(')'); if (!atts.containsKey(Label.HIGH_COMPARAND)) { sb.append(" ZNEAR("); } else { sb.append(" OVERLAP("); } for (Explainer ex : atts.get(Label.LOW_COMPARAND)) { append(ex); sb.append(", "); } sb.setLength(sb.length() - 2); sb.append(')'); break; } sb.append(", "); String columnSchema = (String)atts.get(Label.TABLE_SCHEMA).get(i).get(); String columnTable = (String)atts.get(Label.TABLE_NAME).get(i).get(); if (!indexSchema.equals(columnSchema)) sb.append(columnSchema).append('.').append(columnTable).append('.'); else if (isGroup || !indexTable.equals(columnTable)) sb.append(columnTable).append('.'); append(atts.get(Label.COLUMN_NAME).get(i)); if (i < nequals) { Explainer comparand = atts.get(Label.EQUAL_COMPARAND).get(i); if (isLiteralNull(comparand)) sb.append(" IS NULL"); else { sb.append(" = "); append(comparand); } } else { if (i == nequals) { Explainer lo = null, hi = null; boolean loInc = false, hiInc = false; if (atts.containsKey(Label.LOW_COMPARAND)) { lo = atts.get(Label.LOW_COMPARAND).get(0); loInc = (Boolean)atts.get(Label.LOW_COMPARAND).get(1).get(); if (!loInc && isLiteralNull(lo)) lo = null; } if (atts.containsKey(Label.HIGH_COMPARAND)) { hi = atts.get(Label.HIGH_COMPARAND).get(0); hiInc = (Boolean)atts.get(Label.HIGH_COMPARAND).get(1).get(); if (!hiInc && isLiteralNull(hi)) hi = null; } if (loInc && hiInc) { sb.append(" BETWEEN "); append(lo); sb.append(" AND "); append(hi); } else { if (lo != null) { sb.append((loInc) ? " >= " : " > "); append(lo); } if (hi != null) { if (lo != null) sb.append(" AND"); sb.append((hiInc) ? " <= " : " < "); append(hi); } } } if (i < norders) { sb.append(" "); append(atts.get(Label.ORDERING).get(i)); } } } } } protected void appendFullTextScanOperator(Attributes atts) { append(atts.getAttribute(Label.INDEX)); if (levelOfDetail != LevelOfDetail.BRIEF) { CompoundExplainer pred = (CompoundExplainer)atts.getAttribute(Label.PREDICATE); Attributes patts = pred.get(); if (patts.containsKey(Label.OPERAND)) { for (Explainer entry : patts.get(Label.OPERAND)) { sb.append(", "); append(entry); } } if (atts.containsKey(Label.LIMIT)) { sb.append(", LIMIT ").append(atts.getValue(Label.LIMIT)); } } } private static boolean isLiteralNull(Explainer explainer) { return ((explainer.getType() == Type.LITERAL) && "NULL".equals(((CompoundExplainer)explainer).get().getValue(Label.OPERAND))); } protected void appendLookupOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { append(atts.getAttribute(Label.INPUT_TYPE)); sb.append(" -> "); } for (Explainer table : atts.get(Label.OUTPUT_TYPE)) { append(table); sb.append(", "); } sb.setLength(sb.length() - 2); if ((levelOfDetail != LevelOfDetail.BRIEF)){ if(atts.containsKey(Label.ANCESTOR_TYPE)) { sb.append(" (via "); append(atts.getAttribute(Label.ANCESTOR_TYPE)); sb.append(')'); } if(levelOfDetail != LevelOfDetail.NORMAL) { if (name.equals("GroupLookup_Default") || name.equals("AncestorLookup_Nested") || name.equals("BranchLookup_Nested")) { if ((Long) atts.getValue(Label.PIPELINE) != 1) { sb.append(", Pipelining "); sb.append((long) (atts.getValue(Label.PIPELINE))); } } } } } protected void appendCountOperator(String name, Attributes atts) { sb.append("*"); if ((levelOfDetail != LevelOfDetail.BRIEF) && name.equals("Count_TableStatus")) { sb.append(" FROM "); append(atts.getAttribute(Label.INPUT_TYPE)); } } protected void appendFilterOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { for (Explainer table : atts.get(Label.KEEP_TYPE)) { append(table); sb.append(", "); } sb.setLength(sb.length() - 2); } } protected void appendFlattenOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { append(atts.getAttribute(Label.PARENT_TYPE)); sb.append(' ').append(atts.getValue(Label.JOIN_OPTION)).append(' '); append(atts.getAttribute(Label.CHILD_TYPE)); } } protected void appendProductOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { append(atts.getAttribute(Label.OUTER_TYPE)); sb.append(" x "); append(atts.getAttribute(Label.INNER_TYPE)); } } protected void appendOrderedOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { if (atts.containsKey(Label.SET_OPTION) && "ALL".equals(atts.getValue(Label.SET_OPTION))) { sb.append("all, "); } List<Explainer> skips = atts.get(Label.NUM_SKIP); sb.append("skip "); append(skips.get(0)); if (skips.size() > 1) { sb.append(" left, skip "); append(skips.get(1)); sb.append(" right"); } sb.append(", compare "); append(atts.getAttribute(Label.NUM_COMPARE)); if (name.equals("HKeyUnion")) { sb.append(", shorten to "); append(atts.getAttribute(Label.OUTPUT_TYPE)); } else if (name.equals("Intersect_Ordered")) { String join = (String)atts.getValue(Label.JOIN_OPTION); if (!"INNER".equals(join)) { sb.append(", USING ").append(join); } } } } protected void appendIfEmptyOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { for (Explainer expression : atts.get(Label.OPERAND)) { append(expression); sb.append(", "); } if (!atts.valuePairs().isEmpty()) { sb.setLength(sb.length() - 2); } String inputOption = (String)atts.getValue(Label.INPUT_PRESERVATION); if (!"KEEP_INPUT".equals(inputOption)) { sb.append(", ").append(inputOption); } } } protected void appendLimitOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { append(atts.getAttribute(Label.LIMIT)); } } protected void appendNestedLoopsOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { if (name.equals("Map_NestedLoops")) { if ((levelOfDetail == LevelOfDetail.VERBOSE_WITHOUT_COST) || (levelOfDetail == LevelOfDetail.VERBOSE)) { append(atts.getAttribute(Label.BINDING_POSITION)); if((Boolean)atts.getValue(Label.PIPELINE)){ sb.append(", Pipelining"); } } } } } protected void appendSortOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { int i = 0; for (Explainer ex : atts.get(Label.EXPRESSIONS)) { append(ex); sb.append(' ').append(atts.get(Label.ORDERING).get(i++).get()).append(", "); } if (atts.containsKey(Label.LIMIT)) { sb.append("LIMIT ").append(atts.getValue(Label.LIMIT)).append(", "); } String opt = (String)atts.getValue(Label.SORT_OPTION); if (opt.equals("PRESERVE_DUPLICATES")) sb.setLength(sb.length() - 2); else sb.append(opt.replace('_', ' ')); } } protected void appendDUIOperator(String name, Attributes atts) { if (name.equals("Delete_Default")|| name.equals("Delete_Returning")) { if (atts.containsKey(Label.TABLE_NAME)) { sb.append("FROM "); appendTableName(atts); } else if (atts.containsKey(Label.TABLE_TYPE)) { sb.append("FROM "); append(atts.getAttribute(Label.TABLE_TYPE)); } } else if (name.equals("Insert_Default") || name.equals("Insert_Returning")) { if (atts.containsKey(Label.TABLE_NAME)) { sb.append("INTO "); appendTableName(atts); } else if (atts.containsKey(Label.TABLE_TYPE)) { sb.append("INTO "); append(atts.getAttribute(Label.TABLE_TYPE)); } if (levelOfDetail != LevelOfDetail.BRIEF) { if (atts.containsKey(Label.COLUMN_NAME)) { sb.append('('); for (Explainer ex : atts.get(Label.COLUMN_NAME)) sb.append(ex.get()).append(", "); sb.setLength(sb.length()-2); sb.append(')'); } } } else if (name.equals("Update_Default") || name.equals("Update_Returning")) { if (atts.containsKey(Label.TABLE_NAME)) { appendTableName(atts); } else if (atts.containsKey(Label.TABLE_TYPE)) { append(atts.getAttribute(Label.TABLE_TYPE)); } if (levelOfDetail != LevelOfDetail.BRIEF) { if (atts.containsKey(Label.COLUMN_NAME)) { sb.append(" SET "); int ncols = Math.min(atts.get(Label.COLUMN_NAME).size(), atts.get(Label.EXPRESSIONS).size()); for (int j = 0; j < ncols; j++) { sb.append(atts.get(Label.COLUMN_NAME).get(j).get()); sb.append(" = "); append(atts.get(Label.EXPRESSIONS).get(j)); sb.append(", "); } sb.setLength(sb.length()-2); } } } } protected void appendAggregateOperator(String name, Attributes atts) { int nkeys = ((Number)atts.getValue(Label.GROUPING_OPTION)).intValue(); List<Explainer> aggrs = atts.get(Label.AGGREGATORS); if (levelOfDetail == LevelOfDetail.BRIEF) { if (nkeys > 0) { sb.append("group by ").append(nkeys); if (aggrs != null) sb.append(", "); } if (aggrs != null) { sb.append("aggregate ").append(aggrs.size()); } } else { if (nkeys > 0) { sb.append("GROUP BY "); if (!appendProjectColumns(atts, nkeys)) { // Fallback is just count. sb.append(nkeys).append(" field"); if (nkeys > 1) sb.append("s"); } } if (aggrs != null) { if (nkeys > 0) sb.append(": "); for (Explainer aggr : aggrs) { append(aggr); sb.append(", "); } sb.setLength(sb.length() - 2); } } } protected void appendBloomFilterOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { if (name.equals("Using_BloomFilter")) { appendProjectColumns(atts, -1); } else if (name.equals("Select_BloomFilter")) { if (atts.containsKey(Label.EXPRESSIONS)) { for (Explainer ex : atts.get(Label.EXPRESSIONS)) { append(ex); sb.append(", "); } } if(levelOfDetail != LevelOfDetail.NORMAL && (Boolean)atts.getValue(Label.PIPELINE)){ sb.append("Pipelining"); } else if (atts.containsKey(Label.EXPRESSIONS) && atts.get(Label.EXPRESSIONS).size() > 0){ sb.setLength(sb.length() - 2); } } } } protected void appendHashTableOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { if (levelOfDetail != LevelOfDetail.NORMAL) { for (Explainer ex : atts.get(Label.BINDING_POSITION)) { append(ex); sb.append(", "); } } if (atts.containsKey(Label.EXPRESSIONS)) { for (Explainer ex : atts.get(Label.EXPRESSIONS)) { append(ex); sb.append(", "); } sb.setLength(sb.length() - 2); } } } // If all the inputs are simple columns, display their names. protected boolean appendProjectColumns(Attributes atts, int nfields) { int olen = sb.length(); Attributes inputOperator = ((CompoundExplainer)atts.get(Label.INPUT_OPERATOR).get(0)).get(); boolean allcols = false; if (inputOperator.getValue(Label.NAME).equals("Project_Default")) { allcols = true; List<Explainer> fields = inputOperator.get(Label.PROJECTION); if (nfields < 0) nfields = fields.size(); for (int i = 0; i < nfields; i++) { CompoundExplainer field = (CompoundExplainer)fields.get(i); if (field.getType() == Type.FIELD) { Attributes kattr = field.get(); if (kattr.containsKey(Label.COLUMN_NAME)) { sb.append(kattr.getValue(Label.COLUMN_NAME)).append(", "); continue; } } allcols = false; break; } } sb.setLength(allcols ? sb.length() - 2 : olen); return allcols; } protected void appendDistinctOperator(String name, Attributes atts) { } protected void appendUnionOperator(String name, Attributes atts) { if ((levelOfDetail == LevelOfDetail.VERBOSE_WITHOUT_COST) || (levelOfDetail == LevelOfDetail.VERBOSE)) { if((Boolean) atts.getValue(Label.PIPELINE)){ sb.append("Pipelining"); } } } protected void appendBufferOperator(String name, Attributes atts) { } protected void appendHKeyOperator(String name, Attributes atts) { if (levelOfDetail != LevelOfDetail.BRIEF) { append(atts.getAttribute(Label.OUTPUT_TYPE)); for (Explainer projection : atts.get(Label.PROJECTION)) { sb.append(", "); append(projection); } } } protected void appendProcedure(CompoundExplainer explainer, int depth) { sb.append("CALL "); Attributes atts = explainer.get(); appendTableName(atts); sb.append('['); sb.append(atts.getValue(Label.PROCEDURE_CALLING_CONVENTION)); if (atts.containsKey(Label.PROCEDURE_IMPLEMENTATION)) { for (Explainer entry : atts.get(Label.PROCEDURE_IMPLEMENTATION)) { sb.append(", "); append(entry); } } sb.append(']'); sb.append('('); if (atts.containsKey(Label.OPERAND)) { for (Explainer entry : atts.get(Label.OPERAND)) { append(entry); sb.append(", "); } sb.setLength(sb.length()-2); } sb.append(')'); } protected void appendRow(CompoundExplainer rEx) { Attributes atts = rEx.get(); sb.append('['); if (atts.containsKey(Label.EXPRESSIONS)) { for (Explainer value : atts.get(Label.EXPRESSIONS)) { append(value); sb.append(", "); } sb.setLength(sb.length() - 2); } sb.append(']'); } protected void appendRowType(CompoundExplainer rtEx) { Attributes atts = rtEx.get(); if (atts.containsKey(Label.PARENT_TYPE) && atts.containsKey((Label.CHILD_TYPE))) { append(atts.getAttribute(Label.PARENT_TYPE)); sb.append(" - "); append(atts.getAttribute(Label.CHILD_TYPE)); } else if (atts.containsKey(Label.LEFT_TYPE) && atts.containsKey((Label.RIGHT_TYPE))) { append(atts.getAttribute(Label.LEFT_TYPE)); sb.append(" x "); append(atts.getAttribute(Label.RIGHT_TYPE)); } else if (atts.containsKey(Label.INDEX_NAME)) { sb.append("Index("); appendTableName(atts); sb.append('.').append(atts.getValue(Label.INDEX_NAME)).append(')'); } else if (atts.containsKey(Label.TABLE_NAME)) { appendTableName(atts); } else { sb.append(atts.getValue(Label.NAME)); } } protected void appendTableName(Attributes atts) { if (atts.containsKey(Label.TABLE_SCHEMA)) { String name = atts.getValue(Label.TABLE_SCHEMA).toString(); if (!name.equals(defaultSchemaName)) sb.append(name).append('.'); } sb.append(atts.getValue(Label.TABLE_NAME)); } protected void newRow() { rows.add(sb.toString()); sb.delete(0, sb.length()); } }