Skip to content
Snippets Groups Projects
Commit b49f8160 authored by gmantele's avatar gmantele
Browse files

[ADQL,TAP] Fix 2 bugs notified by M.Taylor:

1/ Bad parsing of UDF's parameter type or return type. Database types whose the
name has a space (e.g. 'double precision') were not accepted. These type names
should be accepted according to TAPRegExt.
2/ An error message thrown by the DBChecker has been modified for the cases
the type of a parameter is unknown. Before the returned type was NUMERIC. Now,
it will be 'param' followed by the parameter index (e.g. 'param1').
parent 1234f1a1
No related branches found
No related tags found
No related merge requests found
......@@ -49,7 +49,7 @@ import adql.query.operand.function.UserDefinedFunction;
* </p>
*
* @author Gr&eacute;gory Mantelet (ARI)
* @version 1.4 (07/2015)
* @version 1.4 (08/2015)
*
* @since 1.3
*/
......@@ -59,7 +59,7 @@ public class FunctionDef implements Comparable<FunctionDef> {
/** Rough regular expression for a function return type or a parameter type.
* The exact type is not checked here ; just the type name syntax is tested, not its value.
* This regular expression allows a type to have exactly one parameter (which is generally the length of a character or binary string. */
protected final static String typeRegExp = "([a-zA-Z]+[0-9a-zA-Z]*)(\\(\\s*([0-9]+)\\s*\\))?";
protected final static String typeRegExp = "([a-zA-Z]+[ 0-9a-zA-Z]*)(\\(\\s*([0-9]+)\\s*\\))?";
/** Rough regular expression for a function parameters' list. */
protected final static String fctParamsRegExp = "\\s*[^,]+\\s*(,\\s*[^,]+\\s*)*";
/** Rough regular expression for a function parameter: a name (see {@link #regularIdentifierRegExp}) and a type (see {@link #typeRegExp}). */
......@@ -348,10 +348,12 @@ public class FunctionDef implements Comparable<FunctionDef> {
* <pre>{fctName}([{param1Name} {param1Type}, ...])[ -> {returnType}]</pre>
*
* <p>
* Allowed parameter types and return types should be one the types listed by the UPLOAD section of the TAP recommendation document.
* <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 DBType}.
* 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.
* 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.
......@@ -426,7 +428,8 @@ public class FunctionDef implements Comparable<FunctionDef> {
*
* @param datatype String representation of a datatype.
* <i>Note: This string must not contain the length parameter or any other parameter.
* These latter should have been separated from the datatype before calling this function.</i>
* These latter should have been separated from the datatype before calling this function.
* It can however contain space(s) in first, last or intern position.</i>
* @param length Length of this datatype.
* <i>Note: This length will be used only for binary (BINARY and VARBINARY)
* and character (CHAR and VARCHAR) types.</i>
......@@ -438,6 +441,9 @@ public class FunctionDef implements Comparable<FunctionDef> {
if (datatype == null)
return null;
// Remove leading and trailing spaces and replace each inner serie of spaces by just one space:
datatype = datatype.trim().replaceAll(" +", " ");
try{
// Try to find a corresponding DBType item:
DBDatatype dbDatatype = DBDatatype.valueOf(datatype.toUpperCase());
......@@ -456,31 +462,29 @@ public class FunctionDef implements Comparable<FunctionDef> {
}catch(IllegalArgumentException iae){
// If there's no corresponding DBType item, try to find a match among the most used DB types:
datatype = datatype.toLowerCase();
if (datatype.equals("bool") || datatype.equals("boolean") || datatype.equals("short"))
return new DBType(DBDatatype.SMALLINT);
else if (datatype.equals("int2"))
if (datatype.equals("bool") || datatype.equals("boolean") || datatype.equals("short") || datatype.equals("int2") || datatype.equals("smallserial") || datatype.equals("serial2"))
return new DBType(DBDatatype.SMALLINT);
else if (datatype.equals("int") || datatype.equals("int4"))
else if (datatype.equals("int") || datatype.equals("int4") || datatype.equals("serial") || datatype.equals("serial4"))
return new DBType(DBDatatype.INTEGER);
else if (datatype.equals("long") || datatype.equals("number") || datatype.equals("bigint") || datatype.equals("int8"))
else if (datatype.equals("long") || datatype.equals("number") || datatype.equals("int8") || datatype.equals("bigserial") || datatype.equals("bigserial8"))
return new DBType(DBDatatype.BIGINT);
else if (datatype.equals("float") || datatype.equals("float4"))
return new DBType(DBDatatype.REAL);
else if (datatype.equals("numeric") || datatype.equals("float8"))
else if (datatype.equals("numeric") || datatype.equals("float8") || datatype.equals("double precision"))
return new DBType(DBDatatype.DOUBLE);
else if (datatype.equals("byte") || datatype.equals("raw"))
else if (datatype.equals("bit") || datatype.equals("byte") || datatype.equals("raw"))
return new DBType(DBDatatype.BINARY, length);
else if (datatype.equals("unsignedByte"))
else if (datatype.equals("unsignedByte") || datatype.equals("bit varying") || datatype.equals("varbit"))
return new DBType(DBDatatype.VARBINARY, length);
else if (datatype.equals("character"))
return new DBType(DBDatatype.CHAR, length);
else if (datatype.equals("string") || datatype.equals("varchar2"))
else if (datatype.equals("string") || datatype.equals("varchar2") || datatype.equals("character varying"))
return new DBType(DBDatatype.VARCHAR, length);
else if (datatype.equals("bytea"))
return new DBType(DBDatatype.BLOB);
else if (datatype.equals("text"))
return new DBType(DBDatatype.CLOB);
else if (datatype.equals("date") || datatype.equals("time"))
else if (datatype.equals("date") || datatype.equals("time") || datatype.equals("timetz") || datatype.equals("timestamptz"))
return new DBType(DBDatatype.TIMESTAMP);
else if (datatype.equals("position"))
return new DBType(DBDatatype.POINT);
......
......@@ -27,7 +27,7 @@ import adql.query.operand.function.ADQLFunction;
* Exception thrown when a function can not be resolved by the library.
*
* @author Gr&eacute;gory Mantelet (ARI)
* @version 1.4 (06/2015)
* @version 1.4 (08/2015)
* @since 1.3
*/
public class UnresolvedFunctionException extends ParseException {
......@@ -100,7 +100,8 @@ public class UnresolvedFunctionException extends ParseException {
*
* <p><i>Note 1:
* A parameter type can be either "NUMERIC", "STRING" or "GEOMETRY". In order to be the most generic has possible,
* no more precision about a type is returned here. If the parameter is none of these type kinds, "???" is returned.
* no more precision about a type is returned here. If the parameter is none of these type kinds, "param" suffixed
* by the parameter index (e.g. "param1") is returned.
* </i></p>
*
* <p><i>Note 2:
......@@ -118,14 +119,16 @@ public class UnresolvedFunctionException extends ParseException {
StringBuffer buf = new StringBuffer(fct.getName().toLowerCase());
buf.append('(');
for(int i = 0; i < fct.getNbParameters(); i++){
if (fct.getParameter(i).isNumeric())
if (fct.getParameter(i).isNumeric() && fct.getParameter(i).isString() && fct.getParameter(i).isGeometry())
buf.append("param").append(i + 1);
else if (fct.getParameter(i).isNumeric())
buf.append("NUMERIC");
else if (fct.getParameter(i).isString())
buf.append("STRING");
else if (fct.getParameter(i).isGeometry())
buf.append("GEOMETRY");
else
buf.append("???");
buf.append("param").append(i + 1);
if ((i + 1) < fct.getNbParameters())
buf.append(", ");
......
......@@ -155,6 +155,30 @@ public class TestDBChecker {
assertEquals("Unresolved function: \"toto('blabla')\"! No UDF has been defined or found with the signature: toto(STRING).", ex.getErrors().next().getMessage());
}
// Test but with at least one column parameter:
try{
parser.parseQuery("SELECT toto(colS) FROM foo;");
fail("This query contains an unknown UDF signature (the fct toto is declared with no parameter): this test should have failed!");
}catch(ParseException e){
assertTrue(e instanceof UnresolvedIdentifiersException);
UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
assertEquals(1, ex.getNbErrors());
assertEquals("Unresolved function: \"toto(colS)\"! No UDF has been defined or found with the signature: toto(STRING).", ex.getErrors().next().getMessage());
}
// Test but with at least one unknown column parameter:
try{
parser.parseQuery("SELECT toto(whatami) FROM foo;");
fail("This query contains an unknown UDF signature (the fct toto is declared with no parameter): this test should have failed!");
}catch(ParseException e){
assertTrue(e instanceof UnresolvedIdentifiersException);
UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)e;
assertEquals(2, ex.getNbErrors());
Iterator<ParseException> errors = ex.getErrors();
assertEquals("Unknown column \"whatami\" !", errors.next().getMessage());
assertEquals("Unresolved function: \"toto(whatami)\"! No UDF has been defined or found with the signature: toto(param1).", errors.next().getMessage());
}
// 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);
......
......@@ -135,6 +135,20 @@ public class TestFunctionDef {
fail("Wrong type parsing!");
}
// TYPE WITH SPACES AND/OR PARAMETER test:
try{
assertEquals("foo() -> DOUBLE", FunctionDef.parse("foo() -> double precision").toString());
assertEquals("foo(bar DOUBLE)", FunctionDef.parse("foo(bar DOUBLE Precision )").toString());
assertEquals("foo() -> VARCHAR", FunctionDef.parse("foo() -> character varying").toString());
assertEquals("foo(bar VARBINARY)", FunctionDef.parse("foo(bar bit varying)").toString());
assertEquals("foo(bar VARCHAR(12))", FunctionDef.parse("foo(bar varchar (12))").toString());
assertEquals("foo(bar VARCHAR(12))", FunctionDef.parse("foo(bar character varying (12))").toString());
assertEquals("foo() -> DOUBLE", FunctionDef.parse("foo() -> double precision (2)").toString());
}catch(Exception ex){
ex.printStackTrace(System.err);
fail("Wrong type parsing!");
}
// WRONG string definitions:
try{
FunctionDef.parse("123()");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment