SQL: Improve validation of unsupported fields (#35675)

Fix bug in Analyzer that caused it to report unsupported fields only
 when declared in projections. The rule has been extended to all field
 declarations.

Fix #35673
This commit is contained in:
Costin Leau 2018-11-18 23:35:18 +02:00 committed by GitHub
parent f8e333b117
commit 4119409b6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 146 additions and 99 deletions

View File

@ -153,8 +153,11 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
//
// Shared methods around the analyzer rules
//
private static Attribute resolveAgainstList(UnresolvedAttribute u, Collection<Attribute> attrList) {
return resolveAgainstList(u, attrList, false);
}
private static Attribute resolveAgainstList(UnresolvedAttribute u, Collection<Attribute> attrList, boolean allowCompound) {
List<Attribute> matches = new ArrayList<>();
// first take into account the qualified version
@ -181,7 +184,7 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
}
if (matches.size() == 1) {
return matches.get(0);
return handleSpecialFields(u, matches.get(0), allowCompound);
}
return u.withUnresolvedMessage("Reference [" + u.qualifiedName()
@ -193,6 +196,25 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
);
}
private static Attribute handleSpecialFields(UnresolvedAttribute u, Attribute named, boolean allowCompound) {
// if it's a object/compound type, keep it unresolved with a nice error message
if (named instanceof FieldAttribute) {
FieldAttribute fa = (FieldAttribute) named;
// unsupported types
if (DataTypes.isUnsupported(fa.dataType())) {
UnsupportedEsField unsupportedField = (UnsupportedEsField) fa.field();
named = u.withUnresolvedMessage(
"Cannot use field [" + fa.name() + "] type [" + unsupportedField.getOriginalType() + "] as is unsupported");
}
// compound fields
else if (allowCompound == false && fa.dataType().isPrimitive() == false) {
named = u.withUnresolvedMessage(
"Cannot use field [" + fa.name() + "] type [" + fa.dataType().esType + "] only its subfields");
}
}
return named;
}
private static boolean hasStar(List<? extends Expression> exprs) {
for (Expression expression : exprs) {
if (expression instanceof UnresolvedStar) {
@ -348,21 +370,6 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
NamedExpression named = resolveAgainstList(u, childrenOutput);
// if resolved, return it; otherwise keep it in place to be resolved later
if (named != null) {
// if it's a object/compound type, keep it unresolved with a nice error message
if (named instanceof FieldAttribute) {
FieldAttribute fa = (FieldAttribute) named;
if (DataTypes.isUnsupported(fa.dataType())) {
UnsupportedEsField unsupportedField = (UnsupportedEsField) fa.field();
named = u.withUnresolvedMessage(
"Cannot use field [" + fa.name() + "] type [" + unsupportedField.getOriginalType() +
"] as is unsupported");
}
else if (!fa.dataType().isPrimitive()) {
named = u.withUnresolvedMessage(
"Cannot use field [" + fa.name() + "] type [" + fa.dataType().esType + "] only its subfields");
}
}
if (log.isTraceEnabled()) {
log.trace("Resolved {} to {}", u, named);
}
@ -407,15 +414,19 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
if (us.qualifier() != null) {
// resolve the so-called qualifier first
// since this is an unresolved start we don't know whether it's a path or an actual qualifier
Attribute q = resolveAgainstList(us.qualifier(), output);
Attribute q = resolveAgainstList(us.qualifier(), output, true);
// the wildcard couldn't be expanded because the field doesn't exist at all
// so, add to the list of expanded attributes its qualifier (the field without the wildcard)
// the qualifier will be unresolved and later used in the error message presented to the user
if (q == null) {
expanded.add(us.qualifier());
return expanded;
return singletonList(us.qualifier());
}
// qualifier is unknown (e.g. unsupported type), bail out early
else if (q.resolved() == false) {
return singletonList(q);
}
// now use the resolved 'qualifier' to match
for (Attribute attr : output) {
// filter the attributes that match based on their path

View File

@ -21,13 +21,13 @@ import java.util.TimeZone;
public class VerifierErrorMessagesTests extends ESTestCase {
private SqlParser parser = new SqlParser();
private String verify(String sql) {
private String error(String sql) {
Map<String, EsField> mapping = TypesTests.loadMapping("mapping-multi-field-with-nested.json");
EsIndex test = new EsIndex("test", mapping);
return verify(IndexResolution.valid(test), sql);
return error(IndexResolution.valid(test), sql);
}
private String verify(IndexResolution getIndexResult, String sql) {
private String error(IndexResolution getIndexResult, String sql) {
Analyzer analyzer = new Analyzer(new FunctionRegistry(), getIndexResult, TimeZone.getTimeZone("UTC"));
AnalysisException e = expectThrows(AnalysisException.class, () -> analyzer.analyze(parser.createStatement(sql), true));
assertTrue(e.getMessage().startsWith("Found "));
@ -35,299 +35,335 @@ public class VerifierErrorMessagesTests extends ESTestCase {
return e.getMessage().substring(header.length());
}
private LogicalPlan accepted(String sql) {
private LogicalPlan accept(String sql) {
Map<String, EsField> mapping = TypesTests.loadMapping("mapping-multi-field-with-nested.json");
EsIndex test = new EsIndex("test", mapping);
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), TimeZone.getTimeZone("UTC"));
return accept(IndexResolution.valid(test), sql);
}
private LogicalPlan accept(IndexResolution resolution, String sql) {
Analyzer analyzer = new Analyzer(new FunctionRegistry(), resolution, TimeZone.getTimeZone("UTC"));
return analyzer.analyze(parser.createStatement(sql), true);
}
public void testMissingIndex() {
assertEquals("1:17: Unknown index [missing]", verify(IndexResolution.notFound("missing"), "SELECT foo FROM missing"));
assertEquals("1:17: Unknown index [missing]", error(IndexResolution.notFound("missing"), "SELECT foo FROM missing"));
}
public void testMissingColumn() {
assertEquals("1:8: Unknown column [xxx]", verify("SELECT xxx FROM test"));
assertEquals("1:8: Unknown column [xxx]", error("SELECT xxx FROM test"));
}
public void testMissingColumnFilter() {
assertEquals("1:26: Unknown column [xxx]", error("SELECT * FROM test WHERE xxx > 1"));
}
public void testMissingColumnWithWildcard() {
assertEquals("1:8: Unknown column [xxx]", verify("SELECT xxx.* FROM test"));
assertEquals("1:8: Unknown column [xxx]", error("SELECT xxx.* FROM test"));
}
public void testMisspelledColumnWithWildcard() {
assertEquals("1:8: Unknown column [tex], did you mean [text]?", verify("SELECT tex.* FROM test"));
assertEquals("1:8: Unknown column [tex], did you mean [text]?", error("SELECT tex.* FROM test"));
}
public void testColumnWithNoSubFields() {
assertEquals("1:8: Cannot determine columns for [text.*]", verify("SELECT text.* FROM test"));
assertEquals("1:8: Cannot determine columns for [text.*]", error("SELECT text.* FROM test"));
}
public void testMultipleColumnsWithWildcard1() {
assertEquals("1:14: Unknown column [a]\n" +
"line 1:17: Unknown column [b]\n" +
"line 1:22: Unknown column [c]\n" +
"line 1:25: Unknown column [tex], did you mean [text]?", verify("SELECT bool, a, b.*, c, tex.* FROM test"));
assertEquals("1:14: Unknown column [a]\n" +
"line 1:17: Unknown column [b]\n" +
"line 1:22: Unknown column [c]\n" +
"line 1:25: Unknown column [tex], did you mean [text]?", error("SELECT bool, a, b.*, c, tex.* FROM test"));
}
public void testMultipleColumnsWithWildcard2() {
assertEquals("1:8: Unknown column [tex], did you mean [text]?\n" +
"line 1:21: Unknown column [a]\n" +
"line 1:24: Unknown column [dat], did you mean [date]?\n" +
"line 1:31: Unknown column [c]", verify("SELECT tex.*, bool, a, dat.*, c FROM test"));
assertEquals("1:8: Unknown column [tex], did you mean [text]?\n" +
"line 1:21: Unknown column [a]\n" +
"line 1:24: Unknown column [dat], did you mean [date]?\n" +
"line 1:31: Unknown column [c]", error("SELECT tex.*, bool, a, dat.*, c FROM test"));
}
public void testMultipleColumnsWithWildcard3() {
assertEquals("1:8: Unknown column [ate], did you mean [date]?\n" +
"line 1:21: Unknown column [keyw], did you mean [keyword]?\n" +
"line 1:29: Unknown column [da], did you mean [date]?" , verify("SELECT ate.*, bool, keyw.*, da FROM test"));
assertEquals("1:8: Unknown column [ate], did you mean [date]?\n" +
"line 1:21: Unknown column [keyw], did you mean [keyword]?\n" +
"line 1:29: Unknown column [da], did you mean [date]?" , error("SELECT ate.*, bool, keyw.*, da FROM test"));
}
public void testMisspelledColumn() {
assertEquals("1:8: Unknown column [txt], did you mean [text]?", verify("SELECT txt FROM test"));
assertEquals("1:8: Unknown column [txt], did you mean [text]?", error("SELECT txt FROM test"));
}
public void testFunctionOverMissingField() {
assertEquals("1:12: Unknown column [xxx]", verify("SELECT ABS(xxx) FROM test"));
assertEquals("1:12: Unknown column [xxx]", error("SELECT ABS(xxx) FROM test"));
}
public void testFunctionOverMissingFieldInFilter() {
assertEquals("1:30: Unknown column [xxx]", error("SELECT * FROM test WHERE ABS(xxx) > 1"));
}
public void testMissingFunction() {
assertEquals("1:8: Unknown function [ZAZ]", verify("SELECT ZAZ(bool) FROM test"));
assertEquals("1:8: Unknown function [ZAZ]", error("SELECT ZAZ(bool) FROM test"));
}
public void testMisspelledFunction() {
assertEquals("1:8: Unknown function [COONT], did you mean any of [COUNT, COT, CONCAT]?", verify("SELECT COONT(bool) FROM test"));
assertEquals("1:8: Unknown function [COONT], did you mean any of [COUNT, COT, CONCAT]?", error("SELECT COONT(bool) FROM test"));
}
public void testMissingColumnInGroupBy() {
assertEquals("1:41: Unknown column [xxx]", verify("SELECT * FROM test GROUP BY DAY_OF_YEAR(xxx)"));
assertEquals("1:41: Unknown column [xxx]", error("SELECT * FROM test GROUP BY DAY_OF_YEAR(xxx)"));
}
public void testFilterOnUnknownColumn() {
assertEquals("1:26: Unknown column [xxx]", verify("SELECT * FROM test WHERE xxx = 1"));
assertEquals("1:26: Unknown column [xxx]", error("SELECT * FROM test WHERE xxx = 1"));
}
public void testMissingColumnInOrderBy() {
// xxx offset is that of the order by field
assertEquals("1:29: Unknown column [xxx]", verify("SELECT * FROM test ORDER BY xxx"));
assertEquals("1:29: Unknown column [xxx]", error("SELECT * FROM test ORDER BY xxx"));
}
public void testMissingColumnFunctionInOrderBy() {
// xxx offset is that of the order by field
assertEquals("1:41: Unknown column [xxx]", verify("SELECT * FROM test ORDER BY DAY_oF_YEAR(xxx)"));
assertEquals("1:41: Unknown column [xxx]", error("SELECT * FROM test ORDER BY DAY_oF_YEAR(xxx)"));
}
public void testMissingExtract() {
assertEquals("1:8: Unknown datetime field [ZAZ]", verify("SELECT EXTRACT(ZAZ FROM date) FROM test"));
assertEquals("1:8: Unknown datetime field [ZAZ]", error("SELECT EXTRACT(ZAZ FROM date) FROM test"));
}
public void testMissingExtractSimilar() {
assertEquals("1:8: Unknown datetime field [DAP], did you mean [DAY]?", verify("SELECT EXTRACT(DAP FROM date) FROM test"));
assertEquals("1:8: Unknown datetime field [DAP], did you mean [DAY]?", error("SELECT EXTRACT(DAP FROM date) FROM test"));
}
public void testMissingExtractSimilarMany() {
assertEquals("1:8: Unknown datetime field [DOP], did you mean any of [DOM, DOW, DOY]?",
verify("SELECT EXTRACT(DOP FROM date) FROM test"));
error("SELECT EXTRACT(DOP FROM date) FROM test"));
}
public void testExtractNonDateTime() {
assertEquals("1:8: Invalid datetime field [ABS]. Use any datetime function.", verify("SELECT EXTRACT(ABS FROM date) FROM test"));
assertEquals("1:8: Invalid datetime field [ABS]. Use any datetime function.", error("SELECT EXTRACT(ABS FROM date) FROM test"));
}
public void testMultipleColumns() {
// xxx offset is that of the order by field
assertEquals("1:43: Unknown column [xxx]\nline 1:8: Unknown column [xxx]",
verify("SELECT xxx FROM test GROUP BY DAY_oF_YEAR(xxx)"));
error("SELECT xxx FROM test GROUP BY DAY_oF_YEAR(xxx)"));
}
// GROUP BY
public void testGroupBySelectNonGrouped() {
assertEquals("1:8: Cannot use non-grouped column [text], expected [int]",
verify("SELECT text, int FROM test GROUP BY int"));
error("SELECT text, int FROM test GROUP BY int"));
}
public void testGroupByOrderByNonGrouped() {
assertEquals("1:50: Cannot order by non-grouped column [bool], expected [text]",
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY bool"));
error("SELECT MAX(int) FROM test GROUP BY text ORDER BY bool"));
}
public void testGroupByOrderByNonGrouped_WithHaving() {
assertEquals("1:71: Cannot order by non-grouped column [bool], expected [text]",
verify("SELECT MAX(int) FROM test GROUP BY text HAVING MAX(int) > 10 ORDER BY bool"));
error("SELECT MAX(int) FROM test GROUP BY text HAVING MAX(int) > 10 ORDER BY bool"));
}
public void testGroupByOrderByAliasedInSelectAllowed() {
LogicalPlan lp = accepted("SELECT text t FROM test GROUP BY text ORDER BY t");
LogicalPlan lp = accept("SELECT text t FROM test GROUP BY text ORDER BY t");
assertNotNull(lp);
}
public void testGroupByOrderByScalarOverNonGrouped() {
assertEquals("1:50: Cannot order by non-grouped column [YEAR(date [UTC])], expected [text]",
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY YEAR(date)"));
error("SELECT MAX(int) FROM test GROUP BY text ORDER BY YEAR(date)"));
}
public void testGroupByOrderByScalarOverNonGrouped_WithHaving() {
assertEquals("1:71: Cannot order by non-grouped column [YEAR(date [UTC])], expected [text]",
verify("SELECT MAX(int) FROM test GROUP BY text HAVING MAX(int) > 10 ORDER BY YEAR(date)"));
error("SELECT MAX(int) FROM test GROUP BY text HAVING MAX(int) > 10 ORDER BY YEAR(date)"));
}
public void testGroupByHavingNonGrouped() {
assertEquals("1:48: Cannot filter by non-grouped column [int], expected [text]",
verify("SELECT AVG(int) FROM test GROUP BY text HAVING int > 10"));
error("SELECT AVG(int) FROM test GROUP BY text HAVING int > 10"));
}
public void testGroupByAggregate() {
assertEquals("1:36: Cannot use an aggregate [AVG] for grouping",
verify("SELECT AVG(int) FROM test GROUP BY AVG(int)"));
error("SELECT AVG(int) FROM test GROUP BY AVG(int)"));
}
public void testStarOnNested() {
assertNotNull(accept("SELECT dep.* FROM test"));
}
public void testGroupByOnNested() {
assertEquals("1:38: Grouping isn't (yet) compatible with nested fields [dep.dep_id]",
verify("SELECT dep.dep_id FROM test GROUP BY dep.dep_id"));
error("SELECT dep.dep_id FROM test GROUP BY dep.dep_id"));
}
public void testHavingOnNested() {
assertEquals("1:51: HAVING isn't (yet) compatible with nested fields [dep.start_date]",
verify("SELECT int FROM test GROUP BY int HAVING AVG(YEAR(dep.start_date)) > 1980"));
error("SELECT int FROM test GROUP BY int HAVING AVG(YEAR(dep.start_date)) > 1980"));
}
public void testGroupByScalarFunctionWithAggOnTarget() {
assertEquals("1:31: Cannot use an aggregate [AVG] for grouping",
verify("SELECT int FROM test GROUP BY AVG(int) + 2"));
error("SELECT int FROM test GROUP BY AVG(int) + 2"));
}
public void testUnsupportedType() {
assertEquals("1:8: Cannot use field [unsupported] type [ip_range] as is unsupported",
verify("SELECT unsupported FROM test"));
error("SELECT unsupported FROM test"));
}
public void testUnsupportedStarExpansion() {
assertEquals("1:8: Cannot use field [unsupported] type [ip_range] as is unsupported",
error("SELECT unsupported.* FROM test"));
}
public void testUnsupportedTypeInFilter() {
assertEquals("1:26: Cannot use field [unsupported] type [ip_range] as is unsupported",
error("SELECT * FROM test WHERE unsupported > 1"));
}
public void testUnsupportedTypeInFunction() {
assertEquals("1:12: Cannot use field [unsupported] type [ip_range] as is unsupported",
error("SELECT ABS(unsupported) FROM test"));
}
public void testUnsupportedTypeInOrder() {
assertEquals("1:29: Cannot use field [unsupported] type [ip_range] as is unsupported",
error("SELECT * FROM test ORDER BY unsupported"));
}
public void testGroupByOrderByNonKey() {
assertEquals("1:52: Cannot order by non-grouped column [a], expected [bool]",
verify("SELECT AVG(int) a FROM test GROUP BY bool ORDER BY a"));
error("SELECT AVG(int) a FROM test GROUP BY bool ORDER BY a"));
}
public void testGroupByOrderByFunctionOverKey() {
assertEquals("1:44: Cannot order by non-grouped column [MAX(int)], expected [int]",
verify("SELECT int FROM test GROUP BY int ORDER BY MAX(int)"));
error("SELECT int FROM test GROUP BY int ORDER BY MAX(int)"));
}
public void testGroupByOrderByScore() {
assertEquals("1:44: Cannot order by non-grouped column [SCORE()], expected [int]",
verify("SELECT int FROM test GROUP BY int ORDER BY SCORE()"));
error("SELECT int FROM test GROUP BY int ORDER BY SCORE()"));
}
public void testHavingOnColumn() {
assertEquals("1:42: Cannot filter HAVING on non-aggregate [int]; consider using WHERE instead",
verify("SELECT int FROM test GROUP BY int HAVING int > 2"));
error("SELECT int FROM test GROUP BY int HAVING int > 2"));
}
public void testHavingOnScalar() {
assertEquals("1:42: Cannot filter HAVING on non-aggregate [int]; consider using WHERE instead",
verify("SELECT int FROM test GROUP BY int HAVING 2 < ABS(int)"));
error("SELECT int FROM test GROUP BY int HAVING 2 < ABS(int)"));
}
public void testInWithDifferentDataTypes_SelectClause() {
assertEquals("1:17: expected data type [INTEGER], value provided is of type [KEYWORD]",
verify("SELECT 1 IN (2, '3', 4)"));
error("SELECT 1 IN (2, '3', 4)"));
}
public void testInNestedWithDifferentDataTypes_SelectClause() {
assertEquals("1:27: expected data type [INTEGER], value provided is of type [KEYWORD]",
verify("SELECT 1 = 1 OR 1 IN (2, '3', 4)"));
error("SELECT 1 = 1 OR 1 IN (2, '3', 4)"));
}
public void testInWithDifferentDataTypesFromLeftValue_SelectClause() {
assertEquals("1:14: expected data type [INTEGER], value provided is of type [KEYWORD]",
verify("SELECT 1 IN ('foo', 'bar')"));
error("SELECT 1 IN ('foo', 'bar')"));
}
public void testInNestedWithDifferentDataTypesFromLeftValue_SelectClause() {
assertEquals("1:29: expected data type [KEYWORD], value provided is of type [INTEGER]",
verify("SELECT 1 = 1 OR 'foo' IN (2, 3)"));
error("SELECT 1 = 1 OR 'foo' IN (2, 3)"));
}
public void testInWithDifferentDataTypes_WhereClause() {
assertEquals("1:49: expected data type [TEXT], value provided is of type [INTEGER]",
verify("SELECT * FROM test WHERE text IN ('foo', 'bar', 4)"));
error("SELECT * FROM test WHERE text IN ('foo', 'bar', 4)"));
}
public void testInNestedWithDifferentDataTypes_WhereClause() {
assertEquals("1:60: expected data type [TEXT], value provided is of type [INTEGER]",
verify("SELECT * FROM test WHERE int = 1 OR text IN ('foo', 'bar', 2)"));
error("SELECT * FROM test WHERE int = 1 OR text IN ('foo', 'bar', 2)"));
}
public void testInWithDifferentDataTypesFromLeftValue_WhereClause() {
assertEquals("1:35: expected data type [TEXT], value provided is of type [INTEGER]",
verify("SELECT * FROM test WHERE text IN (1, 2)"));
error("SELECT * FROM test WHERE text IN (1, 2)"));
}
public void testInNestedWithDifferentDataTypesFromLeftValue_WhereClause() {
assertEquals("1:46: expected data type [TEXT], value provided is of type [INTEGER]",
verify("SELECT * FROM test WHERE int = 1 OR text IN (1, 2)"));
error("SELECT * FROM test WHERE int = 1 OR text IN (1, 2)"));
}
public void testNotSupportedAggregateOnDate() {
assertEquals("1:8: [AVG] argument must be [numeric], found value [date] type [date]",
verify("SELECT AVG(date) FROM test"));
error("SELECT AVG(date) FROM test"));
}
public void testNotSupportedAggregateOnString() {
assertEquals("1:8: [MAX] argument must be [numeric or date], found value [keyword] type [keyword]",
verify("SELECT MAX(keyword) FROM test"));
error("SELECT MAX(keyword) FROM test"));
}
public void testInvalidTypeForStringFunction_WithOneArg() {
assertEquals("1:8: [LENGTH] argument must be [string], found value [1] type [integer]",
verify("SELECT LENGTH(1)"));
error("SELECT LENGTH(1)"));
}
public void testInvalidTypeForNumericFunction_WithOneArg() {
assertEquals("1:8: [COS] argument must be [numeric], found value [foo] type [keyword]",
verify("SELECT COS('foo')"));
error("SELECT COS('foo')"));
}
public void testInvalidTypeForBooleanFunction_WithOneArg() {
assertEquals("1:8: [NOT] argument must be [boolean], found value [foo] type [keyword]",
verify("SELECT NOT 'foo'"));
error("SELECT NOT 'foo'"));
}
public void testInvalidTypeForStringFunction_WithTwoArgs() {
assertEquals("1:8: [CONCAT] first argument must be [string], found value [1] type [integer]",
verify("SELECT CONCAT(1, 'bar')"));
error("SELECT CONCAT(1, 'bar')"));
assertEquals("1:8: [CONCAT] second argument must be [string], found value [2] type [integer]",
verify("SELECT CONCAT('foo', 2)"));
error("SELECT CONCAT('foo', 2)"));
}
public void testInvalidTypeForNumericFunction_WithTwoArgs() {
assertEquals("1:8: [TRUNCATE] first argument must be [numeric], found value [foo] type [keyword]",
verify("SELECT TRUNCATE('foo', 2)"));
error("SELECT TRUNCATE('foo', 2)"));
assertEquals("1:8: [TRUNCATE] second argument must be [numeric], found value [bar] type [keyword]",
verify("SELECT TRUNCATE(1.2, 'bar')"));
error("SELECT TRUNCATE(1.2, 'bar')"));
}
public void testInvalidTypeForBooleanFuntion_WithTwoArgs() {
assertEquals("1:8: [OR] first argument must be [boolean], found value [1] type [integer]",
verify("SELECT 1 OR true"));
error("SELECT 1 OR true"));
assertEquals("1:8: [OR] second argument must be [boolean], found value [2] type [integer]",
verify("SELECT true OR 2"));
error("SELECT true OR 2"));
}
public void testInvalidTypeForFunction_WithThreeArgs() {
assertEquals("1:8: [REPLACE] first argument must be [string], found value [1] type [integer]",
verify("SELECT REPLACE(1, 'foo', 'bar')"));
error("SELECT REPLACE(1, 'foo', 'bar')"));
assertEquals("1:8: [REPLACE] second argument must be [string], found value [2] type [integer]",
verify("SELECT REPLACE('text', 2, 'bar')"));
error("SELECT REPLACE('text', 2, 'bar')"));
assertEquals("1:8: [REPLACE] third argument must be [string], found value [3] type [integer]",
verify("SELECT REPLACE('text', 'foo', 3)"));
error("SELECT REPLACE('text', 'foo', 3)"));
}
public void testInvalidTypeForFunction_WithFourArgs() {
assertEquals("1:8: [INSERT] first argument must be [string], found value [1] type [integer]",
verify("SELECT INSERT(1, 1, 2, 'new')"));
error("SELECT INSERT(1, 1, 2, 'new')"));
assertEquals("1:8: [INSERT] second argument must be [numeric], found value [foo] type [keyword]",
verify("SELECT INSERT('text', 'foo', 2, 'new')"));
error("SELECT INSERT('text', 'foo', 2, 'new')"));
assertEquals("1:8: [INSERT] third argument must be [numeric], found value [bar] type [keyword]",
verify("SELECT INSERT('text', 1, 'bar', 'new')"));
error("SELECT INSERT('text', 1, 'bar', 'new')"));
assertEquals("1:8: [INSERT] fourth argument must be [string], found value [3] type [integer]",
verify("SELECT INSERT('text', 1, 2, 3)"));
error("SELECT INSERT('text', 1, 2, 3)"));
}
}
}