Commit e4366686 authored by Grégory Mantelet's avatar Grégory Mantelet
Browse files

[ADQL] Add examples of ADQL parsing.

parent deba2e14
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
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());
		}

	}

}
+49 −0
Original line number Diff line number Diff line
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&eacute;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());
		}

	}

}
+172 −0
Original line number Diff line number Diff line
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&eacute;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());
		}

	}

}
+108 −0
Original line number Diff line number Diff line
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&eacute;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());

		}

	}

}
+110 −0
Original line number Diff line number Diff line
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&eacute;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());
		}

	}

}