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é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