/* * This file is part of ADQLLibrary. * * ADQLLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ADQLLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ADQLLibrary. If not, see . * * Copyright 2012-2014 - UDS/Centre de DonnM-CM-)es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institute (ARI) */ /* * This JavaCC file implements the BNF definition of ADQL v2.0 (IVOA Recommendation 30 Oct 2008 - http://www.ivoa.net/Documents/cover/ADQL-20081030.html). * To generate the parser with this file use JavaCC. This .jj file has been successfully tested with JavaCC 5.0. * * 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: Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de * Version: 1.2 (03/2014) */ /* ########### */ /* # OPTIONS # */ /* ########### */ options { STATIC = false; IGNORE_CASE = true; DEBUG_PARSER = true; } /* ########## */ /* # PARSER # */ /* ########## */ PARSER_BEGIN(ADQLParser) 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 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.query.operand.function.geometry.GeometryFunction.GeometryValue; import adql.translator.PostgreSQLTranslator; import adql.translator.TranslationException; /** *

Parses an ADQL query thanks to the {@link ADQLParser#Query()} function.

* *

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.

* *

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(...)).

* *

WARNING: To modify this class it's strongly encouraged to modify the .jj file in the section between PARSER_BEGIN and PARSER_END and to re-compile it with JavaCC.

* * @see QueryChecker * @see ADQLQueryFactory * * @author Grégory Mantelet (CDS;ARI) - gmantele@ari.uni-heidelberg.de * @version 1.2 (03/2014) */ public class ADQLParser { /** Tools to build the object representation of the ADQL query. */ private ADQLQueryFactory queryFactory = new ADQLQueryFactory(); /** The stack of queries (because there may be some sub-queries). */ private Stack stackQuery = new Stack(); /** The object representation of the ADQL query to parse. (ONLY USED DURING THE PARSING, else it is always null). */ private ADQLQuery query = null; /** Checks each ADQLQuery (sub-query or not) just after their generation. */ private QueryChecker queryChecker = null; /** The first token of a table/column name. This token is extracted by {@link #Identifier()}. */ private Token currentIdentifierToken = null; /** List of all allowed coordinate systems. */ private ArrayList allowedCoordSys = new ArrayList(); /** * 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. */ public ADQLParser(QueryChecker checker, ADQLQueryFactory factory) { this(); queryChecker = checker; if (factory != null) queryFactory = factory; } /** * 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. */ 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. */ public ADQLParser(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory) { this(stream); queryChecker = checker; if (factory != null) queryFactory = 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. */ 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. */ 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. */ public ADQLParser(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory) { this(stream, encoding); queryChecker = checker; if (factory != null) queryFactory = 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. */ 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. */ 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. */ public ADQLParser(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory) { this(reader); queryChecker = checker; if (factory != null) queryFactory = 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. */ 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. */ 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. */ public ADQLParser(ADQLParserTokenManager tm, QueryChecker checker, ADQLQueryFactory factory) { this(tm); queryChecker = checker; if (factory != null) queryFactory = 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. */ 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. */ public ADQLParser(ADQLParserTokenManager tm, ADQLQueryFactory factory) { this(tm, null, factory); } /** * Parses the query given at the creation of this parser or in the ReInit 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; return 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; ReInit(new java.io.ByteArrayInputStream(q.getBytes())); return 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; ReInit(stream); return Query(); } public final void addCoordinateSystem(final String coordSys){ allowedCoordSys.add(coordSys); } public final void setCoordinateSystems(final Collection coordSys){ allowedCoordSys.clear(); if (coordSys != null) allowedCoordSys.addAll(coordSys); } public final boolean isAllowedCoordSys(final String coordSys) { for(String cs : allowedCoordSys){ if (cs.equalsIgnoreCase(coordSys)) return true; } return false; } public final void setDebug(boolean debug){ if (debug) enable_tracing(); else disable_tracing(); } public final QueryChecker getQueryChecker(){ return queryChecker; } public final void setQueryChecker(QueryChecker checker){ queryChecker = checker; } public final ADQLQueryFactory getQueryFactory(){ return queryFactory; } public final void setQueryFactory(ADQLQueryFactory factory){ queryFactory = (factory!=null)?factory:(new ADQLQueryFactory()); } private final ParseException generateParseException(Exception ex){ if (!(ex instanceof ParseException)){ ParseException pex = new ParseException("["+ex.getClass().getName()+"] "+ex.getMessage()); pex.setStackTrace(ex.getStackTrace()); return pex; }else return (ParseException)ex; } /** *

Gets the specified ADQL query and parses the given ADQL query. The SQL translation is then printed if the syntax is correct.

*

ONLY the syntax is checked: the query is NOT EXECUTED !

*

Supplied parameters are:

  • [-debug] -url http://...
  • [-debug] -file ...
  • [-debug] -query SELECT...

* * @param args * @throws Exception */ public static final void main(String[] args) throws Exception { final String USAGE = "Usage:\n\tadqlParser.jar [-d] [-v] [-e] [-a|-s] [|]\n\nNOTE: If no file or URL is given, the ADQL query is expected in the standard input. This query must end with a ';' !\n\nParameters:\n\t-v or --verbose : Print the main steps of the parsing\n\t-d or --debug : Print stack traces when a grave error occurs\n\t-e or --explain : Explain the ADQL parsing (or Expand the parsing tree)\n\t-a or --adql : Display the understood ADQL query\n\t-s or --sql : Ask the SQL translation of the given ADQL query (SQL compatible with PostgreSQL)\n\nReturn:\n\tBy default: nothing if the query is correct. Otherwise a message explaining why the query is not correct is displayed.\n\tWith the -s option, the SQL translation of the given ADQL query will be returned.\n\tWith the -a option, the ADQL query is returned as it has been understood.\n\nExit status:\n\t0\tOK !\n\t1\tParameter error (missing or incorrect parameter)\n\t2\tFile error (incorrect file/url, reading error, ...)\n\t3\tParsing error (syntactic or semantic error)\n\t4\tTranslation error (a problem has occurred during the translation of the given ADQL query in SQL)."; ADQLParser parser; final String urlRegex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; String file = null, metaFile = null; short mode = -1; boolean verbose=false, debug=false, explain=false; // Parameters reading: for(int i=0; i } /* *********** */ /* Punctuation */ /* *********** */ TOKEN : { < LEFT_PAR: "(" > | < RIGHT_PAR: ")" > | < DOT: "." > | < COMMA: "," > | < EOQ: ";"> | < CONCAT: "||" > } /* ******************** */ /* Arithmetic operators */ /* ******************** */ TOKEN : { < PLUS: "+" > | < MINUS: "-" > | < ASTERISK: "*" > | < DIVIDE: "/" > } /* ******************** */ /* Comparison operators */ /* ******************** */ TOKEN : { < EQUAL: "=" > | < NOT_EQUAL: "<>" | "!=" > | < LESS_THAN: "<" > | < LESS_EQUAL_THAN: "<=" > | < GREATER_THAN: ">" > | < GREATER_EQUAL_THAN: ">=" > } /* *************** */ /* SELECT's tokens */ /* *************** */ TOKEN : { < SELECT: "SELECT" > | < QUANTIFIER: "DISTINCT" | "ALL" > | < TOP: "TOP" > } /* ************* */ /* FROM's tokens */ /* ************* */ TOKEN : { < FROM: "FROM" > | < AS: "AS" > | < NATURAL: "NATURAL" > | < INNER: "INNER" > | < OUTER: "OUTER" > | < RIGHT: "RIGHT" > | < LEFT: "LEFT" > | < FULL: "FULL" > | < JOIN: "JOIN" > | < ON: "ON" > | < USING: "USING" > } /* ************** */ /* WHERE's tokens */ /* ************** */ TOKEN : { < WHERE: "WHERE" > | < AND: "AND" > | < OR: "OR" > | < NOT: "NOT" > | < IS: "IS" > | < NULL: "NULL" > | < BETWEEN: "BETWEEN" > | < LIKE: "LIKE" > | < IN: "IN" > | < EXISTS: "EXISTS" > } /* ********************* */ /* Other clauses' tokens */ /* ********************* */ TOKEN : { < GROUP_BY: "GROUP BY" > | < HAVING: "HAVING" > | < ORDER_BY: "ORDER BY" > | < ASC: "ASC" > | < DESC: "DESC" > } /* ************* */ /* SQL functions */ /* ************* */ TOKEN : { < AVG: "AVG" > | < MAX: "MAX" > | < MIN: "MIN" > | < SUM: "SUM" > | < COUNT: "COUNT" > } /* ************** */ /* ADQL functions */ /* ************** */ TOKEN : { < BOX: "BOX" > | < CENTROID: "CENTROID" > | < CIRCLE: "CIRCLE" > | < POINT: "POINT" > | < POLYGON: "POLYGON" > | < REGION: "REGION" > | < CONTAINS: "CONTAINS" > | < INTERSECTS: "INTERSECTS" > | < AREA: "AREA" > | < COORD1: "COORD1" > | < COORD2: "COORD2" > | < COORDSYS: "COORDSYS" > | < DISTANCE: "DISTANCE" > } /* ********************** */ /* Mathematical functions */ /* ********************** */ TOKEN : { < ABS: "ABS" > | < CEILING: "CEILING" > | < DEGREES: "DEGREES" > | < EXP: "EXP" > | < FLOOR: "FLOOR" > | < LOG: "LOG" > | < LOG10: "LOG10" > | < MOD: "MOD" > | < PI: "PI" > | < POWER: "POWER" > | < RADIANS: "RADIANS" > | < RAND: "RAND" > | < ROUND: "ROUND" > | < SQRT: "SQRT" > | < TRUNCATE: "TRUNCATE" > } /* ************************* */ /* Trigonometrical functions */ /* ************************* */ TOKEN : { < ACOS: "ACOS" > | < ASIN: "ASIN" > | < ATAN: "ATAN" > | < ATAN2: "ATAN2" > | < COS: "COS" > | < COT: "COT" > | < SIN: "SIN" > | < TAN: "TAN" > } /* ******* */ /* Comment */ /* ******* */ MORE : { < ()+ >: WithinComment } SKIP : { < "\n" | "\r" | "\r\n" >: DEFAULT } MORE : { < ~[] > } /* ****** */ /* String */ /* ****** */ MORE : { "'" : WithinString } MORE : { < ~["'"] | ("''") > } TOKEN : { < STRING_LITERAL: "'" >: DEFAULT } /* ************************************************* */ /* Identifier (column, tables, ...) */ /* ************************************************* */ MORE : { "\"" : WithinDelimitedId } MORE : { < ~["\""] | ("\"\"") > } TOKEN : { < DELIMITED_IDENTIFIER: "\"" >: DEFAULT } TOKEN : { < REGULAR_IDENTIFIER: ()+ ( | | "_")* > | < #Letter: ["a"-"z","A"-"Z"] > } /* *************** */ /* Primary numbers */ /* *************** */ TOKEN : { < SCIENTIFIC_NUMBER: (|) "E" (|)? > | < UNSIGNED_FLOAT: ( ()?) | ( ) > | < UNSIGNED_INTEGER: ()+ > | < #DIGIT: ["0"-"9"] > } /* ########## */ /* # SYNTAX # */ /* ########## */ /* ******************* */ /* GENERAL ADQL SYNTAX */ /* ******************* */ /** * Parses the ADQL query given at the parser creation or in the {@link ADQLParser#ReInit(java.io.InputStream)} * or in the parseQuery functions. * * @return The object representation of the query. * @throws ParseException If the query syntax is incorrect. */ ADQLQuery Query(): {ADQLQuery q = null;}{ q=QueryExpression() ( | ) { // check the query: if (queryChecker != null) queryChecker.check(q); return q; } } ADQLQuery QueryExpression(): {TextPosition endPos = null;} { { try{ // create the query: query = queryFactory.createQuery(); stackQuery.push(query); }catch(Exception ex){ throw generateParseException(ex); } } Select() From() {endPos = query.getFrom().getPosition();} [Where() {endPos = query.getWhere().getPosition();}] [GroupBy() {endPos = query.getGroupBy().getPosition();}] [Having() {endPos = query.getHaving().getPosition();}] [OrderBy() {endPos = query.getOrderBy().getPosition();}] { // set the position of the query: query.setPosition(new TextPosition(query.getSelect().getPosition(), endPos)); // get the previous query (!= null if the current query is a sub-query): ADQLQuery previousQuery = stackQuery.pop(); if (stackQuery.isEmpty()) query = null; else query = stackQuery.peek(); return previousQuery; } } ADQLQuery SubQueryExpression(): {ADQLQuery q = null; Token start, end;} { start= q=QueryExpression() end= { q.setPosition(new TextPosition(start, end)); return q; } } void Select(): {ClauseSelect select = query.getSelect(); SelectItem item=null; Token start,t = null;} { start=