@Override public String toString() { String retval = ""; retval += "INDEX: " + ((index == null) ? "NULL" : (index.getParent().getTypeName() + "." + index.getTypeName())) + "\n"; retval += "USE: " + use.toString() + "\n"; retval += "TYPE: " + lookupType.toString() + "\n"; retval += "DIR: " + sortDirection.toString() + "\n"; retval += "ITER?: " + String.valueOf(keyIterate) + "\n"; retval += "NLIJ?: " + String.valueOf(nestLoopIndexJoin) + "\n"; retval += "IDX EXPRS:\n"; int i = 0; for (AbstractExpression expr : indexExprs) retval += "\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; retval += "END EXPRS:\n"; i = 0; for (AbstractExpression expr : endExprs) retval += "\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; retval += "OTHER EXPRS:\n"; i = 0; for (AbstractExpression expr : otherExprs) retval += "\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; retval += "JOIN EXPRS:\n"; i = 0; for (AbstractExpression expr : joinExprs) retval += "\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; return retval; }
@Override protected AbstractPlanNode recursivelyApply(AbstractPlanNode plan) { assert (plan != null); // depth first: // find AggregatePlanNode with exactly one child // where that child is an AbstractScanPlanNode. // Replace any qualifying AggregatePlanNode / AbstractScanPlanNode pair // with an IndexCountPlanNode or TableCountPlanNode ArrayList<AbstractPlanNode> children = new ArrayList<AbstractPlanNode>(); for (int i = 0; i < plan.getChildCount(); i++) children.add(plan.getChild(i)); for (AbstractPlanNode child : children) { // TODO this will break when children feed multiple parents AbstractPlanNode newChild = recursivelyApply(child); // Do a graft into the (parent) plan only if a replacement for a child was found. if (newChild == child) { continue; } boolean replaced = plan.replaceChild(child, newChild); assert (true == replaced); } // check for an aggregation of the right form if ((plan instanceof AggregatePlanNode) == false) return plan; assert (plan.getChildCount() == 1); AggregatePlanNode aggplan = (AggregatePlanNode) plan; // ENG-6131 fixed here. if (!(aggplan.isTableCountStar() || aggplan.isTableNonDistinctCountConstant() || aggplan.isTableCountNonDistinctNullableColumn())) { return plan; } AbstractPlanNode child = plan.getChild(0); // A table count can replace a seq scan only if it has no predicates. if (child instanceof SeqScanPlanNode) { if (((SeqScanPlanNode) child).getPredicate() != null) { return plan; } AbstractExpression postPredicate = aggplan.getPostPredicate(); if (postPredicate != null) { List<AbstractExpression> aggList = postPredicate.findAllSubexpressionsOfClass(AggregateExpression.class); boolean allCountStar = true; for (AbstractExpression expr : aggList) { if (expr.getExpressionType() != ExpressionType.AGGREGATE_COUNT_STAR) { allCountStar = false; break; } } if (allCountStar) { return plan; } } if (hasInlineLimit(aggplan)) { // table count EE executor does not handle inline limit stuff return plan; } return new TableCountPlanNode((AbstractScanPlanNode) child, aggplan); } // Otherwise, optimized counts only replace particular cases of index scan. if ((child instanceof IndexScanPlanNode) == false) return plan; IndexScanPlanNode isp = (IndexScanPlanNode) child; // Guard against (possible future?) cases of indexable subquery. if (((IndexScanPlanNode) child).isSubQuery()) { return plan; } // An index count or table count can replace an index scan only if it has no (post-)predicates // except those (post-)predicates are artifact predicates we added for reverse scan purpose only if (isp.getPredicate() != null && !isp.isPredicatesOptimizableForAggregate()) { return plan; } // With no start or end keys, there's not much a counting index can do. if (isp.getEndExpression() == null && isp.getSearchKeyExpressions().size() == 0) { // An indexed query without a where clause can fall back to a plain old table count. // This can only happen when a confused query like // "select count(*) from table order by index_key;" // meets a naive planner that doesn't just cull the no-op ORDER BY. Who, us? if (hasInlineLimit(aggplan)) { return plan; } return new TableCountPlanNode(isp, aggplan); } // check for the index's support for counting Index idx = isp.getCatalogIndex(); if (!idx.getCountable()) { return plan; } // The core idea is that counting index needs to know the start key and end key to // jump to to get counts instead of actually doing any scanning. // Options to be determined are: // - whether each of the start/end keys is missing, partial (a prefix of a compund key), or // complete, // - whether the count should include or exclude entries exactly matching each of the start/end // keys. // Not all combinations of these options are supported; // unsupportable cases cause the factory method to return null. IndexCountPlanNode countingPlan = IndexCountPlanNode.createOrNull(isp, aggplan); if (countingPlan == null) { return plan; } return countingPlan; }
static String genrateIndexRow(Table table, Index index) { StringBuilder sb = new StringBuilder(); sb.append(" <tr class='primaryrow2'>"); // name column String anchor = (table.getTypeName() + "-" + index.getTypeName()).toLowerCase(); sb.append( "<td style='white-space: nowrap'><i id='s-" + anchor + "--icon' class='icon-chevron-right'></i> <a href='#' id='s-"); sb.append(anchor).append("' class='togglex'>"); sb.append(index.getTypeName()); sb.append("</a></td>"); // type column sb.append("<td>"); sb.append(IndexType.get(index.getType()).toString()); sb.append("</td>"); // columns column sb.append("<td>"); List<ColumnRef> cols = CatalogUtil.getSortedCatalogItems(index.getColumns(), "index"); List<String> columnNames = new ArrayList<String>(); for (ColumnRef colRef : cols) { columnNames.add(colRef.getColumn().getTypeName()); } sb.append(StringUtils.join(columnNames, ", ")); sb.append("</td>"); // uniqueness column sb.append("<td>"); if (index.getAssumeunique()) { tag(sb, "important", "AssumeUnique"); } else if (index.getUnique()) { tag(sb, "important", "Unique"); } else { tag(sb, "info", "Nonunique"); } sb.append("</td>"); sb.append("</tr>\n"); // BUILD THE DROPDOWN FOR THE PLAN/DETAIL TABLE sb.append( "<tr class='dropdown2'><td colspan='5' id='s-" + table.getTypeName().toLowerCase() + "-" + index.getTypeName().toLowerCase() + "--dropdown'>\n"); IndexAnnotation annotation = (IndexAnnotation) index.getAnnotation(); if (annotation != null) { if (annotation.proceduresThatUseThis.size() > 0) { sb.append("<p>Used by procedures: "); List<String> procs = new ArrayList<String>(); for (Procedure proc : annotation.proceduresThatUseThis) { procs.add("<a href='#p-" + proc.getTypeName() + "'>" + proc.getTypeName() + "</a>"); } sb.append(StringUtils.join(procs, ", ")); sb.append("</p>"); } } sb.append("</td></tr>\n"); return sb.toString(); }
static String generateProcedureRow(Procedure procedure) { StringBuilder sb = new StringBuilder(); sb.append("<tr class='primaryrow'>"); // column 1: procedure name String anchor = procedure.getTypeName().toLowerCase(); sb.append( "<td style='white-space: nowrap'><i id='p-" + anchor + "--icon' class='icon-chevron-right'></i> <a href='#p-"); sb.append(anchor).append("' id='p-").append(anchor).append("' class='togglex'>"); sb.append(procedure.getTypeName()); sb.append("</a></td>"); // column 2: parameter types sb.append("<td>"); List<ProcParameter> params = CatalogUtil.getSortedCatalogItems(procedure.getParameters(), "index"); List<String> paramTypes = new ArrayList<String>(); for (ProcParameter param : params) { String paramType = VoltType.get((byte) param.getType()).name(); if (param.getIsarray()) { paramType += "[]"; } paramTypes.add(paramType); } if (paramTypes.size() == 0) { sb.append("<i>None</i>"); } sb.append(StringUtils.join(paramTypes, ", ")); sb.append("</td>"); // column 3: partitioning sb.append("<td>"); if (procedure.getSinglepartition()) { tag(sb, "success", "Single"); } else { tag(sb, "warning", "Multi"); } sb.append("</td>"); // column 4: read/write sb.append("<td>"); if (procedure.getReadonly()) { tag(sb, "success", "Read"); } else { tag(sb, "warning", "Write"); } sb.append("</td>"); // column 5: access sb.append("<td>"); List<String> groupNames = new ArrayList<String>(); for (GroupRef groupRef : procedure.getAuthgroups()) { groupNames.add(groupRef.getGroup().getTypeName()); } if (groupNames.size() == 0) { sb.append("<i>None</i>"); } sb.append(StringUtils.join(groupNames, ", ")); sb.append("</td>"); // column 6: attributes sb.append("<td>"); if (procedure.getHasjava()) { tag(sb, "info", "Java"); } else { tag(sb, null, "Single-Stmt"); } boolean isND = false; int scanCount = 0; for (Statement stmt : procedure.getStatements()) { scanCount += stmt.getSeqscancount(); if (!stmt.getIscontentdeterministic() || !stmt.getIsorderdeterministic()) { isND = false; } } if (isND) { tag(sb, "inverse", "Determinism"); } if (scanCount > 0) { tag(sb, "important", "Scans"); } sb.append("</td>"); sb.append("</tr>\n"); // BUILD THE DROPDOWN FOR THE STATEMENT/DETAIL TABLE sb.append( "<tr class='tablesorter-childRow'><td class='invert' colspan='6' id='p-" + procedure.getTypeName().toLowerCase() + "--dropdown'>\n"); // output partitioning parameter info if (procedure.getSinglepartition()) { String pTable = procedure.getPartitioncolumn().getParent().getTypeName(); String pColumn = procedure.getPartitioncolumn().getTypeName(); int pIndex = procedure.getPartitionparameter(); sb.append( String.format( "<p>Partitioned on parameter %d which maps to column %s" + " of table <a class='invert' href='#s-%s'>%s</a>.</p>", pIndex, pColumn, pTable, pTable)); } // output what schema this interacts with ProcedureAnnotation annotation = (ProcedureAnnotation) procedure.getAnnotation(); if (annotation != null) { // make sure tables appear in only one category annotation.tablesRead.removeAll(annotation.tablesUpdated); if (annotation.tablesRead.size() > 0) { sb.append("<p>Read-only access to tables: "); List<String> tables = new ArrayList<String>(); for (Table table : annotation.tablesRead) { tables.add("<a href='#s-" + table.getTypeName() + "'>" + table.getTypeName() + "</a>"); } sb.append(StringUtils.join(tables, ", ")); sb.append("</p>"); } if (annotation.tablesUpdated.size() > 0) { sb.append("<p>Read/Write access to tables: "); List<String> tables = new ArrayList<String>(); for (Table table : annotation.tablesUpdated) { tables.add("<a href='#s-" + table.getTypeName() + "'>" + table.getTypeName() + "</a>"); } sb.append(StringUtils.join(tables, ", ")); sb.append("</p>"); } if (annotation.indexesUsed.size() > 0) { sb.append("<p>Uses indexes: "); List<String> indexes = new ArrayList<String>(); for (Index index : annotation.indexesUsed) { Table table = (Table) index.getParent(); indexes.add( "<a href='#s-" + table.getTypeName() + "-" + index.getTypeName() + "'>" + index.getTypeName() + "</a>"); } sb.append(StringUtils.join(indexes, ", ")); sb.append("</p>"); } } sb.append(generateStatementsTable(procedure)); sb.append("</td></tr>\n"); return sb.toString(); }
static String genrateStatementRow(Procedure procedure, Statement statement) { StringBuilder sb = new StringBuilder(); sb.append(" <tr class='primaryrow2'>"); // name column String anchor = (procedure.getTypeName() + "-" + statement.getTypeName()).toLowerCase(); sb.append( "<td style='white-space: nowrap'><i id='p-" + anchor + "--icon' class='icon-chevron-right'></i> <a href='#' id='p-"); sb.append(anchor).append("' class='togglex'>"); sb.append(statement.getTypeName()); sb.append("</a></td>"); // sql column sb.append("<td><tt>"); sb.append(statement.getSqltext()); sb.append("</td></tt>"); // params column sb.append("<td>"); List<StmtParameter> params = CatalogUtil.getSortedCatalogItems(statement.getParameters(), "index"); List<String> paramTypes = new ArrayList<String>(); for (StmtParameter param : params) { paramTypes.add(VoltType.get((byte) param.getJavatype()).name()); } if (paramTypes.size() == 0) { sb.append("<i>None</i>"); } sb.append(StringUtils.join(paramTypes, ", ")); sb.append("</td>"); // r/w column sb.append("<td>"); if (statement.getReadonly()) { tag(sb, "success", "Read"); } else { tag(sb, "warning", "Write"); } sb.append("</td>"); // attributes sb.append("<td>"); if (!statement.getIscontentdeterministic() || !statement.getIsorderdeterministic()) { tag(sb, "inverse", "Determinism"); } if (statement.getSeqscancount() > 0) { tag(sb, "important", "Scans"); } sb.append("</td>"); sb.append("</tr>\n"); // BUILD THE DROPDOWN FOR THE PLAN/DETAIL TABLE sb.append( "<tr class='dropdown2'><td colspan='5' id='p-" + procedure.getTypeName().toLowerCase() + "-" + statement.getTypeName().toLowerCase() + "--dropdown'>\n"); sb.append("<div class='well well-small'><h4>Explain Plan:</h4>\n"); StatementAnnotation annotation = (StatementAnnotation) statement.getAnnotation(); if (annotation != null) { String plan = annotation.explainPlan; plan = plan.replace("\n", "<br/>"); plan = plan.replace(" ", " "); for (Table t : annotation.tablesRead) { String name = t.getTypeName().toUpperCase(); String link = "\"<a href='#s-" + t.getTypeName() + "'>" + name + "</a>\""; plan = plan.replace("\"" + name + "\"", link); } for (Table t : annotation.tablesUpdated) { String name = t.getTypeName().toUpperCase(); String link = "\"<a href='#s-" + t.getTypeName() + "'>" + name + "</a>\""; plan = plan.replace("\"" + name + "\"", link); } for (Index i : annotation.indexesUsed) { Table t = (Table) i.getParent(); String name = i.getTypeName().toUpperCase(); String link = "\"<a href='#s-" + t.getTypeName() + "-" + i.getTypeName() + "'>" + name + "</a>\""; plan = plan.replace("\"" + name + "\"", link); } sb.append("<tt>").append(plan).append("</tt>"); } else { sb.append("<i>No SQL explain plan found.</i>\n"); } sb.append("</div>\n"); sb.append("</td></tr>\n"); return sb.toString(); }
public static MaterializedViewInfo addVerticalPartition( final Table catalog_tbl, final Collection<Column> catalog_cols, final boolean createIndex) throws Exception { assert (catalog_cols.isEmpty() == false); Database catalog_db = ((Database) catalog_tbl.getParent()); String viewName = getNextVerticalPartitionName(catalog_tbl, catalog_cols); if (debug.get()) LOG.debug( String.format( "Adding Vertical Partition %s for %s: %s", viewName, catalog_tbl, catalog_cols)); // Create a new virtual table Table virtual_tbl = catalog_db.getTables().get(viewName); if (virtual_tbl == null) { virtual_tbl = catalog_db.getTables().add(viewName); } virtual_tbl.setIsreplicated(true); virtual_tbl.setMaterializer(catalog_tbl); virtual_tbl.setSystable(true); virtual_tbl.getColumns().clear(); // Create MaterializedView and link it to the virtual table MaterializedViewInfo catalog_view = catalog_tbl.getViews().add(viewName); catalog_view.setVerticalpartition(true); catalog_view.setDest(virtual_tbl); List<Column> indexColumns = new ArrayList<Column>(); Column partition_col = catalog_tbl.getPartitioncolumn(); if (partition_col instanceof VerticalPartitionColumn) { partition_col = ((VerticalPartitionColumn) partition_col).getHorizontalColumn(); } if (debug.get()) LOG.debug(catalog_tbl.getName() + " Partition Column: " + partition_col); int i = 0; assert (catalog_cols != null); assert (catalog_cols.isEmpty() == false) : "No vertical partitioning columns for " + catalog_view.fullName(); for (Column catalog_col : catalog_cols) { // MaterializedView ColumnRef ColumnRef catalog_ref = catalog_view.getGroupbycols().add(catalog_col.getName()); catalog_ref.setColumn(catalog_col); catalog_ref.setIndex(i++); // VirtualTable Column Column virtual_col = virtual_tbl.getColumns().add(catalog_col.getName()); virtual_col.setDefaulttype(catalog_col.getDefaulttype()); virtual_col.setDefaultvalue(catalog_col.getDefaultvalue()); virtual_col.setIndex(catalog_col.getIndex()); virtual_col.setNullable(catalog_col.getNullable()); virtual_col.setSize(catalog_col.getSize()); virtual_col.setType(catalog_col.getType()); if (debug.get()) LOG.debug(String.format("Added VerticalPartition column %s", virtual_col.fullName())); // If they want an index, then we'll make one based on every column except for the column // that the table is partitioned on if (createIndex) { boolean include = true; if (partition_col instanceof MultiColumn) { include = (((MultiColumn) partition_col).contains(catalog_col) == false); } else if (catalog_col.equals(partition_col)) { include = false; } if (include) indexColumns.add(virtual_col); } } // FOR if (createIndex) { if (indexColumns.isEmpty()) { Map<String, Object> m = new ListOrderedMap<String, Object>(); m.put("Partition Column", partition_col); m.put("VP Table Columns", virtual_tbl.getColumns()); m.put("Passed-in Columns", CatalogUtil.debug(catalog_cols)); LOG.error("Failed to find index columns\n" + StringUtil.formatMaps(m)); throw new Exception(String.format("No columns selected for index on %s", viewName)); } String idxName = "SYS_IDX_" + viewName; Index virtual_idx = virtual_tbl.getIndexes().get(idxName); if (virtual_idx == null) { virtual_idx = virtual_tbl.getIndexes().add(idxName); } virtual_idx.getColumns().clear(); IndexType idxType = (indexColumns.size() == 1 ? IndexType.HASH_TABLE : IndexType.BALANCED_TREE); virtual_idx.setType(idxType.getValue()); i = 0; for (Column catalog_col : indexColumns) { ColumnRef cref = virtual_idx.getColumns().add(catalog_col.getTypeName()); cref.setColumn(catalog_col); cref.setIndex(i++); } // FOR if (debug.get()) LOG.debug( String.format( "Created %s index '%s' for vertical partition '%s'", idxType, idxName, viewName)); } return (catalog_view); }