From f9fbbec3234544ca4223ef89fa563081da72654d Mon Sep 17 00:00:00 2001
From: gmantele <gmantele@ari.uni-heidelberg.de>
Date: Thu, 16 Apr 2020 18:31:18 +0200
Subject: [PATCH] [ADQL] Allow also `CENTROID` and UDF as arguments of
 `DISTANCE` (2-arg form) & Allow UDF as valid geometry argument (a UDF being
 able to return geometries) like in functions `AREA`, `CENTROID`, `DISTANCE`,
 ...

---
 src/adql/parser/ADQLQueryFactory.java         | 15 ++++--
 src/adql/parser/grammar/adqlGrammar200.jj     | 10 ++--
 src/adql/parser/grammar/adqlGrammar201.jj     | 54 +++++++++++++------
 .../function/geometry/DistanceFunction.java   | 49 ++++++++++-------
 .../function/geometry/GeometryFunction.java   | 43 ++++++++++++---
 test/adql/db/TestDBChecker.java               | 14 ++---
 6 files changed, 125 insertions(+), 60 deletions(-)

diff --git a/src/adql/parser/ADQLQueryFactory.java b/src/adql/parser/ADQLQueryFactory.java
index 5507f7a..5829f32 100644
--- a/src/adql/parser/ADQLQueryFactory.java
+++ b/src/adql/parser/ADQLQueryFactory.java
@@ -16,7 +16,7 @@ package adql.parser;
  * 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-2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2020 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -92,7 +92,7 @@ import adql.query.operand.function.string.LowerFunction;
  * </p>
  *
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (11/2019)
+ * @version 2.0 (04/2020)
  *
  * @see ADQLParser
  */
@@ -349,11 +349,18 @@ public class ADQLQueryFactory {
 		return new DefaultUDF(name, params);
 	}
 
+	/** @deprecated Since 2.0, prefer to use directly {@link #createDistance(GeometryFunction, GeometryFunction)} */
+	@Deprecated
 	public DistanceFunction createDistance(PointFunction point1, PointFunction point2) throws Exception {
-		return new DistanceFunction(new GeometryValue<PointFunction>(point1), new GeometryValue<PointFunction>(point2));
+		return createDistance((GeometryFunction)point1, (GeometryFunction)point2);
+	}
+
+	/** @since 2.0 */
+	public DistanceFunction createDistance(GeometryFunction point1, GeometryFunction point2) throws Exception {
+		return new DistanceFunction(new GeometryValue<GeometryFunction>(point1), new GeometryValue<GeometryFunction>(point2));
 	}
 
