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

[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)
parent 475fcb65
No related branches found
No related tags found
No related merge requests found
...@@ -16,11 +16,10 @@ package adql.db; ...@@ -16,11 +16,10 @@ package adql.db;
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * 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> * <p>
* Describe a full column type as it is described in the IVOA document of TAP. * 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). * Thus, this object contains 2 attributes: <code>type</code> (or datatype) and <code>length</code> (or size).
...@@ -32,7 +31,7 @@ package adql.db; ...@@ -32,7 +31,7 @@ package adql.db;
* It is used to set the attribute type/datatype of this class.</p> * It is used to set the attribute type/datatype of this class.</p>
* *
* @author Gr&eacute;gory Mantelet (ARI) * @author Gr&eacute;gory Mantelet (ARI)
* @version 1.4 (08/2015) * @version 1.4 (03/2016)
* @since 1.3 * @since 1.3
*/ */
public class DBType { public class DBType {
...@@ -41,13 +40,21 @@ 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). * List of all datatypes declared in the IVOA recommendation of TAP (in the section UPLOAD).
* *
* @author Gr&eacute;gory Mantelet (ARI) * @author Gr&eacute;gory Mantelet (ARI)
* @version 1.4 (06/2015) * @version 1.4 (03/2016)
* @since 1.3 * @since 1.3
*/ */
public static enum DBDatatype{ public static enum DBDatatype{
SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION, SMALLINT, INTEGER, BIGINT, REAL, DOUBLE, BINARY, VARBINARY, CHAR, VARCHAR, BLOB, CLOB, TIMESTAMP, POINT, REGION,
/** @since 1.4 */ /** Type to use when the precise datatype is unknown.
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. /** String to return when {@link #toString()} is called.
* @since 1.4*/ * @since 1.4*/
...@@ -60,11 +67,11 @@ public class DBType { ...@@ -60,11 +67,11 @@ public class DBType {
/** /**
* <p>This function lets define the name of the type as provided * <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> * <p><i><b>Important:</b>
* If this {@link DBDatatype} is not {@link #UNKNOWN} or if the given name is NULL or empty, * If this {@link DBDatatype} is not {@link #UNKNOWN} or {@link #UNKNOWN_NUMERIC} or
* this function has no effect. * if the given name is NULL or empty, this function has no effect.
* </i></p> * </i></p>
* *
* @param typeName User type name. * @param typeName User type name.
...@@ -72,7 +79,7 @@ public class DBType { ...@@ -72,7 +79,7 @@ public class DBType {
* @since 1.4 * @since 1.4
*/ */
public void setCustomType(final String typeName){ 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() + "?"; strExp = "?" + typeName.trim() + "?";
} }
} }
...@@ -121,7 +128,8 @@ public class DBType { ...@@ -121,7 +128,8 @@ public class DBType {
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * 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 * 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 * 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> * </i></p>
* *
* @return <code>true</code> if this type is a numeric, <code>false</code> otherwise. * @return <code>true</code> if this type is a numeric, <code>false</code> otherwise.
...@@ -138,6 +146,7 @@ public class DBType { ...@@ -138,6 +146,7 @@ public class DBType {
case BINARY: case BINARY:
case VARBINARY: case VARBINARY:
case BLOB: case BLOB:
case UNKNOWN_NUMERIC:
return true; return true;
default: default:
return false; return false;
...@@ -155,7 +164,7 @@ public class DBType { ...@@ -155,7 +164,7 @@ public class DBType {
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * 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 * 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 * 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> * </i></p>
* *
* @return <code>true</code> if this type is a binary, <code>false</code> otherwise. * @return <code>true</code> if this type is a binary, <code>false</code> otherwise.
...@@ -183,7 +192,7 @@ public class DBType { ...@@ -183,7 +192,7 @@ public class DBType {
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * 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 * 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 * 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> * </i></p>
* *
* @return <code>true</code> if this type is a string, <code>false</code> otherwise. * @return <code>true</code> if this type is a string, <code>false</code> otherwise.
...@@ -211,7 +220,7 @@ public class DBType { ...@@ -211,7 +220,7 @@ public class DBType {
* Since {@link DBDatatype#UNKNOWN UNKNOWN} is an unresolved type, it can potentially be anything. * 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 * 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 * 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> * </i></p>
* *
* @return <code>true</code> if this type is a geometry, <code>false</code> otherwise. * @return <code>true</code> if this type is a geometry, <code>false</code> otherwise.
...@@ -223,8 +232,8 @@ public class DBType { ...@@ -223,8 +232,8 @@ public class DBType {
/** /**
* <p>Tell whether this type has been resolved or not.</p> * <p>Tell whether this type has been resolved or not.</p>
* *
* <p><i>Concerned type: * <p><i>Concerned types:
* {@link DBDatatype#UNKNOWN UNKNOWN}. * {@link DBDatatype#UNKNOWN UNKNOWN} and {@link DBDatatype#UNKNOWN_NUMERIC UNKNOWN_NUMERIC}.
* </i></p> * </i></p>
* *
* @return <code>true</code> if this type has NOT been resolved, <code>false</code> otherwise. * @return <code>true</code> if this type has NOT been resolved, <code>false</code> otherwise.
...@@ -232,7 +241,7 @@ public class DBType { ...@@ -232,7 +241,7 @@ public class DBType {
* @since 1.4 * @since 1.4
*/ */
public boolean isUnknown(){ public boolean isUnknown(){
return type == DBDatatype.UNKNOWN; return type == DBDatatype.UNKNOWN || type == DBDatatype.UNKNOWN_NUMERIC;
} }
/** /**
...@@ -240,8 +249,8 @@ public class DBType { ...@@ -240,8 +249,8 @@ public class DBType {
* *
* <p> * <p>
* Two {@link DBType}s are said compatible if they are both binary, numeric, geometric or string. * 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 * If one of the two types is {@link DBDatatype#UNKNOWN unknown} or {@link DBDatatype#UNKNOWN_NUMERIC unknown_numeric},
* as compatible and will return <code>true</code>. * this function will consider them as compatible and will return <code>true</code>.
* </p> * </p>
* *
* @param t The type to compare to. * @param t The type to compare to.
......
...@@ -59,7 +59,7 @@ public class FunctionDef implements Comparable<FunctionDef> { ...@@ -59,7 +59,7 @@ public class FunctionDef implements Comparable<FunctionDef> {
/** Rough regular expression for a function return type or a parameter type. /** 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. * 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. */ * 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. */ /** Rough regular expression for a function parameters' list. */
protected final static String fctParamsRegExp = "\\s*[^,]+\\s*(,\\s*[^,]+\\s*)*"; 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}). */ /** 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> { ...@@ -350,7 +350,7 @@ public class FunctionDef implements Comparable<FunctionDef> {
* <p> * <p>
* <em>This function must be able to parse functions as defined by TAPRegExt (section 2.3).</em> * <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. * 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 * 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); * 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. * a default interpretation of database types is nevertheless processed by this parser.
......
...@@ -16,7 +16,7 @@ package adql.query; ...@@ -16,7 +16,7 @@ package adql.query;
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * 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) * Astronomisches Rechen Institut (ARI)
*/ */
...@@ -25,12 +25,20 @@ import java.util.Iterator; ...@@ -25,12 +25,20 @@ import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import adql.db.DBColumn; import adql.db.DBColumn;
import adql.db.DBType;
import adql.db.DBType.DBDatatype;
import adql.db.DefaultDBColumn; import adql.db.DefaultDBColumn;
import adql.parser.ADQLParser; import adql.parser.ADQLParser;
import adql.parser.ParseException; import adql.parser.ParseException;
import adql.query.from.FromContent; import adql.query.from.FromContent;
import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLColumn;
import adql.query.operand.ADQLOperand; 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; import adql.search.ISearchHandler;
/** /**
...@@ -38,7 +46,7 @@ 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> * <p>The resulting object of the {@link ADQLParser} is an object of this class.</p>
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
* @version 1.4 (06/2015) * @version 1.4 (03/2016)
*/ */
public class ADQLQuery implements ADQLObject { public class ADQLQuery implements ADQLObject {
...@@ -311,19 +319,50 @@ 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! // Here, this error should not occur any more, since it must have been caught by the DBChecker!
} }
}else{ }else{
// Create the DBColumn:
DBColumn col = null; DBColumn col = null;
// ...whose the name will be set with the SELECT item's alias:
if (item.hasAlias()){ if (item.hasAlias()){
if (operand instanceof ADQLColumn && ((ADQLColumn)operand).getDBLink() != null){ if (operand instanceof ADQLColumn && ((ADQLColumn)operand).getDBLink() != null){
col = ((ADQLColumn)operand).getDBLink(); col = ((ADQLColumn)operand).getDBLink();
col = col.copy(col.getDBName(), item.getAlias(), col.getTable()); col = col.copy(col.getDBName(), item.getAlias(), col.getTable());
}else }else
col = new DefaultDBColumn(item.getAlias(), null); 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) if (operand instanceof ADQLColumn && ((ADQLColumn)operand).getDBLink() != null)
col = ((ADQLColumn)operand).getDBLink(); col = ((ADQLColumn)operand).getDBLink();
if (col == null) if (col == null)
col = new DefaultDBColumn(item.getName(), 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); columns.add(col);
} }
} }
......
...@@ -16,7 +16,7 @@ package adql.query.operand; ...@@ -16,7 +16,7 @@ package adql.query.operand;
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * 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) * Astronomisches Rechen Institut (ARI)
*/ */
...@@ -32,7 +32,7 @@ import adql.query.from.ADQLTable; ...@@ -32,7 +32,7 @@ import adql.query.from.ADQLTable;
* Represents the complete (literal) reference to a column ({schema(s)}.{table}.{column}). * Represents the complete (literal) reference to a column ({schema(s)}.{table}.{column}).
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
* @version 1.4 (06/2015) * @version 1.4 (03/2016)
*/ */
public class ADQLColumn implements ADQLOperand, UnknownType { public class ADQLColumn implements ADQLOperand, UnknownType {
...@@ -469,17 +469,17 @@ public class ADQLColumn implements ADQLOperand, UnknownType { ...@@ -469,17 +469,17 @@ public class ADQLColumn implements ADQLOperand, UnknownType {
@Override @Override
public boolean isNumeric(){ 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 @Override
public boolean isString(){ 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 @Override
public boolean isGeometry(){ 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 @Override
......
package adql; package adql;
import static org.junit.Assert.assertEquals; 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.ArrayList;
import java.util.Iterator; import java.util.Iterator;
...@@ -9,6 +11,9 @@ import java.util.List; ...@@ -9,6 +11,9 @@ import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import adql.db.DBType;
import adql.db.DBType.DBDatatype;
import adql.db.FunctionDef;
import adql.query.ADQLObject; import adql.query.ADQLObject;
import adql.query.ADQLOrder; import adql.query.ADQLOrder;
import adql.query.ADQLQuery; import adql.query.ADQLQuery;
...@@ -21,12 +26,23 @@ import adql.query.constraint.ComparisonOperator; ...@@ -21,12 +26,23 @@ import adql.query.constraint.ComparisonOperator;
import adql.query.constraint.ConstraintsGroup; import adql.query.constraint.ConstraintsGroup;
import adql.query.from.ADQLTable; import adql.query.from.ADQLTable;
import adql.query.operand.ADQLColumn; import adql.query.operand.ADQLColumn;
import adql.query.operand.ADQLOperand;
import adql.query.operand.Concatenation; import adql.query.operand.Concatenation;
import adql.query.operand.NumericConstant; import adql.query.operand.NumericConstant;
import adql.query.operand.Operation; import adql.query.operand.Operation;
import adql.query.operand.OperationType; import adql.query.operand.OperationType;
import adql.query.operand.StringConstant; import adql.query.operand.StringConstant;
import adql.query.operand.WrappedOperand; 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.IReplaceHandler;
import adql.search.ISearchHandler; import adql.search.ISearchHandler;
import adql.search.SearchColumnHandler; import adql.search.SearchColumnHandler;
...@@ -124,4 +140,103 @@ public class TestADQLQuery { ...@@ -124,4 +140,103 @@ public class TestADQLQuery {
assertEquals(expectedCol, results.next()); 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()); 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);
}
} }
...@@ -10,6 +10,7 @@ import org.junit.Test; ...@@ -10,6 +10,7 @@ import org.junit.Test;
import adql.db.DBType.DBDatatype; import adql.db.DBType.DBDatatype;
import adql.db.FunctionDef.FunctionParam; import adql.db.FunctionDef.FunctionParam;
import adql.parser.ParseException; import adql.parser.ParseException;
import adql.query.operand.ADQLColumn;
import adql.query.operand.ADQLOperand; import adql.query.operand.ADQLOperand;
import adql.query.operand.NumericConstant; import adql.query.operand.NumericConstant;
import adql.query.operand.StringConstant; import adql.query.operand.StringConstant;
...@@ -62,6 +63,7 @@ public class TestFunctionDef { ...@@ -62,6 +63,7 @@ public class TestFunctionDef {
case UNKNOWN: case UNKNOWN:
assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric); assertFalse(new FunctionDef("foo", new DBType(type)).isNumeric);
break; break;
case UNKNOWN_NUMERIC:
default: default:
assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric); assertTrue(new FunctionDef("foo", new DBType(type)).isNumeric);
} }
...@@ -338,6 +340,18 @@ public class TestFunctionDef { ...@@ -338,6 +340,18 @@ public class TestFunctionDef {
e.printStackTrace(); e.printStackTrace();
fail(); 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})));
} }
} }
...@@ -70,7 +70,7 @@ public class UnknownTypes { ...@@ -70,7 +70,7 @@ public class UnknownTypes {
@Test @Test
public void testForColumns(){ 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{ try{
// Create the parser: // Create the parser:
...@@ -81,6 +81,7 @@ public class UnknownTypes { ...@@ -81,6 +81,7 @@ public class UnknownTypes {
table1.addColumn(new DefaultDBColumn("C1", table1)); table1.addColumn(new DefaultDBColumn("C1", table1));
table1.addColumn(new DefaultDBColumn("C2", new DBType(DBDatatype.UNKNOWN), 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("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}); Collection<DBTable> tList = Arrays.asList(new DBTable[]{table1});
// Check the type of the column T1.C1: // Check the type of the column T1.C1:
...@@ -98,6 +99,16 @@ public class UnknownTypes { ...@@ -98,6 +99,16 @@ public class UnknownTypes {
assertFalse(col.getDatatype().isGeometry()); assertFalse(col.getDatatype().isGeometry());
assertEquals("UNKNOWN", col.getDatatype().toString()); 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: // Define a UDF, and allow all geometrical functions and coordinate systems:
FunctionDef udf1 = FunctionDef.parse("FOO(x INTEGER) -> INTEGER"); FunctionDef udf1 = FunctionDef.parse("FOO(x INTEGER) -> INTEGER");
Collection<FunctionDef> udfList = Arrays.asList(new FunctionDef[]{udf1}); Collection<FunctionDef> udfList = Arrays.asList(new FunctionDef[]{udf1});
...@@ -122,18 +133,26 @@ public class UnknownTypes { ...@@ -122,18 +133,26 @@ public class UnknownTypes {
assertTrue(pq.getSelect().get(1).getOperand().isNumeric()); assertTrue(pq.getSelect().get(1).getOperand().isNumeric());
assertFalse(pq.getSelect().get(1).getOperand().isString()); assertFalse(pq.getSelect().get(1).getOperand().isString());
assertFalse(pq.getSelect().get(1).getOperand().isGeometry()); 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().isNumeric());
assertTrue(pq.getSelect().get(2).getOperand().isString()); assertFalse(pq.getSelect().get(2).getOperand().isString());
assertTrue(pq.getSelect().get(2).getOperand().isGeometry()); assertFalse(pq.getSelect().get(2).getOperand().isGeometry());
// isNumeric() = isString() = isGeometry() for C2 // isNumeric() = isString() = isGeometry() for C1
assertTrue(pq.getSelect().get(3).getOperand().isNumeric()); assertTrue(pq.getSelect().get(3).getOperand().isNumeric());
assertTrue(pq.getSelect().get(3).getOperand().isString()); assertTrue(pq.getSelect().get(3).getOperand().isString());
assertTrue(pq.getSelect().get(3).getOperand().isGeometry()); assertTrue(pq.getSelect().get(3).getOperand().isGeometry());
// isString() = true for C3, but false for the others // isNumeric() = isString() = isGeometry() for C2
assertFalse(pq.getSelect().get(4).getOperand().isNumeric()); assertTrue(pq.getSelect().get(4).getOperand().isNumeric());
assertTrue(pq.getSelect().get(4).getOperand().isString()); 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){ }catch(Exception ex){
ex.printStackTrace(System.err); 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)"); fail("The construction, configuration and usage of the parser are correct. Nothing should have failed here. (see console for more details)");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment