diff --git a/examples/vollt_examples/adql/parse/A_SimpleQueryParsing.java b/examples/vollt_examples/adql/parse/A_SimpleQueryParsing.java new file mode 100644 index 0000000000000000000000000000000000000000..f0e98dd143f2c22573b6df981bcf2a889c55ffd0 --- /dev/null +++ b/examples/vollt_examples/adql/parse/A_SimpleQueryParsing.java @@ -0,0 +1,49 @@ +package vollt_examples.adql.parse; + +import adql.parser.ADQLParser; +import adql.parser.grammar.ParseException; +import adql.query.ADQLQuery; + +/** + * Minimal lines required to parse an ADQL query. + * + * @author Grégory Mantelet (CDS) + * @version 08/2019 + */ +public class A_SimpleQueryParsing { + + public static void main(final String[] args) { + + // Input query: + final String QUERY = "Select name, ra || ' - ' || dec as \"Position\"\nFrom data\nWhere Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1\nOrder By name;"; + System.out.println("\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + // 1. CREATE A PARSER: + ADQLParser parser = new ADQLParser(); + /* + * To create a parser of a specific version of the ADQL grammar: + * + * import adql.parser.ADQLParser.ADQLVersion; + * [...] + * ADQLParser parser = new ADQLParser(ADQLVersion.V2_0); + * + */ + + // 2. PARSE AN ADQL QUERY: + ADQLQuery query = parser.parseQuery(QUERY); + + System.out.println("\n((i)) Correct ADQL query ((i))"); + + System.out.println("\n((i)) As interpreted: ((i))\n " + query.toADQL().replaceAll("\n", "\n ")); + + } + // 3. EVENTUALLY DEAL WITH ERRORS: + catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getMessage()); + } + + } + +} diff --git a/examples/vollt_examples/adql/parse/B_SimpleClauseParsing.java b/examples/vollt_examples/adql/parse/B_SimpleClauseParsing.java new file mode 100644 index 0000000000000000000000000000000000000000..b3ca4d62e9fe3d0da8704764eb59715f1fb21bdb --- /dev/null +++ b/examples/vollt_examples/adql/parse/B_SimpleClauseParsing.java @@ -0,0 +1,49 @@ +package vollt_examples.adql.parse; + +import adql.parser.ADQLParser; +import adql.parser.grammar.ParseException; +import adql.query.ClauseConstraints; + +/** + * Minimal lines required to parse an ADQL clause (here: <code>WHERE</code>). + * + * @author Grégory Mantelet (CDS) + * @version 08/2019 + */ +public class B_SimpleClauseParsing { + + public static void main(final String[] args) { + + // Input ADQL expression: + final String CLAUSE = "Where Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1"; + System.out.println("\n " + CLAUSE); + + try { + + // 1. CREATE A PARSER: + ADQLParser parser = new ADQLParser(); + /* + * To create a parser of a specific version of the ADQL grammar: + * + * import adql.parser.ADQLParser.ADQLVersion; + * [...] + * ADQLParser parser = new ADQLParser(ADQLVersion.V2_0); + * + */ + + // 2. PARSE A SPECIFIC ADQL CLAUSE: + ClauseConstraints constraints = parser.parseWhere(CLAUSE); + + System.out.println("\n((i)) Correct WHERE clause ((i))"); + + System.out.println("\n((i)) As interpreted: `" + constraints.toADQL() + "` ((i))"); + + } + // 3. EVENTUALLY DEAL WITH ERRORS: + catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT CLAUSE! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getPosition() + " " + ex.getMessage()); + } + + } + +} diff --git a/examples/vollt_examples/adql/parse/C_HandleParseException.java b/examples/vollt_examples/adql/parse/C_HandleParseException.java new file mode 100644 index 0000000000000000000000000000000000000000..36ca42546418d1fbc4716642a94245ec35698415 --- /dev/null +++ b/examples/vollt_examples/adql/parse/C_HandleParseException.java @@ -0,0 +1,172 @@ +package vollt_examples.adql.parse; + +import java.util.HashSet; + +import adql.db.DBChecker; +import adql.db.DBTable; +import adql.db.exception.UnresolvedIdentifiersException; +import adql.parser.ADQLParser; +import adql.parser.ADQLParser.ADQLVersion; +import adql.parser.feature.FeatureSet; +import adql.parser.grammar.ParseException; +import adql.query.TextPosition; + +/** + * Examples and explanations on how to handle with different kinds of parsing + * error. + * + * @author Grégory Mantelet (CDS) + * @version 08/2019 + */ +public class C_HandleParseException { + + public static void main(final String[] args) { + + //////////////////////////////////////////////////////////////////////// + // CASE 1/3: Simple error // + //////////////////////////////////////////////////////////////////////// + + // Input query (i.e. SELECT keyword missing): + String QUERY = "name, ra || ' - ' || dec as \"Position\"\nFrom data\nWhere Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1\nOrder By name;"; + System.out.println("\n##### QUERY #1 #####\n\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + // Parse the query: + (new ADQLParser()).parseQuery(QUERY); + + System.err.println("((X)) This message should never be visible! ((X)"); + + } catch(ParseException ex) { + + // Full name of the caught exception: + final String CLASS = ex.getClass().getName(); + /* + * NOTES: + * ParseException is the root type of all possible exceptions that + * can possibly be generated by an ADQLParser. Special types of + * ParseException are going to be demonstrated with other examples + * below. + */ + + // Position of the error inside the input ADQL query: + final TextPosition POSITION = ex.getPosition(); + /* + * NOTES: + * A TextPosition keeps the begin and end line and column numbers. + * They can be got individually directly with the attributes: + * - TextPosition.beginLine + * - TextPosition.endLine + * - TextPosition.beginColumn + * - TextPosition.endColumn + * + * TextPosition.toString() will conveniently print all these + * information in a string like: `[l.1 c.5 - l.1 c.9]`. + */ + + // Error message + final String MESSAGE = ex.getMessage(); + /* + * NOTES: + * A ParseException is still an error. So a message with + * a human description of the error is also available. + */ + + System.out.println("\n((X)) INCORRECT QUERY! " + CLASS + " ((X))\n" + POSITION + " " + MESSAGE); + + } + + //////////////////////////////////////////////////////////////////////// + // CASE 2/3: Unresolved identifiers // + //////////////////////////////////////////////////////////////////////// + + // Input query (i.e. SELECT keyword missing): + QUERY = "SELECT name, ra || ' - ' || dec as \"Position\"\nFrom data\nWhere Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1\nOrder By name;"; + System.out.println("\n##### QUERY #2 #####\n\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + /* Parse the query (with an empty list of DB tables + * => no table/column can be resolved in any parsed query): */ + (new ADQLParser(null, new DBChecker(new HashSet<DBTable>(0)), null, null)).parseQuery(QUERY); + + System.err.println("\n((X)) This message should never be visible! ((X)"); + + } catch(UnresolvedIdentifiersException ex) { + + /* + * NOTES: + * UnresolvedIdentifiersException is the only extension of + * ParseException able to represent more than one error at a time. + * + * It is possible to count and to list these errors with: + * + * - UnresolvedIdentifiersException.getNbErrors() + * + * - UnresolvedIdentifiersException.getErrors() + * or UnresolvedIdentifiersException.iterator() + * + * As suggested by its name, this exception is very often used to + * list unresolved table/column names. + */ + + System.out.println("\n((X))) INCORRECT QUERY! Cause: " + ex.getNbErrors() + " unresolved identifiers! ((X))"); + for(ParseException pe : ex) { + System.out.println(" - " + pe.getPosition() + " " + pe.getMessage()); + } + + } catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getPosition() + " " + ex.getMessage()); + } + + //////////////////////////////////////////////////////////////////////// + // CASE 3/3: Unsupported features // + //////////////////////////////////////////////////////////////////////// + + // Input query: + QUERY = "Select LOWER(name), MY_UDF('foo')\nFrom data;"; + System.out.println("\n##### QUERY #3 #####\n\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + /* Parse the query (as above, with an empty list of DB tables + * BUT ALSO with absolutely no optional feature supported): */ + (new ADQLParser(ADQLVersion.V2_1, new DBChecker(new HashSet<DBTable>(0)), null, new FeatureSet(false, false))).parseQuery(QUERY); + + System.err.println("\n((X)) This message should never be visible! ((X)"); + + } catch(UnresolvedIdentifiersException ex) { + + /* + * NOTES: + * Since ADQL-2.1, there is the notion of optional language + * features. + * + * Because several unsupported features may be used in a same + * ADQL query, UnresolvedIdentifiersException is now also used to + * list all of them. + * + * However, unresolved identifiers and unsupported features can + * not be raised in the same time. First, supported features are + * tested. If no unsupported features are detected, then, + * identifiers resolution are tested. So, if unsupported features + * are detected, an UnresolvedIdentifiersException listing all of + * them is immediately thrown, which prevent testing the + * identifiers. + * + * So, errors about support of language features always have the + * priority (as illustrated in this example). + */ + + System.out.println("\n((X))) INCORRECT QUERY! Cause: " + ex.getNbErrors() + " unsupported features! ((X))"); + for(ParseException pe : ex) { + System.out.println(" - " + pe.getPosition() + " " + pe.getMessage()); + } + + } catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getPosition() + " " + ex.getMessage()); + } + + } + +} diff --git a/examples/vollt_examples/adql/parse/D_DeclareOptionalFeatures.java b/examples/vollt_examples/adql/parse/D_DeclareOptionalFeatures.java new file mode 100644 index 0000000000000000000000000000000000000000..676b767ec92cf18ac1639120e8af49a44ee57710 --- /dev/null +++ b/examples/vollt_examples/adql/parse/D_DeclareOptionalFeatures.java @@ -0,0 +1,108 @@ +package vollt_examples.adql.parse; + +import adql.db.exception.UnresolvedIdentifiersException; +import adql.parser.ADQLParser; +import adql.parser.ADQLParser.ADQLVersion; +import adql.parser.feature.FeatureSet; +import adql.parser.feature.LanguageFeature; +import adql.parser.grammar.ParseException; +import adql.query.operand.function.string.LowerFunction; + +/** + * Example on how to support/unsupport some optional language features while + * parsing an ADQL query. + * + * @author Grégory Mantelet (CDS) + * @version 08/2019 + */ +public class D_DeclareOptionalFeatures { + + public static void main(final String[] args) throws Throwable { + + // Input query: + final String QUERY = "Select LOWER(name), ra || ' - ' || dec as \"Position\"\nFrom data;"; + System.out.println("\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + // Create a parser (supporting the notion of optional features): + ADQLParser parser = new ADQLParser(ADQLVersion.V2_1); + + /* + * NOTES: + * If none is specified in parameter of the constructor, + * ADQLParser internally creates a default set of supported + * features. + * + * This default may change in function of the ADQL grammar + * version. But generally, all features described in the version + * of an ADQL grammar are by default all supported by ADQLParser. + * + * See ADQLParser.setDefaultFeatures() to know the exact content + * of the default features set created by ADQLParser. + * + * To customize the list of supported features, you have 3 + * possibilities: + * + * 1. Create an instance of FeatureSet and its support(...) + * and unsupport(...) functions to specify which features + * are supported or not. + * Then, give this FeatureSet in parameter when creating + * an ADQLParser. + * + * 2. As in 1., create a custom FeatureSet and set it to an + * existing ADQLParser instance using the function + * ADQLParser.setSupportedFeatures(). + * + * 3. Get the FeatureSet of an already created ADQLParser with + * ADQLParser.getSupportedFeatures(). + * Then, customize it as in 1. and 2. (with support(...) and + * unsupport(...) functions). + * + * The functions FeatureSet.support(...) and + * FeatureSet.unsupport(...) take an instance of LanguageFeature + * in parameter. You DO NOT NEED to create a new one for each + * feature you want to support/unsupport. All ADQLObject + * extensions have a public static attribute named `FEATURE` of + * type LanguageFeature. So if you know the name of the class + * corresponding to your feature, you just have to use its + * attribute FEATURE (as illustrated below). + */ + + // Create an empty set of language features: + FeatureSet features = new FeatureSet(false, false); + + // Support all available features: + features.supportAll(); + + // Just get the LanguageFeature for LOWER (DO NOT CREATE IT): + final LanguageFeature lowerFeature = LowerFunction.FEATURE; + + // Ensures LOWER is now supported: + if (!parser.getSupportedFeatures().isSupporting(lowerFeature)) + throw new Error("This example can not work properly. LOWER seems to be NOT supported in the default FeatureSet of ADQLParser. To fix this example, pick a different optional features."); + + // BUT for our example, now un-support it: + parser.getSupportedFeatures().unsupport(lowerFeature); + + // Parse the query: + parser.parseQuery(QUERY); + + System.err.println("\n((X)) This message should never be visible! ((X)"); + + } catch(UnresolvedIdentifiersException ex) { + + System.out.println("\n((X))) INCORRECT QUERY! Cause: " + ex.getNbErrors() + " unsupported features! ((X))"); + for(ParseException pe : ex) { + System.out.println(" - " + pe.getPosition() + " " + pe.getMessage()); + } + + } catch(ParseException ex) { + + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getPosition() + " " + ex.getMessage()); + + } + + } + +} diff --git a/examples/vollt_examples/adql/parse/E_DeclareUDF.java b/examples/vollt_examples/adql/parse/E_DeclareUDF.java new file mode 100644 index 0000000000000000000000000000000000000000..dd6400dc5722c3927aee724179cf9bd5432c8e2d --- /dev/null +++ b/examples/vollt_examples/adql/parse/E_DeclareUDF.java @@ -0,0 +1,110 @@ +package vollt_examples.adql.parse; + +import adql.db.FunctionDef; +import adql.parser.ADQLParser; +import adql.parser.feature.FeatureSet; +import adql.parser.grammar.ParseException; +import adql.query.ADQLQuery; + +/** + * Examples and explanations about how to declare UDF. + * + * @author Grégory Mantelet (CDS) + * @version 08/2019 + */ +public class E_DeclareUDF { + + public static void main(final String[] args) { + + // Input query: + final String QUERY = "Select MY_UDF(name)\nFrom data"; + + //////////////////////////////////////////////////////////////////////// + // CASE 1/3: Default = any undeclared UDF allowed // + //////////////////////////////////////////////////////////////////////// + + System.out.println("\n##### DEFAULT = ANY UNDECLARED UDF ALLOWED #####\n\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + // Create the parser: + ADQLParser parser = new ADQLParser(); + + // Parse the query: + ADQLQuery query = parser.parseQuery(QUERY); + + System.out.println("\n((i)) Correct ADQL query ((i))"); + + System.out.println("\n((i)) As interpreted: ((i))\n " + query.toADQL().replaceAll("\n", "\n ")); + + } + // 3. EVENTUALLY DEAL WITH ERRORS: + catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getMessage()); + } + + //////////////////////////////////////////////////////////////////////// + // CASE 2/3: No undeclared UDF allowed // + //////////////////////////////////////////////////////////////////////// + + System.out.println("\n##### NO UNDECLARED UDF ALLOWED #####\n\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + // Create the parser: + ADQLParser parser = new ADQLParser(); + + // FORBID ALL UNDECLARED UDF: + parser.getSupportedFeatures().allowAnyUdf(false); + + // Parse the query: + ADQLQuery query = parser.parseQuery(QUERY); + + System.out.println("\n((i)) Correct ADQL query ((i))"); + + System.out.println("\n((i)) As interpreted: ((i))\n " + query.toADQL().replaceAll("\n", "\n ")); + + } + // 3. EVENTUALLY DEAL WITH ERRORS: + catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getMessage()); + } + + //////////////////////////////////////////////////////////////////////// + // CASE 3/3: Declare a UDF // + //////////////////////////////////////////////////////////////////////// + + System.out.println("\n##### DECLARE A UDF #####\n\n " + QUERY.replaceAll("\n", "\n ")); + + try { + + // Create the parser: + ADQLParser parser = new ADQLParser(); + + // FORBID ALL UNDECLARED UDF: + FeatureSet features = parser.getSupportedFeatures(); + features.allowAnyUdf(false); + + // DECLARE A UDF: + // ...define this function: + FunctionDef myUdf = FunctionDef.parse("my_udf(param1 VARCHAR) -> VARCHAR", parser.getADQLVersion()); + // ...now add it to the supported features: + if (!features.support(myUdf.toLanguageFeature())) + throw new Error("Impossible to support the UDF `" + myUdf + "`! This is the important point of this example file."); + + // Parse the query: + ADQLQuery query = parser.parseQuery(QUERY); + + System.out.println("\n((i)) Correct ADQL query ((i))"); + + System.out.println("\n((i)) As interpreted: ((i))\n " + query.toADQL().replaceAll("\n", "\n ")); + + } + // 3. EVENTUALLY DEAL WITH ERRORS: + catch(ParseException ex) { + System.out.println("\n((X)) INCORRECT QUERY! " + ex.getClass().getSimpleName() + " ((X))\n" + ex.getMessage()); + } + + } + +}