/** * @param expr * @param tables */ void getTablesForExpression(AbstractExpression expr, HashSet<Table> tables) { List<TupleValueExpression> tves = ExpressionUtil.getTupleValueExpressions(expr); for (TupleValueExpression tupleExpr : tves) { String tableName = tupleExpr.getTableName(); Table table = getTableFromDB(tableName); tables.add(table); } }
/** * @param exprNode * @return */ private static AbstractExpression parseSimpleColumnExpression(VoltXMLElement exprNode) { // This object is a place holder that gets filled in later based on the display column it // aliases. String alias = exprNode.attributes.get("alias"); TupleValueExpression expr = new TupleValueExpression(); expr.setColumnAlias(alias); return expr; }
// Even though this function applies generally to expressions and tables // and not just to TVEs as such, it is somewhat TVE-related because TVEs // represent the points where expression trees depend on tables. public static boolean isOperandDependentOnTable(AbstractExpression expr, String tableAlias) { assert (tableAlias != null); for (TupleValueExpression tve : ExpressionUtil.getTupleValueExpressions(expr)) { if (tableAlias.equals(tve.getTableAlias())) { return true; } } return false; }
@Override public Object clone() throws CloneNotSupportedException { TupleValueExpression clone = (TupleValueExpression) super.clone(); clone.m_columnIndex = m_columnIndex; clone.m_tableName = m_tableName; clone.m_columnName = m_columnName; clone.m_columnAlias = m_columnAlias; return clone; }
@Override public Object clone() { TupleValueExpression clone = (TupleValueExpression) super.clone(); clone.m_columnIndex = m_columnIndex; clone.m_tableName = m_tableName; clone.m_columnName = m_columnName; clone.m_columnAlias = m_columnAlias; return clone; }
// 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; }
/** * @param exprNode * @return */ private static AbstractExpression parseColumnRefExpression(VoltXMLElement exprNode) { TupleValueExpression expr = new TupleValueExpression(); String alias = exprNode.attributes.get("alias"); String tableName = exprNode.attributes.get("table"); String columnName = exprNode.attributes.get("column"); expr.setColumnAlias(alias); expr.setColumnName(columnName); expr.setTableName(tableName); return expr; }
// test that if scan columns are specified the output schema of // a scan node consists of those columns public void testOutputSchemaSomeScanColumns() { AbstractScanPlanNode dut = new SeqScanPlanNode(); dut.setTargetTableName(TABLE1); int[] scan_col_indexes = {1, 3}; ArrayList<SchemaColumn> scanColumns = new ArrayList<SchemaColumn>(); for (int index : scan_col_indexes) { TupleValueExpression tve = new TupleValueExpression(); tve.setTableName(TABLE1); tve.setColumnName(COLS[index]); tve.setColumnAlias(COLS[index]); tve.setValueType(COLTYPES[index]); tve.setValueSize(COLTYPES[index].getLengthInBytesForFixedTypes()); SchemaColumn col = new SchemaColumn(TABLE1, COLS[index], COLS[index], tve); scanColumns.add(col); } dut.setScanColumns(scanColumns); // Should be able to do this safely and repeatably multiple times for (int i = 0; i < 3; i++) { dut.generateOutputSchema(m_voltdb.getDatabase()); NodeSchema dut_schema = dut.getOutputSchema(); System.out.println(dut_schema.toString()); assertEquals(scan_col_indexes.length, dut_schema.size()); for (int index : scan_col_indexes) { SchemaColumn col = dut_schema.find(TABLE1, COLS[index], ""); assertNotNull(col); assertEquals(col.getExpression().getExpressionType(), ExpressionType.VALUE_TUPLE); assertEquals(col.getExpression().getValueType(), COLTYPES[index]); } } }
/** * Parse the scan_columns element out of the HSQL-generated XML. Fills scanColumns with a list of * the columns used in the plan, hashed by table name. * * @param columnsNode */ void parseScanColumns(VoltXMLElement columnsNode) { scanColumns = new HashMap<String, ArrayList<SchemaColumn>>(); for (VoltXMLElement child : columnsNode.children) { assert (child.name.equals("columnref")); AbstractExpression col_exp = parseExpressionTree(child); // TupleValueExpressions are always specifically typed, // so there is no need for expression type specialization, here. assert (col_exp != null); assert (col_exp instanceof TupleValueExpression); TupleValueExpression tve = (TupleValueExpression) col_exp; SchemaColumn col = new SchemaColumn(tve.getTableName(), tve.getColumnName(), tve.getColumnAlias(), col_exp); ArrayList<SchemaColumn> table_cols = null; if (!scanColumns.containsKey(col.getTableName())) { table_cols = new ArrayList<SchemaColumn>(); scanColumns.put(col.getTableName(), table_cols); } table_cols = scanColumns.get(col.getTableName()); table_cols.add(col); } }
@Override public void processTVE(TupleValueExpression expr, String columnName) { expr.resolveForTable(m_table); }
void parseOrderColumn(VoltXMLElement orderByNode) { // make sure everything is kosher assert (orderByNode.name.equalsIgnoreCase("operation")); String operationType = orderByNode.attributes.get("type"); assert (operationType != null); assert (operationType.equalsIgnoreCase("orderby")); // get desc/asc String desc = orderByNode.attributes.get("desc"); boolean descending = (desc != null) && (desc.equalsIgnoreCase("true")); // get the columnref expression inside the orderby node VoltXMLElement child = orderByNode.children.get(0); assert (child != null); // create the orderby column ParsedColInfo order_col = new ParsedColInfo(); order_col.orderBy = true; order_col.ascending = !descending; AbstractExpression order_exp = parseExpressionTree(child); // Cases: // inner child could be columnref, in which case it's just a normal // column. Just make a ParsedColInfo object for it and the planner // will do the right thing later if (child.name.equals("columnref")) { order_col.columnName = child.attributes.get("column"); order_col.tableName = child.attributes.get("table"); String alias = child.attributes.get("alias"); order_col.alias = alias; // The ORDER BY column MAY be identical to a simple display column, in which case, // tagging the actual display column as being also an order by column // helps later when trying to determine ORDER BY coverage (for determinism). if (order_exp instanceof TupleValueExpression) { ParsedColInfo orig_col = null; for (ParsedColInfo col : displayColumns) { if (col.expression.equals(order_exp)) { orig_col = col; break; } } if (orig_col != null) { orig_col.orderBy = true; orig_col.ascending = order_col.ascending; } } } else if (child.name.equals("operation")) { order_col.columnName = ""; // I'm not sure anyone actually cares about this table name order_col.tableName = "VOLT_TEMP_TABLE"; // If it's a simplecolumn operation. This means that the alias that // we have should refer to a column that we compute // somewhere else (like an aggregate, mostly). // Look up that column in the displayColumns list, // and then create a new order by column with a magic TVE. // This case seems to be the result of cross-referencing a display column // by its position, as in "ORDER BY 2, 3". Otherwise the ORDER BY column // is a columnref as handled in the prior code block. if (order_exp instanceof TupleValueExpression) { String alias = child.attributes.get("alias"); order_col.alias = alias; ParsedColInfo orig_col = null; for (ParsedColInfo col : displayColumns) { if (col.alias.equals(alias)) { orig_col = col; break; } } // We need the original column expression so we can extract // the value size and type for our TVE that refers back to it. // XXX: This check runs into problems for some cases where a display column expression gets // re-used in the ORDER BY. // I THINK one problem case was "select x, max(y) from t group by x order by max(y);" --paul if (orig_col == null) { throw new PlanningErrorException( "Unable to find source " + "column for simplecolumn: " + alias); } // Tagging the actual display column as being also an order by column // helps later when trying to determine ORDER BY coverage (for determinism). orig_col.orderBy = true; orig_col.ascending = order_col.ascending; assert (orig_col.tableName.equals("VOLT_TEMP_TABLE")); // Construct our fake TVE that will point back at the input // column. TupleValueExpression tve = (TupleValueExpression) order_exp; tve.setColumnAlias(alias); tve.setColumnName(""); tve.setColumnIndex(-1); tve.setTableName("VOLT_TEMP_TABLE"); tve.setValueSize(orig_col.expression.getValueSize()); tve.setValueType(orig_col.expression.getValueType()); if (orig_col.expression.hasAnySubexpressionOfClass(AggregateExpression.class)) { tve.setHasAggregate(true); } } } else if (child.name.equals("function") == false) { throw new RuntimeException("ORDER BY parsed with strange child node type: " + child.name); } if (order_exp instanceof ConstantValueExpression) { assert (order_exp.getValueType() != VoltType.NUMERIC); } ExpressionUtil.finalizeValueTypes(order_exp); order_col.expression = order_exp; orderColumns.add(order_col); }
// test that if someone provides their own inline projection // that the output schema of the scan node consists of the output // schema of the projection. Updates will do this so that the // inlined projection fills the values of the output tuples correctly // before it attempts to update them public void testOutputSchemaOverriddenProjection() { AbstractScanPlanNode dut = new SeqScanPlanNode(); dut.setTargetTableName(TABLE1); // Create an output schema like we might see for an inlined projection // generated for update. We'll have 4 output columns, the first will // be the tuple address, the second one a parameter expression, next // will be a constant, and the other will be a more complex expression // that uses some TVEs. NodeSchema proj_schema = new NodeSchema(); String[] cols = new String[4]; TupleAddressExpression col1_exp = new TupleAddressExpression(); proj_schema.addColumn(new SchemaColumn("", "tuple_address", "tuple_address", col1_exp)); cols[0] = "tuple_address"; // update column 1 with a parameter value ParameterValueExpression col2_exp = new ParameterValueExpression(); col2_exp.setParameterIndex(0); col2_exp.setValueType(COLTYPES[1]); col2_exp.setValueSize(COLTYPES[1].getLengthInBytesForFixedTypes()); // XXX I'm not sure what to do with the name for the updated column yet. // I think it should be an alias and not the original table name/col name proj_schema.addColumn(new SchemaColumn(TABLE1, COLS[1], COLS[1], col2_exp)); cols[1] = COLS[1]; // Update column 3 with a constant value ConstantValueExpression col3_exp = new ConstantValueExpression(); col3_exp.setValueType(COLTYPES[3]); col3_exp.setValueSize(COLTYPES[3].getLengthInBytesForFixedTypes()); col3_exp.setValue("3.14159"); proj_schema.addColumn(new SchemaColumn(TABLE1, COLS[3], COLS[3], col3_exp)); cols[2] = COLS[3]; // update column 4 with a sum of columns 0 and 2 OperatorExpression col4_exp = new OperatorExpression(); col4_exp.setValueType(COLTYPES[4]); col4_exp.setValueSize(COLTYPES[4].getLengthInBytesForFixedTypes()); col4_exp.setExpressionType(ExpressionType.OPERATOR_PLUS); TupleValueExpression left = new TupleValueExpression(); left.setTableName(TABLE1); left.setColumnName(COLS[0]); left.setColumnAlias(COLS[0]); left.setValueType(COLTYPES[0]); left.setValueSize(COLTYPES[0].getLengthInBytesForFixedTypes()); TupleValueExpression right = new TupleValueExpression(); right.setTableName(TABLE1); right.setColumnName(COLS[2]); right.setColumnAlias(COLS[2]); right.setValueType(COLTYPES[2]); right.setValueSize(COLTYPES[2].getLengthInBytesForFixedTypes()); col4_exp.setLeft(left); col4_exp.setRight(right); proj_schema.addColumn(new SchemaColumn(TABLE1, COLS[4], "C1", col4_exp)); cols[3] = COLS[4]; ProjectionPlanNode proj_node = new ProjectionPlanNode(); proj_node.setOutputSchema(proj_schema); dut.addInlinePlanNode(proj_node); System.out.println("ProjSchema: " + proj_schema.toString()); dut.generateOutputSchema(m_voltdb.getDatabase()); NodeSchema dut_schema = dut.getOutputSchema(); System.out.println(dut_schema.toString()); for (int i = 0; i < cols.length; i++) { SchemaColumn col = null; if (i == 0) { col = dut_schema.find("", cols[i], cols[i]); } else { col = dut_schema.find(TABLE1, cols[i], cols[i]); } assertNotNull(col); assertEquals(col.getExpression().getExpressionType(), ExpressionType.VALUE_TUPLE); } }