From fc38da51053847d3dd5369bb2bf4f84e4e854cc1 Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Wed, 9 Apr 2014 17:44:27 +0200 Subject: [PATCH] ADQL: Fix an ADQL bug (raised by Dave Morris) in the management of subqueries: before, it was impossible to use (in a clause different from the FROM) columns of a father query inside a subquery. --- src/adql/db/DBChecker.java | 133 +++++- src/adql/parser/ADQLParser.java | 473 ++++++++++---------- src/adql/parser/ADQLParserConstants.java | 1 - src/adql/parser/ADQLParserTokenManager.java | 1 - src/adql/parser/QueryChecker.java | 15 +- src/adql/parser/adqlGrammar.jj | 23 +- src/adql/query/ClauseADQL.java | 10 +- 7 files changed, 392 insertions(+), 264 deletions(-) diff --git a/src/adql/db/DBChecker.java b/src/adql/db/DBChecker.java index 26de9e1..458e9b1 100644 --- a/src/adql/db/DBChecker.java +++ b/src/adql/db/DBChecker.java @@ -17,18 +17,21 @@ package adql.db; * along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2011,2013-2014 - UDS/Centre de Données astronomiques de Strasbourg (CDS), - * Astronomishes Rechen Institute (ARI) + * Astronomishes Rechen Institute (ARI) */ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; +import java.util.Stack; import adql.db.exception.UnresolvedColumnException; import adql.db.exception.UnresolvedIdentifiersException; import adql.db.exception.UnresolvedTableException; import adql.parser.ParseException; import adql.parser.QueryChecker; +import adql.query.ADQLIterator; import adql.query.ADQLObject; import adql.query.ADQLQuery; import adql.query.ClauseSelect; @@ -37,6 +40,7 @@ import adql.query.IdentifierField; import adql.query.SelectAllColumns; import adql.query.SelectItem; import adql.query.from.ADQLTable; +import adql.query.from.FromContent; import adql.query.operand.ADQLColumn; import adql.search.ISearchHandler; import adql.search.SearchColumnHandler; @@ -60,7 +64,7 @@ import adql.search.SimpleSearchHandler; * </i></p> * * @author Grégory Mantelet (CDS;ARI) - * @version 1.1 (11/2013) + * @version 1.2 (04/2014) */ public class DBChecker implements QueryChecker { @@ -111,6 +115,26 @@ public class DBChecker implements QueryChecker { /* ************* */ /* CHECK METHODS */ /* ************* */ + /** + * <p>Check all the columns, tables and UDFs references inside the given query.</p> + * + * <p><i> + * <u>Note:</u> This query has already been parsed ; thus it is already syntactically correct. + * Only the consistency with the published tables, columns and all the defined UDFs must be checked. + * </i></p> + * + * @param query The query to check. + * @param fatherColumns List of all columns available in the father query. + * + * @throws ParseException An {@link UnresolvedIdentifiersException} if some tables or columns can not be resolved. + * + * @see #check(ADQLQuery, Stack) + */ + @Override + public void check(final ADQLQuery query) throws ParseException{ + check(query, null); + } + /** * Followed algorithm: * <pre> @@ -150,17 +174,19 @@ public class DBChecker implements QueryChecker { * End * </pre> * - * @param query The query to check. + * @param query The query to check. + * @param fathersList List of all columns available in the father query. * - * @throws ParseException An {@link UnresolvedIdentifiersException} if some tables or columns can not be resolved. + * @throws UnresolvedIdentifiersException An {@link UnresolvedIdentifiersException} if some tables or columns can not be resolved. + * + * @since 1.2 * * @see #resolveTable(ADQLTable) * @see #generateDBTable(ADQLQuery, String) * @see #resolveColumn(ADQLColumn, SearchColumnList) * @see #checkColumnReference(ColumnReference, ClauseSelect, SearchColumnList) */ - @Override - public void check(final ADQLQuery query) throws ParseException{ + protected void check(final ADQLQuery query, Stack<SearchColumnList> fathersList) throws UnresolvedIdentifiersException{ UnresolvedIdentifiersException errors = new UnresolvedIdentifiersException(); HashMap<DBTable,ADQLTable> mapTables = new HashMap<DBTable,ADQLTable>(); ISearchHandler sHandler; @@ -171,15 +197,20 @@ public class DBChecker implements QueryChecker { for(ADQLObject result : sHandler){ try{ ADQLTable table = (ADQLTable)result; + // resolve the table: DBTable dbTable = null; if (table.isSubQuery()){ + // check the subquery tables: + check(table.getSubQuery(), fathersList); + // generate its DBTable: dbTable = generateDBTable(table.getSubQuery(), table.getAlias()); }else{ dbTable = resolveTable(table); if (table.hasAlias()) dbTable = dbTable.copy(dbTable.getDBName(), table.getAlias()); } + // link with the matched DBTable: table.setDBLink(dbTable); mapTables.put(dbTable, table); @@ -189,6 +220,10 @@ public class DBChecker implements QueryChecker { } // Attach table information on wildcards with the syntax "{tableName}.*" of the SELECT clause: + /* Note: no need to check the table name among the father tables, because there is + * no interest to select a father column in a subquery + * (which can return only one column ; besides, no aggregate is not allowed + * in subqueries).*/ sHandler = new SearchWildCardHandler(); sHandler.search(query.getSelect()); for(ADQLObject result : sHandler){ @@ -213,6 +248,7 @@ public class DBChecker implements QueryChecker { } } + // Get the list of all columns made available in the clause FROM: SearchColumnList list; try{ list = query.getFrom().getDBColumns(); @@ -228,7 +264,7 @@ public class DBChecker implements QueryChecker { try{ ADQLColumn adqlColumn = (ADQLColumn)result; // resolve the column: - DBColumn dbColumn = resolveColumn(adqlColumn, list); + DBColumn dbColumn = resolveColumn(adqlColumn, list, fathersList); // link with the matched DBColumn: adqlColumn.setDBLink(dbColumn); adqlColumn.setAdqlTable(mapTables.get(dbColumn.getTable())); @@ -238,6 +274,8 @@ public class DBChecker implements QueryChecker { } // Check the correctness of all column references: + /* Note: no need to provide the father tables when resolving column references, + * because no father column can be used in ORDER BY and/or GROUP BY. */ sHandler = new SearchColReferenceHandler(); sHandler.search(query); ClauseSelect select = query.getSelect(); @@ -255,6 +293,32 @@ public class DBChecker implements QueryChecker { } } + // Check subqueries outside the clause FROM: + sHandler = new SearchSubQueryHandler(); + sHandler.search(query); + if (sHandler.getNbMatch() > 0){ + + // Push the list of columns in the father columns stack: + if (fathersList == null) + fathersList = new Stack<SearchColumnList>(); + fathersList.push(list); + + // Check each found subquery (except the first one because it is the current query): + for(ADQLObject result : sHandler){ + try{ + check((ADQLQuery)result, fathersList); + }catch(UnresolvedIdentifiersException uie){ + Iterator<ParseException> itPe = uie.getErrors(); + while(itPe.hasNext()) + errors.addException(itPe.next()); + } + } + + // Pop the list of columns from the father columns stack: + fathersList.pop(); + + } + // Throw all errors if any: if (errors.getNbErrors() > 0) throw errors; @@ -284,17 +348,21 @@ public class DBChecker implements QueryChecker { } /** - * Resolves the given column, that's to say searches for the corresponding {@link DBColumn}. + * <p>Resolves the given column, that's to say searches for the corresponding {@link DBColumn}.</p> + * <p>The third parameter is used only if this function is called inside a subquery. In this case, + * column is tried to be resolved with the first list (dbColumns). If no match is found, + * the resolution is tried with the father columns list (fatherColumns).</p> * * @param column The column to resolve. * @param dbColumns List of all available {@link DBColumn}s. + * @param fathersList List of all columns available in the father query ; a list for each father-level. * - * @return The corresponding {@link DBColumn} if found, <i>null</i> otherwise. + * @return The corresponding {@link DBColumn} if found. Otherwise an exception is thrown. * * @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. */ - protected DBColumn resolveColumn(final ADQLColumn column, final SearchColumnList dbColumns) throws ParseException{ + protected DBColumn resolveColumn(final ADQLColumn column, final SearchColumnList dbColumns, Stack<SearchColumnList> fathersList) throws ParseException{ ArrayList<DBColumn> foundColumns = dbColumns.search(column); // good if only one column has been found: @@ -307,8 +375,15 @@ public class DBChecker implements QueryChecker { else throw new UnresolvedTableException(column, (foundColumns.get(0).getTable() == null) ? "<NULL>" : foundColumns.get(0).getTable().getADQLName(), (foundColumns.get(1).getTable() == null) ? "<NULL>" : foundColumns.get(1).getTable().getADQLName()); }// otherwise (no match): unknown column ! - else - throw new UnresolvedColumnException(column); + else{ + if (fathersList == null || fathersList.isEmpty()) + throw new UnresolvedColumnException(column); + else{ + Stack<SearchColumnList> subStack = new Stack<SearchColumnList>(); + subStack.addAll(fathersList.subList(0, fathersList.size() - 1)); + return resolveColumn(column, fathersList.peek(), subStack); + } + } } /** @@ -325,7 +400,7 @@ public class DBChecker implements QueryChecker { * or an {@link UnresolvedTableException} if its table reference can't be resolved. * * @see ClauseSelect#searchByAlias(String) - * @see #resolveColumn(ADQLColumn, SearchColumnList) + * @see #resolveColumn(ADQLColumn, SearchColumnList, SearchColumnList) */ protected DBColumn checkColumnReference(final ColumnReference colRef, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException{ if (colRef.isIndex()){ @@ -352,7 +427,7 @@ public class DBChecker implements QueryChecker { } // check the corresponding column: - return resolveColumn(col, dbColumns); + return resolveColumn(col, dbColumns, null); } } @@ -422,4 +497,34 @@ public class DBChecker implements QueryChecker { } } + /** + * <p>Lets searching subqueries in every clause except the FROM one (hence the modification of the {@link #goInto(ADQLObject)}.</p> + * + * <p><i> + * <u>Note:</u> The function {@link #addMatch(ADQLObject, ADQLIterator)} has been modified in order to + * not have the root search object (here: the main query) in the list of results. + * </i></p> + * + * @author Grégory Mantelet (ARI) + * @version 1.2 (12/2013) + * @since 1.2 + */ + private static class SearchSubQueryHandler extends SimpleSearchHandler { + @Override + protected void addMatch(ADQLObject matchObj, ADQLIterator it){ + if (it != null) + super.addMatch(matchObj, it); + } + + @Override + protected boolean goInto(ADQLObject obj){ + return super.goInto(obj) && !(obj instanceof FromContent); + } + + @Override + protected boolean match(ADQLObject obj){ + return (obj instanceof ADQLQuery); + } + } + } diff --git a/src/adql/parser/ADQLParser.java b/src/adql/parser/ADQLParser.java index cd0c151..e7e2a66 100644 --- a/src/adql/parser/ADQLParser.java +++ b/src/adql/parser/ADQLParser.java @@ -1,53 +1,68 @@ /* Generated By:JavaCC: Do not edit this line. ADQLParser.java */ package adql.parser; -import java.util.Stack; -import java.util.Vector; -import java.util.ArrayList; -import java.util.Collection; - import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Stack; +import java.util.Vector; import adql.db.exception.UnresolvedIdentifiersException; - -import adql.parser.IdentifierItems.IdentifierItem; - import adql.parser.ADQLQueryFactory.JoinType; - -import adql.query.*; -import adql.query.from.*; -import adql.query.constraint.*; - -import adql.query.operand.*; - -import adql.query.operand.function.*; - -import adql.query.operand.function.geometry.*; +import adql.parser.IdentifierItems.IdentifierItem; +import adql.query.ADQLOrder; +import adql.query.ADQLQuery; +import adql.query.ClauseADQL; +import adql.query.ClauseConstraints; +import adql.query.ClauseSelect; +import adql.query.ColumnReference; +import adql.query.SelectAllColumns; +import adql.query.SelectItem; +import adql.query.TextPosition; +import adql.query.constraint.ADQLConstraint; +import adql.query.constraint.Between; +import adql.query.constraint.Comparison; +import adql.query.constraint.ComparisonOperator; +import adql.query.constraint.ConstraintsGroup; +import adql.query.constraint.In; +import adql.query.from.ADQLJoin; +import adql.query.from.FromContent; +import adql.query.operand.ADQLColumn; +import adql.query.operand.ADQLOperand; +import adql.query.operand.Concatenation; +import adql.query.operand.OperationType; +import adql.query.operand.StringConstant; +import adql.query.operand.function.ADQLFunction; +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.UserDefinedFunction; +import adql.query.operand.function.geometry.GeometryFunction; import adql.query.operand.function.geometry.GeometryFunction.GeometryValue; - +import adql.query.operand.function.geometry.PointFunction; import adql.translator.PostgreSQLTranslator; import adql.translator.TranslationException; /** - * <p>Parses an ADQL query thanks to the {@link ADQLParser#Query()} function. </p> - * - * <p>This parser is able, thanks to a {@link QueryChecker} object, to check each ADQLQuery just after its generation. - * It could be used to check the consistency between the ADQL query to parse and the "database" on which the query must be executed. - * By default, there is no {@link QueryChecker}. Thus you must extend {@link QueryChecker} to check semantically all generated ADQLQuery objects.</p> - * - * <p>To create an object representation of the given ADQL query, this parser uses a {@link ADQLQueryFactory} object. So if you want customize some object (ie. CONTAINS) of this representation - * you just have to extend the corresponding default object (ie. ContainsFunction) and to extend the corresponding function of {@link ADQLQueryFactory} (ie. createContains(...)).</p> - * - * <p><b><u>WARNING:</u> To modify this class it's strongly encouraged to modify the .jj file in the section between <i>PARSER_BEGIN</i> and <i>PARSER_END</i> and to re-compile it with JavaCC.</b></p> - * - * @see QueryChecker - * @see ADQLQueryFactory - * - * @author Grégory Mantelet (CDS) - gregory.mantelet@astro.unistra.fr - * @version January 2012 - */ -@SuppressWarnings("all") +* <p>Parses an ADQL query thanks to the {@link ADQLParser#Query()} function. </p> +* +* <p>This parser is able, thanks to a {@link QueryChecker} object, to check each ADQLQuery just after its generation. +* It could be used to check the consistency between the ADQL query to parse and the "database" on which the query must be executed. +* By default, there is no {@link QueryChecker}. Thus you must extend {@link QueryChecker} to check semantically all generated ADQLQuery objects.</p> +* +* <p>To create an object representation of the given ADQL query, this parser uses a {@link ADQLQueryFactory} object. So if you want customize some object (ie. CONTAINS) of this representation +* you just have to extend the corresponding default object (ie. ContainsFunction) and to extend the corresponding function of {@link ADQLQueryFactory} (ie. createContains(...)).</p> +* +* <p><b><u>WARNING:</u> To modify this class it's strongly encouraged to modify the .jj file in the section between <i>PARSER_BEGIN</i> and <i>PARSER_END</i> and to re-compile it with JavaCC.</b></p> +* +* @see QueryChecker +* @see ADQLQueryFactory +* +* @author Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de +* @version 1.2 (12/2013) +*/ public class ADQLParser implements ADQLParserConstants { /** Tools to build the object representation of the ADQL query. */ @@ -69,18 +84,18 @@ public class ADQLParser implements ADQLParserConstants { private ArrayList<String> allowedCoordSys = new ArrayList<String>(); /** - * Builds an ADQL parser without a query to parse. - */ + * Builds an ADQL parser without a query to parse. + */ public ADQLParser(){ this(new java.io.ByteArrayInputStream("".getBytes())); } /** - * Builds an ADQL parser without a query to parse but with a QueryChecker and a ADQLQueryFactory. - * - * @param checker The object to use to check each ADQLQuery. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds an ADQL parser without a query to parse but with a QueryChecker and a ADQLQueryFactory. + * + * @param checker The object to use to check each ADQLQuery. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(QueryChecker checker, ADQLQueryFactory factory){ this(); @@ -91,30 +106,30 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Builds an ADQL parser without a query to parse but with a QueryChecker. - * - * @param checker The object to use to check each ADQLQuery. - */ + * Builds an ADQL parser without a query to parse but with a QueryChecker. + * + * @param checker The object to use to check each ADQLQuery. + */ public ADQLParser(QueryChecker checker){ this(checker, null); } /** - * Builds an ADQL parser without a query to parse but with a ADQLQueryFactory. - * - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds an ADQL parser without a query to parse but with a ADQLQueryFactory. + * + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(ADQLQueryFactory factory){ this((QueryChecker)null, factory); } /** - * Builds a parser with a stream containing the query to parse. - * - * @param stream The stream in which the ADQL query to parse is given. - * @param checker The object to use to check each ADQLQuery. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with a stream containing the query to parse. + * + * @param stream The stream in which the ADQL query to parse is given. + * @param checker The object to use to check each ADQLQuery. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory){ this(stream); @@ -125,33 +140,33 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Builds a parser with a stream containing the query to parse. - * - * @param stream The stream in which the ADQL query to parse is given. - * @param checker The object to use to check each ADQLQuery. - */ + * Builds a parser with a stream containing the query to parse. + * + * @param stream The stream in which the ADQL query to parse is given. + * @param checker The object to use to check each ADQLQuery. + */ public ADQLParser(java.io.InputStream stream, QueryChecker checker){ this(stream, checker, null); } /** - * Builds a parser with a stream containing the query to parse. - * - * @param stream The stream in which the ADQL query to parse is given. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with a stream containing the query to parse. + * + * @param stream The stream in which the ADQL query to parse is given. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(java.io.InputStream stream, ADQLQueryFactory factory){ this(stream, (QueryChecker)null, factory); } /** - * Builds a parser with a stream containing the query to parse. - * - * @param stream The stream in which the ADQL query to parse is given. - * @param encoding The supplied encoding. - * @param checker The object to use to check each ADQLQuery. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with a stream containing the query to parse. + * + * @param stream The stream in which the ADQL query to parse is given. + * @param encoding The supplied encoding. + * @param checker The object to use to check each ADQLQuery. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory){ this(stream, encoding); @@ -162,34 +177,34 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Builds a parser with a stream containing the query to parse. - * - * @param stream The stream in which the ADQL query to parse is given. - * @param encoding The supplied encoding. - * @param checker The object to use to check each ADQLQuery. - */ + * Builds a parser with a stream containing the query to parse. + * + * @param stream The stream in which the ADQL query to parse is given. + * @param encoding The supplied encoding. + * @param checker The object to use to check each ADQLQuery. + */ public ADQLParser(java.io.InputStream stream, String encoding, QueryChecker checker){ this(stream, encoding, checker, null); } /** - * Builds a parser with a stream containing the query to parse. - * - * @param stream The stream in which the ADQL query to parse is given. - * @param encoding The supplied encoding. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with a stream containing the query to parse. + * + * @param stream The stream in which the ADQL query to parse is given. + * @param encoding The supplied encoding. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(java.io.InputStream stream, String encoding, ADQLQueryFactory factory){ this(stream, encoding, null, factory); } /** - * Builds a parser with a reader containing the query to parse. - * - * @param reader The reader in which the ADQL query to parse is given. - * @param checker The object to use to check each ADQLQuery. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with a reader containing the query to parse. + * + * @param reader The reader in which the ADQL query to parse is given. + * @param checker The object to use to check each ADQLQuery. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory){ this(reader); @@ -200,32 +215,32 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Builds a parser with a reader containing the query to parse. - * - * @param reader The reader in which the ADQL query to parse is given. - * @param checker The object to use to check each ADQLQuery. - */ + * Builds a parser with a reader containing the query to parse. + * + * @param reader The reader in which the ADQL query to parse is given. + * @param checker The object to use to check each ADQLQuery. + */ public ADQLParser(java.io.Reader reader, QueryChecker checker){ this(reader, checker, null); } /** - * Builds a parser with a reader containing the query to parse. - * - * @param reader The reader in which the ADQL query to parse is given. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with a reader containing the query to parse. + * + * @param reader The reader in which the ADQL query to parse is given. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(java.io.Reader reader, ADQLQueryFactory factory){ this(reader, null, factory); } /** - * Builds a parser with another token manager. - * - * @param tm The manager which associates a token to a numeric code. - * @param checker The object to use to check each ADQLQuery. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with another token manager. + * + * @param tm The manager which associates a token to a numeric code. + * @param checker The object to use to check each ADQLQuery. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(ADQLParserTokenManager tm, QueryChecker checker, ADQLQueryFactory factory){ this(tm); @@ -236,33 +251,33 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Builds a parser with another token manager. - * - * @param tm The manager which associates a token to a numeric code. - * @param checker The object to use to check each ADQLQuery. - */ + * Builds a parser with another token manager. + * + * @param tm The manager which associates a token to a numeric code. + * @param checker The object to use to check each ADQLQuery. + */ public ADQLParser(ADQLParserTokenManager tm, QueryChecker checker){ this(tm, checker, null); } /** - * Builds a parser with another token manager. - * - * @param tm The manager which associates a token to a numeric code. - * @param factory The object to use to build an object representation of the given ADQL query. - */ + * Builds a parser with another token manager. + * + * @param tm The manager which associates a token to a numeric code. + * @param factory The object to use to build an object representation of the given ADQL query. + */ public ADQLParser(ADQLParserTokenManager tm, ADQLQueryFactory factory){ this(tm, null, factory); } /** - * Parses the query given at the creation of this parser or in the <i>ReInit</i> functions. - * - * @return The object representation of the given ADQL query. - * @throws ParseException If there is at least one syntactic error. - * - * @see ADQLParser#Query() - */ + * Parses the query given at the creation of this parser or in the <i>ReInit</i> functions. + * + * @return The object representation of the given ADQL query. + * @throws ParseException If there is at least one syntactic error. + * + * @see ADQLParser#Query() + */ public final ADQLQuery parseQuery() throws ParseException{ stackQuery.clear(); query = null; @@ -270,16 +285,16 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Parses the query given in parameter. - * - * @param q The ADQL query to parse. - * @return The object representation of the given ADQL query. - * @throws ParseException If there is at least one syntactic error. - * - * @see ADQLParser#ReInit(java.io.InputStream) - * @see ADQLParser#setDebug(boolean) - * @see ADQLParser#Query() - */ + * Parses the query given in parameter. + * + * @param q The ADQL query to parse. + * @return The object representation of the given ADQL query. + * @throws ParseException If there is at least one syntactic error. + * + * @see ADQLParser#ReInit(java.io.InputStream) + * @see ADQLParser#setDebug(boolean) + * @see ADQLParser#Query() + */ public final ADQLQuery parseQuery(String q) throws ParseException{ stackQuery.clear(); query = null; @@ -288,16 +303,16 @@ public class ADQLParser implements ADQLParserConstants { } /** - * Parses the query contained in the stream given in parameter. - * - * @param stream The stream which contains the ADQL query to parse. - * @return The object representation of the given ADQL query. - * @throws ParseException If there is at least one syntactic error. - * - * @see ADQLParser#ReInit(java.io.InputStream) - * @see ADQLParser#setDebug(boolean) - * @see ADQLParser#Query() - */ + * Parses the query contained in the stream given in parameter. + * + * @param stream The stream which contains the ADQL query to parse. + * @return The object representation of the given ADQL query. + * @throws ParseException If there is at least one syntactic error. + * + * @see ADQLParser#ReInit(java.io.InputStream) + * @see ADQLParser#setDebug(boolean) + * @see ADQLParser#Query() + */ public final ADQLQuery parseQuery(java.io.InputStream stream) throws ParseException{ stackQuery.clear(); query = null; @@ -356,13 +371,13 @@ public class ADQLParser implements ADQLParserConstants { } /** - * <p>Gets the specified ADQL query and parses the given ADQL query. The SQL translation is then printed if the syntax is correct.</p> - * <p><b>ONLY the syntax is checked: the query is NOT EXECUTED !</b></p> - * <p>Supplied parameters are: <ul><li>[-debug] -url http://...</li><li>[-debug] -file ...</li><li>[-debug] -query SELECT...</li></ul></p> - * - * @param args - * @throws Exception - */ + * <p>Gets the specified ADQL query and parses the given ADQL query. The SQL translation is then printed if the syntax is correct.</p> + * <p><b>ONLY the syntax is checked: the query is NOT EXECUTED !</b></p> + * <p>Supplied parameters are: <ul><li>[-debug] -url http://...</li><li>[-debug] -file ...</li><li>[-debug] -query SELECT...</li></ul></p> + * + * @param args + * @throws Exception + */ public static final void main(String[] args) throws Exception{ final String USAGE = "Usage:\u005cn\u005ctadqlParser.jar [-d] [-v] [-e] [-a|-s] [<FILE>|<URL>]\u005cn\u005cnNOTE: If no file or URL is given, the ADQL query is expected in the standard input. This query must end with a ';' !\u005cn\u005cnParameters:\u005cn\u005ct-v or --verbose : Print the main steps of the parsing\u005cn\u005ct-d or --debug : Print stack traces when a grave error occurs\u005cn\u005ct-e or --explain : Explain the ADQL parsing (or Expand the parsing tree)\u005cn\u005ct-a or --adql : Display the understood ADQL query\u005cn\u005ct-s or --sql : Ask the SQL translation of the given ADQL query (SQL compatible with PostgreSQL)\u005cn\u005cnReturn:\u005cn\u005ctBy default: nothing if the query is correct. Otherwise a message explaining why the query is not correct is displayed.\u005cn\u005ctWith the -s option, the SQL translation of the given ADQL query will be returned.\u005cn\u005ctWith the -a option, the ADQL query is returned as it has been understood.\u005cn\u005cnExit status:\u005cn\u005ct0\u005ctOK !\u005cn\u005ct1\u005ctParameter error (missing or incorrect parameter)\u005cn\u005ct2\u005ctFile error (incorrect file/url, reading error, ...)\u005cn\u005ct3\u005ctParsing error (syntactic or semantic error)\u005cn\u005ct4\u005ctTranslation error (a problem has occurred during the translation of the given ADQL query in SQL)."; @@ -471,12 +486,12 @@ public class ADQLParser implements ADQLParserConstants { /* GENERAL ADQL SYNTAX */ /* ******************* */ /** - * Parses the ADQL query given at the parser creation or in the {@link ADQLParser#ReInit(java.io.InputStream)} - * or in the <i>parseQuery</i> functions. - * - * @return The object representation of the query. - * @throws ParseException If the query syntax is incorrect. - */ + * Parses the ADQL query given at the parser creation or in the {@link ADQLParser#ReInit(java.io.InputStream)} + * or in the <i>parseQuery</i> functions. + * + * @return The object representation of the query. + * @throws ParseException If the query syntax is incorrect. + */ final public ADQLQuery Query() throws ParseException{ trace_call("Query"); try{ @@ -494,6 +509,10 @@ public class ADQLParser implements ADQLParserConstants { jj_consume_token(-1); throw new ParseException(); } + // check the query: + if (queryChecker != null) + queryChecker.check(q); + { if (true) return q; @@ -551,10 +570,6 @@ public class ADQLParser implements ADQLParserConstants { jj_la1[4] = jj_gen; ; } - // check the query: - if (queryChecker != null) - queryChecker.check(query); - // get the previous query (!= null if the current query is a sub-query): ADQLQuery previousQuery = stackQuery.pop(); if (stackQuery.isEmpty()) @@ -3549,34 +3564,6 @@ public class ADQLParser implements ADQLParserConstants { } } - private boolean jj_3R_165(){ - if (jj_scan_token(NOT)) - return true; - return false; - } - - private boolean jj_3R_44(){ - if (jj_scan_token(SELECT)) - return true; - Token xsp; - xsp = jj_scanpos; - if (jj_3R_136()) - jj_scanpos = xsp; - xsp = jj_scanpos; - if (jj_3R_137()) - jj_scanpos = xsp; - if (jj_3R_138()) - return true; - while(true){ - xsp = jj_scanpos; - if (jj_3R_139()){ - jj_scanpos = xsp; - break; - } - } - return false; - } - private boolean jj_3R_16(){ if (jj_scan_token(LEFT_PAR)) return true; @@ -3616,20 +3603,32 @@ public class ADQLParser implements ADQLParserConstants { return false; } - private boolean jj_3R_111(){ - if (jj_3R_23()) + private boolean jj_3R_120(){ + if (jj_3R_143()) return true; return false; } - private boolean jj_3R_163(){ - if (jj_3R_49()) + private boolean jj_3R_119(){ + if (jj_3R_142()) return true; return false; } - private boolean jj_3R_120(){ - if (jj_3R_143()) + private boolean jj_3R_118(){ + if (jj_3R_141()) + return true; + return false; + } + + private boolean jj_3R_111(){ + if (jj_3R_23()) + return true; + return false; + } + + private boolean jj_3R_163(){ + if (jj_3R_49()) return true; return false; } @@ -3645,18 +3644,6 @@ public class ADQLParser implements ADQLParserConstants { return false; } - private boolean jj_3R_119(){ - if (jj_3R_142()) - return true; - return false; - } - - private boolean jj_3R_118(){ - if (jj_3R_141()) - return true; - return false; - } - private boolean jj_3R_32(){ if (jj_3R_49()) return true; @@ -3689,20 +3676,6 @@ public class ADQLParser implements ADQLParserConstants { return false; } - private boolean jj_3R_65(){ - if (jj_3R_41()) - return true; - Token xsp; - while(true){ - xsp = jj_scanpos; - if (jj_3R_113()){ - jj_scanpos = xsp; - break; - } - } - return false; - } - private boolean jj_3R_28(){ if (jj_3R_44()) return true; @@ -3724,6 +3697,20 @@ public class ADQLParser implements ADQLParserConstants { return false; } + private boolean jj_3R_65(){ + if (jj_3R_41()) + return true; + Token xsp; + while(true){ + xsp = jj_scanpos; + if (jj_3R_113()){ + jj_scanpos = xsp; + break; + } + } + return false; + } + private boolean jj_3R_183(){ if (jj_scan_token(COMMA)) return true; @@ -4857,6 +4844,12 @@ public class ADQLParser implements ADQLParserConstants { return false; } + private boolean jj_3_3(){ + if (jj_3R_17()) + return true; + return false; + } + private boolean jj_3R_100(){ Token xsp; xsp = jj_scanpos; @@ -4880,12 +4873,6 @@ public class ADQLParser implements ADQLParserConstants { return false; } - private boolean jj_3_3(){ - if (jj_3R_17()) - return true; - return false; - } - private boolean jj_3R_150(){ if (jj_3R_41()) return true; @@ -5711,6 +5698,34 @@ public class ADQLParser implements ADQLParserConstants { return false; } + private boolean jj_3R_165(){ + if (jj_scan_token(NOT)) + return true; + return false; + } + + private boolean jj_3R_44(){ + if (jj_scan_token(SELECT)) + return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_136()) + jj_scanpos = xsp; + xsp = jj_scanpos; + if (jj_3R_137()) + jj_scanpos = xsp; + if (jj_3R_138()) + return true; + while(true){ + xsp = jj_scanpos; + if (jj_3R_139()){ + jj_scanpos = xsp; + break; + } + } + return false; + } + /** Generated Token Manager. */ public ADQLParserTokenManager token_source; SimpleCharStream jj_input_stream; diff --git a/src/adql/parser/ADQLParserConstants.java b/src/adql/parser/ADQLParserConstants.java index ee79a43..cf75565 100644 --- a/src/adql/parser/ADQLParserConstants.java +++ b/src/adql/parser/ADQLParserConstants.java @@ -5,7 +5,6 @@ package adql.parser; * Token literal values and constants. * Generated by org.javacc.parser.OtherFilesGen#start() */ -@SuppressWarnings("all") public interface ADQLParserConstants { /** End of File. */ diff --git a/src/adql/parser/ADQLParserTokenManager.java b/src/adql/parser/ADQLParserTokenManager.java index b6efe77..4e4d9cc 100644 --- a/src/adql/parser/ADQLParserTokenManager.java +++ b/src/adql/parser/ADQLParserTokenManager.java @@ -21,7 +21,6 @@ import adql.translator.PostgreSQLTranslator; import adql.translator.TranslationException; /** Token Manager. */ -@SuppressWarnings("all") public class ADQLParserTokenManager implements ADQLParserConstants { /** Debug output. */ diff --git a/src/adql/parser/QueryChecker.java b/src/adql/parser/QueryChecker.java index de5e496..0c15a16 100644 --- a/src/adql/parser/QueryChecker.java +++ b/src/adql/parser/QueryChecker.java @@ -16,7 +16,8 @@ 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS) + * Copyright 2012-2013 - UDS/Centre de Données astronomiques de Strasbourg (CDS), + * Astronomisches Rechen Institute (ARI) */ import adql.db.DBChecker; @@ -27,13 +28,19 @@ import adql.query.ADQLQuery; * * <p>Usually, it consists to check the existence of referenced columns and tables. In this case, one default implementation of this interface can be used: {@link DBChecker}</p> * - * @author Grégory Mantelet (CDS) - * @version 08/2011 + * @author Grégory Mantelet (CDS;ARI) + * @version 1.2 (12/2013) */ public interface QueryChecker { /** - * <p>Checks (non-recursively in sub-queries) the given {@link ADQLQuery}.</p> + * <p>Checks the given {@link ADQLQuery}.</p> + * + * <p><b> + * <u>Important note:</u> + * All subqueries must also be checked when calling this function! + * </b></p> + * * <p>If the query is correct, nothing happens. However at the first detected error, a {@link ParseException} is thrown.</p> * * @param query The query to check. diff --git a/src/adql/parser/adqlGrammar.jj b/src/adql/parser/adqlGrammar.jj index 1f93d62..256eb74 100644 --- a/src/adql/parser/adqlGrammar.jj +++ b/src/adql/parser/adqlGrammar.jj @@ -14,7 +14,8 @@ * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS) + * Copyright 2012-2013 - UDS/Centre de Données astronomiques de Strasbourg (CDS), + * Astronomisches Rechen Institute (ARI) */ /* @@ -24,8 +25,8 @@ * The generated parser checks the syntax of the given ADQL query and generates an object representation but no coherence with any database is done. * If the syntax is not conform to the ADQL definition an error message is printed else it will be the message "Correct syntax". * -* Author: Gregory Mantelet (CDS) - gregory.mantelet@astro.unistra.fr -* Version: January 2012 +* Author: Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de +* Version: 1.2 (12/2013) */ /* ########### */ @@ -87,8 +88,8 @@ import adql.translator.TranslationException; * @see QueryChecker * @see ADQLQueryFactory * -* @author Grégory Mantelet (CDS) - gregory.mantelet@astro.unistra.fr -* @version January 2012 +* @author Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de +* @version 1.2 (12/2013) */ public class ADQLParser { @@ -711,7 +712,13 @@ TOKEN : { */ ADQLQuery Query(): {ADQLQuery q = null;}{ q=QueryExpression() (<EOF> | <EOQ>) - { return q; } + { + // check the query: + if (queryChecker != null) + queryChecker.check(q); + + return q; + } } ADQLQuery QueryExpression(): {} { @@ -731,10 +738,6 @@ ADQLQuery QueryExpression(): {} { [Having()] [OrderBy()] { - // check the query: - if (queryChecker != null) - queryChecker.check(query); - // get the previous query (!= null if the current query is a sub-query): ADQLQuery previousQuery = stackQuery.pop(); if (stackQuery.isEmpty()) diff --git a/src/adql/query/ClauseADQL.java b/src/adql/query/ClauseADQL.java index 1c3c90a..7116b41 100644 --- a/src/adql/query/ClauseADQL.java +++ b/src/adql/query/ClauseADQL.java @@ -16,14 +16,15 @@ package adql.query; * 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 - UDS/Centre de Données astronomiques de Strasbourg (CDS) + * Copyright 2012-2013 - UDS/Centre de Données astronomiques de Strasbourg (CDS), + * Astronomisches Rechen Institute (ARI) */ /** * Represents an ADQL clause (i.e. SELECT, FROM, WHERE, ...). * - * @author Grégory Mantelet (CDS) - * @version 11/2010 + * @author Grégory Mantelet (CDS;ARI) + * @version 1.2 (12/2013) */ public class ClauseADQL< T extends ADQLObject > extends ADQLList<T> { @@ -53,10 +54,9 @@ public class ClauseADQL< T extends ADQLObject > extends ADQLList<T> { super(toCopy); } - @SuppressWarnings("unchecked") @Override public ADQLObject getCopy() throws Exception{ - return new ClauseADQL(this); + return new ClauseADQL<T>(this); } /** -- GitLab