use of org.voltdb.catalog.Index in project voltdb by VoltDB.
the class AbstractParsedStmt method producesOneRowOutput.
// Function evaluates whether the statement results in at most
// one output row. This is implemented for single table by checking
// value equivalence of predicates in where clause and checking
// if all defined unique indexes are in value equivalence set.
// Returns true if the statement results is at most one output
// row else false
protected boolean producesOneRowOutput() {
if (m_tableAliasMap.size() != 1) {
return false;
}
// Get the table. There's only one.
StmtTableScan scan = m_tableAliasMap.values().iterator().next();
Table table = getTableFromDB(scan.getTableName());
// May be sub-query? If can't find the table there's no use to continue.
if (table == null) {
return false;
}
// Get all the indexes defined on the table
CatalogMap<Index> indexes = table.getIndexes();
if (indexes == null || indexes.size() == 0) {
// no indexes defined on the table
return false;
}
// Collect value equivalence expression for the SQL statement
HashMap<AbstractExpression, Set<AbstractExpression>> valueEquivalence = analyzeValueEquivalence();
// If no value equivalence filter defined in SQL statement, there's no use to continue
if (valueEquivalence.isEmpty()) {
return false;
}
// Collect all tve expressions from value equivalence set which have equivalence
// defined to parameterized or constant value expression.
// Eg: T.A = ? or T.A = 1
Set<AbstractExpression> parameterizedConstantKeys = new HashSet<>();
// get all the keys
Set<AbstractExpression> valueEquivalenceKeys = valueEquivalence.keySet();
for (AbstractExpression key : valueEquivalenceKeys) {
if (key instanceof TupleValueExpression) {
Set<AbstractExpression> values = valueEquivalence.get(key);
for (AbstractExpression value : values) {
if ((value instanceof ParameterValueExpression) || (value instanceof ConstantValueExpression)) {
TupleValueExpression tve = (TupleValueExpression) key;
parameterizedConstantKeys.add(tve);
}
}
}
}
// index defined on table appears in tve equivalence expression gathered above.
for (Index index : indexes) {
// Perform lookup only on pure column indices which are unique
if (!index.getUnique() || !index.getExpressionsjson().isEmpty()) {
continue;
}
Set<AbstractExpression> indexExpressions = new HashSet<>();
CatalogMap<ColumnRef> indexColRefs = index.getColumns();
for (ColumnRef indexColRef : indexColRefs) {
Column col = indexColRef.getColumn();
TupleValueExpression tve = new TupleValueExpression(scan.getTableName(), scan.getTableAlias(), col.getName(), col.getName(), col.getIndex());
indexExpressions.add(tve);
}
if (parameterizedConstantKeys.containsAll(indexExpressions)) {
return true;
}
}
return false;
}
use of org.voltdb.catalog.Index in project voltdb by VoltDB.
the class SwapTablesPlanNode method initializeSwapTablesPlanNode.
/**
* Fill out all of the serializable attributes of the node, validating
* its arguments' compatibility along the way to ensure successful
* execution.
* @param theTable the catalog definition of the 1st table swap argument
* @param otherTable the catalog definition of the 2nd table swap argument
* @throws PlannerErrorException if one or more compatibility validations fail
*/
public void initializeSwapTablesPlanNode(Table theTable, Table otherTable) {
String theName = theTable.getTypeName();
setTargetTableName(theName);
String otherName = otherTable.getTypeName();
m_otherTargetTableName = otherName;
FailureMessage failureMessage = new FailureMessage(theName, otherName);
validateTableCompatibility(theName, otherName, theTable, otherTable, failureMessage);
validateColumnCompatibility(theName, otherName, theTable, otherTable, failureMessage);
// Maintain sets of indexes and index-supported (UNIQUE) constraints
// and the primary key index found on otherTable.
// Removing them as they are matched by indexes/constraints on theTable
// and added to the list of swappable indexes should leave the sets empty.
HashSet<Index> otherIndexSet = new HashSet<>();
// The constraint set is actually a HashMap to retain the
// defining constraint name for help with error messages.
// Track the primary key separately since it should match one-to-one.
HashMap<Index, String> otherConstraintIndexMap = new HashMap<>();
Index otherPrimaryKeyIndex = null;
// Collect the system-defined (internal) indexes supporting constraints
// and the primary key index if any.
CatalogMap<Constraint> candidateConstraints = otherTable.getConstraints();
for (Constraint otherConstraint : candidateConstraints) {
Index otherIndex = otherConstraint.getIndex();
if (otherIndex == null) {
// effect on the swap table plan.
continue;
}
// Set aside the one primary key index for special handling.
if (otherConstraint.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
otherPrimaryKeyIndex = otherIndex;
continue;
}
otherConstraintIndexMap.put(otherIndex, otherConstraint.getTypeName());
}
// Collect the user-defined (external) indexes on otherTable. The indexes
// in this set are removed as corresponding matches are found.
// System-generated indexes that support constraints are checked separately,
// so don't add them to this set.
CatalogMap<Index> candidateIndexes = otherTable.getIndexes();
for (Index otherIndex : candidateIndexes) {
if (otherIndex != otherPrimaryKeyIndex && !otherConstraintIndexMap.containsKey(otherIndex)) {
otherIndexSet.add(otherIndex);
}
}
// Collect the indexes that support constraints on theTable
HashSet<Index> theConstraintIndexSet = new HashSet<>();
Index thePrimaryKeyIndex = null;
for (Constraint constraint : theTable.getConstraints()) {
Index theIndex = constraint.getIndex();
if (theIndex == null) {
continue;
}
if (constraint.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
thePrimaryKeyIndex = theIndex;
continue;
}
theConstraintIndexSet.add(constraint.getIndex());
}
// make sure the indexes are swappable.
if (thePrimaryKeyIndex != null && otherPrimaryKeyIndex != null) {
if (indexesCanBeSwapped(thePrimaryKeyIndex, otherPrimaryKeyIndex)) {
m_theIndexes.add(thePrimaryKeyIndex.getTypeName());
m_otherIndexes.add(otherPrimaryKeyIndex.getTypeName());
} else {
failureMessage.addReason("PRIMARY KEY constraints do not match on both tables");
}
} else if ((thePrimaryKeyIndex != null && otherPrimaryKeyIndex == null) || (thePrimaryKeyIndex == null && otherPrimaryKeyIndex != null)) {
failureMessage.addReason("one table has a PRIMARY KEY constraint and the other does not");
}
// Try to cross-reference each user-defined index on the two tables.
for (Index theIndex : theTable.getIndexes()) {
if (theConstraintIndexSet.contains(theIndex) || theIndex == thePrimaryKeyIndex) {
// Constraints are checked below.
continue;
}
boolean matched = false;
for (Index otherIndex : otherIndexSet) {
if (indexesCanBeSwapped(theIndex, otherIndex)) {
m_theIndexes.add(theIndex.getTypeName());
m_otherIndexes.add(otherIndex.getTypeName());
otherIndexSet.remove(otherIndex);
matched = true;
break;
}
}
if (matched) {
continue;
}
// No match: look for a likely near-match based on naming
// convention for the most helpful error message.
// Otherwise, give a more generic error message.
String theIndexName = theIndex.getTypeName();
String message = "the index " + theIndexName + " on table " + theName + " has no corresponding index in the other table";
String otherIndexName = theIndexName.replace(theName, otherName);
Index otherIndex = candidateIndexes.getIgnoreCase(otherIndexName);
if (otherIndex != null) {
message += "; the closest candidate (" + otherIndexName + ") has mismatches in the following attributes: " + String.join(", ", diagnoseIndexMismatch(theIndex, otherIndex));
}
failureMessage.addReason(message);
}
// matched along the way.
if (!otherIndexSet.isEmpty()) {
List<String> indexNames = otherIndexSet.stream().map(idx -> idx.getTypeName()).collect(Collectors.toList());
failureMessage.addReason("the table " + otherName + " contains these index(es) " + "which have no corresponding indexes on " + theName + ": " + "(" + String.join(", ", indexNames) + ")");
}
// constraints on the two tables.
for (Constraint theConstraint : theTable.getConstraints()) {
Index theIndex = theConstraint.getIndex();
if (theIndex == null) {
// effect on the swap table plan.
continue;
}
if (theConstraint.getType() == ConstraintType.PRIMARY_KEY.getValue()) {
// Primary key compatibility checked above.
continue;
}
boolean matched = false;
for (Entry<Index, String> otherEntry : otherConstraintIndexMap.entrySet()) {
Index otherIndex = otherEntry.getKey();
if (indexesCanBeSwapped(theIndex, otherIndex)) {
m_theIndexes.add(theIndex.getTypeName());
m_otherIndexes.add(otherIndex.getTypeName());
otherConstraintIndexMap.remove(otherIndex);
matched = true;
break;
}
}
if (matched) {
continue;
}
String theConstraintName = theConstraint.getTypeName();
failureMessage.addReason("the constraint " + theConstraintName + " on table " + theName + " " + "has no corresponding constraint on the other table");
}
// matched along the way.
if (!otherConstraintIndexMap.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append("these constraints (or system internal index names) on table " + otherName + " " + "have no corresponding constraints on the other table: (");
String separator = "";
for (Entry<Index, String> remainder : otherConstraintIndexMap.entrySet()) {
String constraintName = remainder.getValue();
String description = (constraintName != null && !constraintName.equals("")) ? constraintName : ("<anonymous with system internal index name: " + remainder.getKey().getTypeName() + ">");
sb.append(separator).append(description);
separator = ", ";
}
sb.append(")");
failureMessage.addReason(sb.toString());
}
if (failureMessage.numFailures() > 0) {
throw new PlanningErrorException(failureMessage.getMessage());
}
}
use of org.voltdb.catalog.Index in project voltdb by VoltDB.
the class PlanAssembler method calculateIndexGroupByInfo.
// Sets IndexGroupByInfo for an IndexScan
private void calculateIndexGroupByInfo(IndexScanPlanNode root, IndexGroupByInfo gbInfo) {
String fromTableAlias = root.getTargetTableAlias();
assert (fromTableAlias != null);
Index index = root.getCatalogIndex();
if (!IndexType.isScannable(index.getType())) {
return;
}
ArrayList<AbstractExpression> bindings = new ArrayList<>();
gbInfo.m_coveredGroupByColumns = calculateGroupbyColumnsCovered(index, fromTableAlias, bindings);
gbInfo.m_canBeFullySerialized = (gbInfo.m_coveredGroupByColumns.size() == m_parsedSelect.groupByColumns().size());
}
use of org.voltdb.catalog.Index in project voltdb by VoltDB.
the class CatalogSizing method getTableSize.
private static TableSize getTableSize(Table table, boolean bActiveActiveEnabled) {
// The cardinality is the estimated tuple count or an arbitrary number
// if not estimated.
long cardinality = table.getEstimatedtuplecount();
if (cardinality <= 0) {
cardinality = 1000;
}
//Do we need to adjust for DR-AA?
boolean bAdjustForDrAA = (table.getIsdred() && bActiveActiveEnabled);
// Add up the column widths.
CatalogMap<Column> columnsMap = table.getColumns();
List<Column> columns = new ArrayList<Column>(columnsMap.size());
for (Column column : columnsMap) {
columns.add(column);
}
CatalogItemSizeBase csize = getColumnsSize(columns, false, bAdjustForDrAA);
boolean isView = table.getMaterializer() != null;
TableSize tsize = new TableSize(table, isView, csize.widthMin, csize.widthMax, cardinality);
// Add the table indexes.
CatalogMap<Index> indexes = table.getIndexes();
for (Index index : indexes) {
CatalogItemSizeBase isize = getIndexSize(index);
tsize.addIndex(index, isize.widthMin, isize.widthMax);
}
return tsize;
}
use of org.voltdb.catalog.Index in project voltdb by VoltDB.
the class ReportMaker method generateProcedureRow.
static String generateProcedureRow(CatalogMap<Table> tables, 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));
}
// get the annotation or ensure it's there
ProcedureAnnotation annotation = (ProcedureAnnotation) procedure.getAnnotation();
if (annotation == null) {
annotation = new ProcedureAnnotation();
procedure.setAnnotation(annotation);
}
// this needs to be run before the ProcedureAnnotation is used below
// because it modifies it
String statementsTable = generateStatementsTable(tables, procedure);
// output what schema this interacts with
// 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> tableList = new ArrayList<String>();
for (Table table : annotation.tablesRead) {
tableList.add("<a href='#s-" + table.getTypeName() + "'>" + table.getTypeName() + "</a>");
}
sb.append(StringUtils.join(tableList, ", "));
sb.append("</p>");
}
if (annotation.tablesUpdated.size() > 0) {
sb.append("<p>Read/Write access to tables: ");
List<String> tableList = new ArrayList<String>();
for (Table table : annotation.tablesUpdated) {
tableList.add("<a href='#s-" + table.getTypeName() + "'>" + table.getTypeName() + "</a>");
}
sb.append(StringUtils.join(tableList, ", "));
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(statementsTable);
sb.append("</td></tr>\n");
return sb.toString();
}
Aggregations