From 0003e3436a0684456102015cfe8babbc86a864e0 Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Fri, 4 Mar 2016 20:10:26 +0100 Subject: [PATCH] [ADQL] Set a type to a query's resulting column when it is not originally a column. This is easily possible for concatenations, string constants and User Defined Functions having a FunctionDef. A new special datatype was needed for numeric functions and operations: UNKNOWN_NUMERIC. This special type can not be set with FunctionDef.parse(...) and it behaves exactly like the type UNKNOWN, except that DBType.isNumeric() returns true (as .isUnknown()). Thus, while writing the metadata of a result in TAP, nothing changes: an UNKNOWN_NUMERIC type will be processed similarly as an UNKNOWN type: to use the type returned from the database ResultSet or to set VARCHAR. (no modification of TAP was needed for that) --- src/adql/db/DBType.java | 47 ++++++---- src/adql/db/FunctionDef.java | 4 +- src/adql/query/ADQLQuery.java | 45 +++++++++- src/adql/query/operand/ADQLColumn.java | 10 +-- test/adql/TestADQLQuery.java | 115 +++++++++++++++++++++++++ test/adql/db/TestFunctionDef.java | 14 +++ test/adql/parser/UnknownTypes.java | 35 ++++++-- 7 files changed, 233 insertions(+), 37 deletions(-) diff --git a/src/adql/db/DBType.java b/src/adql/db/DBType.java index ca27139..b81d608 100644 --- a/src/adql/db/DBType.java +++ b/src/adql/db/DBType.java @@ -16,11 +16,10 @@ package adql.db; * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2014-2015 - Astronomisches Rechen Institut (ARI) + * Copyright 2014-2016 - Astronomisches Rechen Institut (ARI) */ /** - * * <p> * Describe a full column type as it is described in the IVOA document of TAP. * Thus, this object contains 2 attributes: <code>type</code> (or datatype) and <code>length</code> (or size). @@ -32,7 +31,7 @@ package adql.db; * It is used to set the attribute type/datatype of this class.</p> * * @author Grégory Mantelet (ARI) - * @version 1.4 (08/2015) + * @version 1.4 (03/2016) * @since 1.3 */ public class DBType { @@ -41,13 +40,21 @@ public class DBType { * List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD). * * @author Grégory Mantelet (ARI) - * @version 1.4 (06/2015) + * @version 1.4 (03/2016) * @since 1.3 */ public static enum DBDatatype{ SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION, - /** @since 1.4 */ - UNKNOWN; + /** Type to use when the precise datatype is unknown. + * @since 1.4 */ + UNKNOWN, + /** <p>Type to use when the type is known as numeric but there is no precise datatype + * (e.g. double, float, integer, ...).</p> + * <p>It is particularly used when creating a {@link DefaultDBColumn} from an ADQL function + * or operation while listing resulting columns of a sub-query.</p> + * <p>This type is similar to {@link #UNKNOWN}.</p> + * @since 1.4 */ + UNKNOWN_NUMERIC; /** String to return when {@link #toString()} is called. * @since 1.4*/ @@ -60,11 +67,11 @@ public class DBType { /** * <p>This function lets define the name of the type as provided - * <b>ONLY FOR {@link #UNKNOWN} {@link DBDatatype}</b>.</p> + * <b>ONLY FOR {@link #UNKNOWN} and {@link #UNKNOWN_NUMERIC} {@link DBDatatype}s</b>.</p> * * <p><i><b>Important:</b> - * If this {@link DBDatatype} is not {@link #UNKNOWN} or if the given name is NULL or empty, - * this function has no effect. + * If this {@link DBDatatype} is not {@link #UNKNOWN} or {@link #UNKNOWN_NUMERIC} or + * if the given name is NULL or empty, this function has no effect. * </i></p> * * @param typeName User type name. @@ -72,7 +79,7 @@ public class DBType { * @since 1.4 */ public void setCustomType(final String typeName){ - if (this == UNKNOWN && typeName != null && typeName.trim().length() > 0) + if ((this == UNKNOWN || this == UNKNOWN_NUMERIC) && typeName != null && typeName.trim().length() > 0) strExp = "?" + typeName.trim() + "?"; } } @@ -121,7 +128,8 @@ public class DBType { * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a numeric although the type is unknown * and is in fact not really a numeric, this function will return <code>false</code> if the type is - * {@link DBDatatype#UNKNOWN UNKNOWN}. + * {@link DBDatatype#UNKNOWN UNKNOWN} <b>BUT</b> <code>true</code> if + * {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. * </i></p> * * @return <code>true</code> if this type is a numeric, <code>false</code> otherwise. @@ -138,6 +146,7 @@ public class DBType { case BINARY: case VARBINARY: case BLOB: + case UNKNOWN_NUMERIC: return true; default: return false; @@ -155,7 +164,7 @@ public class DBType { * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a binary although the type is unknown * and is in fact not really a binary, this function will return <code>false</code> if the type is - * {@link DBDatatype#UNKNOWN UNKNOWN}. + * {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. * </i></p> * * @return <code>true</code> if this type is a binary, <code>false</code> otherwise. @@ -183,7 +192,7 @@ public class DBType { * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a string although the type is unknown * and is in fact not really a string, this function will return <code>false</code> if the type is - * {@link DBDatatype#UNKNOWN UNKNOWN}. + * {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC} * </i></p> * * @return <code>true</code> if this type is a string, <code>false</code> otherwise. @@ -211,7 +220,7 @@ public class DBType { * Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * But, in order to avoid incorrect operation while expecting a geometry although the type is unknown * and is in fact not really a geometry, this function will return <code>false</code> if the type is - * {@link DBDatatype#UNKNOWN UNKNOWN}. + * {@link DBDatatype#UNKNOWN UNKNOWN} or {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. * </i></p> * * @return <code>true</code> if this type is a geometry, <code>false</code> otherwise. @@ -223,8 +232,8 @@ public class DBType { /** * <p>Tell whether this type has been resolved or not.</p> * - * <p><i>Concerned type: - * {@link DBDatatype#UNKNOWN UNKNOWN}. + * <p><i>Concerned types: + * {@link DBDatatype#UNKNOWN UNKNOWN} and {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}. * </i></p> * * @return <code>true</code> if this type has NOT been resolved, <code>false</code> otherwise. @@ -232,7 +241,7 @@ public class DBType { * @since 1.4 */ public boolean isUnknown(){ - return type == DBDatatype.UNKNOWN; + return type == DBDatatype.UNKNOWN || type == DBDatatype.UNKNOWN_NUMERIC; } /** @@ -240,8 +249,8 @@ public class DBType { * * <p> * Two {@link DBType}s are said compatible if they are both binary, numeric, geometric or string. - * If one of the two types is {@link DBDatatype#UNKNOWN unknown}, this function will consider them - * as compatible and will return <code>true</code>. + * If one of the two types is {@link DBDatatype#UNKNOWN unknown} or {@link DBDatatype#UNKNOWN_NUMERIC unknown_numeric}, + * this function will consider them as compatible and will return <code>true</code>. * </p> * * @param t The type to compare to. diff --git a/src/adql/db/FunctionDef.java b/src/adql/db/FunctionDef.java index b6a160b..c334c8b 100644 --- a/src/adql/db/FunctionDef.java +++ b/src/adql/db/FunctionDef.java @@ -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}). */ @@ -350,7 +350,7 @@ public class FunctionDef implements Comparable<FunctionDef> { * <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 DBType}. + * 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. diff --git a/src/adql/query/ADQLQuery.java b/src/adql/query/ADQLQuery.java index 1372e71..805b680 100644 --- a/src/adql/query/ADQLQuery.java +++ b/src/adql/query/ADQLQuery.java @@ -16,7 +16,7 @@ package adql.query; * You should have received a copy of the GNU Lesser General Public License * along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS), + * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ @@ -25,12 +25,20 @@ import java.util.Iterator; import java.util.NoSuchElementException; import adql.db.DBColumn; +import adql.db.DBType; +import adql.db.DBType.DBDatatype; import adql.db.DefaultDBColumn; import adql.parser.ADQLParser; import adql.parser.ParseException; import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; +import adql.query.operand.function.DefaultUDF; +import adql.query.operand.function.geometry.BoxFunction; +import adql.query.operand.function.geometry.CircleFunction; +import adql.query.operand.function.geometry.PointFunction; +import adql.query.operand.function.geometry.PolygonFunction; +import adql.query.operand.function.geometry.RegionFunction; import adql.search.ISearchHandler; /** @@ -38,7 +46,7 @@ import adql.search.ISearchHandler; * <p>The resulting object of the {@link ADQLParser} is an object of this class.</p> * * @author Grégory Mantelet (CDS;ARI) - * @version 1.4 (06/2015) + * @version 1.4 (03/2016) */ public class ADQLQuery implements ADQLObject { @@ -311,19 +319,50 @@ public class ADQLQuery implements ADQLObject { // Here, this error should not occur any more, since it must have been caught by the DBChecker! } }else{ + // Create the DBColumn: DBColumn col = null; + // ...whose the name will be set with the SELECT item's alias: if (item.hasAlias()){ if (operand instanceof ADQLColumn && ((ADQLColumn)operand).getDBLink() != null){ col = ((ADQLColumn)operand).getDBLink(); col = col.copy(col.getDBName(), item.getAlias(), col.getTable()); }else col = new DefaultDBColumn(item.getAlias(), null); - }else{ + } + // ...or whose the name will be the name of the SELECT item: + else{ if (operand instanceof ADQLColumn && ((ADQLColumn)operand).getDBLink() != null) col = ((ADQLColumn)operand).getDBLink(); if (col == null) col = new DefaultDBColumn(item.getName(), null); } + + /* For columns created by default (from functions and operations generally), + * set the adequate type if known: */ + // CASE: Well-defined UDF + if (operand instanceof DefaultUDF && ((DefaultUDF)operand).getDefinition() != null){ + DBType type = ((DefaultUDF)operand).getDefinition().returnType; + ((DefaultDBColumn)col).setDatatype(type); + } + // CASE: Point type: + else if (operand instanceof PointFunction) + ((DefaultDBColumn)col).setDatatype(new DBType(DBDatatype.POINT)); + // CASE: Region type: + else if (operand instanceof RegionFunction || operand instanceof CircleFunction || operand instanceof BoxFunction || operand instanceof PolygonFunction) + ((DefaultDBColumn)col).setDatatype(new DBType(DBDatatype.REGION)); + // CASE: String and numeric types + else if (col instanceof DefaultDBColumn && col.getDatatype() == null && operand.isNumeric() != operand.isString()){ + // CASE: String types + if (operand.isString()) + ((DefaultDBColumn)col).setDatatype(new DBType(DBDatatype.VARCHAR)); + // CASE: Numeric types: + /* Note: a little special case here since a numeric could be a real, double, integer, or anything + * else and that we don't know precisely here. So we set the special UNKNOWN NUMERIC type. */ + else + ((DefaultDBColumn)col).setDatatype(new DBType(DBDatatype.UNKNOWN_NUMERIC)); + } + + // Add the new column to the list: columns.add(col); } } diff --git a/src/adql/query/operand/ADQLColumn.java b/src/adql/query/operand/ADQLColumn.java index af8e9d5..5542f50 100644 --- a/src/adql/query/operand/ADQLColumn.java +++ b/src/adql/query/operand/ADQLColumn.java @@ -16,7 +16,7 @@ package adql.query.operand; * You should have received a copy of the GNU Lesser General Public License * along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS), + * Copyright 2012-2016 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ @@ -32,7 +32,7 @@ import adql.query.from.ADQLTable; * Represents the complete (literal) reference to a column ({schema(s)}.{table}.{column}). * * @author Grégory Mantelet (CDS;ARI) - * @version 1.4 (06/2015) + * @version 1.4 (03/2016) */ public class ADQLColumn implements ADQLOperand, UnknownType { @@ -469,17 +469,17 @@ public class ADQLColumn implements ADQLOperand, UnknownType { @Override public boolean isNumeric(){ - return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isUnknown() || dbLink.getDatatype().isNumeric()); + return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isNumeric() || dbLink.getDatatype().isUnknown()); } @Override public boolean isString(){ - return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isUnknown() || dbLink.getDatatype().isString()); + return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isString() || (dbLink.getDatatype().isUnknown() && !dbLink.getDatatype().isNumeric())); } @Override public boolean isGeometry(){ - return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isUnknown() || dbLink.getDatatype().isGeometry()); + return (dbLink == null || dbLink.getDatatype() == null || dbLink.getDatatype().isGeometry() || (dbLink.getDatatype().isUnknown() && !dbLink.getDatatype().isNumeric())); } @Override diff --git a/test/adql/TestADQLQuery.java b/test/adql/TestADQLQuery.java index 54a704e..1ea715b 100644 --- a/test/adql/TestADQLQuery.java +++ b/test/adql/TestADQLQuery.java @@ -1,6 +1,8 @@ package adql; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Iterator; @@ -9,6 +11,9 @@ import java.util.List; import org.junit.Before; import org.junit.Test; +import adql.db.DBType; +import adql.db.DBType.DBDatatype; +import adql.db.FunctionDef; import adql.query.ADQLObject; import adql.query.ADQLOrder; import adql.query.ADQLQuery; @@ -21,12 +26,23 @@ import adql.query.constraint.ComparisonOperator; import adql.query.constraint.ConstraintsGroup; import adql.query.from.ADQLTable; import adql.query.operand.ADQLColumn; +import adql.query.operand.ADQLOperand; import adql.query.operand.Concatenation; import adql.query.operand.NumericConstant; import adql.query.operand.Operation; import adql.query.operand.OperationType; import adql.query.operand.StringConstant; import adql.query.operand.WrappedOperand; +import adql.query.operand.function.DefaultUDF; +import adql.query.operand.function.MathFunction; +import adql.query.operand.function.MathFunctionType; +import adql.query.operand.function.SQLFunction; +import adql.query.operand.function.SQLFunctionType; +import adql.query.operand.function.geometry.BoxFunction; +import adql.query.operand.function.geometry.CircleFunction; +import adql.query.operand.function.geometry.PointFunction; +import adql.query.operand.function.geometry.PolygonFunction; +import adql.query.operand.function.geometry.RegionFunction; import adql.search.IReplaceHandler; import adql.search.ISearchHandler; import adql.search.SearchColumnHandler; @@ -124,4 +140,103 @@ public class TestADQLQuery { assertEquals(expectedCol, results.next()); assertEquals("SELECT (O.nameObj || ' (' || NewTypeObj || ')') AS Nom objet , O.ra , O.dec\nFROM truc.ObsCore AS O\nWHERE ra/dec > 1 AND (NewTypeObj = 'Star' OR NewTypeObj LIKE 'Galaxy*')\nORDER BY 1 DESC", query.toADQL()); } + + @Test + public void testTypeResultingColumns(){ + ADQLQuery query = new ADQLQuery(); + query.setFrom(new ADQLTable("foo")); + ClauseSelect select = new ClauseSelect(); + query.setSelect(select); + + // Test with a numeric constant: + select.add(new NumericConstant(2.3)); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.UNKNOWN_NUMERIC, query.getResultingColumns()[0].getDatatype().type); + + // Test with a math operation: + select.clear(); + select.add(new Operation(new Operation(new NumericConstant(2), OperationType.MULT, new NumericConstant(3.14)), OperationType.DIV, new NumericConstant(5))); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.UNKNOWN_NUMERIC, query.getResultingColumns()[0].getDatatype().type); + + // Test with a math function: + 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){ + ex.printStackTrace(); + fail("The mathematical function SQRT is well defined. This error should have occurred."); + } + + // Test with an aggregation function: + select.clear(); + select.add(new SQLFunction(SQLFunctionType.SUM, new ADQLColumn("col1"))); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.UNKNOWN_NUMERIC, query.getResultingColumns()[0].getDatatype().type); + + // Test with a string constant: + select.clear(); + select.add(new StringConstant("blabla")); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.VARCHAR, query.getResultingColumns()[0].getDatatype().type); + + // Test with a concatenation: + select.clear(); + Concatenation concat = new Concatenation(); + concat.add(new StringConstant("super ")); + concat.add(new ADQLColumn("foo", "col")); + select.add(concat); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.VARCHAR, query.getResultingColumns()[0].getDatatype().type); + + // Test with a POINT: + try{ + select.clear(); + select.add(new PointFunction(new StringConstant(""), new ADQLColumn("ra"), new ADQLColumn("dec"))); + assertEquals(1, query.getResultingColumns().length); + assertEquals(DBDatatype.POINT, query.getResultingColumns()[0].getDatatype().type); + }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{ + 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))); + ADQLOperand[] points = new ADQLOperand[6]; + points[0] = new ADQLColumn("point1"); + points[1] = new ADQLColumn("point2"); + points[2] = new ADQLColumn("point3"); + points[3] = new ADQLColumn("point4"); + points[4] = new ADQLColumn("point5"); + points[5] = new ADQLColumn("point6"); + select.add(new PolygonFunction(new StringConstant(""), points)); + select.add(new RegionFunction(new StringConstant("CIRCLE '' ra dec 2.3"))); + assertEquals(4, query.getResultingColumns().length); + for(int i = 0; i < 4; i++) + assertEquals(DBDatatype.REGION, query.getResultingColumns()[i].getDatatype().type); + }catch(Exception ex){ + ex.printStackTrace(); + fail("The geometrical functions are well defined. This error should have occurred."); + } + + // Test with a UDF having no definition: + select.clear(); + select.add(new DefaultUDF("foo", new ADQLOperand[0])); + assertEquals(1, query.getResultingColumns().length); + 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); + + } } diff --git a/test/adql/db/TestFunctionDef.java b/test/adql/db/TestFunctionDef.java index e9ea725..db30b5c 100644 --- a/test/adql/db/TestFunctionDef.java +++ b/test/adql/db/TestFunctionDef.java @@ -10,6 +10,7 @@ import org.junit.Test; import adql.db.DBType.DBDatatype; import adql.db.FunctionDef.FunctionParam; import adql.parser.ParseException; +import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLOperand; import adql.query.operand.NumericConstant; import adql.query.operand.StringConstant; @@ -62,6 +63,7 @@ public class TestFunctionDef { case UNKNOWN: assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric); break; + case UNKNOWN_NUMERIC: default: assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric); } @@ -338,6 +340,18 @@ public class TestFunctionDef { 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))}); + 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}))); + // 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}))); } } diff --git a/test/adql/parser/UnknownTypes.java b/test/adql/parser/UnknownTypes.java index 3ecd60d..5f311a5 100644 --- a/test/adql/parser/UnknownTypes.java +++ b/test/adql/parser/UnknownTypes.java @@ -70,7 +70,7 @@ public class UnknownTypes { @Test public void testForColumns(){ - final String QUERY_TXT = "SELECT FOO(C1), FOO(C2), C1, C2, C3 FROM T1"; + final String QUERY_TXT = "SELECT FOO(C1), FOO(C2), FOO(C4), C1, C2, C3, C4 FROM T1"; try{ // Create the parser: @@ -81,6 +81,7 @@ public class UnknownTypes { table1.addColumn(new DefaultDBColumn("C1", table1)); table1.addColumn(new DefaultDBColumn("C2", new DBType(DBDatatype.UNKNOWN), table1)); table1.addColumn(new DefaultDBColumn("C3", new DBType(DBDatatype.VARCHAR), table1)); + table1.addColumn(new DefaultDBColumn("C4", new DBType(DBDatatype.UNKNOWN_NUMERIC), table1)); Collection<DBTable> tList = Arrays.asList(new DBTable[]{table1}); // Check the type of the column T1.C1: @@ -98,6 +99,16 @@ public class UnknownTypes { assertFalse(col.getDatatype().isGeometry()); assertEquals("UNKNOWN", col.getDatatype().toString()); + // Check the type of the column T1.C4: + col = table1.getColumn("C4", true); + assertNotNull(col); + assertNotNull(col.getDatatype()); + assertTrue(col.getDatatype().isUnknown()); + assertTrue(col.getDatatype().isNumeric()); + assertFalse(col.getDatatype().isString()); + assertFalse(col.getDatatype().isGeometry()); + assertEquals("UNKNOWN_NUMERIC", col.getDatatype().toString()); + // Define a UDF, and allow all geometrical functions and coordinate systems: FunctionDef udf1 = FunctionDef.parse("FOO(x INTEGER) -> INTEGER"); Collection<FunctionDef> udfList = Arrays.asList(new FunctionDef[]{udf1}); @@ -122,18 +133,26 @@ public class UnknownTypes { assertTrue(pq.getSelect().get(1).getOperand().isNumeric()); assertFalse(pq.getSelect().get(1).getOperand().isString()); assertFalse(pq.getSelect().get(1).getOperand().isGeometry()); - // isNumeric() = isString() = isGeometry() for C1 + // isNumeric() = true for FOO(C4), but false for the others assertTrue(pq.getSelect().get(2).getOperand().isNumeric()); - assertTrue(pq.getSelect().get(2).getOperand().isString()); - assertTrue(pq.getSelect().get(2).getOperand().isGeometry()); - // isNumeric() = isString() = isGeometry() for C2 + assertFalse(pq.getSelect().get(2).getOperand().isString()); + assertFalse(pq.getSelect().get(2).getOperand().isGeometry()); + // isNumeric() = isString() = isGeometry() for C1 assertTrue(pq.getSelect().get(3).getOperand().isNumeric()); assertTrue(pq.getSelect().get(3).getOperand().isString()); assertTrue(pq.getSelect().get(3).getOperand().isGeometry()); - // isString() = true for C3, but false for the others - assertFalse(pq.getSelect().get(4).getOperand().isNumeric()); + // isNumeric() = isString() = isGeometry() for C2 + assertTrue(pq.getSelect().get(4).getOperand().isNumeric()); assertTrue(pq.getSelect().get(4).getOperand().isString()); - assertFalse(pq.getSelect().get(4).getOperand().isGeometry()); + assertTrue(pq.getSelect().get(4).getOperand().isGeometry()); + // isString() = true for C3, but false for the others + assertFalse(pq.getSelect().get(5).getOperand().isNumeric()); + assertTrue(pq.getSelect().get(5).getOperand().isString()); + assertFalse(pq.getSelect().get(5).getOperand().isGeometry()); + // isString() = true for C4, but false for the others + assertTrue(pq.getSelect().get(6).getOperand().isNumeric()); + assertFalse(pq.getSelect().get(6).getOperand().isString()); + assertFalse(pq.getSelect().get(6).getOperand().isGeometry()); }catch(Exception ex){ ex.printStackTrace(System.err); fail("The construction, configuration and usage of the parser are correct. Nothing should have failed here. (see console for more details)"); -- GitLab