diff --git a/src/adql/db/DBChecker.java b/src/adql/db/DBChecker.java index dd8c2b9f40d1de448d6c15ac4a5604a8154fedf6..082e4a34bbc0fc8379208e45131b2ef100900293 100644 --- a/src/adql/db/DBChecker.java +++ b/src/adql/db/DBChecker.java @@ -16,7 +16,7 @@ package adql.db; * 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 2011-2017 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), + * Copyright 2011-2019 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ @@ -70,49 +70,69 @@ import adql.search.SimpleReplaceHandler; import adql.search.SimpleSearchHandler; /** - * This {@link QueryChecker} implementation is able to do the following verifications on an ADQL query: + * This {@link QueryChecker} implementation is able to do the following + * verifications on an ADQL query: * <ol> - * <li>Check the existence of all table and column references found in a query</li> - * <li>Resolve all unknown functions as supported User Defined Functions (UDFs)</li> - * <li>Check whether all used geometrical functions are supported</li> + * <li>Check the existence of all table and column references found in a + * query</li> + * <li>Resolve all unknown functions as supported User Defined Functions + * (UDFs)</li> * <li>Check whether all used coordinate systems are supported</li> * <li>Check that types of columns and UDFs match with their context</li> * </ol> * + * <p><i><b>IMPORTANT note:</b> + * Since v2.0, the check of supported geometrical functions is performed + * directly in ADQLParser through the notion of Optional Features. + * The declaration of supported geometrical functions must now be done + * with {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()} + * (see also {@link adql.parser.feature.FeatureSet FeatureSet}). + * </i></p> + * * <h3>Check tables and columns</h3> * <p> - * In addition to check the existence of tables and columns referenced in the query, - * this checked will also attach database metadata on these references ({@link ADQLTable} - * and {@link ADQLColumn} instances when they are resolved. + * In addition to check the existence of tables and columns referenced in the + * query, this checked will also attach database metadata on these references + * ({@link ADQLTable} and {@link ADQLColumn} instances when they are resolved. * </p> * * <p>These information are:</p> * <ul> - * <li>the corresponding {@link DBTable} or {@link DBColumn} (see getter and setter for DBLink in {@link ADQLTable} and {@link ADQLColumn})</li> + * <li>the corresponding {@link DBTable} or {@link DBColumn} (see getter and + * setter for DBLink in {@link ADQLTable} and {@link ADQLColumn})</li> * <li>the link between an {@link ADQLColumn} and its {@link ADQLTable}</li> * </ul> * * <p><i><u>Note:</u> - * Knowing DB metadata of {@link ADQLTable} and {@link ADQLColumn} is particularly useful for the translation of the ADQL query to SQL, - * because the ADQL name of columns and tables can be replaced in SQL by their DB name, if different. This mapping is done automatically - * by {@link adql.translator.JDBCTranslator}. + * Knowing DB metadata of {@link ADQLTable} and {@link ADQLColumn} is + * particularly useful for the translation of the ADQL query to SQL, because + * the ADQL name of columns and tables can be replaced in SQL by their DB name, + * if different. This mapping is done automatically by + * {@link adql.translator.JDBCTranslator}. * </i></p> * * @author Grégory Mantelet (CDS;ARI) - * @version 1.4 (11/2017) + * @version 2.0 (07/2019) */ public class DBChecker implements QueryChecker { /** List of all available tables ({@link DBTable}). */ protected SearchTableApi lstTables; - /** <p>List of all allowed geometrical functions (i.e. CONTAINS, REGION, POINT, COORD2, ...).</p> + /** List of all allowed geometrical functions (i.e. CONTAINS, REGION, POINT, + * COORD2, ...). + * * <p> * If this list is NULL, all geometrical functions are allowed. - * However, if not, all items of this list must be the only allowed geometrical functions. - * So, if the list is empty, no such function is allowed. + * However, if not, all items of this list must be the only allowed + * geometrical functions. So, if the list is empty, no such function is + * allowed. * </p> - * @since 1.3 */ + * + * @since 1.3 + * @deprecated Since v2.0, supported geometrical functions must be declared + * in ADQLParser. */ + @Deprecated protected String[] allowedGeo = null; /** <p>List of all allowed coordinate systems.</p> @@ -156,11 +176,10 @@ public class DBChecker implements QueryChecker { * <ul> * <li>Existence of tables and columns: <b>NO <i>(even unknown or fake tables and columns are allowed)</i></b></li> * <li>Existence of User Defined Functions (UDFs): <b>NO <i>(any "unknown" function is allowed)</i></b></li> - * <li>Support of geometrical functions: <b>NO <i>(all valid geometrical functions are allowed)</i></b></li> * <li>Support of coordinate systems: <b>NO <i>(all valid coordinate systems are allowed)</i></b></li> * </ul> */ - public DBChecker(){ + public DBChecker() { this(null, null); } @@ -171,13 +190,12 @@ public class DBChecker implements QueryChecker { * <ul> * <li>Existence of tables and columns: <b>OK</b></li> * <li>Existence of User Defined Functions (UDFs): <b>NO <i>(any "unknown" function is allowed)</i></b></li> - * <li>Support of geometrical functions: <b>NO <i>(all valid geometrical functions are allowed)</i></b></li> * <li>Support of coordinate systems: <b>NO <i>(all valid coordinate systems are allowed)</i></b></li> * </ul> * * @param tables List of all available tables. */ - public DBChecker(final Collection<? extends DBTable> tables){ + public DBChecker(final Collection<? extends DBTable> tables) { this(tables, null); } @@ -188,7 +206,6 @@ public class DBChecker implements QueryChecker { * <ul> * <li>Existence of tables and columns: <b>OK</b></li> * <li>Existence of User Defined Functions (UDFs): <b>OK</b></li> - * <li>Support of geometrical functions: <b>NO <i>(all valid geometrical functions are allowed)</i></b></li> * <li>Support of coordinate systems: <b>NO <i>(all valid coordinate systems are allowed)</i></b></li> * </ul> * @@ -200,7 +217,7 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs){ + public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs) { // Sort and store the given tables: setTables(tables); @@ -208,11 +225,11 @@ public class DBChecker implements QueryChecker { int cnt; // Store all allowed UDFs in a sorted array: - if (allowedUdfs != null){ + if (allowedUdfs != null) { // Remove all NULL and empty strings: tmp = new FunctionDef[allowedUdfs.size()]; cnt = 0; - for(FunctionDef udf : allowedUdfs){ + for(FunctionDef udf : allowedUdfs) { if (udf != null && udf.name.trim().length() > 0) tmp[cnt++] = udf; } @@ -251,8 +268,13 @@ public class DBChecker implements QueryChecker { * If it is empty, no coordinate system is allowed (except the default values - generally expressed by an empty string: ''). * * @since 1.3 + * @deprecated Since v2.0, the check of geometrical functions support is + * performed in ADQLParser. It must now be done with + * {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()} + * (see also {@link adql.parser.feature.FeatureSet FeatureSet}). */ - public DBChecker(final Collection<? extends DBTable> tables, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException{ + @Deprecated + public DBChecker(final Collection<? extends DBTable> tables, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException { this(tables, null, allowedGeoFcts, allowedCoordSys); } @@ -263,10 +285,17 @@ public class DBChecker implements QueryChecker { * <ul> * <li>Existence of tables and columns: <b>OK</b></li> * <li>Existence of User Defined Functions (UDFs): <b>OK</b></li> - * <li>Support of geometrical functions: <b>OK</b></li> * <li>Support of coordinate systems: <b>OK</b></li> * </ul> * + * <p><i><b>IMPORTANT note:</b> + * Since v2.0, the check of supported geometrical functions is performed + * directly in ADQLParser through the notion of Optional Features. + * The declaration of supported geometrical functions must now be done + * with {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()} + * (see also {@link adql.parser.feature.FeatureSet FeatureSet}). + * </i></p> + * * @param tables List of all available tables. * @param allowedUdfs List of all allowed user defined functions. * If NULL, no verification will be done (and so, all UDFs are allowed). @@ -284,9 +313,14 @@ public class DBChecker implements QueryChecker { * If the given list is NULL, no verification will be done (and so, all coordinate systems are allowed). * If it is empty, no coordinate system is allowed (except the default values - generally expressed by an empty string: ''). * - * @since 1.3 + * @since 2.0 + * @deprecated Since v2.0, the check of geometrical functions support is + * performed in ADQLParser. It must now be done with + * {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()} + * (see also {@link adql.parser.feature.FeatureSet FeatureSet}). */ - public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException{ + @Deprecated + public DBChecker(final Collection<? extends DBTable> tables, final Collection<? extends FunctionDef> allowedUdfs, final Collection<String> allowedGeoFcts, final Collection<String> allowedCoordSys) throws ParseException { // Set the list of available tables + Set the list of all known UDFs: this(tables, allowedUdfs); @@ -308,7 +342,7 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected final static String[] specialSort(final Collection<String> items){ + protected final static String[] specialSort(final Collection<String> items) { // Nothing to do if the array is NULL: if (items == null) return null; @@ -316,7 +350,7 @@ public class DBChecker implements QueryChecker { // Keep only valid items (not NULL and not empty string): String[] tmp = new String[items.size()]; int cnt = 0; - for(String item : items){ + for(String item : items) { if (item != null && item.trim().length() > 0) tmp[cnt++] = item; } @@ -345,7 +379,7 @@ public class DBChecker implements QueryChecker { * * @param tables List of {@link DBTable}s. */ - public final void setTables(final Collection<? extends DBTable> tables){ + public final void setTables(final Collection<? extends DBTable> tables) { if (tables == null) lstTables = new SearchTableList(); else if (tables instanceof SearchTableApi) @@ -372,7 +406,7 @@ public class DBChecker implements QueryChecker { * @see #check(ADQLQuery, Stack) */ @Override - public final void check(final ADQLQuery query) throws ParseException{ + public final void check(final ADQLQuery query) throws ParseException { check(query, null); } @@ -404,7 +438,7 @@ public class DBChecker implements QueryChecker { * @see #checkGeometries(ADQLQuery, UnresolvedIdentifiersException) * @see #checkTypes(ADQLQuery, UnresolvedIdentifiersException) */ - protected void check(final ADQLQuery query, final Stack<SearchColumnList> fathersList) throws UnresolvedIdentifiersException{ + protected void check(final ADQLQuery query, final Stack<SearchColumnList> fathersList) throws UnresolvedIdentifiersException { UnresolvedIdentifiersException errors = new UnresolvedIdentifiersException(); // A. Check DB items (tables and columns): @@ -456,15 +490,15 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected SearchColumnList checkDBItems(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final UnresolvedIdentifiersException errors){ + protected SearchColumnList checkDBItems(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final UnresolvedIdentifiersException errors) { // a. Resolve all tables: - Map<DBTable,ADQLTable> mapTables = resolveTables(query, fathersList, errors); + Map<DBTable, ADQLTable> mapTables = resolveTables(query, fathersList, errors); // b. Get the list of all columns made available in the clause FROM: SearchColumnList availableColumns; - try{ + try { availableColumns = query.getFrom().getDBColumns(); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(pe); availableColumns = new SearchColumnList(); } @@ -526,25 +560,25 @@ public class DBChecker implements QueryChecker { * * @return An associative map of all the resolved tables. */ - protected Map<DBTable,ADQLTable> resolveTables(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final UnresolvedIdentifiersException errors){ - HashMap<DBTable,ADQLTable> mapTables = new HashMap<DBTable,ADQLTable>(); + protected Map<DBTable, ADQLTable> resolveTables(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final UnresolvedIdentifiersException errors) { + HashMap<DBTable, ADQLTable> mapTables = new HashMap<DBTable, ADQLTable>(); ISearchHandler sHandler; // Check the existence of all tables: sHandler = new SearchTableHandler(); sHandler.search(query.getFrom()); - for(ADQLObject result : sHandler){ - try{ + for(ADQLObject result : sHandler) { + try { ADQLTable table = (ADQLTable)result; // resolve the table: DBTable dbTable = null; - if (table.isSubQuery()){ + if (table.isSubQuery()) { // check the sub-query tables: check(table.getSubQuery(), fathersList); // generate its DBTable: dbTable = generateDBTable(table.getSubQuery(), table.getAlias()); - }else{ + } else { dbTable = resolveTable(table); // wrap this table metadata if an alias should be used: if (dbTable != null && table.hasAlias()) @@ -554,7 +588,7 @@ public class DBChecker implements QueryChecker { // link with the matched DBTable: table.setDBLink(dbTable); mapTables.put(dbTable, table); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(pe); } } @@ -566,14 +600,14 @@ public class DBChecker implements QueryChecker { * in sub-queries).*/ sHandler = new SearchWildCardHandler(); sHandler.search(query.getSelect()); - for(ADQLObject result : sHandler){ - try{ + for(ADQLObject result : sHandler) { + try { SelectAllColumns wildcard = (SelectAllColumns)result; ADQLTable table = wildcard.getAdqlTable(); DBTable dbTable = null; // first, try to resolve the table by table alias: - if (table.getTableName() != null && table.getSchemaName() == null){ + if (table.getTableName() != null && table.getSchemaName() == null) { List<ADQLTable> tables = query.getFrom().getTablesByAlias(table.getTableName(), table.isCaseSensitive(IdentifierField.TABLE)); if (tables.size() == 1) dbTable = tables.get(0).getDBLink(); @@ -585,7 +619,7 @@ public class DBChecker implements QueryChecker { // set the corresponding tables among the list of resolved tables: wildcard.setAdqlTable(mapTables.get(dbTable)); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(pe); } } @@ -602,7 +636,7 @@ public class DBChecker implements QueryChecker { * * @throws ParseException An {@link UnresolvedTableException} if the given table can't be resolved. */ - protected DBTable resolveTable(final ADQLTable table) throws ParseException{ + protected DBTable resolveTable(final ADQLTable table) throws ParseException { List<DBTable> tables = lstTables.search(table); // good if only one table has been found: @@ -640,21 +674,21 @@ public class DBChecker implements QueryChecker { * @param list List of column metadata to complete in this function each time a column reference is resolved. * @param errors List of errors to complete in this function each time an unknown table or column is encountered. */ - protected void resolveColumns(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final Map<DBTable,ADQLTable> mapTables, final SearchColumnList list, final UnresolvedIdentifiersException errors){ + protected void resolveColumns(final ADQLQuery query, final Stack<SearchColumnList> fathersList, final Map<DBTable, ADQLTable> mapTables, final SearchColumnList list, final UnresolvedIdentifiersException errors) { ISearchHandler sHandler; // Check the existence of all columns: sHandler = new SearchColumnOutsideGroupByHandler(); sHandler.search(query); - for(ADQLObject result : sHandler){ - try{ + for(ADQLObject result : sHandler) { + try { ADQLColumn adqlColumn = (ADQLColumn)result; // resolve the column: DBColumn dbColumn = resolveColumn(adqlColumn, list, fathersList); // link with the matched DBColumn: adqlColumn.setDBLink(dbColumn); adqlColumn.setAdqlTable(mapTables.get(dbColumn.getTable())); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(pe); } } @@ -663,17 +697,17 @@ public class DBChecker implements QueryChecker { ClauseSelect select = query.getSelect(); sHandler = new SearchColumnHandler(); sHandler.search(query.getGroupBy()); - for(ADQLObject result : sHandler){ - try{ + 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){ + if (dbColumn != null) { adqlColumn.setDBLink(dbColumn); adqlColumn.setAdqlTable(mapTables.get(dbColumn.getTable())); } - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(pe); } } @@ -683,8 +717,8 @@ public class DBChecker implements QueryChecker { * because no father column can be used in ORDER BY. */ sHandler = new SearchColReferenceHandler(); sHandler.search(query); - for(ADQLObject result : sHandler){ - try{ + for(ADQLObject result : sHandler) { + try { ColumnReference colRef = (ColumnReference)result; // resolve the column reference: DBColumn dbColumn = checkColumnReference(colRef, select, list); @@ -692,7 +726,7 @@ public class DBChecker implements QueryChecker { colRef.setDBLink(dbColumn); if (dbColumn != null) colRef.setAdqlTable(mapTables.get(dbColumn.getTable())); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(pe); } } @@ -718,23 +752,23 @@ public class DBChecker implements QueryChecker { * @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, Stack<SearchColumnList> fathersList) throws ParseException{ + protected DBColumn resolveColumn(final ADQLColumn column, final SearchColumnList dbColumns, Stack<SearchColumnList> fathersList) throws ParseException { List<DBColumn> foundColumns = dbColumns.search(column); // good if only one column has been found: if (foundColumns.size() == 1) return foundColumns.get(0); // but if more than one: ambiguous table reference ! - else if (foundColumns.size() > 1){ + else if (foundColumns.size() > 1) { if (column.getTableName() == null) throw new UnresolvedColumnException(column, (foundColumns.get(0).getTable() == null) ? "<NULL>" : (foundColumns.get(0).getTable().getADQLName() + "." + foundColumns.get(0).getADQLName()), (foundColumns.get(1).getTable() == null) ? "<NULL>" : (foundColumns.get(1).getTable().getADQLName() + "." + foundColumns.get(1).getADQLName())); 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{ + else { if (fathersList == null || fathersList.isEmpty()) throw new UnresolvedColumnException(column); - else{ + else { Stack<SearchColumnList> subStack = new Stack<SearchColumnList>(); subStack.addAll(fathersList.subList(0, fathersList.size() - 1)); return resolveColumn(column, fathersList.peek(), subStack); @@ -760,11 +794,11 @@ public class DBChecker implements QueryChecker { * * @since 1.4 */ - protected DBColumn checkGroupByItem(final ADQLColumn col, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException{ + 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){ + if (col.getTableName() == null) { List<SelectItem> founds = select.searchByAlias(col.getColumnName(), col.isCaseSensitive(IdentifierField.COLUMN)); if (founds.size() == 1) return null; @@ -790,18 +824,18 @@ public class DBChecker implements QueryChecker { * @see ClauseSelect#searchByAlias(String) * @see #resolveColumn(ADQLColumn, SearchColumnList, Stack) */ - protected DBColumn checkColumnReference(final ColumnReference colRef, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException{ - if (colRef.isIndex()){ + protected DBColumn checkColumnReference(final ColumnReference colRef, final ClauseSelect select, final SearchColumnList dbColumns) throws ParseException { + if (colRef.isIndex()) { int index = colRef.getColumnIndex(); - if (index > 0 && index <= select.size()){ + if (index > 0 && index <= select.size()) { SelectItem item = select.get(index - 1); if (item.getOperand() instanceof ADQLColumn) return ((ADQLColumn)item.getOperand()).getDBLink(); else return null; - }else + } else throw new ParseException("Column index out of bounds: " + index + " (must be between 1 and " + select.size() + ") !", colRef.getPosition()); - }else{ + } else { ADQLColumn col = new ADQLColumn(null, colRef.getColumnName()); col.setCaseSensitive(colRef.isCaseSensitive()); col.setPosition(colRef.getPosition()); @@ -829,7 +863,7 @@ public class DBChecker implements QueryChecker { * * @throws ParseException Can be used to explain why the table has not been found. <i>Note: not used by default.</i> */ - public static DBTable generateDBTable(final ADQLQuery subQuery, final String tableName) throws ParseException{ + public static DBTable generateDBTable(final ADQLQuery subQuery, final String tableName) throws ParseException { DefaultDBTable dbTable = new DefaultDBTable(tableName); DBColumn[] columns = subQuery.getResultingColumns(); @@ -862,24 +896,24 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void checkUDFs(final ADQLQuery query, final UnresolvedIdentifiersException errors){ + protected void checkUDFs(final ADQLQuery query, final UnresolvedIdentifiersException errors) { // 1. Search all UDFs: ISearchHandler sHandler = new SearchUDFHandler(); sHandler.search(query); // If no UDF are allowed, throw immediately an error: - if (allowedUdfs.length == 0){ + if (allowedUdfs.length == 0) { for(ADQLObject result : sHandler) errors.addException(new UnresolvedFunctionException((UserDefinedFunction)result)); } // 2. Try to resolve all of them: - else{ + else { ArrayList<UserDefinedFunction> toResolveLater = new ArrayList<UserDefinedFunction>(); UserDefinedFunction udf; int match; - BinarySearch<FunctionDef,UserDefinedFunction> binSearch = new BinarySearch<FunctionDef,UserDefinedFunction>(){ + BinarySearch<FunctionDef, UserDefinedFunction> binSearch = new BinarySearch<FunctionDef, UserDefinedFunction>() { @Override - protected int compare(UserDefinedFunction searchItem, FunctionDef arrayItem){ + protected int compare(UserDefinedFunction searchItem, FunctionDef arrayItem) { return arrayItem.compareTo(searchItem) * -1; } }; @@ -888,13 +922,13 @@ public class DBChecker implements QueryChecker { /* Note: at this stage, it can happen that UDFs can not be yet resolved because the building of * their signature depends of other UDFs. That's why, these special cases should be kept * for a later resolution try. */ - for(ADQLObject result : sHandler){ + for(ADQLObject result : sHandler) { udf = (UserDefinedFunction)result; // if the type of not all parameters are resolved, postpone the resolution: if (!isAllParamTypesResolved(udf)) toResolveLater.add(udf); // otherwise: - else{ + else { // search for a match: match = binSearch.search(udf, allowedUdfs); // if no match... @@ -909,7 +943,7 @@ public class DBChecker implements QueryChecker { // Try to resolve UDFs whose some parameter types are depending of other UDFs: /* Note: we need to iterate from the end in order to resolve first the most wrapped functions * (e.g. fct1(fct2(...)) ; fct2 must be resolved before fct1). */ - for(int i = toResolveLater.size() - 1; i >= 0; i--){ + for(int i = toResolveLater.size() - 1; i >= 0; i--) { udf = toResolveLater.get(i); // search for a match: match = binSearch.search(udf, allowedUdfs); @@ -943,8 +977,8 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected final boolean isAllParamTypesResolved(final ADQLFunction fct){ - for(ADQLOperand op : fct.getParameters()){ + protected final boolean isAllParamTypesResolved(final ADQLFunction fct) { + for(ADQLOperand op : fct.getParameters()) { if (op.isGeometry() == op.isNumeric() && op.isNumeric() == op.isString()) return false; } @@ -960,38 +994,40 @@ public class DBChecker implements QueryChecker { * * <p>Operations done in this function:</p> * <ol> - * <li>Check that all geometrical functions are supported</li> * <li>Check that all explicit (string constant) coordinate system definitions are supported</i></li> * <li>Check all STC-S expressions (only in {@link RegionFunction} for the moment) and * Apply the 2 previous checks on them</li> * </ol> * + * <p><i><b>IMPORTANT note:</b> + * Since v2.0, the check of supported geometrical functions is performed + * directly in ADQLParser through the notion of Optional Features. + * The declaration of supported geometrical functions must now be done + * with {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()} + * (see also {@link adql.parser.feature.FeatureSet FeatureSet}). + * </i></p> + * * @param query Query in which geometries must be checked. * @param errors List of errors to complete in this function each time a geometry item is not supported. * - * @see #resolveGeometryFunctions(ADQLQuery, BinarySearch, UnresolvedIdentifiersException) * @see #resolveCoordinateSystems(ADQLQuery, UnresolvedIdentifiersException) * @see #resolveSTCSExpressions(ADQLQuery, BinarySearch, UnresolvedIdentifiersException) * * @since 1.3 */ - protected void checkGeometries(final ADQLQuery query, final UnresolvedIdentifiersException errors){ - BinarySearch<String,String> binSearch = new BinarySearch<String,String>(){ + protected void checkGeometries(final ADQLQuery query, final UnresolvedIdentifiersException errors) { + BinarySearch<String, String> binSearch = new BinarySearch<String, String>() { @Override - protected int compare(String searchItem, String arrayItem){ + protected int compare(String searchItem, String arrayItem) { return searchItem.compareToIgnoreCase(arrayItem); } }; - // a. Ensure that all used geometry functions are allowed: - if (allowedGeo != null) - resolveGeometryFunctions(query, binSearch, errors); - - // b. Check whether the coordinate systems are allowed: + // a. Check whether the coordinate systems are allowed: if (allowedCoordSys != null) resolveCoordinateSystems(query, errors); - // c. Check all STC-S expressions (in RegionFunctions only) + the used coordinate systems (if StringConstant only): + // b. Check all STC-S expressions (in RegionFunctions only) + the used coordinate systems (if StringConstant only): if (allowedGeo == null || (allowedGeo.length > 0 && binSearch.search("REGION", allowedGeo) >= 0)) resolveSTCSExpressions(query, binSearch, errors); } @@ -1005,13 +1041,19 @@ public class DBChecker implements QueryChecker { * @see #checkGeometryFunction(String, ADQLFunction, BinarySearch, UnresolvedIdentifiersException) * * @since 1.3 + * @deprecated No more used since v2.0. Check of the geometric functions is + * now performed directly in ADQLParser. Geometric functions + * are optional features and should be declared as such in the + * ADQLParser if they are supported (see + * {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()}). */ - protected void resolveGeometryFunctions(final ADQLQuery query, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){ + @Deprecated + protected final void resolveGeometryFunctions(final ADQLQuery query, final BinarySearch<String, String> binSearch, final UnresolvedIdentifiersException errors) { ISearchHandler sHandler = new SearchGeometryHandler(); sHandler.search(query); String fctName; - for(ADQLObject result : sHandler){ + for(ADQLObject result : sHandler) { fctName = result.getName(); checkGeometryFunction(fctName, (ADQLFunction)result, binSearch, errors); } @@ -1034,8 +1076,14 @@ public class DBChecker implements QueryChecker { * @param errors List of errors to complete in this function each time a geometrical function is not supported. * * @since 1.3 + * @deprecated No more used since v2.0. Check of the geometric functions is + * now performed directly in ADQLParser. Geometric functions + * are optional features and should be declared as such in the + * ADQLParser if they are supported (see + * {@link adql.parser.ADQLParser#getSupportedFeatures() ADQLParser.getSupportedFeatures()}). */ - protected void checkGeometryFunction(final String fctName, final ADQLFunction fct, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){ + @Deprecated + protected final void checkGeometryFunction(final String fctName, final ADQLFunction fct, final BinarySearch<String, String> binSearch, final UnresolvedIdentifiersException errors) { int match = -1; if (allowedGeo.length != 0) match = binSearch.search(fctName, allowedGeo); @@ -1059,7 +1107,7 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void resolveCoordinateSystems(final ADQLQuery query, final UnresolvedIdentifiersException errors){ + protected void resolveCoordinateSystems(final ADQLQuery query, final UnresolvedIdentifiersException errors) { ISearchHandler sHandler = new SearchCoordSysHandler(); sHandler.search(query); for(ADQLObject result : sHandler) @@ -1077,11 +1125,11 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void checkCoordinateSystem(final StringConstant adqlCoordSys, final UnresolvedIdentifiersException errors){ + protected void checkCoordinateSystem(final StringConstant adqlCoordSys, final UnresolvedIdentifiersException errors) { String coordSysStr = adqlCoordSys.getValue(); - try{ + try { checkCoordinateSystem(STCS.parseCoordSys(coordSysStr), adqlCoordSys, errors); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(new ParseException(pe.getMessage(), adqlCoordSys.getPosition())); } } @@ -1095,11 +1143,11 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void checkCoordinateSystem(final CoordSys coordSys, final ADQLOperand operand, final UnresolvedIdentifiersException errors){ - if (coordSysRegExp != null && coordSys != null && !coordSys.toFullSTCS().matches(coordSysRegExp)){ + protected void checkCoordinateSystem(final CoordSys coordSys, final ADQLOperand operand, final UnresolvedIdentifiersException errors) { + if (coordSysRegExp != null && coordSys != null && !coordSys.toFullSTCS().matches(coordSysRegExp)) { StringBuffer buf = new StringBuffer(); - if (allowedCoordSys != null){ - for(String cs : allowedCoordSys){ + if (allowedCoordSys != null) { + for(String cs : allowedCoordSys) { if (buf.length() > 0) buf.append(", "); buf.append(cs); @@ -1133,7 +1181,7 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void resolveSTCSExpressions(final ADQLQuery query, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){ + protected void resolveSTCSExpressions(final ADQLQuery query, final BinarySearch<String, String> binSearch, final UnresolvedIdentifiersException errors) { // Search REGION functions: ISearchHandler sHandler = new SearchRegionHandler(); sHandler.search(query); @@ -1141,8 +1189,8 @@ public class DBChecker implements QueryChecker { // Parse and check their STC-S expression: String stcs; Region region; - for(ADQLObject result : sHandler){ - try{ + for(ADQLObject result : sHandler) { + try { // get the STC-S expression: stcs = ((StringConstant)((RegionFunction)result).getParameter(0)).getValue(); @@ -1151,7 +1199,7 @@ public class DBChecker implements QueryChecker { // check whether the regions (this one + the possible inner ones) and the coordinate systems are allowed: checkRegion(region, (RegionFunction)result, binSearch, errors); - }catch(ParseException pe){ + } catch(ParseException pe) { errors.addException(new ParseException(pe.getMessage(), result.getPosition())); } } @@ -1177,7 +1225,7 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void checkRegion(final Region r, final RegionFunction fct, final BinarySearch<String,String> binSearch, final UnresolvedIdentifiersException errors){ + protected void checkRegion(final Region r, final RegionFunction fct, final BinarySearch<String, String> binSearch, final UnresolvedIdentifiersException errors) { if (r == null) return; @@ -1186,7 +1234,7 @@ public class DBChecker implements QueryChecker { checkCoordinateSystem(r.coordSys, fct, errors); // Check that the region type is allowed: - if (allowedGeo != null){ + if (allowedGeo != null) { if (allowedGeo.length == 0) errors.addException(new UnresolvedFunctionException("The region type \"" + r.type + "\" is not available in this implementation!", fct)); else @@ -1194,7 +1242,7 @@ public class DBChecker implements QueryChecker { } // Check all the inner regions: - if (r.regions != null){ + if (r.regions != null) { for(Region innerR : r.regions) checkRegion(innerR, fct, binSearch, errors); } @@ -1234,16 +1282,16 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void checkTypes(final ADQLQuery query, final UnresolvedIdentifiersException errors){ + protected void checkTypes(final ADQLQuery query, final UnresolvedIdentifiersException errors) { // Search all unknown types: ISearchHandler sHandler = new SearchUnknownTypeHandler(); sHandler.search(query); // Check whether their type matches the expected one: UnknownType unknown; - for(ADQLObject result : sHandler){ + for(ADQLObject result : sHandler) { unknown = (UnknownType)result; - switch(unknown.getExpectedType()){ + switch(unknown.getExpectedType()) { case 'G': case 'g': if (!unknown.isGeometry()) @@ -1294,11 +1342,11 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected void checkSubQueries(final ADQLQuery query, Stack<SearchColumnList> fathersList, final SearchColumnList availableColumns, final UnresolvedIdentifiersException errors){ + protected void checkSubQueries(final ADQLQuery query, Stack<SearchColumnList> fathersList, final SearchColumnList availableColumns, final UnresolvedIdentifiersException errors) { // Check sub-queries outside the clause FROM: ISearchHandler sHandler = new SearchSubQueryHandler(); sHandler.search(query); - if (sHandler.getNbMatch() > 0){ + if (sHandler.getNbMatch() > 0) { // Push the list of columns into the father columns stack: if (fathersList == null) @@ -1306,10 +1354,10 @@ public class DBChecker implements QueryChecker { fathersList.push(availableColumns); // Check each found sub-query: - for(ADQLObject result : sHandler){ - try{ + for(ADQLObject result : sHandler) { + try { check((ADQLQuery)result, fathersList); - }catch(UnresolvedIdentifiersException uie){ + } catch(UnresolvedIdentifiersException uie) { Iterator<ParseException> itPe = uie.getErrors(); while(itPe.hasNext()) errors.addException(itPe.next()); @@ -1339,7 +1387,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchColumnOutsideGroupByHandler extends SearchColumnHandler { @Override - protected boolean goInto(final ADQLObject obj){ + protected boolean goInto(final ADQLObject obj) { return !(obj instanceof ClauseADQL<?> && ((ClauseADQL<?>)obj).getName() != null && ((ClauseADQL<?>)obj).getName().equalsIgnoreCase("GROUP BY")) && super.goInto(obj); } } @@ -1352,7 +1400,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchTableHandler extends SimpleSearchHandler { @Override - public boolean match(final ADQLObject obj){ + public boolean match(final ADQLObject obj) { return obj instanceof ADQLTable; } } @@ -1365,7 +1413,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchWildCardHandler extends SimpleSearchHandler { @Override - public boolean match(final ADQLObject obj){ + public boolean match(final ADQLObject obj) { return (obj instanceof SelectAllColumns) && (((SelectAllColumns)obj).getAdqlTable() != null); } } @@ -1378,7 +1426,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchColReferenceHandler extends SimpleSearchHandler { @Override - public boolean match(final ADQLObject obj){ + public boolean match(final ADQLObject obj) { return (obj instanceof ColumnReference); } } @@ -1397,18 +1445,18 @@ public class DBChecker implements QueryChecker { */ private static class SearchSubQueryHandler extends SimpleSearchHandler { @Override - protected void addMatch(ADQLObject matchObj, ADQLIterator it){ + protected void addMatch(ADQLObject matchObj, ADQLIterator it) { if (it != null) super.addMatch(matchObj, it); } @Override - protected boolean goInto(ADQLObject obj){ + protected boolean goInto(ADQLObject obj) { return super.goInto(obj) && !(obj instanceof FromContent); } @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { return (obj instanceof ADQLQuery); } } @@ -1422,7 +1470,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchUDFHandler extends SimpleSearchHandler { @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { return (obj instanceof UserDefinedFunction); } } @@ -1441,12 +1489,12 @@ public class DBChecker implements QueryChecker { private static class ReplaceDefaultUDFHandler extends SimpleReplaceHandler { private final UnresolvedIdentifiersException errors; - public ReplaceDefaultUDFHandler(final UnresolvedIdentifiersException errorsContainer){ + public ReplaceDefaultUDFHandler(final UnresolvedIdentifiersException errorsContainer) { errors = errorsContainer; } @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { return (obj.getClass().getName().equals(DefaultUDF.class.getName())) && (((DefaultUDF)obj).getDefinition() != null) && (((DefaultUDF)obj).getDefinition().getUDFClass() != null); /* Note: detection of DefaultUDF is done on the exact class name rather than using "instanceof" in order to have only direct instances of DefaultUDF, * and not extensions of it. Indeed, DefaultUDFs are generally created automatically by the ADQLQueryFactory ; so, extensions of it can only be custom @@ -1454,15 +1502,15 @@ public class DBChecker implements QueryChecker { } @Override - protected ADQLObject getReplacer(ADQLObject objToReplace) throws UnsupportedOperationException{ - try{ + protected ADQLObject getReplacer(ADQLObject objToReplace) throws UnsupportedOperationException { + try { // get the associated UDF class: Class<? extends UserDefinedFunction> udfClass = ((DefaultUDF)objToReplace).getDefinition().getUDFClass(); // get the constructor with a single parameter of type ADQLOperand[]: Constructor<? extends UserDefinedFunction> constructor = udfClass.getConstructor(ADQLOperand[].class); // create a new instance of this UDF class with the operands stored in the object to replace: return constructor.newInstance((Object)(((DefaultUDF)objToReplace).getParameters())); /* note: without this class, each item of the given array will be considered as a single parameter. */ - }catch(Exception ex){ + } catch(Exception ex) { // IF NO INSTANCE CAN BE CREATED... // ...keep the error for further report: errors.addException(new UnresolvedFunctionException("Impossible to represent the function \"" + ((DefaultUDF)objToReplace).getName() + "\": the following error occured while creating this representation: \"" + ((ex instanceof InvocationTargetException) ? "[" + ex.getCause().getClass().getSimpleName() + "] " + ex.getCause().getMessage() : ex.getMessage()) + "\"", (DefaultUDF)objToReplace)); @@ -1481,7 +1529,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchGeometryHandler extends SimpleSearchHandler { @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { return (obj instanceof GeometryFunction); } } @@ -1501,11 +1549,11 @@ public class DBChecker implements QueryChecker { */ private static class SearchUnknownTypeHandler extends SimpleSearchHandler { @Override - protected boolean match(ADQLObject obj){ - if (obj instanceof UnknownType){ + protected boolean match(ADQLObject obj) { + if (obj instanceof UnknownType) { char expected = ((UnknownType)obj).getExpectedType(); return (expected == 'G' || expected == 'g' || expected == 'S' || expected == 's' || expected == 'N' || expected == 'n'); - }else + } else return false; } } @@ -1520,7 +1568,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchCoordSysHandler extends SimpleSearchHandler { @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { if (obj instanceof PointFunction || obj instanceof BoxFunction || obj instanceof CircleFunction || obj instanceof PolygonFunction) return (((GeometryFunction)obj).getCoordinateSystem() instanceof StringConstant); else @@ -1528,7 +1576,7 @@ public class DBChecker implements QueryChecker { } @Override - protected void addMatch(ADQLObject matchObj, ADQLIterator it){ + protected void addMatch(ADQLObject matchObj, ADQLIterator it) { results.add(((GeometryFunction)matchObj).getCoordinateSystem()); } @@ -1543,7 +1591,7 @@ public class DBChecker implements QueryChecker { */ private static class SearchRegionHandler extends SimpleSearchHandler { @Override - protected boolean match(ADQLObject obj){ + protected boolean match(ADQLObject obj) { if (obj instanceof RegionFunction) return (((RegionFunction)obj).getParameter(0) instanceof StringConstant); else @@ -1573,7 +1621,7 @@ public class DBChecker implements QueryChecker { * * @since 1.3 */ - protected static abstract class BinarySearch< T, S > { + protected static abstract class BinarySearch<T, S> { private int s, e, m, comp; /** @@ -1590,10 +1638,10 @@ public class DBChecker implements QueryChecker { * * @return The array index of the first item of all matches. */ - public int search(final S searchItem, final T[] array){ + public int search(final S searchItem, final T[] array) { s = 0; e = array.length - 1; - while(s < e){ + while(s < e) { // middle of the sorted array: m = s + ((e - s) / 2); // compare the fct with the middle item of the array: diff --git a/src/adql/parser/ADQLParser200.java b/src/adql/parser/ADQLParser200.java index 9d003955df00c2ab584cac6180043f74d8d52bda..42126da635909d306e010f7bdacbcd74c6a3304d 100644 --- a/src/adql/parser/ADQLParser200.java +++ b/src/adql/parser/ADQLParser200.java @@ -26,10 +26,14 @@ import java.util.ArrayList; import java.util.Stack; import java.util.Vector; +import adql.db.exception.UnresolvedIdentifiersException; +import adql.db.exception.UnsupportedFeatureException; import adql.parser.ADQLParserFactory.ADQLVersion; import adql.parser.ADQLQueryFactory.JoinType; import adql.parser.IdentifierItems.IdentifierItem; import adql.parser.feature.FeatureSet; +import adql.parser.feature.LanguageFeature; +import adql.query.ADQLObject; import adql.query.ADQLOrder; import adql.query.ADQLQuery; import adql.query.ClauseADQL; @@ -67,6 +71,7 @@ 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.search.SearchOptionalFeaturesHandler; /** * Parses an ADQL-2.0 query thanks to the {@link ADQLParser200#Query() Query()} function. @@ -117,6 +122,12 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { /** Tools to build the object representation of the ADQL query. */ private ADQLQueryFactory queryFactory = new ADQLQueryFactory(); + /** Default set of supported language features. + * <p><i><b>Note:</b> + * By default, all optional features are supported. + * </i></p> */ + private FeatureSet supportedFeatures = new FeatureSet(false); + /** The stack of queries (because there may be some sub-queries). */ private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>(); @@ -137,6 +148,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { */ public ADQLParser200() { this(new java.io.ByteArrayInputStream("".getBytes())); + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); } @@ -188,7 +200,8 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { */ public ADQLParser200(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory) { this(stream); - setDebug(false); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); @@ -230,6 +243,9 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { */ public ADQLParser200(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory) { this(stream, encoding); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); + setDebug(false); queryChecker = checker; @@ -271,7 +287,8 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { */ public ADQLParser200(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory) { this(reader); - setDebug(false); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); @@ -312,7 +329,8 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { */ public ADQLParser200(ADQLParser200TokenManager tm, QueryChecker checker, ADQLQueryFactory factory) { this(tm); - setDebug(false); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); @@ -380,15 +398,13 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { @Override public final FeatureSet getSupportedFeatures() { - FeatureSet languageFeatures = new FeatureSet(); - languageFeatures.unsupportAll(); - return languageFeatures; + return supportedFeatures; } @Override - public final void setSupportedFeatures(final FeatureSet languageFeatures) { - /* Nothing to do here for ADQL-2.0 because no optional feature can be - * supported)! */ + public void setSupportedFeatures(final FeatureSet features) { + if (features != null) + supportedFeatures = features; } /* EXCEPTION HELPER FUNCTION */ @@ -533,7 +549,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a SELECT clause: Select(); @@ -555,7 +571,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a FROM clause: From(); @@ -577,7 +593,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a WHERE clause: Where(); @@ -599,7 +615,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a ORDER BY clause: OrderBy(); @@ -621,7 +637,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a GROUP BY clause: GroupBy(); @@ -972,6 +988,21 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { jj_consume_token(-1); throw new ParseException(); } + /* check the optional features before any other check: + * (note: this check is very close to grammar check...hence its higher + * priority) */ + UnresolvedIdentifiersException exUnsupportedFeatures = new UnresolvedIdentifiersException("unsupported expression"); + SearchOptionalFeaturesHandler sFeaturesHandler = new SearchOptionalFeaturesHandler(true, false); + sFeaturesHandler.search(q); + for(ADQLObject obj : sFeaturesHandler) { + if (!supportedFeatures.isSupporting(obj.getFeatureDescription())) + exUnsupportedFeatures.addException(new UnsupportedFeatureException(obj)); + } + if (exUnsupportedFeatures.getNbErrors() > 0) { + if (true) + throw exUnsupportedFeatures; + } + // check the query: if (queryChecker != null) queryChecker.check(q); @@ -992,7 +1023,7 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { TextPosition endPos = null; try { // create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); stackQuery.push(query); } catch(Exception ex) { { @@ -4428,65 +4459,6 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { } } - private boolean jj_3_18() { - if (jj_3R_16()) - return true; - return false; - } - - private boolean jj_3R_55() { - Token xsp; - xsp = jj_scanpos; - if (jj_scan_token(25)) { - jj_scanpos = xsp; - if (jj_3R_74()) - return true; - } - return false; - } - - private boolean jj_3R_35() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_55()) - jj_scanpos = xsp; - if (jj_scan_token(JOIN)) - return true; - if (jj_3R_56()) - return true; - return false; - } - - private boolean jj_3R_34() { - if (jj_scan_token(NATURAL)) - return true; - Token xsp; - xsp = jj_scanpos; - if (jj_3R_54()) - jj_scanpos = xsp; - if (jj_scan_token(JOIN)) - return true; - return false; - } - - private boolean jj_3R_28() { - Token xsp; - xsp = jj_scanpos; - if (jj_scan_token(36)) - jj_scanpos = xsp; - if (jj_scan_token(BETWEEN)) - return true; - if (jj_3R_51()) - return true; - return false; - } - - private boolean jj_3_15() { - if (jj_3R_28()) - return true; - return false; - } - private boolean jj_3R_17() { Token xsp; xsp = jj_scanpos; @@ -6155,6 +6127,65 @@ public class ADQLParser200 implements ADQLParser, ADQLParser200Constants { return false; } + private boolean jj_3_18() { + if (jj_3R_16()) + return true; + return false; + } + + private boolean jj_3R_55() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(25)) { + jj_scanpos = xsp; + if (jj_3R_74()) + return true; + } + return false; + } + + private boolean jj_3R_35() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_55()) + jj_scanpos = xsp; + if (jj_scan_token(JOIN)) + return true; + if (jj_3R_56()) + return true; + return false; + } + + private boolean jj_3R_34() { + if (jj_scan_token(NATURAL)) + return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_54()) + jj_scanpos = xsp; + if (jj_scan_token(JOIN)) + return true; + return false; + } + + private boolean jj_3R_28() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(36)) + jj_scanpos = xsp; + if (jj_scan_token(BETWEEN)) + return true; + if (jj_3R_51()) + return true; + return false; + } + + private boolean jj_3_15() { + if (jj_3R_28()) + return true; + return false; + } + /** Generated Token Manager. */ public ADQLParser200TokenManager token_source; SimpleCharStream jj_input_stream; diff --git a/src/adql/parser/ADQLParser200TokenManager.java b/src/adql/parser/ADQLParser200TokenManager.java index 48f66b8b0551fb5f7947df65e552bc9bb036e685..3e87d17eb1df94451d18b4555352debede0f65b4 100644 --- a/src/adql/parser/ADQLParser200TokenManager.java +++ b/src/adql/parser/ADQLParser200TokenManager.java @@ -27,10 +27,12 @@ import java.util.Collection; import java.io.FileReader; import java.io.IOException; import adql.db.exception.UnresolvedIdentifiersException; +import adql.db.exception.UnsupportedFeatureException; import adql.parser.ADQLParserFactory.ADQLVersion; import adql.parser.IdentifierItems.IdentifierItem; import adql.parser.ADQLQueryFactory.JoinType; import adql.parser.feature.FeatureSet; +import adql.parser.feature.LanguageFeature; import adql.query.*; import adql.query.from.*; import adql.query.constraint.*; @@ -38,6 +40,7 @@ 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.search.SearchOptionalFeaturesHandler; import adql.translator.PostgreSQLTranslator; import adql.translator.TranslationException; diff --git a/src/adql/parser/ADQLParser201.java b/src/adql/parser/ADQLParser201.java index 3ad21c96346dcbba44ff5a189e9000cfff7a5b17..189eefb4c3dd005b768b11aaed1c2447ca885af4 100644 --- a/src/adql/parser/ADQLParser201.java +++ b/src/adql/parser/ADQLParser201.java @@ -542,7 +542,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a SELECT clause: Select(); @@ -564,7 +564,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a FROM clause: From(); @@ -586,7 +586,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a WHERE clause: Where(); @@ -608,7 +608,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a ORDER BY clause: OrderBy(); @@ -630,7 +630,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a GROUP BY clause: GroupBy(); @@ -1016,7 +1016,7 @@ public class ADQLParser201 implements ADQLParser, ADQLParser201Constants { TextPosition endPos = null; try { // create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); stackQuery.push(query); } catch(Exception ex) { { diff --git a/src/adql/parser/ADQLQueryFactory.java b/src/adql/parser/ADQLQueryFactory.java index 655b32fd125f04772bef8e4ddb16acfaa11060ea..a75c3c493958eb8eba8f2fa8240f2e247df747de 100644 --- a/src/adql/parser/ADQLQueryFactory.java +++ b/src/adql/parser/ADQLQueryFactory.java @@ -23,6 +23,7 @@ package adql.parser; import java.util.Collection; import adql.db.FunctionDef; +import adql.parser.ADQLParserFactory.ADQLVersion; import adql.parser.IdentifierItems.IdentifierItem; import adql.query.ADQLOrder; import adql.query.ADQLQuery; @@ -88,7 +89,7 @@ import adql.query.operand.function.string.LowerFunction; * </p> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (04/2019) + * @version 2.0 (07/2019) * * @see ADQLParser */ @@ -111,10 +112,30 @@ public class ADQLQueryFactory { ; } - public ADQLQuery createQuery() throws Exception { + /** + * @deprecated Since v2.0, {@link #createQuery(ADQLVersion)} must be + * used/extended instead. */ + @Deprecated + public final ADQLQuery createQuery() throws Exception { return new ADQLQuery(); } + /** + * Create an instance of {@link ADQLQuery}. + * + * @param version The version of the ADQL grammar followed by the query to + * create. + * + * @return A new {@link ADQLQuery}. + * + * @throws Exception If any error occurs while creating a new query. + * + * @since 2.0 + */ + public ADQLQuery createQuery(final ADQLVersion version) throws Exception { + return new ADQLQuery(version); + } + public ADQLTable createTable(final IdentifierItems idItems, final IdentifierItem alias) throws Exception { ADQLTable t = new ADQLTable(idItems.getCatalog(), idItems.getSchema(), idItems.getTable()); diff --git a/src/adql/parser/adqlGrammar200.jj b/src/adql/parser/adqlGrammar200.jj index 19129a3f75a7f7071f04c54d92e680674b298c39..36cffe7ed9bee43888462074da1bc4b2bbe6da81 100644 --- a/src/adql/parser/adqlGrammar200.jj +++ b/src/adql/parser/adqlGrammar200.jj @@ -79,6 +79,7 @@ import java.io.FileReader; import java.io.IOException; import adql.db.exception.UnresolvedIdentifiersException; +import adql.db.exception.UnsupportedFeatureException; import adql.parser.ADQLParserFactory.ADQLVersion; @@ -87,6 +88,7 @@ import adql.parser.IdentifierItems.IdentifierItem; import adql.parser.ADQLQueryFactory.JoinType; import adql.parser.feature.FeatureSet; +import adql.parser.feature.LanguageFeature; import adql.query.*; import adql.query.from.*; @@ -99,6 +101,8 @@ import adql.query.operand.function.*; import adql.query.operand.function.geometry.*; import adql.query.operand.function.geometry.GeometryFunction.GeometryValue; +import adql.search.SearchOptionalFeaturesHandler; + import adql.translator.PostgreSQLTranslator; import adql.translator.TranslationException; @@ -151,6 +155,12 @@ public class ADQLParser200 implements ADQLParser { /** Tools to build the object representation of the ADQL query. */ private ADQLQueryFactory queryFactory = new ADQLQueryFactory(); + /** Default set of supported language features. + * <p><i><b>Note:</b> + * By default, all optional features are supported. + * </i></p> */ + private FeatureSet supportedFeatures = new FeatureSet(false); + /** The stack of queries (because there may be some sub-queries). */ private Stack<ADQLQuery> stackQuery = new Stack<ADQLQuery>(); @@ -171,6 +181,7 @@ public class ADQLParser200 implements ADQLParser { */ public ADQLParser200(){ this(new java.io.ByteArrayInputStream("".getBytes())); + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); } @@ -222,7 +233,8 @@ public class ADQLParser200 implements ADQLParser { */ public ADQLParser200(java.io.InputStream stream, QueryChecker checker, ADQLQueryFactory factory) { this(stream); - setDebug(false); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); @@ -264,6 +276,9 @@ public class ADQLParser200 implements ADQLParser { */ public ADQLParser200(java.io.InputStream stream, String encoding, QueryChecker checker, ADQLQueryFactory factory) { this(stream, encoding); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); + setDebug(false); queryChecker = checker; @@ -305,7 +320,8 @@ public class ADQLParser200 implements ADQLParser { */ public ADQLParser200(java.io.Reader reader, QueryChecker checker, ADQLQueryFactory factory) { this(reader); - setDebug(false); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); @@ -346,7 +362,8 @@ public class ADQLParser200 implements ADQLParser { */ public ADQLParser200(ADQLParser200TokenManager tm, QueryChecker checker, ADQLQueryFactory factory) { this(tm); - setDebug(false); + + supportedFeatures.supportAll(LanguageFeature.TYPE_ADQL_GEO); setDebug(false); @@ -405,14 +422,12 @@ public class ADQLParser200 implements ADQLParser { } public final FeatureSet getSupportedFeatures() { - FeatureSet languageFeatures = new FeatureSet(); - languageFeatures.unsupportAll(); - return languageFeatures; + return supportedFeatures; } - public final void setSupportedFeatures(final FeatureSet languageFeatures) { - /* Nothing to do here for ADQL-2.0 because no optional feature can be - * supported)! */ + public void setSupportedFeatures(final FeatureSet features) { + if (features != null) + supportedFeatures = features; } /* EXCEPTION HELPER FUNCTION */ @@ -552,7 +567,7 @@ public class ADQLParser200 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a SELECT clause: Select(); @@ -574,7 +589,7 @@ public class ADQLParser200 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a FROM clause: From(); @@ -596,7 +611,7 @@ public class ADQLParser200 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a WHERE clause: Where(); @@ -618,7 +633,7 @@ public class ADQLParser200 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a ORDER BY clause: OrderBy(); @@ -640,7 +655,7 @@ public class ADQLParser200 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); // Parse the string as a GROUP BY clause: GroupBy(); @@ -1201,6 +1216,19 @@ TOKEN : { ADQLQuery Query(): {ADQLQuery q = null;}{ q=QueryExpression() (<EOF> | <EOQ>) { + /* check the optional features before any other check: + * (note: this check is very close to grammar check...hence its higher + * priority) */ + UnresolvedIdentifiersException exUnsupportedFeatures = new UnresolvedIdentifiersException("unsupported expression"); + SearchOptionalFeaturesHandler sFeaturesHandler = new SearchOptionalFeaturesHandler(true, false); + sFeaturesHandler.search(q); + for(ADQLObject obj : sFeaturesHandler) { + if (!supportedFeatures.isSupporting(obj.getFeatureDescription())) + exUnsupportedFeatures.addException(new UnsupportedFeatureException(obj)); + } + if (exUnsupportedFeatures.getNbErrors() > 0) + throw exUnsupportedFeatures; + // check the query: if (queryChecker != null) queryChecker.check(q); @@ -1213,7 +1241,7 @@ ADQLQuery QueryExpression(): {TextPosition endPos = null;} { { try{ // create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_0); stackQuery.push(query); }catch(Exception ex){ throw generateParseException(ex); diff --git a/src/adql/parser/adqlGrammar201.jj b/src/adql/parser/adqlGrammar201.jj index 3506da9c616935a79691cf256006bcfc2be3202c..2ca25408bb39e8f16ead1837971f4c8c4bd2467c 100644 --- a/src/adql/parser/adqlGrammar201.jj +++ b/src/adql/parser/adqlGrammar201.jj @@ -563,7 +563,7 @@ public class ADQLParser201 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a SELECT clause: Select(); @@ -585,7 +585,7 @@ public class ADQLParser201 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a FROM clause: From(); @@ -607,7 +607,7 @@ public class ADQLParser201 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a WHERE clause: Where(); @@ -629,7 +629,7 @@ public class ADQLParser201 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a ORDER BY clause: OrderBy(); @@ -651,7 +651,7 @@ public class ADQLParser201 implements ADQLParser { try { // Create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); // Parse the string as a GROUP BY clause: GroupBy(); @@ -1245,7 +1245,7 @@ ADQLQuery QueryExpression(): {TextPosition endPos = null;} { { try{ // create the query: - query = queryFactory.createQuery(); + query = queryFactory.createQuery(ADQLVersion.V2_1); stackQuery.push(query); }catch(Exception ex){ throw generateParseException(ex); diff --git a/src/adql/query/ADQLQuery.java b/src/adql/query/ADQLQuery.java index fc679f378ca6321dc21e05dca8e4ac8fd0ad6463..1d280337d221af9a997fc4df66ba5c355985b572 100644 --- a/src/adql/query/ADQLQuery.java +++ b/src/adql/query/ADQLQuery.java @@ -29,6 +29,8 @@ import adql.db.DBType; import adql.db.DBType.DBDatatype; import adql.db.DefaultDBColumn; import adql.parser.ADQLParser; +import adql.parser.ADQLParserFactory; +import adql.parser.ADQLParserFactory.ADQLVersion; import adql.parser.ParseException; import adql.parser.feature.LanguageFeature; import adql.query.from.FromContent; @@ -59,6 +61,10 @@ public class ADQLQuery implements ADQLObject { * @since 2.0 */ public static final LanguageFeature FEATURE = new LanguageFeature(null, "QUERY", false, "An entire ADQL (sub-)query."); + /** Version of the ADQL grammar in which this query is written. + * @since 2.0 */ + private final ADQLVersion adqlVersion; + /** The ADQL clause SELECT. */ private ClauseSelect select; @@ -86,6 +92,21 @@ public class ADQLQuery implements ADQLObject { * Builds an empty ADQL query. */ public ADQLQuery() { + this(ADQLParserFactory.DEFAULT_VERSION); + } + + /** + * Builds an empty ADQL query following the specified ADQL grammar. + * + * @param version Followed version of the ADQL grammar. + * <i>If NULL, the + * {@link ADQLParserFactory#DEFAULT_VERSION default version} + * will be set.</i> + * + * @since 2.0 + */ + public ADQLQuery(final ADQLVersion version) { + this.adqlVersion = (version == null ? ADQLParserFactory.DEFAULT_VERSION : version); select = new ClauseSelect(); from = null; where = new ClauseConstraints("WHERE"); @@ -103,6 +124,7 @@ public class ADQLQuery implements ADQLObject { */ @SuppressWarnings("unchecked") public ADQLQuery(ADQLQuery toCopy) throws Exception { + adqlVersion = toCopy.adqlVersion; select = (ClauseSelect)toCopy.select.getCopy(); from = (FromContent)toCopy.from.getCopy(); where = (ClauseConstraints)toCopy.where.getCopy(); @@ -117,6 +139,15 @@ public class ADQLQuery implements ADQLObject { return FEATURE; } + /** + * Get the followed version of the ADQL grammar. + * + * @return The followed ADQL grammar version. + */ + public final ADQLVersion getADQLVersion() { + return adqlVersion; + } + /** * Clear all the clauses. */ diff --git a/test/adql/db/TestDBChecker.java b/test/adql/db/TestDBChecker.java index 27cace648390def8b5358ba9eec82c8aba1118a9..cb602976ef94ef22bef51634f5fa430ee3e9b437 100644 --- a/test/adql/db/TestDBChecker.java +++ b/test/adql/db/TestDBChecker.java @@ -32,6 +32,10 @@ import adql.query.operand.ADQLOperand; import adql.query.operand.StringConstant; import adql.query.operand.function.DefaultUDF; import adql.query.operand.function.UserDefinedFunction; +import adql.query.operand.function.geometry.CircleFunction; +import adql.query.operand.function.geometry.ContainsFunction; +import adql.query.operand.function.geometry.PointFunction; +import adql.query.operand.function.geometry.RegionFunction; import adql.search.SimpleSearchHandler; import adql.translator.ADQLTranslator; import adql.translator.PostgreSQLTranslator; @@ -399,7 +403,11 @@ public class TestDBChecker { // Test with several geometries while only the allowed ones: try { parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, new ArrayList<FunctionDef>(0), Arrays.asList(new String[]{ "CONTAINS", "POINT", "CIRCLE" }), null)); + parser.getSupportedFeatures().unsupportAll(LanguageFeature.TYPE_ADQL_GEO); + parser.getSupportedFeatures().support(ContainsFunction.FEATURE); + parser.getSupportedFeatures().support(PointFunction.FEATURE); + parser.getSupportedFeatures().support(CircleFunction.FEATURE); + assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;")); } catch(ParseException pe) { pe.printStackTrace(); @@ -412,18 +420,24 @@ public class TestDBChecker { assertTrue(pe instanceof UnresolvedIdentifiersException); UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe; assertEquals(1, ex.getNbErrors()); - assertEquals("The geometrical function \"INTERSECTS\" is not available in this implementation!", ex.getErrors().next().getMessage()); + assertEquals("Unsupported ADQL feature: \"INTERSECTS\" (of type 'ivo://ivoa.net/std/TAPRegExt#features-adql-geo')!", ex.getErrors().next().getMessage()); } // Test by adding REGION: try { parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, new ArrayList<FunctionDef>(0), Arrays.asList(new String[]{ "CONTAINS", "POINT", "CIRCLE", "REGION" }), null)); + parser.getSupportedFeatures().unsupportAll(LanguageFeature.TYPE_ADQL_GEO); + parser.getSupportedFeatures().support(ContainsFunction.FEATURE); + parser.getSupportedFeatures().support(PointFunction.FEATURE); + parser.getSupportedFeatures().support(CircleFunction.FEATURE); + parser.getSupportedFeatures().support(RegionFunction.FEATURE); + assertNotNull(parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('Position 12.3 45.6'), REGION('circle 1.2 2.3 5')) = 1;")); } catch(ParseException pe) { pe.printStackTrace(); fail("This query contains several geometries, and all are theoretically allowed: this test should have succeeded!"); } + // TODO Deal with un/supported Regions inside STC-S! try { parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(REGION('Position 12.3 45.6'), REGION('BOX 1.2 2.3 5 9')) = 1;"); fail("This query contains a not-allowed geometry function (BOX): this test should have failed!"); @@ -437,7 +451,8 @@ public class TestDBChecker { // Test with several geometries while none geometry is allowed: try { parser = parserFactory.createParser(); - parser.setQueryChecker(new DBChecker(tables, new ArrayList<FunctionDef>(0), new ArrayList<String>(0), null)); + parser.getSupportedFeatures().unsupportAll(LanguageFeature.TYPE_ADQL_GEO); + parser.parseQuery("SELECT * FROM foo WHERE CONTAINS(POINT('', 12.3, 45.6), CIRCLE('', 1.2, 2.3, 5)) = 1;"); fail("This query contains geometries while they are all forbidden: this test should have failed!"); } catch(ParseException pe) { @@ -445,9 +460,9 @@ public class TestDBChecker { UnresolvedIdentifiersException ex = (UnresolvedIdentifiersException)pe; assertEquals(3, ex.getNbErrors()); Iterator<ParseException> itErrors = ex.getErrors(); - assertEquals("The geometrical function \"CONTAINS\" is not available in this implementation!", itErrors.next().getMessage()); - assertEquals("The geometrical function \"POINT\" is not available in this implementation!", itErrors.next().getMessage()); - assertEquals("The geometrical function \"CIRCLE\" is not available in this implementation!", itErrors.next().getMessage()); + assertEquals("Unsupported ADQL feature: \"CONTAINS\" (of type 'ivo://ivoa.net/std/TAPRegExt#features-adql-geo')!", itErrors.next().getMessage()); + assertEquals("Unsupported ADQL feature: \"POINT\" (of type 'ivo://ivoa.net/std/TAPRegExt#features-adql-geo')!", itErrors.next().getMessage()); + assertEquals("Unsupported ADQL feature: \"CIRCLE\" (of type 'ivo://ivoa.net/std/TAPRegExt#features-adql-geo')!", itErrors.next().getMessage()); } } diff --git a/test/adql/parser/TestADQLParser.java b/test/adql/parser/TestADQLParser.java index c955962471bc61846c15dcdba9793cecadeb6193..1e6893cf6d68e5fa945b5c4457283ad6be3d3330 100644 --- a/test/adql/parser/TestADQLParser.java +++ b/test/adql/parser/TestADQLParser.java @@ -19,6 +19,7 @@ import adql.query.ADQLQuery; import adql.query.from.ADQLJoin; import adql.query.from.ADQLTable; import adql.query.operand.StringConstant; +import adql.query.operand.function.geometry.PointFunction; import adql.query.operand.function.string.LowerFunction; public class TestADQLParser { @@ -427,6 +428,45 @@ public class TestADQLParser { assertEquals(UnsupportedFeatureException.class, err.getClass()); assertEquals("Unsupported ADQL feature: \"LOWER\" (of type '" + LanguageFeature.TYPE_ADQL_STRING + "')!", err.getMessage()); } + + /* ***************************************************************** */ + /* NOTE: Geometrical functions are the only optional features in 2.0 */ + /* ***************************************************************** */ + + parser = parserFactory.createParser(ADQLVersion.V2_0); + + // CASE: By default all geometries are supported so if one is used => OK + try { + assertNotNull(parser.parseQuery("SELECT POINT('', ra, dec) FROM aTable")); + } catch(Throwable t) { + t.printStackTrace(); + fail("Unexpected error! This query should have passed. (see console for more details)"); + } + + // unsupport all features: + parser.getSupportedFeatures().unsupportAll(); + + // CASE: No geometry supported so if one is used => ERROR + try { + parser.parseQuery("SELECT POINT('', ra, dec) FROM aTable"); + fail("The geometrical function \"POINT\" is not declared. This query should not pass."); + } catch(Throwable t) { + assertEquals(UnresolvedIdentifiersException.class, t.getClass()); + UnresolvedIdentifiersException allErrors = (UnresolvedIdentifiersException)t; + assertEquals(1, allErrors.getNbErrors()); + assertEquals("Unsupported ADQL feature: \"POINT\" (of type 'ivo://ivoa.net/std/TAPRegExt#features-adql-geo')!", allErrors.getErrors().next().getMessage()); + } + + // now support only POINT: + assertTrue(parser.getSupportedFeatures().support(PointFunction.FEATURE)); + + // CASE: Just supporting the only used geometry => OK + try { + assertNotNull(parser.parseQuery("SELECT POINT('', ra, dec) FROM aTable")); + } catch(Throwable t) { + t.printStackTrace(); + fail("Unexpected error! This query should have passed. (see console for more details)"); + } } }