From 225c49e1c12c6b1539af92ac357efb75534f927c Mon Sep 17 00:00:00 2001
From: gmantele <gmantele@ari.uni-heidelberg.de>
Date: Thu, 20 Apr 2017 19:25:23 +0200
Subject: [PATCH] [ADQL] Grouping by a SELECTed item's alias was not possible
 any more since the commit 8e2fa9ffc78e92d9b40caaeb97e55f1e7f931e26.

---
 src/adql/db/DBChecker.java      | 75 ++++++++++++++++++++++++++++++++-
 test/adql/db/TestDBChecker.java |  8 ++++
 2 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/src/adql/db/DBChecker.java b/src/adql/db/DBChecker.java
index 8e6b8ae..2f476e5 100644
--- a/src/adql/db/DBChecker.java
+++ b/src/adql/db/DBChecker.java
@@ -42,6 +42,7 @@ import adql.parser.QueryChecker;
 import adql.query.ADQLIterator;
 import adql.query.ADQLObject;
 import adql.query.ADQLQuery;
+import adql.query.ClauseADQL;
 import adql.query.ClauseSelect;
 import adql.query.ColumnReference;
 import adql.query.IdentifierField;
@@ -615,7 +616,7 @@ public class DBChecker implements QueryChecker {
 		ISearchHandler sHandler;
 
 		// Check the existence of all columns:
-		sHandler = new SearchColumnHandler();
+		sHandler = new SearchColumnOutsideGroupByHandler();
 		sHandler.search(query);
 		for(ADQLObject result : sHandler){
 			try{
@@ -630,12 +631,30 @@ public class DBChecker implements QueryChecker {
 			}
 		}
 
+		// Check the GROUP BY items:
+		ClauseSelect select = query.getSelect();
+		sHandler = new SearchColumnHandler();
+		sHandler.search(query.getGroupBy());
+		for(ADQLObject result : sHandler){
+			try{
+				ADQLColumn adqlColumn = (ADQLColumn)result;
+				// resolve the column:
+				DBColumn dbColumn = checkGroupByItem(adqlColumn, select, list);
+				// link with the matched DBColumn:
+				if (dbColumn != null){
+					adqlColumn.setDBLink(dbColumn);
+					adqlColumn.setAdqlTable(mapTables.get(dbColumn.getTable()));
+				}
+			}catch(ParseException pe){
+				errors.addException(pe);
+			}
+		}
+
 		// Check the correctness of all column references (= references to selected columns):
 		/* Note: no need to provide the father tables when resolving column references,
 		 *       because no father column can be used in ORDER BY. */
 		sHandler = new SearchColReferenceHandler();
 		sHandler.search(query);
-		ClauseSelect select = query.getSelect();
 		for(ADQLObject result : sHandler){
 			try{
 				ColumnReference colRef = (ColumnReference)result;
@@ -695,6 +714,38 @@ public class DBChecker implements QueryChecker {
 		}
 	}
 
+	/**
+	 * Check whether the given column corresponds to a selected item's alias or to an existing column.
+	 * 
+	 * @param col			The column to check.
+	 * @param select		The SELECT clause of the ADQL query.
+	 * @param dbColumns		The list of all available columns.
+	 * 
+	 * @return 	The corresponding {@link DBColumn} if this column corresponds to an existing column,
+	 *        	<i>NULL</i> otherwise.
+	 * 
+	 * @throws ParseException	An {@link UnresolvedColumnException} if the given column can't be resolved
+	 * 							or an {@link UnresolvedTableException} if its table reference can't be resolved.
+	 * 
+	 * @see ClauseSelect#searchByAlias(String)
+	 * @see #resolveColumn(ADQLColumn, SearchColumnList, Stack)
+	 * 
+	 * @since 1.4
+	 */
+	protected DBColumn checkGroupByItem(final ADQLColumn col, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException{
+		/* If the column name is not qualified, it may be a SELECT-item's alias.
+		 * So, try resolving the name as an alias.
+		 * If it fails, perform the normal column resolution.*/
+		if (col.getTableName() == null){
+			ArrayList<SelectItem> founds = select.searchByAlias(col.getColumnName(), col.isCaseSensitive(IdentifierField.COLUMN));
+			if (founds.size() == 1)
+				return null;
+			else if (founds.size() > 1)
+				throw new UnresolvedColumnException(col, founds.get(0).getAlias(), founds.get(1).getAlias());
+		}
+		return resolveColumn(col, dbColumns, null);
+	}
+
 	/**
 	 * Check whether the given column reference corresponds to a selected item (column or an expression with an alias)
 	 * or to an existing column.
@@ -1245,6 +1296,26 @@ public class DBChecker implements QueryChecker {
 	/* *************** */
 	/* SEARCH HANDLERS */
 	/* *************** */
+
+	/**
+	 * Lets searching all {@link ADQLColumn} in the given object, EXCEPT in the GROUP BY clause.
+	 * 
+	 * <p>
+	 * 	{@link ADQLColumn}s of the GROUP BY may be aliases and so, they can not be checked
+	 *	exactly as a normal column.
+	 * </p>
+	 * 
+	 * @author Gr&eacute;gory Mantelet (ARI)
+	 * @version 1.4 (04/2017)
+	 * @since 1.4
+	 */
+	private static class SearchColumnOutsideGroupByHandler extends SearchColumnHandler {
+		@Override
+		protected boolean goInto(final ADQLObject obj){
+			return !(obj instanceof ClauseADQL<?> && ((ClauseADQL<?>)obj).getName().equalsIgnoreCase("GROUP BY")) && super.goInto(obj);
+		}
+	}
+
 	/**
 	 * Lets searching all tables.
 	 * 
diff --git a/test/adql/db/TestDBChecker.java b/test/adql/db/TestDBChecker.java
index aec904d..c2b978a 100644
--- a/test/adql/db/TestDBChecker.java
+++ b/test/adql/db/TestDBChecker.java
@@ -106,6 +106,8 @@ public class TestDBChecker {
 			parser.parseQuery("SELECT foo.colI, COUNT(*) AS cnt FROM foo GROUP BY foo.colI");
 			// Qualified with the table alias:
 			parser.parseQuery("SELECT f.colI, COUNT(*) AS cnt FROM foo AS f GROUP BY f.colI");
+			// With the SELECT item alias:
+			parser.parseQuery("SELECT colI AS mycol, COUNT(*) AS cnt FROM foo GROUP BY mycol");
 		}catch(ParseException pe){
 			pe.printStackTrace();
 			fail();
@@ -156,9 +158,15 @@ public class TestDBChecker {
 	public void testColRefWithDottedAlias(){
 		ADQLParser parser = new ADQLParser(new DBChecker(tables));
 		try{
+			// ORDER BY
 			ADQLQuery adql = parser.parseQuery("SELECT colI AS \"col.I\" FROM aschema.foo ORDER BY \"col.I\"");
 			assertNotNull(adql);
 			assertEquals("SELECT \"aschema\".\"foo\".\"colI\" AS \"col.I\"\nFROM \"aschema\".\"foo\"\nORDER BY \"col.I\" ASC", (new PostgreSQLTranslator()).translate(adql));
+
+			// GROUP BY
+			adql = parser.parseQuery("SELECT colI AS \"col.I\" FROM aschema.foo GROUP BY \"col.I\"");
+			assertNotNull(adql);
+			assertEquals("SELECT \"aschema\".\"foo\".\"colI\" AS \"col.I\"\nFROM \"aschema\".\"foo\"\nGROUP BY \"col.I\"", (new PostgreSQLTranslator()).translate(adql));
 		}catch(ParseException pe){
 			pe.printStackTrace();
 			fail();
-- 
GitLab