blob: ff99085b5263f1758cb87e436bde8e709a29231b [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.impala.analysis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.impala.catalog.Function;
import org.apache.impala.catalog.ScalarType;
import org.apache.impala.catalog.Type;
import org.apache.impala.common.AnalysisException;
import org.apache.impala.common.FrontendTestBase;
import org.apache.impala.thrift.TExpr;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
public class AnalyzerTest extends FrontendTestBase {
protected final static Logger LOG = LoggerFactory.getLogger(AnalyzerTest.class);
// maps from type to string that will result in literal of that type
protected static Map<ScalarType, String> typeToLiteralValue_ =
new HashMap<ScalarType, String>();
static {
typeToLiteralValue_.put(Type.BOOLEAN, "true");
typeToLiteralValue_.put(Type.TINYINT, "1");
typeToLiteralValue_.put(Type.SMALLINT, (Byte.MAX_VALUE + 1) + "");
typeToLiteralValue_.put(Type.INT, (Short.MAX_VALUE + 1) + "");
typeToLiteralValue_.put(Type.BIGINT,
((long) Integer.MAX_VALUE + 1) + "");
typeToLiteralValue_.put(Type.FLOAT, "cast(1.0 as float)");
typeToLiteralValue_.put(Type.DOUBLE,
"cast(" + (Float.MAX_VALUE + 1) + " as double)");
typeToLiteralValue_.put(Type.TIMESTAMP,
"cast('2012-12-21 00:00:00.000' as timestamp)");
typeToLiteralValue_.put(Type.STRING, "'Hello, World!'");
typeToLiteralValue_.put(Type.NULL, "NULL");
}
/**
* Check whether SelectStmt components can be converted to thrift.
*/
protected void checkSelectToThrift(SelectStmt node) {
// convert select list exprs and where clause to thrift
List<Expr> selectListExprs = node.getResultExprs();
List<TExpr> thriftExprs = Expr.treesToThrift(selectListExprs);
LOG.info("select list:\n");
for (TExpr expr: thriftExprs) {
LOG.info(expr.toString() + "\n");
}
for (Expr expr: selectListExprs) {
checkBinaryExprs(expr);
}
if (node.getWhereClause() != null) {
TExpr thriftWhere = node.getWhereClause().treeToThrift();
LOG.info("WHERE pred: " + thriftWhere.toString() + "\n");
checkBinaryExprs(node.getWhereClause());
}
AggregateInfo aggInfo = node.getAggInfo();
if (aggInfo != null) {
if (aggInfo.getGroupingExprs() != null) {
LOG.info("grouping exprs:\n");
for (Expr expr: aggInfo.getGroupingExprs()) {
LOG.info(expr.treeToThrift().toString() + "\n");
checkBinaryExprs(expr);
}
}
LOG.info("aggregate exprs:\n");
for (Expr expr: aggInfo.getAggregateExprs()) {
LOG.info(expr.treeToThrift().toString() + "\n");
checkBinaryExprs(expr);
}
if (node.getHavingPred() != null) {
TExpr thriftHaving = node.getHavingPred().treeToThrift();
LOG.info("HAVING pred: " + thriftHaving.toString() + "\n");
checkBinaryExprs(node.getHavingPred());
}
}
}
/**
* Generates and analyzes two variants of the given query by replacing all occurrences
* of "$TBL" in the query string with the unqualified and fully-qualified version of
* the given table name. The unqualified variant is analyzed using an analyzer that has
* tbl's db set as the default database.
* Example:
* query = "select id from $TBL, $TBL"
* tbl = "functional.alltypes"
* Variants generated and analyzed:
* select id from alltypes, alltypes (default db is "functional")
* select id from functional.alltypes, functional.alltypes (default db is "default")
*/
protected void TblsAnalyzeOk(String query, TableName tbl) {
Preconditions.checkState(tbl.isFullyQualified());
Preconditions.checkState(query.contains("$TBL"));
String uqQuery = query.replace("$TBL", tbl.getTbl());
AnalyzesOk(uqQuery, createAnalyzer(tbl.getDb()));
String fqQuery = query.replace("$TBL", tbl.toString());
AnalyzesOk(fqQuery);
}
/**
* Same as TblsAnalyzeOk(), except that analysis of all variants is expected
* to fail with the given error message.
*/
protected void TblsAnalysisError(String query, TableName tbl,
String expectedError) {
Preconditions.checkState(tbl.isFullyQualified());
Preconditions.checkState(query.contains("$TBL"));
String uqQuery = query.replace("$TBL", tbl.getTbl());
AnalysisError(uqQuery, createAnalyzer(tbl.getDb()), expectedError);
String fqQuery = query.replace("$TBL", tbl.toString());
AnalysisError(fqQuery, expectedError);
}
/**
* Makes sure that operands to binary exprs having same type.
*/
private void checkBinaryExprs(Expr expr) {
if (expr instanceof BinaryPredicate
|| (expr instanceof ArithmeticExpr
&& ((ArithmeticExpr) expr).getOp() != ArithmeticExpr.Operator.BITNOT)) {
Assert.assertEquals(expr.getChildren().size(), 2);
// The types must be equal or one of them is NULL_TYPE.
Assert.assertTrue(expr.getChild(0).getType() == expr.getChild(1).getType()
|| expr.getChild(0).getType().isNull() || expr.getChild(1).getType().isNull());
}
for (Expr child: expr.getChildren()) {
checkBinaryExprs(child);
}
}
@Test
public void TestCompressedText() throws AnalysisException {
AnalyzesOk("SELECT count(*) FROM functional_text_lzo.tinyinttable");
// TODO: Disabling the text/{gzip,bzip,snap} analysis test until the corresponding
// databases are loaded.
// AnalyzesOk("SELECT count(*) FROM functional_text_gzip.tinyinttable");
// AnalyzesOk("SELECT count(*) FROM functional_text_snap.tinyinttable");
// AnalyzesOk("SELECT count(*) FROM functional_text_bzip.tinyinttable");
}
@Test
public void TestMemLayout() throws AnalysisException {
testSelectStar();
testNonNullable();
testMixedNullable();
testNonMaterializedSlots();
}
private void testSelectStar() throws AnalysisException {
SelectStmt stmt = (SelectStmt) AnalyzesOk("select * from functional.AllTypes");
Analyzer analyzer = stmt.getAnalyzer();
DescriptorTable descTbl = analyzer.getDescTbl();
TupleDescriptor tupleDesc = descTbl.getTupleDesc(new TupleId(0));
tupleDesc.materializeSlots();
descTbl.computeMemLayout();
Assert.assertEquals(97.0f, tupleDesc.getAvgSerializedSize(), 0.0);
checkLayoutParams("functional.alltypes.date_string_col", 16, 0, 88, 0, analyzer);
checkLayoutParams("functional.alltypes.string_col", 16, 16, 88, 1, analyzer);
checkLayoutParams("functional.alltypes.timestamp_col", 16, 32, 88, 2, analyzer);
checkLayoutParams("functional.alltypes.bigint_col", 8, 48, 88, 3, analyzer);
checkLayoutParams("functional.alltypes.double_col", 8, 56, 88, 4, analyzer);
checkLayoutParams("functional.alltypes.id", 4, 64, 88, 5, analyzer);
checkLayoutParams("functional.alltypes.int_col", 4, 68, 88, 6, analyzer);
checkLayoutParams("functional.alltypes.float_col", 4, 72, 88, 7, analyzer);
checkLayoutParams("functional.alltypes.year", 4, 76, 89, 0, analyzer);
checkLayoutParams("functional.alltypes.month", 4, 80, 89, 1, analyzer);
checkLayoutParams("functional.alltypes.smallint_col", 2, 84, 89, 2, analyzer);
checkLayoutParams("functional.alltypes.bool_col", 1, 86, 89, 3, analyzer);
checkLayoutParams("functional.alltypes.tinyint_col", 1, 87, 89, 4, analyzer);
}
private void testNonNullable() throws AnalysisException {
// both slots are non-nullable bigints. The layout should look like:
// (byte range : data)
// 0 - 7: count(int_col)
// 8 - 15: count(*)
SelectStmt stmt = (SelectStmt) AnalyzesOk(
"select count(int_col), count(*) from functional.AllTypes");
DescriptorTable descTbl = stmt.getAnalyzer().getDescTbl();
TupleDescriptor aggDesc = descTbl.getTupleDesc(new TupleId(1));
aggDesc.materializeSlots();
descTbl.computeMemLayout();
Assert.assertEquals(16.0f, aggDesc.getAvgSerializedSize(), 0.0);
Assert.assertEquals(16, aggDesc.getByteSize());
checkLayoutParams(aggDesc.getSlots().get(0), 8, 0, 0, -1);
checkLayoutParams(aggDesc.getSlots().get(1), 8, 8, 0, -1);
}
private void testMixedNullable() throws AnalysisException {
// one slot is nullable, one is not. The layout should look like:
// (byte range : data)
// 0 - 7: sum(int_col)
// 8 - 15: count(*)
// 16 - 17: nullable-byte (only 1 bit used)
SelectStmt stmt = (SelectStmt) AnalyzesOk(
"select sum(int_col), count(*) from functional.AllTypes");
DescriptorTable descTbl = stmt.getAnalyzer().getDescTbl();
TupleDescriptor aggDesc = descTbl.getTupleDesc(new TupleId(1));
aggDesc.materializeSlots();
descTbl.computeMemLayout();
Assert.assertEquals(16.0f, aggDesc.getAvgSerializedSize(), 0.0);
Assert.assertEquals(17, aggDesc.getByteSize());
checkLayoutParams(aggDesc.getSlots().get(0), 8, 0, 16, 0);
checkLayoutParams(aggDesc.getSlots().get(1), 8, 8, 0, -1);
}
/**
* Tests that computeMemLayout() ignores non-materialized slots.
*/
private void testNonMaterializedSlots() throws AnalysisException {
SelectStmt stmt = (SelectStmt) AnalyzesOk("select * from functional.alltypes");
Analyzer analyzer = stmt.getAnalyzer();
DescriptorTable descTbl = analyzer.getDescTbl();
TupleDescriptor tupleDesc = descTbl.getTupleDesc(new TupleId(0));
tupleDesc.materializeSlots();
// Mark slots 0 (id), 7 (double_col), 9 (string_col) as non-materialized.
ArrayList<SlotDescriptor> slots = tupleDesc.getSlots();
slots.get(0).setIsMaterialized(false);
slots.get(7).setIsMaterialized(false);
slots.get(9).setIsMaterialized(false);
descTbl.computeMemLayout();
Assert.assertEquals(68.0f, tupleDesc.getAvgSerializedSize(), 0.0);
// Check non-materialized slots.
checkLayoutParams("functional.alltypes.id", 0, -1, 0, 0, analyzer);
checkLayoutParams("functional.alltypes.double_col", 0, -1, 0, 0, analyzer);
checkLayoutParams("functional.alltypes.string_col", 0, -1, 0, 0, analyzer);
// Check materialized slots.
checkLayoutParams("functional.alltypes.date_string_col", 16, 0, 60, 0, analyzer);
checkLayoutParams("functional.alltypes.timestamp_col", 16, 16, 60, 1, analyzer);
checkLayoutParams("functional.alltypes.bigint_col", 8, 32, 60, 2, analyzer);
checkLayoutParams("functional.alltypes.int_col", 4, 40, 60, 3, analyzer);
checkLayoutParams("functional.alltypes.float_col", 4, 44, 60, 4, analyzer);
checkLayoutParams("functional.alltypes.year", 4, 48, 60, 5, analyzer);
checkLayoutParams("functional.alltypes.month", 4, 52, 60, 6, analyzer);
checkLayoutParams("functional.alltypes.smallint_col", 2, 56, 60, 7, analyzer);
checkLayoutParams("functional.alltypes.bool_col", 1, 58, 61, 0, analyzer);
checkLayoutParams("functional.alltypes.tinyint_col", 1, 59, 61, 1, analyzer);
}
private void checkLayoutParams(SlotDescriptor d, int byteSize, int byteOffset,
int nullIndicatorByte, int nullIndicatorBit) {
Assert.assertEquals(byteSize, d.getByteSize());
Assert.assertEquals(byteOffset, d.getByteOffset());
Assert.assertEquals(nullIndicatorByte, d.getNullIndicatorByte());
Assert.assertEquals(nullIndicatorBit, d.getNullIndicatorBit());
}
private void checkLayoutParams(String colAlias, int byteSize, int byteOffset,
int nullIndicatorByte, int nullIndicatorBit, Analyzer analyzer) {
SlotDescriptor d = analyzer.getSlotDescriptor(colAlias);
checkLayoutParams(d, byteSize, byteOffset, nullIndicatorByte, nullIndicatorBit);
}
// Analyzes query and asserts that the first result expr returns the given type.
// Requires query to parse to a SelectStmt.
protected void checkExprType(String query, Type type) {
SelectStmt select = (SelectStmt) AnalyzesOk(query);
Assert.assertEquals(select.getResultExprs().get(0).getType(), type);
}
/**
* We distinguish between three classes of unsupported types:
* 1. Complex types, e.g., map
* For tables with such types we prevent loading the table metadata.
* 2. Primitive types
* For tables with unsupported primitive types (e.g., binary)
* we can run queries as long as the unsupported columns are not referenced.
* We fail analysis if a query references an unsupported primitive column.
* 3. Partition-column types
* We do not support table partitioning on timestamp columns
*/
@Test
public void TestUnsupportedTypes() {
// Select supported types from a table with mixed supported/unsupported types.
AnalyzesOk("select int_col, str_col, bigint_col from functional.unsupported_types");
// Select supported types from a table with mixed supported/unsupported types.
AnalyzesOk("select int_col, str_col, bigint_col from functional.unsupported_types");
// Unsupported type binary.
AnalysisError("select bin_col from functional.unsupported_types",
"Unsupported type 'BINARY' in 'bin_col'.");
// Unsupported type date in a star expansion.
AnalysisError("select * from functional.unsupported_types",
"Unsupported type 'DATE' in 'functional.unsupported_types.date_col'.");
// Mixed supported/unsupported types.
AnalysisError("select int_col, str_col, bin_col " +
"from functional.unsupported_types",
"Unsupported type 'BINARY' in 'bin_col'.");
AnalysisError("create table tmp as select * from functional.unsupported_types",
"Unsupported type 'DATE' in 'functional.unsupported_types.date_col'.");
// Unsupported type in the target insert table.
AnalysisError("insert into functional.unsupported_types " +
"values(null, null, null, null, null, null)",
"Unable to INSERT into target table (functional.unsupported_types) because " +
"the column 'date_col' has an unsupported type 'DATE'");
// Unsupported partition-column type.
AnalysisError("select * from functional.unsupported_partition_types",
"Failed to load metadata for table: 'functional.unsupported_partition_types'");
// Try with hbase
AnalyzesOk("describe functional_hbase.allcomplextypes");
for (ScalarType t: Type.getUnsupportedTypes()) {
// Create/Alter table.
AnalysisError(String.format("create table new_table (new_col %s)", t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
AnalysisError(String.format(
"create table new_table (new_col int) PARTITIONED BY (p_col %s)", t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
AnalysisError(String.format(
"alter table functional.alltypes add columns (new_col %s)", t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
AnalysisError(String.format(
"alter table functional.alltypes change column int_col new_col %s", t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
// UDFs.
final String udfSuffix = " LOCATION '/test-warehouse/libTestUdfs.so' " +
"SYMBOL='_Z8IdentityPN10impala_udf15FunctionContextERKNS_10BooleanValE'";
AnalysisError(String.format(
"create function foo(VARCHAR(5)) RETURNS %s" + udfSuffix, t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
AnalysisError(String.format(
"create function foo(%s) RETURNS int" + udfSuffix, t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
// UDAs.
final String udaSuffix = " LOCATION '/test-warehouse/libTestUdas.so' " +
"UPDATE_FN='AggUpdate'";
AnalysisError(String.format("create aggregate function foo(string, double) " +
"RETURNS %s" + udaSuffix, t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
AnalysisError(String.format("create aggregate function foo(%s, double) " +
"RETURNS int" + udaSuffix, t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
// Cast,
AnalysisError(String.format("select cast('abc' as %s)", t.toSql()),
String.format("Unsupported data type: %s", t.toSql()));
}
}
@Test
public void TestBinaryHBaseTable() {
AnalyzesOk("select * from functional_hbase.alltypessmallbinary");
}
@Test
public void TestUnsupportedSerde() {
AnalysisError("select * from functional.bad_serde",
"Failed to load metadata for table: 'functional.bad_serde'");
}
@Test
public void TestResetMetadata() {
AnalyzesOk("invalidate metadata");
AnalyzesOk("invalidate metadata functional.alltypessmall");
AnalyzesOk("invalidate metadata functional.alltypes_view");
AnalyzesOk("invalidate metadata functional.bad_serde");
AnalyzesOk("refresh functional.alltypessmall");
AnalyzesOk("refresh functional.alltypes_view");
AnalyzesOk("refresh functional.bad_serde");
AnalyzesOk("refresh functional.alltypessmall partition (year=2009, month=1)");
AnalyzesOk("refresh functional.alltypessmall partition (year=2009, month=NULL)");
// invalidate metadata <table name> checks the Hive Metastore for table existence
// and should not throw an AnalysisError if the table or db does not exist.
AnalyzesOk("invalidate metadata functional.unknown_table");
AnalyzesOk("invalidate metadata unknown_db.unknown_table");
AnalysisError("refresh functional.unknown_table",
"Table does not exist: functional.unknown_table");
AnalysisError("refresh unknown_db.unknown_table",
"Database does not exist: unknown_db");
AnalysisError("refresh functional.alltypessmall partition (year=2009, int_col=10)",
"Column 'int_col' is not a partition column in table: functional.alltypessmall");
AnalysisError("refresh functional.alltypessmall partition (year=2009)",
"Items in partition spec must exactly match the partition columns in "
+ "the table definition: functional.alltypessmall (1 vs 2)");
AnalysisError("refresh functional.alltypessmall partition (year=2009, year=2009)",
"Duplicate partition key name: year");
AnalysisError(
"refresh functional.alltypessmall partition (year=2009, month='foo')",
"Value of partition spec (column=month) has incompatible type: 'STRING'. "
+ "Expected type: 'INT'");
AnalysisError("refresh functional.zipcode_incomes partition (year=2009, month=1)",
"Table is not partitioned: functional.zipcode_incomes");
}
@Test
public void TestExplain() {
// Analysis error from explain insert: too many partitioning columns.
AnalysisError("explain insert into table functional.alltypessmall " +
"partition (year=2009, month=4, year=10)" +
"select id, bool_col, tinyint_col, smallint_col, int_col, bigint_col, " +
"float_col, double_col, date_string_col, string_col, timestamp_col " +
"from functional.alltypes",
"Duplicate column 'year' in partition clause");
// Analysis error from explain query
AnalysisError("explain " +
"select id from (select id+2 from functional_hbase.alltypessmall) a",
"Could not resolve column/field reference: 'id'");
// Analysis error from explain upsert
AnalysisError("explain upsert into table functional.alltypes select * from " +
"functional.alltypes", "UPSERT is only supported for Kudu tables");
// Positive test for explain query
AnalyzesOk("explain select * from functional.AllTypes");
// Positive test for explain insert
AnalyzesOk("explain insert into table functional.alltypessmall " +
"partition (year=2009, month=4)" +
"select id, bool_col, tinyint_col, smallint_col, int_col, int_col, " +
"float_col, float_col, date_string_col, string_col, timestamp_col " +
"from functional.alltypes");
// Positive test for explain upsert
AnalyzesOk("explain upsert into table functional_kudu.testtbl select * from " +
"functional_kudu.testtbl");
}
@Test
public void TestLimitAndOffset() {
// Arithmetic expressions that result in a positive, integral value are OK
AnalyzesOk("select * from functional.AllTypes limit 10 * 10 + 10 - 10 % 10");
AnalyzesOk("select * from functional.AllTypes limit 1 ^ 0 | 3 & 3");
// Test offset, requires order by and limit
AnalyzesOk("select * from functional.AllTypes order by id limit 10 offset 1+2*3%4");
// Test offset within an inline view and with-clause view
AnalyzesOk("select t5.id from (select id from functional.AllTypes order by id " +
"limit 10 offset 2) t5");
AnalyzesOk("with t5 as (select id from functional.AllTypes order by id limit 10 " +
"offset 2) select * from t5");
// Casting to int is fine
AnalyzesOk("select id, bool_col from functional.AllTypes limit CAST(10.0 AS INT)");
AnalyzesOk("select id, bool_col from functional.AllTypes limit " +
"CAST(NOT FALSE AS INT)");
AnalyzesOk("select * from functional.AllTypes order by id limit 10 " +
"offset CAST(1.0 AS INT)");
// Analysis error from negative values
AnalysisError("select * from functional.AllTypes limit 10 - 20",
"LIMIT must be a non-negative integer: 10 - 20 = -10");
AnalysisError("select * from functional.AllTypes order by id limit 10 " +
"offset 10 - 20",
"OFFSET must be a non-negative integer: 10 - 20 = -10");
// Analysis error from non-integral values
AnalysisError("select * from functional.AllTypes limit 10.0",
"LIMIT expression must be an integer type but is 'DECIMAL(3,1)': 10.0");
AnalysisError("select * from functional.AllTypes limit NOT FALSE",
"LIMIT expression must be an integer type but is 'BOOLEAN': NOT FALSE");
AnalysisError("select * from functional.AllTypes limit CAST(\"asdf\" AS INT)",
"LIMIT expression evaluates to NULL: CAST('asdf' AS INT)");
AnalysisError("select * from functional.AllTypes order by id limit 10 " +
"OFFSET 10.0",
"OFFSET expression must be an integer type but is 'DECIMAL(3,1)': 10.0");
AnalysisError("select * from functional.AllTypes order by id limit 10 " +
"offset CAST('asdf' AS INT)",
"OFFSET expression evaluates to NULL: CAST('asdf' AS INT)");
// Analysis error from non-constant expressions
AnalysisError("select id, bool_col from functional.AllTypes limit id < 10",
"LIMIT expression must be a constant expression: id < 10");
AnalysisError("select id, bool_col from functional.AllTypes order by id limit 10 " +
"offset id < 10",
"OFFSET expression must be a constant expression: id < 10");
// Offset is only valid with an order by
AnalysisError("SELECT a FROM test LIMIT 10 OFFSET 5",
"OFFSET requires an ORDER BY clause: LIMIT 10 OFFSET 5");
AnalysisError("SELECT x.id FROM (SELECT id FROM alltypesagg LIMIT 5 OFFSET 5) x " +
"ORDER BY x.id LIMIT 100 OFFSET 4",
"OFFSET requires an ORDER BY clause: LIMIT 5 OFFSET 5");
AnalysisError("SELECT a FROM test OFFSET 5",
"OFFSET requires an ORDER BY clause: OFFSET 5");
AnalyzesOk("SELECT id FROM functional.Alltypes ORDER BY bool_col OFFSET 5");
}
@Test
public void TestAnalyzeShowCreateTable() {
AnalyzesOk("show create table functional.AllTypes");
AnalyzesOk("show create table functional.alltypes_view");
AnalysisError("show create table functional.not_a_table",
"Table does not exist: functional.not_a_table");
AnalysisError("show create table doesnt_exist",
"Table does not exist: default.doesnt_exist");
}
private Function createFunction(boolean hasVarArgs, Type... args) {
return new Function(new FunctionName("test"), args, Type.INVALID, hasVarArgs);
}
@Test
// Test matching function signatures.
public void TestFunctionMatching() {
Function[] fns = new Function[14];
// test()
fns[0] = createFunction(false);
// test(int)
fns[1] = createFunction(false, Type.INT);
// test(int...)
fns[2] = createFunction(true, Type.INT);
// test(tinyint)
fns[3] = createFunction(false, Type.TINYINT);
// test(tinyint...)
fns[4] = createFunction(true, Type.TINYINT);
// test(double)
fns[5] = createFunction(false, Type.DOUBLE);
// test(double...)
fns[6] = createFunction(true, Type.DOUBLE);
// test(double, double)
fns[7] = createFunction(false, Type.DOUBLE, Type.DOUBLE);
// test(double, double...)
fns[8] = createFunction(true, Type.DOUBLE, Type.DOUBLE);
// test(smallint, tinyint)
fns[9] = createFunction(false, Type.SMALLINT, Type.TINYINT);
// test(int, double, double, double)
fns[10] = createFunction(false, Type.INT, Type.DOUBLE, Type.DOUBLE, Type.DOUBLE);
// test(int, string, int...)
fns[11] = createFunction(true, Type.INT, Type.STRING, Type.INT);
// test(tinying, string, tinyint, int, tinyint)
fns[12] = createFunction(false, Type.TINYINT, Type.STRING, Type.TINYINT, Type.INT,
Type.TINYINT);
// test(tinying, string, bigint, int, tinyint)
fns[13] = createFunction(false, Type.TINYINT, Type.STRING, Type.BIGINT, Type.INT,
Type.TINYINT);
Assert.assertFalse(fns[1].compare(fns[0], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[1].compare(fns[2], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[1].compare(fns[3], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[1].compare(fns[4], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertFalse(fns[1].compare(fns[5], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertFalse(fns[1].compare(fns[6], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertFalse(fns[1].compare(fns[7], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertFalse(fns[1].compare(fns[8], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[1].compare(fns[2], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertTrue(fns[3].compare(fns[4], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertTrue(fns[5].compare(fns[6], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertFalse(fns[5].compare(fns[7], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertFalse(fns[5].compare(fns[8], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertTrue(fns[6].compare(fns[7], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertTrue(fns[6].compare(fns[8], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertTrue(fns[7].compare(fns[8], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertFalse(fns[1].compare(fns[3], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertFalse(fns[1].compare(fns[4], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertFalse(fns[9].compare(fns[4], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[2].compare(fns[9], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[8].compare(fns[10], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertFalse(fns[10].compare(fns[8], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertTrue(fns[11].compare(fns[12], Function.CompareMode.IS_SUPERTYPE_OF));
Assert.assertFalse(fns[11].compare(fns[13], Function.CompareMode.IS_SUPERTYPE_OF));
for (int i = 0; i < fns.length; ++i) {
for (int j = 0; j < fns.length; ++j) {
if (i == j) {
Assert.assertTrue(
fns[i].compare(fns[i], Function.CompareMode.IS_IDENTICAL));
Assert.assertTrue(
fns[i].compare(fns[i], Function.CompareMode.IS_INDISTINGUISHABLE));
Assert.assertTrue(
fns[i].compare(fns[i], Function.CompareMode.IS_SUPERTYPE_OF));
} else {
Assert.assertFalse(fns[i].compare(fns[j], Function.CompareMode.IS_IDENTICAL));
if (fns[i].compare(fns[j], Function.CompareMode.IS_INDISTINGUISHABLE)) {
// If it's a indistinguishable, at least one of them must be a super type
// of the other
Assert.assertTrue(
fns[i].compare(fns[j], Function.CompareMode.IS_SUPERTYPE_OF) ||
fns[j].compare(fns[i], Function.CompareMode.IS_SUPERTYPE_OF));
} else if (fns[i].compare(fns[j], Function.CompareMode.IS_INDISTINGUISHABLE)) {
// This is reflexive
Assert.assertTrue(
fns[j].compare(fns[i], Function.CompareMode.IS_INDISTINGUISHABLE));
}
}
}
}
}
}