Getting started

The first goal of this library is to provide a simple API to parse ADQL queries. Here are your first steps with it....

To show you how to use this library, let's take a simple use case !

We have a database in which the opposite table includes the name, the type and the position of some astronomical objects. The goal is that any user can query this table with ADQL as in the following example:

Select name, ra || ' - ' || dec as "Position"
From data
Where Contains(Point('ICRS', ra, dec), Circle('ICRS', 10, 5, 1)) = 1
Order By name;

We expect that the library:

  1. checks the syntax of the query. In case of error, it must reports as precisely as possible the position of the errors
  2. reports incorrect table/column names and identifiers
  3. once the query is successfully parsed, lets translate it (and particularly the ADQL geometrical functions) into an SQL query understandable by our DataBase Manager System (DBMS)
DB Schema

In order to check the syntax of a given query, no particular development is required. You just have to create an instance of ADQLParser and call the method parseQuery with either an InputStream or a String.

try{
	// Create a parser:
	ADQLParser parser = new ADQLParser();
	
	// Parse ADQL:
	ADQLQuery query = parser.parseQuery(System.in);
	System.out.println("Correct ADQL !");
	
}catch(ParseException pe){
	System.err.println("ADQL syntax incorrect between "+pe.getPosition()+": "+pe.getMessage());
	System.exit(2);
}
  • Success The library generates an object of type ADQLQuery. This object is actually a tree, in which we can fetch all clauses: Select, From, Where, ... This tree lets do anything you want with the query. It is particularly useful to modify some part of the query (for example, to optimize its execution in the database) - see B.3. Tree manipulation.
  • Syntax Error A ParseException is thrown. It has a description but also the position of the error. The position is an object of type TextPosition, which has a begin line, end line, begin column and end column. Thus you can highlight precisely the part of query which causes the error.
Note: If you play with the demo/validator page, you will see that:
  • the ADQL tree generated by the library is drawn. For each node, you have the Java class name (with a link to the Javadoc) and the type of the ADQL part it represents.
  • errors are explained and highlighted in the query itself

Parsing ADQL does not mean only detecting lexical and syntactic errors. It may also include checking the consistency with the database schema. By merely providing the list of all available tables and columns, the library will be able to identify them in the query.

try{
	// Create a parser:
	ADQLParser parser = new ADQLParser();

	// Describe the table:
	DefaultDBTable table = new DefaultDBTable("data");
	table.addColumn(new DefaultDBColumn("id", table));
	table.addColumn(new DefaultDBColumn("name", table));
	table.addColumn(new DefaultDBColumn("ra", table));
	table.addColumn(new DefaultDBColumn("dec", table));
	table.addColumn(new DefaultDBColumn("coord", table));
	table.addColumn(new DefaultDBColumn("type", table));

	// List all available tables:
	ArrayList<DBTable> tables = new ArrayList(1);
	tables.add(table);
	// Create a special type of QueryChecker:
	QueryChecker checker = new DBChecker(tables);
	// Set the DBChecker to the parser:
	parser.setQueryChecker(checker);
	// Parse ADQL:
	ADQLQuery query = parser.parseQuery(System.in);
	System.out.println("Correct ADQL !");
}catch(UnresolvedIdentifiersException uie){
	System.err.println(uie.getNbErrors() + " unresolved identifiers:");
	Iterator<ParseException> it = uie.getErrors();
	ParseException ex = null;
	while(it.hasNext()){
		ex = it.next();
		System.err.println("    * " + ex.getPosition() + " " + ex.getMessage());
	}
	System.exit(2);
}catch(ParseException pe){
	System.err.println("ADQL syntax incorrect between " + pe.getPosition() + ": " + pe.getMessage());
	System.exit(2);
}

QueryChecker

Before the end of the parsing process, the library calls the method check(ADQLQuery) of the interface QueryChecker. This is the way a developer can add more checks during the parsing phase. If he decides that something is incorrect in the query, he can throw a ParseException. DBChecker is an implementation of this interface which checks that all tables and columns used in the ADQL query exist in the database.

DBTable & DBColumn

The database or more precisely the list of available tables, is described by the interfaces DBTable and DBColumn. One default implementation exists in the library for each of them: DefaultDBTable and DefaultDBColumn.

DB Interfaces

Metadata links

Knowing the database content, the DBChecker will link column and table references of the ADQL tree to the corresponding DBTable and DBColumn. So if you have more useful information about them, you should write your own implementation of the interfaces.

UnresolvedIdentifiersException

Since there may be several unresolved table/column references, the DBChecker will throw an UnresolvedIdentifiersException which extends ParseException and which gathers several errors about incorrect table/column references. So to have details, you must iterate on its errors collection, as it is done opposite.

Translating ADQL into SQL totally depends from your DBMS or more precisely on how you manage geometrical functions. The library provides the interface ADQLTranslator that you must implement in function of your solution. However a partial JDBC implementation as well as more precise and complete default implementations for PostgreSQL (with or without PgSphere), MySQL and MS-SQLServer are also provided.

try{
	// Build the translator (for PostgreSQL+PgSphere):
	ADQLTranslator translator = new PgSphereTranslator();

	// Translate:
	String sql = translator.translate(query);
	System.out.println("*** SQL ***\n"+sql);

}catch(TranslationException te){
	System.err.println("Translation into SQL failed: "+te.getMessage());
}

ADQLTranslator

Thanks to this interface, you can translate an ADQL query into another query language (like SQL). ADQLTranslator has one function translate for each ADQL object (column, table, join, ...). By implementing it you will have to write the translation of all parts of an ADQL query.

JDBCTranslator

A partial abstract implementation of ADQLTranslator already covers the translation of all functions, but the geometrical functions which remains abstract: JDBCTranslator

Of course, each DBMS has its own particularities regarding the SQL syntax. So adaptations are almost always needed while extending JDBCTranslator. All of the default implementations described below already attemps to cover all of them for the targeted DBMS.

DBMS-specific translators

The library includes a translator for all the below DBMS: