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 checks the syntax of the query and points as exactly as possible the position of the errors. Besides, it would be great that it also reports incorrect table/column names. Finally, once the query is successfully parsed, we want to translate it into an SQL understandable by our DataBase Manager System (DBMS), and particularly the ADQL geometrical functions.

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:
	DBTable 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 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 it = ((UnresolvedIdentifiersException)pe).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, the library calls the method check(ADQLQuery) of the interface QueryChecker. That is the way a developer can add more checks while 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 default implementations for PostgreSQL and PostgreSQL+PgSphere 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.

PostgreSQL

The library already includes a translator for PostgreSQL: PostgreSQLTranslator. Thus ADQL is translated into SQL specific to Postgres. Obviously the generated SQL can work for other DBMS but there are always slight differences between the SQL of each DBMS. That's why you may have to write your own translator either from the interface or by extending the Postgres one.

PgSphere

The Postgres translator is able to translate all SQL-like parts of an ADQL query but not the geometrical functions (i.e. contains, intersects, point, ....) whose the SQL translation is actually the ADQL expression. Each DBMS manages geometrical information differently. With Postgres you have (at least) 2 extensions for that: Q3C and PgSphere. Each one has its own syntax. That's why you have to write one translator for each one. In the library, the translator for PostgreSQL+PgSphere already exists: PgSphereTranslator. It directly extends the Postgres translator.