Browse Source

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
Costin Leau 7 years ago
parent
commit
4119409b6d

+ 31 - 20
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java

@@ -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

+ 115 - 79
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java

@@ -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)"));
     }
-}
+}