// silly helper to add datasources for a table catalog object private void addDataSources(Table table, int hostId, List<Pair<Integer, Long>> partitions) { for (Pair<Integer, Long> p : partitions) { Integer partition = p.getFirst(); Long site = p.getSecond(); /* * IOException can occur if there is a problem * with the persistent aspects of the datasource storage */ try { HashMap<String, ExportDataSource> dataSourcesForPartition = m_dataSourcesByPartition.get(partition); if (dataSourcesForPartition == null) { dataSourcesForPartition = new HashMap<String, ExportDataSource>(); m_dataSourcesByPartition.put(partition, dataSourcesForPartition); } ExportDataSource exportDataSource = new ExportDataSource( m_onSourceDrained, "database", table.getTypeName(), partition, site, table.getSignature(), m_timestamp, table.getColumns(), m_directory.getPath()); m_numSources++; exportLog.info( "Creating ExportDataSource for table " + table.getTypeName() + " signature " + table.getSignature() + " partition id " + partition); dataSourcesForPartition.put(table.getSignature(), exportDataSource); } catch (IOException e) { VoltDB.crashLocalVoltDB( "Error creating datasources for table " + table.getTypeName() + " host id " + hostId, true, e); } } }
// Even though this function applies generally to expressions and tables and not just to TVEs as // such, // this function is somewhat TVE-related because TVEs DO represent the points where expression // trees // depend on tables. public static boolean isOperandDependentOnTable(AbstractExpression expr, Table table) { for (TupleValueExpression tve : ExpressionUtil.getTupleValueExpressions(expr)) { // TODO: This clumsy testing of table names regardless of table aliases is // EXACTLY why we can't have nice things like self-joins. if (table.getTypeName().equals(tve.getTableName())) { return true; } } return false; }
@Override public void resolveForTable(Table table) { assert (table != null); // It MAY be that for the case in which this function is called (expression indexes), the // column's // table name is not specified (and not missed?). // It is possible to "correct" that here by cribbing it from the supplied table (base table for // the index) // -- not bothering for now. Column column = table.getColumns().getExact(m_columnName); assert (column != null); m_tableName = table.getTypeName(); m_columnIndex = column.getIndex(); setTypeSizeAndInBytes(column); }
@Override public boolean computeEstimatesRecursively( PlanStatistics stats, Cluster cluster, Database db, DatabaseEstimates estimates, ScalarValueHints[] paramHints) { Table target = db.getTables().getIgnoreCase(m_targetTableName); assert (target != null); DatabaseEstimates.TableEstimates tableEstimates = estimates.getEstimatesForTable(target.getTypeName()); stats.incrementStatistic(0, StatsField.TUPLES_READ, tableEstimates.maxTuples); m_estimatedOutputTupleCount = tableEstimates.maxTuples; return true; }
/** * Parse tables and parameters * * @param root * @param db */ @Override void parseTablesAndParams(VoltXMLElement stmtNode) { assert (stmtNode.children.size() > 1); tableList.clear(); for (VoltXMLElement childSQL : stmtNode.children) { if (childSQL.name.equalsIgnoreCase(SELECT_NODE_NAME)) { AbstractParsedStmt childStmt = new ParsedSelectStmt(this.m_paramValues, this.m_db); childStmt.parseTablesAndParams(childSQL); m_children.add(childStmt); // So far T UNION T (as well as T JOIN T) are not handled properly // by the fragmentizer. Need to give an error if any table is mentioned // in the UNION TREE more than once. if (childStmt.scanColumns != null) { for (Table table : childStmt.tableList) { String tableName = table.getTypeName(); if (m_uniqueTables.contains(tableName)) { // The table is not 'unique' across the union throw new PlanningErrorException( "Table " + tableName + " appears more than once in the union statement"); } else { m_uniqueTables.add(tableName); } } } else { throw new PlanningErrorException("Scan columns are NULL the UNION statement"); } // Add statement's tables to the consolidated list tableList.addAll(childStmt.tableList); } else if (childSQL.name.equalsIgnoreCase(UNION_NODE_NAME)) { ParsedUnionStmt childStmt = new ParsedUnionStmt(this.m_paramValues, this.m_db); // Pass already accumulated unique tables to the child union childStmt.m_uniqueTables = m_uniqueTables; childStmt.parseTablesAndParams(childSQL); m_children.add(childStmt); // Add statement's tables to the consolidated list tableList.addAll(childStmt.tableList); // Child's unique tables now contains the consolidated list m_uniqueTables = childStmt.m_uniqueTables; } else { throw new PlanningErrorException("Unexpected Element in UNION statement: " + childSQL.name); } } }
@Override public Callable<Boolean> createSetup( String file_path, String file_nonce, long txnId, Map<Integer, Long> partitionTransactionIds, JSONObject jsData, SystemProcedureExecutionContext context, VoltTable result, Map<String, Map<Integer, Pair<Long, Long>>> exportSequenceNumbers, SiteTracker tracker, HashinatorSnapshotData hashinatorData, long timestamp) { assert SnapshotSiteProcessor.ExecutionSitesCurrentlySnapshotting.isEmpty(); final IndexSnapshotRequestConfig config = new IndexSnapshotRequestConfig(jsData, context.getDatabase()); final Map<Integer, Long> pidToLocalHSIds = findLocalSources(config.partitionRanges, tracker); // mark snapshot start in registry final AtomicInteger numTables = new AtomicInteger(config.tables.length); m_snapshotRecord = SnapshotRegistry.startSnapshot( txnId, context.getHostId(), file_path, file_nonce, SnapshotFormat.INDEX, config.tables); // create table tasks for (Table table : config.tables) { createTasksForTable( table, config.partitionRanges, pidToLocalHSIds, numTables, m_snapshotRecord); result.addRow( context.getHostId(), CoreUtils.getHostnameOrAddress(), table.getTypeName(), "SUCCESS", ""); } return null; }
/** For each site, generate a task for each target it has for this table. */ private void createTasksForTable( Table table, Collection<IndexSnapshotRequestConfig.PartitionRanges> partitionRanges, Map<Integer, Long> pidToLocalHSIDs, AtomicInteger numTables, SnapshotRegistry.Snapshot snapshotRecord) { // no work on this node if (pidToLocalHSIDs.isEmpty()) { return; } // create a null data target final DevNullSnapshotTarget dataTarget = new DevNullSnapshotTarget(); final Runnable onClose = new TargetStatsClosure(dataTarget, table.getTypeName(), numTables, snapshotRecord); dataTarget.setOnCloseHandler(onClose); m_targets.add(dataTarget); // go over all local sites, create a task for each source site for (IndexSnapshotRequestConfig.PartitionRanges partitionRange : partitionRanges) { Long localHSId = pidToLocalHSIDs.get(partitionRange.partitionId); // The partition may not exist on this node. If so, keep calm and carry on if (localHSId != null) { // based on the source partition, the predicate is different final SnapshotTableTask task = new SnapshotTableTask( table, new SnapshotDataFilter[0], createIndexExpressionForTable(table, partitionRange.ranges), false); task.setTarget(dataTarget); placeTask(task, Arrays.asList(localHSId)); } } }
@Override public String getTableName() { return m_table.getTypeName(); }
/** Determine which parameter is the partition indicator */ static void parsePartitionInfo( VoltCompiler compiler, Database db, Procedure procedure, String info) throws VoltCompilerException { assert (procedure.getSinglepartition() == true); // check this isn't empty if (info.length() == 0) { String msg = "Missing or Truncated PartitionInfo in attribute for procedure: " + procedure.getClassname(); throw compiler.new VoltCompilerException(msg); } // split on the colon String[] parts = info.split(":"); // if the colon doesn't split well, we have a problem if (parts.length != 2) { String msg = "Possibly invalid PartitionInfo in attribute for procedure: " + procedure.getClassname(); throw compiler.new VoltCompilerException(msg); } // relabel the parts for code readability String columnInfo = parts[0].trim(); int paramIndex = Integer.parseInt(parts[1].trim()); int paramCount = procedure.getParameters().size(); if ((paramIndex < 0) || (paramIndex >= paramCount)) { String msg = "PartitionInfo specifies invalid parameter index for procedure: " + procedure.getClassname(); throw compiler.new VoltCompilerException(msg); } // locate the parameter procedure.setPartitionparameter(paramIndex); // split the columninfo parts = columnInfo.split("\\."); if (parts.length != 2) { String msg = "Possibly invalid PartitionInfo in attribute for procedure: " + procedure.getClassname(); throw compiler.new VoltCompilerException(msg); } // relabel the parts for code readability String tableName = parts[0].trim(); String columnName = parts[1].trim(); // locate the partition column CatalogMap<Table> tables = db.getTables(); for (Table table : tables) { if (table.getTypeName().equalsIgnoreCase(tableName)) { CatalogMap<Column> columns = table.getColumns(); Column partitionColumn = table.getPartitioncolumn(); if (partitionColumn == null) { String msg = String.format( "PartitionInfo for procedure %s references table %s which has no partition column (may be replicated).", procedure.getClassname(), table.getTypeName()); throw compiler.new VoltCompilerException(msg); } for (Column column : columns) { if (column.getTypeName().equalsIgnoreCase(columnName)) { if (partitionColumn.getTypeName().equals(column.getTypeName())) { procedure.setPartitioncolumn(column); procedure.setPartitiontable(table); return; } else { String msg = "PartitionInfo for procedure " + procedure.getClassname() + " refers to a column in schema which is not a partition key."; throw compiler.new VoltCompilerException(msg); } } } } } String msg = "PartitionInfo for procedure " + procedure.getClassname() + " refers to a column in schema which can't be found."; throw compiler.new VoltCompilerException(msg); }
@Override public String toString() { String retval = "SQL:\n\t" + sql + "\n"; retval += "PARAMETERS:\n\t"; for (VoltType param : paramList) { retval += param.toString() + " "; } retval += "\nTABLE SOURCES:\n\t"; for (Table table : tableList) { retval += table.getTypeName() + " "; } retval += "\nSCAN COLUMNS:\n"; if (scanColumns != null) { for (String table : scanColumns.keySet()) { retval += "\tTable: " + table + ":\n"; for (SchemaColumn col : scanColumns.get(table)) { retval += "\t\tColumn: " + col.getColumnName() + ": "; retval += col.getExpression().toString() + "\n"; } } } else { retval += "\tALL\n"; } if (where != null) { retval += "\nWHERE:\n"; retval += "\t" + where.toString() + "\n"; retval += "WHERE SELECTION LIST:\n"; int i = 0; for (AbstractExpression expr : whereSelectionList) retval += "\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; retval += "NO TABLE SELECTION LIST:\n"; i = 0; for (AbstractExpression expr : noTableSelectionList) retval += "\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; retval += "TABLE FILTER LIST:\n"; for (Entry<Table, ArrayList<AbstractExpression>> pair : tableFilterList.entrySet()) { i = 0; retval += "\tTABLE: " + pair.getKey().getTypeName() + "\n"; for (AbstractExpression expr : pair.getValue()) retval += "\t\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; } retval += "JOIN CLAUSE LIST:\n"; for (Entry<TablePair, ArrayList<AbstractExpression>> pair : joinSelectionList.entrySet()) { i = 0; retval += "\tTABLES: " + pair.getKey().t1.getTypeName() + " and " + pair.getKey().t2.getTypeName() + "\n"; for (AbstractExpression expr : pair.getValue()) retval += "\t\t(" + String.valueOf(i++) + ") " + expr.toString() + "\n"; } } return retval; }
protected boolean createSetupInternal( String file_path, String file_nonce, long txnId, Map<Integer, Long> partitionTransactionIds, JSONObject jsData, SystemProcedureExecutionContext context, String hostname, final VoltTable result, Map<String, Map<Integer, Pair<Long, Long>>> exportSequenceNumbers, SiteTracker tracker, long timestamp) throws IOException { assert (SnapshotSiteProcessor.ExecutionSitesCurrentlySnapshotting.isEmpty()); /* * List of partitions to include if this snapshot is * going to be deduped. Attempts to break up the work * by seeding and RNG selecting * a random replica to do the work. Will not work in failure * cases, but we don't use dedupe when we want durability. */ List<Long> sitesToInclude = CSVSnapshotWritePlan.computeDedupedLocalSites(txnId, tracker); // If there's no work to do on this host, just claim success and get out: if (sitesToInclude.isEmpty() && !tracker.isFirstHost()) { return false; } NativeSnapshotWritePlan.createFileBasedCompletionTasks( file_path, file_nonce, txnId, partitionTransactionIds, context, exportSequenceNumbers, timestamp); final List<Table> tables = SnapshotUtil.getTablesToSave(context.getDatabase()); final AtomicInteger numTables = new AtomicInteger(tables.size()); final SnapshotRegistry.Snapshot snapshotRecord = SnapshotRegistry.startSnapshot( txnId, context.getHostId(), file_path, file_nonce, SnapshotFormat.CSV, tables.toArray(new Table[0])); SnapshotDataTarget sdt = null; boolean noTargetsCreated = true; final ArrayList<SnapshotTableTask> partitionedSnapshotTasks = new ArrayList<SnapshotTableTask>(); final ArrayList<SnapshotTableTask> replicatedSnapshotTasks = new ArrayList<SnapshotTableTask>(); for (final Table table : tables) { /* * For a deduped csv snapshot, only produce the replicated tables on the "leader" * host. */ if (table.getIsreplicated() && !tracker.isFirstHost()) { snapshotRecord.removeTable(table.getTypeName()); continue; } File saveFilePath = null; saveFilePath = SnapshotUtil.constructFileForTable( table, file_path, file_nonce, SnapshotFormat.CSV, context.getHostId()); try { sdt = new SimpleFileSnapshotDataTarget(saveFilePath, !table.getIsreplicated()); m_targets.add(sdt); final Runnable onClose = new TargetStatsClosure(sdt, table.getTypeName(), numTables, snapshotRecord); sdt.setOnCloseHandler(onClose); List<SnapshotDataFilter> filters = new ArrayList<SnapshotDataFilter>(); filters.add(new CSVSnapshotFilter(CatalogUtil.getVoltTable(table), ',', null)); final SnapshotTableTask task = new SnapshotTableTask( table.getRelativeIndex(), sdt, filters.toArray(new SnapshotDataFilter[filters.size()]), table.getIsreplicated(), table.getTypeName()); if (table.getIsreplicated()) { replicatedSnapshotTasks.add(task); } else { partitionedSnapshotTasks.add(task); } noTargetsCreated = false; result.addRow(context.getHostId(), hostname, table.getTypeName(), "SUCCESS", ""); } catch (IOException ex) { handleTargetCreationError( sdt, context, file_nonce, hostname, table.getTypeName(), ex, result); } } if (noTargetsCreated) { SnapshotRegistry.discardSnapshot(snapshotRecord); } // CSV snapshots do the partitioned work only on the specified sites for de-duping, // but since we've pre-filtered the replicated task list to only contain entries on // one node, we can go ahead and distribute them across all of the sites on that node. placePartitionedTasks(partitionedSnapshotTasks, sitesToInclude); placeReplicatedTasks(replicatedSnapshotTasks, tracker.getSitesForHost(context.getHostId())); return noTargetsCreated; }
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(); }
static String generateSchemaRow(Table table, boolean isExportTable) { StringBuilder sb = new StringBuilder(); sb.append("<tr class='primaryrow'>"); // column 1: table name String anchor = table.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(table.getTypeName()); sb.append("</a></td>"); // column 2: type sb.append("<td>"); if (table.getMaterializer() != null) { tag(sb, "info", "Materialized View"); } else { if (isExportTable) { tag(sb, "inverse", "Export Table"); } else { tag(sb, null, "Table"); } } sb.append("</td>"); // column 3: partitioning sb.append("<td style='whitespace: nowrap;'>"); if (table.getIsreplicated()) { tag(sb, "warning", "Replicated"); } else { tag(sb, "success", "Partitioned"); Column partitionCol = table.getPartitioncolumn(); if (partitionCol != null) { sb.append("<small> on " + partitionCol.getName() + "</small>"); } else { Table matSrc = table.getMaterializer(); if (matSrc != null) { sb.append("<small> with " + matSrc.getTypeName() + "</small>"); } } } sb.append("</td>"); // column 4: column count sb.append("<td>"); sb.append(table.getColumns().size()); sb.append("</td>"); // column 5: index count sb.append("<td>"); sb.append(table.getIndexes().size()); sb.append("</td>"); // column 6: has pkey sb.append("<td>"); boolean found = false; for (Constraint constraint : table.getConstraints()) { if (ConstraintType.get(constraint.getType()) == ConstraintType.PRIMARY_KEY) { found = true; break; } } if (found) { tag(sb, "info", "Has-PKey"); } else { tag(sb, null, "No-PKey"); } sb.append("</td>"); // column 6: has tuple limit sb.append("<td>"); if (table.getTuplelimit() != Integer.MAX_VALUE) { tag(sb, "info", String.valueOf(table.getTuplelimit())); } else { tag(sb, null, "No-limit"); } sb.append("</td>"); sb.append("</tr>\n"); // BUILD THE DROPDOWN FOR THE DDL / INDEXES DETAIL sb.append( "<tr class='tablesorter-childRow'><td class='invert' colspan='7' id='s-" + table.getTypeName().toLowerCase() + "--dropdown'>\n"); TableAnnotation annotation = (TableAnnotation) table.getAnnotation(); if (annotation != null) { // output the DDL if (annotation.ddl == null) { sb.append("<p>MISSING DDL</p>\n"); } else { String ddl = annotation.ddl; sb.append("<p><pre>" + ddl + "</pre></p>\n"); } // make sure procs appear in only one category annotation.proceduresThatReadThis.removeAll(annotation.proceduresThatUpdateThis); if (annotation.proceduresThatReadThis.size() > 0) { sb.append("<p>Read-only by procedures: "); List<String> procs = new ArrayList<String>(); for (Procedure proc : annotation.proceduresThatReadThis) { procs.add("<a href='#p-" + proc.getTypeName() + "'>" + proc.getTypeName() + "</a>"); } sb.append(StringUtils.join(procs, ", ")); sb.append("</p>"); } if (annotation.proceduresThatUpdateThis.size() > 0) { sb.append("<p>Read/Write by procedures: "); List<String> procs = new ArrayList<String>(); for (Procedure proc : annotation.proceduresThatUpdateThis) { procs.add("<a href='#p-" + proc.getTypeName() + "'>" + proc.getTypeName() + "</a>"); } sb.append(StringUtils.join(procs, ", ")); sb.append("</p>"); } } if (table.getIndexes().size() > 0) { sb.append(generateIndexesTable(table)); } else { sb.append("<p>No indexes defined on table.</p>\n"); } sb.append("</td></tr>\n"); return sb.toString(); }