From 61b1a0793330a65f819b140ab982a5545f34841a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gr=C3=A9gory=20Mantelet?=
 <gregory.mantelet@astro.unistra.fr>
Date: Mon, 26 Aug 2019 15:50:48 +0200
Subject: [PATCH] [ADQL] Improve error report in case of incorrect argument for
 numeric functions.

---
 src/adql/parser/grammar/ADQLGrammarBase.java |  6 ++++-
 src/adql/parser/grammar/adqlGrammar201.jj    | 16 +++++++-----
 test/adql/parser/TestADQLParser.java         | 26 ++++++++++++++++++++
 3 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/src/adql/parser/grammar/ADQLGrammarBase.java b/src/adql/parser/grammar/ADQLGrammarBase.java
index eecb0cb..71d1ee5 100644
--- a/src/adql/parser/grammar/ADQLGrammarBase.java
+++ b/src/adql/parser/grammar/ADQLGrammarBase.java
@@ -133,7 +133,11 @@ public abstract class ADQLGrammarBase implements ADQLGrammar {
 	@Override
 	public final ParseException generateParseException(Exception ex) {
 		if (!(ex instanceof ParseException)) {
-			ParseException pex = new ParseException("[" + ex.getClass().getName() + "] " + ex.getMessage());
+			ParseException pex;
+			if (ex instanceof IllegalArgumentException)
+				pex = new ParseException("Incorrect argument: " + ex.getMessage());
+			else
+				pex = new ParseException("[" + ex.getClass().getName() + "] " + ex.getMessage());
 			pex.setStackTrace(ex.getStackTrace());
 			return pex;
 		} else
diff --git a/src/adql/parser/grammar/adqlGrammar201.jj b/src/adql/parser/grammar/adqlGrammar201.jj
index 8568988..9567045 100644
--- a/src/adql/parser/grammar/adqlGrammar201.jj
+++ b/src/adql/parser/grammar/adqlGrammar201.jj
@@ -892,14 +892,16 @@ ADQLOperand ValueExpression(): {ADQLOperand valueExpr = null; Token left, right;
 		| LOOKAHEAD(3) valueExpr=Factor()
 
 		/* At this position in this switch, all possibilities (including
-		 * Column()) have already been tested and failed.
+		 * Column() and NumericFunction) have already been tested and failed.
 		 * 
-		 * So, this final choice actually aims to throw an error set with the
+		 * So, these final choices actually aim to throw an error set with the
 		 * current token and with an error message implying that a column name
-		 * was expected (which is generally the case in an ADQL query).
-		 *
-		 * Note: This choice will generally be reached if an unexpected ADQL/SQL
-		 *       word is ending the query. */
+		 * was expected (which is generally the case in an ADQL query) or that
+		 * the parameters of the numeric function are incorrect. */
+		| LOOKAHEAD(2) valueExpr=NumericFunction()
+		/*
+		 * Note: Besides, this particular choice will generally be reached if an
+		 *       unexpected ADQL/SQL word is ending the query. */
 		| valueExpr=Column() )
 		{return valueExpr;}
 	}catch(Exception ex){
@@ -1003,6 +1005,8 @@ ADQLOperand StringExpression(): {ADQLOperand leftOp; ADQLOperand rightOp = null;
 		if (leftOp instanceof Concatenation){
 			Concatenation concat = (Concatenation)leftOp;
 			concat.setPosition(new TextPosition(concat.get(0).getPosition(), concat.get(concat.size()-1).getPosition()));
+		}else if (leftOp instanceof ADQLColumn){
+			((ADQLColumn)leftOp).setExpectedType('S');
 		}
 	  return leftOp;
 	}
diff --git a/test/adql/parser/TestADQLParser.java b/test/adql/parser/TestADQLParser.java
index f46d1e1..6e3a76c 100644
--- a/test/adql/parser/TestADQLParser.java
+++ b/test/adql/parser/TestADQLParser.java
@@ -55,6 +55,32 @@ public class TestADQLParser {
 	public void tearDown() throws Exception {
 	}
 
+	@Test
+	public void testNumericFunctionParams() {
+
+		ADQLParser parser = new ADQLParser(ADQLVersion.V2_1);
+
+		/* CASE: LOWER can only take a string in parameter, but according to the
+		 *       grammar (and BNF), an unsigned numeric is a string (??).
+		 *       In such case, an error should be raised: */
+		try {
+			parser.parseQuery("SELECT LOWER(123) FROM foo");
+			fail("LOWER can not take a numeric in parameter.");
+		} catch(Exception ex) {
+			assertEquals(ParseException.class, ex.getClass());
+			assertEquals("Incorrect argument: The ADQL function LOWER must have one parameter of type VARCHAR (i.e. a String)!", ex.getMessage());
+		}
+
+		// CASE: Idem for a second parameter:
+		try {
+			parser.parseQuery("SELECT IN_UNIT(12.3, 123) FROM foo");
+			fail("IN_UNIT can not take a numeric in 2nd parameter.");
+		} catch(Exception ex) {
+			assertEquals(ParseException.class, ex.getClass());
+			assertEquals("Incorrect argument: The 2nd argument of the ADQL function IN_UNIT (i.e. target unit) must be of type VARCHAR (i.e. a string)!", ex.getMessage());
+		}
+	}
+
 	@Test
 	public void testOperatorsPrecedence() {
 
-- 
GitLab