From 3509775af71e9c1f57fb9f7aaab371497384eb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Mantelet?= <gregory.mantelet@astro.unistra.fr> Date: Thu, 8 Aug 2019 14:00:28 +0200 Subject: [PATCH] [ADQL] Check the UDF name while creating a FunctionDef and so, also while creating the corresponding LanguageFeature. Now, FunctionDef throws a ParseException when the UDF name is invalid. --- src/adql/db/FunctionDef.java | 177 +++++++++- src/adql/parser/ADQLParser.java | 2 + src/adql/parser/ADQLParser200.java | 104 +++--- src/adql/parser/ADQLParser201.java | 120 ++++--- src/adql/parser/adqlGrammar200.jj | 17 + src/adql/parser/adqlGrammar201.jj | 17 + .../query/operand/function/DefaultUDF.java | 19 +- test/adql/db/TestDBChecker.java | 96 ++++-- test/adql/db/TestFunctionDef.java | 318 +++++++++++++----- test/adql/parser/feature/TestFeatureSet.java | 27 +- test/adql/query/TestADQLQuery.java | 47 +-- 11 files changed, 684 insertions(+), 260 deletions(-) diff --git a/src/adql/db/FunctionDef.java b/src/adql/db/FunctionDef.java index b1e27b1..4a3d1f6 100644 --- a/src/adql/db/FunctionDef.java +++ b/src/adql/db/FunctionDef.java @@ -25,7 +25,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import adql.db.DBType.DBDatatype; +import adql.parser.ADQLParser; +import adql.parser.ADQLParserFactory; +import adql.parser.ADQLParserFactory.ADQLVersion; import adql.parser.ParseException; +import adql.parser.Token; import adql.parser.feature.LanguageFeature; import adql.query.operand.ADQLOperand; import adql.query.operand.function.ADQLFunction; @@ -52,10 +56,10 @@ import adql.query.operand.function.UserDefinedFunction; * A description of this function may be set thanks to the public class * attribute {@link #description}. * </p> - * + * * @author Grégory Mantelet (CDS;ARI) * @version 2.0 (08/2020) - * + * * @since 1.3 */ public class FunctionDef implements Comparable<FunctionDef> { @@ -264,9 +268,13 @@ public class FunctionDef implements Comparable<FunctionDef> { * </p> * * @param fctName Name of the function. + * + * @throws ParseException If the given UDF name is invalid according to + * the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * of the ADQL grammar. */ - public FunctionDef(final String fctName) { - this(fctName, null, null); + public FunctionDef(final String fctName) throws ParseException { + this(fctName, null, null, null); } /** @@ -277,13 +285,13 @@ public class FunctionDef implements Comparable<FunctionDef> { * null) and <b>no parameter</b>. * </p> * - * @param fctName Name of the function. + * @param fctName Name of the function. * @param returnType Return type of the function. * <i>If NULL, this function will have no return * type.</i> */ - public FunctionDef(final String fctName, final DBType returnType) { - this(fctName, returnType, null); + public FunctionDef(final String fctName, final DBType returnType) throws ParseException { + this(fctName, returnType, null, null); } /** @@ -295,19 +303,65 @@ public class FunctionDef implements Comparable<FunctionDef> { * </p> * * @param fctName Name of the function. - * @param params Parameters of this function. - * <i>If NULL or empty, this function will have no - * parameter.</i> + * @param params Parameters of this function. + * <i>If NULL or empty, this function will have no + * parameter.</i> + * + * @throws ParseException If the given UDF name is invalid according to + * the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * of the ADQL grammar. + */ + public FunctionDef(final String fctName, final FunctionParam[] params) throws ParseException { + this(fctName, null, params, null); + } + + /** + * Create a function definition. + * + * @param fctName Name of the function. + * @param returnType Return type of the function. + * <i>If NULL, this function will have no return type</i> + * @param params Parameters of this function. + * <i>If NULL or empty, this function will have no + * parameter.</i> + * + * @throws ParseException If the given UDF name is invalid according to + * the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * of the ADQL grammar. */ - public FunctionDef(final String fctName, final FunctionParam[] params) { - this(fctName, null, params); + public FunctionDef(final String fctName, final DBType returnType, final FunctionParam[] params) throws ParseException { + this(fctName, returnType, params, ADQLParserFactory.DEFAULT_VERSION); } - public FunctionDef(final String fctName, final DBType returnType, final FunctionParam[] params) { + /** + * Create a function definition. + * + * @param fctName Name of the function. + * @param returnType Return type of the function. + * <i>If NULL, this function will have no return type</i> + * @param params Parameters of this function. + * <i>If NULL or empty, this function will have no + * parameter.</i> + * @param targetADQL Targeted ADQL grammar's version. + * <i>If NULL, the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * will be used. This parameter is used only to check + * the UDF name.</i> + * + * + * @throws ParseException If the given UDF name is invalid according to + * the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * of the ADQL grammar. + * + * @since 2.0 + */ + public FunctionDef(final String fctName, final DBType returnType, final FunctionParam[] params, final ADQLVersion targetADQL) throws ParseException { // Set the name: if (fctName == null) throw new NullPointerException("Missing name! Can not create this function definition."); - this.name = fctName; + this.name = fctName.trim(); + + // Ensure the function name is valid: + checkUDFName(fctName, targetADQL); // Set the parameters: this.params = (params == null || params.length == 0) ? null : params; @@ -336,6 +390,56 @@ public class FunctionDef implements Comparable<FunctionDef> { compareForm = bufCmp.toString(); } + /** + * Check that the given UDF name is valid according to the ADQL grammar. + * + * @param fctName Name of the UDF to check. + * @param adqlVersion Version of the targeted ADQL grammar. + * <i>If NULL, the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * will be used.</i> + * + * @throws ParseException If the given name is invalid. + * + * @since 2.0 + */ + protected static void checkUDFName(final String fctName, final ADQLVersion adqlVersion) throws ParseException { + if (fctName == null) + throw new ParseException("Invalid UDF name: missing User Defined Function's name!"); + + ADQLParser parser = null; + Token[] tokens = new Token[0]; + + // Tokenize the given function name: + try { + parser = (new ADQLParserFactory()).createParser(adqlVersion == null ? ADQLParserFactory.DEFAULT_VERSION : adqlVersion); + tokens = parser.tokenize(fctName); + } catch(ParseException ex) { + throw new ParseException("Invalid UDF name: " + ex.getMessage()); + } + + // Ensure there is only one word: + if (tokens.length == 0) + throw new ParseException("Invalid UDF name: missing User Defined Function's name!"); + else if (tokens.length > 1) + throw new ParseException("Invalid UDF name: too many words (a function name must be a single Regular Identifier)!"); + + // ...that it is a regular identifier: + if (!parser.isRegularIdentifier(tokens[0].image)) + throw new ParseException("Invalid UDF name: \"" + fctName + "\" is not a Regular Identifier!"); + + // ...that it is not already an existing ADQL function name: + if (tokens[0].isFunctionName) + throw new ParseException("Invalid UDF name: \"" + fctName + "\" already exists in ADQL!"); + + // ...that it is not an ADQL reserved keyword: + if (tokens[0].adqlReserved) + throw new ParseException("Invalid UDF name: \"" + fctName + "\" is an ADQL Reserved Keyword!"); + + // ...and that it is neither an SQL reserver keyword: + if (tokens[0].sqlReserved) + throw new ParseException("Invalid UDF name: \"" + fctName + "\" is an SQL Reserved Keyword!"); + } + /** * Tell whether this function returns a numeric. * @@ -586,9 +690,49 @@ public class FunctionDef implements Comparable<FunctionDef> { * @return The object representation of the given string definition. * * @throws ParseException If the given string has a wrong syntax or uses - * unknown types. + * unknown types, + * or if the function name is invalid according to + * the ADQL grammar. */ public static FunctionDef parse(final String strDefinition) throws ParseException { + return parse(strDefinition, null); + } + + /** + * Let parsing the serialized form of a function definition. + * + * <p>The expected syntax is <i>(items between brackets are optional)</i>:</p> + * <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre> + * + * <p> + * <em>This function must be able to parse functions as defined by + * TAPRegExt (section 2.3).</em> + * Hence, allowed parameter types and return types should be one of the + * types listed by the UPLOAD section of the TAP recommendation document. + * These types are listed in the enumeration object {@link DBDatatype}. + * However, other types should be accepted like the common database + * types...but it should be better to not rely on that since the conversion + * of those types to TAP types should not be exactly what is expected + * (because depending from the used DBMS); a default interpretation of + * database types is nevertheless processed by this parser. + * </p> + * + * @param strDefinition Serialized function definition to parse. + * @param targetADQL Targeted ADQL grammar's version. + * <i>If NULL, the {@link ADQLParserFactory#DEFAULT_VERSION default version} + * will be used. This parameter is used only to check + * the UDF name.</i> + * + * @return The object representation of the given string definition. + * + * @throws ParseException If the given string has a wrong syntax or uses + * unknown types, + * or if the function name is invalid according to + * the ADQL grammar. + * + * @since 2.0 + */ + public static FunctionDef parse(final String strDefinition, final ADQLVersion targetADQL) throws ParseException { if (strDefinition == null) throw new NullPointerException("Missing string definition to build a FunctionDef!"); @@ -599,6 +743,9 @@ public class FunctionDef implements Comparable<FunctionDef> { // Get the function name: String fctName = m.group(1); + // Ensure the function name is valid: + checkUDFName(fctName, targetADQL); + // Parse and get the return type: DBType returnType = null; if (m.group(3) != null) { diff --git a/src/adql/parser/ADQLParser.java b/src/adql/parser/ADQLParser.java index ba40071..9292f13 100644 --- a/src/adql/parser/ADQLParser.java +++ b/src/adql/parser/ADQLParser.java @@ -134,4 +134,6 @@ public interface ADQLParser { ADQLOperand StringExpression() throws ParseException; + public Token[] tokenize(final String expr) throws ParseException; + } diff --git a/src/adql/parser/ADQLParser200.java b/src/adql/parser/ADQLParser200.java index a4b1e1d..c3f7eca 100644 --- a/src/adql/parser/ADQLParser200.java +++ b/src/adql/parser/ADQLParser200.java @@ -652,6 +652,24 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { } } + /* TOKENIZATION FUNCTION */ + + @Override + public Token[] tokenize(final String expr) throws ParseException { + ADQLParser200TokenManager parser = new ADQLParser200TokenManager(new SimpleCharStream(new java.io.ByteArrayInputStream(expr.getBytes()))); + try { + ArrayList<Token> tokens = new ArrayList<Token>(); + Token token; + while(!isEnd((token = parser.getNextToken()))) { + tokens.add(token); + } + return tokens.toArray(new Token[tokens.size()]); + } catch(TokenMgrError err) { + // wrap such errors and propagate them: + throw new ParseException(err); + } + } + /* CORRECTION SUGGESTION */ /** @@ -4459,49 +4477,6 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { } } - private boolean jj_3R_17() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_34()) { - jj_scanpos = xsp; - if (jj_3R_35()) - return true; - } - return false; - } - - private boolean jj_3_17() { - if (jj_3R_29()) - return true; - Token xsp; - xsp = jj_scanpos; - if (jj_scan_token(36)) - jj_scanpos = xsp; - if (jj_scan_token(LIKE)) - return true; - return false; - } - - private boolean jj_3R_76() { - if (jj_scan_token(LEFT_PAR)) - return true; - return false; - } - - private boolean jj_3_3() { - if (jj_3R_17()) - return true; - return false; - } - - private boolean jj_3_16() { - if (jj_3R_22()) - return true; - if (jj_scan_token(IS)) - return true; - return false; - } - private boolean jj_3R_134() { if (jj_scan_token(COMMA)) return true; @@ -6186,6 +6161,49 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { return false; } + private boolean jj_3R_17() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_34()) { + jj_scanpos = xsp; + if (jj_3R_35()) + return true; + } + return false; + } + + private boolean jj_3_17() { + if (jj_3R_29()) + return true; + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(36)) + jj_scanpos = xsp; + if (jj_scan_token(LIKE)) + return true; + return false; + } + + private boolean jj_3R_76() { + if (jj_scan_token(LEFT_PAR)) + return true; + return false; + } + + private boolean jj_3_3() { + if (jj_3R_17()) + return true; + return false; + } + + private boolean jj_3_16() { + if (jj_3R_22()) + return true; + if (jj_scan_token(IS)) + return true; + return false; + } + /** Generated Token Manager. */ public ADQLParser200TokenManager token_source; SimpleCharStream jj_input_stream; diff --git a/src/adql/parser/ADQLParser201.java b/src/adql/parser/ADQLParser201.java index 29691f2..1d8499f 100644 --- a/src/adql/parser/ADQLParser201.java +++ b/src/adql/parser/ADQLParser201.java @@ -643,6 +643,24 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { } } + /* TOKENIZATION FUNCTION */ + + @Override + public Token[] tokenize(final String expr) throws ParseException { + ADQLParser201TokenManager parser = new ADQLParser201TokenManager(new SimpleCharStream(new java.io.ByteArrayInputStream(expr.getBytes()))); + try { + ArrayList<Token> tokens = new ArrayList<Token>(); + Token token; + while(!isEnd((token = parser.getNextToken()))) { + tokens.add(token); + } + return tokens.toArray(new Token[tokens.size()]); + } catch(TokenMgrError err) { + // wrap such errors and propagate them: + throw new ParseException(err); + } + } + /* CORRECTION SUGGESTION */ /** @@ -4476,57 +4494,6 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { } } - private boolean jj_3R_17() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_34()) { - jj_scanpos = xsp; - if (jj_3R_35()) - return true; - } - return false; - } - - private boolean jj_3_17() { - if (jj_3R_29()) - return true; - Token xsp; - xsp = jj_scanpos; - if (jj_scan_token(36)) - jj_scanpos = xsp; - if (jj_scan_token(LIKE)) - return true; - return false; - } - - private boolean jj_3R_135() { - if (jj_scan_token(COMMA)) - return true; - if (jj_3R_50()) - return true; - return false; - } - - private boolean jj_3R_76() { - if (jj_scan_token(LEFT_PAR)) - return true; - return false; - } - - private boolean jj_3_3() { - if (jj_3R_17()) - return true; - return false; - } - - private boolean jj_3_16() { - if (jj_3R_22()) - return true; - if (jj_scan_token(IS)) - return true; - return false; - } - private boolean jj_3_2() { if (jj_3R_16()) return true; @@ -6212,6 +6179,57 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { return false; } + private boolean jj_3R_17() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_34()) { + jj_scanpos = xsp; + if (jj_3R_35()) + return true; + } + return false; + } + + private boolean jj_3_17() { + if (jj_3R_29()) + return true; + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(36)) + jj_scanpos = xsp; + if (jj_scan_token(LIKE)) + return true; + return false; + } + + private boolean jj_3R_135() { + if (jj_scan_token(COMMA)) + return true; + if (jj_3R_50()) + return true; + return false; + } + + private boolean jj_3R_76() { + if (jj_scan_token(LEFT_PAR)) + return true; + return false; + } + + private boolean jj_3_3() { + if (jj_3R_17()) + return true; + return false; + } + + private boolean jj_3_16() { + if (jj_3R_22()) + return true; + if (jj_scan_token(IS)) + return true; + return false; + } + /** Generated Token Manager. */ public ADQLParser201TokenManager token_source; SimpleCharStream jj_input_stream; diff --git a/src/adql/parser/adqlGrammar200.jj b/src/adql/parser/adqlGrammar200.jj index a0ac86c..360d0b8 100644 --- a/src/adql/parser/adqlGrammar200.jj +++ b/src/adql/parser/adqlGrammar200.jj @@ -670,6 +670,23 @@ public class ADQLParser200 implements ADQLParser { } } + /* TOKENIZATION FUNCTION */ + + public Token[] tokenize(final String expr) throws ParseException { + ADQLParser200TokenManager parser = new ADQLParser200TokenManager(new SimpleCharStream(new java.io.ByteArrayInputStream(expr.getBytes()))); + try{ + ArrayList<Token> tokens = new ArrayList<Token>(); + Token token; + while(!isEnd((token=parser.getNextToken()))){ + tokens.add(token); + } + return tokens.toArray(new Token[tokens.size()]); + }catch(TokenMgrError err){ + // wrap such errors and propagate them: + throw new ParseException(err); + } + } + /* CORRECTION SUGGESTION */ /** diff --git a/src/adql/parser/adqlGrammar201.jj b/src/adql/parser/adqlGrammar201.jj index a5db23b..98a75d6 100644 --- a/src/adql/parser/adqlGrammar201.jj +++ b/src/adql/parser/adqlGrammar201.jj @@ -664,6 +664,23 @@ public class ADQLParser201 implements ADQLParser { } } + /* TOKENIZATION FUNCTION */ + + public Token[] tokenize(final String expr) throws ParseException { + ADQLParser201TokenManager parser = new ADQLParser201TokenManager(new SimpleCharStream(new java.io.ByteArrayInputStream(expr.getBytes()))); + try{ + ArrayList<Token> tokens = new ArrayList<Token>(); + Token token; + while(!isEnd((token=parser.getNextToken()))){ + tokens.add(token); + } + return tokens.toArray(new Token[tokens.size()]); + }catch(TokenMgrError err){ + // wrap such errors and propagate them: + throw new ParseException(err); + } + } + /* CORRECTION SUGGESTION */ /** diff --git a/src/adql/query/operand/function/DefaultUDF.java b/src/adql/query/operand/function/DefaultUDF.java index b67b40d..326f5b8 100644 --- a/src/adql/query/operand/function/DefaultUDF.java +++ b/src/adql/query/operand/function/DefaultUDF.java @@ -26,6 +26,7 @@ import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.FunctionDef; import adql.db.FunctionDef.FunctionParam; +import adql.parser.ParseException; import adql.parser.feature.LanguageFeature; import adql.query.ADQLList; import adql.query.ADQLObject; @@ -49,7 +50,10 @@ public final class DefaultUDF extends UserDefinedFunction { /** Define/Describe this user defined function. * This object gives the return type and the number and type of all - * parameters. */ + * parameters. + * <p><i><b>Note:</b> NULL if the function name is invalid. See + * {@link FunctionDef#FunctionDef(String, DBType, FunctionParam[], adql.parser.ADQLParserFactory.ADQLVersion) FunctionDef.FunctionDef(..., ADQLVersion)} + * for more details.</i></p> */ protected FunctionDef definition = null; /** Its parsed parameters. */ @@ -163,11 +167,16 @@ public final class DefaultUDF extends UserDefinedFunction { for(int i = 1; i <= parameters.size(); i++) inputParams[i - 1] = new FunctionParam("param" + i, unknownType); - // Create the Function Definition: - FunctionDef fctDef = new FunctionDef(functionName, unknownType, inputParams); + try { + // Create the Function Definition: + FunctionDef fctDef = new FunctionDef(functionName, unknownType, inputParams); - // Finally create the LanguageFeature: - languageFeature = new LanguageFeature(fctDef); + // Finally create the LanguageFeature: + languageFeature = new LanguageFeature(fctDef); + } catch(ParseException pe) { + // TODO Invalid function name. TO LOG in some way! + languageFeature = null; + } } @Override diff --git a/test/adql/db/TestDBChecker.java b/test/adql/db/TestDBChecker.java index 72ca3dd..7555e5d 100644 --- a/test/adql/db/TestDBChecker.java +++ b/test/adql/db/TestDBChecker.java @@ -290,10 +290,16 @@ public class TestDBChecker { } // DECLARE THE UDFs: - FunctionDef[] udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)), new FunctionDef("tata", new DBType(DBDatatype.INTEGER)) }; - parser = parserFactory.createParser(); - parser.getSupportedFeatures().allowAnyUdf(true); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + FunctionDef[] udfs; + try { + udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)), new FunctionDef("tata", new DBType(DBDatatype.INTEGER)) }; + parser = parserFactory.createParser(); + parser.getSupportedFeatures().allowAnyUdf(true); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } // Test again: try { @@ -340,11 +346,17 @@ public class TestDBChecker { } // Test with a UDF whose the class is specified ; the corresponding object in the ADQL tree must be replace by an instance of this class: - udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("txt", new DBType(DBDatatype.VARCHAR)) }) }; - udfs[0].setUDFClass(UDFToto.class); - parser = parserFactory.createParser(); - parser.getSupportedFeatures().allowAnyUdf(true); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + try { + udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("txt", new DBType(DBDatatype.VARCHAR)) }) }; + udfs[0].setUDFClass(UDFToto.class); + parser = parserFactory.createParser(); + parser.getSupportedFeatures().allowAnyUdf(true); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } + try { ADQLQuery query = parser.parseQuery("SELECT toto('blabla') FROM foo;"); assertNotNull(query); @@ -374,11 +386,17 @@ public class TestDBChecker { } // Test with UDF class constructor throwing an exception: - udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("txt", new DBType(DBDatatype.VARCHAR)) }) }; - udfs[0].setUDFClass(WrongUDFToto.class); - parser = parserFactory.createParser(); - parser.getSupportedFeatures().allowAnyUdf(true); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + try { + udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("txt", new DBType(DBDatatype.VARCHAR)) }) }; + udfs[0].setUDFClass(WrongUDFToto.class); + parser = parserFactory.createParser(); + parser.getSupportedFeatures().allowAnyUdf(true); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } + try { parser.parseQuery("SELECT toto('blabla') FROM foo;"); fail("The set UDF class constructor has throw an error: this test should have failed!"); @@ -673,9 +691,15 @@ public class TestDBChecker { } // DECLARE SOME UDFs: - FunctionDef[] udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)), new FunctionDef("tata", new DBType(DBDatatype.INTEGER)), new FunctionDef("titi", new DBType(DBDatatype.REGION)) }; - parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + FunctionDef[] udfs; + try { + udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)), new FunctionDef("tata", new DBType(DBDatatype.INTEGER)), new FunctionDef("titi", new DBType(DBDatatype.REGION)) }; + parser = parserFactory.createParser(); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } // Test the return type of the function TOTO generated by the parser: try { @@ -782,12 +806,18 @@ public class TestDBChecker { // Try with functions wrapped on 2 levels: // i.e. fct1('blabla', fct2(fct3('blabla'))) - FunctionDef[] complexFcts = new FunctionDef[3]; - complexFcts[0] = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("str", new DBType(DBDatatype.VARCHAR)), new FunctionParam("num", new DBType(DBDatatype.INTEGER)) }); - complexFcts[1] = new FunctionDef("fct2", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("str", new DBType(DBDatatype.VARCHAR)) }); - complexFcts[2] = new FunctionDef("fct3", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("str", new DBType(DBDatatype.VARCHAR)) }); - parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(complexFcts))); + try { + FunctionDef[] complexFcts = new FunctionDef[3]; + complexFcts[0] = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("str", new DBType(DBDatatype.VARCHAR)), new FunctionParam("num", new DBType(DBDatatype.INTEGER)) }); + complexFcts[1] = new FunctionDef("fct2", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("str", new DBType(DBDatatype.VARCHAR)) }); + complexFcts[2] = new FunctionDef("fct3", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("str", new DBType(DBDatatype.VARCHAR)) }); + parser = parserFactory.createParser(); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(complexFcts))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } + // With parameters of the good type: try { assertNotNull(parser.parseQuery("SELECT fct1('blabla', fct2(fct3('blabla'))) FROM foo")); @@ -832,8 +862,13 @@ public class TestDBChecker { } // DECLARE THE UDF (while unknown functions are allowed): - parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)) }))); + try { + parser = parserFactory.createParser(); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR)) }))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } // Test the return type of the function generated by the parser: try { @@ -849,9 +884,14 @@ public class TestDBChecker { } // DECLARE UDFs WITH SAME NAMES BUT DIFFERENT TYPE OF ARGUMENT: - udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("attr", new DBType(DBDatatype.VARCHAR)) }), new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("attr", new DBType(DBDatatype.INTEGER)) }), new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("attr", new DBType(DBDatatype.POINT)) }) }; - parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + try { + udfs = new FunctionDef[]{ new FunctionDef("toto", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("attr", new DBType(DBDatatype.VARCHAR)) }), new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("attr", new DBType(DBDatatype.INTEGER)) }), new FunctionDef("toto", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("attr", new DBType(DBDatatype.POINT)) }) }; + parser = parserFactory.createParser(); + parser.setQueryChecker(new DBChecker(tables, Arrays.asList(udfs))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } // Test the return type in function of the parameter: try { diff --git a/test/adql/db/TestFunctionDef.java b/test/adql/db/TestFunctionDef.java index b46b73c..92bbfc0 100644 --- a/test/adql/db/TestFunctionDef.java +++ b/test/adql/db/TestFunctionDef.java @@ -10,6 +10,8 @@ import org.junit.Test; import adql.db.DBType.DBDatatype; import adql.db.FunctionDef.FunctionParam; +import adql.parser.ADQLParserFactory; +import adql.parser.ADQLParserFactory.ADQLVersion; import adql.parser.ParseException; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; @@ -27,63 +29,83 @@ public class TestFunctionDef { } @Test - public void testIsString() { - for(DBDatatype type : DBDatatype.values()) { - switch(type) { - case CHAR: - case VARCHAR: - case TIMESTAMP: - case CLOB: - assertTrue(new FunctionDef("foo", new DBType(type)).isString); - break; - default: - assertFalse(new FunctionDef("foo", new DBType(type)).isString); + public void testIsString(){ + try { + for(DBDatatype type : DBDatatype.values()){ + switch(type){ + case CHAR: + case VARCHAR: + case TIMESTAMP: + case CLOB: + assertTrue(new FunctionDef("foo", new DBType(type)).isString); + break; + default: + assertFalse(new FunctionDef("foo", new DBType(type)).isString); + } } + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); } } @Test - public void testIsGeometry() { - for(DBDatatype type : DBDatatype.values()) { - switch(type) { - case POINT: - case REGION: - assertTrue(new FunctionDef("foo", new DBType(type)).isGeometry); - break; - default: - assertFalse(new FunctionDef("foo", new DBType(type)).isGeometry); + public void testIsGeometry(){ + try { + for(DBDatatype type : DBDatatype.values()){ + switch(type){ + case POINT: + case REGION: + assertTrue(new FunctionDef("foo", new DBType(type)).isGeometry); + break; + default: + assertFalse(new FunctionDef("foo", new DBType(type)).isGeometry); + } } + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); } } @Test - public void testIsNumeric() { - for(DBDatatype type : DBDatatype.values()) { - switch(type) { - case CHAR: - case VARCHAR: - case TIMESTAMP: - case POINT: - case REGION: - case CLOB: - case UNKNOWN: - assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric); - break; - case UNKNOWN_NUMERIC: - default: - assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric); + public void testIsNumeric(){ + try { + for(DBDatatype type : DBDatatype.values()){ + switch(type){ + case CHAR: + case VARCHAR: + case TIMESTAMP: + case POINT: + case REGION: + case CLOB: + case UNKNOWN: + assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric); + break; + case UNKNOWN_NUMERIC: + default: + assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric); + } } + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); } } @Test - public void testToString() { - assertEquals("fct1()", new FunctionDef("fct1").toString()); - assertEquals("fct1() -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)).toString()); - assertEquals("fct1(foo DOUBLE) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)) }).toString()); - assertEquals("fct1(foo DOUBLE)", new FunctionDef("fct1", new FunctionParam[]{ new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)) }).toString()); - assertEquals("fct1(foo DOUBLE, pt POINT) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)), new FunctionParam("pt", new DBType(DBDatatype.POINT)) }).toString()); - assertEquals("fct1(foo DOUBLE, pt POINT)", new FunctionDef("fct1", null, new FunctionParam[]{ new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)), new FunctionParam("pt", new DBType(DBDatatype.POINT)) }).toString()); + public void testToString(){ + try { + assertEquals("fct1()", new FunctionDef("fct1").toString()); + assertEquals("fct1() -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)).toString()); + assertEquals("fct1(foo DOUBLE) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE))}).toString()); + assertEquals("fct1(foo DOUBLE)", new FunctionDef("fct1", new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE))}).toString()); + assertEquals("fct1(foo DOUBLE, pt POINT) -> VARCHAR", new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)),new FunctionParam("pt", new DBType(DBDatatype.POINT))}).toString()); + assertEquals("fct1(foo DOUBLE, pt POINT)", new FunctionDef("fct1", null, new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.DOUBLE)),new FunctionParam("pt", new DBType(DBDatatype.POINT))}).toString()); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } } @Test @@ -269,66 +291,72 @@ public class TestFunctionDef { } @Test - public void testCompareToFunctionDef() { - // DEFINITION 1 :: fct1() -> VARCHAR - FunctionDef def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)); + public void testCompareToFunctionDef(){ + try { + // DEFINITION 1 :: fct1() -> VARCHAR + FunctionDef def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)); - // TEST :: Identity test (def1 with def1): [EQUAL] - assertEquals(0, def1.compareTo(def1)); + // TEST :: Identity test (def1 with def1): [EQUAL] + assertEquals(0, def1.compareTo(def1)); - // TEST :: With a function having a different name and also no parameter: [GREATER] - assertEquals(1, def1.compareTo(new FunctionDef("fct0", new DBType(DBDatatype.VARCHAR)))); + // TEST :: With a function having a different name and also no parameter: [GREATER] + assertEquals(1, def1.compareTo(new FunctionDef("fct0", new DBType(DBDatatype.VARCHAR)))); - // TEST :: With a function having the same name, but a different return type: [EQUAL} - assertEquals(0, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER)))); + // TEST :: With a function having the same name, but a different return type: [EQUAL} + assertEquals(0, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER)))); - // TEST :: With a function having the same name, but 2 parameters: [LESS (6 characters: ø against 100100)] - assertEquals(-6, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER), new FunctionParam[]{ new FunctionParam("foo", new DBType(DBDatatype.INTEGER)), new FunctionParam("foo", new DBType(DBDatatype.INTEGER)) }))); + // TEST :: With a function having the same name, but 2 parameters: [LESS (6 characters: ø against 100100)] + assertEquals(-6, def1.compareTo(new FunctionDef("fct1", new DBType(DBDatatype.INTEGER), new FunctionParam[]{new FunctionParam("foo", new DBType(DBDatatype.INTEGER)),new FunctionParam("foo", new DBType(DBDatatype.INTEGER))}))); - // DEFINITION 1 :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR - def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)), new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE)) }); + // DEFINITION 1 :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR + def1 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))}); - // TEST :: Identity test (def1 with def1): [EQUAL] - assertEquals(0, def1.compareTo(def1)); + // TEST :: Identity test (def1 with def1): [EQUAL] + assertEquals(0, def1.compareTo(def1)); - // DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 VARCHAR) -> VARCHAR - FunctionDef def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)), new FunctionParam("foo2", new DBType(DBDatatype.VARCHAR)) }); + // DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 VARCHAR) -> VARCHAR + FunctionDef def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.VARCHAR))}); - // TEST :: Identity test (def2 with def2): [EQUAL] - assertEquals(0, def2.compareTo(def2)); + // TEST :: Identity test (def2 with def2): [EQUAL] + assertEquals(0, def2.compareTo(def2)); - // TEST :: Same name, but different type for the last parameter only: [GREATER (because Numeric = 10 > String = 01)] - assertEquals(1, def1.compareTo(def2)); + // TEST :: Same name, but different type for the last parameter only: [GREATER (because Numeric = 10 > String = 01)] + assertEquals(1, def1.compareTo(def2)); - // DEFINITION 2 :: fct2(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR - def2 = new FunctionDef("fct2", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)), new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE)) }); + // DEFINITION 2 :: fct2(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR + def2 = new FunctionDef("fct2", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))}); - // TEST :: Identity test (def2 with def2): [EQUAL] - assertEquals(0, def2.compareTo(def2)); + // TEST :: Identity test (def2 with def2): [EQUAL] + assertEquals(0, def2.compareTo(def2)); - // TEST :: Different name but same parameters: [LESS] - assertEquals(-1, def1.compareTo(def2)); + // TEST :: Different name but same parameters: [LESS] + assertEquals(-1, def1.compareTo(def2)); - // DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 POINT) -> VARCHAR - def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)), new FunctionParam("foo2", new DBType(DBDatatype.POINT)) }); + // DEFINITION 2 :: fct1(foo1 CHAR(12), foo2 POINT) -> VARCHAR + def2 = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.POINT))}); - // TEST :: Identity test (def2 with def2): [EQUAL] - assertEquals(0, def2.compareTo(def2)); + // TEST :: Identity test (def2 with def2): [EQUAL] + assertEquals(0, def2.compareTo(def2)); - // TEST :: Same name, but different type for the last parameter only: [GREATER] - assertEquals(1, def1.compareTo(def2)); + // TEST :: Same name, but different type for the last parameter only: [GREATER] + assertEquals(1, def1.compareTo(def2)); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } } @Test - public void testCompareToADQLFunction() { + public void testCompareToADQLFunction(){ + try { // DEFINITION :: fct1() -> VARCHAR FunctionDef def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR)); // TEST :: NULL: - try { + try{ def.compareTo((ADQLFunction)null); fail("Missing ADQL function for comparison with FunctionDef!"); - } catch(Exception e) { + }catch(Exception e){ assertTrue(e instanceof NullPointerException); assertEquals("Missing ADQL function with which comparing this function definition!", e.getMessage()); } @@ -340,33 +368,149 @@ public class TestFunctionDef { assertEquals(1, def.compareTo(new DefaultUDF("fct0", null))); // TEST :: "fct1(12.3, 3.14)": [LESS (of 2 params)] - assertEquals(-2, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{ new NumericConstant(12.3), new NumericConstant(3.14) }))); + assertEquals(-2, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new NumericConstant(12.3),new NumericConstant(3.14)}))); // DEFINITION :: fct1(foo1 CHAR(12), foo2 DOUBLE) -> VARCHAR - def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{ new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)), new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE)) }); + def = new FunctionDef("fct1", new DBType(DBDatatype.VARCHAR), new FunctionParam[]{new FunctionParam("foo1", new DBType(DBDatatype.CHAR, 12)),new FunctionParam("foo2", new DBType(DBDatatype.DOUBLE))}); // TEST :: "fct1('blabla', 'blabla2')": [GREATER (because the second param is numeric and Numeric = 10 > String = 01)] - assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{ new StringConstant("blabla"), new StringConstant("blabla2") }))); + assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new StringConstant("blabla"),new StringConstant("blabla2")}))); // TEST :: "fct1('blabla', POINT('COORDSYS', 1.2, 3.4))": [GREATER (same reason ; POINT is considered as a String)] - try { - assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{ new StringConstant("blabla"), new PointFunction(new StringConstant("COORDSYS"), new NumericConstant(1.2), new NumericConstant(3.4)) }))); - } catch(Exception e) { + try{ + assertEquals(1, def.compareTo(new DefaultUDF("fct1", new ADQLOperand[]{new StringConstant("blabla"),new PointFunction(new StringConstant("COORDSYS"), new NumericConstant(1.2), new NumericConstant(3.4))}))); + }catch(Exception e){ e.printStackTrace(); fail(); } // Test with an UNKNOWN numeric type: // TEST :: "fct0(foo)", where foo is a simple UNKNOWN [EQUAL] - FunctionDef def0 = new FunctionDef("fct0", null, new FunctionParam[]{ new FunctionParam("whatever", new DBType(DBDatatype.VARCHAR)) }); + FunctionDef def0 = new FunctionDef("fct0", null, new FunctionParam[]{new FunctionParam("whatever", new DBType(DBDatatype.VARCHAR))}); DefaultDBColumn dbcol = new DefaultDBColumn("foo", new DefaultDBTable("toto")); dbcol.setDatatype(new DBType(DBDatatype.UNKNOWN)); ADQLColumn col = new ADQLColumn("foo"); col.setDBLink(dbcol); - assertEquals(0, def0.compareTo(new DefaultUDF("fct0", new ADQLOperand[]{ col }))); + assertEquals(0, def0.compareTo(new DefaultUDF("fct0", new ADQLOperand[]{col}))); // TEST :: "fct0(foo)", where foo is an UNKNOWN NUMERIC [LESS] dbcol.setDatatype(new DBType(DBDatatype.UNKNOWN_NUMERIC)); - assertEquals(-1, def0.compareTo(new DefaultUDF("fct0", new ADQLOperand[]{ col }))); + assertEquals(-1, def0.compareTo(new DefaultUDF("fct0", new ADQLOperand[]{col}))); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } + } + + @Test + public void testCheckUDFName() { + + // TEST: no function name + String[] missingNames = new String[]{ null, "", " ", " ", " ", "\n" }; + for(String n : missingNames) { + try { + FunctionDef.checkUDFName(n, null); + fail("Impossible to create a UDF with no name!"); + } catch(Exception ex) { + assertEquals(ParseException.class, ex.getClass()); + assertEquals("Invalid UDF name: missing User Defined Function's name!", ex.getMessage()); + } + } + + // TEST: more than one word + try { + FunctionDef.checkUDFName("too many words", null); + fail("Impossible to create a UDF with more than a single word!"); + } catch(Exception ex) { + assertEquals(ParseException.class, ex.getClass()); + assertEquals("Invalid UDF name: too many words (a function name must be a single Regular Identifier)!", ex.getMessage()); + } + + // TEST: not a regular identifier + String[] notRegularIDs = new String[]{ "_foo", "4foo" }; + for(String n : notRegularIDs) { + try { + FunctionDef.checkUDFName(n, null); + fail("Impossible to create a UDF whose the name is not a regular identifier!"); + } catch(Exception ex) { + assertEquals(ParseException.class, ex.getClass()); + assertEquals("Invalid UDF name: \"" + n + "\" is not a Regular Identifier!", ex.getMessage()); + } + } + + // TEST: existing ADQL function + String[] existingFctNames = new String[]{ "point", "POINT", "MaX", "round" }; + for(String n : existingFctNames) { + try { + FunctionDef.checkUDFName(n, null); + fail("Impossible to create a UDF with the name of an existing ADQL function!"); + } catch(Exception ex) { + assertEquals(ParseException.class, ex.getClass()); + assertEquals("Invalid UDF name: \"" + n + "\" already exists in ADQL!", ex.getMessage()); + } + } + + // TEST: reserved ADQL keyword + String[] reservedADQLKeywords = new String[]{ "select", "FroM", "as", "JOIN" }; + for(String n : reservedADQLKeywords) { + try { + FunctionDef.checkUDFName(n, null); + fail("Impossible to create a UDF whose the name is an ADQL reserved keyword!"); + } catch(Exception ex) { + assertEquals(ParseException.class, ex.getClass()); + assertEquals("Invalid UDF name: \"" + n + "\" is an ADQL Reserved Keyword!", ex.getMessage()); + } + } + + // TEST: reserved SQL keyword + String[] reservedSQLKeywords = new String[]{ "Insert", "INTO", "upDate", "date" }; + for(String n : reservedSQLKeywords) { + try { + FunctionDef.checkUDFName(n, null); + fail("Impossible to create a UDF whose the name is an SQL reserved keyword!"); + } catch(Exception ex) { + assertEquals(ParseException.class, ex.getClass()); + assertEquals("Invalid UDF name: \"" + n + "\" is an SQL Reserved Keyword!", ex.getMessage()); + } + } + + final String MSG_LOWER_2_0 = "Invalid UDF name: \"lower\" is an SQL Reserved Keyword!"; + final String MSG_LOWER_2_1 = "Invalid UDF name: \"lower\" already exists in ADQL!"; + + /* TEST: the ADQL version is taken into account (the error message for + * the function LOWER existing only in ADQL-2.1 should be + * different): */ + try { + FunctionDef.checkUDFName("lower", ADQLVersion.V2_0); + fail("LOWER is supposed to be a reserved keyword!"); + } catch(Exception e) { + assertEquals(ParseException.class, e.getClass()); + assertEquals(MSG_LOWER_2_0, e.getMessage()); + } + try { + FunctionDef.checkUDFName("lower", ADQLVersion.V2_1); + fail("LOWER is supposed to be a reserved keyword!"); + } catch(Exception e) { + assertEquals(ParseException.class, e.getClass()); + assertEquals(MSG_LOWER_2_1, e.getMessage()); + } + + // TEST: no ADQL version = default version: + try { + FunctionDef.checkUDFName("lower", null); + fail("LOWER is supposed to be a reserved keyword!"); + } catch(Exception e) { + assertEquals(ParseException.class, e.getClass()); + switch(ADQLParserFactory.DEFAULT_VERSION) { + case V2_0: + assertEquals(MSG_LOWER_2_0, e.getMessage()); + break; + case V2_1: + assertEquals(MSG_LOWER_2_1, e.getMessage()); + break; + default: + fail("New ADQL Version not yet taken into account in this JUnit Test!"); + } + } } @Test diff --git a/test/adql/parser/feature/TestFeatureSet.java b/test/adql/parser/feature/TestFeatureSet.java index 3d256aa..78e050a 100644 --- a/test/adql/parser/feature/TestFeatureSet.java +++ b/test/adql/parser/feature/TestFeatureSet.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.HashSet; import java.util.Iterator; @@ -14,6 +15,7 @@ import org.junit.Test; import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.FunctionDef; +import adql.parser.ParseException; import adql.query.ColumnReference; import adql.query.operand.function.geometry.BoxFunction; import adql.query.operand.function.geometry.PolygonFunction; @@ -216,20 +218,25 @@ public class TestFeatureSet { @Test public void testUnsupportAll() { - FeatureSet set = new FeatureSet(true); + try { + FeatureSet set = new FeatureSet(true); - /* here is a custom Language Feature (i.e. not part of the - * availableFeatures list): */ - set.support(new LanguageFeature(new FunctionDef("foo", new DBType(DBDatatype.SMALLINT), new FunctionDef.FunctionParam[]{ new FunctionDef.FunctionParam("", new DBType(DBDatatype.VARCHAR)) }))); + /* here is a custom Language Feature (i.e. not part of the + * availableFeatures list): */ + set.support(new LanguageFeature(new FunctionDef("foo", new DBType(DBDatatype.SMALLINT), new FunctionDef.FunctionParam[]{ new FunctionDef.FunctionParam("", new DBType(DBDatatype.VARCHAR)) }))); - // unsupport all currently supported features: - set.unsupportAll(); + // unsupport all currently supported features: + set.unsupportAll(); - // ensure the list of supported features is really empty: - assertEquals(0, set.supportedFeatures.size()); + // ensure the list of supported features is really empty: + assertEquals(0, set.supportedFeatures.size()); - // ...and that no non-declared UDF is allowed: - assertFalse(set.isAnyUdfAllowed()); + // ...and that no non-declared UDF is allowed: + assertFalse(set.isAnyUdfAllowed()); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } } @Test diff --git a/test/adql/query/TestADQLQuery.java b/test/adql/query/TestADQLQuery.java index 272622e..6478cb1 100644 --- a/test/adql/query/TestADQLQuery.java +++ b/test/adql/query/TestADQLQuery.java @@ -14,6 +14,7 @@ import org.junit.Test; import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.FunctionDef; +import adql.parser.ParseException; import adql.query.constraint.Comparison; import adql.query.constraint.ComparisonOperator; import adql.query.constraint.ConstraintsGroup; @@ -50,7 +51,7 @@ public class TestADQLQuery { private List<ADQLColumn> typeObjColumns = new ArrayList<ADQLColumn>(3); @Before - public void setUp(){ + public void setUp() { query = new ADQLQuery(); columns.clear(); typeObjColumns.clear(); @@ -102,12 +103,12 @@ public class TestADQLQuery { } @Test - public void testADQLQuery(){ + public void testADQLQuery() { assertEquals("SELECT (O.nameObj || ' (' || O.typeObj || ')') AS Nom objet , O.ra , O.dec\nFROM truc.ObsCore AS O\nWHERE ra/dec > 1 AND (typeObj = 'Star' OR typeObj LIKE 'Galaxy*')\nORDER BY 1 DESC", query.toADQL()); } @Test - public void testSearch(){ + public void testSearch() { ISearchHandler sHandler = new SearchColumnHandler(false); Iterator<ADQLObject> results = query.search(sHandler); assertEquals(columns.size(), sHandler.getNbMatch()); @@ -116,15 +117,15 @@ public class TestADQLQuery { } @Test - public void testReplace(){ - IReplaceHandler sHandler = new SimpleReplaceHandler(false, false){ + public void testReplace() { + IReplaceHandler sHandler = new SimpleReplaceHandler(false, false) { @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { return (obj instanceof ADQLColumn) && (((ADQLColumn)obj).getColumnName().equalsIgnoreCase("typeObj")); } @Override - public ADQLObject getReplacer(ADQLObject objToReplace) throws UnsupportedOperationException{ + public ADQLObject getReplacer(ADQLObject objToReplace) throws UnsupportedOperationException { return new ADQLColumn("NewTypeObj"); } }; @@ -138,7 +139,7 @@ public class TestADQLQuery { } @Test - public void testTypeResultingColumns(){ + public void testTypeResultingColumns() { ADQLQuery query = new ADQLQuery(); query.setFrom(new ADQLTable("foo")); ClauseSelect select = new ClauseSelect(); @@ -156,12 +157,12 @@ public class TestADQLQuery { assertEquals(DBDatatype.UNKNOWN_NUMERIC, query.getResultingColumns()[0].getDatatype().type); // Test with a math function: - try{ + try { select.clear(); select.add(new MathFunction(MathFunctionType.SQRT, new ADQLColumn("col1"))); assertEquals(1, query.getResultingColumns().length); assertEquals(DBDatatype.UNKNOWN_NUMERIC, query.getResultingColumns()[0].getDatatype().type); - }catch(Exception ex){ + } catch(Exception ex) { ex.printStackTrace(); fail("The mathematical function SQRT is well defined. This error should have occurred."); } @@ -188,20 +189,20 @@ public class TestADQLQuery { assertEquals(DBDatatype.VARCHAR, query.getResultingColumns()[0].getDatatype().type); // Test with a POINT: - try{ + try { select.clear(); select.add(new PointFunction(new StringConstant(""), new ADQLColumn("ra"), new ADQLColumn("dec"))); select.add(new CentroidFunction(new GeometryValue<GeometryFunction>(new ADQLColumn("aRegion")))); assertEquals(2, query.getResultingColumns().length); for(int i = 0; i < 2; i++) assertEquals(DBDatatype.POINT, query.getResultingColumns()[i].getDatatype().type); - }catch(Exception ex){ + } catch(Exception ex) { ex.printStackTrace(); fail("The POINT function is well defined. This error should have occurred."); } // Test with a REGION (CIRCLE, BOX, POLYGON and REGION functions): - try{ + try { select.clear(); select.add(new CircleFunction(new StringConstant(""), new ADQLColumn("ra"), new ADQLColumn("dec"), new NumericConstant(1))); select.add(new BoxFunction(new StringConstant(""), new ADQLColumn("ra"), new ADQLColumn("dec"), new NumericConstant(10), new NumericConstant(20))); @@ -217,7 +218,7 @@ public class TestADQLQuery { assertEquals(4, query.getResultingColumns().length); for(int i = 0; i < 4; i++) assertEquals(DBDatatype.REGION, query.getResultingColumns()[i].getDatatype().type); - }catch(Exception ex){ + } catch(Exception ex) { ex.printStackTrace(); fail("The geometrical functions are well defined. This error should have occurred."); } @@ -229,12 +230,16 @@ public class TestADQLQuery { assertNull(query.getResultingColumns()[0].getDatatype()); // Test with a UDF having a definition: - select.clear(); - DefaultUDF udf = new DefaultUDF("foo", new ADQLOperand[0]); - udf.setDefinition(new FunctionDef("foo", new DBType(DBDatatype.INTEGER))); - select.add(udf); - assertEquals(1, query.getResultingColumns().length); - assertEquals(DBDatatype.INTEGER, query.getResultingColumns()[0].getDatatype().type); - + try { + select.clear(); + DefaultUDF udf = new DefaultUDF("foo", new ADQLOperand[0]); + udf.setDefinition(new FunctionDef("foo", new DBType(DBDatatype.INTEGER))); + select.add(udf); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.INTEGER, query.getResultingColumns()[0].getDatatype().type); + } catch(ParseException pe) { + pe.printStackTrace(); + fail("Failed initialization because of an invalid UDF declaration! Cause: (cf console)"); + } } } -- GitLab