/* This file is part of VoltDB.
* Copyright (C) 2008-2014 VoltDB Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.voltdb.plannodes;
import java.util.ArrayList;
import junit.framework.TestCase;
import org.voltdb.MockVoltDB;
import org.voltdb.VoltType;
import org.voltdb.expressions.ConstantValueExpression;
import org.voltdb.expressions.OperatorExpression;
import org.voltdb.expressions.ParameterValueExpression;
import org.voltdb.expressions.TupleAddressExpression;
import org.voltdb.expressions.TupleValueExpression;
import org.voltdb.types.ExpressionType;
public class TestScanPlanNode extends TestCase
{
static final String TABLE1 = "TABLE1";
static final String[] COLS = { "COL0", "COL1", "COL2", "COL3", "COL4" };
static final VoltType[] COLTYPES = { VoltType.INTEGER, VoltType.TINYINT,
VoltType.TIMESTAMP, VoltType.FLOAT,
VoltType.BIGINT };
MockVoltDB m_voltdb;
@Override
protected void setUp()
{
m_voltdb = new MockVoltDB();
m_voltdb.addTable(TABLE1, false);
for (int i = 0; i < COLS.length; ++i)
{
m_voltdb.addColumnToTable(TABLE1, COLS[i], COLTYPES[i], false, "",
COLTYPES[i]);
}
}
@Override
protected void tearDown() throws Exception {
m_voltdb.shutdown(null);
}
// test that if no scan columns are specified, the output schema of
// a scan node is the schema of the table
public void testOutputSchemaNoScanColumns()
{
AbstractScanPlanNode dut = new SeqScanPlanNode(TABLE1, TABLE1);
dut.generateOutputSchema(m_voltdb.getDatabase());
NodeSchema dut_schema = dut.getOutputSchema();
System.out.println(dut_schema.toString());
assertEquals(COLS.length, dut_schema.size());
for (int i = 0; i < COLS.length; ++i)
{
SchemaColumn col = dut_schema.find(TABLE1, TABLE1, COLS[i], COLS[i]);
assertNotNull(col);
assertEquals(col.getExpression().getExpressionType(),
ExpressionType.VALUE_TUPLE);
assertEquals(col.getExpression().getValueType(),
COLTYPES[i]);
}
}
// test that if scan columns are specified the output schema of
// a scan node consists of those columns
public void testOutputSchemaSomeScanColumns()
{
int[] scan_col_indexes = { 1, 3 };
ArrayList<SchemaColumn> scanColumns = new ArrayList<SchemaColumn>();
for (int index : scan_col_indexes) {
TupleValueExpression tve = new TupleValueExpression(TABLE1, TABLE1, COLS[index], COLS[index]);
tve.setValueType(COLTYPES[index]);
tve.setValueSize(COLTYPES[index].getLengthInBytesForFixedTypes());
SchemaColumn col = new SchemaColumn(TABLE1, TABLE1, COLS[index], COLS[index], tve);
scanColumns.add(col);
}
AbstractScanPlanNode dut = SeqScanPlanNode.createDummyForTest(TABLE1, 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, TABLE1, COLS[index], "");
assertNotNull(col);
assertEquals(col.getExpression().getExpressionType(), ExpressionType.VALUE_TUPLE);
assertEquals(col.getExpression().getValueType(), COLTYPES[index]);
}
}
}
// 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(TABLE1, 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, 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, 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(TABLE1, TABLE1, COLS[0], COLS[0]);
left.setValueType(COLTYPES[0]);
left.setValueSize(COLTYPES[0].getLengthInBytesForFixedTypes());
TupleValueExpression right = new TupleValueExpression(TABLE1, TABLE1, COLS[2], 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, 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, TABLE1, cols[i], cols[i]);
}
assertNotNull(col);
assertEquals(col.getExpression().getExpressionType(),
ExpressionType.VALUE_TUPLE);
}
}
}