-	public DistanceFunction createDistance(GeometryValue<PointFunction> point1, GeometryValue<PointFunction> point2) throws Exception {
+	public DistanceFunction createDistance(GeometryValue<GeometryFunction> point1, GeometryValue<GeometryFunction> point2) throws Exception {
 		return new DistanceFunction(point1, point2);
 	}
 
diff --git a/src/adql/parser/grammar/adqlGrammar200.jj b/src/adql/parser/grammar/adqlGrammar200.jj
index 360c11c..bdcf299 100644
--- a/src/adql/parser/grammar/adqlGrammar200.jj
+++ b/src/adql/parser/grammar/adqlGrammar200.jj
@@ -1183,7 +1183,7 @@ ADQLOperand[] Coordinates(): {ADQLOperand[] ops = new ADQLOperand[2];} {
 	{return ops;}
 }
 
-GeometryFunction GeometryFunction(): {Token fct=null, end; GeometryValue<GeometryFunction> gvf1, gvf2; GeometryValue<PointFunction> gvp1, gvp2; GeometryFunction gf = null; PointFunction p1=null, p2=null; ADQLColumn col1 = null, col2 = null;} {
+GeometryFunction GeometryFunction(): {Token fct=null, end; GeometryValue<GeometryFunction> gvf1, gvf2; GeometryValue<GeometryFunction> gvp1, gvp2; GeometryFunction gf = null; PointFunction p1=null, p2=null; ADQLColumn col1 = null, col2 = null;} {
 	try{
 		// predicate_geometry_function
 		(
@@ -1203,20 +1203,20 @@ GeometryFunction GeometryFunction(): {Token fct=null, end; GeometryValue<Geometr
 				(p1=Point()|col1=Column()) 
 				{
 					if (p1 != null)
-						gvp1 = new GeometryValue<PointFunction>(p1);
+						gvp1 = new GeometryValue<GeometryFunction>(p1);
 					else{
 						col1.setExpectedType('G');
-						gvp1 = new GeometryValue<PointFunction>(col1);
+						gvp1 = new GeometryValue<GeometryFunction>(col1);
 					}
 				}
 				<COMMA>
 				(p2=Point()|col2=Column())
 				{
 					if (p2 != null)
-						gvp2 = new GeometryValue<PointFunction>(p2);
+						gvp2 = new GeometryValue<GeometryFunction>(p2);
 					else{
 						col2.setExpectedType('G');
-						gvp2 = new GeometryValue<PointFunction>(col2);
+						gvp2 = new GeometryValue<GeometryFunction>(col2);
 					}
 				} 
 				end=<RIGHT_PAR>
diff --git a/src/adql/parser/grammar/adqlGrammar201.jj b/src/adql/parser/grammar/adqlGrammar201.jj
index 3242668..99f0718 100644
--- a/src/adql/parser/grammar/adqlGrammar201.jj
+++ b/src/adql/parser/grammar/adqlGrammar201.jj
@@ -31,7 +31,7 @@
 * ParseException is thrown.
 *
 * Author:  Gr&eacute;gory Mantelet (CDS)
-* Version: 2.0 (03/2020)
+* Version: 2.0 (04/2020)
 */
 
 							/* ########### */
@@ -107,7 +107,7 @@ import adql.query.operand.function.geometry.GeometryFunction.GeometryValue;
  * @see ADQLParser
  *
  * @author Gr&eacute;gory Mantelet (CDS)
- * @version 2.0 (03/2020)
+ * @version 2.0 (04/2020)
  * @since 2.0
  */
 public class ADQLGrammar201 extends ADQLGrammarBase {
@@ -1030,12 +1030,15 @@ ADQLOperand StringFactor(): {ADQLOperand op;} {
 	{return op;}
 }
 
-GeometryValue<GeometryFunction> GeometryExpression(): {ADQLColumn col = null; GeometryFunction gf = null;} {
-	(col=Column() | gf=GeometryValueFunction())
+GeometryValue<GeometryFunction> GeometryExpression(): {ADQLColumn col = null; UserDefinedFunction udf = null; GeometryFunction gf = null;} {
+	( gf=GeometryValueFunction() | LOOKAHEAD(2) udf=UserDefinedFunction() | col=Column())
 	{
 		if (col != null){
 		  	col.setExpectedType('G');
 			return new GeometryValue<GeometryFunction>(col);
+		}else if (udf != null){
+			udf.setExpectedType('G');
+			return new GeometryValue<GeometryFunction>(udf);
 		}else
 			return new GeometryValue<GeometryFunction>(gf);
 	}
@@ -1277,7 +1280,7 @@ GeometryFunction GeometryFunction(): {Token fct=null, end=null; GeometryValue<Ge
 	}
 }
 
-DistanceFunction DistanceFunction(): { Token fct=null, end=null; DistanceFunction gf; ADQLOperand lon, lat; GeometryValue<PointFunction> gvp1, gvp2; } {
+DistanceFunction DistanceFunction(): { Token fct=null, end=null; DistanceFunction gf; ADQLOperand lon, lat; GeometryValue<GeometryFunction> gvp1, gvp2; } {
 	try {
 		// DISTANCE(POINT,POINT)
 		(LOOKAHEAD(DistanceFunction2())
@@ -1286,10 +1289,10 @@ DistanceFunction DistanceFunction(): { Token fct=null, end=null; DistanceFunctio
 		// DISTANCE(lon1, lat1, lon2, lat2)
 			fct=<DISTANCE> <LEFT_PAR>
 			lon=NumericExpression() <COMMA> lat=NumericExpression() 
-			{ gvp1 = new GeometryValue<PointFunction>(queryFactory.createPoint(null, lon, lat)); }
+			{ gvp1 = new GeometryValue<GeometryFunction>(queryFactory.createPoint(null, lon, lat)); }
 			<COMMA>
 			lon=NumericExpression() <COMMA> lat=NumericExpression() 
-			{ gvp2 = new GeometryValue<PointFunction>(queryFactory.createPoint(null, lon, lat)); } 
+			{ gvp2 = new GeometryValue<GeometryFunction>(queryFactory.createPoint(null, lon, lat)); } 
 			end=<RIGHT_PAR>
 			{
 				gf = queryFactory.createDistance(gvp1, gvp2);
@@ -1302,26 +1305,32 @@ DistanceFunction DistanceFunction(): { Token fct=null, end=null; DistanceFunctio
 	}
 }
 
-DistanceFunction DistanceFunction2(): { Token fct=null, end=null; DistanceFunction gf; GeometryValue<PointFunction> gvp1, gvp2; PointFunction p1=null, p2=null; ADQLColumn col1=null, col2=null; } {
+DistanceFunction DistanceFunction2(): { Token fct=null, end=null; DistanceFunction gf; GeometryValue<GeometryFunction> gvp1, gvp2; GeometryFunction p1=null, p2=null; ADQLColumn col1=null, col2=null; UserDefinedFunction udf1=null, udf2=null; } {
 	try {
 		fct=<DISTANCE> <LEFT_PAR>
-		(p1=Point()|col1=Column()) 
+		(p1=Point()|p1=Centroid()|LOOKAHEAD(2) udf1=UserDefinedFunction()|col1=Column()) 
 		{
 			if (p1 != null)
-				gvp1 = new GeometryValue<PointFunction>(p1);
-			else{
+				gvp1 = new GeometryValue<GeometryFunction>(p1);
+			else if (udf1 != null){
+			  	udf1.setExpectedType('G');
+				gvp1 = new GeometryValue<GeometryFunction>(udf1);
+			}else{
 				col1.setExpectedType('G');
-				gvp1 = new GeometryValue<PointFunction>(col1);
+				gvp1 = new GeometryValue<GeometryFunction>(col1);
 			}
 		}
 		<COMMA>
-		(p2=Point()|col2=Column())
+		(p2=Point()|p2=Centroid()|LOOKAHEAD(2) udf2=UserDefinedFunction()|col2=Column())
 		{
 			if (p2 != null)
-				gvp2 = new GeometryValue<PointFunction>(p2);
-			else{
+				gvp2 = new GeometryValue<GeometryFunction>(p2);
+			else if (udf2 != null){
+			  	udf2.setExpectedType('G');
+				gvp2 = new GeometryValue<GeometryFunction>(udf2);
+			}else{
 				col2.setExpectedType('G');
-				gvp2 = new GeometryValue<PointFunction>(col2);
+				gvp2 = new GeometryValue<GeometryFunction>(col2);
 			}
 		} 
 		end=<RIGHT_PAR>
@@ -1353,7 +1362,7 @@ GeometryFunction GeometryValueFunction(): {Token fct=null, end=null; ADQLOperand
 		 {gf = queryFactory.createBox(null, coords[0], coords[1], width, height);}
 		
 		// CENTROID:
-		| (fct=<CENTROID> <LEFT_PAR> gvf=GeometryExpression() end=<RIGHT_PAR>) {gf = queryFactory.createCentroid(gvf);}
+		| gf=Centroid()
 
 		// CIRCLE (deprecated since ADQL-2.1)
 		| LOOKAHEAD(CircleWithCooSys()) gf=CircleWithCooSys()
@@ -1424,6 +1433,17 @@ GeometryFunction CircleWithCooSys(): { Token fct=null, end=null; ADQLOperand coo
 	}
 }
 
+CentroidFunction Centroid(): {Token fct=null, end=null; GeometryValue<GeometryFunction> gvf = null; } {
+	(fct=<CENTROID> <LEFT_PAR> gvf=GeometryExpression() end=<RIGHT_PAR>)
+	{
+		try {
+	  		return queryFactory.createCentroid(gvf);
+		}catch(Exception ex){
+			throw generateParseException(ex);
+		}
+	}
+}
+
 PointFunction Point(): {Token start, end; ADQLOperand[] coords; PointFunction pf;} {
 	// POINT (depecrated since ADQL-2.1)
 	(LOOKAHEAD(PointWithCooSys())
diff --git a/src/adql/query/operand/function/geometry/DistanceFunction.java b/src/adql/query/operand/function/geometry/DistanceFunction.java
index e57c275..1065340 100644
--- a/src/adql/query/operand/function/geometry/DistanceFunction.java
+++ b/src/adql/query/operand/function/geometry/DistanceFunction.java
@@ -16,7 +16,7 @@ package adql.query.operand.function.geometry;
  * 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-2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2020 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -39,6 +39,15 @@ import adql.query.operand.ADQLOperand;
  * 	accepts four separate numeric values.
  * </p>
  *
+ * <p><i><b>Implementation note:</b>
+ * 	In this current implementation, the 2-argument form allows 2 geometries
+ * 	instead of 2 points. The goal is to be more generic. POINT is supposed to
+ * 	be the main expected type of argument, but it could also be a CENTROID
+ * 	(which returns a POINT). Moreover, some extension of this library might
+ * 	want to support DISTANCE between any type of geometries instead of just
+ * 	points.
+ * </i></p>
+ *
  * <p>
  * 	If an ADQL service implementation declares support for DISTANCE, then it
  * 	must implement both the two parameter and four parameter forms of the
@@ -67,8 +76,8 @@ import adql.query.operand.ADQLOperand;
  * <i>
  * <p><b>Example:</b></p>
  * <p>
- * 	The distance between to points stored in the database could be calculated as
- * 	follows:
+ * 	The distance between two points stored in the database could be calculated
+ * 	as follows:
  * </p>
  * <pre>DISTANCE(t1.base, t2.target)</pre>
  * <p>
@@ -92,7 +101,7 @@ import adql.query.operand.ADQLOperand;
  * </p>
  *
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.0 (07/2019)
+ * @version 2.0 (04/2020)
  */
 public class DistanceFunction extends GeometryFunction {
 
@@ -101,10 +110,10 @@ public class DistanceFunction extends GeometryFunction {
 	public static final LanguageFeature FEATURE = new LanguageFeature(LanguageFeature.TYPE_ADQL_GEO, "DISTANCE", true, "Compute the arc length along a great circle between two points and returns a numeric value expression in degrees.");
 
 	/** The first point. */
-	private GeometryValue<PointFunction> p1;
+	private GeometryValue<GeometryFunction> p1;
 
 	/** The second point. */
-	private GeometryValue<PointFunction> p2;
+	private GeometryValue<GeometryFunction> p2;
 
 	/**
 	 * Builds a DISTANCE function.
@@ -113,7 +122,7 @@ public class DistanceFunction extends GeometryFunction {
 	 * @param point2				The second point.
 	 * @throws NullPointerException	If one of the parameters are incorrect.
 	 */
-	public DistanceFunction(GeometryValue<PointFunction> point1, GeometryValue<PointFunction> point2) throws NullPointerException {
+	public DistanceFunction(GeometryValue<GeometryFunction> point1, GeometryValue<GeometryFunction> point2) throws NullPointerException {
 		super();
 		if (point1 == null || point2 == null)
 			throw new NullPointerException("All parameters of the DISTANCE function must be different from null!");
@@ -131,8 +140,8 @@ public class DistanceFunction extends GeometryFunction {
 	@SuppressWarnings("unchecked")
 	public DistanceFunction(DistanceFunction toCopy) throws Exception {
 		super(toCopy);
-		p1 = (GeometryValue<PointFunction>)(toCopy.p1.getCopy());
-		p2 = (GeometryValue<PointFunction>)(toCopy.p2.getCopy());
+		p1 = (GeometryValue<GeometryFunction>)(toCopy.p1.getCopy());
+		p2 = (GeometryValue<GeometryFunction>)(toCopy.p2.getCopy());
 	}
 
 	@Override
@@ -175,7 +184,7 @@ public class DistanceFunction extends GeometryFunction {
 	 *
 	 * @return A point.
 	 */
-	public final GeometryValue<PointFunction> getP1() {
+	public final GeometryValue<GeometryFunction> getP1() {
 		return p1;
 	}
 
@@ -184,7 +193,7 @@ public class DistanceFunction extends GeometryFunction {
 	 *
 	 * @param p1 A point.
 	 */
-	public final void setP1(GeometryValue<PointFunction> p1) {
+	public final void setP1(GeometryValue<GeometryFunction> p1) {
 		this.p1 = p1;
 		setPosition(null);
 	}
@@ -194,7 +203,7 @@ public class DistanceFunction extends GeometryFunction {
 	 *
 	 * @return A point.
 	 */
-	public final GeometryValue<PointFunction> getP2() {
+	public final GeometryValue<GeometryFunction> getP2() {
 		return p2;
 	}
 
@@ -203,7 +212,7 @@ public class DistanceFunction extends GeometryFunction {
 	 *
 	 * @param p2 A point.
 	 */
-	public final void setP2(GeometryValue<PointFunction> p2) {
+	public final void setP2(GeometryValue<GeometryFunction> p2) {
 		this.p2 = p2;
 		setPosition(null);
 	}
@@ -235,23 +244,23 @@ public class DistanceFunction extends GeometryFunction {
 	public ADQLOperand setParameter(int index, ADQLOperand replacer) throws ArrayIndexOutOfBoundsException, NullPointerException, Exception {
 		if (replacer == null)
 			throw new NullPointerException("Impossible to remove a parameter from the function " + getName() + "!");
-		else if (!(replacer instanceof GeometryValue || replacer instanceof ADQLColumn || replacer instanceof PointFunction))
-			throw new Exception("Impossible to replace a GeometryValue/Column/PointFunction by " + replacer.getClass().getName() + " (" + replacer.toADQL() + ")!");
+		else if (!(replacer instanceof GeometryValue || replacer instanceof ADQLColumn || replacer instanceof GeometryFunction))
+			throw new Exception("Impossible to replace a GeometryValue/Column/GeometryFunction by " + replacer.getClass().getName() + " (" + replacer.toADQL() + ")!");
 
 		ADQLOperand replaced = null;
-		GeometryValue<PointFunction> toUpdate = null;
+		GeometryValue<GeometryFunction> toUpdate = null;
 		switch(index) {
 			case 0:
 				replaced = p1.getValue();
 				if (replacer instanceof GeometryValue)
-					p1 = (GeometryValue<PointFunction>)replacer;
+					p1 = (GeometryValue<GeometryFunction>)replacer;
 				else
 					toUpdate = p1;
 				break;
 			case 1:
 				replaced = p2.getValue();
 				if (replacer instanceof GeometryValue)
-					p2 = (GeometryValue<PointFunction>)replacer;
+					p2 = (GeometryValue<GeometryFunction>)replacer;
 				else
 					toUpdate = p2;
 				break;
@@ -262,8 +271,8 @@ public class DistanceFunction extends GeometryFunction {
 		if (toUpdate != null) {
 			if (replacer instanceof ADQLColumn)
 				toUpdate.setColumn((ADQLColumn)replacer);
-			else if (replacer instanceof PointFunction)
-				toUpdate.setGeometry((PointFunction)replacer);
+			else if (replacer instanceof GeometryFunction)
+				toUpdate.setGeometry((GeometryFunction)replacer);
 		}
 
 		setPosition(null);
diff --git a/src/adql/query/operand/function/geometry/GeometryFunction.java b/src/adql/query/operand/function/geometry/GeometryFunction.java
index 6669a04..629cf85 100644
--- a/src/adql/query/operand/function/geometry/GeometryFunction.java
+++ b/src/adql/query/operand/function/geometry/GeometryFunction.java
@@ -16,7 +16,7 @@ package adql.query.operand.function.geometry;
  * 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-2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
+ * Copyright 2012-2020 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -29,6 +29,7 @@ import adql.query.operand.ADQLColumn;
 import adql.query.operand.ADQLOperand;
 import adql.query.operand.StringConstant;
 import adql.query.operand.function.ADQLFunction;
+import adql.query.operand.function.UserDefinedFunction;
 
 /**
  * It represents any geometric function of ADQL.
@@ -109,14 +110,20 @@ public abstract class GeometryFunction extends ADQLFunction {
 
 	/**
 	 * This class represents a parameter of a geometry function
-	 * which, in general, is either a GeometryFunction or a Column.
+	 * which, in general, is either a GeometryFunction, a Column or a
+	 * UserDefinedFunction.
 	 *
 	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
-	 * @version 2.0 (07/2019)
+	 * @version 2.0 (04/2020)
 	 */
 	public static final class GeometryValue<F extends GeometryFunction> implements ADQLOperand {
+
 		private ADQLColumn column;
 		private F geomFunct;
+
+		/** @since 2.0 */
+		private UserDefinedFunction udf;
+
 		/** Position of this {@link GeometryValue} in the ADQL query string.
 		 * @since 1.4 */
 		private TextPosition position = null;
@@ -125,16 +132,19 @@ public abstract class GeometryFunction extends ADQLFunction {
 			if (col == null)
 				throw new NullPointerException("Impossible to build a GeometryValue without a column or a geometry function!");
 			setColumn(col);
-			if (col.getPosition() != null)
-				position = col.getPosition();
 		}
 
 		public GeometryValue(F geometry) throws NullPointerException {
 			if (geometry == null)
 				throw new NullPointerException("Impossible to build a GeometryValue without a column or a geometry function!");
 			setGeometry(geometry);
-			if (geometry.getPosition() != null)
-				position = geometry.getPosition();
+		}
+
+		/** @since 2.0 */
+		public GeometryValue(UserDefinedFunction udf) throws NullPointerException {
+			if (udf == null)
+				throw new NullPointerException("Impossible to build a GeometryValue without a column, a geometry function or User Defined Function!");
+			setUDF(udf);
 		}
 
 		@SuppressWarnings("unchecked")
@@ -151,6 +161,7 @@ public abstract class GeometryFunction extends ADQLFunction {
 
 		public void setColumn(ADQLColumn col) {
 			if (col != null) {
+				udf = null;
 				geomFunct = null;
 				column = col;
 				position = (column.getPosition() != null) ? column.getPosition() : null;
@@ -159,14 +170,30 @@ public abstract class GeometryFunction extends ADQLFunction {
 
 		public void setGeometry(F geometry) {
 			if (geometry != null) {
+				udf = null;
 				column = null;
 				geomFunct = geometry;
 				position = (geomFunct.getPosition() != null) ? geomFunct.getPosition() : null;
 			}
 		}
 
+		/** @since 2.0 */
+		public void setUDF(UserDefinedFunction udf) {
+			if (udf != null) {
+				column = null;
+				geomFunct = null;
+				this.udf = udf;
+				position = (udf.getPosition() != null) ? udf.getPosition() : null;
+			}
+		}
+
 		public ADQLOperand getValue() {
-			return (column != null) ? column : geomFunct;
+			if (column != null)
+				return column;
+			else if (geomFunct != null)
+				return geomFunct;
+			else
+				return udf;
 		}
 
 		public boolean isColumn() {
diff --git a/test/adql/db/TestDBChecker.java b/test/adql/db/TestDBChecker.java
index 012decc..b150a2b 100644
--- a/test/adql/db/TestDBChecker.java
+++ b/test/adql/db/TestDBChecker.java
@@ -653,12 +653,14 @@ public class TestDBChecker {
 			}
 
 			// Test the return type checking inside a whole query:
-			try {
-				parser.parseQuery("SELECT CONTAINS(colG, titi()) ' AS \"Super\" FROM foo;");
-				fail("Geometrical UDFs are not allowed for the moment in the ADQL language: this test should have failed!");
-			} catch(ParseException e1) {
-				assertTrue(e1 instanceof ParseException);
-				assertEquals(" Encountered \"(\". Was expecting one of: \")\" \".\" \".\" \")\" ", e1.getMessage());
+			if (parser.getADQLVersion() == ADQLVersion.V2_0) {
+				try {
+					parser.parseQuery("SELECT CONTAINS(colG, titi()) AS \"SuperError\" FROM foo;");
+					fail("Geometrical UDFs are not allowed for the moment in the ADQL language: this test should have failed!");
+				} catch(ParseException e1) {
+					assertTrue(e1 instanceof ParseException);
+					assertEquals(" Encountered \"(\". Was expecting one of: \")\" \".\" \".\" \")\" ", e1.getMessage());
+				}
 			}
 			try {
 				parser.parseQuery("SELECT titi()*3 AS \"SuperError\" FROM foo;");
-- 
GitLab